• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2015-2022 The Khronos Group Inc.
2 // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
3 // reserved.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #include "source/opcode.h"
18 
19 #include <assert.h>
20 #include <string.h>
21 
22 #include <algorithm>
23 #include <cstdlib>
24 
25 #include "source/instruction.h"
26 #include "source/macro.h"
27 #include "source/spirv_constant.h"
28 #include "source/spirv_endian.h"
29 #include "source/spirv_target_env.h"
30 #include "spirv-tools/libspirv.h"
31 
32 namespace {
33 struct OpcodeDescPtrLen {
34   const spv_opcode_desc_t* ptr;
35   uint32_t len;
36 };
37 
38 #include "core.insts-unified1.inc"
39 
40 static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries),
41                                                 kOpcodeTableEntries};
42 
43 // Represents a vendor tool entry in the SPIR-V XML Registry.
44 struct VendorTool {
45   uint32_t value;
46   const char* vendor;
47   const char* tool;         // Might be empty string.
48   const char* vendor_tool;  // Combination of vendor and tool.
49 };
50 
51 const VendorTool vendor_tools[] = {
52 #include "generators.inc"
53 };
54 
55 }  // anonymous namespace
56 
57 // TODO(dneto): Move this to another file.  It doesn't belong with opcode
58 // processing.
spvGeneratorStr(uint32_t generator)59 const char* spvGeneratorStr(uint32_t generator) {
60   auto where = std::find_if(
61       std::begin(vendor_tools), std::end(vendor_tools),
62       [generator](const VendorTool& vt) { return generator == vt.value; });
63   if (where != std::end(vendor_tools)) return where->vendor_tool;
64   return "Unknown";
65 }
66 
spvOpcodeMake(uint16_t wordCount,spv::Op opcode)67 uint32_t spvOpcodeMake(uint16_t wordCount, spv::Op opcode) {
68   return ((uint32_t)opcode) | (((uint32_t)wordCount) << 16);
69 }
70 
spvOpcodeSplit(const uint32_t word,uint16_t * pWordCount,uint16_t * pOpcode)71 void spvOpcodeSplit(const uint32_t word, uint16_t* pWordCount,
72                     uint16_t* pOpcode) {
73   if (pWordCount) {
74     *pWordCount = (uint16_t)((0xffff0000 & word) >> 16);
75   }
76   if (pOpcode) {
77     *pOpcode = 0x0000ffff & word;
78   }
79 }
80 
spvOpcodeTableGet(spv_opcode_table * pInstTable,spv_target_env)81 spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable, spv_target_env) {
82   if (!pInstTable) return SPV_ERROR_INVALID_POINTER;
83 
84   // Descriptions of each opcode.  Each entry describes the format of the
85   // instruction that follows a particular opcode.
86 
87   *pInstTable = &kOpcodeTable;
88   return SPV_SUCCESS;
89 }
90 
spvOpcodeTableNameLookup(spv_target_env env,const spv_opcode_table table,const char * name,spv_opcode_desc * pEntry)91 spv_result_t spvOpcodeTableNameLookup(spv_target_env env,
92                                       const spv_opcode_table table,
93                                       const char* name,
94                                       spv_opcode_desc* pEntry) {
95   if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER;
96   if (!table) return SPV_ERROR_INVALID_TABLE;
97 
98   // TODO: This lookup of the Opcode table is suboptimal! Binary sort would be
99   // preferable but the table requires sorting on the Opcode name, but it's
100   // static const initialized and matches the order of the spec.
101   const size_t nameLength = strlen(name);
102   const auto version = spvVersionForTargetEnv(env);
103   for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) {
104     const spv_opcode_desc_t& entry = table->entries[opcodeIndex];
105     // We considers the current opcode as available as long as
106     // 1. The target environment satisfies the minimal requirement of the
107     //    opcode; or
108     // 2. There is at least one extension enabling this opcode.
109     //
110     // Note that the second rule assumes the extension enabling this instruction
111     // is indeed requested in the SPIR-V code; checking that should be
112     // validator's work.
113     if (((version >= entry.minVersion && version <= entry.lastVersion) ||
114          entry.numExtensions > 0u || entry.numCapabilities > 0u) &&
115         nameLength == strlen(entry.name) &&
116         !strncmp(name, entry.name, nameLength)) {
117       // NOTE: Found out Opcode!
118       *pEntry = &entry;
119       return SPV_SUCCESS;
120     }
121   }
122 
123   return SPV_ERROR_INVALID_LOOKUP;
124 }
125 
spvOpcodeTableValueLookup(spv_target_env env,const spv_opcode_table table,const spv::Op opcode,spv_opcode_desc * pEntry)126 spv_result_t spvOpcodeTableValueLookup(spv_target_env env,
127                                        const spv_opcode_table table,
128                                        const spv::Op opcode,
129                                        spv_opcode_desc* pEntry) {
130   if (!table) return SPV_ERROR_INVALID_TABLE;
131   if (!pEntry) return SPV_ERROR_INVALID_POINTER;
132 
133   const auto beg = table->entries;
134   const auto end = table->entries + table->count;
135 
136   spv_opcode_desc_t needle = {"",    opcode, 0, nullptr, 0,   {},
137                               false, false,  0, nullptr, ~0u, ~0u};
138 
139   auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
140     return lhs.opcode < rhs.opcode;
141   };
142 
143   // We need to loop here because there can exist multiple symbols for the same
144   // opcode value, and they can be introduced in different target environments,
145   // which means they can have different minimal version requirements.
146   // Assumes the underlying table is already sorted ascendingly according to
147   // opcode value.
148   const auto version = spvVersionForTargetEnv(env);
149   for (auto it = std::lower_bound(beg, end, needle, comp);
150        it != end && it->opcode == opcode; ++it) {
151     // We considers the current opcode as available as long as
152     // 1. The target environment satisfies the minimal requirement of the
153     //    opcode; or
154     // 2. There is at least one extension enabling this opcode.
155     //
156     // Note that the second rule assumes the extension enabling this instruction
157     // is indeed requested in the SPIR-V code; checking that should be
158     // validator's work.
159     if ((version >= it->minVersion && version <= it->lastVersion) ||
160         it->numExtensions > 0u || it->numCapabilities > 0u) {
161       *pEntry = it;
162       return SPV_SUCCESS;
163     }
164   }
165 
166   return SPV_ERROR_INVALID_LOOKUP;
167 }
168 
spvInstructionCopy(const uint32_t * words,const spv::Op opcode,const uint16_t wordCount,const spv_endianness_t endian,spv_instruction_t * pInst)169 void spvInstructionCopy(const uint32_t* words, const spv::Op opcode,
170                         const uint16_t wordCount, const spv_endianness_t endian,
171                         spv_instruction_t* pInst) {
172   pInst->opcode = opcode;
173   pInst->words.resize(wordCount);
174   for (uint16_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) {
175     pInst->words[wordIndex] = spvFixWord(words[wordIndex], endian);
176     if (!wordIndex) {
177       uint16_t thisWordCount;
178       uint16_t thisOpcode;
179       spvOpcodeSplit(pInst->words[wordIndex], &thisWordCount, &thisOpcode);
180       assert(opcode == static_cast<spv::Op>(thisOpcode) &&
181              wordCount == thisWordCount && "Endianness failed!");
182     }
183   }
184 }
185 
spvOpcodeString(const uint32_t opcode)186 const char* spvOpcodeString(const uint32_t opcode) {
187   const auto beg = kOpcodeTableEntries;
188   const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries);
189   spv_opcode_desc_t needle = {"",    static_cast<spv::Op>(opcode),
190                               0,     nullptr,
191                               0,     {},
192                               false, false,
193                               0,     nullptr,
194                               ~0u,   ~0u};
195   auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
196     return lhs.opcode < rhs.opcode;
197   };
198   auto it = std::lower_bound(beg, end, needle, comp);
199   if (it != end && it->opcode == spv::Op(opcode)) {
200     return it->name;
201   }
202 
203   assert(0 && "Unreachable!");
204   return "unknown";
205 }
206 
spvOpcodeString(const spv::Op opcode)207 const char* spvOpcodeString(const spv::Op opcode) {
208   return spvOpcodeString(static_cast<uint32_t>(opcode));
209 }
210 
spvOpcodeIsScalarType(const spv::Op opcode)211 int32_t spvOpcodeIsScalarType(const spv::Op opcode) {
212   switch (opcode) {
213     case spv::Op::OpTypeInt:
214     case spv::Op::OpTypeFloat:
215     case spv::Op::OpTypeBool:
216       return true;
217     default:
218       return false;
219   }
220 }
221 
spvOpcodeIsSpecConstant(const spv::Op opcode)222 int32_t spvOpcodeIsSpecConstant(const spv::Op opcode) {
223   switch (opcode) {
224     case spv::Op::OpSpecConstantTrue:
225     case spv::Op::OpSpecConstantFalse:
226     case spv::Op::OpSpecConstant:
227     case spv::Op::OpSpecConstantComposite:
228     case spv::Op::OpSpecConstantOp:
229       return true;
230     default:
231       return false;
232   }
233 }
234 
spvOpcodeIsConstant(const spv::Op opcode)235 int32_t spvOpcodeIsConstant(const spv::Op opcode) {
236   switch (opcode) {
237     case spv::Op::OpConstantTrue:
238     case spv::Op::OpConstantFalse:
239     case spv::Op::OpConstant:
240     case spv::Op::OpConstantComposite:
241     case spv::Op::OpConstantSampler:
242     case spv::Op::OpConstantNull:
243     case spv::Op::OpConstantFunctionPointerINTEL:
244     case spv::Op::OpSpecConstantTrue:
245     case spv::Op::OpSpecConstantFalse:
246     case spv::Op::OpSpecConstant:
247     case spv::Op::OpSpecConstantComposite:
248     case spv::Op::OpSpecConstantOp:
249       return true;
250     default:
251       return false;
252   }
253 }
254 
spvOpcodeIsConstantOrUndef(const spv::Op opcode)255 bool spvOpcodeIsConstantOrUndef(const spv::Op opcode) {
256   return opcode == spv::Op::OpUndef || spvOpcodeIsConstant(opcode);
257 }
258 
spvOpcodeIsScalarSpecConstant(const spv::Op opcode)259 bool spvOpcodeIsScalarSpecConstant(const spv::Op opcode) {
260   switch (opcode) {
261     case spv::Op::OpSpecConstantTrue:
262     case spv::Op::OpSpecConstantFalse:
263     case spv::Op::OpSpecConstant:
264       return true;
265     default:
266       return false;
267   }
268 }
269 
spvOpcodeIsComposite(const spv::Op opcode)270 int32_t spvOpcodeIsComposite(const spv::Op opcode) {
271   switch (opcode) {
272     case spv::Op::OpTypeVector:
273     case spv::Op::OpTypeMatrix:
274     case spv::Op::OpTypeArray:
275     case spv::Op::OpTypeStruct:
276     case spv::Op::OpTypeCooperativeMatrixNV:
277     case spv::Op::OpTypeCooperativeMatrixKHR:
278       return true;
279     default:
280       return false;
281   }
282 }
283 
spvOpcodeReturnsLogicalVariablePointer(const spv::Op opcode)284 bool spvOpcodeReturnsLogicalVariablePointer(const spv::Op opcode) {
285   switch (opcode) {
286     case spv::Op::OpVariable:
287     case spv::Op::OpAccessChain:
288     case spv::Op::OpInBoundsAccessChain:
289     case spv::Op::OpFunctionParameter:
290     case spv::Op::OpImageTexelPointer:
291     case spv::Op::OpCopyObject:
292     case spv::Op::OpSelect:
293     case spv::Op::OpPhi:
294     case spv::Op::OpFunctionCall:
295     case spv::Op::OpPtrAccessChain:
296     case spv::Op::OpLoad:
297     case spv::Op::OpConstantNull:
298       return true;
299     default:
300       return false;
301   }
302 }
303 
spvOpcodeReturnsLogicalPointer(const spv::Op opcode)304 int32_t spvOpcodeReturnsLogicalPointer(const spv::Op opcode) {
305   switch (opcode) {
306     case spv::Op::OpVariable:
307     case spv::Op::OpAccessChain:
308     case spv::Op::OpInBoundsAccessChain:
309     case spv::Op::OpFunctionParameter:
310     case spv::Op::OpImageTexelPointer:
311     case spv::Op::OpCopyObject:
312       return true;
313     default:
314       return false;
315   }
316 }
317 
spvOpcodeGeneratesType(spv::Op op)318 int32_t spvOpcodeGeneratesType(spv::Op op) {
319   switch (op) {
320     case spv::Op::OpTypeVoid:
321     case spv::Op::OpTypeBool:
322     case spv::Op::OpTypeInt:
323     case spv::Op::OpTypeFloat:
324     case spv::Op::OpTypeVector:
325     case spv::Op::OpTypeMatrix:
326     case spv::Op::OpTypeImage:
327     case spv::Op::OpTypeSampler:
328     case spv::Op::OpTypeSampledImage:
329     case spv::Op::OpTypeArray:
330     case spv::Op::OpTypeRuntimeArray:
331     case spv::Op::OpTypeStruct:
332     case spv::Op::OpTypeOpaque:
333     case spv::Op::OpTypePointer:
334     case spv::Op::OpTypeFunction:
335     case spv::Op::OpTypeEvent:
336     case spv::Op::OpTypeDeviceEvent:
337     case spv::Op::OpTypeReserveId:
338     case spv::Op::OpTypeQueue:
339     case spv::Op::OpTypePipe:
340     case spv::Op::OpTypePipeStorage:
341     case spv::Op::OpTypeNamedBarrier:
342     case spv::Op::OpTypeAccelerationStructureNV:
343     case spv::Op::OpTypeCooperativeMatrixNV:
344     case spv::Op::OpTypeCooperativeMatrixKHR:
345     // case spv::Op::OpTypeAccelerationStructureKHR: covered by
346     // spv::Op::OpTypeAccelerationStructureNV
347     case spv::Op::OpTypeRayQueryKHR:
348     case spv::Op::OpTypeHitObjectNV:
349       return true;
350     default:
351       // In particular, OpTypeForwardPointer does not generate a type,
352       // but declares a storage class for a pointer type generated
353       // by a different instruction.
354       break;
355   }
356   return 0;
357 }
358 
spvOpcodeIsDecoration(const spv::Op opcode)359 bool spvOpcodeIsDecoration(const spv::Op opcode) {
360   switch (opcode) {
361     case spv::Op::OpDecorate:
362     case spv::Op::OpDecorateId:
363     case spv::Op::OpMemberDecorate:
364     case spv::Op::OpGroupDecorate:
365     case spv::Op::OpGroupMemberDecorate:
366     case spv::Op::OpDecorateStringGOOGLE:
367     case spv::Op::OpMemberDecorateStringGOOGLE:
368       return true;
369     default:
370       break;
371   }
372   return false;
373 }
374 
spvOpcodeIsLoad(const spv::Op opcode)375 bool spvOpcodeIsLoad(const spv::Op opcode) {
376   switch (opcode) {
377     case spv::Op::OpLoad:
378     case spv::Op::OpImageSampleExplicitLod:
379     case spv::Op::OpImageSampleImplicitLod:
380     case spv::Op::OpImageSampleDrefImplicitLod:
381     case spv::Op::OpImageSampleDrefExplicitLod:
382     case spv::Op::OpImageSampleProjImplicitLod:
383     case spv::Op::OpImageSampleProjExplicitLod:
384     case spv::Op::OpImageSampleProjDrefImplicitLod:
385     case spv::Op::OpImageSampleProjDrefExplicitLod:
386     case spv::Op::OpImageFetch:
387     case spv::Op::OpImageGather:
388     case spv::Op::OpImageDrefGather:
389     case spv::Op::OpImageRead:
390     case spv::Op::OpImageSparseSampleImplicitLod:
391     case spv::Op::OpImageSparseSampleExplicitLod:
392     case spv::Op::OpImageSparseSampleDrefExplicitLod:
393     case spv::Op::OpImageSparseSampleDrefImplicitLod:
394     case spv::Op::OpImageSparseFetch:
395     case spv::Op::OpImageSparseGather:
396     case spv::Op::OpImageSparseDrefGather:
397     case spv::Op::OpImageSparseRead:
398       return true;
399     default:
400       return false;
401   }
402 }
403 
spvOpcodeIsBranch(spv::Op opcode)404 bool spvOpcodeIsBranch(spv::Op opcode) {
405   switch (opcode) {
406     case spv::Op::OpBranch:
407     case spv::Op::OpBranchConditional:
408     case spv::Op::OpSwitch:
409       return true;
410     default:
411       return false;
412   }
413 }
414 
spvOpcodeIsAtomicWithLoad(const spv::Op opcode)415 bool spvOpcodeIsAtomicWithLoad(const spv::Op opcode) {
416   switch (opcode) {
417     case spv::Op::OpAtomicLoad:
418     case spv::Op::OpAtomicExchange:
419     case spv::Op::OpAtomicCompareExchange:
420     case spv::Op::OpAtomicCompareExchangeWeak:
421     case spv::Op::OpAtomicIIncrement:
422     case spv::Op::OpAtomicIDecrement:
423     case spv::Op::OpAtomicIAdd:
424     case spv::Op::OpAtomicFAddEXT:
425     case spv::Op::OpAtomicISub:
426     case spv::Op::OpAtomicSMin:
427     case spv::Op::OpAtomicUMin:
428     case spv::Op::OpAtomicFMinEXT:
429     case spv::Op::OpAtomicSMax:
430     case spv::Op::OpAtomicUMax:
431     case spv::Op::OpAtomicFMaxEXT:
432     case spv::Op::OpAtomicAnd:
433     case spv::Op::OpAtomicOr:
434     case spv::Op::OpAtomicXor:
435     case spv::Op::OpAtomicFlagTestAndSet:
436       return true;
437     default:
438       return false;
439   }
440 }
441 
spvOpcodeIsAtomicOp(const spv::Op opcode)442 bool spvOpcodeIsAtomicOp(const spv::Op opcode) {
443   return (spvOpcodeIsAtomicWithLoad(opcode) ||
444           opcode == spv::Op::OpAtomicStore ||
445           opcode == spv::Op::OpAtomicFlagClear);
446 }
447 
spvOpcodeIsReturn(spv::Op opcode)448 bool spvOpcodeIsReturn(spv::Op opcode) {
449   switch (opcode) {
450     case spv::Op::OpReturn:
451     case spv::Op::OpReturnValue:
452       return true;
453     default:
454       return false;
455   }
456 }
457 
spvOpcodeIsAbort(spv::Op opcode)458 bool spvOpcodeIsAbort(spv::Op opcode) {
459   switch (opcode) {
460     case spv::Op::OpKill:
461     case spv::Op::OpUnreachable:
462     case spv::Op::OpTerminateInvocation:
463     case spv::Op::OpTerminateRayKHR:
464     case spv::Op::OpIgnoreIntersectionKHR:
465     case spv::Op::OpEmitMeshTasksEXT:
466       return true;
467     default:
468       return false;
469   }
470 }
471 
spvOpcodeIsReturnOrAbort(spv::Op opcode)472 bool spvOpcodeIsReturnOrAbort(spv::Op opcode) {
473   return spvOpcodeIsReturn(opcode) || spvOpcodeIsAbort(opcode);
474 }
475 
spvOpcodeIsBlockTerminator(spv::Op opcode)476 bool spvOpcodeIsBlockTerminator(spv::Op opcode) {
477   return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode);
478 }
479 
spvOpcodeIsBaseOpaqueType(spv::Op opcode)480 bool spvOpcodeIsBaseOpaqueType(spv::Op opcode) {
481   switch (opcode) {
482     case spv::Op::OpTypeImage:
483     case spv::Op::OpTypeSampler:
484     case spv::Op::OpTypeSampledImage:
485     case spv::Op::OpTypeOpaque:
486     case spv::Op::OpTypeEvent:
487     case spv::Op::OpTypeDeviceEvent:
488     case spv::Op::OpTypeReserveId:
489     case spv::Op::OpTypeQueue:
490     case spv::Op::OpTypePipe:
491     case spv::Op::OpTypeForwardPointer:
492     case spv::Op::OpTypePipeStorage:
493     case spv::Op::OpTypeNamedBarrier:
494       return true;
495     default:
496       return false;
497   }
498 }
499 
spvOpcodeIsNonUniformGroupOperation(spv::Op opcode)500 bool spvOpcodeIsNonUniformGroupOperation(spv::Op opcode) {
501   switch (opcode) {
502     case spv::Op::OpGroupNonUniformElect:
503     case spv::Op::OpGroupNonUniformAll:
504     case spv::Op::OpGroupNonUniformAny:
505     case spv::Op::OpGroupNonUniformAllEqual:
506     case spv::Op::OpGroupNonUniformBroadcast:
507     case spv::Op::OpGroupNonUniformBroadcastFirst:
508     case spv::Op::OpGroupNonUniformBallot:
509     case spv::Op::OpGroupNonUniformInverseBallot:
510     case spv::Op::OpGroupNonUniformBallotBitExtract:
511     case spv::Op::OpGroupNonUniformBallotBitCount:
512     case spv::Op::OpGroupNonUniformBallotFindLSB:
513     case spv::Op::OpGroupNonUniformBallotFindMSB:
514     case spv::Op::OpGroupNonUniformShuffle:
515     case spv::Op::OpGroupNonUniformShuffleXor:
516     case spv::Op::OpGroupNonUniformShuffleUp:
517     case spv::Op::OpGroupNonUniformShuffleDown:
518     case spv::Op::OpGroupNonUniformIAdd:
519     case spv::Op::OpGroupNonUniformFAdd:
520     case spv::Op::OpGroupNonUniformIMul:
521     case spv::Op::OpGroupNonUniformFMul:
522     case spv::Op::OpGroupNonUniformSMin:
523     case spv::Op::OpGroupNonUniformUMin:
524     case spv::Op::OpGroupNonUniformFMin:
525     case spv::Op::OpGroupNonUniformSMax:
526     case spv::Op::OpGroupNonUniformUMax:
527     case spv::Op::OpGroupNonUniformFMax:
528     case spv::Op::OpGroupNonUniformBitwiseAnd:
529     case spv::Op::OpGroupNonUniformBitwiseOr:
530     case spv::Op::OpGroupNonUniformBitwiseXor:
531     case spv::Op::OpGroupNonUniformLogicalAnd:
532     case spv::Op::OpGroupNonUniformLogicalOr:
533     case spv::Op::OpGroupNonUniformLogicalXor:
534     case spv::Op::OpGroupNonUniformQuadBroadcast:
535     case spv::Op::OpGroupNonUniformQuadSwap:
536     case spv::Op::OpGroupNonUniformRotateKHR:
537       return true;
538     default:
539       return false;
540   }
541 }
542 
spvOpcodeIsScalarizable(spv::Op opcode)543 bool spvOpcodeIsScalarizable(spv::Op opcode) {
544   switch (opcode) {
545     case spv::Op::OpPhi:
546     case spv::Op::OpCopyObject:
547     case spv::Op::OpConvertFToU:
548     case spv::Op::OpConvertFToS:
549     case spv::Op::OpConvertSToF:
550     case spv::Op::OpConvertUToF:
551     case spv::Op::OpUConvert:
552     case spv::Op::OpSConvert:
553     case spv::Op::OpFConvert:
554     case spv::Op::OpQuantizeToF16:
555     case spv::Op::OpVectorInsertDynamic:
556     case spv::Op::OpSNegate:
557     case spv::Op::OpFNegate:
558     case spv::Op::OpIAdd:
559     case spv::Op::OpFAdd:
560     case spv::Op::OpISub:
561     case spv::Op::OpFSub:
562     case spv::Op::OpIMul:
563     case spv::Op::OpFMul:
564     case spv::Op::OpUDiv:
565     case spv::Op::OpSDiv:
566     case spv::Op::OpFDiv:
567     case spv::Op::OpUMod:
568     case spv::Op::OpSRem:
569     case spv::Op::OpSMod:
570     case spv::Op::OpFRem:
571     case spv::Op::OpFMod:
572     case spv::Op::OpVectorTimesScalar:
573     case spv::Op::OpIAddCarry:
574     case spv::Op::OpISubBorrow:
575     case spv::Op::OpUMulExtended:
576     case spv::Op::OpSMulExtended:
577     case spv::Op::OpShiftRightLogical:
578     case spv::Op::OpShiftRightArithmetic:
579     case spv::Op::OpShiftLeftLogical:
580     case spv::Op::OpBitwiseOr:
581     case spv::Op::OpBitwiseAnd:
582     case spv::Op::OpNot:
583     case spv::Op::OpBitFieldInsert:
584     case spv::Op::OpBitFieldSExtract:
585     case spv::Op::OpBitFieldUExtract:
586     case spv::Op::OpBitReverse:
587     case spv::Op::OpBitCount:
588     case spv::Op::OpIsNan:
589     case spv::Op::OpIsInf:
590     case spv::Op::OpIsFinite:
591     case spv::Op::OpIsNormal:
592     case spv::Op::OpSignBitSet:
593     case spv::Op::OpLessOrGreater:
594     case spv::Op::OpOrdered:
595     case spv::Op::OpUnordered:
596     case spv::Op::OpLogicalEqual:
597     case spv::Op::OpLogicalNotEqual:
598     case spv::Op::OpLogicalOr:
599     case spv::Op::OpLogicalAnd:
600     case spv::Op::OpLogicalNot:
601     case spv::Op::OpSelect:
602     case spv::Op::OpIEqual:
603     case spv::Op::OpINotEqual:
604     case spv::Op::OpUGreaterThan:
605     case spv::Op::OpSGreaterThan:
606     case spv::Op::OpUGreaterThanEqual:
607     case spv::Op::OpSGreaterThanEqual:
608     case spv::Op::OpULessThan:
609     case spv::Op::OpSLessThan:
610     case spv::Op::OpULessThanEqual:
611     case spv::Op::OpSLessThanEqual:
612     case spv::Op::OpFOrdEqual:
613     case spv::Op::OpFUnordEqual:
614     case spv::Op::OpFOrdNotEqual:
615     case spv::Op::OpFUnordNotEqual:
616     case spv::Op::OpFOrdLessThan:
617     case spv::Op::OpFUnordLessThan:
618     case spv::Op::OpFOrdGreaterThan:
619     case spv::Op::OpFUnordGreaterThan:
620     case spv::Op::OpFOrdLessThanEqual:
621     case spv::Op::OpFUnordLessThanEqual:
622     case spv::Op::OpFOrdGreaterThanEqual:
623     case spv::Op::OpFUnordGreaterThanEqual:
624       return true;
625     default:
626       return false;
627   }
628 }
629 
spvOpcodeIsDebug(spv::Op opcode)630 bool spvOpcodeIsDebug(spv::Op opcode) {
631   switch (opcode) {
632     case spv::Op::OpName:
633     case spv::Op::OpMemberName:
634     case spv::Op::OpSource:
635     case spv::Op::OpSourceContinued:
636     case spv::Op::OpSourceExtension:
637     case spv::Op::OpString:
638     case spv::Op::OpLine:
639     case spv::Op::OpNoLine:
640     case spv::Op::OpModuleProcessed:
641       return true;
642     default:
643       return false;
644   }
645 }
646 
spvOpcodeIsCommutativeBinaryOperator(spv::Op opcode)647 bool spvOpcodeIsCommutativeBinaryOperator(spv::Op opcode) {
648   switch (opcode) {
649     case spv::Op::OpPtrEqual:
650     case spv::Op::OpPtrNotEqual:
651     case spv::Op::OpIAdd:
652     case spv::Op::OpFAdd:
653     case spv::Op::OpIMul:
654     case spv::Op::OpFMul:
655     case spv::Op::OpDot:
656     case spv::Op::OpIAddCarry:
657     case spv::Op::OpUMulExtended:
658     case spv::Op::OpSMulExtended:
659     case spv::Op::OpBitwiseOr:
660     case spv::Op::OpBitwiseXor:
661     case spv::Op::OpBitwiseAnd:
662     case spv::Op::OpOrdered:
663     case spv::Op::OpUnordered:
664     case spv::Op::OpLogicalEqual:
665     case spv::Op::OpLogicalNotEqual:
666     case spv::Op::OpLogicalOr:
667     case spv::Op::OpLogicalAnd:
668     case spv::Op::OpIEqual:
669     case spv::Op::OpINotEqual:
670     case spv::Op::OpFOrdEqual:
671     case spv::Op::OpFUnordEqual:
672     case spv::Op::OpFOrdNotEqual:
673     case spv::Op::OpFUnordNotEqual:
674       return true;
675     default:
676       return false;
677   }
678 }
679 
spvOpcodeIsLinearAlgebra(spv::Op opcode)680 bool spvOpcodeIsLinearAlgebra(spv::Op opcode) {
681   switch (opcode) {
682     case spv::Op::OpTranspose:
683     case spv::Op::OpVectorTimesScalar:
684     case spv::Op::OpMatrixTimesScalar:
685     case spv::Op::OpVectorTimesMatrix:
686     case spv::Op::OpMatrixTimesVector:
687     case spv::Op::OpMatrixTimesMatrix:
688     case spv::Op::OpOuterProduct:
689     case spv::Op::OpDot:
690       return true;
691     default:
692       return false;
693   }
694 }
695 
spvOpcodeIsImageSample(const spv::Op opcode)696 bool spvOpcodeIsImageSample(const spv::Op opcode) {
697   switch (opcode) {
698     case spv::Op::OpImageSampleImplicitLod:
699     case spv::Op::OpImageSampleExplicitLod:
700     case spv::Op::OpImageSampleDrefImplicitLod:
701     case spv::Op::OpImageSampleDrefExplicitLod:
702     case spv::Op::OpImageSampleProjImplicitLod:
703     case spv::Op::OpImageSampleProjExplicitLod:
704     case spv::Op::OpImageSampleProjDrefImplicitLod:
705     case spv::Op::OpImageSampleProjDrefExplicitLod:
706     case spv::Op::OpImageSparseSampleImplicitLod:
707     case spv::Op::OpImageSparseSampleExplicitLod:
708     case spv::Op::OpImageSparseSampleDrefImplicitLod:
709     case spv::Op::OpImageSparseSampleDrefExplicitLod:
710       return true;
711     default:
712       return false;
713   }
714 }
715 
spvOpcodeMemorySemanticsOperandIndices(spv::Op opcode)716 std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(spv::Op opcode) {
717   switch (opcode) {
718     case spv::Op::OpMemoryBarrier:
719       return {1};
720     case spv::Op::OpAtomicStore:
721     case spv::Op::OpControlBarrier:
722     case spv::Op::OpAtomicFlagClear:
723     case spv::Op::OpMemoryNamedBarrier:
724       return {2};
725     case spv::Op::OpAtomicLoad:
726     case spv::Op::OpAtomicExchange:
727     case spv::Op::OpAtomicIIncrement:
728     case spv::Op::OpAtomicIDecrement:
729     case spv::Op::OpAtomicIAdd:
730     case spv::Op::OpAtomicFAddEXT:
731     case spv::Op::OpAtomicISub:
732     case spv::Op::OpAtomicSMin:
733     case spv::Op::OpAtomicUMin:
734     case spv::Op::OpAtomicSMax:
735     case spv::Op::OpAtomicUMax:
736     case spv::Op::OpAtomicAnd:
737     case spv::Op::OpAtomicOr:
738     case spv::Op::OpAtomicXor:
739     case spv::Op::OpAtomicFlagTestAndSet:
740       return {4};
741     case spv::Op::OpAtomicCompareExchange:
742     case spv::Op::OpAtomicCompareExchangeWeak:
743       return {4, 5};
744     default:
745       return {};
746   }
747 }
748 
spvOpcodeIsAccessChain(spv::Op opcode)749 bool spvOpcodeIsAccessChain(spv::Op opcode) {
750   switch (opcode) {
751     case spv::Op::OpAccessChain:
752     case spv::Op::OpInBoundsAccessChain:
753     case spv::Op::OpPtrAccessChain:
754     case spv::Op::OpInBoundsPtrAccessChain:
755       return true;
756     default:
757       return false;
758   }
759 }
760 
spvOpcodeIsBit(spv::Op opcode)761 bool spvOpcodeIsBit(spv::Op opcode) {
762   switch (opcode) {
763     case spv::Op::OpShiftRightLogical:
764     case spv::Op::OpShiftRightArithmetic:
765     case spv::Op::OpShiftLeftLogical:
766     case spv::Op::OpBitwiseOr:
767     case spv::Op::OpBitwiseXor:
768     case spv::Op::OpBitwiseAnd:
769     case spv::Op::OpNot:
770     case spv::Op::OpBitReverse:
771     case spv::Op::OpBitCount:
772       return true;
773     default:
774       return false;
775   }
776 }
777