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