diff --git a/source/enum_set.h b/source/enum_set.h index 6d3ec73b..b956b495 100644 --- a/source/enum_set.h +++ b/source/enum_set.h @@ -46,6 +46,9 @@ class EnumSet { EnumSet(std::initializer_list cs) { for (auto c : cs) Add(c); } + EnumSet(uint32_t count, const EnumType* ptr) { + for (uint32_t i = 0; i < count; ++i) Add(ptr[i]); + } // Copy constructor. EnumSet(const EnumSet& other) { *this = other; } // Move constructor. The moved-from set is emptied. @@ -95,15 +98,12 @@ class EnumSet { bool HasAnyOf(const EnumSet& in_set) const { if (in_set.IsEmpty()) return true; - if (mask_ & in_set.mask_) - return true; + if (mask_ & in_set.mask_) return true; - if (!overflow_ || !in_set.overflow_) - return false; + if (!overflow_ || !in_set.overflow_) return false; for (uint32_t item : *in_set.overflow_) { - if (overflow_->find(item) != overflow_->end()) - return true; + if (overflow_->find(item) != overflow_->end()) return true; } return false; diff --git a/source/enum_string_mapping.h b/source/enum_string_mapping.h index 773d40c4..c9ac58ad 100644 --- a/source/enum_string_mapping.h +++ b/source/enum_string_mapping.h @@ -19,9 +19,9 @@ #include "spirv/1.1/spirv.h" -namespace libspirv { +#include "extensions.h" -enum class Extension; +namespace libspirv { // Finds Extension enum corresponding to |str|. Returns false if not found. bool GetExtensionFromString(const std::string& str, Extension* extension); diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp index 71f72fc9..7942c47d 100644 --- a/source/ext_inst.cpp +++ b/source/ext_inst.cpp @@ -23,56 +23,36 @@ #include "macro.h" +#include "glsl.std.450.insts-1.0.inc" // defines glsl_entries +#include "opencl.std.insts-1.0.inc" // defines opencl_entries + +#include "spv-amd-gcn-shader.insts.inc" +#include "spv-amd-shader-ballot.insts.inc" +#include "spv-amd-shader-explicit-vertex-parameter.insts.inc" +#include "spv-amd-shader-trinary-minmax.insts.inc" + +static const spv_ext_inst_group_t kGroups_1_0[] = { + {SPV_EXT_INST_TYPE_GLSL_STD_450, ARRAY_SIZE(glsl_entries), glsl_entries}, + {SPV_EXT_INST_TYPE_OPENCL_STD, ARRAY_SIZE(opencl_entries), opencl_entries}, + {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER, + ARRAY_SIZE(spv_amd_shader_explicit_vertex_parameter_entries), + spv_amd_shader_explicit_vertex_parameter_entries}, + {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_TRINARY_MINMAX, + ARRAY_SIZE(spv_amd_shader_trinary_minmax_entries), + spv_amd_shader_trinary_minmax_entries}, + {SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER, + ARRAY_SIZE(spv_amd_gcn_shader_entries), spv_amd_gcn_shader_entries}, + {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT, + ARRAY_SIZE(spv_amd_shader_ballot_entries), spv_amd_shader_ballot_entries}, +}; + +static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0), + kGroups_1_0}; + spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable, spv_target_env env) { if (!pExtInstTable) return SPV_ERROR_INVALID_POINTER; - static const spv_ext_inst_desc_t glslStd450Entries_1_0[] = { -#include "glsl.std.450.insts-1.0.inc" - }; - - static const spv_ext_inst_desc_t openclEntries_1_0[] = { -#include "opencl.std.insts-1.0.inc" - }; - - static const spv_ext_inst_desc_t - spv_amd_shader_explicit_vertex_parameter_entries[] = { -#include "spv-amd-shader-explicit-vertex-parameter.insts.inc" - }; - - static const spv_ext_inst_desc_t spv_amd_shader_trinary_minmax_entries[] = { -#include "spv-amd-shader-trinary-minmax.insts.inc" - }; - - static const spv_ext_inst_desc_t spv_amd_gcn_shader_entries[] = { -#include "spv-amd-gcn-shader.insts.inc" - }; - - static const spv_ext_inst_desc_t spv_amd_shader_ballot_entries[] = { -#include "spv-amd-shader-ballot.insts.inc" - }; - - static const spv_ext_inst_group_t groups_1_0[] = { - {SPV_EXT_INST_TYPE_GLSL_STD_450, ARRAY_SIZE(glslStd450Entries_1_0), - glslStd450Entries_1_0}, - {SPV_EXT_INST_TYPE_OPENCL_STD, ARRAY_SIZE(openclEntries_1_0), - openclEntries_1_0}, - {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER, - ARRAY_SIZE(spv_amd_shader_explicit_vertex_parameter_entries), - spv_amd_shader_explicit_vertex_parameter_entries}, - {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_TRINARY_MINMAX, - ARRAY_SIZE(spv_amd_shader_trinary_minmax_entries), - spv_amd_shader_trinary_minmax_entries}, - {SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER, - ARRAY_SIZE(spv_amd_gcn_shader_entries), spv_amd_gcn_shader_entries}, - {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT, - ARRAY_SIZE(spv_amd_shader_ballot_entries), - spv_amd_shader_ballot_entries}, - }; - - static const spv_ext_inst_table_t table_1_0 = {ARRAY_SIZE(groups_1_0), - groups_1_0}; - switch (env) { // The extended instruction sets are all version 1.0 so far. case SPV_ENV_UNIVERSAL_1_0: @@ -86,7 +66,7 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable, case SPV_ENV_OPENGL_4_2: case SPV_ENV_OPENGL_4_3: case SPV_ENV_OPENGL_4_5: - *pExtInstTable = &table_1_0; + *pExtInstTable = &kTable_1_0; return SPV_SUCCESS; default: assert(0 && "Unknown spv_target_env in spvExtInstTableGet()"); diff --git a/source/extensions.h b/source/extensions.h index 2eee7cfa..9947f1a9 100644 --- a/source/extensions.h +++ b/source/extensions.h @@ -23,7 +23,7 @@ namespace libspirv { // The known SPIR-V extensions. -enum class Extension { +enum Extension { #include "extension_enum.inc" }; diff --git a/source/opcode.cpp b/source/opcode.cpp index 9bb8cb73..6c779d15 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -32,13 +32,16 @@ struct OpcodeDescPtrLen { uint32_t len; }; -OpcodeDescPtrLen getOpcodeTableEntries_1_2() { - static const spv_opcode_desc_t opcodeTableEntries_1_2[] = { -#include "core.insts-1.2.inc" - }; +#include "core.insts-1.0.inc" // defines kOpcodeTableEntries_1_0 +#include "core.insts-1.1.inc" // defines kOpcodeTableEntries_1_1 +#include "core.insts-1.2.inc" // defines kOpcodeTableEntries_1_2 - return {opcodeTableEntries_1_2, ARRAY_SIZE(opcodeTableEntries_1_2)}; -} +static const spv_opcode_table_t kTable_1_0 = { + ARRAY_SIZE(kOpcodeTableEntries_1_0), kOpcodeTableEntries_1_0}; +static const spv_opcode_table_t kTable_1_1 = { + ARRAY_SIZE(kOpcodeTableEntries_1_1), kOpcodeTableEntries_1_1}; +static const spv_opcode_table_t kTable_1_2 = { + ARRAY_SIZE(kOpcodeTableEntries_1_2), kOpcodeTableEntries_1_2}; // Represents a vendor tool entry in the SPIR-V XML Regsitry. struct VendorTool { @@ -84,20 +87,6 @@ spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable, // Descriptions of each opcode. Each entry describes the format of the // instruction that follows a particular opcode. - static const spv_opcode_desc_t opcodeTableEntries_1_0[] = { -#include "core.insts-1.0.inc" - }; - static const spv_opcode_desc_t opcodeTableEntries_1_1[] = { -#include "core.insts-1.1.inc" - }; - - const auto ptr_len = getOpcodeTableEntries_1_2(); - - static const spv_opcode_table_t table_1_0 = { - ARRAY_SIZE(opcodeTableEntries_1_0), opcodeTableEntries_1_0}; - static const spv_opcode_table_t table_1_1 = { - ARRAY_SIZE(opcodeTableEntries_1_1), opcodeTableEntries_1_1}; - static const spv_opcode_table_t table_1_2 = {ptr_len.len, ptr_len.ptr}; switch (env) { case SPV_ENV_UNIVERSAL_1_0: @@ -108,14 +97,14 @@ spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable, case SPV_ENV_OPENGL_4_2: case SPV_ENV_OPENGL_4_3: case SPV_ENV_OPENGL_4_5: - *pInstTable = &table_1_0; + *pInstTable = &kTable_1_0; return SPV_SUCCESS; case SPV_ENV_UNIVERSAL_1_1: - *pInstTable = &table_1_1; + *pInstTable = &kTable_1_1; return SPV_SUCCESS; case SPV_ENV_UNIVERSAL_1_2: case SPV_ENV_OPENCL_2_2: - *pInstTable = &table_1_2; + *pInstTable = &kTable_1_2; return SPV_SUCCESS; } assert(0 && "Unknown spv_target_env in spvOpcodeTableGet()"); @@ -183,10 +172,9 @@ void spvInstructionCopy(const uint32_t* words, const SpvOp opcode, const char* spvOpcodeString(const SpvOp opcode) { // Use the latest SPIR-V version, which should be backward-compatible with all // previous ones. - const auto entries = getOpcodeTableEntries_1_2(); - - for (uint32_t i = 0; i < entries.len; ++i) { - if (entries.ptr[i].opcode == opcode) return entries.ptr[i].name; + for (uint32_t i = 0; i < ARRAY_SIZE(kOpcodeTableEntries_1_2); ++i) { + if (kOpcodeTableEntries_1_2[i].opcode == opcode) + return kOpcodeTableEntries_1_2[i].name; } assert(0 && "Unreachable!"); diff --git a/source/operand.cpp b/source/operand.cpp index 3176eb7d..d406199a 100644 --- a/source/operand.cpp +++ b/source/operand.cpp @@ -20,23 +20,23 @@ #include "macro.h" -spv_result_t spvOperandTableGet(spv_operand_table* pOperandTable, - spv_target_env env) { - if (!pOperandTable) return SPV_ERROR_INVALID_POINTER; - #include "operand.kinds-1.0.inc" #include "operand.kinds-1.1.inc" #include "operand.kinds-1.2.inc" - static const spv_operand_table_t table_1_0 = { - ARRAY_SIZE(pygen_variable_OperandInfoTable_1_0), - pygen_variable_OperandInfoTable_1_0}; - static const spv_operand_table_t table_1_1 = { - ARRAY_SIZE(pygen_variable_OperandInfoTable_1_1), - pygen_variable_OperandInfoTable_1_1}; - static const spv_operand_table_t table_1_2 = { - ARRAY_SIZE(pygen_variable_OperandInfoTable_1_2), - pygen_variable_OperandInfoTable_1_2}; +static const spv_operand_table_t kTable_1_0 = { + ARRAY_SIZE(pygen_variable_OperandInfoTable_1_0), + pygen_variable_OperandInfoTable_1_0}; +static const spv_operand_table_t kTable_1_1 = { + ARRAY_SIZE(pygen_variable_OperandInfoTable_1_1), + pygen_variable_OperandInfoTable_1_1}; +static const spv_operand_table_t kTable_1_2 = { + ARRAY_SIZE(pygen_variable_OperandInfoTable_1_2), + pygen_variable_OperandInfoTable_1_2}; + +spv_result_t spvOperandTableGet(spv_operand_table* pOperandTable, + spv_target_env env) { + if (!pOperandTable) return SPV_ERROR_INVALID_POINTER; switch (env) { case SPV_ENV_UNIVERSAL_1_0: @@ -47,14 +47,14 @@ spv_result_t spvOperandTableGet(spv_operand_table* pOperandTable, case SPV_ENV_OPENGL_4_2: case SPV_ENV_OPENGL_4_3: case SPV_ENV_OPENGL_4_5: - *pOperandTable = &table_1_0; + *pOperandTable = &kTable_1_0; return SPV_SUCCESS; case SPV_ENV_UNIVERSAL_1_1: - *pOperandTable = &table_1_1; + *pOperandTable = &kTable_1_1; return SPV_SUCCESS; case SPV_ENV_UNIVERSAL_1_2: case SPV_ENV_OPENCL_2_2: - *pOperandTable = &table_1_2; + *pOperandTable = &kTable_1_2; return SPV_SUCCESS; } assert(0 && "Unknown spv_target_env in spvOperandTableGet()"); diff --git a/source/table.h b/source/table.h index a7dffaaf..ff952096 100644 --- a/source/table.h +++ b/source/table.h @@ -24,7 +24,8 @@ typedef struct spv_opcode_desc_t { const char* name; const SpvOp opcode; - const libspirv::CapabilitySet capabilities; + const uint32_t numCapabilities; + const SpvCapability* capabilities; // operandTypes[0..numTypes-1] describe logical operands for the instruction. // The operand types include result id and result-type id, followed by // the types of arguments. @@ -37,12 +38,14 @@ typedef struct spv_opcode_desc_t { typedef struct spv_operand_desc_t { const char* name; const uint32_t value; - const libspirv::CapabilitySet capabilities; + const uint32_t numCapabilities; + const SpvCapability* capabilities; // A set of extensions that enable this feature. If empty then this operand // value is always enabled, i.e. it's in core. The assembler, binary parser, // and disassembler ignore this rule, so you can freely process invalid // modules. - const libspirv::ExtensionSet extensions; + const uint32_t numExtensions; + const libspirv::Extension* extensions; const spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger? } spv_operand_desc_t; @@ -55,7 +58,8 @@ typedef struct spv_operand_desc_group_t { typedef struct spv_ext_inst_desc_t { const char* name; const uint32_t ext_inst; - const libspirv::CapabilitySet capabilities; + const uint32_t numCapabilities; + const SpvCapability* capabilities; const spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger? } spv_ext_inst_desc_t; diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 63748409..e5a5f84c 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -215,15 +215,13 @@ bool ValidationState_t::IsDefinedId(uint32_t id) const { const Instruction* ValidationState_t::FindDef(uint32_t id) const { auto it = all_definitions_.find(id); - if (it == all_definitions_.end()) - return nullptr; + if (it == all_definitions_.end()) return nullptr; return it->second; } Instruction* ValidationState_t::FindDef(uint32_t id) { auto it = all_definitions_.find(id); - if (it == all_definitions_.end()) - return nullptr; + if (it == all_definitions_.end()) return nullptr; return it->second; } @@ -283,8 +281,8 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) { spv_operand_desc desc; if (SPV_SUCCESS == grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, cap, &desc)) { - desc->capabilities.ForEach( - [this](SpvCapability c) { RegisterCapability(c); }); + CapabilitySet(desc->numCapabilities, desc->capabilities) + .ForEach([this](SpvCapability c) { RegisterCapability(c); }); } switch (cap) { @@ -464,8 +462,7 @@ uint32_t ValidationState_t::GetComponentType(uint32_t id) const { break; } - if (inst->type_id()) - return GetComponentType(inst->type_id()); + if (inst->type_id()) return GetComponentType(inst->type_id()); assert(0); return 0; @@ -489,8 +486,7 @@ uint32_t ValidationState_t::GetDimension(uint32_t id) const { break; } - if (inst->type_id()) - return GetDimension(inst->type_id()); + if (inst->type_id()) return GetDimension(inst->type_id()); assert(0); return 0; @@ -504,8 +500,7 @@ uint32_t ValidationState_t::GetBitWidth(uint32_t id) const { if (inst->opcode() == SpvOpTypeFloat || inst->opcode() == SpvOpTypeInt) return inst->word(2); - if (inst->opcode() == SpvOpTypeBool) - return 1; + if (inst->opcode() == SpvOpTypeBool) return 1; assert(0); return 0; @@ -607,16 +602,15 @@ bool ValidationState_t::IsFloatMatrixType(uint32_t id) const { return false; } -bool ValidationState_t::GetMatrixTypeInfo( - uint32_t id, uint32_t* num_rows, uint32_t* num_cols, - uint32_t* column_type, uint32_t* component_type) const { - if (!id) - return false; +bool ValidationState_t::GetMatrixTypeInfo(uint32_t id, uint32_t* num_rows, + uint32_t* num_cols, + uint32_t* column_type, + uint32_t* component_type) const { + if (!id) return false; const Instruction* mat_inst = FindDef(id); assert(mat_inst); - if (mat_inst->opcode() != SpvOpTypeMatrix) - return false; + if (mat_inst->opcode() != SpvOpTypeMatrix) return false; const uint32_t vec_type = mat_inst->word(2); const Instruction* vec_inst = FindDef(vec_type); @@ -638,19 +632,16 @@ bool ValidationState_t::GetMatrixTypeInfo( bool ValidationState_t::GetStructMemberTypes( uint32_t struct_type_id, std::vector* member_types) const { member_types->clear(); - if (!struct_type_id) - return false; + if (!struct_type_id) return false; const Instruction* inst = FindDef(struct_type_id); assert(inst); - if (inst->opcode() != SpvOpTypeStruct) - return false; + if (inst->opcode() != SpvOpTypeStruct) return false; - *member_types = std::vector(inst->words().cbegin() + 2, - inst->words().cend()); + *member_types = + std::vector(inst->words().cbegin() + 2, inst->words().cend()); - if (member_types->empty()) - return false; + if (member_types->empty()) return false; return true; } @@ -661,15 +652,13 @@ bool ValidationState_t::IsPointerType(uint32_t id) const { return inst->opcode() == SpvOpTypePointer; } -bool ValidationState_t::GetPointerTypeInfo( - uint32_t id, uint32_t* data_type, uint32_t* storage_class) const { - if (!id) - return false; +bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type, + uint32_t* storage_class) const { + if (!id) return false; const Instruction* inst = FindDef(id); assert(inst); - if (inst->opcode() != SpvOpTypePointer) - return false; + if (inst->opcode() != SpvOpTypePointer) return false; *storage_class = inst->word(2); *data_type = inst->word(3); @@ -677,12 +666,11 @@ bool ValidationState_t::GetPointerTypeInfo( } uint32_t ValidationState_t::GetOperandTypeId( - const spv_parsed_instruction_t* inst, - size_t operand_index) const { + const spv_parsed_instruction_t* inst, size_t operand_index) const { assert(operand_index < inst->num_operands); const spv_parsed_operand_t& operand = inst->operands[operand_index]; assert(operand.num_words == 1); return GetTypeId(inst->words[operand.offset]); } -} /// namespace libspirv +} // namespace libspirv diff --git a/source/validate_capability.cpp b/source/validate_capability.cpp index cd685528..a3341ab3 100644 --- a/source/validate_capability.cpp +++ b/source/validate_capability.cpp @@ -80,17 +80,18 @@ bool IsSupportOptionalVulkan_1_0(uint32_t capability) { // Checks if |capability| was enabled by extension. bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) { spv_operand_desc operand_desc = nullptr; - _.grammar().lookupOperand( - SPV_OPERAND_TYPE_CAPABILITY, capability, &operand_desc); + _.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability, + &operand_desc); // operand_desc is expected to be not null, otherwise validator would have // failed at an earlier stage. This 'assert' is 'just in case'. assert(operand_desc); - if (operand_desc->extensions.IsEmpty()) - return false; + ExtensionSet operand_exts(operand_desc->numExtensions, + operand_desc->extensions); + if (operand_exts.IsEmpty()) return false; - return _.HasAnyOfExtensions(operand_desc->extensions); + return _.HasAnyOfExtensions(operand_exts); } } // namespace @@ -100,8 +101,7 @@ bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) { spv_result_t CapabilityPass(ValidationState_t& _, const spv_parsed_instruction_t* inst) { const SpvOp opcode = static_cast(inst->opcode); - if (opcode != SpvOpCapability) - return SPV_SUCCESS; + if (opcode != SpvOpCapability) return SPV_SUCCESS; assert(inst->num_operands == 1); @@ -118,9 +118,9 @@ spv_result_t CapabilityPass(ValidationState_t& _, !IsSupportOptionalVulkan_1_0(capability) && !IsEnabledByExtension(_, capability)) { return _.diag(SPV_ERROR_INVALID_CAPABILITY) - << "Capability value " << capability - << " is not allowed by Vulkan 1.0 specification" - << " (or requires extension)"; + << "Capability value " << capability + << " is not allowed by Vulkan 1.0 specification" + << " (or requires extension)"; } } diff --git a/source/validate_instruction.cpp b/source/validate_instruction.cpp index aafb7b04..01815078 100644 --- a/source/validate_instruction.cpp +++ b/source/validate_instruction.cpp @@ -94,7 +94,8 @@ CapabilitySet RequiredCapabilities(const ValidationState_t& state, spv_operand_desc operand_desc; const auto ret = state.grammar().lookupOperand(type, operand, &operand_desc); if (ret == SPV_SUCCESS) { - CapabilitySet result = operand_desc->capabilities; + CapabilitySet result(operand_desc->numCapabilities, + operand_desc->capabilities); // Allow FPRoundingMode decoration if requested if (state.features().free_fp_rounding_mode && @@ -115,7 +116,7 @@ ExtensionSet RequiredExtensions(const ValidationState_t& state, if (state.grammar().lookupOperand(type, operand, &operand_desc) == SPV_SUCCESS) { assert(operand_desc); - return operand_desc->extensions; + return {operand_desc->numExtensions, operand_desc->extensions}; } return ExtensionSet(); @@ -127,14 +128,17 @@ namespace libspirv { spv_result_t CapabilityCheck(ValidationState_t& _, const spv_parsed_instruction_t* inst) { - spv_opcode_desc opcode_desc; + spv_opcode_desc opcode_desc = {}; const SpvOp opcode = static_cast(inst->opcode); - if (SPV_SUCCESS == _.grammar().lookupOpcode(opcode, &opcode_desc) && - !_.HasAnyOfCapabilities(opcode_desc->capabilities)) - return _.diag(SPV_ERROR_INVALID_CAPABILITY) - << "Opcode " << spvOpcodeString(opcode) - << " requires one of these capabilities: " - << ToString(opcode_desc->capabilities, _.grammar()); + if (SPV_SUCCESS == _.grammar().lookupOpcode(opcode, &opcode_desc)) { + CapabilitySet opcode_caps(opcode_desc->numCapabilities, + opcode_desc->capabilities); + if (!_.HasAnyOfCapabilities(opcode_caps)) + return _.diag(SPV_ERROR_INVALID_CAPABILITY) + << "Opcode " << spvOpcodeString(opcode) + << " requires one of these capabilities: " + << ToString(opcode_caps, _.grammar()); + } for (int i = 0; i < inst->num_operands; ++i) { const auto& operand = inst->operands[i]; const auto word = inst->words[operand.offset]; @@ -176,10 +180,10 @@ spv_result_t ExtensionCheck(ValidationState_t& _, RequiredExtensions(_, operand.type, word); if (!_.HasAnyOfExtensions(required_extensions)) { return _.diag(SPV_ERROR_MISSING_EXTENSION) - << spvutils::CardinalToOrdinal(operand_index + 1) << " operand of " - << spvOpcodeString(opcode) << ": operand " << word - << " requires one of these extensions: " - << ExtensionSetToString(required_extensions); + << spvutils::CardinalToOrdinal(operand_index + 1) << " operand of " + << spvOpcodeString(opcode) << ": operand " << word + << " requires one of these extensions: " + << ExtensionSetToString(required_extensions); } } return SPV_SUCCESS; @@ -194,8 +198,8 @@ spv_result_t ReservedCheck(ValidationState_t& _, case SpvOpImageSparseSampleProjExplicitLod: case SpvOpImageSparseSampleProjDrefImplicitLod: case SpvOpImageSparseSampleProjDrefExplicitLod: - return _.diag(SPV_ERROR_INVALID_VALUE) << spvOpcodeString(opcode) - << " is reserved for future use."; + return _.diag(SPV_ERROR_INVALID_VALUE) + << spvOpcodeString(opcode) << " is reserved for future use."; default: return SPV_SUCCESS; } @@ -393,8 +397,7 @@ void CheckIfKnownExtension(ValidationState_t& _, spv_result_t InstructionPass(ValidationState_t& _, const spv_parsed_instruction_t* inst) { const SpvOp opcode = static_cast(inst->opcode); - if (opcode == SpvOpExtension) - CheckIfKnownExtension(_, inst); + if (opcode == SpvOpExtension) CheckIfKnownExtension(_, inst); if (opcode == SpvOpCapability) { _.RegisterCapability( static_cast(inst->words[inst->operands[0].offset])); diff --git a/test/opcode_require_capabilities_test.cpp b/test/opcode_require_capabilities_test.cpp index 31a17366..14b56bd2 100644 --- a/test/opcode_require_capabilities_test.cpp +++ b/test/opcode_require_capabilities_test.cpp @@ -37,8 +37,9 @@ TEST_P(OpcodeTableCapabilitiesTest, TableEntryMatchesExpectedCapabilities) { spv_opcode_desc entry; ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableValueLookup(opcodeTable, GetParam().opcode, &entry)); - EXPECT_EQ(ElementsIn(GetParam().capabilities), - ElementsIn(entry->capabilities)); + EXPECT_EQ( + ElementsIn(GetParam().capabilities), + ElementsIn(CapabilitySet(entry->numCapabilities, entry->capabilities))); } INSTANTIATE_TEST_CASE_P( diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp index 39e5022a..850a5eff 100644 --- a/test/operand_capabilities_test.cpp +++ b/test/operand_capabilities_test.cpp @@ -51,8 +51,9 @@ TEST_P(EnumCapabilityTest, Sample) { ASSERT_EQ(SPV_SUCCESS, spvOperandTableValueLookup(operandTable, get<1>(GetParam()).type, get<1>(GetParam()).value, &entry)); - EXPECT_THAT(ElementsIn(entry->capabilities), - Eq(ElementsIn(get<1>(GetParam()).expected_capabilities))) + EXPECT_THAT( + ElementsIn(CapabilitySet(entry->numCapabilities, entry->capabilities)), + Eq(ElementsIn(get<1>(GetParam()).expected_capabilities))) << " capability value " << get<1>(GetParam()).value; } @@ -279,30 +280,30 @@ INSTANTIATE_TEST_CASE_P( // See SPIR-V Section 3.12 Image Channel Order INSTANTIATE_TEST_CASE_P( ImageChannelOrder, EnumCapabilityTest, - Combine( - Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), - ValuesIn(std::vector{ - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderR, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderA, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRG, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRA, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGB, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGBA, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderBGRA, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderARGB, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderIntensity, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderLuminance, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRx, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGx, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGBx, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepth, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepthStencil, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGB, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGBx, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGBA, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersBGRA, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderABGR, Kernel), - })), ); + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderR, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderA, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRG, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRA, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGB, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGBA, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderBGRA, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderARGB, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderIntensity, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderLuminance, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRx, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGx, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGBx, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepth, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepthStencil, + Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGB, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGBx, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGBA, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersBGRA, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderABGR, Kernel), + })), ); // See SPIR-V Section 3.13 Image Channel Data Type INSTANTIATE_TEST_CASE_P( diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py index e58d57b2..0ec1dc4e 100755 --- a/utils/generate_grammar_tables.py +++ b/utils/generate_grammar_tables.py @@ -24,7 +24,7 @@ import re # Prefix for all C variables generated by this script. PYGEN_VARIABLE_PREFIX = 'pygen_variable' -# Extensions to recognize, but which don't necessarily come from the SPIRV-V +# Extensions to recognize, but which don't necessarily come from the SPIR-V # core grammar. Get this list from the SPIR-V registery web page. EXTENSIONS_FROM_SPIRV_REGISTRY = """ SPV_AMD_shader_explicit_vertex_parameter @@ -56,7 +56,6 @@ SPV_AMD_shader_fragment_mask """ - def make_path_to_file(f): """Makes all ancestor directories to the given file, if they don't yet exist. @@ -86,6 +85,33 @@ def compose_capability_list(caps): return "{" + ", ".join(['SpvCapability{}'.format(c) for c in caps]) + "}" +def get_capability_array_name(caps, version): + """Returns the name of the array containing all the given capabilities. + + Args: + - caps: a sequence of capability names + """ + if not caps: + return 'nullptr' + else: + return '{}_caps_{}_{}'.format( + PYGEN_VARIABLE_PREFIX, ''.join(caps), version) + + +def generate_capability_arrays(caps, version): + """Returns the arrays of capabilities. + + Arguments: + - caps: a sequence of sequence of capability names + """ + caps = sorted(set([tuple(c) for c in caps if c])) + arrays = [ + 'static const SpvCapability {}[] = {};'.format( + get_capability_array_name(c, version), compose_capability_list(c)) + for c in caps] + return '\n'.join(arrays) + + def compose_extension_list(exts): """Returns a string containing a braced list of extensions as enums. @@ -99,6 +125,33 @@ def compose_extension_list(exts): ['libspirv::Extension::k{}'.format(e) for e in exts]) + "}" +def get_extension_array_name(extensions, version): + """Returns the name of the array containing all the given extensions. + + Args: + - extensions: a sequence of extension names + """ + if not extensions: + return 'nullptr' + else: + return '{}_exts_{}_{}'.format( + PYGEN_VARIABLE_PREFIX, ''.join(extensions), version) + + +def generate_extension_arrays(extensions, version): + """Returns the arrays of extensions. + + Arguments: + - caps: a sequence of sequence of extension names + """ + extensions = sorted(set([tuple(e) for e in extensions if e])) + arrays = [ + 'static const libspirv::Extension {}[] = {};'.format( + get_extension_array_name(e, version), compose_extension_list(e)) + for e in extensions] + return '\n'.join(arrays) + + def convert_operand_kind(operand_tuple): """Returns the corresponding operand type used in spirv-tools for the given operand kind and quantifier used in the JSON grammar. @@ -166,17 +219,19 @@ class InstInitializer(object): """Instances holds a SPIR-V instruction suitable for printing as the initializer for spv_opcode_desc_t.""" - def __init__(self, opname, caps, operands): + def __init__(self, opname, caps, operands, version): """Initialization. Arguments: - opname: opcode name (with the 'Op' prefix) - caps: a sequence of capability names required by this opcode - operands: a sequence of (operand-kind, operand-quantifier) tuples + - version: version of the instruction set """ assert opname.startswith('Op') self.opname = opname[2:] # Remove the "Op" prefix. - self.caps_mask = compose_capability_list(caps) + self.num_caps = len(caps) + self.caps_mask = get_capability_array_name(caps, version) self.operands = [convert_operand_kind(o) for o in operands] self.fix_syntax() @@ -198,11 +253,13 @@ class InstInitializer(object): self.operands.pop() def __str__(self): - template = ['{{"{opname}"', 'SpvOp{opname}', '{caps_mask}', + template = ['{{"{opname}"', 'SpvOp{opname}', + '{num_caps}', '{caps_mask}', '{num_operands}', '{{{operands}}}', '{def_result_id}', '{ref_type_id}}}'] return ', '.join(template).format( opname=self.opname, + num_caps=self.num_caps, caps_mask=self.caps_mask, num_operands=len(self.operands), operands=', '.join(self.operands), @@ -214,7 +271,7 @@ class ExtInstInitializer(object): """Instances holds a SPIR-V extended instruction suitable for printing as the initializer for spv_ext_inst_desc_t.""" - def __init__(self, opname, opcode, caps, operands): + def __init__(self, opname, opcode, caps, operands, version): """Initialization. Arguments: @@ -222,24 +279,27 @@ class ExtInstInitializer(object): - opcode: enumerant value for this opcode - caps: a sequence of capability names required by this opcode - operands: a sequence of (operand-kind, operand-quantifier) tuples + - version: version of the extended instruction set """ self.opname = opname self.opcode = opcode - self.caps_mask = compose_capability_list(caps) + self.num_caps = len(caps) + self.caps_mask = get_capability_array_name(caps, version) self.operands = [convert_operand_kind(o) for o in operands] self.operands.append('SPV_OPERAND_TYPE_NONE') def __str__(self): - template = ['{{"{opname}"', '{opcode}', '{caps_mask}', + template = ['{{"{opname}"', '{opcode}', '{num_caps}', '{caps_mask}', '{{{operands}}}}}'] return ', '.join(template).format( opname=self.opname, opcode=self.opcode, + num_caps=self.num_caps, caps_mask=self.caps_mask, operands=', '.join(self.operands)) -def generate_instruction(inst, is_ext_inst): +def generate_instruction(inst, version, is_ext_inst): """Returns the C initializer for the given SPIR-V instruction. Arguments: @@ -260,27 +320,49 @@ def generate_instruction(inst, is_ext_inst): assert opname is not None if is_ext_inst: - return str(ExtInstInitializer(opname, opcode, caps, operands)) + return str(ExtInstInitializer(opname, opcode, caps, operands, version)) else: - return str(InstInitializer(opname, caps, operands)) + return str(InstInitializer(opname, caps, operands, version)) -def generate_instruction_table(inst_table, is_ext_inst): - """Returns the info table containing all SPIR-V instructions. +def generate_instruction_table(inst_table, version): + """Returns the info table containing all SPIR-V instructions, + prefixed by capability arrays. Arguments: - inst_table: a dict containing all SPIR-V instructions. - - is_ext_inst: a bool indicating whether |inst_table| is for - an extended instruction set. + - vesion: SPIR-V version. """ - return ',\n'.join([generate_instruction(inst, is_ext_inst) - for inst in inst_table]) + caps_arrays = generate_capability_arrays( + [inst.get('capabilities', []) for inst in inst_table], version) + insts = [generate_instruction(inst, version, False) for inst in inst_table] + insts = ['static const spv_opcode_desc_t kOpcodeTableEntries_{}[] = {{\n' + ' {}\n}};'.format(version, ',\n '.join(insts))] + + return '{}\n\n{}'.format(caps_arrays, '\n'.join(insts)) + + +def generate_extended_instruction_table(inst_table, set_name, version): + """Returns the info table containing all SPIR-V extended instructions, + prefixed by capability arrays. + + Arguments: + - inst_table: a dict containing all SPIR-V instructions. + - set_name: the name of the extended instruction set. + """ + caps = [inst.get('capabilities', []) for inst in inst_table] + caps_arrays = generate_capability_arrays(caps, version) + insts = [generate_instruction(inst, version, True) for inst in inst_table] + insts = ['static const spv_ext_inst_desc_t {}_entries[] = {{\n' + ' {}\n}};'.format(set_name, ',\n '.join(insts))] + + return '{}\n\n{}'.format(caps_arrays, '\n'.join(insts)) class EnumerantInitializer(object): """Prints an enumerant as the initializer for spv_operand_desc_t.""" - def __init__(self, enumerant, value, caps, exts, parameters): + def __init__(self, enumerant, value, caps, exts, parameters, version): """Initialization. Arguments: @@ -292,22 +374,26 @@ class EnumerantInitializer(object): """ self.enumerant = enumerant self.value = value - self.caps = compose_capability_list(caps) - self.exts = compose_extension_list(exts) + self.num_caps = len(caps) + self.caps = get_capability_array_name(caps, version) + self.num_exts = len(exts) + self.exts = get_extension_array_name(exts, version) self.parameters = [convert_operand_kind(p) for p in parameters] def __str__(self): - template = ['{{"{enumerant}"', '{value}', - '{caps}', '{exts}', '{{{parameters}}}}}'] + template = ['{{"{enumerant}"', '{value}', '{num_caps}', + '{caps}', '{num_exts}', '{exts}', '{{{parameters}}}}}'] return ', '.join(template).format( enumerant=self.enumerant, value=self.value, + num_caps=self.num_caps, caps=self.caps, + num_exts=self.num_exts, exts=self.exts, parameters=', '.join(self.parameters)) -def generate_enum_operand_kind_entry(entry): +def generate_enum_operand_kind_entry(entry, version): """Returns the C initializer for the given operand enum entry. Arguments: @@ -327,7 +413,8 @@ def generate_enum_operand_kind_entry(entry): assert enumerant is not None assert value is not None - return str(EnumerantInitializer(enumerant, value, caps, exts, params)) + return str(EnumerantInitializer( + enumerant, value, caps, exts, params, version)) def generate_enum_operand_kind(enum, version): @@ -336,7 +423,7 @@ def generate_enum_operand_kind(enum, version): assert kind is not None name = '{}_{}Entries_{}'.format(PYGEN_VARIABLE_PREFIX, kind, version) - entries = [' {}'.format(generate_enum_operand_kind_entry(e)) + entries = [' {}'.format(generate_enum_operand_kind_entry(e, version)) for e in enum.get('enumerants', [])] template = ['static const spv_operand_desc_t {name}[] = {{', @@ -351,9 +438,19 @@ def generate_enum_operand_kind(enum, version): def generate_operand_kind_table(enums, version): """Returns the info table containing all SPIR-V operand kinds.""" # We only need to output info tables for those operand kinds that are enums. - enums = [generate_enum_operand_kind(e, version) - for e in enums - if e.get('category') in ['ValueEnum', 'BitEnum']] + enums = [e for e in enums if e.get('category') in ['ValueEnum', 'BitEnum']] + + caps = [entry.get('capabilities', []) + for enum in enums + for entry in enum.get('enumerants', [])] + caps_arrays = generate_capability_arrays(caps, version) + + exts = [entry.get('extensions', []) + for enum in enums + for entry in enum.get('enumerants', [])] + exts_arrays = generate_extension_arrays(exts, version) + + enums = [generate_enum_operand_kind(e, version) for e in enums] # We have three operand kinds that requires their optional counterpart to # exist in the operand info table. three_optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess'] @@ -377,7 +474,7 @@ def generate_operand_kind_table(enums, version): table = '\n'.join(template).format( p=PYGEN_VARIABLE_PREFIX, v=version, enums=',\n'.join(table_entries)) - return '\n\n'.join(enum_entries + (table,)) + return '\n\n'.join((caps_arrays,) + (exts_arrays,) + enum_entries + (table,)) def get_extension_list(operands): @@ -580,11 +677,11 @@ def main(): if args.core_insts_output is not None: make_path_to_file(args.core_insts_output) make_path_to_file(args.operand_kinds_output) - print(generate_instruction_table( - grammar['instructions'], False), - file=open(args.core_insts_output, 'w')) version = '{}_{}'.format(grammar['major_version'], grammar['minor_version']) + print(generate_instruction_table( + grammar['instructions'], version), + file=open(args.core_insts_output, 'w')) print(generate_operand_kind_table( grammar['operand_kinds'], version), file=open(args.operand_kinds_output, 'w')) @@ -602,21 +699,27 @@ def main(): with open(args.extinst_glsl_grammar) as json_file: grammar = json.loads(json_file.read()) make_path_to_file(args.glsl_insts_output) - print(generate_instruction_table(grammar['instructions'], True), + print(generate_extended_instruction_table( + grammar['instructions'], "glsl", "1_0"), file=open(args.glsl_insts_output, 'w')) if args.extinst_opencl_grammar is not None: with open(args.extinst_opencl_grammar) as json_file: grammar = json.loads(json_file.read()) make_path_to_file(args.opencl_insts_output) - print(generate_instruction_table(grammar['instructions'], True), + print(generate_extended_instruction_table( + grammar['instructions'], "opencl", "1_0"), file=open(args.opencl_insts_output, 'w')) if args.extinst_vendor_grammar is not None: with open(args.extinst_vendor_grammar) as json_file: grammar = json.loads(json_file.read()) make_path_to_file(args.vendor_insts_output) - print(generate_instruction_table(grammar['instructions'], True), + name = args.extinst_vendor_grammar + start = name.find("extinst.") + len("extinst.") + name = name[start:-len(".grammar.json")].replace("-", "_") + print(generate_extended_instruction_table( + grammar['instructions'], name, "1_0"), file=open(args.vendor_insts_output, 'w'))