[llvm] [SPIRV] Handle unknown intrinsics (PR #166284)

Alex Voicu via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 4 06:52:38 PST 2025


https://github.com/AlexVlx updated https://github.com/llvm/llvm-project/pull/166284

>From 3ff79b508202b47fb3cc6fa51197e0b9348deeeb Mon Sep 17 00:00:00 2001
From: Alex Voicu <alexandru.voicu at amd.com>
Date: Tue, 4 Nov 2025 02:01:10 +0000
Subject: [PATCH 1/4] Add support for handling unknown intrinsics.

---
 .../Target/SPIRV/SPIRVPrepareFunctions.cpp    | 21 ++++++++++
 .../CodeGen/SPIRV/allow_unknown_intrinsics.ll | 38 +++++++++++++++++++
 2 files changed, 59 insertions(+)
 create mode 100644 llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
index 4e4e6fb4ab791..3334bb18deab9 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
@@ -56,6 +56,13 @@ class SPIRVPrepareFunctions : public ModulePass {
   }
 };
 
+static cl::list<std::string> SPVAllowUnknownIntrinsics(
+    "spv-allow-unknown-intrinsics", cl::CommaSeparated,
+    cl::desc("Emit unknown intrinsics as calls to external functions. If a "
+             "comma-separated input list of intrinsic prefixes is provided, "
+             "only intrinsics carrying a listed prefix get emitted. Otherwise, "
+             "all unknown intrinsics are emitted"),
+    cl::value_desc("intrinsic_prefix_0,intrinsic_prefix_1"), cl::ValueOptional);
 } // namespace
 
 char SPIRVPrepareFunctions::ID = 0;
@@ -445,6 +452,20 @@ bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
                                        EraseFromParent);
         Changed = true;
         break;
+      default:
+        if (TM.getTargetTriple().getVendor() == Triple::AMD ||
+            any_of(SPVAllowUnknownIntrinsics, [II](auto &&Prefix) {
+              return II->getCalledFunction()->getName().starts_with(Prefix);
+            }))
+          Changed |= lowerIntrinsicToFunction(II);
+        else
+          report_fatal_error(
+              "Encountered unknown intrinsic: " +
+                II->getCalledFunction()->getName() +
+                ", which requires the --spv-allow-unknown-intrinsics option, "
+                "in function: " + II->getParent()->getParent()->getName(),
+              false);
+        break;
       }
     }
   }
diff --git a/llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll b/llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll
new file mode 100644
index 0000000000000..872c4a68a8160
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll
@@ -0,0 +1,38 @@
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o %t.spvt 2>&1 | FileCheck -check-prefix=CHECK-ERROR %s
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=notllvm %s -o %t.spvt 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm.some.custom %s -o %t.spvt 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm.readcyclecounter %s -o %t.spvt 2>&1 | FileCheck --check-prefix=CHECK-ERROR1 %s
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics %s -o - | FileCheck %s
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm. %s -o - | FileCheck %s
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm.,random.prefix %s -o - | FileCheck %s
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm %s -o - | FileCheck %s
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-amd-amdhsa %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics %s -o - -filetype=obj | spirv-val %}
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-amd-amdhsa %s -o - -filetype=obj | spirv-val %}
+
+; The test checks command-line option which allows to represent unknown
+; intrinsics as external function calls in SPIR-V.
+
+; CHECK-ERROR: LLVM ERROR: Encountered unknown intrinsic: llvm.readcyclecounter, which requires the --spv-allow-unknown-intrinsics option, in function: foo
+; CHECK-ERROR1: LLVM ERROR: Encountered unknown intrinsic: llvm.some.custom.intrinsic, which requires the --spv-allow-unknown-intrinsics option, in function: foo
+
+; CHECK: Name %[[READCYCLECOUNTER:[0-9]+]] "spirv.llvm_readcyclecounter"
+; CHECK: Name %[[SOME_CUSTOM_INTRINSIC:[0-9]+]] "spirv.llvm_some_custom_intrinsic"
+; CHECK-DAG: Decorate %[[READCYCLECOUNTER]] LinkageAttributes {{.*}} Import
+; CHECK: Decorate %[[SOME_CUSTOM_INTRINSIC]] LinkageAttributes {{.*}} Import
+; CHECK-DAG: %[[I64:[0-9]+]] = OpTypeInt 64
+; CHECK: %[[FnTy:[0-9]+]] = OpTypeFunction %[[I64]]
+; CHECK: %[[READCYCLECOUNTER]] = OpFunction %[[I64]] {{.*}} %[[FnTy]]
+; CHECK-DAG: %[[SOME_CUSTOM_INTRINSIC]] = OpFunction %[[I64]] {{.*}} %[[FnTy]]
+; CHECK-DAG: OpFunctionCall %[[I64]] %[[READCYCLECOUNTER]]
+; CHECK:     OpFunctionCall %[[I64]] %[[SOME_CUSTOM_INTRINSIC]]
+
+define spir_func void @foo() {
+entry:
+  %0 = call i64 @llvm.readcyclecounter()
+  %1 = call i64 @llvm.some.custom.intrinsic()
+  ret void
+}
+
+declare i64 @llvm.readcyclecounter()
+declare i64 @llvm.some.custom.intrinsic()

