| // Copyright (c) 2014-2024 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 <assert.h> |
| #include <string.h> |
| #include <algorithm> |
| #include <cstdlib> |
| #include <iostream> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <utility> |
| #include <fstream> |
| |
| #include "jsoncpp/dist/json/json.h" |
| |
| #include "jsonToSpirv.h" |
| |
| namespace { |
| // Returns true if the given string is a valid SPIR-V version. |
| bool validSpirvVersionString(const std::string s) { |
| return |
| s == "1.0" || |
| s == "1.1" || |
| s == "1.2" || |
| s == "1.3" || |
| s == "1.4" || |
| s == "1.5" || |
| s == "1.6"; |
| } |
| |
| // Returns true if the given string is a valid version |
| // specifier in the grammar file. |
| bool validSpirvVersionStringSpecifier(const std::string s) { |
| return s == "None" || validSpirvVersionString(s); |
| } |
| } // anonymous namespace |
| |
| namespace spv { |
| |
| bool IsLegacyDoublyEnabledInstruction(const std::string& instruction) { |
| static std::unordered_set<std::string> allowed = { |
| "OpSubgroupBallotKHR", |
| "OpSubgroupFirstInvocationKHR", |
| "OpSubgroupAllKHR", |
| "OpSubgroupAnyKHR", |
| "OpSubgroupAllEqualKHR", |
| "OpSubgroupReadInvocationKHR", |
| "OpTraceRayKHR", |
| "OpExecuteCallableKHR", |
| "OpConvertUToAccelerationStructureKHR", |
| "OpIgnoreIntersectionKHR", |
| "OpTerminateRayKHR", |
| "OpTypeRayQueryKHR", |
| "OpRayQueryInitializeKHR", |
| "OpRayQueryTerminateKHR", |
| "OpRayQueryGenerateIntersectionKHR", |
| "OpRayQueryConfirmIntersectionKHR", |
| "OpRayQueryProceedKHR", |
| "OpRayQueryGetIntersectionTypeKHR", |
| "OpGroupIAddNonUniformAMD", |
| "OpGroupFAddNonUniformAMD", |
| "OpGroupFMinNonUniformAMD", |
| "OpGroupUMinNonUniformAMD", |
| "OpGroupSMinNonUniformAMD", |
| "OpGroupFMaxNonUniformAMD", |
| "OpGroupUMaxNonUniformAMD", |
| "OpGroupSMaxNonUniformAMD", |
| "OpFragmentMaskFetchAMD", |
| "OpFragmentFetchAMD", |
| "OpImageSampleFootprintNV", |
| "OpGroupNonUniformPartitionNV", |
| "OpWritePackedPrimitiveIndices4x8NV", |
| "OpReportIntersectionNV", |
| "OpReportIntersectionKHR", |
| "OpIgnoreIntersectionNV", |
| "OpTerminateRayNV", |
| "OpTraceNV", |
| "OpTraceMotionNV", |
| "OpTraceRayMotionNV", |
| "OpTypeAccelerationStructureNV", |
| "OpTypeAccelerationStructureKHR", |
| "OpExecuteCallableNV", |
| "OpTypeCooperativeMatrixNV", |
| "OpCooperativeMatrixLoadNV", |
| "OpCooperativeMatrixStoreNV", |
| "OpCooperativeMatrixMulAddNV", |
| "OpCooperativeMatrixLengthNV", |
| "OpBeginInvocationInterlockEXT", |
| "OpEndInvocationInterlockEXT", |
| "OpIsHelperInvocationEXT", |
| "OpConstantFunctionPointerINTEL", |
| "OpFunctionPointerCallINTEL", |
| "OpAssumeTrueKHR", |
| "OpExpectKHR", |
| "OpLoopControlINTEL", |
| "OpAliasDomainDeclINTEL", |
| "OpAliasScopeDeclINTEL", |
| "OpAliasScopeListDeclINTEL", |
| "OpReadPipeBlockingINTEL", |
| "OpWritePipeBlockingINTEL", |
| "OpFPGARegINTEL", |
| "OpRayQueryGetRayTMinKHR", |
| "OpRayQueryGetRayFlagsKHR", |
| "OpRayQueryGetIntersectionTKHR", |
| "OpRayQueryGetIntersectionInstanceCustomIndexKHR", |
| "OpRayQueryGetIntersectionInstanceIdKHR", |
| "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR", |
| "OpRayQueryGetIntersectionGeometryIndexKHR", |
| "OpRayQueryGetIntersectionPrimitiveIndexKHR", |
| "OpRayQueryGetIntersectionBarycentricsKHR", |
| "OpRayQueryGetIntersectionFrontFaceKHR", |
| "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR", |
| "OpRayQueryGetIntersectionObjectRayDirectionKHR", |
| "OpRayQueryGetIntersectionObjectRayOriginKHR", |
| "OpRayQueryGetWorldRayDirectionKHR", |
| "OpRayQueryGetWorldRayOriginKHR", |
| "OpRayQueryGetIntersectionObjectToWorldKHR", |
| "OpRayQueryGetIntersectionWorldToObjectKHR", |
| "OpAtomicFAddEXT", |
| }; |
| return allowed.count(instruction) != 0; |
| } |
| |
| bool EnumValue::IsValid(OperandClass oc, const std::string& context) const |
| { |
| bool result = true; |
| if (firstVersion.empty()) { |
| std::cerr << "Error: " << context << " " << name << " \"version\" must be set, probably to \"None\"" << std::endl; |
| result = false; |
| } else if (!validSpirvVersionStringSpecifier(firstVersion)) { |
| std::cerr << "Error: " << context << " " << name << " \"version\" is invalid: " << firstVersion << std::endl; |
| result = false; |
| } |
| if (!lastVersion.empty() && !validSpirvVersionString(lastVersion)) { |
| std::cerr << "Error: " << context << " " << name << " \"lastVersion\" is invalid: " << lastVersion << std::endl; |
| result = false; |
| } |
| |
| // When a feature is introduced by an extension, the firstVersion is set to |
| // "None". There are three cases: |
| // - A new capability should be guarded/enabled by the extension |
| // - A new instruction should be: |
| // - Guarded/enabled by a new capability. |
| // - Not enabled by *both* a capability and an extension. |
| // There are many existing instructions that are already like this, |
| // and we grandparent them as allowed. |
| // - Other enums fall into two cases: |
| // 1. The enum is part of a new operand kind introduced by the extension. |
| // In this case we rely on transitivity: The use of the operand occurs |
| // in a new instruction that itself is guarded; or as the operand of |
| // another operand that itself is (recursively) guarded. |
| // 2. The enum is a new case in an existing operand kind. This case |
| // should be guarded by a capability. However, we do not check this |
| // here. Checking it requires more context than we have here. |
| if (oc == OperandOpcode) { |
| const bool instruction_unusable = |
| (firstVersion == "None") && extensions.empty() && capabilities.empty(); |
| if (instruction_unusable) { |
| std::cerr << "Error: " << context << " " << name << " is not usable: " |
| << "its version is set to \"None\", and it is not enabled by a " |
| << "capability or extension. Guard it with a capability." |
| << std::endl; |
| result = false; |
| } |
| // Complain if an instruction is not in any core version and also enabled by |
| // both an extension and a capability. |
| // It's important to check the "not in any core version" case, because, |
| // for example, OpTerminateInvocation is in SPIR-V 1.6 *and* enabled by an |
| // extension, and guarded by the Shader capability. |
| const bool instruction_doubly_enabled = (firstVersion == "None") && |
| !extensions.empty() && |
| !capabilities.empty(); |
| if (instruction_doubly_enabled && !IsLegacyDoublyEnabledInstruction(name)) { |
| std::cerr << "Error: " << context << " " << name << " is doubly-enabled: " |
| << "it is enabled by both a capability and an extension. " |
| << "Guard it with a capability only." << std::endl; |
| result = false; |
| } |
| } |
| if (oc == OperandCapability) { |
| // If capability X lists capabilities Y and Z, then Y and Z are *enabled* |
| // when X is enabled. They are not *guards* on X's use. |
| // Only versions and extensions can guard a capability. |
| const bool capability_unusable = |
| (firstVersion == "None") && extensions.empty(); |
| if (capability_unusable) { |
| std::cerr << "Error: " << context << " " << name << " is not usable: " |
| << "its version is set to \"None\", and it is not enabled by " |
| << "an extension. Guard it with an extension." << std::endl; |
| result = false; |
| } |
| } |
| |
| return result; |
| } |
| |
| // The set of objects that hold all the instruction/operand |
| // parameterization information. |
| InstructionValues InstructionDesc; |
| |
| // The ordered list (in printing order) of printing classes |
| // (specification subsections). |
| PrintingClasses InstructionPrintingClasses; |
| |
| // Note: There is no entry for OperandOpcode. Use InstructionDesc instead. |
| EnumDefinition OperandClassParams[OperandOpcode]; |
| EnumValues SourceLanguageParams; |
| EnumValues ExecutionModelParams; |
| EnumValues AddressingParams; |
| EnumValues MemoryParams; |
| EnumValues ExecutionModeParams; |
| EnumValues StorageParams; |
| EnumValues SamplerAddressingModeParams; |
| EnumValues SamplerFilterModeParams; |
| EnumValues ImageFormatParams; |
| EnumValues ImageChannelOrderParams; |
| EnumValues ImageChannelDataTypeParams; |
| EnumValues ImageOperandsParams; |
| EnumValues FPFastMathParams; |
| EnumValues FPRoundingModeParams; |
| EnumValues FPDenormModeParams; |
| EnumValues FPOperationModeParams; |
| EnumValues QuantizationModesParams; |
| EnumValues OverflowModesParams; |
| EnumValues LinkageTypeParams; |
| EnumValues DecorationParams; |
| EnumValues BuiltInParams; |
| EnumValues DimensionalityParams; |
| EnumValues FuncParamAttrParams; |
| EnumValues AccessQualifierParams; |
| EnumValues GroupOperationParams; |
| EnumValues LoopControlParams; |
| EnumValues SelectionControlParams; |
| EnumValues FunctionControlParams; |
| EnumValues MemorySemanticsParams; |
| EnumValues MemoryAccessParams; |
| EnumValues ScopeParams; |
| EnumValues KernelEnqueueFlagsParams; |
| EnumValues KernelProfilingInfoParams; |
| EnumValues CapabilityParams; |
| EnumValues RayFlagsParams; |
| EnumValues RayQueryIntersectionParams; |
| EnumValues RayQueryCommittedIntersectionTypeParams; |
| EnumValues RayQueryCandidateIntersectionTypeParams; |
| EnumValues FragmentShadingRateParams; |
| EnumValues PackedVectorFormatParams; |
| EnumValues CooperativeMatrixOperandsParams; |
| EnumValues CooperativeMatrixLayoutParams; |
| EnumValues CooperativeMatrixUseParams; |
| EnumValues InitializationModeQualifierParams; |
| EnumValues HostAccessQualifierParams; |
| EnumValues LoadCacheControlParams; |
| EnumValues StoreCacheControlParams; |
| EnumValues NamedMaximumNumberOfRegistersParams; |
| EnumValues RawAccessChainOperandsParams; |
| |
| std::pair<bool, std::string> ReadFile(const std::string& path) |
| { |
| std::ifstream fstream(path, std::ios::in); |
| if (fstream) { |
| std::string contents; |
| fstream.seekg(0, std::ios::end); |
| contents.reserve((unsigned int)fstream.tellg()); |
| fstream.seekg(0, std::ios::beg); |
| contents.assign((std::istreambuf_iterator<char>(fstream)), |
| std::istreambuf_iterator<char>()); |
| return std::make_pair(true, contents); |
| } |
| return std::make_pair(false, ""); |
| } |
| |
| struct ClassOptionality { |
| OperandClass type; |
| bool optional; |
| }; |
| |
| // Converts the |operandKind| and |quantifier| pair used to describe operands |
| // in the JSON grammar to OperandClass and optionality used in this repo. |
| ClassOptionality ToOperandClassAndOptionality(const std::string& operandKind, const std::string& quantifier) |
| { |
| assert(quantifier.empty() || quantifier == "?" || quantifier == "*"); |
| |
| if (operandKind == "IdRef") { |
| if (quantifier.empty()) |
| return {OperandId, false}; |
| else if (quantifier == "?") |
| return {OperandId, true}; |
| else |
| return {OperandVariableIds, false}; |
| } else if (operandKind == "LiteralInteger") { |
| if (quantifier.empty()) |
| return {OperandLiteralNumber, false}; |
| if (quantifier == "?") |
| return {OperandOptionalLiteral, true}; |
| else |
| return {OperandVariableLiterals, false}; |
| } else if (operandKind == "LiteralString") { |
| if (quantifier.empty()) |
| return {OperandLiteralString, false}; |
| else if (quantifier == "?") |
| return {OperandLiteralString, true}; |
| else { |
| return {OperandOptionalLiteralStrings, false}; |
| } |
| } else if (operandKind == "PairLiteralIntegerIdRef") { |
| // Used by OpSwitch in the grammar |
| return {OperandVariableLiteralId, false}; |
| } else if (operandKind == "PairIdRefLiteralInteger") { |
| // Used by OpGroupMemberDecorate in the grammar |
| return {OperandVariableIdLiteral, false}; |
| } else if (operandKind == "PairIdRefIdRef") { |
| // Used by OpPhi in the grammar |
| return {OperandVariableIds, false}; |
| } else { |
| OperandClass type = OperandNone; |
| if (operandKind == "IdMemorySemantics" || operandKind == "MemorySemantics") { |
| type = OperandMemorySemantics; |
| } else if (operandKind == "IdScope" || operandKind == "Scope") { |
| type = OperandScope; |
| } else if (operandKind == "LiteralExtInstInteger") { |
| type = OperandLiteralNumber; |
| } else if (operandKind == "LiteralSpecConstantOpInteger") { |
| type = OperandLiteralNumber; |
| } else if (operandKind == "LiteralContextDependentNumber") { |
| type = OperandAnySizeLiteralNumber; |
| } else if (operandKind == "LiteralFloat") { |
| type = OperandLiteralNumber; |
| } else if (operandKind == "SourceLanguage") { |
| type = OperandSource; |
| } else if (operandKind == "ExecutionModel") { |
| type = OperandExecutionModel; |
| } else if (operandKind == "AddressingModel") { |
| type = OperandAddressing; |
| } else if (operandKind == "MemoryModel") { |
| type = OperandMemory; |
| } else if (operandKind == "ExecutionMode") { |
| type = OperandExecutionMode; |
| } else if (operandKind == "StorageClass") { |
| type = OperandStorage; |
| } else if (operandKind == "Dim") { |
| type = OperandDimensionality; |
| } else if (operandKind == "SamplerAddressingMode") { |
| type = OperandSamplerAddressingMode; |
| } else if (operandKind == "SamplerFilterMode") { |
| type = OperandSamplerFilterMode; |
| } else if (operandKind == "ImageFormat") { |
| type = OperandSamplerImageFormat; |
| } else if (operandKind == "ImageChannelOrder") { |
| type = OperandImageChannelOrder; |
| } else if (operandKind == "ImageChannelDataType") { |
| type = OperandImageChannelDataType; |
| } else if (operandKind == "FPRoundingMode") { |
| type = OperandFPRoundingMode; |
| } else if (operandKind == "FPDenormMode") { |
| type = OperandFPDenormMode; |
| } else if (operandKind == "FPOperationMode") { |
| type = OperandFPOperationMode; |
| } else if (operandKind == "QuantizationModes") { |
| type = OperandQuantizationModes; |
| } else if (operandKind == "OverflowModes") { |
| type = OperandOverflowModes; |
| } else if (operandKind == "LinkageType") { |
| type = OperandLinkageType; |
| } else if (operandKind == "AccessQualifier") { |
| type = OperandAccessQualifier; |
| } else if (operandKind == "FunctionParameterAttribute") { |
| type = OperandFuncParamAttr; |
| } else if (operandKind == "Decoration") { |
| type = OperandDecoration; |
| } else if (operandKind == "BuiltIn") { |
| type = OperandBuiltIn; |
| } else if (operandKind == "GroupOperation") { |
| type = OperandGroupOperation; |
| } else if (operandKind == "KernelEnqueueFlags") { |
| type = OperandKernelEnqueueFlags; |
| } else if (operandKind == "KernelProfilingInfo") { |
| type = OperandKernelProfilingInfo; |
| } else if (operandKind == "Capability") { |
| type = OperandCapability; |
| } else if (operandKind == "ImageOperands") { |
| type = OperandImageOperands; |
| } else if (operandKind == "FPFastMathMode") { |
| type = OperandFPFastMath; |
| } else if (operandKind == "SelectionControl") { |
| type = OperandSelect; |
| } else if (operandKind == "LoopControl") { |
| type = OperandLoop; |
| } else if (operandKind == "FunctionControl") { |
| type = OperandFunction; |
| } else if (operandKind == "MemoryAccess") { |
| type = OperandMemoryOperands; |
| } else if (operandKind == "RayFlags") { |
| type = OperandRayFlags; |
| } else if (operandKind == "RayQueryIntersection") { |
| type = OperandRayQueryIntersection; |
| } else if (operandKind == "RayQueryCommittedIntersectionType") { |
| type = OperandRayQueryCommittedIntersectionType; |
| } else if (operandKind == "RayQueryCandidateIntersectionType") { |
| type = OperandRayQueryCandidateIntersectionType; |
| } else if (operandKind == "FragmentShadingRate") { |
| type = OperandFragmentShadingRate; |
| } else if (operandKind == "PackedVectorFormat") { |
| type = OperandPackedVectorFormat; |
| } else if (operandKind == "CooperativeMatrixOperands") { |
| type = OperandCooperativeMatrixOperands; |
| } else if (operandKind == "CooperativeMatrixLayout") { |
| type = OperandCooperativeMatrixLayout; |
| } else if (operandKind == "CooperativeMatrixUse") { |
| type = OperandCooperativeMatrixUse; |
| } else if (operandKind == "InitializationModeQualifier") { |
| type = OperandInitializationModeQualifier; |
| } else if (operandKind == "HostAccessQualifier") { |
| type = OperandHostAccessQualifier; |
| } else if (operandKind == "LoadCacheControl") { |
| type = OperandLoadCacheControl; |
| } else if (operandKind == "StoreCacheControl") { |
| type = OperandStoreCacheControl; |
| } else if (operandKind == "NamedMaximumNumberOfRegisters") { |
| type = OperandNamedMaximumNumberOfRegisters; |
| } else if (operandKind == "RawAccessChainOperands") { |
| type = OperandRawAccessChainOperands; |
| } |
| |
| if (type == OperandNone) { |
| std::cerr << "Unhandled operand kind found: " << operandKind << std::endl; |
| exit(1); |
| } |
| return {type, !quantifier.empty()}; |
| } |
| } |
| |
| bool IsTypeOrResultId(const std::string& str, bool* isType, bool* isResult) |
| { |
| if (str == "IdResultType") |
| return *isType = true; |
| if (str == "IdResult") |
| return *isResult = true; |
| return false; |
| } |
| |
| // Given a number string, returns the position of the only bits set in the number. |
| // So it requires the number is a power of two. |
| unsigned int NumberStringToBit(const std::string& str) |
| { |
| char* parseEnd; |
| unsigned int value = (unsigned int)std::strtol(str.c_str(), &parseEnd, 16); |
| assert(!(value & (value - 1)) && "input number is not a power of 2"); |
| unsigned int bit = 0; |
| for (; value; value >>= 1) ++bit; |
| return bit; |
| } |
| |
| void jsonToSpirv(const std::string& jsonPath, bool buildingHeaders) |
| { |
| // only do this once. |
| static bool initialized = false; |
| if (initialized) |
| return; |
| initialized = true; |
| |
| size_t errorCount = 0; |
| |
| // Read the JSON grammar file. |
| bool fileReadOk = false; |
| std::string content; |
| std::tie(fileReadOk, content) = ReadFile(jsonPath); |
| if (!fileReadOk) { |
| std::cerr << "Failed to read JSON grammar file: " |
| << jsonPath << std::endl; |
| exit(1); |
| } |
| |
| // Decode the JSON grammar file. |
| Json::Reader reader; |
| Json::Value root; |
| if (!reader.parse(content, root)) { |
| std::cerr << "Failed to parse JSON grammar:\n" |
| << reader.getFormattedErrorMessages(); |
| exit(1); |
| } |
| |
| // Layouts for all instructions. |
| |
| // A lambda for returning capabilities from a JSON object as strings. |
| const auto getCaps = [](const Json::Value& object) { |
| EnumCaps result; |
| const auto& caps = object["capabilities"]; |
| if (!caps.empty()) { |
| assert(caps.isArray()); |
| for (const auto& cap : caps) { |
| result.emplace_back(cap.asString()); |
| } |
| } |
| return result; |
| }; |
| |
| // A lambda for returning extensions from a JSON object as strings. |
| const auto getExts = [](const Json::Value& object) { |
| Extensions result; |
| const auto& exts = object["extensions"]; |
| if (!exts.empty()) { |
| assert(exts.isArray()); |
| for (const auto& ext : exts) { |
| result.emplace_back(ext.asString()); |
| } |
| } |
| return result; |
| }; |
| |
| // set up the printing classes |
| std::unordered_set<std::string> tags; // short-lived local for error checking below |
| const Json::Value printingClasses = root["instruction_printing_class"]; |
| for (const auto& printingClass : printingClasses) { |
| if (printingClass["tag"].asString().size() > 0) |
| tags.insert(printingClass["tag"].asString()); // just for error checking |
| else { |
| std::cerr << "Error: each instruction_printing_class requires a non-empty \"tag\"" << std::endl; |
| std::exit(1); |
| } |
| if (buildingHeaders || printingClass["tag"].asString() != "@exclude") { |
| InstructionPrintingClasses.push_back({printingClass["tag"].asString(), |
| printingClass["heading"].asString()}); |
| } |
| } |
| |
| // process the instructions |
| const Json::Value insts = root["instructions"]; |
| unsigned maxOpcode = 0; |
| bool firstOpcode = true; |
| for (const auto& inst : insts) { |
| const auto printingClass = inst["class"].asString(); |
| if (printingClass.size() == 0) { |
| std::cerr << "Error: " << inst["opname"].asString() |
| << " requires a non-empty printing \"class\" tag" << std::endl; |
| std::exit(1); |
| } |
| if (!buildingHeaders && printingClass == "@exclude") |
| continue; |
| if (tags.find(printingClass) == tags.end()) { |
| std::cerr << "Error: " << inst["opname"].asString() |
| << " requires a \"class\" declared as a \"tag\" in \"instruction printing_class\"" |
| << std::endl; |
| std::exit(1); |
| } |
| const auto opcode = inst["opcode"].asUInt(); |
| const std::string name = inst["opname"].asString(); |
| if (firstOpcode) { |
| maxOpcode = opcode; |
| firstOpcode = false; |
| } else { |
| if (maxOpcode > opcode) { |
| std::cerr << "Error: " << name |
| << " is out of order. It follows the instruction with opcode " << maxOpcode |
| << std::endl; |
| std::exit(1); |
| } else { |
| maxOpcode = opcode; |
| } |
| } |
| EnumCaps caps = getCaps(inst); |
| std::string version = inst["version"].asString(); |
| std::string lastVersion = inst["lastVersion"].asString(); |
| Extensions exts = getExts(inst); |
| OperandParameters operands; |
| bool defResultId = false; |
| bool defTypeId = false; |
| for (const auto& operand : inst["operands"]) { |
| const std::string kind = operand["kind"].asString(); |
| const std::string quantifier = operand.get("quantifier", "").asString(); |
| const std::string doc = operand.get("name", "").asString(); |
| if (!IsTypeOrResultId(kind, &defTypeId, &defResultId)) { |
| const auto p = ToOperandClassAndOptionality(kind, quantifier); |
| operands.push(p.type, doc, p.optional); |
| } |
| } |
| InstructionDesc.emplace_back( |
| std::move(EnumValue(opcode, name, |
| std::move(caps), std::move(version), std::move(lastVersion), std::move(exts), |
| std::move(operands))), |
| printingClass, defTypeId, defResultId); |
| if (!InstructionDesc.back().IsValid(OperandOpcode, "instruction")) { |
| errorCount++; |
| } |
| } |
| |
| // Specific additional context-dependent operands |
| |
| // Populate dest with EnumValue objects constructed from source. |
| const auto populateEnumValues = [&getCaps,&getExts,&errorCount](EnumValues* dest, const Json::Value& source, bool bitEnum) { |
| // A lambda for determining the numeric value to be used for a given |
| // enumerant in JSON form, and whether that value is a 0 in a bitfield. |
| auto getValue = [&bitEnum](const Json::Value& enumerant) { |
| std::pair<unsigned, bool> result{0u,false}; |
| if (!bitEnum) { |
| result.first = enumerant["value"].asUInt(); |
| } else { |
| const unsigned int bit = NumberStringToBit(enumerant["value"].asString()); |
| if (bit == 0) |
| result.second = true; |
| else |
| result.first = bit - 1; // This is the *shift* amount. |
| } |
| return result; |
| }; |
| |
| unsigned maxValue = 0; |
| bool firstValue = true; |
| for (const auto& enumerant : source["enumerants"]) { |
| unsigned value; |
| bool skip_zero_in_bitfield; |
| std::tie(value, skip_zero_in_bitfield) = getValue(enumerant); |
| if (skip_zero_in_bitfield) |
| continue; |
| if (firstValue) { |
| maxValue = value; |
| firstValue = false; |
| } else { |
| if (maxValue > value) { |
| std::cerr << "Error: " << source["kind"] << " enumerant " << enumerant["enumerant"] |
| << " is out of order. It has value " << value |
| << " but follows the enumerant with value " << maxValue << std::endl; |
| std::exit(1); |
| } else { |
| maxValue = value; |
| } |
| } |
| EnumCaps caps(getCaps(enumerant)); |
| std::string version = enumerant["version"].asString(); |
| std::string lastVersion = enumerant["lastVersion"].asString(); |
| Extensions exts(getExts(enumerant)); |
| OperandParameters params; |
| const Json::Value& paramsJson = enumerant["parameters"]; |
| if (!paramsJson.empty()) { // This enumerant has parameters. |
| assert(paramsJson.isArray()); |
| for (const auto& param : paramsJson) { |
| const std::string kind = param["kind"].asString(); |
| const std::string doc = param.get("name", "").asString(); |
| const auto p = ToOperandClassAndOptionality(kind, ""); // All parameters are required! |
| params.push(p.type, doc); |
| } |
| } |
| dest->emplace_back( |
| value, enumerant["enumerant"].asString(), |
| std::move(caps), std::move(version), std::move(lastVersion), std::move(exts), std::move(params)); |
| } |
| }; |
| |
| const auto establishOperandClass = [&populateEnumValues,&errorCount]( |
| const std::string& enumName, spv::OperandClass operandClass, |
| spv::EnumValues* enumValues, const Json::Value& operandEnum, const std::string& category) { |
| assert(category == "BitEnum" || category == "ValueEnum"); |
| bool bitEnum = (category == "BitEnum"); |
| if (!operandEnum["version"].empty()) { |
| std::cerr << "Error: container for " << enumName << " operand_kind must not have a version field" << std::endl; |
| errorCount++; |
| } |
| populateEnumValues(enumValues, operandEnum, bitEnum); |
| const std::string errContext = "enum " + enumName; |
| for (const auto& e: *enumValues) { |
| if (!e.IsValid(operandClass, errContext)) { |
| errorCount++; |
| } |
| } |
| OperandClassParams[operandClass].set(enumName, enumValues, bitEnum); |
| }; |
| |
| const Json::Value operandEnums = root["operand_kinds"]; |
| for (const auto& operandEnum : operandEnums) { |
| const std::string enumName = operandEnum["kind"].asString(); |
| const std::string category = operandEnum["category"].asString(); |
| if (enumName == "SourceLanguage") { |
| establishOperandClass(enumName, OperandSource, &SourceLanguageParams, operandEnum, category); |
| } else if (enumName == "Decoration") { |
| establishOperandClass(enumName, OperandDecoration, &DecorationParams, operandEnum, category); |
| } else if (enumName == "ExecutionMode") { |
| establishOperandClass(enumName, OperandExecutionMode, &ExecutionModeParams, operandEnum, category); |
| } else if (enumName == "Capability") { |
| establishOperandClass(enumName, OperandCapability, &CapabilityParams, operandEnum, category); |
| } else if (enumName == "AddressingModel") { |
| establishOperandClass(enumName, OperandAddressing, &AddressingParams, operandEnum, category); |
| } else if (enumName == "MemoryModel") { |
| establishOperandClass(enumName, OperandMemory, &MemoryParams, operandEnum, category); |
| } else if (enumName == "MemorySemantics") { |
| establishOperandClass(enumName, OperandMemorySemantics, &MemorySemanticsParams, operandEnum, category); |
| } else if (enumName == "ExecutionModel") { |
| establishOperandClass(enumName, OperandExecutionModel, &ExecutionModelParams, operandEnum, category); |
| } else if (enumName == "StorageClass") { |
| establishOperandClass(enumName, OperandStorage, &StorageParams, operandEnum, category); |
| } else if (enumName == "SamplerAddressingMode") { |
| establishOperandClass(enumName, OperandSamplerAddressingMode, &SamplerAddressingModeParams, operandEnum, category); |
| } else if (enumName == "SamplerFilterMode") { |
| establishOperandClass(enumName, OperandSamplerFilterMode, &SamplerFilterModeParams, operandEnum, category); |
| } else if (enumName == "ImageFormat") { |
| establishOperandClass(enumName, OperandSamplerImageFormat, &ImageFormatParams, operandEnum, category); |
| } else if (enumName == "ImageChannelOrder") { |
| establishOperandClass(enumName, OperandImageChannelOrder, &ImageChannelOrderParams, operandEnum, category); |
| } else if (enumName == "ImageChannelDataType") { |
| establishOperandClass(enumName, OperandImageChannelDataType, &ImageChannelDataTypeParams, operandEnum, category); |
| } else if (enumName == "ImageOperands") { |
| establishOperandClass(enumName, OperandImageOperands, &ImageOperandsParams, operandEnum, category); |
| } else if (enumName == "FPFastMathMode") { |
| establishOperandClass(enumName, OperandFPFastMath, &FPFastMathParams, operandEnum, category); |
| } else if (enumName == "FPRoundingMode") { |
| establishOperandClass(enumName, OperandFPRoundingMode, &FPRoundingModeParams, operandEnum, category); |
| } else if (enumName == "FPDenormMode") { |
| establishOperandClass(enumName, OperandFPDenormMode, &FPDenormModeParams, operandEnum, category); |
| } else if (enumName == "FPOperationMode") { |
| establishOperandClass(enumName, OperandFPOperationMode, &FPOperationModeParams, operandEnum, category); |
| } else if (enumName == "QuantizationModes") { |
| establishOperandClass(enumName, OperandQuantizationModes, &QuantizationModesParams, operandEnum, category); |
| } else if (enumName == "OverflowModes") { |
| establishOperandClass(enumName, OperandOverflowModes, &OverflowModesParams, operandEnum, category); |
| } else if (enumName == "LinkageType") { |
| establishOperandClass(enumName, OperandLinkageType, &LinkageTypeParams, operandEnum, category); |
| } else if (enumName == "FunctionParameterAttribute") { |
| establishOperandClass(enumName, OperandFuncParamAttr, &FuncParamAttrParams, operandEnum, category); |
| } else if (enumName == "AccessQualifier") { |
| establishOperandClass(enumName, OperandAccessQualifier, &AccessQualifierParams, operandEnum, category); |
| } else if (enumName == "BuiltIn") { |
| establishOperandClass(enumName, OperandBuiltIn, &BuiltInParams, operandEnum, category); |
| } else if (enumName == "SelectionControl") { |
| establishOperandClass(enumName, OperandSelect, &SelectionControlParams, operandEnum, category); |
| } else if (enumName == "LoopControl") { |
| establishOperandClass(enumName, OperandLoop, &LoopControlParams, operandEnum, category); |
| } else if (enumName == "FunctionControl") { |
| establishOperandClass(enumName, OperandFunction, &FunctionControlParams, operandEnum, category); |
| } else if (enumName == "Dim") { |
| establishOperandClass(enumName, OperandDimensionality, &DimensionalityParams, operandEnum, category); |
| } else if (enumName == "MemoryAccess") { |
| establishOperandClass(enumName, OperandMemoryOperands, &MemoryAccessParams, operandEnum, category); |
| } else if (enumName == "Scope") { |
| establishOperandClass(enumName, OperandScope, &ScopeParams, operandEnum, category); |
| } else if (enumName == "GroupOperation") { |
| establishOperandClass(enumName, OperandGroupOperation, &GroupOperationParams, operandEnum, category); |
| } else if (enumName == "KernelEnqueueFlags") { |
| establishOperandClass(enumName, OperandKernelEnqueueFlags, &KernelEnqueueFlagsParams, operandEnum, category); |
| } else if (enumName == "KernelProfilingInfo") { |
| establishOperandClass(enumName, OperandKernelProfilingInfo, &KernelProfilingInfoParams, operandEnum, category); |
| } else if (enumName == "RayFlags") { |
| establishOperandClass(enumName, OperandRayFlags, &RayFlagsParams, operandEnum, category); |
| } else if (enumName == "RayQueryIntersection") { |
| establishOperandClass(enumName, OperandRayQueryIntersection, &RayQueryIntersectionParams, operandEnum, category); |
| } else if (enumName == "RayQueryCommittedIntersectionType") { |
| establishOperandClass(enumName, OperandRayQueryCommittedIntersectionType, &RayQueryCommittedIntersectionTypeParams, operandEnum, category); |
| } else if (enumName == "RayQueryCandidateIntersectionType") { |
| establishOperandClass(enumName, OperandRayQueryCandidateIntersectionType, &RayQueryCandidateIntersectionTypeParams, operandEnum, category); |
| } else if (enumName == "FragmentShadingRate") { |
| establishOperandClass(enumName, OperandFragmentShadingRate, &FragmentShadingRateParams, operandEnum, category); |
| } else if (enumName == "PackedVectorFormat") { |
| establishOperandClass(enumName, OperandPackedVectorFormat, &PackedVectorFormatParams, operandEnum, category); |
| } else if (enumName == "CooperativeMatrixOperands") { |
| establishOperandClass(enumName, OperandCooperativeMatrixOperands, &CooperativeMatrixOperandsParams, operandEnum, category); |
| } else if (enumName == "CooperativeMatrixLayout") { |
| establishOperandClass(enumName, OperandCooperativeMatrixLayout, &CooperativeMatrixLayoutParams, operandEnum, category); |
| } else if (enumName == "CooperativeMatrixUse") { |
| establishOperandClass(enumName, OperandCooperativeMatrixUse, &CooperativeMatrixUseParams, operandEnum, category); |
| } else if (enumName == "InitializationModeQualifier") { |
| establishOperandClass(enumName, OperandInitializationModeQualifier, &InitializationModeQualifierParams, operandEnum, category); |
| } else if (enumName == "HostAccessQualifier") { |
| establishOperandClass(enumName, OperandHostAccessQualifier, &HostAccessQualifierParams, operandEnum, category); |
| } else if (enumName == "LoadCacheControl") { |
| establishOperandClass(enumName, OperandLoadCacheControl, &LoadCacheControlParams, operandEnum, category); |
| } else if (enumName == "StoreCacheControl") { |
| establishOperandClass(enumName, OperandStoreCacheControl, &StoreCacheControlParams, operandEnum, category); |
| } else if (enumName == "NamedMaximumNumberOfRegisters") { |
| establishOperandClass(enumName, OperandNamedMaximumNumberOfRegisters, &NamedMaximumNumberOfRegistersParams, operandEnum, category); |
| } else if (enumName == "RawAccessChainOperands") { |
| establishOperandClass(enumName, OperandRawAccessChainOperands, &RawAccessChainOperandsParams, operandEnum, category); |
| } |
| } |
| |
| if (errorCount > 0) { |
| std::exit(1); |
| } |
| } |
| |
| }; // end namespace spv |