[llvm] [SPIR-V] Reject builtin calls if mangled argument types do not match the IR (PR #208152)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 20 21:56:44 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/208152

>From 9813254c71167c0670817c1c85d7fe2e12519236 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 8 Jul 2026 08:38:38 +0200
Subject: [PATCH 1/4] [SPIR-V] Fix crash on get_global_id with non-integer
 dimension index

---
 llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp           |  3 +++
 .../SPIRV/get-global-id-non-integer-dim.ll        | 15 +++++++++++++++
 2 files changed, 18 insertions(+)
 create mode 100644 llvm/test/CodeGen/SPIRV/get-global-id-non-integer-dim.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 7f01fc37af545..dc3088a3901e0 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -1685,6 +1685,9 @@ static bool genWorkgroupQuery(const SPIRV::IncomingCall *Call,
                               SPIRV::BuiltIn::BuiltIn BuiltinValue,
                               uint64_t DefaultValue) {
   Register IndexRegister = Call->Arguments[0];
+  SPIRVTypeInst IndexRegisterType = GR->getSPIRVTypeForVReg(IndexRegister);
+  if (!IndexRegisterType || IndexRegisterType->getOpcode() != SPIRV::OpTypeInt)
+    report_fatal_error("Expect an integer <Dimindx> argument");
   const unsigned ResultWidth = Call->ReturnType->getOperand(1).getImm();
   const unsigned PointerSize = GR->getPointerSize();
   const SPIRVTypeInst PointerSizeType =
diff --git a/llvm/test/CodeGen/SPIRV/get-global-id-non-integer-dim.ll b/llvm/test/CodeGen/SPIRV/get-global-id-non-integer-dim.ll
new file mode 100644
index 0000000000000..59e62493fe890
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/get-global-id-non-integer-dim.ll
@@ -0,0 +1,15 @@
+; RUN: not --crash llc -O0 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s
+; RUN: not --crash llc -O2 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s
+
+; get_global_id and its sibling workgroup-query builtins take an integer
+; dimension index.
+
+; CHECK: LLVM ERROR: Expect an integer <Dimindx> argument
+
+declare spir_func i64 @_Z13get_global_idj(bfloat)
+
+define spir_kernel void @fuzz_kernel(ptr addrspace(1) %out, bfloat %dim) {
+  %id = call spir_func i64 @_Z13get_global_idj(bfloat %dim)
+  store i64 %id, ptr addrspace(1) %out
+  ret void
+}

>From 066c211cfa34e1fd8373d4fee874b37c9c535354 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 9 Jul 2026 11:47:55 +0200
Subject: [PATCH 2/4] Address comment

---
 llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp       | 85 ++++++++++++++++++-
 .../tf32-conv.ll                              | 24 +++---
 .../SPIRV/get-global-id-non-integer-dim.ll    |  9 +-
 3 files changed, 100 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index dc3088a3901e0..284763a7ae15a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -1685,9 +1685,6 @@ static bool genWorkgroupQuery(const SPIRV::IncomingCall *Call,
                               SPIRV::BuiltIn::BuiltIn BuiltinValue,
                               uint64_t DefaultValue) {
   Register IndexRegister = Call->Arguments[0];
-  SPIRVTypeInst IndexRegisterType = GR->getSPIRVTypeForVReg(IndexRegister);
-  if (!IndexRegisterType || IndexRegisterType->getOpcode() != SPIRV::OpTypeInt)
-    report_fatal_error("Expect an integer <Dimindx> argument");
   const unsigned ResultWidth = Call->ReturnType->getOperand(1).getImm();
   const unsigned PointerSize = GR->getPointerSize();
   const SPIRVTypeInst PointerSizeType =
@@ -3488,6 +3485,77 @@ mapBuiltinToOpcode(StringRef DemangledCall,
   return std::make_tuple(-1, 0, 0);
 }
 
+/// Checks that scalar/vector numeric arguments of \p Call match the types
+/// implied by their mangling in \p DemangledCall. Pointers and opaque
+/// builtin types (images, samplers, pipes, etc.) are not validated here, as
+/// mangling does not enforce their exact spelling.
+///
+/// \returns false if a numeric argument's SPIR-V type disagrees with the
+/// type implied by the mangled name, true otherwise.
+static bool demangledArgTypesMatchIR(const SPIRV::IncomingCall *Call,
+                                     StringRef DemangledCall,
+                                     SPIRVGlobalRegistry *GR,
+                                     LLVMContext &Ctx) {
+  if (Call->isSpirvOp())
+    return true;
+
+  SmallVector<StringRef, 10> ArgTypeStrs;
+  SPIRV::parseBuiltinTypeStr(ArgTypeStrs, DemangledCall, Ctx);
+
+  for (unsigned ArgIdx = 0; ArgIdx < Call->Arguments.size(); ++ArgIdx) {
+    if (ArgIdx >= ArgTypeStrs.size())
+      continue;
+    StringRef ArgTypeStr = ArgTypeStrs[ArgIdx].trim();
+    // Opaque/builtin OpenCL and SPIR-V types (images, samplers, pipes,
+    // reserve_id, etc.) are not validated here, as mangling does not enforce
+    // their exact spelling, and some builtin type names have no TableGen
+    // record and would otherwise abort compilation when parsed.
+    if (hasBuiltinTypePrefix(ArgTypeStr))
+      continue;
+
+    Type *ExpectedType = SPIRV::parseBuiltinCallArgumentType(ArgTypeStr, Ctx);
+    if (!ExpectedType || ExpectedType->isVoidTy() ||
+        ExpectedType->isPointerTy() || ExpectedType->isTargetExtTy())
+      continue;
+
+    SPIRVTypeInst ArgType = GR->getSPIRVTypeForVReg(Call->Arguments[ArgIdx]);
+    if (!ArgType)
+      continue;
+    unsigned ArgTypeOpcode = ArgType->getOpcode();
+    if (ArgTypeOpcode != SPIRV::OpTypeInt &&
+        ArgTypeOpcode != SPIRV::OpTypeFloat &&
+        ArgTypeOpcode != SPIRV::OpTypeBool &&
+        ArgTypeOpcode != SPIRV::OpTypeVector)
+      continue;
+
+    Type *ExpectedScalarType =
+        ExpectedType->isVectorTy()
+            ? cast<VectorType>(ExpectedType)->getElementType()
+            : ExpectedType;
+    SPIRVTypeInst ArgScalarType = GR->getScalarOrVectorComponentType(ArgType);
+    if (!ArgScalarType)
+      continue;
+
+    bool ExpectedIsInt = ExpectedScalarType->isIntegerTy();
+    bool ExpectedIsFloat = ExpectedScalarType->isFloatingPointTy();
+    unsigned ArgOpcode = ArgScalarType->getOpcode();
+    bool ArgIsInt =
+        ArgOpcode == SPIRV::OpTypeInt || ArgOpcode == SPIRV::OpTypeBool;
+    bool ArgIsFloat = ArgOpcode == SPIRV::OpTypeFloat;
+
+    if ((ExpectedIsInt && !ArgIsInt) || (ExpectedIsFloat && !ArgIsFloat))
+      return false;
+
+    unsigned ExpectedElts =
+        ExpectedType->isVectorTy()
+            ? cast<VectorType>(ExpectedType)->getElementCount().getFixedValue()
+            : 1;
+    if (ExpectedElts != GR->getScalarOrVectorComponentCount(ArgType))
+      return false;
+  }
+  return true;
+}
+
 std::optional<bool> lowerBuiltin(StringRef DemangledCall,
                                  SPIRV::InstructionSet::InstructionSet Set,
                                  MachineIRBuilder &MIRBuilder,
@@ -3524,6 +3592,17 @@ std::optional<bool> lowerBuiltin(StringRef DemangledCall,
     return std::nullopt;
   }
 
+  // Check that argument types match what the mangling implies. If not
+  // (e.g. broken mangling), treat the call as a regular function call
+  // rather than crashing.
+  if (!demangledArgTypesMatchIR(Call.get(), DemangledCall, GR,
+                                MIRBuilder.getContext())) {
+    LLVM_DEBUG(dbgs() << "Argument types do not match mangled types for "
+                      << "builtin " << DemangledCall
+                      << "; treating as a normal function\n");
+    return std::nullopt;
+  }
+
   // Match the builtin with implementation based on the grouping.
   switch (Call->Builtin->Group) {
   case SPIRV::Extended:
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_tensor_float32_conversion/tf32-conv.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_tensor_float32_conversion/tf32-conv.ll
index dcad78d17bff7..62481c1e876dd 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_tensor_float32_conversion/tf32-conv.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_tensor_float32_conversion/tf32-conv.ll
@@ -45,18 +45,18 @@ declare spir_func <8 x float> @_Z25__spirv_RoundFToTF32INTELDv8_f(<8 x float>)
 
 define dso_local spir_kernel void @test_ocl(float %a) {
 entry:
-  %res4 = call spir_func float @_Z35intel_round_as_tensor_float32_floatt(float 0.000000e+00)
-  %res5 = call spir_func <2 x float> @_Z37intel_round_as_tensor_float322_float2Dv2_t(<2 x float> zeroinitializer)
-  %res6 = call spir_func <3 x float> @_Z37intel_round_as_tensor_float323_float3Dv3_t(<3 x float> zeroinitializer)
-  %res7 = call spir_func <4 x float> @_Z37intel_round_as_tensor_float324_float4Dv4_t(<4 x float> zeroinitializer)
-  %res8 = call spir_func <8 x float> @_Z37intel_round_as_tensor_float328_float8Dv8_t(<8 x float> zeroinitializer)
-  %res9 = call spir_func <16 x float> @_Z39intel_round_as_tensor_float3216_float16Dv16_t(<16 x float> zeroinitializer)
+  %res4 = call spir_func float @_Z35intel_round_as_tensor_float32_floatf(float 0.000000e+00)
+  %res5 = call spir_func <2 x float> @_Z37intel_round_as_tensor_float322_float2Dv2_f(<2 x float> zeroinitializer)
+  %res6 = call spir_func <3 x float> @_Z37intel_round_as_tensor_float323_float3Dv3_f(<3 x float> zeroinitializer)
+  %res7 = call spir_func <4 x float> @_Z37intel_round_as_tensor_float324_float4Dv4_f(<4 x float> zeroinitializer)
+  %res8 = call spir_func <8 x float> @_Z37intel_round_as_tensor_float328_float8Dv8_f(<8 x float> zeroinitializer)
+  %res9 = call spir_func <16 x float> @_Z39intel_round_as_tensor_float3216_float16Dv16_f(<16 x float> zeroinitializer)
   ret void
 }
 
-declare spir_func float @_Z35intel_round_as_tensor_float32_floatt(float)
-declare spir_func <2 x float> @_Z37intel_round_as_tensor_float322_float2Dv2_t(<2 x float>)
-declare spir_func <3 x float> @_Z37intel_round_as_tensor_float323_float3Dv3_t(<3 x float>)
-declare spir_func <4 x float> @_Z37intel_round_as_tensor_float324_float4Dv4_t(<4 x float>)
-declare spir_func <8 x float> @_Z37intel_round_as_tensor_float328_float8Dv8_t(<8 x float>)
-declare spir_func <16 x float> @_Z39intel_round_as_tensor_float3216_float16Dv16_t(<16 x float>)
+declare spir_func float @_Z35intel_round_as_tensor_float32_floatf(float)
+declare spir_func <2 x float> @_Z37intel_round_as_tensor_float322_float2Dv2_f(<2 x float>)
+declare spir_func <3 x float> @_Z37intel_round_as_tensor_float323_float3Dv3_f(<3 x float>)
+declare spir_func <4 x float> @_Z37intel_round_as_tensor_float324_float4Dv4_f(<4 x float>)
+declare spir_func <8 x float> @_Z37intel_round_as_tensor_float328_float8Dv8_f(<8 x float>)
+declare spir_func <16 x float> @_Z39intel_round_as_tensor_float3216_float16Dv16_f(<16 x float>)
diff --git a/llvm/test/CodeGen/SPIRV/get-global-id-non-integer-dim.ll b/llvm/test/CodeGen/SPIRV/get-global-id-non-integer-dim.ll
index 59e62493fe890..a9c19df446eaa 100644
--- a/llvm/test/CodeGen/SPIRV/get-global-id-non-integer-dim.ll
+++ b/llvm/test/CodeGen/SPIRV/get-global-id-non-integer-dim.ll
@@ -1,10 +1,13 @@
-; RUN: not --crash llc -O0 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s
-; RUN: not --crash llc -O2 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown -spirv-ext=+SPV_KHR_bfloat16 %s -o - | FileCheck %s
+; RUN: llc -O2 -mtriple=spirv64-unknown-unknown -spirv-ext=+SPV_KHR_bfloat16 %s -o - | FileCheck %s
 
 ; get_global_id and its sibling workgroup-query builtins take an integer
 ; dimension index.
 
-; CHECK: LLVM ERROR: Expect an integer <Dimindx> argument
+; CHECK: %[[#Func:]] = OpFunction %[[#]] None %[[#]]
+; CHECK: OpFunctionParameter
+; CHECK: OpFunctionEnd
+; CHECK: OpFunctionCall %[[#]] %[[#Func]]
 
 declare spir_func i64 @_Z13get_global_idj(bfloat)
 

>From 2aee47dca817d5a84d290943a1993cf5548649d0 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Mon, 20 Jul 2026 11:49:00 +0200
Subject: [PATCH 3/4] comments

---
 llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 284763a7ae15a..58e536deb9412 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -3500,11 +3500,12 @@ static bool demangledArgTypesMatchIR(const SPIRV::IncomingCall *Call,
     return true;
 
   SmallVector<StringRef, 10> ArgTypeStrs;
-  SPIRV::parseBuiltinTypeStr(ArgTypeStrs, DemangledCall, Ctx);
+  if (!SPIRV::parseBuiltinTypeStr(ArgTypeStrs, DemangledCall, Ctx))
+    return true;
 
-  for (unsigned ArgIdx = 0; ArgIdx < Call->Arguments.size(); ++ArgIdx) {
-    if (ArgIdx >= ArgTypeStrs.size())
-      continue;
+  unsigned NumArgsToCheck =
+      std::min(Call->Arguments.size(), ArgTypeStrs.size());
+  for (unsigned ArgIdx = 0; ArgIdx < NumArgsToCheck; ++ArgIdx) {
     StringRef ArgTypeStr = ArgTypeStrs[ArgIdx].trim();
     // Opaque/builtin OpenCL and SPIR-V types (images, samplers, pipes,
     // reserve_id, etc.) are not validated here, as mangling does not enforce
@@ -3528,28 +3529,24 @@ static bool demangledArgTypesMatchIR(const SPIRV::IncomingCall *Call,
         ArgTypeOpcode != SPIRV::OpTypeVector)
       continue;
 
+    auto *ExpectedVecType = dyn_cast<VectorType>(ExpectedType);
     Type *ExpectedScalarType =
-        ExpectedType->isVectorTy()
-            ? cast<VectorType>(ExpectedType)->getElementType()
-            : ExpectedType;
+        ExpectedVecType ? ExpectedVecType->getElementType() : ExpectedType;
     SPIRVTypeInst ArgScalarType = GR->getScalarOrVectorComponentType(ArgType);
     if (!ArgScalarType)
       continue;
 
     bool ExpectedIsInt = ExpectedScalarType->isIntegerTy();
-    bool ExpectedIsFloat = ExpectedScalarType->isFloatingPointTy();
     unsigned ArgOpcode = ArgScalarType->getOpcode();
     bool ArgIsInt =
         ArgOpcode == SPIRV::OpTypeInt || ArgOpcode == SPIRV::OpTypeBool;
-    bool ArgIsFloat = ArgOpcode == SPIRV::OpTypeFloat;
 
-    if ((ExpectedIsInt && !ArgIsInt) || (ExpectedIsFloat && !ArgIsFloat))
+    if (ExpectedIsInt != ArgIsInt)
       return false;
 
     unsigned ExpectedElts =
-        ExpectedType->isVectorTy()
-            ? cast<VectorType>(ExpectedType)->getElementCount().getFixedValue()
-            : 1;
+        ExpectedVecType ? ExpectedVecType->getElementCount().getFixedValue()
+                        : 1;
     if (ExpectedElts != GR->getScalarOrVectorComponentCount(ArgType))
       return false;
   }

>From 16997ee4c78691cb454a5a4fa69eb94cded7c8e6 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Mon, 20 Jul 2026 17:23:27 +0200
Subject: [PATCH 4/4] handle sret

---
 llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 58e536deb9412..ed14d3cf0e4d2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -3494,8 +3494,8 @@ mapBuiltinToOpcode(StringRef DemangledCall,
 /// type implied by the mangled name, true otherwise.
 static bool demangledArgTypesMatchIR(const SPIRV::IncomingCall *Call,
                                      StringRef DemangledCall,
-                                     SPIRVGlobalRegistry *GR,
-                                     LLVMContext &Ctx) {
+                                     SPIRVGlobalRegistry *GR, LLVMContext &Ctx,
+                                     const CallBase &CB) {
   if (Call->isSpirvOp())
     return true;
 
@@ -3503,8 +3503,12 @@ static bool demangledArgTypesMatchIR(const SPIRV::IncomingCall *Call,
   if (!SPIRV::parseBuiltinTypeStr(ArgTypeStrs, DemangledCall, Ctx))
     return true;
 
+  unsigned ArgBase = CB.hasStructRetAttr() ? 1 : 0;
+  if (Call->Arguments.size() < ArgBase)
+    return true;
+  unsigned NumMangledArgs = Call->Arguments.size() - ArgBase;
   unsigned NumArgsToCheck =
-      std::min(Call->Arguments.size(), ArgTypeStrs.size());
+      std::min<unsigned>(NumMangledArgs, ArgTypeStrs.size());
   for (unsigned ArgIdx = 0; ArgIdx < NumArgsToCheck; ++ArgIdx) {
     StringRef ArgTypeStr = ArgTypeStrs[ArgIdx].trim();
     // Opaque/builtin OpenCL and SPIR-V types (images, samplers, pipes,
@@ -3519,7 +3523,8 @@ static bool demangledArgTypesMatchIR(const SPIRV::IncomingCall *Call,
         ExpectedType->isPointerTy() || ExpectedType->isTargetExtTy())
       continue;
 
-    SPIRVTypeInst ArgType = GR->getSPIRVTypeForVReg(Call->Arguments[ArgIdx]);
+    SPIRVTypeInst ArgType =
+        GR->getSPIRVTypeForVReg(Call->Arguments[ArgIdx + ArgBase]);
     if (!ArgType)
       continue;
     unsigned ArgTypeOpcode = ArgType->getOpcode();
@@ -3593,7 +3598,7 @@ std::optional<bool> lowerBuiltin(StringRef DemangledCall,
   // (e.g. broken mangling), treat the call as a regular function call
   // rather than crashing.
   if (!demangledArgTypesMatchIR(Call.get(), DemangledCall, GR,
-                                MIRBuilder.getContext())) {
+                                MIRBuilder.getContext(), CB)) {
     LLVM_DEBUG(dbgs() << "Argument types do not match mangled types for "
                       << "builtin " << DemangledCall
                       << "; treating as a normal function\n");



More information about the llvm-commits mailing list