upgrade_memory_model.cpp 30 KB

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