upgrade_memory_model.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. // Copyright (c) 2018 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 "upgrade_memory_model.h"
  15. #include <utility>
  16. #include "source/opt/ir_builder.h"
  17. #include "source/opt/ir_context.h"
  18. #include "source/util/make_unique.h"
  19. namespace spvtools {
  20. namespace opt {
  21. Pass::Status UpgradeMemoryModel::Process() {
  22. // Only update Logical GLSL450 to Logical VulkanKHR.
  23. Instruction* memory_model = get_module()->GetMemoryModel();
  24. if (memory_model->GetSingleWordInOperand(0u) != SpvAddressingModelLogical ||
  25. memory_model->GetSingleWordInOperand(1u) != SpvMemoryModelGLSL450) {
  26. return Pass::Status::SuccessWithoutChange;
  27. }
  28. UpgradeMemoryModelInstruction();
  29. UpgradeInstructions();
  30. CleanupDecorations();
  31. UpgradeBarriers();
  32. UpgradeMemoryScope();
  33. return Pass::Status::SuccessWithChange;
  34. }
  35. void UpgradeMemoryModel::UpgradeMemoryModelInstruction() {
  36. // Overall changes necessary:
  37. // 1. Add the OpExtension.
  38. // 2. Add the OpCapability.
  39. // 3. Modify the memory model.
  40. Instruction* memory_model = get_module()->GetMemoryModel();
  41. get_module()->AddCapability(MakeUnique<Instruction>(
  42. context(), SpvOpCapability, 0, 0,
  43. std::initializer_list<Operand>{
  44. {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityVulkanMemoryModelKHR}}}));
  45. const std::string extension = "SPV_KHR_vulkan_memory_model";
  46. std::vector<uint32_t> words(extension.size() / 4 + 1, 0);
  47. char* dst = reinterpret_cast<char*>(words.data());
  48. strncpy(dst, extension.c_str(), extension.size());
  49. get_module()->AddExtension(
  50. MakeUnique<Instruction>(context(), SpvOpExtension, 0, 0,
  51. std::initializer_list<Operand>{
  52. {SPV_OPERAND_TYPE_LITERAL_STRING, words}}));
  53. memory_model->SetInOperand(1u, {SpvMemoryModelVulkanKHR});
  54. }
  55. void UpgradeMemoryModel::UpgradeInstructions() {
  56. // Coherent and Volatile decorations are deprecated. Remove them and replace
  57. // with flags on the memory/image operations. The decorations can occur on
  58. // OpVariable, OpFunctionParameter (of pointer type) and OpStructType (member
  59. // decoration). Trace from the decoration target(s) to the final memory/image
  60. // instructions. Additionally, Workgroup storage class variables and function
  61. // parameters are implicitly coherent in GLSL450.
  62. // Upgrade modf and frexp first since they generate new stores.
  63. for (auto& func : *get_module()) {
  64. func.ForEachInst([this](Instruction* inst) {
  65. if (inst->opcode() == SpvOpExtInst) {
  66. auto ext_inst = inst->GetSingleWordInOperand(1u);
  67. if (ext_inst == GLSLstd450Modf || ext_inst == GLSLstd450Frexp) {
  68. auto import =
  69. get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u));
  70. if (reinterpret_cast<char*>(import->GetInOperand(0u).words.data()) ==
  71. std::string("GLSL.std.450")) {
  72. UpgradeExtInst(inst);
  73. }
  74. }
  75. }
  76. });
  77. }
  78. for (auto& func : *get_module()) {
  79. func.ForEachInst([this](Instruction* inst) {
  80. bool is_coherent = false;
  81. bool is_volatile = false;
  82. bool src_coherent = false;
  83. bool src_volatile = false;
  84. bool dst_coherent = false;
  85. bool dst_volatile = false;
  86. SpvScope scope = SpvScopeQueueFamilyKHR;
  87. SpvScope src_scope = SpvScopeQueueFamilyKHR;
  88. SpvScope dst_scope = SpvScopeQueueFamilyKHR;
  89. switch (inst->opcode()) {
  90. case SpvOpLoad:
  91. case SpvOpStore:
  92. std::tie(is_coherent, is_volatile, scope) =
  93. GetInstructionAttributes(inst->GetSingleWordInOperand(0u));
  94. break;
  95. case SpvOpImageRead:
  96. case SpvOpImageSparseRead:
  97. case SpvOpImageWrite:
  98. std::tie(is_coherent, is_volatile, scope) =
  99. GetInstructionAttributes(inst->GetSingleWordInOperand(0u));
  100. break;
  101. case SpvOpCopyMemory:
  102. case SpvOpCopyMemorySized:
  103. std::tie(dst_coherent, dst_volatile, dst_scope) =
  104. GetInstructionAttributes(inst->GetSingleWordInOperand(0u));
  105. std::tie(src_coherent, src_volatile, src_scope) =
  106. GetInstructionAttributes(inst->GetSingleWordInOperand(1u));
  107. break;
  108. default:
  109. break;
  110. }
  111. switch (inst->opcode()) {
  112. case SpvOpLoad:
  113. UpgradeFlags(inst, 1u, is_coherent, is_volatile, kVisibility,
  114. kMemory);
  115. break;
  116. case SpvOpStore:
  117. UpgradeFlags(inst, 2u, is_coherent, is_volatile, kAvailability,
  118. kMemory);
  119. break;
  120. case SpvOpCopyMemory:
  121. UpgradeFlags(inst, 2u, dst_coherent, dst_volatile, kAvailability,
  122. kMemory);
  123. UpgradeFlags(inst, 2u, src_coherent, src_volatile, kVisibility,
  124. kMemory);
  125. break;
  126. case SpvOpCopyMemorySized:
  127. UpgradeFlags(inst, 3u, dst_coherent, dst_volatile, kAvailability,
  128. kMemory);
  129. UpgradeFlags(inst, 3u, src_coherent, src_volatile, kVisibility,
  130. kMemory);
  131. break;
  132. case SpvOpImageRead:
  133. case SpvOpImageSparseRead:
  134. UpgradeFlags(inst, 2u, is_coherent, is_volatile, kVisibility, kImage);
  135. break;
  136. case SpvOpImageWrite:
  137. UpgradeFlags(inst, 3u, is_coherent, is_volatile, kAvailability,
  138. kImage);
  139. break;
  140. default:
  141. break;
  142. }
  143. // |is_coherent| is never used for the same instructions as
  144. // |src_coherent| and |dst_coherent|.
  145. if (is_coherent) {
  146. inst->AddOperand(
  147. {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(scope)}});
  148. }
  149. // According to SPV_KHR_vulkan_memory_model, if both available and
  150. // visible flags are used the first scope operand is for availability
  151. // (writes) and the second is for visibility (reads).
  152. if (dst_coherent) {
  153. inst->AddOperand(
  154. {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(dst_scope)}});
  155. }
  156. if (src_coherent) {
  157. inst->AddOperand(
  158. {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(src_scope)}});
  159. }
  160. });
  161. }
  162. }
  163. std::tuple<bool, bool, SpvScope> UpgradeMemoryModel::GetInstructionAttributes(
  164. uint32_t id) {
  165. // |id| is a pointer used in a memory/image instruction. Need to determine if
  166. // that pointer points to volatile or coherent memory. Workgroup storage
  167. // class is implicitly coherent and cannot be decorated with volatile, so
  168. // short circuit that case.
  169. Instruction* inst = context()->get_def_use_mgr()->GetDef(id);
  170. analysis::Type* type = context()->get_type_mgr()->GetType(inst->type_id());
  171. if (type->AsPointer() &&
  172. type->AsPointer()->storage_class() == SpvStorageClassWorkgroup) {
  173. return std::make_tuple(true, false, SpvScopeWorkgroup);
  174. }
  175. bool is_coherent = false;
  176. bool is_volatile = false;
  177. std::unordered_set<uint32_t> visited;
  178. std::tie(is_coherent, is_volatile) =
  179. TraceInstruction(context()->get_def_use_mgr()->GetDef(id),
  180. std::vector<uint32_t>(), &visited);
  181. return std::make_tuple(is_coherent, is_volatile, SpvScopeQueueFamilyKHR);
  182. }
  183. std::pair<bool, bool> UpgradeMemoryModel::TraceInstruction(
  184. Instruction* inst, std::vector<uint32_t> indices,
  185. std::unordered_set<uint32_t>* visited) {
  186. auto iter = cache_.find(std::make_pair(inst->result_id(), indices));
  187. if (iter != cache_.end()) {
  188. return iter->second;
  189. }
  190. if (!visited->insert(inst->result_id()).second) {
  191. return std::make_pair(false, false);
  192. }
  193. // Initialize the cache before |indices| is (potentially) modified.
  194. auto& cached_result = cache_[std::make_pair(inst->result_id(), indices)];
  195. cached_result.first = false;
  196. cached_result.second = false;
  197. bool is_coherent = false;
  198. bool is_volatile = false;
  199. switch (inst->opcode()) {
  200. case SpvOpVariable:
  201. case SpvOpFunctionParameter:
  202. is_coherent |= HasDecoration(inst, 0, SpvDecorationCoherent);
  203. is_volatile |= HasDecoration(inst, 0, SpvDecorationVolatile);
  204. if (!is_coherent || !is_volatile) {
  205. bool type_coherent = false;
  206. bool type_volatile = false;
  207. std::tie(type_coherent, type_volatile) =
  208. CheckType(inst->type_id(), indices);
  209. is_coherent |= type_coherent;
  210. is_volatile |= type_volatile;
  211. }
  212. break;
  213. case SpvOpAccessChain:
  214. case SpvOpInBoundsAccessChain:
  215. // Store indices in reverse order.
  216. for (uint32_t i = inst->NumInOperands() - 1; i > 0; --i) {
  217. indices.push_back(inst->GetSingleWordInOperand(i));
  218. }
  219. break;
  220. case SpvOpPtrAccessChain:
  221. // Store indices in reverse order. Skip the |Element| operand.
  222. for (uint32_t i = inst->NumInOperands() - 1; i > 1; --i) {
  223. indices.push_back(inst->GetSingleWordInOperand(i));
  224. }
  225. break;
  226. default:
  227. break;
  228. }
  229. // No point searching further.
  230. if (is_coherent && is_volatile) {
  231. cached_result.first = true;
  232. cached_result.second = true;
  233. return std::make_pair(true, true);
  234. }
  235. // Variables and function parameters are sources. Continue searching until we
  236. // reach them.
  237. if (inst->opcode() != SpvOpVariable &&
  238. inst->opcode() != SpvOpFunctionParameter) {
  239. inst->ForEachInId([this, &is_coherent, &is_volatile, &indices,
  240. &visited](const uint32_t* id_ptr) {
  241. Instruction* op_inst = context()->get_def_use_mgr()->GetDef(*id_ptr);
  242. const analysis::Type* type =
  243. context()->get_type_mgr()->GetType(op_inst->type_id());
  244. if (type &&
  245. (type->AsPointer() || type->AsImage() || type->AsSampledImage())) {
  246. bool operand_coherent = false;
  247. bool operand_volatile = false;
  248. std::tie(operand_coherent, operand_volatile) =
  249. TraceInstruction(op_inst, indices, visited);
  250. is_coherent |= operand_coherent;
  251. is_volatile |= operand_volatile;
  252. }
  253. });
  254. }
  255. cached_result.first = is_coherent;
  256. cached_result.second = is_volatile;
  257. return std::make_pair(is_coherent, is_volatile);
  258. }
  259. std::pair<bool, bool> UpgradeMemoryModel::CheckType(
  260. uint32_t type_id, const std::vector<uint32_t>& indices) {
  261. bool is_coherent = false;
  262. bool is_volatile = false;
  263. Instruction* type_inst = context()->get_def_use_mgr()->GetDef(type_id);
  264. assert(type_inst->opcode() == SpvOpTypePointer);
  265. Instruction* element_inst = context()->get_def_use_mgr()->GetDef(
  266. type_inst->GetSingleWordInOperand(1u));
  267. for (int i = (int)indices.size() - 1; i >= 0; --i) {
  268. if (is_coherent && is_volatile) break;
  269. if (element_inst->opcode() == SpvOpTypePointer) {
  270. element_inst = context()->get_def_use_mgr()->GetDef(
  271. element_inst->GetSingleWordInOperand(1u));
  272. } else if (element_inst->opcode() == SpvOpTypeStruct) {
  273. uint32_t index = indices.at(i);
  274. Instruction* index_inst = context()->get_def_use_mgr()->GetDef(index);
  275. assert(index_inst->opcode() == SpvOpConstant);
  276. uint64_t value = GetIndexValue(index_inst);
  277. is_coherent |= HasDecoration(element_inst, static_cast<uint32_t>(value),
  278. SpvDecorationCoherent);
  279. is_volatile |= HasDecoration(element_inst, static_cast<uint32_t>(value),
  280. SpvDecorationVolatile);
  281. element_inst = context()->get_def_use_mgr()->GetDef(
  282. element_inst->GetSingleWordInOperand(static_cast<uint32_t>(value)));
  283. } else {
  284. assert(spvOpcodeIsComposite(element_inst->opcode()));
  285. element_inst = context()->get_def_use_mgr()->GetDef(
  286. element_inst->GetSingleWordInOperand(1u));
  287. }
  288. }
  289. if (!is_coherent || !is_volatile) {
  290. bool remaining_coherent = false;
  291. bool remaining_volatile = false;
  292. std::tie(remaining_coherent, remaining_volatile) =
  293. CheckAllTypes(element_inst);
  294. is_coherent |= remaining_coherent;
  295. is_volatile |= remaining_volatile;
  296. }
  297. return std::make_pair(is_coherent, is_volatile);
  298. }
  299. std::pair<bool, bool> UpgradeMemoryModel::CheckAllTypes(
  300. const Instruction* inst) {
  301. std::unordered_set<const Instruction*> visited;
  302. std::vector<const Instruction*> stack;
  303. stack.push_back(inst);
  304. bool is_coherent = false;
  305. bool is_volatile = false;
  306. while (!stack.empty()) {
  307. const Instruction* def = stack.back();
  308. stack.pop_back();
  309. if (!visited.insert(def).second) continue;
  310. if (def->opcode() == SpvOpTypeStruct) {
  311. // Any member decorated with coherent and/or volatile is enough to have
  312. // the related operation be flagged as coherent and/or volatile.
  313. is_coherent |= HasDecoration(def, std::numeric_limits<uint32_t>::max(),
  314. SpvDecorationCoherent);
  315. is_volatile |= HasDecoration(def, std::numeric_limits<uint32_t>::max(),
  316. SpvDecorationVolatile);
  317. if (is_coherent && is_volatile)
  318. return std::make_pair(is_coherent, is_volatile);
  319. // Check the subtypes.
  320. for (uint32_t i = 0; i < def->NumInOperands(); ++i) {
  321. stack.push_back(context()->get_def_use_mgr()->GetDef(
  322. def->GetSingleWordInOperand(i)));
  323. }
  324. } else if (spvOpcodeIsComposite(def->opcode())) {
  325. stack.push_back(context()->get_def_use_mgr()->GetDef(
  326. def->GetSingleWordInOperand(0u)));
  327. } else if (def->opcode() == SpvOpTypePointer) {
  328. stack.push_back(context()->get_def_use_mgr()->GetDef(
  329. def->GetSingleWordInOperand(1u)));
  330. }
  331. }
  332. return std::make_pair(is_coherent, is_volatile);
  333. }
  334. uint64_t UpgradeMemoryModel::GetIndexValue(Instruction* index_inst) {
  335. const analysis::Constant* index_constant =
  336. context()->get_constant_mgr()->GetConstantFromInst(index_inst);
  337. assert(index_constant->AsIntConstant());
  338. if (index_constant->type()->AsInteger()->IsSigned()) {
  339. if (index_constant->type()->AsInteger()->width() == 32) {
  340. return index_constant->GetS32();
  341. } else {
  342. return index_constant->GetS64();
  343. }
  344. } else {
  345. if (index_constant->type()->AsInteger()->width() == 32) {
  346. return index_constant->GetU32();
  347. } else {
  348. return index_constant->GetU64();
  349. }
  350. }
  351. }
  352. bool UpgradeMemoryModel::HasDecoration(const Instruction* inst, uint32_t value,
  353. SpvDecoration decoration) {
  354. // If the iteration was terminated early then an appropriate decoration was
  355. // found.
  356. return !context()->get_decoration_mgr()->WhileEachDecoration(
  357. inst->result_id(), decoration, [value](const Instruction& i) {
  358. if (i.opcode() == SpvOpDecorate || i.opcode() == SpvOpDecorateId) {
  359. return false;
  360. } else if (i.opcode() == SpvOpMemberDecorate) {
  361. if (value == i.GetSingleWordInOperand(1u) ||
  362. value == std::numeric_limits<uint32_t>::max())
  363. return false;
  364. }
  365. return true;
  366. });
  367. }
  368. void UpgradeMemoryModel::UpgradeFlags(Instruction* inst, uint32_t in_operand,
  369. bool is_coherent, bool is_volatile,
  370. OperationType operation_type,
  371. InstructionType inst_type) {
  372. if (!is_coherent && !is_volatile) return;
  373. uint32_t flags = 0;
  374. if (inst->NumInOperands() > in_operand) {
  375. flags |= inst->GetSingleWordInOperand(in_operand);
  376. }
  377. if (is_coherent) {
  378. if (inst_type == kMemory) {
  379. flags |= SpvMemoryAccessNonPrivatePointerKHRMask;
  380. if (operation_type == kVisibility) {
  381. flags |= SpvMemoryAccessMakePointerVisibleKHRMask;
  382. } else {
  383. flags |= SpvMemoryAccessMakePointerAvailableKHRMask;
  384. }
  385. } else {
  386. flags |= SpvImageOperandsNonPrivateTexelKHRMask;
  387. if (operation_type == kVisibility) {
  388. flags |= SpvImageOperandsMakeTexelVisibleKHRMask;
  389. } else {
  390. flags |= SpvImageOperandsMakeTexelAvailableKHRMask;
  391. }
  392. }
  393. }
  394. if (is_volatile) {
  395. if (inst_type == kMemory) {
  396. flags |= SpvMemoryAccessVolatileMask;
  397. } else {
  398. flags |= SpvImageOperandsVolatileTexelKHRMask;
  399. }
  400. }
  401. if (inst->NumInOperands() > in_operand) {
  402. inst->SetInOperand(in_operand, {flags});
  403. } else if (inst_type == kMemory) {
  404. inst->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, {flags}});
  405. } else {
  406. inst->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_IMAGE, {flags}});
  407. }
  408. }
  409. uint32_t UpgradeMemoryModel::GetScopeConstant(SpvScope scope) {
  410. analysis::Integer int_ty(32, false);
  411. uint32_t int_id = context()->get_type_mgr()->GetTypeInstruction(&int_ty);
  412. const analysis::Constant* constant =
  413. context()->get_constant_mgr()->GetConstant(
  414. context()->get_type_mgr()->GetType(int_id),
  415. {static_cast<uint32_t>(scope)});
  416. return context()
  417. ->get_constant_mgr()
  418. ->GetDefiningInstruction(constant)
  419. ->result_id();
  420. }
  421. void UpgradeMemoryModel::CleanupDecorations() {
  422. // All of the volatile and coherent decorations have been dealt with, so now
  423. // we can just remove them.
  424. get_module()->ForEachInst([this](Instruction* inst) {
  425. if (inst->result_id() != 0) {
  426. context()->get_decoration_mgr()->RemoveDecorationsFrom(
  427. inst->result_id(), [](const Instruction& dec) {
  428. switch (dec.opcode()) {
  429. case SpvOpDecorate:
  430. case SpvOpDecorateId:
  431. if (dec.GetSingleWordInOperand(1u) == SpvDecorationCoherent ||
  432. dec.GetSingleWordInOperand(1u) == SpvDecorationVolatile)
  433. return true;
  434. break;
  435. case SpvOpMemberDecorate:
  436. if (dec.GetSingleWordInOperand(2u) == SpvDecorationCoherent ||
  437. dec.GetSingleWordInOperand(2u) == SpvDecorationVolatile)
  438. return true;
  439. break;
  440. default:
  441. break;
  442. }
  443. return false;
  444. });
  445. }
  446. });
  447. }
  448. void UpgradeMemoryModel::UpgradeBarriers() {
  449. std::vector<Instruction*> barriers;
  450. // Collects all the control barriers in |function|. Returns true if the
  451. // function operates on the Output storage class.
  452. ProcessFunction CollectBarriers = [this, &barriers](Function* function) {
  453. bool operates_on_output = false;
  454. for (auto& block : *function) {
  455. block.ForEachInst([this, &barriers,
  456. &operates_on_output](Instruction* inst) {
  457. if (inst->opcode() == SpvOpControlBarrier) {
  458. barriers.push_back(inst);
  459. } else if (!operates_on_output) {
  460. // This instruction operates on output storage class if it is a
  461. // pointer to output type or any input operand is a pointer to output
  462. // type.
  463. analysis::Type* type =
  464. context()->get_type_mgr()->GetType(inst->type_id());
  465. if (type && type->AsPointer() &&
  466. type->AsPointer()->storage_class() == SpvStorageClassOutput) {
  467. operates_on_output = true;
  468. return;
  469. }
  470. inst->ForEachInId([this, &operates_on_output](uint32_t* id_ptr) {
  471. Instruction* op_inst =
  472. context()->get_def_use_mgr()->GetDef(*id_ptr);
  473. analysis::Type* op_type =
  474. context()->get_type_mgr()->GetType(op_inst->type_id());
  475. if (op_type && op_type->AsPointer() &&
  476. op_type->AsPointer()->storage_class() == SpvStorageClassOutput)
  477. operates_on_output = true;
  478. });
  479. }
  480. });
  481. }
  482. return operates_on_output;
  483. };
  484. std::queue<uint32_t> roots;
  485. for (auto& e : get_module()->entry_points())
  486. if (e.GetSingleWordInOperand(0u) == SpvExecutionModelTessellationControl) {
  487. roots.push(e.GetSingleWordInOperand(1u));
  488. if (context()->ProcessCallTreeFromRoots(CollectBarriers, &roots)) {
  489. for (auto barrier : barriers) {
  490. // Add OutputMemoryKHR to the semantics of the barriers.
  491. uint32_t semantics_id = barrier->GetSingleWordInOperand(2u);
  492. Instruction* semantics_inst =
  493. context()->get_def_use_mgr()->GetDef(semantics_id);
  494. analysis::Type* semantics_type =
  495. context()->get_type_mgr()->GetType(semantics_inst->type_id());
  496. uint64_t semantics_value = GetIndexValue(semantics_inst);
  497. const analysis::Constant* constant =
  498. context()->get_constant_mgr()->GetConstant(
  499. semantics_type, {static_cast<uint32_t>(semantics_value) |
  500. SpvMemorySemanticsOutputMemoryKHRMask});
  501. barrier->SetInOperand(2u, {context()
  502. ->get_constant_mgr()
  503. ->GetDefiningInstruction(constant)
  504. ->result_id()});
  505. }
  506. }
  507. barriers.clear();
  508. }
  509. }
  510. void UpgradeMemoryModel::UpgradeMemoryScope() {
  511. get_module()->ForEachInst([this](Instruction* inst) {
  512. // Don't need to handle all the operations that take a scope.
  513. // * Group operations can only be subgroup
  514. // * Non-uniform can only be workgroup or subgroup
  515. // * Named barriers are not supported by Vulkan
  516. // * Workgroup ops (e.g. async_copy) have at most workgroup scope.
  517. if (spvOpcodeIsAtomicOp(inst->opcode())) {
  518. if (IsDeviceScope(inst->GetSingleWordInOperand(1))) {
  519. inst->SetInOperand(1, {GetScopeConstant(SpvScopeQueueFamilyKHR)});
  520. }
  521. } else if (inst->opcode() == SpvOpControlBarrier) {
  522. if (IsDeviceScope(inst->GetSingleWordInOperand(1))) {
  523. inst->SetInOperand(1, {GetScopeConstant(SpvScopeQueueFamilyKHR)});
  524. }
  525. } else if (inst->opcode() == SpvOpMemoryBarrier) {
  526. if (IsDeviceScope(inst->GetSingleWordInOperand(0))) {
  527. inst->SetInOperand(0, {GetScopeConstant(SpvScopeQueueFamilyKHR)});
  528. }
  529. }
  530. });
  531. }
  532. bool UpgradeMemoryModel::IsDeviceScope(uint32_t scope_id) {
  533. const analysis::Constant* constant =
  534. context()->get_constant_mgr()->FindDeclaredConstant(scope_id);
  535. assert(constant && "Memory scope must be a constant");
  536. const analysis::Integer* type = constant->type()->AsInteger();
  537. assert(type);
  538. assert(type->width() == 32 || type->width() == 64);
  539. if (type->width() == 32) {
  540. if (type->IsSigned())
  541. return static_cast<uint32_t>(constant->GetS32()) == SpvScopeDevice;
  542. else
  543. return static_cast<uint32_t>(constant->GetU32()) == SpvScopeDevice;
  544. } else {
  545. if (type->IsSigned())
  546. return static_cast<uint32_t>(constant->GetS64()) == SpvScopeDevice;
  547. else
  548. return static_cast<uint32_t>(constant->GetU64()) == SpvScopeDevice;
  549. }
  550. assert(false);
  551. return false;
  552. }
  553. void UpgradeMemoryModel::UpgradeExtInst(Instruction* ext_inst) {
  554. const bool is_modf = ext_inst->GetSingleWordInOperand(1u) == GLSLstd450Modf;
  555. auto ptr_id = ext_inst->GetSingleWordInOperand(3u);
  556. auto ptr_type_id = get_def_use_mgr()->GetDef(ptr_id)->type_id();
  557. auto pointee_type_id =
  558. get_def_use_mgr()->GetDef(ptr_type_id)->GetSingleWordInOperand(1u);
  559. auto element_type_id = ext_inst->type_id();
  560. std::vector<const analysis::Type*> element_types(2);
  561. element_types[0] = context()->get_type_mgr()->GetType(element_type_id);
  562. element_types[1] = context()->get_type_mgr()->GetType(pointee_type_id);
  563. analysis::Struct struct_type(element_types);
  564. uint32_t struct_id =
  565. context()->get_type_mgr()->GetTypeInstruction(&struct_type);
  566. // Change the operation
  567. GLSLstd450 new_op = is_modf ? GLSLstd450ModfStruct : GLSLstd450FrexpStruct;
  568. ext_inst->SetOperand(3u, {static_cast<uint32_t>(new_op)});
  569. // Remove the pointer argument
  570. ext_inst->RemoveOperand(5u);
  571. // Set the type id to the new struct.
  572. ext_inst->SetResultType(struct_id);
  573. // The result is now a struct of the original result. The zero'th element is
  574. // old result and should replace the old result. The one'th element needs to
  575. // be stored via a new instruction.
  576. auto where = ext_inst->NextNode();
  577. InstructionBuilder builder(
  578. context(), where,
  579. IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
  580. auto extract_0 =
  581. builder.AddCompositeExtract(element_type_id, ext_inst->result_id(), {0});
  582. context()->ReplaceAllUsesWith(ext_inst->result_id(), extract_0->result_id());
  583. // The extract's input was just changed to itself, so fix that.
  584. extract_0->SetInOperand(0u, {ext_inst->result_id()});
  585. auto extract_1 =
  586. builder.AddCompositeExtract(pointee_type_id, ext_inst->result_id(), {1});
  587. builder.AddStore(ptr_id, extract_1->result_id());
  588. }
  589. } // namespace opt
  590. } // namespace spvtools