upgrade_memory_model.cpp 31 KB

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