[clang] [llvm] [SPIR-V] Enable variadic function lowering for the SPIR-V target (PR #175076)
Joseph Huber via llvm-commits
llvm-commits at lists.llvm.org
Thu Jan 8 13:52:57 PST 2026
https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/175076
>From 3b6e96d95a69214ec51befb917103371c3c9c1f3 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Thu, 8 Jan 2026 15:48:20 -0600
Subject: [PATCH] [SPIR-V] Enable variadic function lowering for the SPIR-V
target
Summary:
We support variadic functions in AMDGPU / NVPTX via an LLVM-IR pass.
This patch applies the same handling here to support them on this
target.
I am unsure what the ABI should look like here, I have mostly copied the
one we use for NVPTX where it's basically a struct layout with natural
alignment. This wastes some space, which is why AMDGPU does not pad
them.
Additionally, this required allowing the SPIRV_FUNC calling convention.
I'm assuming this is compatible with the C calling convention in IR, but
I will need someone to confirm that for me.
---
clang/lib/CodeGen/Targets/SPIR.cpp | 15 ++
clang/test/CodeGenSPIRV/Builtins/variadic.c | 76 ++++++++++
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 6 -
llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp | 2 +
llvm/lib/Transforms/IPO/ExpandVariadics.cpp | 38 ++++-
.../SPIRV/function/variadics-lowering.ll | 135 ++++++++++++++++++
6 files changed, 264 insertions(+), 8 deletions(-)
create mode 100644 clang/test/CodeGenSPIRV/Builtins/variadic.c
create mode 100644 llvm/test/CodeGen/SPIRV/function/variadics-lowering.ll
diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp
index 6c6c4794bba49..ba90ab3e67053 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -36,6 +36,8 @@ class SPIRVABIInfo : public CommonSPIRABIInfo {
public:
SPIRVABIInfo(CodeGenTypes &CGT) : CommonSPIRABIInfo(CGT) {}
void computeInfo(CGFunctionInfo &FI) const override;
+ RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
+ AggValueSlot Slot) const override;
private:
ABIArgInfo classifyKernelArgumentType(QualType Ty) const;
@@ -207,6 +209,11 @@ void SPIRVABIInfo::computeInfo(CGFunctionInfo &FI) const {
// arguments handling.
llvm::CallingConv::ID CC = FI.getCallingConvention();
+ for (auto &&[ArgumentsCount, I] : llvm::enumerate(FI.arguments()))
+ I.info = ArgumentsCount < FI.getNumRequiredArgs()
+ ? classifyArgumentType(I.type)
+ : ABIArgInfo::getDirect();
+
if (!getCXXABI().classifyReturnType(FI))
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
@@ -219,6 +226,14 @@ void SPIRVABIInfo::computeInfo(CGFunctionInfo &FI) const {
}
}
+RValue SPIRVABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
+ QualType Ty, AggValueSlot Slot) const {
+ return emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*IsIndirect=*/false,
+ getContext().getTypeInfoInChars(Ty),
+ CharUnits::fromQuantity(1),
+ /*AllowHigherAlign=*/true, Slot);
+}
+
unsigned AMDGCNSPIRVABIInfo::numRegsForType(QualType Ty) const {
// This duplicates the AMDGPUABI computation.
unsigned NumRegs = 0;
diff --git a/clang/test/CodeGenSPIRV/Builtins/variadic.c b/clang/test/CodeGenSPIRV/Builtins/variadic.c
new file mode 100644
index 0000000000000..adf7b117812eb
--- /dev/null
+++ b/clang/test/CodeGenSPIRV/Builtins/variadic.c
@@ -0,0 +1,76 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// RUN: %clang_cc1 -triple spirv64 -emit-llvm -o - %s | FileCheck %s
+
+extern void varargs_simple(int, ...);
+
+// CHECK-LABEL: define spir_func void @foo(
+// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[C:%.*]] = alloca i8, align 1
+// CHECK-NEXT: [[S:%.*]] = alloca i16, align 2
+// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4
+// CHECK-NEXT: [[L:%.*]] = alloca i64, align 8
+// CHECK-NEXT: [[F:%.*]] = alloca float, align 4
+// CHECK-NEXT: [[D:%.*]] = alloca double, align 8
+// CHECK-NEXT: [[A:%.*]] = alloca [[STRUCT_ANON:%.*]], align 4
+// CHECK-NEXT: [[V:%.*]] = alloca <4 x i32>, align 16
+// CHECK-NEXT: [[T:%.*]] = alloca [[STRUCT_ANON_0:%.*]], align 1
+// CHECK-NEXT: store i8 1, ptr [[C]], align 1
+// CHECK-NEXT: store i16 1, ptr [[S]], align 2
+// CHECK-NEXT: store i32 1, ptr [[I]], align 4
+// CHECK-NEXT: store i64 1, ptr [[L]], align 8
+// CHECK-NEXT: store float 1.000000e+00, ptr [[F]], align 4
+// CHECK-NEXT: store double 1.000000e+00, ptr [[D]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[C]], align 1
+// CHECK-NEXT: [[CONV:%.*]] = sext i8 [[TMP0]] to i32
+// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[S]], align 2
+// CHECK-NEXT: [[CONV1:%.*]] = sext i16 [[TMP1]] to i32
+// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[I]], align 4
+// CHECK-NEXT: [[TMP3:%.*]] = load i64, ptr [[L]], align 8
+// CHECK-NEXT: [[TMP4:%.*]] = load float, ptr [[F]], align 4
+// CHECK-NEXT: [[CONV2:%.*]] = fpext float [[TMP4]] to double
+// CHECK-NEXT: [[TMP5:%.*]] = load double, ptr [[D]], align 8
+// CHECK-NEXT: call spir_func void (i32, ...) @varargs_simple(i32 noundef 0, i32 noundef [[CONV]], i32 noundef [[CONV1]], i32 noundef [[TMP2]], i64 noundef [[TMP3]], double noundef [[CONV2]], double noundef [[TMP5]])
+// CHECK-NEXT: call void @llvm.memcpy.p0.p1.i64(ptr align 4 [[A]], ptr addrspace(1) align 4 @__const.foo.a, i64 12, i1 false)
+// CHECK-NEXT: call spir_func void (i32, ...) @varargs_simple(i32 noundef 0, ptr noundef byval([[STRUCT_ANON]]) align 4 [[A]])
+// CHECK-NEXT: store <4 x i32> splat (i32 1), ptr [[V]], align 16
+// CHECK-NEXT: [[TMP6:%.*]] = load <4 x i32>, ptr [[V]], align 16
+// CHECK-NEXT: call spir_func void (i32, ...) @varargs_simple(i32 noundef 0, <4 x i32> noundef [[TMP6]])
+// CHECK-NEXT: call spir_func void (i32, ...) @varargs_simple(i32 noundef 0, ptr noundef byval([[STRUCT_ANON_0]]) align 1 [[T]], ptr noundef byval([[STRUCT_ANON_0]]) align 1 [[T]], i32 noundef 0, ptr noundef byval([[STRUCT_ANON_0]]) align 1 [[T]])
+// CHECK-NEXT: ret void
+//
+void foo() {
+ char c = '\x1';
+ short s = 1;
+ int i = 1;
+ long l = 1;
+ float f = 1.f;
+ double d = 1.;
+ varargs_simple(0, c, s, i, l, f, d);
+
+ struct {int x; char c; int y;} a = {1, '\x1', 1};
+ varargs_simple(0, a);
+
+ typedef int __attribute__((ext_vector_type(4))) int4;
+ int4 v = {1, 1, 1, 1};
+ varargs_simple(0, v);
+
+ struct {char c, d;} t;
+ varargs_simple(0, t, t, 0, t);
+}
+
+typedef struct {long x; long y;} S;
+extern void varargs_complex(S, S, ...);
+
+// CHECK-LABEL: define spir_func void @bar(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8
+// CHECK-NEXT: call void @llvm.memcpy.p0.p1.i64(ptr align 8 [[S]], ptr addrspace(1) align 8 @__const.bar.s, i64 16, i1 false)
+// CHECK-NEXT: call spir_func void (ptr, ptr, ...) @varargs_complex(ptr noundef byval([[STRUCT_S]]) align 8 [[S]], ptr noundef byval([[STRUCT_S]]) align 8 [[S]], i32 noundef 1, i64 noundef 1, double noundef 1.000000e+00)
+// CHECK-NEXT: ret void
+//
+void bar() {
+ S s = {1l, 1l};
+ varargs_complex(s, s, 1, 1l, 1.0);
+}
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 9a2b0771e4dc0..adaf34b897ab3 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -1020,12 +1020,6 @@ SPIRVType *SPIRVGlobalRegistry::getOpTypeFunction(
const FunctionType *Ty, SPIRVType *RetType,
const SmallVectorImpl<SPIRVType *> &ArgTypes,
MachineIRBuilder &MIRBuilder) {
- if (Ty->isVarArg()) {
- Function &Fn = MIRBuilder.getMF().getFunction();
- Ty->getContext().diagnose(DiagnosticInfoUnsupported(
- Fn, "SPIR-V does not support variadic functions",
- MIRBuilder.getDebugLoc()));
- }
return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
auto MIB = MIRBuilder.buildInstr(SPIRV::OpTypeFunction)
.addDef(createTypeVReg(MIRBuilder))
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index 10038753f4a75..4769d6a92eba4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -33,6 +33,7 @@
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Target/TargetOptions.h"
+#include "llvm/Transforms/IPO/ExpandVariadics.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils.h"
#include <optional>
@@ -178,6 +179,7 @@ void SPIRVPassConfig::addIRPasses() {
addPass(createSPIRVRegularizerPass());
addPass(createSPIRVPrepareFunctionsPass(TM));
addPass(createSPIRVPrepareGlobalsPass());
+ addPass(createExpandVariadicsPass(ExpandVariadicsMode::Lowering));
}
void SPIRVPassConfig::addISelPrepare() {
diff --git a/llvm/lib/Transforms/IPO/ExpandVariadics.cpp b/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
index 4863d6ba789a8..6a92a0dca4d37 100644
--- a/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
+++ b/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
@@ -230,7 +230,8 @@ class ExpandVariadics : public ModulePass {
F->hasFnAttribute(Attribute::Naked))
return false;
- if (F->getCallingConv() != CallingConv::C)
+ if (F->getCallingConv() != CallingConv::C &&
+ F->getCallingConv() != CallingConv::SPIR_FUNC)
return false;
if (rewriteABI())
@@ -249,7 +250,8 @@ class ExpandVariadics : public ModulePass {
return false;
}
- if (CI->getCallingConv() != CallingConv::C)
+ if (CI->getCallingConv() != CallingConv::C &&
+ CI->getCallingConv() != CallingConv::SPIR_FUNC)
return false;
return true;
@@ -940,6 +942,33 @@ struct NVPTX final : public VariadicABIInfo {
}
};
+struct SPIRV final : public VariadicABIInfo {
+
+ bool enableForTarget() override { return true; }
+
+ bool vaListPassedInSSARegister() override { return true; }
+
+ Type *vaListType(LLVMContext &Ctx) override {
+ return PointerType::getUnqual(Ctx);
+ }
+
+ Type *vaListParameterType(Module &M) override {
+ return PointerType::getUnqual(M.getContext());
+ }
+
+ Value *initializeVaList(Module &M, LLVMContext &Ctx, IRBuilder<> &Builder,
+ AllocaInst *, Value *Buffer) override {
+ return Builder.CreateAddrSpaceCast(Buffer, vaListParameterType(M));
+ }
+
+ VAArgSlotInfo slotInfo(const DataLayout &DL, Type *Parameter) override {
+ // Expects natural alignment in all cases. The variadic call ABI will handle
+ // promoting types to their appropriate size and alignment.
+ Align A = DL.getABITypeAlign(Parameter);
+ return {A, false};
+ }
+};
+
struct Wasm final : public VariadicABIInfo {
bool enableForTarget() override {
@@ -995,6 +1024,11 @@ std::unique_ptr<VariadicABIInfo> VariadicABIInfo::create(const Triple &T) {
return std::make_unique<NVPTX>();
}
+ case Triple::spirv:
+ case Triple::spirv64: {
+ return std::make_unique<SPIRV>();
+ }
+
default:
return {};
}
diff --git a/llvm/test/CodeGen/SPIRV/function/variadics-lowering.ll b/llvm/test/CodeGen/SPIRV/function/variadics-lowering.ll
new file mode 100644
index 0000000000000..5ce4414c65ada
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/function/variadics-lowering.ll
@@ -0,0 +1,135 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -mtriple=spirv64-- --passes=expand-variadics --expand-variadics-override=lowering < %s | FileCheck %s
+
+%struct.S = type { i64, i64 }
+%struct.anon = type { i32, i8, i32 }
+%struct.anon.0 = type { i8, i8 }
+
+ at __const.foo.a = private unnamed_addr addrspace(1) constant { i32, i8, [3 x i8], i32 } { i32 1, i8 1, [3 x i8] zeroinitializer, i32 1 }, align 4
+ at __const.bar.s = private unnamed_addr addrspace(1) constant %struct.S { i64 1, i64 1 }, align 8
+
+define spir_func void @foo() {
+; CHECK-LABEL: define spir_func void @foo() {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = alloca i8, align 1
+; CHECK-NEXT: [[S:%.*]] = alloca i16, align 2
+; CHECK-NEXT: [[I:%.*]] = alloca i32, align 4
+; CHECK-NEXT: [[L:%.*]] = alloca i64, align 8
+; CHECK-NEXT: [[F:%.*]] = alloca float, align 4
+; CHECK-NEXT: [[D:%.*]] = alloca double, align 8
+; CHECK-NEXT: [[A:%.*]] = alloca [[STRUCT_ANON:%.*]], align 4
+; CHECK-NEXT: [[V:%.*]] = alloca <4 x i32>, align 16
+; CHECK-NEXT: [[T:%.*]] = alloca [[STRUCT_ANON_0:%.*]], align 1
+; CHECK-NEXT: [[VARARG_BUFFER:%.*]] = alloca [[FOO_VARARG:%.*]], align 4
+; CHECK-NEXT: [[VARARG_BUFFER1:%.*]] = alloca [[FOO_VARARG_0:%.*]], align 16
+; CHECK-NEXT: [[VARARG_BUFFER2:%.*]] = alloca [[FOO_VARARG_1:%.*]], align 4
+; CHECK-NEXT: [[VARARG_BUFFER3:%.*]] = alloca [[FOO_VARARG_2:%.*]], align 8
+; CHECK-NEXT: [[VARARG_BUFFER4:%.*]] = alloca [[FOO_VARARG_3:%.*]], align 8
+; CHECK-NEXT: store i8 1, ptr [[C]], align 1
+; CHECK-NEXT: store i16 1, ptr [[S]], align 2
+; CHECK-NEXT: store i32 1, ptr [[I]], align 4
+; CHECK-NEXT: store i64 1, ptr [[L]], align 8
+; CHECK-NEXT: store float 1.000000e+00, ptr [[F]], align 4
+; CHECK-NEXT: store double 1.000000e+00, ptr [[D]], align 8
+; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[C]], align 1
+; CHECK-NEXT: [[CONV:%.*]] = sext i8 [[TMP0]] to i32
+; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[S]], align 2
+; CHECK-NEXT: [[CONV1:%.*]] = sext i16 [[TMP1]] to i32
+; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[I]], align 4
+; CHECK-NEXT: [[TMP3:%.*]] = load i64, ptr [[L]], align 8
+; CHECK-NEXT: [[TMP4:%.*]] = load float, ptr [[F]], align 4
+; CHECK-NEXT: [[CONV2:%.*]] = fpext float [[TMP4]] to double
+; CHECK-NEXT: [[TMP5:%.*]] = load double, ptr [[D]], align 8
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VARARG_BUFFER3]])
+; CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw [[FOO_VARARG_2]], ptr [[VARARG_BUFFER3]], i32 0, i32 0
+; CHECK-NEXT: store i32 [[CONV]], ptr [[TMP13]], align 4
+; CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw [[FOO_VARARG_2]], ptr [[VARARG_BUFFER3]], i32 0, i32 1
+; CHECK-NEXT: store i32 [[CONV1]], ptr [[TMP7]], align 4
+; CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw [[FOO_VARARG_2]], ptr [[VARARG_BUFFER3]], i32 0, i32 2
+; CHECK-NEXT: store i32 [[TMP2]], ptr [[TMP8]], align 4
+; CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw [[FOO_VARARG_2]], ptr [[VARARG_BUFFER3]], i32 0, i32 4
+; CHECK-NEXT: store i64 [[TMP3]], ptr [[TMP9]], align 8
+; CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw [[FOO_VARARG_2]], ptr [[VARARG_BUFFER3]], i32 0, i32 5
+; CHECK-NEXT: store double [[CONV2]], ptr [[TMP10]], align 8
+; CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw [[FOO_VARARG_2]], ptr [[VARARG_BUFFER3]], i32 0, i32 6
+; CHECK-NEXT: store double [[TMP5]], ptr [[TMP11]], align 8
+; CHECK-NEXT: call spir_func void @varargs_simple(i32 0, ptr [[VARARG_BUFFER3]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VARARG_BUFFER3]])
+; CHECK-NEXT: call void @llvm.memcpy.p0.p1.i64(ptr align 4 [[A]], ptr addrspace(1) align 4 @__const.foo.a, i64 12, i1 false)
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VARARG_BUFFER2]])
+; CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw [[FOO_VARARG_1]], ptr [[VARARG_BUFFER2]], i32 0, i32 0
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[TMP12]], ptr [[A]], i64 12, i1 false)
+; CHECK-NEXT: call spir_func void @varargs_simple(i32 0, ptr [[VARARG_BUFFER2]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VARARG_BUFFER2]])
+; CHECK-NEXT: store <4 x i32> splat (i32 1), ptr [[V]], align 16
+; CHECK-NEXT: [[TMP6:%.*]] = load <4 x i32>, ptr [[V]], align 16
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VARARG_BUFFER1]])
+; CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw [[FOO_VARARG_0]], ptr [[VARARG_BUFFER1]], i32 0, i32 0
+; CHECK-NEXT: store <4 x i32> [[TMP6]], ptr [[TMP14]], align 16
+; CHECK-NEXT: call spir_func void @varargs_simple(i32 0, ptr [[VARARG_BUFFER1]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VARARG_BUFFER1]])
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VARARG_BUFFER]])
+; CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw [[FOO_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 0
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[TMP15]], ptr [[T]], i64 2, i1 false)
+; CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw [[FOO_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 1
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[TMP16]], ptr [[T]], i64 2, i1 false)
+; CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw [[FOO_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 2
+; CHECK-NEXT: store i32 0, ptr [[TMP17]], align 4
+; CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw [[FOO_VARARG]], ptr [[VARARG_BUFFER]], i32 0, i32 3
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[TMP18]], ptr [[T]], i64 2, i1 false)
+; CHECK-NEXT: call spir_func void @varargs_simple(i32 0, ptr [[VARARG_BUFFER]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VARARG_BUFFER]])
+; CHECK-NEXT: [[R:%.*]] = alloca [[STRUCT_S:%.*]], align 8
+; CHECK-NEXT: call void @llvm.memcpy.p0.p1.i64(ptr align 8 [[R]], ptr addrspace(1) align 8 @__const.bar.s, i64 16, i1 false)
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VARARG_BUFFER4]])
+; CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw [[FOO_VARARG_3]], ptr [[VARARG_BUFFER4]], i32 0, i32 0
+; CHECK-NEXT: store i32 1, ptr [[TMP19]], align 4
+; CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw [[FOO_VARARG_3]], ptr [[VARARG_BUFFER4]], i32 0, i32 2
+; CHECK-NEXT: store i64 1, ptr [[TMP20]], align 8
+; CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw [[FOO_VARARG_3]], ptr [[VARARG_BUFFER4]], i32 0, i32 3
+; CHECK-NEXT: store double 1.000000e+00, ptr [[TMP21]], align 8
+; CHECK-NEXT: call spir_func void @varargs_complex(ptr byval([[STRUCT_S]]) align 8 [[R]], ptr byval([[STRUCT_S]]) align 8 [[S]], ptr [[VARARG_BUFFER4]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VARARG_BUFFER4]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %c = alloca i8, align 1
+ %s = alloca i16, align 2
+ %i = alloca i32, align 4
+ %l = alloca i64, align 8
+ %f = alloca float, align 4
+ %d = alloca double, align 8
+ %a = alloca %struct.anon, align 4
+ %v = alloca <4 x i32>, align 16
+ %t = alloca %struct.anon.0, align 1
+ store i8 1, ptr %c, align 1
+ store i16 1, ptr %s, align 2
+ store i32 1, ptr %i, align 4
+ store i64 1, ptr %l, align 8
+ store float 1.000000e+00, ptr %f, align 4
+ store double 1.000000e+00, ptr %d, align 8
+ %0 = load i8, ptr %c, align 1
+ %conv = sext i8 %0 to i32
+ %1 = load i16, ptr %s, align 2
+ %conv1 = sext i16 %1 to i32
+ %2 = load i32, ptr %i, align 4
+ %3 = load i64, ptr %l, align 8
+ %4 = load float, ptr %f, align 4
+ %conv2 = fpext float %4 to double
+ %5 = load double, ptr %d, align 8
+ call spir_func void (i32, ...) @varargs_simple(i32 0, i32 %conv, i32 %conv1, i32 %2, i64 %3, double %conv2, double %5)
+ call void @llvm.memcpy.p0.p1.i64(ptr align 4 %a, ptr addrspace(1) align 4 @__const.foo.a, i64 12, i1 false)
+ call spir_func void (i32, ...) @varargs_simple(i32 0, ptr byval(%struct.anon) align 4 %a)
+ store <4 x i32> splat (i32 1), ptr %v, align 16
+ %6 = load <4 x i32>, ptr %v, align 16
+ call spir_func void (i32, ...) @varargs_simple(i32 0, <4 x i32> %6)
+ call spir_func void (i32, ...) @varargs_simple(i32 0, ptr byval(%struct.anon.0) align 1 %t, ptr byval(%struct.anon.0) align 1 %t, i32 0, ptr byval(%struct.anon.0) align 1 %t)
+ %r = alloca %struct.S, align 8
+ call void @llvm.memcpy.p0.p1.i64(ptr align 8 %r, ptr addrspace(1) align 8 @__const.bar.s, i64 16, i1 false)
+ call spir_func void (ptr, ptr, ...) @varargs_complex(ptr byval(%struct.S) align 8 %r, ptr byval(%struct.S) align 8 %s, i32 1, i64 1, double 1.000000e+00)
+ ret void
+}
+
+declare spir_func void @varargs_simple(i32 noundef, ...)
+
+declare spir_func void @varargs_complex(ptr byval(%struct.S) align 8, ptr byval(%struct.S) align 8, ...)
More information about the llvm-commits
mailing list