1 // Copyright (c) 2015-2016 The Khronos Group Inc.
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 "source/opcode.h"
16
17 #include <assert.h>
18 #include <string.h>
19
20 #include <algorithm>
21 #include <cstdlib>
22
23 #include "source/instruction.h"
24 #include "source/macro.h"
25 #include "source/spirv_constant.h"
26 #include "source/spirv_endian.h"
27 #include "source/spirv_target_env.h"
28 #include "spirv-tools/libspirv.h"
29
30 namespace {
31 struct OpcodeDescPtrLen {
32 const spv_opcode_desc_t* ptr;
33 uint32_t len;
34 };
35
36 #include "core.insts-unified1.inc"
37
38 static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries),
39 kOpcodeTableEntries};
40
41 // Represents a vendor tool entry in the SPIR-V XML Regsitry.
42 struct VendorTool {
43 uint32_t value;
44 const char* vendor;
45 const char* tool; // Might be empty string.
46 const char* vendor_tool; // Combiantion of vendor and tool.
47 };
48
49 const VendorTool vendor_tools[] = {
50 #include "generators.inc"
51 };
52
53 } // anonymous namespace
54
55 // TODO(dneto): Move this to another file. It doesn't belong with opcode
56 // processing.
spvGeneratorStr(uint32_t generator)57 const char* spvGeneratorStr(uint32_t generator) {
58 auto where = std::find_if(
59 std::begin(vendor_tools), std::end(vendor_tools),
60 [generator](const VendorTool& vt) { return generator == vt.value; });
61 if (where != std::end(vendor_tools)) return where->vendor_tool;
62 return "Unknown";
63 }
64
spvOpcodeMake(uint16_t wordCount,SpvOp opcode)65 uint32_t spvOpcodeMake(uint16_t wordCount, SpvOp opcode) {
66 return ((uint32_t)opcode) | (((uint32_t)wordCount) << 16);
67 }
68
spvOpcodeSplit(const uint32_t word,uint16_t * pWordCount,uint16_t * pOpcode)69 void spvOpcodeSplit(const uint32_t word, uint16_t* pWordCount,
70 uint16_t* pOpcode) {
71 if (pWordCount) {
72 *pWordCount = (uint16_t)((0xffff0000 & word) >> 16);
73 }
74 if (pOpcode) {
75 *pOpcode = 0x0000ffff & word;
76 }
77 }
78
spvOpcodeTableGet(spv_opcode_table * pInstTable,spv_target_env)79 spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable, spv_target_env) {
80 if (!pInstTable) return SPV_ERROR_INVALID_POINTER;
81
82 // Descriptions of each opcode. Each entry describes the format of the
83 // instruction that follows a particular opcode.
84
85 *pInstTable = &kOpcodeTable;
86 return SPV_SUCCESS;
87 }
88
spvOpcodeTableNameLookup(spv_target_env env,const spv_opcode_table table,const char * name,spv_opcode_desc * pEntry)89 spv_result_t spvOpcodeTableNameLookup(spv_target_env env,
90 const spv_opcode_table table,
91 const char* name,
92 spv_opcode_desc* pEntry) {
93 if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER;
94 if (!table) return SPV_ERROR_INVALID_TABLE;
95
96 // TODO: This lookup of the Opcode table is suboptimal! Binary sort would be
97 // preferable but the table requires sorting on the Opcode name, but it's
98 // static const initialized and matches the order of the spec.
99 const size_t nameLength = strlen(name);
100 for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) {
101 const spv_opcode_desc_t& entry = table->entries[opcodeIndex];
102 // We considers the current opcode as available as long as
103 // 1. The target environment satisfies the minimal requirement of the
104 // opcode; or
105 // 2. There is at least one extension enabling this opcode.
106 //
107 // Note that the second rule assumes the extension enabling this instruction
108 // is indeed requested in the SPIR-V code; checking that should be
109 // validator's work.
110 if ((spvVersionForTargetEnv(env) >= entry.minVersion ||
111 entry.numExtensions > 0u || entry.numCapabilities > 0u) &&
112 nameLength == strlen(entry.name) &&
113 !strncmp(name, entry.name, nameLength)) {
114 // NOTE: Found out Opcode!
115 *pEntry = &entry;
116 return SPV_SUCCESS;
117 }
118 }
119
120 return SPV_ERROR_INVALID_LOOKUP;
121 }
122
spvOpcodeTableValueLookup(spv_target_env env,const spv_opcode_table table,const SpvOp opcode,spv_opcode_desc * pEntry)123 spv_result_t spvOpcodeTableValueLookup(spv_target_env env,
124 const spv_opcode_table table,
125 const SpvOp opcode,
126 spv_opcode_desc* pEntry) {
127 if (!table) return SPV_ERROR_INVALID_TABLE;
128 if (!pEntry) return SPV_ERROR_INVALID_POINTER;
129
130 const auto beg = table->entries;
131 const auto end = table->entries + table->count;
132
133 spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {},
134 false, false, 0, nullptr, ~0u};
135
136 auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
137 return lhs.opcode < rhs.opcode;
138 };
139
140 // We need to loop here because there can exist multiple symbols for the same
141 // opcode value, and they can be introduced in different target environments,
142 // which means they can have different minimal version requirements.
143 // Assumes the underlying table is already sorted ascendingly according to
144 // opcode value.
145 for (auto it = std::lower_bound(beg, end, needle, comp);
146 it != end && it->opcode == opcode; ++it) {
147 // We considers the current opcode as available as long as
148 // 1. The target environment satisfies the minimal requirement of the
149 // opcode; or
150 // 2. There is at least one extension enabling this opcode.
151 //
152 // Note that the second rule assumes the extension enabling this instruction
153 // is indeed requested in the SPIR-V code; checking that should be
154 // validator's work.
155 if (spvVersionForTargetEnv(env) >= it->minVersion ||
156 it->numExtensions > 0u || it->numCapabilities > 0u) {
157 *pEntry = it;
158 return SPV_SUCCESS;
159 }
160 }
161
162 return SPV_ERROR_INVALID_LOOKUP;
163 }
164
spvInstructionCopy(const uint32_t * words,const SpvOp opcode,const uint16_t wordCount,const spv_endianness_t endian,spv_instruction_t * pInst)165 void spvInstructionCopy(const uint32_t* words, const SpvOp opcode,
166 const uint16_t wordCount, const spv_endianness_t endian,
167 spv_instruction_t* pInst) {
168 pInst->opcode = opcode;
169 pInst->words.resize(wordCount);
170 for (uint16_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) {
171 pInst->words[wordIndex] = spvFixWord(words[wordIndex], endian);
172 if (!wordIndex) {
173 uint16_t thisWordCount;
174 uint16_t thisOpcode;
175 spvOpcodeSplit(pInst->words[wordIndex], &thisWordCount, &thisOpcode);
176 assert(opcode == static_cast<SpvOp>(thisOpcode) &&
177 wordCount == thisWordCount && "Endianness failed!");
178 }
179 }
180 }
181
spvOpcodeString(const SpvOp opcode)182 const char* spvOpcodeString(const SpvOp opcode) {
183 const auto beg = kOpcodeTableEntries;
184 const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries);
185 spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {},
186 false, false, 0, nullptr, ~0u};
187 auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
188 return lhs.opcode < rhs.opcode;
189 };
190 auto it = std::lower_bound(beg, end, needle, comp);
191 if (it != end && it->opcode == opcode) {
192 return it->name;
193 }
194
195 assert(0 && "Unreachable!");
196 return "unknown";
197 }
198
spvOpcodeIsScalarType(const SpvOp opcode)199 int32_t spvOpcodeIsScalarType(const SpvOp opcode) {
200 switch (opcode) {
201 case SpvOpTypeInt:
202 case SpvOpTypeFloat:
203 case SpvOpTypeBool:
204 return true;
205 default:
206 return false;
207 }
208 }
209
spvOpcodeIsSpecConstant(const SpvOp opcode)210 int32_t spvOpcodeIsSpecConstant(const SpvOp opcode) {
211 switch (opcode) {
212 case SpvOpSpecConstantTrue:
213 case SpvOpSpecConstantFalse:
214 case SpvOpSpecConstant:
215 case SpvOpSpecConstantComposite:
216 case SpvOpSpecConstantOp:
217 return true;
218 default:
219 return false;
220 }
221 }
222
spvOpcodeIsConstant(const SpvOp opcode)223 int32_t spvOpcodeIsConstant(const SpvOp opcode) {
224 switch (opcode) {
225 case SpvOpConstantTrue:
226 case SpvOpConstantFalse:
227 case SpvOpConstant:
228 case SpvOpConstantComposite:
229 case SpvOpConstantSampler:
230 case SpvOpConstantNull:
231 case SpvOpSpecConstantTrue:
232 case SpvOpSpecConstantFalse:
233 case SpvOpSpecConstant:
234 case SpvOpSpecConstantComposite:
235 case SpvOpSpecConstantOp:
236 return true;
237 default:
238 return false;
239 }
240 }
241
spvOpcodeIsConstantOrUndef(const SpvOp opcode)242 bool spvOpcodeIsConstantOrUndef(const SpvOp opcode) {
243 return opcode == SpvOpUndef || spvOpcodeIsConstant(opcode);
244 }
245
spvOpcodeIsScalarSpecConstant(const SpvOp opcode)246 bool spvOpcodeIsScalarSpecConstant(const SpvOp opcode) {
247 switch (opcode) {
248 case SpvOpSpecConstantTrue:
249 case SpvOpSpecConstantFalse:
250 case SpvOpSpecConstant:
251 return true;
252 default:
253 return false;
254 }
255 }
256
spvOpcodeIsComposite(const SpvOp opcode)257 int32_t spvOpcodeIsComposite(const SpvOp opcode) {
258 switch (opcode) {
259 case SpvOpTypeVector:
260 case SpvOpTypeMatrix:
261 case SpvOpTypeArray:
262 case SpvOpTypeStruct:
263 return true;
264 default:
265 return false;
266 }
267 }
268
spvOpcodeReturnsLogicalVariablePointer(const SpvOp opcode)269 bool spvOpcodeReturnsLogicalVariablePointer(const SpvOp opcode) {
270 switch (opcode) {
271 case SpvOpVariable:
272 case SpvOpAccessChain:
273 case SpvOpInBoundsAccessChain:
274 case SpvOpFunctionParameter:
275 case SpvOpImageTexelPointer:
276 case SpvOpCopyObject:
277 case SpvOpSelect:
278 case SpvOpPhi:
279 case SpvOpFunctionCall:
280 case SpvOpPtrAccessChain:
281 case SpvOpLoad:
282 case SpvOpConstantNull:
283 return true;
284 default:
285 return false;
286 }
287 }
288
spvOpcodeReturnsLogicalPointer(const SpvOp opcode)289 int32_t spvOpcodeReturnsLogicalPointer(const SpvOp opcode) {
290 switch (opcode) {
291 case SpvOpVariable:
292 case SpvOpAccessChain:
293 case SpvOpInBoundsAccessChain:
294 case SpvOpFunctionParameter:
295 case SpvOpImageTexelPointer:
296 case SpvOpCopyObject:
297 return true;
298 default:
299 return false;
300 }
301 }
302
spvOpcodeGeneratesType(SpvOp op)303 int32_t spvOpcodeGeneratesType(SpvOp op) {
304 switch (op) {
305 case SpvOpTypeVoid:
306 case SpvOpTypeBool:
307 case SpvOpTypeInt:
308 case SpvOpTypeFloat:
309 case SpvOpTypeVector:
310 case SpvOpTypeMatrix:
311 case SpvOpTypeImage:
312 case SpvOpTypeSampler:
313 case SpvOpTypeSampledImage:
314 case SpvOpTypeArray:
315 case SpvOpTypeRuntimeArray:
316 case SpvOpTypeStruct:
317 case SpvOpTypeOpaque:
318 case SpvOpTypePointer:
319 case SpvOpTypeFunction:
320 case SpvOpTypeEvent:
321 case SpvOpTypeDeviceEvent:
322 case SpvOpTypeReserveId:
323 case SpvOpTypeQueue:
324 case SpvOpTypePipe:
325 case SpvOpTypePipeStorage:
326 case SpvOpTypeNamedBarrier:
327 case SpvOpTypeAccelerationStructureNV:
328 return true;
329 default:
330 // In particular, OpTypeForwardPointer does not generate a type,
331 // but declares a storage class for a pointer type generated
332 // by a different instruction.
333 break;
334 }
335 return 0;
336 }
337
spvOpcodeIsDecoration(const SpvOp opcode)338 bool spvOpcodeIsDecoration(const SpvOp opcode) {
339 switch (opcode) {
340 case SpvOpDecorate:
341 case SpvOpDecorateId:
342 case SpvOpMemberDecorate:
343 case SpvOpGroupDecorate:
344 case SpvOpGroupMemberDecorate:
345 case SpvOpDecorateStringGOOGLE:
346 case SpvOpMemberDecorateStringGOOGLE:
347 return true;
348 default:
349 break;
350 }
351 return false;
352 }
353
spvOpcodeIsLoad(const SpvOp opcode)354 bool spvOpcodeIsLoad(const SpvOp opcode) {
355 switch (opcode) {
356 case SpvOpLoad:
357 case SpvOpImageSampleExplicitLod:
358 case SpvOpImageSampleImplicitLod:
359 case SpvOpImageSampleDrefImplicitLod:
360 case SpvOpImageSampleDrefExplicitLod:
361 case SpvOpImageSampleProjImplicitLod:
362 case SpvOpImageSampleProjExplicitLod:
363 case SpvOpImageSampleProjDrefImplicitLod:
364 case SpvOpImageSampleProjDrefExplicitLod:
365 case SpvOpImageFetch:
366 case SpvOpImageGather:
367 case SpvOpImageDrefGather:
368 case SpvOpImageRead:
369 case SpvOpImageSparseSampleImplicitLod:
370 case SpvOpImageSparseSampleExplicitLod:
371 case SpvOpImageSparseSampleDrefExplicitLod:
372 case SpvOpImageSparseSampleDrefImplicitLod:
373 case SpvOpImageSparseFetch:
374 case SpvOpImageSparseGather:
375 case SpvOpImageSparseDrefGather:
376 case SpvOpImageSparseRead:
377 return true;
378 default:
379 return false;
380 }
381 }
382
spvOpcodeIsBranch(SpvOp opcode)383 bool spvOpcodeIsBranch(SpvOp opcode) {
384 switch (opcode) {
385 case SpvOpBranch:
386 case SpvOpBranchConditional:
387 case SpvOpSwitch:
388 return true;
389 default:
390 return false;
391 }
392 }
393
spvOpcodeIsAtomicWithLoad(const SpvOp opcode)394 bool spvOpcodeIsAtomicWithLoad(const SpvOp opcode) {
395 switch (opcode) {
396 case SpvOpAtomicLoad:
397 case SpvOpAtomicExchange:
398 case SpvOpAtomicCompareExchange:
399 case SpvOpAtomicCompareExchangeWeak:
400 case SpvOpAtomicIIncrement:
401 case SpvOpAtomicIDecrement:
402 case SpvOpAtomicIAdd:
403 case SpvOpAtomicISub:
404 case SpvOpAtomicSMin:
405 case SpvOpAtomicUMin:
406 case SpvOpAtomicSMax:
407 case SpvOpAtomicUMax:
408 case SpvOpAtomicAnd:
409 case SpvOpAtomicOr:
410 case SpvOpAtomicXor:
411 case SpvOpAtomicFlagTestAndSet:
412 return true;
413 default:
414 return false;
415 }
416 }
417
spvOpcodeIsAtomicOp(const SpvOp opcode)418 bool spvOpcodeIsAtomicOp(const SpvOp opcode) {
419 return (spvOpcodeIsAtomicWithLoad(opcode) || opcode == SpvOpAtomicStore ||
420 opcode == SpvOpAtomicFlagClear);
421 }
422
spvOpcodeIsReturn(SpvOp opcode)423 bool spvOpcodeIsReturn(SpvOp opcode) {
424 switch (opcode) {
425 case SpvOpReturn:
426 case SpvOpReturnValue:
427 return true;
428 default:
429 return false;
430 }
431 }
432
spvOpcodeIsReturnOrAbort(SpvOp opcode)433 bool spvOpcodeIsReturnOrAbort(SpvOp opcode) {
434 return spvOpcodeIsReturn(opcode) || opcode == SpvOpKill ||
435 opcode == SpvOpUnreachable;
436 }
437
spvOpcodeIsBlockTerminator(SpvOp opcode)438 bool spvOpcodeIsBlockTerminator(SpvOp opcode) {
439 return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode);
440 }
441
spvOpcodeIsBaseOpaqueType(SpvOp opcode)442 bool spvOpcodeIsBaseOpaqueType(SpvOp opcode) {
443 switch (opcode) {
444 case SpvOpTypeImage:
445 case SpvOpTypeSampler:
446 case SpvOpTypeSampledImage:
447 case SpvOpTypeOpaque:
448 case SpvOpTypeEvent:
449 case SpvOpTypeDeviceEvent:
450 case SpvOpTypeReserveId:
451 case SpvOpTypeQueue:
452 case SpvOpTypePipe:
453 case SpvOpTypeForwardPointer:
454 case SpvOpTypePipeStorage:
455 case SpvOpTypeNamedBarrier:
456 return true;
457 default:
458 return false;
459 }
460 }
461
spvOpcodeIsNonUniformGroupOperation(SpvOp opcode)462 bool spvOpcodeIsNonUniformGroupOperation(SpvOp opcode) {
463 switch (opcode) {
464 case SpvOpGroupNonUniformElect:
465 case SpvOpGroupNonUniformAll:
466 case SpvOpGroupNonUniformAny:
467 case SpvOpGroupNonUniformAllEqual:
468 case SpvOpGroupNonUniformBroadcast:
469 case SpvOpGroupNonUniformBroadcastFirst:
470 case SpvOpGroupNonUniformBallot:
471 case SpvOpGroupNonUniformInverseBallot:
472 case SpvOpGroupNonUniformBallotBitExtract:
473 case SpvOpGroupNonUniformBallotBitCount:
474 case SpvOpGroupNonUniformBallotFindLSB:
475 case SpvOpGroupNonUniformBallotFindMSB:
476 case SpvOpGroupNonUniformShuffle:
477 case SpvOpGroupNonUniformShuffleXor:
478 case SpvOpGroupNonUniformShuffleUp:
479 case SpvOpGroupNonUniformShuffleDown:
480 case SpvOpGroupNonUniformIAdd:
481 case SpvOpGroupNonUniformFAdd:
482 case SpvOpGroupNonUniformIMul:
483 case SpvOpGroupNonUniformFMul:
484 case SpvOpGroupNonUniformSMin:
485 case SpvOpGroupNonUniformUMin:
486 case SpvOpGroupNonUniformFMin:
487 case SpvOpGroupNonUniformSMax:
488 case SpvOpGroupNonUniformUMax:
489 case SpvOpGroupNonUniformFMax:
490 case SpvOpGroupNonUniformBitwiseAnd:
491 case SpvOpGroupNonUniformBitwiseOr:
492 case SpvOpGroupNonUniformBitwiseXor:
493 case SpvOpGroupNonUniformLogicalAnd:
494 case SpvOpGroupNonUniformLogicalOr:
495 case SpvOpGroupNonUniformLogicalXor:
496 case SpvOpGroupNonUniformQuadBroadcast:
497 case SpvOpGroupNonUniformQuadSwap:
498 return true;
499 default:
500 return false;
501 }
502 }
503
spvOpcodeIsScalarizable(SpvOp opcode)504 bool spvOpcodeIsScalarizable(SpvOp opcode) {
505 switch (opcode) {
506 case SpvOpPhi:
507 case SpvOpCopyObject:
508 case SpvOpConvertFToU:
509 case SpvOpConvertFToS:
510 case SpvOpConvertSToF:
511 case SpvOpConvertUToF:
512 case SpvOpUConvert:
513 case SpvOpSConvert:
514 case SpvOpFConvert:
515 case SpvOpQuantizeToF16:
516 case SpvOpVectorInsertDynamic:
517 case SpvOpSNegate:
518 case SpvOpFNegate:
519 case SpvOpIAdd:
520 case SpvOpFAdd:
521 case SpvOpISub:
522 case SpvOpFSub:
523 case SpvOpIMul:
524 case SpvOpFMul:
525 case SpvOpUDiv:
526 case SpvOpSDiv:
527 case SpvOpFDiv:
528 case SpvOpUMod:
529 case SpvOpSRem:
530 case SpvOpSMod:
531 case SpvOpFRem:
532 case SpvOpFMod:
533 case SpvOpVectorTimesScalar:
534 case SpvOpIAddCarry:
535 case SpvOpISubBorrow:
536 case SpvOpUMulExtended:
537 case SpvOpSMulExtended:
538 case SpvOpShiftRightLogical:
539 case SpvOpShiftRightArithmetic:
540 case SpvOpShiftLeftLogical:
541 case SpvOpBitwiseOr:
542 case SpvOpBitwiseAnd:
543 case SpvOpNot:
544 case SpvOpBitFieldInsert:
545 case SpvOpBitFieldSExtract:
546 case SpvOpBitFieldUExtract:
547 case SpvOpBitReverse:
548 case SpvOpBitCount:
549 case SpvOpIsNan:
550 case SpvOpIsInf:
551 case SpvOpIsFinite:
552 case SpvOpIsNormal:
553 case SpvOpSignBitSet:
554 case SpvOpLessOrGreater:
555 case SpvOpOrdered:
556 case SpvOpUnordered:
557 case SpvOpLogicalEqual:
558 case SpvOpLogicalNotEqual:
559 case SpvOpLogicalOr:
560 case SpvOpLogicalAnd:
561 case SpvOpLogicalNot:
562 case SpvOpSelect:
563 case SpvOpIEqual:
564 case SpvOpINotEqual:
565 case SpvOpUGreaterThan:
566 case SpvOpSGreaterThan:
567 case SpvOpUGreaterThanEqual:
568 case SpvOpSGreaterThanEqual:
569 case SpvOpULessThan:
570 case SpvOpSLessThan:
571 case SpvOpULessThanEqual:
572 case SpvOpSLessThanEqual:
573 case SpvOpFOrdEqual:
574 case SpvOpFUnordEqual:
575 case SpvOpFOrdNotEqual:
576 case SpvOpFUnordNotEqual:
577 case SpvOpFOrdLessThan:
578 case SpvOpFUnordLessThan:
579 case SpvOpFOrdGreaterThan:
580 case SpvOpFUnordGreaterThan:
581 case SpvOpFOrdLessThanEqual:
582 case SpvOpFUnordLessThanEqual:
583 case SpvOpFOrdGreaterThanEqual:
584 case SpvOpFUnordGreaterThanEqual:
585 return true;
586 default:
587 return false;
588 }
589 }
590
spvOpcodeIsDebug(SpvOp opcode)591 bool spvOpcodeIsDebug(SpvOp opcode) {
592 switch (opcode) {
593 case SpvOpName:
594 case SpvOpMemberName:
595 case SpvOpSource:
596 case SpvOpSourceContinued:
597 case SpvOpSourceExtension:
598 case SpvOpString:
599 case SpvOpLine:
600 case SpvOpNoLine:
601 return true;
602 default:
603 return false;
604 }
605 }
606