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 case spv::Op::OpRawAccessChainNV:
299 return true;
300 default:
301 return false;
302 }
303 }
304
spvOpcodeReturnsLogicalPointer(const spv::Op opcode)305 int32_t spvOpcodeReturnsLogicalPointer(const spv::Op opcode) {
306 switch (opcode) {
307 case spv::Op::OpVariable:
308 case spv::Op::OpAccessChain:
309 case spv::Op::OpInBoundsAccessChain:
310 case spv::Op::OpFunctionParameter:
311 case spv::Op::OpImageTexelPointer:
312 case spv::Op::OpCopyObject:
313 case spv::Op::OpRawAccessChainNV:
314 return true;
315 default:
316 return false;
317 }
318 }
319
spvOpcodeGeneratesType(spv::Op op)320 int32_t spvOpcodeGeneratesType(spv::Op op) {
321 switch (op) {
322 case spv::Op::OpTypeVoid:
323 case spv::Op::OpTypeBool:
324 case spv::Op::OpTypeInt:
325 case spv::Op::OpTypeFloat:
326 case spv::Op::OpTypeVector:
327 case spv::Op::OpTypeMatrix:
328 case spv::Op::OpTypeImage:
329 case spv::Op::OpTypeSampler:
330 case spv::Op::OpTypeSampledImage:
331 case spv::Op::OpTypeArray:
332 case spv::Op::OpTypeRuntimeArray:
333 case spv::Op::OpTypeStruct:
334 case spv::Op::OpTypeOpaque:
335 case spv::Op::OpTypePointer:
336 case spv::Op::OpTypeFunction:
337 case spv::Op::OpTypeEvent:
338 case spv::Op::OpTypeDeviceEvent:
339 case spv::Op::OpTypeReserveId:
340 case spv::Op::OpTypeQueue:
341 case spv::Op::OpTypePipe:
342 case spv::Op::OpTypePipeStorage:
343 case spv::Op::OpTypeNamedBarrier:
344 case spv::Op::OpTypeAccelerationStructureNV:
345 case spv::Op::OpTypeCooperativeMatrixNV:
346 case spv::Op::OpTypeCooperativeMatrixKHR:
347 // case spv::Op::OpTypeAccelerationStructureKHR: covered by
348 // spv::Op::OpTypeAccelerationStructureNV
349 case spv::Op::OpTypeRayQueryKHR:
350 case spv::Op::OpTypeHitObjectNV:
351 return true;
352 default:
353 // In particular, OpTypeForwardPointer does not generate a type,
354 // but declares a storage class for a pointer type generated
355 // by a different instruction.
356 break;
357 }
358 return 0;
359 }
360
spvOpcodeIsDecoration(const spv::Op opcode)361 bool spvOpcodeIsDecoration(const spv::Op opcode) {
362 switch (opcode) {
363 case spv::Op::OpDecorate:
364 case spv::Op::OpDecorateId:
365 case spv::Op::OpMemberDecorate:
366 case spv::Op::OpGroupDecorate:
367 case spv::Op::OpGroupMemberDecorate:
368 case spv::Op::OpDecorateStringGOOGLE:
369 case spv::Op::OpMemberDecorateStringGOOGLE:
370 return true;
371 default:
372 break;
373 }
374 return false;
375 }
376
spvOpcodeIsLoad(const spv::Op opcode)377 bool spvOpcodeIsLoad(const spv::Op opcode) {
378 switch (opcode) {
379 case spv::Op::OpLoad:
380 case spv::Op::OpImageSampleExplicitLod:
381 case spv::Op::OpImageSampleImplicitLod:
382 case spv::Op::OpImageSampleDrefImplicitLod:
383 case spv::Op::OpImageSampleDrefExplicitLod:
384 case spv::Op::OpImageSampleProjImplicitLod:
385 case spv::Op::OpImageSampleProjExplicitLod:
386 case spv::Op::OpImageSampleProjDrefImplicitLod:
387 case spv::Op::OpImageSampleProjDrefExplicitLod:
388 case spv::Op::OpImageFetch:
389 case spv::Op::OpImageGather:
390 case spv::Op::OpImageDrefGather:
391 case spv::Op::OpImageRead:
392 case spv::Op::OpImageSparseSampleImplicitLod:
393 case spv::Op::OpImageSparseSampleExplicitLod:
394 case spv::Op::OpImageSparseSampleDrefExplicitLod:
395 case spv::Op::OpImageSparseSampleDrefImplicitLod:
396 case spv::Op::OpImageSparseFetch:
397 case spv::Op::OpImageSparseGather:
398 case spv::Op::OpImageSparseDrefGather:
399 case spv::Op::OpImageSparseRead:
400 return true;
401 default:
402 return false;
403 }
404 }
405
spvOpcodeIsBranch(spv::Op opcode)406 bool spvOpcodeIsBranch(spv::Op opcode) {
407 switch (opcode) {
408 case spv::Op::OpBranch:
409 case spv::Op::OpBranchConditional:
410 case spv::Op::OpSwitch:
411 return true;
412 default:
413 return false;
414 }
415 }
416
spvOpcodeIsAtomicWithLoad(const spv::Op opcode)417 bool spvOpcodeIsAtomicWithLoad(const spv::Op opcode) {
418 switch (opcode) {
419 case spv::Op::OpAtomicLoad:
420 case spv::Op::OpAtomicExchange:
421 case spv::Op::OpAtomicCompareExchange:
422 case spv::Op::OpAtomicCompareExchangeWeak:
423 case spv::Op::OpAtomicIIncrement:
424 case spv::Op::OpAtomicIDecrement:
425 case spv::Op::OpAtomicIAdd:
426 case spv::Op::OpAtomicFAddEXT:
427 case spv::Op::OpAtomicISub:
428 case spv::Op::OpAtomicSMin:
429 case spv::Op::OpAtomicUMin:
430 case spv::Op::OpAtomicFMinEXT:
431 case spv::Op::OpAtomicSMax:
432 case spv::Op::OpAtomicUMax:
433 case spv::Op::OpAtomicFMaxEXT:
434 case spv::Op::OpAtomicAnd:
435 case spv::Op::OpAtomicOr:
436 case spv::Op::OpAtomicXor:
437 case spv::Op::OpAtomicFlagTestAndSet:
438 return true;
439 default:
440 return false;
441 }
442 }
443
spvOpcodeIsAtomicOp(const spv::Op opcode)444 bool spvOpcodeIsAtomicOp(const spv::Op opcode) {
445 return (spvOpcodeIsAtomicWithLoad(opcode) ||
446 opcode == spv::Op::OpAtomicStore ||
447 opcode == spv::Op::OpAtomicFlagClear);
448 }
449
spvOpcodeIsReturn(spv::Op opcode)450 bool spvOpcodeIsReturn(spv::Op opcode) {
451 switch (opcode) {
452 case spv::Op::OpReturn:
453 case spv::Op::OpReturnValue:
454 return true;
455 default:
456 return false;
457 }
458 }
459
spvOpcodeIsAbort(spv::Op opcode)460 bool spvOpcodeIsAbort(spv::Op opcode) {
461 switch (opcode) {
462 case spv::Op::OpKill:
463 case spv::Op::OpUnreachable:
464 case spv::Op::OpTerminateInvocation:
465 case spv::Op::OpTerminateRayKHR:
466 case spv::Op::OpIgnoreIntersectionKHR:
467 case spv::Op::OpEmitMeshTasksEXT:
468 return true;
469 default:
470 return false;
471 }
472 }
473
spvOpcodeIsReturnOrAbort(spv::Op opcode)474 bool spvOpcodeIsReturnOrAbort(spv::Op opcode) {
475 return spvOpcodeIsReturn(opcode) || spvOpcodeIsAbort(opcode);
476 }
477
spvOpcodeIsBlockTerminator(spv::Op opcode)478 bool spvOpcodeIsBlockTerminator(spv::Op opcode) {
479 return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode);
480 }
481
spvOpcodeIsBaseOpaqueType(spv::Op opcode)482 bool spvOpcodeIsBaseOpaqueType(spv::Op opcode) {
483 switch (opcode) {
484 case spv::Op::OpTypeImage:
485 case spv::Op::OpTypeSampler:
486 case spv::Op::OpTypeSampledImage:
487 case spv::Op::OpTypeOpaque:
488 case spv::Op::OpTypeEvent:
489 case spv::Op::OpTypeDeviceEvent:
490 case spv::Op::OpTypeReserveId:
491 case spv::Op::OpTypeQueue:
492 case spv::Op::OpTypePipe:
493 case spv::Op::OpTypeForwardPointer:
494 case spv::Op::OpTypePipeStorage:
495 case spv::Op::OpTypeNamedBarrier:
496 return true;
497 default:
498 return false;
499 }
500 }
501
spvOpcodeIsNonUniformGroupOperation(spv::Op opcode)502 bool spvOpcodeIsNonUniformGroupOperation(spv::Op opcode) {
503 switch (opcode) {
504 case spv::Op::OpGroupNonUniformElect:
505 case spv::Op::OpGroupNonUniformAll:
506 case spv::Op::OpGroupNonUniformAny:
507 case spv::Op::OpGroupNonUniformAllEqual:
508 case spv::Op::OpGroupNonUniformBroadcast:
509 case spv::Op::OpGroupNonUniformBroadcastFirst:
510 case spv::Op::OpGroupNonUniformBallot:
511 case spv::Op::OpGroupNonUniformInverseBallot:
512 case spv::Op::OpGroupNonUniformBallotBitExtract:
513 case spv::Op::OpGroupNonUniformBallotBitCount:
514 case spv::Op::OpGroupNonUniformBallotFindLSB:
515 case spv::Op::OpGroupNonUniformBallotFindMSB:
516 case spv::Op::OpGroupNonUniformShuffle:
517 case spv::Op::OpGroupNonUniformShuffleXor:
518 case spv::Op::OpGroupNonUniformShuffleUp:
519 case spv::Op::OpGroupNonUniformShuffleDown:
520 case spv::Op::OpGroupNonUniformIAdd:
521 case spv::Op::OpGroupNonUniformFAdd:
522 case spv::Op::OpGroupNonUniformIMul:
523 case spv::Op::OpGroupNonUniformFMul:
524 case spv::Op::OpGroupNonUniformSMin:
525 case spv::Op::OpGroupNonUniformUMin:
526 case spv::Op::OpGroupNonUniformFMin:
527 case spv::Op::OpGroupNonUniformSMax:
528 case spv::Op::OpGroupNonUniformUMax:
529 case spv::Op::OpGroupNonUniformFMax:
530 case spv::Op::OpGroupNonUniformBitwiseAnd:
531 case spv::Op::OpGroupNonUniformBitwiseOr:
532 case spv::Op::OpGroupNonUniformBitwiseXor:
533 case spv::Op::OpGroupNonUniformLogicalAnd:
534 case spv::Op::OpGroupNonUniformLogicalOr:
535 case spv::Op::OpGroupNonUniformLogicalXor:
536 case spv::Op::OpGroupNonUniformQuadBroadcast:
537 case spv::Op::OpGroupNonUniformQuadSwap:
538 case spv::Op::OpGroupNonUniformRotateKHR:
539 case spv::Op::OpGroupNonUniformQuadAllKHR:
540 case spv::Op::OpGroupNonUniformQuadAnyKHR:
541 return true;
542 default:
543 return false;
544 }
545 }
546
spvOpcodeIsScalarizable(spv::Op opcode)547 bool spvOpcodeIsScalarizable(spv::Op opcode) {
548 switch (opcode) {
549 case spv::Op::OpPhi:
550 case spv::Op::OpCopyObject:
551 case spv::Op::OpConvertFToU:
552 case spv::Op::OpConvertFToS:
553 case spv::Op::OpConvertSToF:
554 case spv::Op::OpConvertUToF:
555 case spv::Op::OpUConvert:
556 case spv::Op::OpSConvert:
557 case spv::Op::OpFConvert:
558 case spv::Op::OpQuantizeToF16:
559 case spv::Op::OpVectorInsertDynamic:
560 case spv::Op::OpSNegate:
561 case spv::Op::OpFNegate:
562 case spv::Op::OpIAdd:
563 case spv::Op::OpFAdd:
564 case spv::Op::OpISub:
565 case spv::Op::OpFSub:
566 case spv::Op::OpIMul:
567 case spv::Op::OpFMul:
568 case spv::Op::OpUDiv:
569 case spv::Op::OpSDiv:
570 case spv::Op::OpFDiv:
571 case spv::Op::OpUMod:
572 case spv::Op::OpSRem:
573 case spv::Op::OpSMod:
574 case spv::Op::OpFRem:
575 case spv::Op::OpFMod:
576 case spv::Op::OpVectorTimesScalar:
577 case spv::Op::OpIAddCarry:
578 case spv::Op::OpISubBorrow:
579 case spv::Op::OpUMulExtended:
580 case spv::Op::OpSMulExtended:
581 case spv::Op::OpShiftRightLogical:
582 case spv::Op::OpShiftRightArithmetic:
583 case spv::Op::OpShiftLeftLogical:
584 case spv::Op::OpBitwiseOr:
585 case spv::Op::OpBitwiseAnd:
586 case spv::Op::OpNot:
587 case spv::Op::OpBitFieldInsert:
588 case spv::Op::OpBitFieldSExtract:
589 case spv::Op::OpBitFieldUExtract:
590 case spv::Op::OpBitReverse:
591 case spv::Op::OpBitCount:
592 case spv::Op::OpIsNan:
593 case spv::Op::OpIsInf:
594 case spv::Op::OpIsFinite:
595 case spv::Op::OpIsNormal:
596 case spv::Op::OpSignBitSet:
597 case spv::Op::OpLessOrGreater:
598 case spv::Op::OpOrdered:
599 case spv::Op::OpUnordered:
600 case spv::Op::OpLogicalEqual:
601 case spv::Op::OpLogicalNotEqual:
602 case spv::Op::OpLogicalOr:
603 case spv::Op::OpLogicalAnd:
604 case spv::Op::OpLogicalNot:
605 case spv::Op::OpSelect:
606 case spv::Op::OpIEqual:
607 case spv::Op::OpINotEqual:
608 case spv::Op::OpUGreaterThan:
609 case spv::Op::OpSGreaterThan:
610 case spv::Op::OpUGreaterThanEqual:
611 case spv::Op::OpSGreaterThanEqual:
612 case spv::Op::OpULessThan:
613 case spv::Op::OpSLessThan:
614 case spv::Op::OpULessThanEqual:
615 case spv::Op::OpSLessThanEqual:
616 case spv::Op::OpFOrdEqual:
617 case spv::Op::OpFUnordEqual:
618 case spv::Op::OpFOrdNotEqual:
619 case spv::Op::OpFUnordNotEqual:
620 case spv::Op::OpFOrdLessThan:
621 case spv::Op::OpFUnordLessThan:
622 case spv::Op::OpFOrdGreaterThan:
623 case spv::Op::OpFUnordGreaterThan:
624 case spv::Op::OpFOrdLessThanEqual:
625 case spv::Op::OpFUnordLessThanEqual:
626 case spv::Op::OpFOrdGreaterThanEqual:
627 case spv::Op::OpFUnordGreaterThanEqual:
628 return true;
629 default:
630 return false;
631 }
632 }
633
spvOpcodeIsDebug(spv::Op opcode)634 bool spvOpcodeIsDebug(spv::Op opcode) {
635 switch (opcode) {
636 case spv::Op::OpName:
637 case spv::Op::OpMemberName:
638 case spv::Op::OpSource:
639 case spv::Op::OpSourceContinued:
640 case spv::Op::OpSourceExtension:
641 case spv::Op::OpString:
642 case spv::Op::OpLine:
643 case spv::Op::OpNoLine:
644 case spv::Op::OpModuleProcessed:
645 return true;
646 default:
647 return false;
648 }
649 }
650
spvOpcodeIsCommutativeBinaryOperator(spv::Op opcode)651 bool spvOpcodeIsCommutativeBinaryOperator(spv::Op opcode) {
652 switch (opcode) {
653 case spv::Op::OpPtrEqual:
654 case spv::Op::OpPtrNotEqual:
655 case spv::Op::OpIAdd:
656 case spv::Op::OpFAdd:
657 case spv::Op::OpIMul:
658 case spv::Op::OpFMul:
659 case spv::Op::OpDot:
660 case spv::Op::OpIAddCarry:
661 case spv::Op::OpUMulExtended:
662 case spv::Op::OpSMulExtended:
663 case spv::Op::OpBitwiseOr:
664 case spv::Op::OpBitwiseXor:
665 case spv::Op::OpBitwiseAnd:
666 case spv::Op::OpOrdered:
667 case spv::Op::OpUnordered:
668 case spv::Op::OpLogicalEqual:
669 case spv::Op::OpLogicalNotEqual:
670 case spv::Op::OpLogicalOr:
671 case spv::Op::OpLogicalAnd:
672 case spv::Op::OpIEqual:
673 case spv::Op::OpINotEqual:
674 case spv::Op::OpFOrdEqual:
675 case spv::Op::OpFUnordEqual:
676 case spv::Op::OpFOrdNotEqual:
677 case spv::Op::OpFUnordNotEqual:
678 return true;
679 default:
680 return false;
681 }
682 }
683
spvOpcodeIsLinearAlgebra(spv::Op opcode)684 bool spvOpcodeIsLinearAlgebra(spv::Op opcode) {
685 switch (opcode) {
686 case spv::Op::OpTranspose:
687 case spv::Op::OpVectorTimesScalar:
688 case spv::Op::OpMatrixTimesScalar:
689 case spv::Op::OpVectorTimesMatrix:
690 case spv::Op::OpMatrixTimesVector:
691 case spv::Op::OpMatrixTimesMatrix:
692 case spv::Op::OpOuterProduct:
693 case spv::Op::OpDot:
694 return true;
695 default:
696 return false;
697 }
698 }
699
spvOpcodeIsImageSample(const spv::Op opcode)700 bool spvOpcodeIsImageSample(const spv::Op opcode) {
701 switch (opcode) {
702 case spv::Op::OpImageSampleImplicitLod:
703 case spv::Op::OpImageSampleExplicitLod:
704 case spv::Op::OpImageSampleDrefImplicitLod:
705 case spv::Op::OpImageSampleDrefExplicitLod:
706 case spv::Op::OpImageSampleProjImplicitLod:
707 case spv::Op::OpImageSampleProjExplicitLod:
708 case spv::Op::OpImageSampleProjDrefImplicitLod:
709 case spv::Op::OpImageSampleProjDrefExplicitLod:
710 case spv::Op::OpImageSparseSampleImplicitLod:
711 case spv::Op::OpImageSparseSampleExplicitLod:
712 case spv::Op::OpImageSparseSampleDrefImplicitLod:
713 case spv::Op::OpImageSparseSampleDrefExplicitLod:
714 return true;
715 default:
716 return false;
717 }
718 }
719
spvOpcodeMemorySemanticsOperandIndices(spv::Op opcode)720 std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(spv::Op opcode) {
721 switch (opcode) {
722 case spv::Op::OpMemoryBarrier:
723 return {1};
724 case spv::Op::OpAtomicStore:
725 case spv::Op::OpControlBarrier:
726 case spv::Op::OpAtomicFlagClear:
727 case spv::Op::OpMemoryNamedBarrier:
728 return {2};
729 case spv::Op::OpAtomicLoad:
730 case spv::Op::OpAtomicExchange:
731 case spv::Op::OpAtomicIIncrement:
732 case spv::Op::OpAtomicIDecrement:
733 case spv::Op::OpAtomicIAdd:
734 case spv::Op::OpAtomicFAddEXT:
735 case spv::Op::OpAtomicISub:
736 case spv::Op::OpAtomicSMin:
737 case spv::Op::OpAtomicUMin:
738 case spv::Op::OpAtomicSMax:
739 case spv::Op::OpAtomicUMax:
740 case spv::Op::OpAtomicAnd:
741 case spv::Op::OpAtomicOr:
742 case spv::Op::OpAtomicXor:
743 case spv::Op::OpAtomicFlagTestAndSet:
744 return {4};
745 case spv::Op::OpAtomicCompareExchange:
746 case spv::Op::OpAtomicCompareExchangeWeak:
747 return {4, 5};
748 default:
749 return {};
750 }
751 }
752
spvOpcodeIsAccessChain(spv::Op opcode)753 bool spvOpcodeIsAccessChain(spv::Op opcode) {
754 switch (opcode) {
755 case spv::Op::OpAccessChain:
756 case spv::Op::OpInBoundsAccessChain:
757 case spv::Op::OpPtrAccessChain:
758 case spv::Op::OpInBoundsPtrAccessChain:
759 case spv::Op::OpRawAccessChainNV:
760 return true;
761 default:
762 return false;
763 }
764 }
765
spvOpcodeIsBit(spv::Op opcode)766 bool spvOpcodeIsBit(spv::Op opcode) {
767 switch (opcode) {
768 case spv::Op::OpShiftRightLogical:
769 case spv::Op::OpShiftRightArithmetic:
770 case spv::Op::OpShiftLeftLogical:
771 case spv::Op::OpBitwiseOr:
772 case spv::Op::OpBitwiseXor:
773 case spv::Op::OpBitwiseAnd:
774 case spv::Op::OpNot:
775 case spv::Op::OpBitReverse:
776 case spv::Op::OpBitCount:
777 return true;
778 default:
779 return false;
780 }
781 }
782