>From a06986e8b2a352a589f24f2a21a62eff2d9e17c7 Mon Sep 17 00:00:00 2001
From: Alex Voicu <alexandru.voicu at amd.com>
Date: Tue, 4 Nov 2025 02:56:02 +0000
Subject: [PATCH 2/4] Fix formatting.

---
 llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
index 3334bb18deab9..1e16b82396ba2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
@@ -461,9 +461,10 @@ bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
         else
           report_fatal_error(
               "Encountered unknown intrinsic: " +
-                II->getCalledFunction()->getName() +
-                ", which requires the --spv-allow-unknown-intrinsics option, "
-                "in function: " + II->getParent()->getParent()->getName(),
+                  II->getCalledFunction()->getName() +
+                  ", which requires the --spv-allow-unknown-intrinsics option, "
+                  "in function: " +
+                  II->getParent()->getParent()->getName(),
               false);
         break;
       }

>From 893974678bbc67ceb481db79dba377983f1aa763 Mon Sep 17 00:00:00 2001
From: Alex Voicu <alexandru.voicu at amd.com>
Date: Tue, 4 Nov 2025 03:17:42 +0000
Subject: [PATCH 3/4] Fix breakage, we cannot error out so aggressively.

---
 llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp     | 8 --------
 llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll | 4 +---
 2 files changed, 1 insertion(+), 11 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
index 1e16b82396ba2..804cfb6b17490 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
@@ -458,14 +458,6 @@ bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
               return II->getCalledFunction()->getName().starts_with(Prefix);
             }))
           Changed |= lowerIntrinsicToFunction(II);
-        else
-          report_fatal_error(
-              "Encountered unknown intrinsic: " +
-                  II->getCalledFunction()->getName() +
-                  ", which requires the --spv-allow-unknown-intrinsics option, "
-                  "in function: " +
-                  II->getParent()->getParent()->getName(),
-              false);
         break;
       }
     }
diff --git a/llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll b/llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll
index 872c4a68a8160..c7af93e8fd0fd 100644
--- a/llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll
+++ b/llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll
@@ -1,7 +1,6 @@
 ; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o %t.spvt 2>&1 | FileCheck -check-prefix=CHECK-ERROR %s
 ; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=notllvm %s -o %t.spvt 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
 ; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm.some.custom %s -o %t.spvt 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
-; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm.readcyclecounter %s -o %t.spvt 2>&1 | FileCheck --check-prefix=CHECK-ERROR1 %s
 ; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics %s -o - | FileCheck %s
 ; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm. %s -o - | FileCheck %s
 ; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm.,random.prefix %s -o - | FileCheck %s
