spread_volatile_semantics.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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. constexpr uint32_t kOpDecorateInOperandBuiltinDecoration = 2u;
  22. constexpr uint32_t kOpLoadInOperandMemoryOperands = 1u;
  23. constexpr uint32_t kOpEntryPointInOperandEntryPoint = 1u;
  24. constexpr 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, uint32_t(spv::Decoration::BuiltIn),
  29. [built_in](const Instruction& inst) {
  30. return built_in == inst.GetSingleWordInOperand(
  31. kOpDecorateInOperandBuiltinDecoration);
  32. });
  33. }
  34. bool IsBuiltInForRayTracingVolatileSemantics(spv::BuiltIn built_in) {
  35. switch (built_in) {
  36. case spv::BuiltIn::SMIDNV:
  37. case spv::BuiltIn::WarpIDNV:
  38. case spv::BuiltIn::SubgroupSize:
  39. case spv::BuiltIn::SubgroupLocalInvocationId:
  40. case spv::BuiltIn::SubgroupEqMask:
  41. case spv::BuiltIn::SubgroupGeMask:
  42. case spv::BuiltIn::SubgroupGtMask:
  43. case spv::BuiltIn::SubgroupLeMask:
  44. case spv::BuiltIn::SubgroupLtMask:
  45. return true;
  46. default:
  47. return false;
  48. }
  49. }
  50. bool HasBuiltinForRayTracingVolatileSemantics(
  51. analysis::DecorationManager* decoration_manager, uint32_t var_id) {
  52. return decoration_manager->FindDecoration(
  53. var_id, uint32_t(spv::Decoration::BuiltIn), [](const Instruction& inst) {
  54. spv::BuiltIn built_in = spv::BuiltIn(
  55. inst.GetSingleWordInOperand(kOpDecorateInOperandBuiltinDecoration));
  56. return IsBuiltInForRayTracingVolatileSemantics(built_in);
  57. });
  58. }
  59. bool HasVolatileDecoration(analysis::DecorationManager* decoration_manager,
  60. uint32_t var_id) {
  61. return decoration_manager->HasDecoration(var_id,
  62. uint32_t(spv::Decoration::Volatile));
  63. }
  64. } // namespace
  65. Pass::Status SpreadVolatileSemantics::Process() {
  66. if (HasNoExecutionModel()) {
  67. return Status::SuccessWithoutChange;
  68. }
  69. const bool is_vk_memory_model_enabled =
  70. context()->get_feature_mgr()->HasCapability(
  71. spv::Capability::VulkanMemoryModel);
  72. CollectTargetsForVolatileSemantics(is_vk_memory_model_enabled);
  73. // If VulkanMemoryModel capability is not enabled, we have to set Volatile
  74. // decoration for interface variables instead of setting Volatile for load
  75. // instructions. If an interface (or pointers to it) is used by two load
  76. // instructions in two entry points and one must be volatile while another
  77. // is not, we have to report an error for the conflict.
  78. if (!is_vk_memory_model_enabled &&
  79. HasInterfaceInConflictOfVolatileSemantics()) {
  80. return Status::Failure;
  81. }
  82. return SpreadVolatileSemanticsToVariables(is_vk_memory_model_enabled);
  83. }
  84. Pass::Status SpreadVolatileSemantics::SpreadVolatileSemanticsToVariables(
  85. const bool is_vk_memory_model_enabled) {
  86. Status status = Status::SuccessWithoutChange;
  87. for (Instruction& var : context()->types_values()) {
  88. auto entry_function_ids =
  89. EntryFunctionsToSpreadVolatileSemanticsForVar(var.result_id());
  90. if (entry_function_ids.empty()) {
  91. continue;
  92. }
  93. if (is_vk_memory_model_enabled) {
  94. SetVolatileForLoadsInEntries(&var, entry_function_ids);
  95. } else {
  96. DecorateVarWithVolatile(&var);
  97. }
  98. status = Status::SuccessWithChange;
  99. }
  100. return status;
  101. }
  102. bool SpreadVolatileSemantics::IsTargetUsedByNonVolatileLoadInEntryPoint(
  103. uint32_t var_id, Instruction* entry_point) {
  104. uint32_t entry_function_id =
  105. entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint);
  106. std::unordered_set<uint32_t> funcs;
  107. context()->CollectCallTreeFromRoots(entry_function_id, &funcs);
  108. return !VisitLoadsOfPointersToVariableInEntries(
  109. var_id,
  110. [](Instruction* load) {
  111. // If it has a load without volatile memory operand, finish traversal
  112. // and return false.
  113. if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) {
  114. return false;
  115. }
  116. uint32_t memory_operands =
  117. load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
  118. return (memory_operands & uint32_t(spv::MemoryAccessMask::Volatile)) !=
  119. 0;
  120. },
  121. funcs);
  122. }
  123. bool SpreadVolatileSemantics::HasInterfaceInConflictOfVolatileSemantics() {
  124. for (Instruction& entry_point : get_module()->entry_points()) {
  125. spv::ExecutionModel execution_model =
  126. static_cast<spv::ExecutionModel>(entry_point.GetSingleWordInOperand(0));
  127. for (uint32_t operand_index = kOpEntryPointInOperandInterface;
  128. operand_index < entry_point.NumInOperands(); ++operand_index) {
  129. uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index);
  130. if (!EntryFunctionsToSpreadVolatileSemanticsForVar(var_id).empty() &&
  131. !IsTargetForVolatileSemantics(var_id, execution_model) &&
  132. IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) {
  133. Instruction* inst = context()->get_def_use_mgr()->GetDef(var_id);
  134. context()->EmitErrorMessage(
  135. "Variable is a target for Volatile semantics for an entry point, "
  136. "but it is not for another entry point",
  137. inst);
  138. return true;
  139. }
  140. }
  141. }
  142. return false;
  143. }
  144. void SpreadVolatileSemantics::MarkVolatileSemanticsForVariable(
  145. uint32_t var_id, Instruction* entry_point) {
  146. uint32_t entry_function_id =
  147. entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint);
  148. auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id);
  149. if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) {
  150. var_ids_to_entry_fn_for_volatile_semantics_[var_id] = {entry_function_id};
  151. return;
  152. }
  153. itr->second.insert(entry_function_id);
  154. }
  155. void SpreadVolatileSemantics::CollectTargetsForVolatileSemantics(
  156. const bool is_vk_memory_model_enabled) {
  157. for (Instruction& entry_point : get_module()->entry_points()) {
  158. spv::ExecutionModel execution_model =
  159. static_cast<spv::ExecutionModel>(entry_point.GetSingleWordInOperand(0));
  160. for (uint32_t operand_index = kOpEntryPointInOperandInterface;
  161. operand_index < entry_point.NumInOperands(); ++operand_index) {
  162. uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index);
  163. if (!IsTargetForVolatileSemantics(var_id, execution_model)) {
  164. continue;
  165. }
  166. if (is_vk_memory_model_enabled ||
  167. IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) {
  168. MarkVolatileSemanticsForVariable(var_id, &entry_point);
  169. }
  170. }
  171. }
  172. }
  173. void SpreadVolatileSemantics::DecorateVarWithVolatile(Instruction* var) {
  174. analysis::DecorationManager* decoration_manager =
  175. context()->get_decoration_mgr();
  176. uint32_t var_id = var->result_id();
  177. if (HasVolatileDecoration(decoration_manager, var_id)) {
  178. return;
  179. }
  180. get_decoration_mgr()->AddDecoration(
  181. spv::Op::OpDecorate,
  182. {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}},
  183. {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
  184. {uint32_t(spv::Decoration::Volatile)}}});
  185. }
  186. bool SpreadVolatileSemantics::VisitLoadsOfPointersToVariableInEntries(
  187. uint32_t var_id, const std::function<bool(Instruction*)>& handle_load,
  188. const std::unordered_set<uint32_t>& function_ids) {
  189. std::vector<uint32_t> worklist({var_id});
  190. auto* def_use_mgr = context()->get_def_use_mgr();
  191. while (!worklist.empty()) {
  192. uint32_t ptr_id = worklist.back();
  193. worklist.pop_back();
  194. bool finish_traversal = !def_use_mgr->WhileEachUser(
  195. ptr_id, [this, &worklist, &ptr_id, handle_load,
  196. &function_ids](Instruction* user) {
  197. BasicBlock* block = context()->get_instr_block(user);
  198. if (block == nullptr ||
  199. function_ids.find(block->GetParent()->result_id()) ==
  200. function_ids.end()) {
  201. return true;
  202. }
  203. if (user->opcode() == spv::Op::OpAccessChain ||
  204. user->opcode() == spv::Op::OpInBoundsAccessChain ||
  205. user->opcode() == spv::Op::OpPtrAccessChain ||
  206. user->opcode() == spv::Op::OpInBoundsPtrAccessChain ||
  207. user->opcode() == spv::Op::OpCopyObject) {
  208. if (ptr_id == user->GetSingleWordInOperand(0))
  209. worklist.push_back(user->result_id());
  210. return true;
  211. }
  212. if (user->opcode() != spv::Op::OpLoad) {
  213. return true;
  214. }
  215. return handle_load(user);
  216. });
  217. if (finish_traversal) return false;
  218. }
  219. return true;
  220. }
  221. void SpreadVolatileSemantics::SetVolatileForLoadsInEntries(
  222. Instruction* var, const std::unordered_set<uint32_t>& entry_function_ids) {
  223. // Set Volatile memory operand for all load instructions if they do not have
  224. // it.
  225. for (auto entry_id : entry_function_ids) {
  226. std::unordered_set<uint32_t> funcs;
  227. context()->CollectCallTreeFromRoots(entry_id, &funcs);
  228. VisitLoadsOfPointersToVariableInEntries(
  229. var->result_id(),
  230. [](Instruction* load) {
  231. if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) {
  232. load->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS,
  233. {uint32_t(spv::MemoryAccessMask::Volatile)}});
  234. return true;
  235. }
  236. uint32_t memory_operands =
  237. load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
  238. memory_operands |= uint32_t(spv::MemoryAccessMask::Volatile);
  239. load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands});
  240. return true;
  241. },
  242. funcs);
  243. }
  244. }
  245. bool SpreadVolatileSemantics::IsTargetForVolatileSemantics(
  246. uint32_t var_id, spv::ExecutionModel execution_model) {
  247. analysis::DecorationManager* decoration_manager =
  248. context()->get_decoration_mgr();
  249. if (execution_model == spv::ExecutionModel::Fragment) {
  250. return get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 6) &&
  251. HasBuiltinDecoration(decoration_manager, var_id,
  252. uint32_t(spv::BuiltIn::HelperInvocation));
  253. }
  254. if (execution_model == spv::ExecutionModel::IntersectionKHR ||
  255. execution_model == spv::ExecutionModel::IntersectionNV) {
  256. if (HasBuiltinDecoration(decoration_manager, var_id,
  257. uint32_t(spv::BuiltIn::RayTmaxKHR))) {
  258. return true;
  259. }
  260. }
  261. switch (execution_model) {
  262. case spv::ExecutionModel::RayGenerationKHR:
  263. case spv::ExecutionModel::ClosestHitKHR:
  264. case spv::ExecutionModel::MissKHR:
  265. case spv::ExecutionModel::CallableKHR:
  266. case spv::ExecutionModel::IntersectionKHR:
  267. return HasBuiltinForRayTracingVolatileSemantics(decoration_manager,
  268. var_id);
  269. default:
  270. return false;
  271. }
  272. }
  273. } // namespace opt
  274. } // namespace spvtools