[SPIR-V] Don't lower builtin variadic functions#188517
Conversation
Signed-off-by: Nick Sarnie <[email protected]>
jhuber6
left a comment
There was a problem hiding this comment.
So this goes back to the old handling?
|
For builtins, unfortunately yes. My previous change was totally wrong, I thought the IR extracted each arg out of the Builtins aren't going to do that, the calls to them get replaced directly with an instruction, and trying to add new logic to the backend to extract args out of the vararg buffer seems not the right solution. |
My preferred solution would be to stop handling printf in 50 billion different ways, but that's a pipe dream apparently. |
|
My lawyer answer is this is intended for any variadic builtins and is true because of the direct function to instruction replacement but yeah, not exactly clean code here. |
|
@llvm/pr-subscribers-llvm-transforms Author: Nick Sarnie (sarnex) ChangesIn #178980 I tried to remove the special handling for However the root issue is with any builtin, they are just replaced directly with instructions, and the only place that could generate code to honor the variadic ABI would bei in the SPIR-V backend, and it would be pretty complicated for pretty much no benefit. It's much simpler to teach the pass to skip certain functions, as we did before, but this time skip all SPIR-V builtin. 2 Files Affected:
diff --git a/llvm/lib/Transforms/IPO/ExpandVariadics.cpp b/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
index 97b95cce2a407..c07ea8093c9d6 100644
--- a/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
+++ b/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
@@ -129,6 +129,9 @@ class VariadicABIInfo {
bool vaEndIsNop() { return true; }
bool vaCopyIsMemcpy() { return true; }
+ // Per-target overrides of special symbols.
+ virtual bool ignoreFunction(Function *F) { return false; }
+
// Any additional address spaces used in va intrinsics that should be
// expanded.
virtual SmallVector<unsigned> getTargetSpecificVaIntrinAddrSpaces() const {
@@ -242,6 +245,9 @@ class ExpandVariadics : public ModulePass {
F->hasFnAttribute(Attribute::Naked))
return false;
+ if (ABI->ignoreFunction(F))
+ return false;
+
if (!isValidCallingConv(F))
return false;
@@ -627,6 +633,9 @@ bool ExpandVariadics::expandCall(Module &M, IRBuilder<> &Builder, CallBase *CB,
bool Changed = false;
const DataLayout &DL = M.getDataLayout();
+ if (ABI->ignoreFunction(CB->getCalledFunction()))
+ return Changed;
+
if (!expansionApplicableToFunctionCall(CB)) {
if (rewriteABI())
report_fatal_error("Cannot lower callbase instruction");
@@ -984,6 +993,29 @@ struct SPIRV final : public VariadicABIInfo {
return {A, false};
}
+ // The SPIR-V backend has special handling for builtins.
+ bool ignoreFunction(Function *F) override {
+ if (!F->isDeclaration())
+ return false;
+
+ StringRef Name = F->getName();
+
+ // Skip any SPIR-V builtins.
+ if (Name.contains("__spirv_"))
+ return true;
+
+ // Skip the builtin printf function.
+ if (Name.contains("printf")) {
+ std::string Demangled = llvm::demangle(Name.str());
+ // Demangled name will be "printf(...)" for the builtin, "printf" for
+ // unmangled extern "C", or "namespace::printf(...)" for namespaced.
+ if (StringRef(Demangled).starts_with("printf(") || Demangled == "printf")
+ return true;
+ }
+
+ return false;
+ }
+
// We will likely see va intrinsics in the generic addrspace (4).
SmallVector<unsigned> getTargetSpecificVaIntrinAddrSpaces() const override {
return {4};
diff --git a/llvm/test/CodeGen/SPIRV/printf.ll b/llvm/test/CodeGen/SPIRV/printf.ll
index f2747901c54e0..1227b75fca92d 100644
--- a/llvm/test/CodeGen/SPIRV/printf.ll
+++ b/llvm/test/CodeGen/SPIRV/printf.ll
@@ -6,35 +6,33 @@
; CHECK: %[[#ExtImport:]] = OpExtInstImport "OpenCL.std"
; CHECK: %[[#Char:]] = OpTypeInt 8 0
-; CHECK: %[[#ConstCharPtr:]] = OpTypePointer UniformConstant %[[#Char]]
-; CHECK: %[[#VarargStruct:]] = OpTypeStruct %[[#Char]]
-; CHECK: %[[#VarargStructPtr:]] = OpTypePointer Function %[[#VarargStruct]]
-; CHECK: %[[#CharPtr:]] = OpTypePointer Function %[[#Char]]
-; CHECK: %[[#IntConst:]] = OpConstant %[[#Char]] 97
+; CHECK: %[[#CharPtr:]] = OpTypePointer UniformConstant %[[#Char]]
; CHECK: %[[#GV:]] = OpVariable %[[#]] UniformConstant %[[#]]
; CHECK: OpFunction
-; CHECK: %[[#Arg:]] = OpFunctionParameter
-; CHECK: %[[#VarargBuffer1:]] = OpVariable %[[#VarargStructPtr]] Function
-; CHECK: %[[#VarargBuffer2:]] = OpVariable %[[#VarargStructPtr]] Function
-; CHECK: %[[#CastedBuffer1:]] = OpBitcast %[[#CharPtr]] %[[#VarargBuffer1]]
-; CHECK: %[[#GEP1:]] = OpInBoundsPtrAccessChain %[[#CharPtr]] %[[#VarargBuffer1]]
-; CHECK: OpStore %[[#GEP1]] %[[#IntConst]] Aligned 1
-; CHECK: %[[#CastedGV:]] = OpBitcast %[[#ConstCharPtr]] %[[#GV]]
-; CHECK: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedGV]] %[[#CastedBuffer1:]]
-; CHECK: %[[#CastedBuffer2:]] = OpBitcast %[[#CharPtr]] %[[#VarargBuffer2]]
-; CHECK: %[[#GEP2:]] = OpInBoundsPtrAccessChain %[[#CharPtr]] %[[#VarargBuffer2]]
-; CHECK: OpStore %[[#GEP2]] %[[#IntConst]] Aligned 1
-; CHECK: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#Arg]] %[[#CastedBuffer2:]]
+; CHECK: %[[#Arg1:]] = OpFunctionParameter
+; CHECK: %[[#Arg2:]] = OpFunctionParameter
+; CHECK: %[[#CastedGV:]] = OpBitcast %[[#CharPtr]] %[[#GV]]
+; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedGV]] %[[#ArgConst:]]
+; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedGV]] %[[#ArgConst]]
+; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#Arg1]] %[[#ArgConst]]
+; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#Arg1]] %[[#ArgConst]]
+; CHECK-NEXT: %[[#CastedArg2:]] = OpBitcast %[[#CharPtr]] %[[#Arg2]]
+; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedArg2]] %[[#ArgConst]]
+; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedArg2]] %[[#ArgConst]]
; CHECK: OpFunctionEnd
%struct = type { [6 x i8] }
@FmtStr = internal addrspace(2) constant [6 x i8] c"c=%c\0A\00", align 1
-define spir_kernel void @foo(ptr addrspace(2) %_arg_fmt) {
+define spir_kernel void @foo(ptr addrspace(2) %_arg_fmt1, ptr addrspace(2) byval(%struct) %_arg_fmt2) {
entry:
%r1 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z6printfPU3AS2Kcz(ptr addrspace(2) @FmtStr, i8 signext 97)
- %r2 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt, i8 signext 97)
+ %r2 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) @FmtStr, i8 signext 97)
+ %r3 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z6printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt1, i8 signext 97)
+ %r4 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt1, i8 signext 97)
+ %r5 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z6printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt2, i8 signext 97)
+ %r6 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt2, i8 signext 97)
ret void
}
|
|
@llvm/pr-subscribers-backend-spir-v Author: Nick Sarnie (sarnex) ChangesIn #178980 I tried to remove the special handling for However the root issue is with any builtin, they are just replaced directly with instructions, and the only place that could generate code to honor the variadic ABI would bei in the SPIR-V backend, and it would be pretty complicated for pretty much no benefit. It's much simpler to teach the pass to skip certain functions, as we did before, but this time skip all SPIR-V builtin. 2 Files Affected:
diff --git a/llvm/lib/Transforms/IPO/ExpandVariadics.cpp b/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
index 97b95cce2a407..c07ea8093c9d6 100644
--- a/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
+++ b/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
@@ -129,6 +129,9 @@ class VariadicABIInfo {
bool vaEndIsNop() { return true; }
bool vaCopyIsMemcpy() { return true; }
+ // Per-target overrides of special symbols.
+ virtual bool ignoreFunction(Function *F) { return false; }
+
// Any additional address spaces used in va intrinsics that should be
// expanded.
virtual SmallVector<unsigned> getTargetSpecificVaIntrinAddrSpaces() const {
@@ -242,6 +245,9 @@ class ExpandVariadics : public ModulePass {
F->hasFnAttribute(Attribute::Naked))
return false;
+ if (ABI->ignoreFunction(F))
+ return false;
+
if (!isValidCallingConv(F))
return false;
@@ -627,6 +633,9 @@ bool ExpandVariadics::expandCall(Module &M, IRBuilder<> &Builder, CallBase *CB,
bool Changed = false;
const DataLayout &DL = M.getDataLayout();
+ if (ABI->ignoreFunction(CB->getCalledFunction()))
+ return Changed;
+
if (!expansionApplicableToFunctionCall(CB)) {
if (rewriteABI())
report_fatal_error("Cannot lower callbase instruction");
@@ -984,6 +993,29 @@ struct SPIRV final : public VariadicABIInfo {
return {A, false};
}
+ // The SPIR-V backend has special handling for builtins.
+ bool ignoreFunction(Function *F) override {
+ if (!F->isDeclaration())
+ return false;
+
+ StringRef Name = F->getName();
+
+ // Skip any SPIR-V builtins.
+ if (Name.contains("__spirv_"))
+ return true;
+
+ // Skip the builtin printf function.
+ if (Name.contains("printf")) {
+ std::string Demangled = llvm::demangle(Name.str());
+ // Demangled name will be "printf(...)" for the builtin, "printf" for
+ // unmangled extern "C", or "namespace::printf(...)" for namespaced.
+ if (StringRef(Demangled).starts_with("printf(") || Demangled == "printf")
+ return true;
+ }
+
+ return false;
+ }
+
// We will likely see va intrinsics in the generic addrspace (4).
SmallVector<unsigned> getTargetSpecificVaIntrinAddrSpaces() const override {
return {4};
diff --git a/llvm/test/CodeGen/SPIRV/printf.ll b/llvm/test/CodeGen/SPIRV/printf.ll
index f2747901c54e0..1227b75fca92d 100644
--- a/llvm/test/CodeGen/SPIRV/printf.ll
+++ b/llvm/test/CodeGen/SPIRV/printf.ll
@@ -6,35 +6,33 @@
; CHECK: %[[#ExtImport:]] = OpExtInstImport "OpenCL.std"
; CHECK: %[[#Char:]] = OpTypeInt 8 0
-; CHECK: %[[#ConstCharPtr:]] = OpTypePointer UniformConstant %[[#Char]]
-; CHECK: %[[#VarargStruct:]] = OpTypeStruct %[[#Char]]
-; CHECK: %[[#VarargStructPtr:]] = OpTypePointer Function %[[#VarargStruct]]
-; CHECK: %[[#CharPtr:]] = OpTypePointer Function %[[#Char]]
-; CHECK: %[[#IntConst:]] = OpConstant %[[#Char]] 97
+; CHECK: %[[#CharPtr:]] = OpTypePointer UniformConstant %[[#Char]]
; CHECK: %[[#GV:]] = OpVariable %[[#]] UniformConstant %[[#]]
; CHECK: OpFunction
-; CHECK: %[[#Arg:]] = OpFunctionParameter
-; CHECK: %[[#VarargBuffer1:]] = OpVariable %[[#VarargStructPtr]] Function
-; CHECK: %[[#VarargBuffer2:]] = OpVariable %[[#VarargStructPtr]] Function
-; CHECK: %[[#CastedBuffer1:]] = OpBitcast %[[#CharPtr]] %[[#VarargBuffer1]]
-; CHECK: %[[#GEP1:]] = OpInBoundsPtrAccessChain %[[#CharPtr]] %[[#VarargBuffer1]]
-; CHECK: OpStore %[[#GEP1]] %[[#IntConst]] Aligned 1
-; CHECK: %[[#CastedGV:]] = OpBitcast %[[#ConstCharPtr]] %[[#GV]]
-; CHECK: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedGV]] %[[#CastedBuffer1:]]
-; CHECK: %[[#CastedBuffer2:]] = OpBitcast %[[#CharPtr]] %[[#VarargBuffer2]]
-; CHECK: %[[#GEP2:]] = OpInBoundsPtrAccessChain %[[#CharPtr]] %[[#VarargBuffer2]]
-; CHECK: OpStore %[[#GEP2]] %[[#IntConst]] Aligned 1
-; CHECK: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#Arg]] %[[#CastedBuffer2:]]
+; CHECK: %[[#Arg1:]] = OpFunctionParameter
+; CHECK: %[[#Arg2:]] = OpFunctionParameter
+; CHECK: %[[#CastedGV:]] = OpBitcast %[[#CharPtr]] %[[#GV]]
+; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedGV]] %[[#ArgConst:]]
+; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedGV]] %[[#ArgConst]]
+; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#Arg1]] %[[#ArgConst]]
+; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#Arg1]] %[[#ArgConst]]
+; CHECK-NEXT: %[[#CastedArg2:]] = OpBitcast %[[#CharPtr]] %[[#Arg2]]
+; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedArg2]] %[[#ArgConst]]
+; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedArg2]] %[[#ArgConst]]
; CHECK: OpFunctionEnd
%struct = type { [6 x i8] }
@FmtStr = internal addrspace(2) constant [6 x i8] c"c=%c\0A\00", align 1
-define spir_kernel void @foo(ptr addrspace(2) %_arg_fmt) {
+define spir_kernel void @foo(ptr addrspace(2) %_arg_fmt1, ptr addrspace(2) byval(%struct) %_arg_fmt2) {
entry:
%r1 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z6printfPU3AS2Kcz(ptr addrspace(2) @FmtStr, i8 signext 97)
- %r2 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt, i8 signext 97)
+ %r2 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) @FmtStr, i8 signext 97)
+ %r3 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z6printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt1, i8 signext 97)
+ %r4 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt1, i8 signext 97)
+ %r5 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z6printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt2, i8 signext 97)
+ %r6 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt2, i8 signext 97)
ret void
}
|
Signed-off-by: Nick Sarnie <[email protected]>
MrSidims
left a comment
There was a problem hiding this comment.
LGTM with a note about function names check is not efficient. But I feel like my review is neither gating nor sufficient for this PR.
Signed-off-by: Nick Sarnie <[email protected]>
| %r3 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z6printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt1, i8 signext 97) | ||
| %r4 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt1, i8 signext 97) | ||
| %r5 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z6printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt2, i8 signext 97) | ||
| %r6 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt2, i8 signext 97) |
There was a problem hiding this comment.
Can we add a couple of negative test cases having __spirv_ and printf at the middle of the name, so we make sure those would not be skipped?
There was a problem hiding this comment.
Thanks for the review. Sure, let me add those.
Signed-off-by: Nick Sarnie <[email protected]>
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/129/builds/41797 Here is the relevant piece of the build log for the reference |
llvm#188517 fixed regression.
In #178980 I tried to remove the special handling for
printf, but the fix was wrong, the pass expects thatprintfabides by the variadic ABI (single buffer passed in, args extracted by caller), which isn't how it works.However the root issue is with any builtin, they are just replaced directly with instructions, and the only place that could generate code to honor the variadic ABI would bei in the SPIR-V backend, and it would be pretty complicated for pretty much no benefit.
It's much simpler to teach the pass to skip certain functions, as we did before, but this time skip all SPIR-V builtin.