From 28a883ba4c67f58a9540fb0651c647bb02883622 Mon Sep 17 00:00:00 2001 From: David Neto Date: Wed, 25 Jun 2025 11:27:23 -0400 Subject: [PATCH] SPV_INTEL_function_variants: basic asm, dis support (#6195) The challenging part is that there are instructions that take zero or more Capability operands. So we have to introduce SPV_TYPE_OPERAND_VARIABLE_CAPABILITY and SPV_TYPE_OPERAND_OPTIONAL_CAPABILITY. Remove deprecated enums for the first and last variable or optional enums. --- DEPS | 2 +- include/spirv-tools/libspirv.h | 48 +++++++---------- source/binary.cpp | 3 ++ source/disassemble.cpp | 1 + source/operand.cpp | 7 +++ test/text_to_binary.extension_test.cpp | 73 ++++++++++++++++++++++++++ utils/ggt.py | 3 +- 7 files changed, 107 insertions(+), 30 deletions(-) diff --git a/DEPS b/DEPS index e25ca51360..511fd71887 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ vars = { 're2_revision': 'c84a140c93352cdabbfb547c531be34515b12228', - 'spirv_headers_revision': '2a611a970fdbc41ac2e3e328802aed9985352dca', + 'spirv_headers_revision': '9e3836d7d6023843a72ecd3fbf3f09b1b6747a9e', 'mimalloc_revision': '09a27098aa6e9286518bd9c74e6ffa7199c3f04e', } diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index a2a032a07e..2a604e94d9 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -189,36 +189,24 @@ typedef enum spv_operand_type_t { SPV_OPERAND_TYPE_MEMORY_ACCESS, // SPIR-V Sec 3.26 SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE, // SPIR-V Sec 3.FSR -// NOTE: New concrete enum values should be added at the end. - -// The "optional" and "variable" operand types are only used internally by -// the assembler and the binary parser. -// There are two categories: -// Optional : expands to 0 or 1 operand, like ? in regular expressions. -// Variable : expands to 0, 1 or many operands or pairs of operands. -// This is similar to * in regular expressions. - -// NOTE: These FIRST_* and LAST_* enum values are DEPRECATED. -// The concept of "optional" and "variable" operand types are only intended -// for use as an implementation detail of parsing SPIR-V, either in text or -// binary form. Instead of using enum ranges, use characteristic function -// spvOperandIsConcrete. -// The use of enum value ranges in a public API makes it difficult to insert -// new values into a range without also breaking binary compatibility. -// -// Macros for defining bounds on optional and variable operand types. -// Any variable operand type is also optional. -// TODO(dneto): Remove SPV_OPERAND_TYPE_FIRST_* and SPV_OPERAND_TYPE_LAST_* -#define FIRST_OPTIONAL(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_OPTIONAL_TYPE = ENUM -#define FIRST_VARIABLE(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE = ENUM -#define LAST_VARIABLE(ENUM) \ - ENUM, SPV_OPERAND_TYPE_LAST_VARIABLE_TYPE = ENUM, \ - SPV_OPERAND_TYPE_LAST_OPTIONAL_TYPE = ENUM + // NOTE: New concrete enum values should be added at the end. + + // The "optional" and "variable" operand types are only used internally by + // the assembler and the binary parser. + // There are two categories: + // Optional : expands to 0 or 1 operand, like ? in regular expressions. + // Variable : expands to 0, 1 or many operands or pairs of operands. + // This is similar to * in regular expressions. + + // Use characteristic function spvOperandIsConcrete to classify the + // operand types; when it returns false, the operand is optional or variable. + // + // Any variable operand type is also optional. // An optional operand represents zero or one logical operands. // In an instruction definition, this may only appear at the end of the // operand types. - FIRST_OPTIONAL(SPV_OPERAND_TYPE_OPTIONAL_ID), + SPV_OPERAND_TYPE_OPTIONAL_ID, // An optional image operand type. SPV_OPERAND_TYPE_OPTIONAL_IMAGE, // An optional memory access type. @@ -243,7 +231,7 @@ typedef enum spv_operand_type_t { // A variable operand represents zero or more logical operands. // In an instruction definition, this may only appear at the end of the // operand types. - FIRST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID), + SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER, // A sequence of zero or more pairs of (typed literal integer, Id). // Expands to zero or more: @@ -251,7 +239,7 @@ typedef enum spv_operand_type_t { // where the literal number must always be an integer of some sort. SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID, // A sequence of zero or more pairs of (Id, Literal integer) - LAST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER), + SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER, // The following are concrete enum types from the DebugInfo extended // instruction set. @@ -343,6 +331,10 @@ typedef enum spv_operand_type_t { SPV_OPERAND_TYPE_TENSOR_OPERANDS, SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS, + // SPV_INTEL_function_variants + SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY, + SPV_OPERAND_TYPE_VARIABLE_CAPABILITY, + // This is a sentinel value, and does not represent an operand type. // It should come last. SPV_OPERAND_TYPE_NUM_OPERAND_TYPES, diff --git a/source/binary.cpp b/source/binary.cpp index 180d0a9996..8e4d899f7d 100644 --- a/source/binary.cpp +++ b/source/binary.cpp @@ -636,6 +636,7 @@ spv_result_t Parser::parseOperand(size_t inst_offset, } break; case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY: case SPV_OPERAND_TYPE_EXECUTION_MODEL: case SPV_OPERAND_TYPE_ADDRESSING_MODEL: case SPV_OPERAND_TYPE_MEMORY_MODEL: @@ -689,6 +690,8 @@ spv_result_t Parser::parseOperand(size_t inst_offset, parsed_operand.type = SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT; if (type == SPV_OPERAND_TYPE_OPTIONAL_FPENCODING) parsed_operand.type = SPV_OPERAND_TYPE_FPENCODING; + if (type == SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY) + parsed_operand.type = SPV_OPERAND_TYPE_CAPABILITY; const spvtools::OperandDesc* entry = nullptr; if (spvtools::LookupOperand(type, word, &entry)) { diff --git a/source/disassemble.cpp b/source/disassemble.cpp index 2d9bb0ff02..4267333a00 100644 --- a/source/disassemble.cpp +++ b/source/disassemble.cpp @@ -907,6 +907,7 @@ void InstructionDisassembler::EmitOperand(std::ostream& stream, stream << '"'; } break; case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY: case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: case SPV_OPERAND_TYPE_EXECUTION_MODEL: case SPV_OPERAND_TYPE_ADDRESSING_MODEL: diff --git a/source/operand.cpp b/source/operand.cpp index c635c72d61..d7fc535ce2 100644 --- a/source/operand.cpp +++ b/source/operand.cpp @@ -111,6 +111,7 @@ const char* spvOperandTypeStr(spv_operand_type_t type) { case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: return "kernel profiling info"; case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY: return "capability"; case SPV_OPERAND_TYPE_RAY_FLAGS: return "ray flags"; @@ -394,6 +395,7 @@ bool spvOperandIsOptional(spv_operand_type_t type) { case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: case SPV_OPERAND_TYPE_OPTIONAL_FPENCODING: case SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY: return true; default: break; @@ -408,6 +410,7 @@ bool spvOperandIsVariable(spv_operand_type_t type) { case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER: case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID: case SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_VARIABLE_CAPABILITY: return true; default: break; @@ -439,6 +442,10 @@ bool spvExpandOperandSequenceOnce(spv_operand_type_t type, pattern->push_back(SPV_OPERAND_TYPE_LITERAL_INTEGER); pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_ID); return true; + case SPV_OPERAND_TYPE_VARIABLE_CAPABILITY: + pattern->push_back(type); + pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY); + return true; default: break; } diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp index 65079d1bf8..39accfc10f 100644 --- a/test/text_to_binary.extension_test.cpp +++ b/test/text_to_binary.extension_test.cpp @@ -1495,5 +1495,78 @@ INSTANTIATE_TEST_SUITE_P( SaturatedToLargestFloat8NormalConversionEXT)})}, }))); +// SPV_INTEL_function_variants +// https://github.com/intel/llvm/blob/sycl/sycl/doc/design/spirv-extensions/SPV_INTEL_function_variants.asciidoc +INSTANTIATE_TEST_SUITE_P( + SPV_INTEL_function_variants, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6), + ValuesIn(std::vector{ + {"OpExtension \"SPV_INTEL_function_variants\"\n", + MakeInstruction(spv::Op::OpExtension, + MakeVector("SPV_INTEL_function_variants"))}, + {"OpCapability SpecConditionalINTEL\n", + MakeInstruction( + spv::Op::OpCapability, + {(uint32_t)spv::Capability::SpecConditionalINTEL})}, + {"OpCapability FunctionVariantsINTEL\n", + MakeInstruction( + spv::Op::OpCapability, + {(uint32_t)spv::Capability::FunctionVariantsINTEL})}, + {"OpDecorate %1 ConditionalINTEL %2\n", + MakeInstruction(spv::Op::OpDecorate, + {1, (uint32_t)spv::Decoration::ConditionalINTEL, + 2})}, + + {"OpConditionalExtensionINTEL %1 \"foo\"\n", + MakeInstruction(spv::Op::OpConditionalExtensionINTEL, {1}, + MakeVector("foo"))}, + + {"OpConditionalEntryPointINTEL %1 Kernel %2 \"foo\"\n", + MakeInstruction(spv::Op::OpConditionalEntryPointINTEL, + {1, (uint32_t)spv::ExecutionModel::Kernel, 2}, + MakeVector("foo"))}, + + {"OpConditionalCapabilityINTEL %1 Kernel\n", + MakeInstruction(spv::Op::OpConditionalCapabilityINTEL, + {1, (uint32_t)spv::ExecutionModel::Kernel})}, + + {"%2 = OpSpecConstantTargetINTEL %1 42\n", + MakeInstruction(spv::Op::OpSpecConstantTargetINTEL, {1, 2, 42})}, + + {"%2 = OpSpecConstantTargetINTEL %1 42 99\n", + MakeInstruction(spv::Op::OpSpecConstantTargetINTEL, + {1, 2, 42, 99})}, + + {"%2 = OpSpecConstantTargetINTEL %1 42 99 108\n", + MakeInstruction(spv::Op::OpSpecConstantTargetINTEL, + {1, 2, 42, 99, 108})}, + + {"%2 = OpSpecConstantArchitectureINTEL %1 42 99 108 72\n", + MakeInstruction(spv::Op::OpSpecConstantArchitectureINTEL, + {1, 2, 42, 99, 108, 72})}, + + {"%2 = OpSpecConstantCapabilitiesINTEL %1\n", + MakeInstruction(spv::Op::OpSpecConstantCapabilitiesINTEL, {1, 2})}, + + {"%2 = OpSpecConstantCapabilitiesINTEL %1 Kernel\n", + MakeInstruction(spv::Op::OpSpecConstantCapabilitiesINTEL, + {1, 2, (uint32_t)spv::Capability::Kernel})}, + + {"%2 = OpSpecConstantCapabilitiesINTEL %1 Kernel Shader\n", + MakeInstruction(spv::Op::OpSpecConstantCapabilitiesINTEL, + {1, 2, (uint32_t)spv::Capability::Kernel, + (uint32_t)spv::Capability::Shader})}, + + {"%2 = OpConditionalCopyObjectINTEL %1 %3 %4\n", + MakeInstruction(spv::Op::OpConditionalCopyObjectINTEL, + {1, 2, 3, 4})}, + + {"%2 = OpConditionalCopyObjectINTEL %1 %3 %4 %5 %6\n", + MakeInstruction(spv::Op::OpConditionalCopyObjectINTEL, + {1, 2, 3, 4, 5, 6})}, + + }))); + } // namespace } // namespace spvtools diff --git a/utils/ggt.py b/utils/ggt.py index 258c1b002f..45262ba89c 100755 --- a/utils/ggt.py +++ b/utils/ggt.py @@ -242,7 +242,8 @@ def __init__(self, extensions: List[str], operand_kinds:List[dict], printing_cla 'MatrixMultiplyAccumulateOperands', 'RawAccessChainOperands', 'FPEncoding', - 'TensorOperands'] + 'TensorOperands', + 'Capability'] def dump(self) -> None: self.context.dump()