mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-23 04:00:05 +00:00
537e77663d
We need to know how to generate correct SPIRV for cases like OpConstant %int64 42 since the current parser will encode the 42 as a 32-bit value incorrectly. This change is the first of a pair. This one tracks types, and makes sure that OpConstant and OpSpecConstant are only ever called with Integer or Float types, and OpSwitch is only called with integer generating values.
837 lines
25 KiB
C++
837 lines
25 KiB
C++
// 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 "binary.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_NUMBER;
|
|
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_NUMBER;
|
|
case OperandLiteralNumber: return SPV_OPERAND_TYPE_LITERAL_NUMBER;
|
|
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. It's only an artifact of spec generation.
|
|
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_NUMBER;
|
|
|
|
// Used by Switch
|
|
case OperandVariableLiteralId: return SPV_OPERAND_TYPE_VARIABLE_LITERAL_NUMBER_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_VALVE:
|
|
return "Valve";
|
|
case SPV_GENERATOR_LUNARG:
|
|
return "LunarG";
|
|
case SPV_GENERATOR_CODEPLAY:
|
|
return "Codeplay Software Ltd.";
|
|
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 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;
|
|
}
|