@@ -13,8 +12,7 @@
 ; The test checks command-line option which allows to represent unknown
 ; intrinsics as external function calls in SPIR-V.
 
-; CHECK-ERROR: LLVM ERROR: Encountered unknown intrinsic: llvm.readcyclecounter, which requires the --spv-allow-unknown-intrinsics option, in function: foo
-; CHECK-ERROR1: LLVM ERROR: Encountered unknown intrinsic: llvm.some.custom.intrinsic, which requires the --spv-allow-unknown-intrinsics option, in function: foo
+; CHECK-ERROR: LLVM ERROR: unable to legalize instruction: %3:iid(s64) = G_READCYCLECOUNTER (in function: foo)
 
 ; CHECK: Name %[[READCYCLECOUNTER:[0-9]+]] "spirv.llvm_readcyclecounter"
 ; CHECK: Name %[[SOME_CUSTOM_INTRINSIC:[0-9]+]] "spirv.llvm_some_custom_intrinsic"

>From a71afc4f1102ac163340f31492d47dc710e13657 Mon Sep 17 00:00:00 2001
From: Alex Voicu <alexandru.voicu at amd.com>
Date: Tue, 4 Nov 2025 14:52:30 +0000
Subject: [PATCH 4/4] Implement review suggestion.

---
 llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp     | 10 ++++++----
 llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll |  5 ++---
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
index 804cfb6b17490..be88f334d2171 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
@@ -58,10 +58,10 @@ class SPIRVPrepareFunctions : public ModulePass {
 
 static cl::list<std::string> SPVAllowUnknownIntrinsics(
     "spv-allow-unknown-intrinsics", cl::CommaSeparated,
-    cl::desc("Emit unknown intrinsics as calls to external functions. If a "
-             "comma-separated input list of intrinsic prefixes is provided, "
-             "only intrinsics carrying a listed prefix get emitted. Otherwise, "
-             "all unknown intrinsics are emitted"),
+    cl::desc("Emit unknown intrinsics as calls to external functions. A "
+             "comma-separated input list of intrinsic prefixes must be "
+             "provided, and only intrinsics carrying a listed prefix get "
+             "emitted as described."),
     cl::value_desc("intrinsic_prefix_0,intrinsic_prefix_1"), cl::ValueOptional);
 } // namespace
 
@@ -455,6 +455,8 @@ bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
       default:
         if (TM.getTargetTriple().getVendor() == Triple::AMD ||
             any_of(SPVAllowUnknownIntrinsics, [II](auto &&Prefix) {
+              if (Prefix.empty())
+                return false;
               return II->getCalledFunction()->getName().starts_with(Prefix);
             }))
           Changed |= lowerIntrinsicToFunction(II);
diff --git a/llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll b/llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll
index c7af93e8fd0fd..843fd4ae469f8 100644
--- a/llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll
+++ b/llvm/test/CodeGen/SPIRV/allow_unknown_intrinsics.ll
@@ -1,12 +1,11 @@
 ; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o %t.spvt 2>&1 | FileCheck -check-prefix=CHECK-ERROR %s
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics %s -o %t.spvt 2>&1 | FileCheck -check-prefix=CHECK-ERROR %s
 ; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=notllvm %s -o %t.spvt 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
 ; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm.some.custom %s -o %t.spvt 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
-; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics %s -o - | FileCheck %s
 ; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm. %s -o - | FileCheck %s
 ; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm.,random.prefix %s -o - | FileCheck %s
-; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm %s -o - | FileCheck %s
 ; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-amd-amdhsa %s -o - | FileCheck %s
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics %s -o - -filetype=obj | spirv-val %}
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm. %s -o - -filetype=obj | spirv-val %}
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-amd-amdhsa %s -o - -filetype=obj | spirv-val %}
 
 ; The test checks command-line option which allows to represent unknown



More information about the llvm-commits mailing list