upgrade_memory_model.cpp 30 KB

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