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