spread_volatile_semantics.cpp 11 KB

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