spread_volatile_semantics.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // Copyright (c) 2022 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "source/opt/spread_volatile_semantics.h"
  15. #include "source/opt/decoration_manager.h"
  16. #include "source/opt/ir_builder.h"
  17. #include "source/spirv_constant.h"
  18. namespace spvtools {
  19. namespace opt {
  20. namespace {
  21. const uint32_t kOpDecorateInOperandBuiltinDecoration = 2u;
  22. const uint32_t kOpLoadInOperandMemoryOperands = 1u;
  23. const uint32_t kOpEntryPointInOperandEntryPoint = 1u;
  24. const uint32_t kOpEntryPointInOperandInterface = 3u;
  25. bool HasBuiltinDecoration(analysis::DecorationManager* decoration_manager,
  26. uint32_t var_id, uint32_t built_in) {
  27. return decoration_manager->FindDecoration(
  28. var_id, SpvDecorationBuiltIn, [built_in](const Instruction& inst) {
  29. return built_in == inst.GetSingleWordInOperand(
  30. kOpDecorateInOperandBuiltinDecoration);
  31. });
  32. }
  33. bool IsBuiltInForRayTracingVolatileSemantics(uint32_t built_in) {
  34. switch (built_in) {
  35. case SpvBuiltInSMIDNV:
  36. case SpvBuiltInWarpIDNV:
  37. case SpvBuiltInSubgroupSize:
  38. case SpvBuiltInSubgroupLocalInvocationId:
  39. case SpvBuiltInSubgroupEqMask:
  40. case SpvBuiltInSubgroupGeMask:
  41. case SpvBuiltInSubgroupGtMask:
  42. case SpvBuiltInSubgroupLeMask:
  43. case SpvBuiltInSubgroupLtMask:
  44. return true;
  45. default:
  46. return false;
  47. }
  48. }
  49. bool HasBuiltinForRayTracingVolatileSemantics(
  50. analysis::DecorationManager* decoration_manager, uint32_t var_id) {
  51. return decoration_manager->FindDecoration(
  52. var_id, SpvDecorationBuiltIn, [](const Instruction& inst) {
  53. uint32_t built_in =
  54. inst.GetSingleWordInOperand(kOpDecorateInOperandBuiltinDecoration);
  55. return IsBuiltInForRayTracingVolatileSemantics(built_in);
  56. });
  57. }
  58. bool HasVolatileDecoration(analysis::DecorationManager* decoration_manager,
  59. uint32_t var_id) {
  60. return decoration_manager->HasDecoration(var_id, SpvDecorationVolatile);
  61. }
  62. } // namespace
  63. Pass::Status SpreadVolatileSemantics::Process() {
  64. if (HasNoExecutionModel()) {
  65. return Status::SuccessWithoutChange;
  66. }
  67. const bool is_vk_memory_model_enabled =
  68. context()->get_feature_mgr()->HasCapability(
  69. SpvCapabilityVulkanMemoryModel);
  70. CollectTargetsForVolatileSemantics(is_vk_memory_model_enabled);
  71. // If VulkanMemoryModel capability is not enabled, we have to set Volatile
  72. // decoration for interface variables instead of setting Volatile for load
  73. // instructions. If an interface (or pointers to it) is used by two load
  74. // instructions in two entry points and one must be volatile while another
  75. // is not, we have to report an error for the conflict.
  76. if (!is_vk_memory_model_enabled &&
  77. HasInterfaceInConflictOfVolatileSemantics()) {
  78. return Status::Failure;
  79. }
  80. return SpreadVolatileSemanticsToVariables(is_vk_memory_model_enabled);
  81. }
  82. Pass::Status SpreadVolatileSemantics::SpreadVolatileSemanticsToVariables(
  83. const bool is_vk_memory_model_enabled) {
  84. Status status = Status::SuccessWithoutChange;
  85. for (Instruction& var : context()->types_values()) {
  86. auto entry_function_ids =
  87. EntryFunctionsToSpreadVolatileSemanticsForVar(var.result_id());
  88. if (entry_function_ids.empty()) {
  89. continue;
  90. }
  91. if (is_vk_memory_model_enabled) {
  92. SetVolatileForLoadsInEntries(&var, entry_function_ids);
  93. } else {
  94. DecorateVarWithVolatile(&var);
  95. }
  96. status = Status::SuccessWithChange;
  97. }
  98. return status;
  99. }
  100. bool SpreadVolatileSemantics::IsTargetUsedByNonVolatileLoadInEntryPoint(
  101. uint32_t var_id, Instruction* entry_point) {
  102. uint32_t entry_function_id =
  103. entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint);
  104. std::unordered_set<uint32_t> funcs;
  105. context()->CollectCallTreeFromRoots(entry_function_id, &funcs);
  106. return !VisitLoadsOfPointersToVariableInEntries(
  107. var_id,
  108. [](Instruction* load) {
  109. // If it has a load without volatile memory operand, finish traversal
  110. // and return false.
  111. if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) {
  112. return false;
  113. }
  114. uint32_t memory_operands =
  115. load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
  116. return (memory_operands & SpvMemoryAccessVolatileMask) != 0;
  117. },
  118. funcs);
  119. }
  120. bool SpreadVolatileSemantics::HasInterfaceInConflictOfVolatileSemantics() {
  121. for (Instruction& entry_point : get_module()->entry_points()) {
  122. SpvExecutionModel execution_model =
  123. static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
  124. for (uint32_t operand_index = kOpEntryPointInOperandInterface;
  125. operand_index < entry_point.NumInOperands(); ++operand_index) {
  126. uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index);
  127. if (!EntryFunctionsToSpreadVolatileSemanticsForVar(var_id).empty() &&
  128. !IsTargetForVolatileSemantics(var_id, execution_model) &&
  129. IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) {
  130. Instruction* inst = context()->get_def_use_mgr()->GetDef(var_id);
  131. context()->EmitErrorMessage(
  132. "Variable is a target for Volatile semantics for an entry point, "
  133. "but it is not for another entry point",
  134. inst);
  135. return true;
  136. }
  137. }
  138. }
  139. return false;
  140. }
  141. void SpreadVolatileSemantics::MarkVolatileSemanticsForVariable(
  142. uint32_t var_id, Instruction* entry_point) {
  143. uint32_t entry_function_id =
  144. entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint);
  145. auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id);
  146. if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) {
  147. var_ids_to_entry_fn_for_volatile_semantics_[var_id] = {entry_function_id};
  148. return;
  149. }
  150. itr->second.insert(entry_function_id);
  151. }
  152. void SpreadVolatileSemantics::CollectTargetsForVolatileSemantics(
  153. const bool is_vk_memory_model_enabled) {
  154. for (Instruction& entry_point : get_module()->entry_points()) {
  155. SpvExecutionModel execution_model =
  156. static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
  157. for (uint32_t operand_index = kOpEntryPointInOperandInterface;
  158. operand_index < entry_point.NumInOperands(); ++operand_index) {
  159. uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index);
  160. if (!IsTargetForVolatileSemantics(var_id, execution_model)) {
  161. continue;
  162. }
  163. if (is_vk_memory_model_enabled ||
  164. IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) {
  165. MarkVolatileSemanticsForVariable(var_id, &entry_point);
  166. }
  167. }
  168. }
  169. }
  170. void SpreadVolatileSemantics::DecorateVarWithVolatile(Instruction* var) {
  171. analysis::DecorationManager* decoration_manager =
  172. context()->get_decoration_mgr();
  173. uint32_t var_id = var->result_id();
  174. if (HasVolatileDecoration(decoration_manager, var_id)) {
  175. return;
  176. }
  177. get_decoration_mgr()->AddDecoration(
  178. SpvOpDecorate, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}},
  179. {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
  180. {SpvDecorationVolatile}}});
  181. }
  182. bool SpreadVolatileSemantics::VisitLoadsOfPointersToVariableInEntries(
  183. uint32_t var_id, const std::function<bool(Instruction*)>& handle_load,
  184. const std::unordered_set<uint32_t>& function_ids) {
  185. std::vector<uint32_t> worklist({var_id});
  186. auto* def_use_mgr = context()->get_def_use_mgr();
  187. while (!worklist.empty()) {
  188. uint32_t ptr_id = worklist.back();
  189. worklist.pop_back();
  190. bool finish_traversal = !def_use_mgr->WhileEachUser(
  191. ptr_id, [this, &worklist, &ptr_id, handle_load,
  192. &function_ids](Instruction* user) {
  193. BasicBlock* block = context()->get_instr_block(user);
  194. if (block == nullptr ||
  195. function_ids.find(block->GetParent()->result_id()) ==
  196. function_ids.end()) {
  197. return true;
  198. }
  199. if (user->opcode() == SpvOpAccessChain ||
  200. user->opcode() == SpvOpInBoundsAccessChain ||
  201. user->opcode() == SpvOpPtrAccessChain ||
  202. user->opcode() == SpvOpInBoundsPtrAccessChain ||
  203. user->opcode() == SpvOpCopyObject) {
  204. if (ptr_id == user->GetSingleWordInOperand(0))
  205. worklist.push_back(user->result_id());
  206. return true;
  207. }
  208. if (user->opcode() != SpvOpLoad) {
  209. return true;
  210. }
  211. return handle_load(user);
  212. });
  213. if (finish_traversal) return false;
  214. }
  215. return true;
  216. }
  217. void SpreadVolatileSemantics::SetVolatileForLoadsInEntries(
  218. Instruction* var, const std::unordered_set<uint32_t>& entry_function_ids) {
  219. // Set Volatile memory operand for all load instructions if they do not have
  220. // it.
  221. for (auto entry_id : entry_function_ids) {
  222. std::unordered_set<uint32_t> funcs;
  223. context()->CollectCallTreeFromRoots(entry_id, &funcs);
  224. VisitLoadsOfPointersToVariableInEntries(
  225. var->result_id(),
  226. [](Instruction* load) {
  227. if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) {
  228. load->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS,
  229. {SpvMemoryAccessVolatileMask}});
  230. return true;
  231. }
  232. uint32_t memory_operands =
  233. load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
  234. memory_operands |= SpvMemoryAccessVolatileMask;
  235. load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands});
  236. return true;
  237. },
  238. funcs);
  239. }
  240. }
  241. bool SpreadVolatileSemantics::IsTargetForVolatileSemantics(
  242. uint32_t var_id, SpvExecutionModel execution_model) {
  243. analysis::DecorationManager* decoration_manager =
  244. context()->get_decoration_mgr();
  245. if (execution_model == SpvExecutionModelFragment) {
  246. return get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 6) &&
  247. HasBuiltinDecoration(decoration_manager, var_id,
  248. SpvBuiltInHelperInvocation);
  249. }
  250. if (execution_model == SpvExecutionModelIntersectionKHR ||
  251. execution_model == SpvExecutionModelIntersectionNV) {
  252. if (HasBuiltinDecoration(decoration_manager, var_id,
  253. SpvBuiltInRayTmaxKHR)) {
  254. return true;
  255. }
  256. }
  257. switch (execution_model) {
  258. case SpvExecutionModelRayGenerationKHR:
  259. case SpvExecutionModelClosestHitKHR:
  260. case SpvExecutionModelMissKHR:
  261. case SpvExecutionModelCallableKHR:
  262. case SpvExecutionModelIntersectionKHR:
  263. return HasBuiltinForRayTracingVolatileSemantics(decoration_manager,
  264. var_id);
  265. default:
  266. return false;
  267. }
  268. }
  269. } // namespace opt
  270. } // namespace spvtools