// Copyright (c) 2014-2020 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 #include #include #include #include #include #include #include #include #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 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; std::pair 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(fstream)), std::istreambuf_iterator()); 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; } 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 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; 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; } 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; } 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 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); } } if (errorCount > 0) { std::exit(1); } } }; // end namespace spv