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 consider 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 // Exact match case.
116 if (nameLength == strlen(entry.name) &&
117 !strncmp(name, entry.name, nameLength)) {
118 *pEntry = &entry;
119 return SPV_SUCCESS;
120 }
121 // Lack of binary search really hurts here. There isn't an easy filter to
122 // apply before checking aliases since we need to handle promotion from
123 // vendor to KHR/EXT and KHR/EXT to core. It would require a sure-fire way
124 // of dropping suffices. Fortunately, most lookup are based on token
125 // value.
126 //
127 // If this was a binary search we could iterate between the lower and
128 // upper bounds.
129 if (entry.numAliases > 0) {
130 for (uint32_t aliasIndex = 0; aliasIndex < entry.numAliases;
131 aliasIndex++) {
132 // Skip Op prefix. Should this be encoded in the table instead?
133 const auto alias = entry.aliases[aliasIndex] + 2;
134 const size_t aliasLength = strlen(alias);
135 if (nameLength == aliasLength && !strncmp(name, alias, nameLength)) {
136 *pEntry = &entry;
137 return SPV_SUCCESS;
138 }
139 }
140 }
141 }
142 }
143
144 return SPV_ERROR_INVALID_LOOKUP;
145 }
146
spvOpcodeTableValueLookup(spv_target_env env,const spv_opcode_table table,const spv::Op opcode,spv_opcode_desc * pEntry)147 spv_result_t spvOpcodeTableValueLookup(spv_target_env env,
148 const spv_opcode_table table,
149 const spv::Op opcode,
150 spv_opcode_desc* pEntry) {
151 if (!table) return SPV_ERROR_INVALID_TABLE;
152 if (!pEntry) return SPV_ERROR_INVALID_POINTER;
153
154 const auto beg = table->entries;
155 const auto end = table->entries + table->count;
156
157 spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {}, 0,
158 {}, false, false, 0, nullptr, ~0u, ~0u};
159
160 auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
161 return lhs.opcode < rhs.opcode;
162 };
163
164 // We need to loop here because there can exist multiple symbols for the same
165 // opcode value, and they can be introduced in different target environments,
166 // which means they can have different minimal version requirements.
167 // Assumes the underlying table is already sorted ascendingly according to
168 // opcode value.
169 const auto version = spvVersionForTargetEnv(env);
170 for (auto it = std::lower_bound(beg, end, needle, comp);
171 it != end && it->opcode == opcode; ++it) {
172 // We considers the current opcode as available as long as
173 // 1. The target environment satisfies the minimal requirement of the
174 // opcode; or
175 // 2. There is at least one extension enabling this opcode.
176 //
177 // Note that the second rule assumes the extension enabling this instruction
178 // is indeed requested in the SPIR-V code; checking that should be
179 // validator's work.
180 if ((version >= it->minVersion && version <= it->lastVersion) ||
181 it->numExtensions > 0u || it->numCapabilities > 0u) {
182 *pEntry = it;
183 return SPV_SUCCESS;
184 }
185 }
186
187 return SPV_ERROR_INVALID_LOOKUP;
188 }
189
spvInstructionCopy(const uint32_t * words,const spv::Op opcode,const uint16_t wordCount,const spv_endianness_t endian,spv_instruction_t * pInst)190 void spvInstructionCopy(const uint32_t* words, const spv::Op opcode,
191 const uint16_t wordCount, const spv_endianness_t endian,
192 spv_instruction_t* pInst) {
193 pInst->opcode = opcode;
194 pInst->words.resize(wordCount);
195 for (uint16_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) {
196 pInst->words[wordIndex] = spvFixWord(words[wordIndex], endian);
197 if (!wordIndex) {
198 uint16_t thisWordCount;
199 uint16_t thisOpcode;
200 spvOpcodeSplit(pInst->words[wordIndex], &thisWordCount, &thisOpcode);
201 assert(opcode == static_cast<spv::Op>(thisOpcode) &&
202 wordCount == thisWordCount && "Endianness failed!");
203 }
204 }
205 }
206
spvOpcodeString(const uint32_t opcode)207 const char* spvOpcodeString(const uint32_t opcode) {
208 const auto beg = kOpcodeTableEntries;
209 const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries);
210 spv_opcode_desc_t needle = {"", static_cast<spv::Op>(opcode),
211 0, nullptr,
212 0, {},
213 0, {},
214 false, false,
215 0, nullptr,
216 ~0u, ~0u};
217 auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
218 return lhs.opcode < rhs.opcode;
219 };
220 auto it = std::lower_bound(beg, end, needle, comp);
221 if (it != end && it->opcode == spv::Op(opcode)) {
222 return it->name;
223 }
224
225 assert(0 && "Unreachable!");
226 return "unknown";
227 }
228
spvOpcodeString(const spv::Op opcode)229 const char* spvOpcodeString(const spv::Op opcode) {
230 return spvOpcodeString(static_cast<uint32_t>(opcode));
231 }
232
spvOpcodeIsScalarType(const spv::Op opcode)233 int32_t spvOpcodeIsScalarType(const spv::Op opcode) {
234 switch (opcode) {
235 case spv::Op::OpTypeInt:
236 case spv::Op::OpTypeFloat:
237 case spv::Op::OpTypeBool:
238 return true;
239 default:
240 return false;
241 }
242 }
243
spvOpcodeIsSpecConstant(const spv::Op opcode)244 int32_t spvOpcodeIsSpecConstant(const spv::Op opcode) {
245 switch (opcode) {
246 case spv::Op::OpSpecConstantTrue:
247 case spv::Op::OpSpecConstantFalse:
248 case spv::Op::OpSpecConstant:
249 case spv::Op::OpSpecConstantComposite:
250 case spv::Op::OpSpecConstantCompositeReplicateEXT:
251 case spv::Op::OpSpecConstantOp:
252 return true;
253 default:
254 return false;
255 }
256 }
257
spvOpcodeIsConstant(const spv::Op opcode)258 int32_t spvOpcodeIsConstant(const spv::Op opcode) {
259 switch (opcode) {
260 case spv::Op::OpConstantTrue:
261 case spv::Op::OpConstantFalse:
262 case spv::Op::OpConstant:
263 case spv::Op::OpConstantComposite:
264 case spv::Op::OpConstantCompositeReplicateEXT:
265 case spv::Op::OpConstantSampler:
266 case spv::Op::OpConstantNull:
267 case spv::Op::OpConstantFunctionPointerINTEL:
268 case spv::Op::OpSpecConstantTrue:
269 case spv::Op::OpSpecConstantFalse:
270 case spv::Op::OpSpecConstant:
271 case spv::Op::OpSpecConstantComposite:
272 case spv::Op::OpSpecConstantCompositeReplicateEXT:
273 case spv::Op::OpSpecConstantOp:
274 return true;
275 default:
276 return false;
277 }
278 }
279
spvOpcodeIsConstantOrUndef(const spv::Op opcode)280 bool spvOpcodeIsConstantOrUndef(const spv::Op opcode) {
281 return opcode == spv::Op::OpUndef || spvOpcodeIsConstant(opcode);
282 }
283
spvOpcodeIsScalarSpecConstant(const spv::Op opcode)284 bool spvOpcodeIsScalarSpecConstant(const spv::Op opcode) {
285 switch (opcode) {
286 case spv::Op::OpSpecConstantTrue:
287 case spv::Op::OpSpecConstantFalse:
288 case spv::Op::OpSpecConstant:
289 return true;
290 default:
291 return false;
292 }
293 }
294
spvOpcodeIsComposite(const spv::Op opcode)295 int32_t spvOpcodeIsComposite(const spv::Op opcode) {
296 switch (opcode) {
297 case spv::Op::OpTypeVector:
298 case spv::Op::OpTypeMatrix:
299 case spv::Op::OpTypeArray:
300 case spv::Op::OpTypeStruct:
301 case spv::Op::OpTypeRuntimeArray:
302 case spv::Op::OpTypeCooperativeMatrixNV:
303 case spv::Op::OpTypeCooperativeMatrixKHR:
304 return true;
305 default:
306 return false;
307 }
308 }
309
spvOpcodeReturnsLogicalVariablePointer(const spv::Op opcode)310 bool spvOpcodeReturnsLogicalVariablePointer(const spv::Op opcode) {
311 switch (opcode) {
312 case spv::Op::OpVariable:
313 case spv::Op::OpUntypedVariableKHR:
314 case spv::Op::OpAccessChain:
315 case spv::Op::OpInBoundsAccessChain:
316 case spv::Op::OpUntypedAccessChainKHR:
317 case spv::Op::OpUntypedInBoundsAccessChainKHR:
318 case spv::Op::OpFunctionParameter:
319 case spv::Op::OpImageTexelPointer:
320 case spv::Op::OpCopyObject:
321 case spv::Op::OpSelect:
322 case spv::Op::OpPhi:
323 case spv::Op::OpFunctionCall:
324 case spv::Op::OpPtrAccessChain:
325 case spv::Op::OpUntypedPtrAccessChainKHR:
326 case spv::Op::OpLoad:
327 case spv::Op::OpConstantNull:
328 case spv::Op::OpRawAccessChainNV:
329 return true;
330 default:
331 return false;
332 }
333 }
334
spvOpcodeReturnsLogicalPointer(const spv::Op opcode)335 int32_t spvOpcodeReturnsLogicalPointer(const spv::Op opcode) {
336 switch (opcode) {
337 case spv::Op::OpVariable:
338 case spv::Op::OpUntypedVariableKHR:
339 case spv::Op::OpAccessChain:
340 case spv::Op::OpInBoundsAccessChain:
341 case spv::Op::OpUntypedAccessChainKHR:
342 case spv::Op::OpUntypedInBoundsAccessChainKHR:
343 case spv::Op::OpFunctionParameter:
344 case spv::Op::OpImageTexelPointer:
345 case spv::Op::OpCopyObject:
346 case spv::Op::OpRawAccessChainNV:
347 return true;
348 default:
349 return false;
350 }
351 }
352
spvOpcodeGeneratesType(spv::Op op)353 int32_t spvOpcodeGeneratesType(spv::Op op) {
354 switch (op) {
355 case spv::Op::OpTypeVoid:
356 case spv::Op::OpTypeBool:
357 case spv::Op::OpTypeInt:
358 case spv::Op::OpTypeFloat:
359 case spv::Op::OpTypeVector:
360 case spv::Op::OpTypeMatrix:
361 case spv::Op::OpTypeImage:
362 case spv::Op::OpTypeSampler:
363 case spv::Op::OpTypeSampledImage:
364 case spv::Op::OpTypeArray:
365 case spv::Op::OpTypeRuntimeArray:
366 case spv::Op::OpTypeStruct:
367 case spv::Op::OpTypeOpaque:
368 case spv::Op::OpTypePointer:
369 case spv::Op::OpTypeFunction:
370 case spv::Op::OpTypeEvent:
371 case spv::Op::OpTypeDeviceEvent:
372 case spv::Op::OpTypeReserveId:
373 case spv::Op::OpTypeQueue:
374 case spv::Op::OpTypePipe:
375 case spv::Op::OpTypePipeStorage:
376 case spv::Op::OpTypeNamedBarrier:
377 case spv::Op::OpTypeAccelerationStructureNV:
378 case spv::Op::OpTypeCooperativeMatrixNV:
379 case spv::Op::OpTypeCooperativeMatrixKHR:
380 // case spv::Op::OpTypeAccelerationStructureKHR: covered by
381 // spv::Op::OpTypeAccelerationStructureNV
382 case spv::Op::OpTypeRayQueryKHR:
383 case spv::Op::OpTypeHitObjectNV:
384 case spv::Op::OpTypeUntypedPointerKHR:
385 case spv::Op::OpTypeTensorLayoutNV:
386 case spv::Op::OpTypeTensorViewNV:
387 return true;
388 default:
389 // In particular, OpTypeForwardPointer does not generate a type,
390 // but declares a storage class for a pointer type generated
391 // by a different instruction.
392 break;
393 }
394 return 0;
395 }
396
spvOpcodeIsDecoration(const spv::Op opcode)397 bool spvOpcodeIsDecoration(const spv::Op opcode) {
398 switch (opcode) {
399 case spv::Op::OpDecorate:
400 case spv::Op::OpDecorateId:
401 case spv::Op::OpMemberDecorate:
402 case spv::Op::OpGroupDecorate:
403 case spv::Op::OpGroupMemberDecorate:
404 case spv::Op::OpDecorateStringGOOGLE:
405 case spv::Op::OpMemberDecorateStringGOOGLE:
406 return true;
407 default:
408 break;
409 }
410 return false;
411 }
412
spvOpcodeIsLoad(const spv::Op opcode)413 bool spvOpcodeIsLoad(const spv::Op opcode) {
414 switch (opcode) {
415 case spv::Op::OpLoad:
416 case spv::Op::OpImageSampleExplicitLod:
417 case spv::Op::OpImageSampleImplicitLod:
418 case spv::Op::OpImageSampleDrefImplicitLod:
419 case spv::Op::OpImageSampleDrefExplicitLod:
420 case spv::Op::OpImageSampleProjImplicitLod:
421 case spv::Op::OpImageSampleProjExplicitLod:
422 case spv::Op::OpImageSampleProjDrefImplicitLod:
423 case spv::Op::OpImageSampleProjDrefExplicitLod:
424 case spv::Op::OpImageFetch:
425 case spv::Op::OpImageGather:
426 case spv::Op::OpImageDrefGather:
427 case spv::Op::OpImageRead:
428 case spv::Op::OpImageSparseSampleImplicitLod:
429 case spv::Op::OpImageSparseSampleExplicitLod:
430 case spv::Op::OpImageSparseSampleDrefExplicitLod:
431 case spv::Op::OpImageSparseSampleDrefImplicitLod:
432 case spv::Op::OpImageSparseFetch:
433 case spv::Op::OpImageSparseGather:
434 case spv::Op::OpImageSparseDrefGather:
435 case spv::Op::OpImageSparseRead:
436 return true;
437 default:
438 return false;
439 }
440 }
441
spvOpcodeIsBranch(spv::Op opcode)442 bool spvOpcodeIsBranch(spv::Op opcode) {
443 switch (opcode) {
444 case spv::Op::OpBranch:
445 case spv::Op::OpBranchConditional:
446 case spv::Op::OpSwitch:
447 return true;
448 default:
449 return false;
450 }
451 }
452
spvOpcodeIsAtomicWithLoad(const spv::Op opcode)453 bool spvOpcodeIsAtomicWithLoad(const spv::Op opcode) {
454 switch (opcode) {
455 case spv::Op::OpAtomicLoad:
456 case spv::Op::OpAtomicExchange:
457 case spv::Op::OpAtomicCompareExchange:
458 case spv::Op::OpAtomicCompareExchangeWeak:
459 case spv::Op::OpAtomicIIncrement:
460 case spv::Op::OpAtomicIDecrement:
461 case spv::Op::OpAtomicIAdd:
462 case spv::Op::OpAtomicFAddEXT:
463 case spv::Op::OpAtomicISub:
464 case spv::Op::OpAtomicSMin:
465 case spv::Op::OpAtomicUMin:
466 case spv::Op::OpAtomicFMinEXT:
467 case spv::Op::OpAtomicSMax:
468 case spv::Op::OpAtomicUMax:
469 case spv::Op::OpAtomicFMaxEXT:
470 case spv::Op::OpAtomicAnd:
471 case spv::Op::OpAtomicOr:
472 case spv::Op::OpAtomicXor:
473 case spv::Op::OpAtomicFlagTestAndSet:
474 return true;
475 default:
476 return false;
477 }
478 }
479
spvOpcodeIsAtomicOp(const spv::Op opcode)480 bool spvOpcodeIsAtomicOp(const spv::Op opcode) {
481 return (spvOpcodeIsAtomicWithLoad(opcode) ||
482 opcode == spv::Op::OpAtomicStore ||
483 opcode == spv::Op::OpAtomicFlagClear);
484 }
485
spvOpcodeIsReturn(spv::Op opcode)486 bool spvOpcodeIsReturn(spv::Op opcode) {
487 switch (opcode) {
488 case spv::Op::OpReturn:
489 case spv::Op::OpReturnValue:
490 return true;
491 default:
492 return false;
493 }
494 }
495
spvOpcodeIsAbort(spv::Op opcode)496 bool spvOpcodeIsAbort(spv::Op opcode) {
497 switch (opcode) {
498 case spv::Op::OpKill:
499 case spv::Op::OpUnreachable:
500 case spv::Op::OpTerminateInvocation:
501 case spv::Op::OpTerminateRayKHR:
502 case spv::Op::OpIgnoreIntersectionKHR:
503 case spv::Op::OpEmitMeshTasksEXT:
504 return true;
505 default:
506 return false;
507 }
508 }
509
spvOpcodeIsReturnOrAbort(spv::Op opcode)510 bool spvOpcodeIsReturnOrAbort(spv::Op opcode) {
511 return spvOpcodeIsReturn(opcode) || spvOpcodeIsAbort(opcode);
512 }
513
spvOpcodeIsBlockTerminator(spv::Op opcode)514 bool spvOpcodeIsBlockTerminator(spv::Op opcode) {
515 return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode);
516 }
517
spvOpcodeIsBaseOpaqueType(spv::Op opcode)518 bool spvOpcodeIsBaseOpaqueType(spv::Op opcode) {
519 switch (opcode) {
520 case spv::Op::OpTypeImage:
521 case spv::Op::OpTypeSampler:
522 case spv::Op::OpTypeSampledImage:
523 case spv::Op::OpTypeOpaque:
524 case spv::Op::OpTypeEvent:
525 case spv::Op::OpTypeDeviceEvent:
526 case spv::Op::OpTypeReserveId:
527 case spv::Op::OpTypeQueue:
528 case spv::Op::OpTypePipe:
529 case spv::Op::OpTypeForwardPointer:
530 case spv::Op::OpTypePipeStorage:
531 case spv::Op::OpTypeNamedBarrier:
532 return true;
533 default:
534 return false;
535 }
536 }
537
spvOpcodeIsNonUniformGroupOperation(spv::Op opcode)538 bool spvOpcodeIsNonUniformGroupOperation(spv::Op opcode) {
539 switch (opcode) {
540 case spv::Op::OpGroupNonUniformElect:
541 case spv::Op::OpGroupNonUniformAll:
542 case spv::Op::OpGroupNonUniformAny:
543 case spv::Op::OpGroupNonUniformAllEqual:
544 case spv::Op::OpGroupNonUniformBroadcast:
545 case spv::Op::OpGroupNonUniformBroadcastFirst:
546 case spv::Op::OpGroupNonUniformBallot:
547 case spv::Op::OpGroupNonUniformInverseBallot:
548 case spv::Op::OpGroupNonUniformBallotBitExtract:
549 case spv::Op::OpGroupNonUniformBallotBitCount:
550 case spv::Op::OpGroupNonUniformBallotFindLSB:
551 case spv::Op::OpGroupNonUniformBallotFindMSB:
552 case spv::Op::OpGroupNonUniformShuffle:
553 case spv::Op::OpGroupNonUniformShuffleXor:
554 case spv::Op::OpGroupNonUniformShuffleUp:
555 case spv::Op::OpGroupNonUniformShuffleDown:
556 case spv::Op::OpGroupNonUniformIAdd:
557 case spv::Op::OpGroupNonUniformFAdd:
558 case spv::Op::OpGroupNonUniformIMul:
559 case spv::Op::OpGroupNonUniformFMul:
560 case spv::Op::OpGroupNonUniformSMin:
561 case spv::Op::OpGroupNonUniformUMin:
562 case spv::Op::OpGroupNonUniformFMin:
563 case spv::Op::OpGroupNonUniformSMax:
564 case spv::Op::OpGroupNonUniformUMax:
565 case spv::Op::OpGroupNonUniformFMax:
566 case spv::Op::OpGroupNonUniformBitwiseAnd:
567 case spv::Op::OpGroupNonUniformBitwiseOr:
568 case spv::Op::OpGroupNonUniformBitwiseXor:
569 case spv::Op::OpGroupNonUniformLogicalAnd:
570 case spv::Op::OpGroupNonUniformLogicalOr:
571 case spv::Op::OpGroupNonUniformLogicalXor:
572 case spv::Op::OpGroupNonUniformQuadBroadcast:
573 case spv::Op::OpGroupNonUniformQuadSwap:
574 case spv::Op::OpGroupNonUniformRotateKHR:
575 case spv::Op::OpGroupNonUniformQuadAllKHR:
576 case spv::Op::OpGroupNonUniformQuadAnyKHR:
577 return true;
578 default:
579 return false;
580 }
581 }
582
spvOpcodeIsScalarizable(spv::Op opcode)583 bool spvOpcodeIsScalarizable(spv::Op opcode) {
584 switch (opcode) {
585 case spv::Op::OpPhi:
586 case spv::Op::OpCopyObject:
587 case spv::Op::OpConvertFToU:
588 case spv::Op::OpConvertFToS:
589 case spv::Op::OpConvertSToF:
590 case spv::Op::OpConvertUToF:
591 case spv::Op::OpUConvert:
592 case spv::Op::OpSConvert:
593 case spv::Op::OpFConvert:
594 case spv::Op::OpQuantizeToF16:
595 case spv::Op::OpVectorInsertDynamic:
596 case spv::Op::OpSNegate:
597 case spv::Op::OpFNegate:
598 case spv::Op::OpIAdd:
599 case spv::Op::OpFAdd:
600 case spv::Op::OpISub:
601 case spv::Op::OpFSub:
602 case spv::Op::OpIMul:
603 case spv::Op::OpFMul:
604 case spv::Op::OpUDiv:
605 case spv::Op::OpSDiv:
606 case spv::Op::OpFDiv:
607 case spv::Op::OpUMod:
608 case spv::Op::OpSRem:
609 case spv::Op::OpSMod:
610 case spv::Op::OpFRem:
611 case spv::Op::OpFMod:
612 case spv::Op::OpVectorTimesScalar:
613 case spv::Op::OpIAddCarry:
614 case spv::Op::OpISubBorrow:
615 case spv::Op::OpUMulExtended:
616 case spv::Op::OpSMulExtended:
617 case spv::Op::OpShiftRightLogical:
618 case spv::Op::OpShiftRightArithmetic:
619 case spv::Op::OpShiftLeftLogical:
620 case spv::Op::OpBitwiseOr:
621 case spv::Op::OpBitwiseAnd:
622 case spv::Op::OpNot:
623 case spv::Op::OpBitFieldInsert:
624 case spv::Op::OpBitFieldSExtract:
625 case spv::Op::OpBitFieldUExtract:
626 case spv::Op::OpBitReverse:
627 case spv::Op::OpBitCount:
628 case spv::Op::OpIsNan:
629 case spv::Op::OpIsInf:
630 case spv::Op::OpIsFinite:
631 case spv::Op::OpIsNormal:
632 case spv::Op::OpSignBitSet:
633 case spv::Op::OpLessOrGreater:
634 case spv::Op::OpOrdered:
635 case spv::Op::OpUnordered:
636 case spv::Op::OpLogicalEqual:
637 case spv::Op::OpLogicalNotEqual:
638 case spv::Op::OpLogicalOr:
639 case spv::Op::OpLogicalAnd:
640 case spv::Op::OpLogicalNot:
641 case spv::Op::OpSelect:
642 case spv::Op::OpIEqual:
643 case spv::Op::OpINotEqual:
644 case spv::Op::OpUGreaterThan:
645 case spv::Op::OpSGreaterThan:
646 case spv::Op::OpUGreaterThanEqual:
647 case spv::Op::OpSGreaterThanEqual:
648 case spv::Op::OpULessThan:
649 case spv::Op::OpSLessThan:
650 case spv::Op::OpULessThanEqual:
651 case spv::Op::OpSLessThanEqual:
652 case spv::Op::OpFOrdEqual:
653 case spv::Op::OpFUnordEqual:
654 case spv::Op::OpFOrdNotEqual:
655 case spv::Op::OpFUnordNotEqual:
656 case spv::Op::OpFOrdLessThan:
657 case spv::Op::OpFUnordLessThan:
658 case spv::Op::OpFOrdGreaterThan:
659 case spv::Op::OpFUnordGreaterThan:
660 case spv::Op::OpFOrdLessThanEqual:
661 case spv::Op::OpFUnordLessThanEqual:
662 case spv::Op::OpFOrdGreaterThanEqual:
663 case spv::Op::OpFUnordGreaterThanEqual:
664 return true;
665 default:
666 return false;
667 }
668 }
669
spvOpcodeIsDebug(spv::Op opcode)670 bool spvOpcodeIsDebug(spv::Op opcode) {
671 switch (opcode) {
672 case spv::Op::OpName:
673 case spv::Op::OpMemberName:
674 case spv::Op::OpSource:
675 case spv::Op::OpSourceContinued:
676 case spv::Op::OpSourceExtension:
677 case spv::Op::OpString:
678 case spv::Op::OpLine:
679 case spv::Op::OpNoLine:
680 case spv::Op::OpModuleProcessed:
681 return true;
682 default:
683 return false;
684 }
685 }
686
spvOpcodeIsCommutativeBinaryOperator(spv::Op opcode)687 bool spvOpcodeIsCommutativeBinaryOperator(spv::Op opcode) {
688 switch (opcode) {
689 case spv::Op::OpPtrEqual:
690 case spv::Op::OpPtrNotEqual:
691 case spv::Op::OpIAdd:
692 case spv::Op::OpFAdd:
693 case spv::Op::OpIMul:
694 case spv::Op::OpFMul:
695 case spv::Op::OpDot:
696 case spv::Op::OpIAddCarry:
697 case spv::Op::OpUMulExtended:
698 case spv::Op::OpSMulExtended:
699 case spv::Op::OpBitwiseOr:
700 case spv::Op::OpBitwiseXor:
701 case spv::Op::OpBitwiseAnd:
702 case spv::Op::OpOrdered:
703 case spv::Op::OpUnordered:
704 case spv::Op::OpLogicalEqual:
705 case spv::Op::OpLogicalNotEqual:
706 case spv::Op::OpLogicalOr:
707 case spv::Op::OpLogicalAnd:
708 case spv::Op::OpIEqual:
709 case spv::Op::OpINotEqual:
710 case spv::Op::OpFOrdEqual:
711 case spv::Op::OpFUnordEqual:
712 case spv::Op::OpFOrdNotEqual:
713 case spv::Op::OpFUnordNotEqual:
714 return true;
715 default:
716 return false;
717 }
718 }
719
spvOpcodeIsLinearAlgebra(spv::Op opcode)720 bool spvOpcodeIsLinearAlgebra(spv::Op opcode) {
721 switch (opcode) {
722 case spv::Op::OpTranspose:
723 case spv::Op::OpVectorTimesScalar:
724 case spv::Op::OpMatrixTimesScalar:
725 case spv::Op::OpVectorTimesMatrix:
726 case spv::Op::OpMatrixTimesVector:
727 case spv::Op::OpMatrixTimesMatrix:
728 case spv::Op::OpOuterProduct:
729 case spv::Op::OpDot:
730 return true;
731 default:
732 return false;
733 }
734 }
735
spvOpcodeIsImageSample(const spv::Op opcode)736 bool spvOpcodeIsImageSample(const spv::Op opcode) {
737 switch (opcode) {
738 case spv::Op::OpImageSampleImplicitLod:
739 case spv::Op::OpImageSampleExplicitLod:
740 case spv::Op::OpImageSampleDrefImplicitLod:
741 case spv::Op::OpImageSampleDrefExplicitLod:
742 case spv::Op::OpImageSampleProjImplicitLod:
743 case spv::Op::OpImageSampleProjExplicitLod:
744 case spv::Op::OpImageSampleProjDrefImplicitLod:
745 case spv::Op::OpImageSampleProjDrefExplicitLod:
746 case spv::Op::OpImageSparseSampleImplicitLod:
747 case spv::Op::OpImageSparseSampleExplicitLod:
748 case spv::Op::OpImageSparseSampleDrefImplicitLod:
749 case spv::Op::OpImageSparseSampleDrefExplicitLod:
750 return true;
751 default:
752 return false;
753 }
754 }
755
spvIsExtendedInstruction(const spv::Op opcode)756 bool spvIsExtendedInstruction(const spv::Op opcode) {
757 switch (opcode) {
758 case spv::Op::OpExtInst:
759 case spv::Op::OpExtInstWithForwardRefsKHR:
760 return true;
761 default:
762 return false;
763 }
764 }
765
spvOpcodeMemorySemanticsOperandIndices(spv::Op opcode)766 std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(spv::Op opcode) {
767 switch (opcode) {
768 case spv::Op::OpMemoryBarrier:
769 return {1};
770 case spv::Op::OpAtomicStore:
771 case spv::Op::OpControlBarrier:
772 case spv::Op::OpAtomicFlagClear:
773 case spv::Op::OpMemoryNamedBarrier:
774 return {2};
775 case spv::Op::OpAtomicLoad:
776 case spv::Op::OpAtomicExchange:
777 case spv::Op::OpAtomicIIncrement:
778 case spv::Op::OpAtomicIDecrement:
779 case spv::Op::OpAtomicIAdd:
780 case spv::Op::OpAtomicFAddEXT:
781 case spv::Op::OpAtomicISub:
782 case spv::Op::OpAtomicSMin:
783 case spv::Op::OpAtomicUMin:
784 case spv::Op::OpAtomicSMax:
785 case spv::Op::OpAtomicUMax:
786 case spv::Op::OpAtomicAnd:
787 case spv::Op::OpAtomicOr:
788 case spv::Op::OpAtomicXor:
789 case spv::Op::OpAtomicFlagTestAndSet:
790 return {4};
791 case spv::Op::OpAtomicCompareExchange:
792 case spv::Op::OpAtomicCompareExchangeWeak:
793 return {4, 5};
794 default:
795 return {};
796 }
797 }
798
spvOpcodeIsAccessChain(spv::Op opcode)799 bool spvOpcodeIsAccessChain(spv::Op opcode) {
800 switch (opcode) {
801 case spv::Op::OpAccessChain:
802 case spv::Op::OpInBoundsAccessChain:
803 case spv::Op::OpPtrAccessChain:
804 case spv::Op::OpInBoundsPtrAccessChain:
805 case spv::Op::OpRawAccessChainNV:
806 return true;
807 default:
808 return false;
809 }
810 }
811
spvOpcodeIsBit(spv::Op opcode)812 bool spvOpcodeIsBit(spv::Op opcode) {
813 switch (opcode) {
814 case spv::Op::OpShiftRightLogical:
815 case spv::Op::OpShiftRightArithmetic:
816 case spv::Op::OpShiftLeftLogical:
817 case spv::Op::OpBitwiseOr:
818 case spv::Op::OpBitwiseXor:
819 case spv::Op::OpBitwiseAnd:
820 case spv::Op::OpNot:
821 case spv::Op::OpBitReverse:
822 case spv::Op::OpBitCount:
823 return true;
824 default:
825 return false;
826 }
827 }
828
spvOpcodeGeneratesUntypedPointer(spv::Op opcode)829 bool spvOpcodeGeneratesUntypedPointer(spv::Op opcode) {
830 switch (opcode) {
831 case spv::Op::OpUntypedVariableKHR:
832 case spv::Op::OpUntypedAccessChainKHR:
833 case spv::Op::OpUntypedInBoundsAccessChainKHR:
834 case spv::Op::OpUntypedPtrAccessChainKHR:
835 case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
836 return true;
837 default:
838 return false;
839 }
840 }
841