Skip to content

Commit 06ec470

Browse files
authored
[SPIRV] Handle unknown intrinsics (llvm#166284)
This ports rather useful functionality that was already available in the Translator, and was mostly implemented in the BE. Today, if we encounter an unknown intrinsic, we pipe it through and hope for the best, which in practice yields either obtuse ISEL errors, or potentially impossible to use SPIR-V. With this change, if instructed via a newly added `--spv-allow-unknown-intrinsics` flag, we emit allowed intrinsics as calls to extern (import) functions. The test is also mostly lifted from the Translator.
1 parent 59f6f33 commit 06ec470

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ class SPIRVPrepareFunctions : public ModulePass {
5656
}
5757
};
5858

59+
static cl::list<std::string> SPVAllowUnknownIntrinsics(
60+
"spv-allow-unknown-intrinsics", cl::CommaSeparated,
61+
cl::desc("Emit unknown intrinsics as calls to external functions. A "
62+
"comma-separated input list of intrinsic prefixes must be "
63+
"provided, and only intrinsics carrying a listed prefix get "
64+
"emitted as described."),
65+
cl::value_desc("intrinsic_prefix_0,intrinsic_prefix_1"), cl::ValueOptional);
5966
} // namespace
6067

6168
char SPIRVPrepareFunctions::ID = 0;
@@ -445,6 +452,15 @@ bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
445452
EraseFromParent);
446453
Changed = true;
447454
break;
455+
default:
456+
if (TM.getTargetTriple().getVendor() == Triple::AMD ||
457+
any_of(SPVAllowUnknownIntrinsics, [II](auto &&Prefix) {
458+
if (Prefix.empty())
459+
return false;
460+
return II->getCalledFunction()->getName().starts_with(Prefix);
461+
}))
462+
Changed |= lowerIntrinsicToFunction(II);
463+
break;
448464
}
449465
}
450466
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o %t.spvt 2>&1 | FileCheck -check-prefix=CHECK-ERROR %s
2+
; 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
3+
; 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
4+
; 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
5+
; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm. %s -o - | FileCheck %s
6+
; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm.,random.prefix %s -o - | FileCheck %s
7+
; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-amd-amdhsa %s -o - | FileCheck %s
8+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spv-allow-unknown-intrinsics=llvm. %s -o - -filetype=obj | spirv-val %}
9+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-amd-amdhsa %s -o - -filetype=obj | spirv-val %}
10+
11+
; The test checks command-line option which allows to represent unknown
12+
; intrinsics as external function calls in SPIR-V.
13+
14+
; CHECK-ERROR: LLVM ERROR: unable to legalize instruction: %3:iid(s64) = G_READCYCLECOUNTER (in function: foo)
15+
16+
; CHECK: Name %[[READCYCLECOUNTER:[0-9]+]] "spirv.llvm_readcyclecounter"
17+
; CHECK: Name %[[SOME_CUSTOM_INTRINSIC:[0-9]+]] "spirv.llvm_some_custom_intrinsic"
18+
; CHECK-DAG: Decorate %[[READCYCLECOUNTER]] LinkageAttributes {{.*}} Import
19+
; CHECK: Decorate %[[SOME_CUSTOM_INTRINSIC]] LinkageAttributes {{.*}} Import
20+
; CHECK-DAG: %[[I64:[0-9]+]] = OpTypeInt 64
21+
; CHECK: %[[FnTy:[0-9]+]] = OpTypeFunction %[[I64]]
22+
; CHECK: %[[READCYCLECOUNTER]] = OpFunction %[[I64]] {{.*}} %[[FnTy]]
23+
; CHECK-DAG: %[[SOME_CUSTOM_INTRINSIC]] = OpFunction %[[I64]] {{.*}} %[[FnTy]]
24+
; CHECK-DAG: OpFunctionCall %[[I64]] %[[READCYCLECOUNTER]]
25+
; CHECK: OpFunctionCall %[[I64]] %[[SOME_CUSTOM_INTRINSIC]]
26+
27+
define spir_func void @foo() {
28+
entry:
29+
; TODO: if and when the SPIR-V learns how to lower readcyclecounter, we will have to pick another unhandled intrinsic
30+
%0 = call i64 @llvm.readcyclecounter()
31+
%1 = call i64 @llvm.some.custom.intrinsic()
32+
ret void
33+
}
34+
35+
declare i64 @llvm.readcyclecounter()
36+
declare i64 @llvm.some.custom.intrinsic()

0 commit comments

Comments
 (0)