| // Copyright (c) 2015 The Khronos Group Inc. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a |
| // copy of this software and/or associated documentation files (the |
| // "Materials"), to deal in the Materials without restriction, including |
| // without limitation the rights to use, copy, modify, merge, publish, |
| // distribute, sublicense, and/or sell copies of the Materials, and to |
| // permit persons to whom the Materials are furnished to do so, subject to |
| // the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included |
| // in all copies or substantial portions of the Materials. |
| // |
| // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS |
| // KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS |
| // SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT |
| // https://www.khronos.org/registry/ |
| // |
| // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. |
| |
| #include <libspirv/libspirv.h> |
| #include "endian.h" |
| #include "instruction.h" |
| #include "opcode.h" |
| |
| #include <assert.h> |
| #include <string.h> |
| |
| namespace { |
| |
| // Descriptions of each opcode. Each entry describes the format of the |
| // instruction that follows a particular opcode. |
| // |
| // Most fields are initialized statically by including an automatically |
| // generated file. |
| // The operandTypes fields are initialized during spvOpcodeInitialize(). |
| // |
| // TODO(dneto): Some of the macros are quite unreadable. We could make |
| // good use of constexpr functions, but some compilers don't support that yet. |
| spv_opcode_desc_t opcodeTableEntries[] = { |
| #define EmptyList {} |
| #define List(...) {__VA_ARGS__} |
| #define Capability(X) SPV_CAPABILITY_AS_MASK(Capability##X) |
| #define Capability2(X,Y) Capability(X)|Capability(Y) |
| #define CapabilityNone 0 // Needed so Capability(None) still expands to valid syntax. |
| #define Instruction(Name,HasResult,HasType,NumLogicalOperands,NumCapabilities,CapabilityRequired,IsVariable,LogicalArgsList) \ |
| { #Name, \ |
| Op##Name, \ |
| (NumCapabilities) ? (CapabilityRequired) : 0, \ |
| 0, {}, /* Filled in later. Operand list, including result id and type id, if needed */ \ |
| HasResult, \ |
| HasType, \ |
| LogicalArgsList }, |
| #include "opcode.inc" |
| #undef EmptyList |
| #undef List |
| #undef Capability |
| #undef Capability2 |
| #undef CapabilityNone |
| #undef Instruction |
| }; |
| |
| // Has the opcodeTableEntries table been fully elaborated? |
| // That is, are the operandTypes fields initialized? |
| bool opcodeTableInitialized = false; |
| |
| // Opcode API |
| |
| // Converts the given operand class enum (from the SPIR-V document generation |
| // logic) to the operand type required by the parser. |
| // This only applies to logical operands. |
| spv_operand_type_t convertOperandClassToType(spv::Op opcode, |
| spv::OperandClass operandClass) { |
| // The spec document generator uses OptionalOperandLiteral for several kinds |
| // of repeating values. Our parser needs more specific information about |
| // what is being repeated. |
| if (operandClass == OperandOptionalLiteral) { |
| switch (opcode) { |
| case spv::OpLoad: |
| case spv::OpStore: |
| case spv::OpCopyMemory: |
| case spv::OpCopyMemorySized: |
| // Expect an optional mask. When the Aligned bit is set in the mask, |
| // we will later add the expectation of a literal number operand. |
| return SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS; |
| case spv::OpExecutionMode: |
| return SPV_OPERAND_TYPE_VARIABLE_EXECUTION_MODE; |
| default: |
| break; |
| } |
| } else if (operandClass == OperandVariableLiterals) { |
| if (opcode == spv::OpConstant || opcode == spv::OpSpecConstant) |
| return SPV_OPERAND_TYPE_MULTIWORD_LITERAL_NUMBER; |
| } |
| |
| switch(operandClass) { |
| case OperandNone: return SPV_OPERAND_TYPE_NONE; |
| case OperandId: return SPV_OPERAND_TYPE_ID; |
| case OperandOptionalId: return SPV_OPERAND_TYPE_OPTIONAL_ID; |
| case OperandOptionalImage: return SPV_OPERAND_TYPE_OPTIONAL_IMAGE; |
| case OperandVariableIds: return SPV_OPERAND_TYPE_VARIABLE_ID; |
| // The spec only uses OptionalLiteral for an optional literal number. |
| case OperandOptionalLiteral: return SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER; |
| case OperandOptionalLiteralString: return SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING; |
| // This is only used for sequences of literal numbers. |
| case OperandVariableLiterals: return SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER; |
| case OperandLiteralNumber: |
| if (opcode == spv::OpExtInst) { |
| // We use a special operand type for the extension instruction number. |
| // For now, we assume there is only one LiteraNumber argument to OpExtInst, |
| // and it is the extension instruction argument. |
| // See the ExtInst entry in opcode.inc |
| // TODO(dneto): Use a function to confirm the assumption, and to verify |
| // that the index into the operandClass is 1, as expected. |
| return SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER; |
| } |
| return SPV_OPERAND_TYPE_LITERAL_INTEGER; |
| case OperandLiteralString: return SPV_OPERAND_TYPE_LITERAL_STRING; |
| case OperandSource: return SPV_OPERAND_TYPE_SOURCE_LANGUAGE; |
| case OperandExecutionModel: return SPV_OPERAND_TYPE_EXECUTION_MODEL; |
| case OperandAddressing: return SPV_OPERAND_TYPE_ADDRESSING_MODEL; |
| case OperandMemory: return SPV_OPERAND_TYPE_MEMORY_MODEL; |
| case OperandExecutionMode: return SPV_OPERAND_TYPE_EXECUTION_MODE; |
| case OperandStorage: return SPV_OPERAND_TYPE_STORAGE_CLASS; |
| case OperandDimensionality: return SPV_OPERAND_TYPE_DIMENSIONALITY; |
| case OperandSamplerAddressingMode: return SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE; |
| case OperandSamplerFilterMode: return SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE; |
| case OperandSamplerImageFormat: return SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT; |
| case OperandImageChannelOrder: |
| // This is only used to describe the value generated by OpImageQueryOrder. |
| // It is not used as an operand. |
| break; |
| case OperandImageChannelDataType: |
| // This is only used to describe the value generated by OpImageQueryFormat. |
| // It is not used as an operand. |
| break; |
| case OperandImageOperands: |
| // This is not used in opcode.inc. It only exists to generate the |
| // corresponding spec section. In parsing, image operands meld into the |
| // OperandOptionalImage case. |
| break; |
| case OperandFPFastMath: return SPV_OPERAND_TYPE_FP_FAST_MATH_MODE; |
| case OperandFPRoundingMode: return SPV_OPERAND_TYPE_FP_ROUNDING_MODE; |
| case OperandLinkageType: return SPV_OPERAND_TYPE_LINKAGE_TYPE; |
| case OperandAccessQualifier: return SPV_OPERAND_TYPE_ACCESS_QUALIFIER; |
| case OperandFuncParamAttr: return SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE; |
| case OperandDecoration: return SPV_OPERAND_TYPE_DECORATION; |
| case OperandBuiltIn: return SPV_OPERAND_TYPE_BUILT_IN; |
| case OperandSelect: return SPV_OPERAND_TYPE_SELECTION_CONTROL; |
| case OperandLoop: return SPV_OPERAND_TYPE_LOOP_CONTROL; |
| case OperandFunction: return SPV_OPERAND_TYPE_FUNCTION_CONTROL; |
| case OperandMemorySemantics: return SPV_OPERAND_TYPE_MEMORY_SEMANTICS; |
| case OperandMemoryAccess: |
| // This case does not occur in the table for SPIR-V 0.99 Rev 32. |
| // We expect that it will become SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, |
| // and we can remove the special casing above for memory operation |
| // instructions. |
| break; |
| case OperandScope: return SPV_OPERAND_TYPE_EXECUTION_SCOPE; |
| case OperandGroupOperation: return SPV_OPERAND_TYPE_GROUP_OPERATION; |
| case OperandKernelEnqueueFlags: return SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS; |
| case OperandKernelProfilingInfo: return SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO; |
| case OperandCapability: return SPV_OPERAND_TYPE_CAPABILITY; |
| |
| // Used by GroupMemberDecorate |
| case OperandVariableIdLiteral: return SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER; |
| |
| // Used by Switch |
| case OperandVariableLiteralId: return SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID; |
| |
| // These exceptional cases shouldn't occur. |
| case OperandCount: |
| default: |
| break; |
| } |
| assert(0 && "Unexpected operand class"); |
| return SPV_OPERAND_TYPE_NONE; |
| } |
| |
| } // anonymous namespace |
| |
| // Finish populating the opcodeTableEntries array. |
| void spvOpcodeTableInitialize() { |
| // Compute the operandTypes field for each entry. |
| for (auto &opcode : opcodeTableEntries) { |
| opcode.numTypes = 0; |
| // Type ID always comes first, if present. |
| if (opcode.hasType) |
| opcode.operandTypes[opcode.numTypes++] = SPV_OPERAND_TYPE_TYPE_ID; |
| // Result ID always comes next, if present |
| if (opcode.hasResult) |
| opcode.operandTypes[opcode.numTypes++] = SPV_OPERAND_TYPE_RESULT_ID; |
| const uint16_t maxNumOperands = |
| sizeof(opcode.operandTypes) / sizeof(opcode.operandTypes[0]); |
| const uint16_t maxNumClasses = |
| sizeof(opcode.operandClass) / sizeof(opcode.operandClass[0]); |
| for (uint16_t classIndex = 0; |
| opcode.numTypes < maxNumOperands && classIndex < maxNumClasses; |
| classIndex++) { |
| const OperandClass operandClass = opcode.operandClass[classIndex]; |
| opcode.operandTypes[opcode.numTypes++] = |
| convertOperandClassToType(opcode.opcode, operandClass); |
| // The OperandNone value is not explicitly represented in the .inc file. |
| // However, it is the zero value, and is created via implicit value |
| // initialization. |
| if (operandClass == OperandNone) { |
| opcode.numTypes--; |
| break; |
| } |
| } |
| // We should have written the terminating SPV_OPERAND_TYPE_NONE entry, but |
| // also without overflowing. |
| assert((opcode.numTypes < maxNumOperands) && |
| "Operand class list is too long. Expand " |
| "spv_opcode_desc_t.operandClass"); |
| } |
| opcodeTableInitialized = true; |
| } |
| |
| const char *spvGeneratorStr(uint32_t generator) { |
| switch (generator) { |
| case SPV_GENERATOR_KHRONOS: |
| return "Khronos"; |
| case SPV_GENERATOR_LUNARG: |
| return "LunarG"; |
| case SPV_GENERATOR_VALVE: |
| return "Valve"; |
| case SPV_GENERATOR_CODEPLAY: |
| return "Codeplay Software Ltd."; |
| case SPV_GENERATOR_NVIDIA: |
| return "NVIDIA"; |
| case SPV_GENERATOR_ARM: |
| return "ARM"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| uint32_t spvOpcodeMake(uint16_t wordCount, Op opcode) { |
| return ((uint32_t)opcode) | (((uint32_t)wordCount) << 16); |
| } |
| |
| void spvOpcodeSplit(const uint32_t word, uint16_t *pWordCount, Op *pOpcode) { |
| if (pWordCount) { |
| *pWordCount = (uint16_t)((0xffff0000 & word) >> 16); |
| } |
| if (pOpcode) { |
| *pOpcode = (Op)(0x0000ffff & word); |
| } |
| } |
| |
| spv_result_t spvOpcodeTableGet(spv_opcode_table *pInstTable) { |
| if (!pInstTable) return SPV_ERROR_INVALID_POINTER; |
| |
| static spv_opcode_table_t table = { |
| sizeof(opcodeTableEntries) / sizeof(spv_opcode_desc_t), |
| opcodeTableEntries}; |
| |
| // TODO(dneto): Consider thread safety of initialization. |
| // That is, ordering effects of the flag vs. the table updates. |
| if (!opcodeTableInitialized) spvOpcodeTableInitialize(); |
| |
| *pInstTable = &table; |
| |
| return SPV_SUCCESS; |
| } |
| |
| spv_result_t spvOpcodeTableNameLookup(const spv_opcode_table table, |
| const char *name, |
| spv_opcode_desc *pEntry) { |
| if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER; |
| if (!table) return SPV_ERROR_INVALID_TABLE; |
| |
| // TODO: This lookup of the Opcode table is suboptimal! Binary sort would be |
| // preferable but the table requires sorting on the Opcode name, but it's |
| // static |
| // const initialized and matches the order of the spec. |
| const size_t nameLength = strlen(name); |
| for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) { |
| if (nameLength == strlen(table->entries[opcodeIndex].name) && |
| !strncmp(name, table->entries[opcodeIndex].name, nameLength)) { |
| // NOTE: Found out Opcode! |
| *pEntry = &table->entries[opcodeIndex]; |
| return SPV_SUCCESS; |
| } |
| } |
| |
| return SPV_ERROR_INVALID_LOOKUP; |
| } |
| |
| spv_result_t spvOpcodeTableValueLookup(const spv_opcode_table table, |
| const Op opcode, |
| spv_opcode_desc *pEntry) { |
| if (!table) return SPV_ERROR_INVALID_TABLE; |
| if (!pEntry) return SPV_ERROR_INVALID_POINTER; |
| |
| // TODO: As above this lookup is not optimal. |
| for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) { |
| if (opcode == table->entries[opcodeIndex].opcode) { |
| // NOTE: Found the Opcode! |
| *pEntry = &table->entries[opcodeIndex]; |
| return SPV_SUCCESS; |
| } |
| } |
| |
| return SPV_ERROR_INVALID_LOOKUP; |
| } |
| |
| int16_t spvOpcodeResultIdIndex(spv_opcode_desc entry) { |
| for (int16_t i = 0; i < entry->numTypes; ++i) { |
| if (SPV_OPERAND_TYPE_RESULT_ID == entry->operandTypes[i]) return i; |
| } |
| return SPV_OPERAND_INVALID_RESULT_ID_INDEX; |
| } |
| |
| int32_t spvOpcodeRequiresCapabilities(spv_opcode_desc entry) { |
| return entry->capabilities != 0; |
| } |
| |
| void spvInstructionCopy(const uint32_t *words, const Op opcode, |
| const uint16_t wordCount, const spv_endianness_t endian, |
| spv_instruction_t *pInst) { |
| pInst->opcode = opcode; |
| pInst->words.resize(wordCount); |
| for (uint16_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) { |
| pInst->words[wordIndex] = spvFixWord(words[wordIndex], endian); |
| if (!wordIndex) { |
| uint16_t thisWordCount; |
| Op thisOpcode; |
| spvOpcodeSplit(pInst->words[wordIndex], &thisWordCount, &thisOpcode); |
| assert(opcode == thisOpcode && wordCount == thisWordCount && |
| "Endianness failed!"); |
| } |
| } |
| } |
| |
| const char *spvOpcodeString(const Op opcode) { |
| #define CASE(OPCODE) \ |
| case OPCODE: \ |
| return #OPCODE; |
| switch (opcode) { |
| CASE(OpNop) |
| CASE(OpSource) |
| CASE(OpSourceExtension) |
| CASE(OpExtension) |
| CASE(OpExtInstImport) |
| CASE(OpMemoryModel) |
| CASE(OpEntryPoint) |
| CASE(OpExecutionMode) |
| CASE(OpTypeVoid) |
| CASE(OpTypeBool) |
| CASE(OpTypeInt) |
| CASE(OpTypeFloat) |
| CASE(OpTypeVector) |
| CASE(OpTypeMatrix) |
| CASE(OpTypeSampler) |
| CASE(OpTypeArray) |
| CASE(OpTypeRuntimeArray) |
| CASE(OpTypeStruct) |
| CASE(OpTypeOpaque) |
| CASE(OpTypePointer) |
| CASE(OpTypeFunction) |
| CASE(OpTypeEvent) |
| CASE(OpTypeDeviceEvent) |
| CASE(OpTypeReserveId) |
| CASE(OpTypeQueue) |
| CASE(OpTypePipe) |
| CASE(OpConstantTrue) |
| CASE(OpConstantFalse) |
| CASE(OpConstant) |
| CASE(OpConstantComposite) |
| CASE(OpConstantSampler) |
| CASE(OpConstantNull) |
| CASE(OpSpecConstantTrue) |
| CASE(OpSpecConstantFalse) |
| CASE(OpSpecConstant) |
| CASE(OpSpecConstantComposite) |
| CASE(OpVariable) |
| CASE(OpFunction) |
| CASE(OpFunctionParameter) |
| CASE(OpFunctionEnd) |
| CASE(OpFunctionCall) |
| CASE(OpExtInst) |
| CASE(OpUndef) |
| CASE(OpLoad) |
| CASE(OpStore) |
| CASE(OpPhi) |
| CASE(OpDecorationGroup) |
| CASE(OpDecorate) |
| CASE(OpMemberDecorate) |
| CASE(OpGroupDecorate) |
| CASE(OpGroupMemberDecorate) |
| CASE(OpName) |
| CASE(OpMemberName) |
| CASE(OpString) |
| CASE(OpLine) |
| CASE(OpVectorExtractDynamic) |
| CASE(OpVectorInsertDynamic) |
| CASE(OpVectorShuffle) |
| CASE(OpCompositeConstruct) |
| CASE(OpCompositeExtract) |
| CASE(OpCompositeInsert) |
| CASE(OpCopyObject) |
| CASE(OpCopyMemory) |
| CASE(OpCopyMemorySized) |
| CASE(OpAccessChain) |
| CASE(OpInBoundsAccessChain) |
| CASE(OpSNegate) |
| CASE(OpFNegate) |
| CASE(OpNot) |
| CASE(OpAny) |
| CASE(OpAll) |
| CASE(OpConvertFToU) |
| CASE(OpConvertFToS) |
| CASE(OpConvertSToF) |
| CASE(OpConvertUToF) |
| CASE(OpUConvert) |
| CASE(OpSConvert) |
| CASE(OpFConvert) |
| CASE(OpConvertPtrToU) |
| CASE(OpConvertUToPtr) |
| CASE(OpPtrCastToGeneric) |
| CASE(OpGenericCastToPtr) |
| CASE(OpBitcast) |
| CASE(OpTranspose) |
| CASE(OpIsNan) |
| CASE(OpIsInf) |
| CASE(OpIsFinite) |
| CASE(OpIsNormal) |
| CASE(OpSignBitSet) |
| CASE(OpLessOrGreater) |
| CASE(OpOrdered) |
| CASE(OpUnordered) |
| CASE(OpArrayLength) |
| CASE(OpIAdd) |
| CASE(OpFAdd) |
| CASE(OpISub) |
| CASE(OpFSub) |
| CASE(OpIMul) |
| CASE(OpFMul) |
| CASE(OpUDiv) |
| CASE(OpSDiv) |
| CASE(OpFDiv) |
| CASE(OpUMod) |
| CASE(OpSRem) |
| CASE(OpSMod) |
| CASE(OpFRem) |
| CASE(OpFMod) |
| CASE(OpVectorTimesScalar) |
| CASE(OpMatrixTimesScalar) |
| CASE(OpVectorTimesMatrix) |
| CASE(OpMatrixTimesVector) |
| CASE(OpMatrixTimesMatrix) |
| CASE(OpOuterProduct) |
| CASE(OpDot) |
| CASE(OpShiftRightLogical) |
| CASE(OpShiftRightArithmetic) |
| CASE(OpShiftLeftLogical) |
| CASE(OpLogicalOr) |
| CASE(OpLogicalAnd) |
| CASE(OpBitwiseOr) |
| CASE(OpBitwiseXor) |
| CASE(OpBitwiseAnd) |
| CASE(OpSelect) |
| CASE(OpIEqual) |
| CASE(OpFOrdEqual) |
| CASE(OpFUnordEqual) |
| CASE(OpINotEqual) |
| CASE(OpFOrdNotEqual) |
| CASE(OpFUnordNotEqual) |
| CASE(OpULessThan) |
| CASE(OpSLessThan) |
| CASE(OpFOrdLessThan) |
| CASE(OpFUnordLessThan) |
| CASE(OpUGreaterThan) |
| CASE(OpSGreaterThan) |
| CASE(OpFOrdGreaterThan) |
| CASE(OpFUnordGreaterThan) |
| CASE(OpULessThanEqual) |
| CASE(OpSLessThanEqual) |
| CASE(OpFOrdLessThanEqual) |
| CASE(OpFUnordLessThanEqual) |
| CASE(OpUGreaterThanEqual) |
| CASE(OpSGreaterThanEqual) |
| CASE(OpFOrdGreaterThanEqual) |
| CASE(OpFUnordGreaterThanEqual) |
| CASE(OpDPdx) |
| CASE(OpDPdy) |
| CASE(OpFwidth) |
| CASE(OpDPdxFine) |
| CASE(OpDPdyFine) |
| CASE(OpFwidthFine) |
| CASE(OpDPdxCoarse) |
| CASE(OpDPdyCoarse) |
| CASE(OpFwidthCoarse) |
| CASE(OpEmitVertex) |
| CASE(OpEndPrimitive) |
| CASE(OpEmitStreamVertex) |
| CASE(OpEndStreamPrimitive) |
| CASE(OpControlBarrier) |
| CASE(OpMemoryBarrier) |
| CASE(OpAtomicLoad) |
| CASE(OpAtomicStore) |
| CASE(OpAtomicExchange) |
| CASE(OpAtomicCompareExchange) |
| CASE(OpAtomicCompareExchangeWeak) |
| CASE(OpAtomicIIncrement) |
| CASE(OpAtomicIDecrement) |
| CASE(OpAtomicIAdd) |
| CASE(OpAtomicISub) |
| CASE(OpAtomicUMin) |
| CASE(OpAtomicUMax) |
| CASE(OpAtomicAnd) |
| CASE(OpAtomicOr) |
| CASE(OpAtomicXor) |
| CASE(OpLoopMerge) |
| CASE(OpSelectionMerge) |
| CASE(OpLabel) |
| CASE(OpBranch) |
| CASE(OpBranchConditional) |
| CASE(OpSwitch) |
| CASE(OpKill) |
| CASE(OpReturn) |
| CASE(OpReturnValue) |
| CASE(OpUnreachable) |
| CASE(OpLifetimeStart) |
| CASE(OpLifetimeStop) |
| CASE(OpAsyncGroupCopy) |
| CASE(OpWaitGroupEvents) |
| CASE(OpGroupAll) |
| CASE(OpGroupAny) |
| CASE(OpGroupBroadcast) |
| CASE(OpGroupIAdd) |
| CASE(OpGroupFAdd) |
| CASE(OpGroupFMin) |
| CASE(OpGroupUMin) |
| CASE(OpGroupSMin) |
| CASE(OpGroupFMax) |
| CASE(OpGroupUMax) |
| CASE(OpGroupSMax) |
| CASE(OpGenericCastToPtrExplicit) |
| CASE(OpGenericPtrMemSemantics) |
| CASE(OpReadPipe) |
| CASE(OpWritePipe) |
| CASE(OpReservedReadPipe) |
| CASE(OpReservedWritePipe) |
| CASE(OpReserveReadPipePackets) |
| CASE(OpReserveWritePipePackets) |
| CASE(OpCommitReadPipe) |
| CASE(OpCommitWritePipe) |
| CASE(OpIsValidReserveId) |
| CASE(OpGetNumPipePackets) |
| CASE(OpGetMaxPipePackets) |
| CASE(OpGroupReserveReadPipePackets) |
| CASE(OpGroupReserveWritePipePackets) |
| CASE(OpGroupCommitReadPipe) |
| CASE(OpGroupCommitWritePipe) |
| CASE(OpEnqueueMarker) |
| CASE(OpEnqueueKernel) |
| CASE(OpGetKernelNDrangeSubGroupCount) |
| CASE(OpGetKernelNDrangeMaxSubGroupSize) |
| CASE(OpGetKernelWorkGroupSize) |
| CASE(OpGetKernelPreferredWorkGroupSizeMultiple) |
| CASE(OpRetainEvent) |
| CASE(OpReleaseEvent) |
| CASE(OpCreateUserEvent) |
| CASE(OpIsValidEvent) |
| CASE(OpSetUserEventStatus) |
| CASE(OpCaptureEventProfilingInfo) |
| CASE(OpGetDefaultQueue) |
| CASE(OpBuildNDRange) |
| default: |
| assert(0 && "Unreachable!"); |
| } |
| #undef CASE |
| return "unknown"; |
| } |
| |
| int32_t spvOpcodeIsType(const Op opcode) { |
| switch (opcode) { |
| case OpTypeVoid: |
| case OpTypeBool: |
| case OpTypeInt: |
| case OpTypeFloat: |
| case OpTypeVector: |
| case OpTypeMatrix: |
| case OpTypeSampler: |
| case OpTypeSampledImage: |
| case OpTypeArray: |
| case OpTypeRuntimeArray: |
| case OpTypeStruct: |
| case OpTypeOpaque: |
| case OpTypePointer: |
| case OpTypeFunction: |
| case OpTypeEvent: |
| case OpTypeDeviceEvent: |
| case OpTypeReserveId: |
| case OpTypeQueue: |
| case OpTypePipe: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| int32_t spvOpcodeIsScalarType(const Op opcode) { |
| switch (opcode) { |
| case OpTypeInt: |
| case OpTypeFloat: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| int32_t spvOpcodeIsConstant(const Op opcode) { |
| switch (opcode) { |
| case OpConstantTrue: |
| case OpConstantFalse: |
| case OpConstant: |
| case OpConstantComposite: |
| case OpConstantSampler: |
| // case OpConstantNull: |
| case OpConstantNull: |
| case OpSpecConstantTrue: |
| case OpSpecConstantFalse: |
| case OpSpecConstant: |
| case OpSpecConstantComposite: |
| // case OpSpecConstantOp: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| int32_t spvOpcodeIsComposite(const Op opcode) { |
| switch (opcode) { |
| case OpTypeVector: |
| case OpTypeMatrix: |
| case OpTypeArray: |
| case OpTypeStruct: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| int32_t spvOpcodeAreTypesEqual(const spv_instruction_t *pTypeInst0, |
| const spv_instruction_t *pTypeInst1) { |
| if (pTypeInst0->opcode != pTypeInst1->opcode) return false; |
| if (pTypeInst0->words[1] != pTypeInst1->words[1]) return false; |
| return true; |
| } |
| |
| int32_t spvOpcodeIsPointer(const Op opcode) { |
| switch (opcode) { |
| case OpVariable: |
| case OpAccessChain: |
| case OpInBoundsAccessChain: |
| case OpFunctionParameter: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| int32_t spvOpcodeIsObject(const Op opcode) { |
| switch (opcode) { |
| case OpConstantTrue: |
| case OpConstantFalse: |
| case OpConstant: |
| case OpConstantComposite: |
| // TODO: case OpConstantSampler: |
| case OpConstantNull: |
| case OpSpecConstantTrue: |
| case OpSpecConstantFalse: |
| case OpSpecConstant: |
| case OpSpecConstantComposite: |
| // TODO: case OpSpecConstantOp: |
| case OpVariable: |
| case OpAccessChain: |
| case OpInBoundsAccessChain: |
| case OpConvertFToU: |
| case OpConvertFToS: |
| case OpConvertSToF: |
| case OpConvertUToF: |
| case OpUConvert: |
| case OpSConvert: |
| case OpFConvert: |
| case OpConvertPtrToU: |
| // TODO: case OpConvertUToPtr: |
| case OpPtrCastToGeneric: |
| // TODO: case OpGenericCastToPtr: |
| case OpBitcast: |
| // TODO: case OpGenericCastToPtrExplicit: |
| case OpSatConvertSToU: |
| case OpSatConvertUToS: |
| case OpVectorExtractDynamic: |
| case OpCompositeConstruct: |
| case OpCompositeExtract: |
| case OpCopyObject: |
| case OpTranspose: |
| case OpSNegate: |
| case OpFNegate: |
| case OpNot: |
| case OpIAdd: |
| case OpFAdd: |
| case OpISub: |
| case OpFSub: |
| case OpIMul: |
| case OpFMul: |
| case OpUDiv: |
| case OpSDiv: |
| case OpFDiv: |
| case OpUMod: |
| case OpSRem: |
| case OpSMod: |
| case OpVectorTimesScalar: |
| case OpMatrixTimesScalar: |
| case OpVectorTimesMatrix: |
| case OpMatrixTimesVector: |
| case OpMatrixTimesMatrix: |
| case OpOuterProduct: |
| case OpDot: |
| case OpShiftRightLogical: |
| case OpShiftRightArithmetic: |
| case OpShiftLeftLogical: |
| case OpBitwiseOr: |
| case OpBitwiseXor: |
| case OpBitwiseAnd: |
| case OpAny: |
| case OpAll: |
| case OpIsNan: |
| case OpIsInf: |
| case OpIsFinite: |
| case OpIsNormal: |
| case OpSignBitSet: |
| case OpLessOrGreater: |
| case OpOrdered: |
| case OpUnordered: |
| case OpLogicalOr: |
| case OpLogicalAnd: |
| case OpSelect: |
| case OpIEqual: |
| case OpFOrdEqual: |
| case OpFUnordEqual: |
| case OpINotEqual: |
| case OpFOrdNotEqual: |
| case OpFUnordNotEqual: |
| case OpULessThan: |
| case OpSLessThan: |
| case OpFOrdLessThan: |
| case OpFUnordLessThan: |
| case OpUGreaterThan: |
| case OpSGreaterThan: |
| case OpFOrdGreaterThan: |
| case OpFUnordGreaterThan: |
| case OpULessThanEqual: |
| case OpSLessThanEqual: |
| case OpFOrdLessThanEqual: |
| case OpFUnordLessThanEqual: |
| case OpUGreaterThanEqual: |
| case OpSGreaterThanEqual: |
| case OpFOrdGreaterThanEqual: |
| case OpFUnordGreaterThanEqual: |
| case OpDPdx: |
| case OpDPdy: |
| case OpFwidth: |
| case OpDPdxFine: |
| case OpDPdyFine: |
| case OpFwidthFine: |
| case OpDPdxCoarse: |
| case OpDPdyCoarse: |
| case OpFwidthCoarse: |
| case OpReturnValue: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| int32_t spvOpcodeIsBasicTypeNullable(Op opcode) { |
| switch (opcode) { |
| case OpTypeBool: |
| case OpTypeInt: |
| case OpTypeFloat: |
| case OpTypePointer: |
| case OpTypeEvent: |
| case OpTypeDeviceEvent: |
| case OpTypeReserveId: |
| case OpTypeQueue: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| int32_t spvInstructionIsInBasicBlock(const spv_instruction_t *pFirstInst, |
| const spv_instruction_t *pInst) { |
| while (pFirstInst != pInst) { |
| if (OpFunction == pInst->opcode) break; |
| pInst--; |
| } |
| if (OpFunction != pInst->opcode) return false; |
| return true; |
| } |
| |
| int32_t spvOpcodeIsValue(Op opcode) { |
| if (spvOpcodeIsPointer(opcode)) return true; |
| if (spvOpcodeIsConstant(opcode)) return true; |
| switch (opcode) { |
| case OpLoad: |
| // TODO: Other Opcode's resulting in a value |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| int32_t spvOpcodeGeneratesType(Op op) { |
| switch(op) { |
| case OpTypeVoid: |
| case OpTypeBool: |
| case OpTypeInt: |
| case OpTypeFloat: |
| case OpTypeVector: |
| case OpTypeMatrix: |
| case OpTypeImage: |
| case OpTypeSampler: |
| case OpTypeSampledImage: |
| case OpTypeArray: |
| case OpTypeRuntimeArray: |
| case OpTypeStruct: |
| case OpTypeOpaque: |
| case OpTypePointer: |
| case OpTypeFunction: |
| case OpTypeEvent: |
| case OpTypeDeviceEvent: |
| case OpTypeReserveId: |
| case OpTypeQueue: |
| case OpTypePipe: |
| case OpTypeForwardPointer: |
| return true; |
| default:; |
| } |
| return 0; |
| } |