From 28a883ba4c67f58a9540fb0651c647bb02883622 Mon Sep 17 00:00:00 2001
From: David Neto <dneto@google.com>
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<AssemblyCase>{
+            {"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()
