Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions clang/lib/CodeGen/Targets/SPIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());

Expand All @@ -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;
Expand Down
25 changes: 15 additions & 10 deletions clang/test/CodeGen/varargs-with-nonzero-default-address-space.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
// RUN: %clang_cc1 -triple spirv64-unknown-unknown -fcuda-is-device -emit-llvm -o - %s | FileCheck %s

struct x {
Expand All @@ -8,32 +8,37 @@ struct x {

// CHECK-LABEL: define spir_func void @testva(
// CHECK-SAME: i32 noundef [[N:%.*]], ...) #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[N_ADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[AP:%.*]] = alloca ptr addrspace(4), align 8
// CHECK-NEXT: [[T:%.*]] = alloca [[STRUCT_X:%.*]], align 8
// CHECK-NEXT: [[AP2:%.*]] = alloca ptr addrspace(4), align 8
// CHECK-NEXT: [[V:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[VARET:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[N_ADDR_ASCAST:%.*]] = addrspacecast ptr [[N_ADDR]] to ptr addrspace(4)
// CHECK-NEXT: [[AP_ASCAST:%.*]] = addrspacecast ptr [[AP]] to ptr addrspace(4)
// CHECK-NEXT: [[T_ASCAST:%.*]] = addrspacecast ptr [[T]] to ptr addrspace(4)
// CHECK-NEXT: [[AP2_ASCAST:%.*]] = addrspacecast ptr [[AP2]] to ptr addrspace(4)
// CHECK-NEXT: [[V_ASCAST:%.*]] = addrspacecast ptr [[V]] to ptr addrspace(4)
// CHECK-NEXT: [[VARET_ASCAST:%.*]] = addrspacecast ptr [[VARET]] to ptr addrspace(4)
// CHECK-NEXT: store i32 [[N]], ptr addrspace(4) [[N_ADDR_ASCAST]], align 4
// CHECK-NEXT: call void @llvm.va_start.p4(ptr addrspace(4) [[AP_ASCAST]])
// CHECK-NEXT: [[TMP0:%.*]] = va_arg ptr addrspace(4) [[AP_ASCAST]], ptr
// CHECK-NEXT: call void @llvm.memcpy.p4.p0.i64(ptr addrspace(4) align 8 [[T_ASCAST]], ptr align 8 [[TMP0]], i64 16, i1 false)
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr addrspace(4), ptr addrspace(4) [[AP_ASCAST]], align 8
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ARGP_CUR]], i32 7
// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr addrspace(4) @llvm.ptrmask.p4.i64(ptr addrspace(4) [[TMP0]], i64 -8)
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ARGP_CUR_ALIGNED]], i64 16
// CHECK-NEXT: store ptr addrspace(4) [[ARGP_NEXT]], ptr addrspace(4) [[AP_ASCAST]], align 8
// CHECK-NEXT: call void @llvm.memcpy.p4.p4.i64(ptr addrspace(4) align 8 [[T_ASCAST]], ptr addrspace(4) align 8 [[ARGP_CUR_ALIGNED]], i64 16, i1 false)
// CHECK-NEXT: call void @llvm.va_copy.p4(ptr addrspace(4) [[AP2_ASCAST]], ptr addrspace(4) [[AP_ASCAST]])
// CHECK-NEXT: [[TMP1:%.*]] = va_arg ptr addrspace(4) [[AP2_ASCAST]], i32
// CHECK-NEXT: store i32 [[TMP1]], ptr addrspace(4) [[VARET_ASCAST]], align 4
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr addrspace(4) [[VARET_ASCAST]], align 4
// CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr addrspace(4), ptr addrspace(4) [[AP2_ASCAST]], align 8
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ARGP_CUR1]], i32 3
// CHECK-NEXT: [[ARGP_CUR1_ALIGNED:%.*]] = call ptr addrspace(4) @llvm.ptrmask.p4.i64(ptr addrspace(4) [[TMP1]], i64 -4)
// CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ARGP_CUR1_ALIGNED]], i64 4
// CHECK-NEXT: store ptr addrspace(4) [[ARGP_NEXT2]], ptr addrspace(4) [[AP2_ASCAST]], align 8
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr addrspace(4) [[ARGP_CUR1_ALIGNED]], align 4
// CHECK-NEXT: store i32 [[TMP2]], ptr addrspace(4) [[V_ASCAST]], align 4
// CHECK-NEXT: call void @llvm.va_end.p4(ptr addrspace(4) [[AP2_ASCAST]])
// CHECK-NEXT: call void @llvm.va_end.p4(ptr addrspace(4) [[AP_ASCAST]])
// CHECK-NEXT: ret void

//
void testva(int n, ...) {
__builtin_va_list ap;
__builtin_va_start(ap, n);
Expand Down
76 changes: 76 additions & 0 deletions clang/test/CodeGenSPIRV/Builtins/variadic.c
Original file line number Diff line number Diff line change
@@ -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);
}
1 change: 1 addition & 0 deletions llvm/lib/Target/SPIRV/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ add_llvm_target(SPIRVCodeGen
Demangle
GlobalISel
FrontendHLSL
IPO
MC
SPIRVAnalysis
SPIRVDesc
Expand Down
6 changes: 4 additions & 2 deletions llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1020,10 +1020,12 @@ SPIRVType *SPIRVGlobalRegistry::getOpTypeFunction(
const FunctionType *Ty, SPIRVType *RetType,
const SmallVectorImpl<SPIRVType *> &ArgTypes,
MachineIRBuilder &MIRBuilder) {
if (Ty->isVarArg()) {
const SPIRVSubtarget *ST =
static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
if (Ty->isVarArg() && ST->isShader()) {
Function &Fn = MIRBuilder.getMF().getFunction();
Ty->getContext().diagnose(DiagnosticInfoUnsupported(
Fn, "SPIR-V does not support variadic functions",
Fn, "SPIR-V shaders do not support variadic functions",
MIRBuilder.getDebugLoc()));
}
return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -178,6 +179,11 @@ void SPIRVPassConfig::addIRPasses() {
addPass(createSPIRVRegularizerPass());
addPass(createSPIRVPrepareFunctionsPass(TM));
addPass(createSPIRVPrepareGlobalsPass());

// Variadic function calls aren't supported in shader code.
if (!TM.getSubtargetImpl()->isShader()) {
addPass(createExpandVariadicsPass(ExpandVariadicsMode::Lowering));
}
}

void SPIRVPassConfig::addISelPrepare() {
Expand Down
56 changes: 54 additions & 2 deletions llvm/lib/Transforms/IPO/ExpandVariadics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ class VariadicABIInfo {
};
virtual VAArgSlotInfo slotInfo(const DataLayout &DL, Type *Parameter) = 0;

// Per-target overrides of special symbols.
virtual bool ignoreFunction(Function *F) { return false; }

// Targets implemented so far all have the same trivial lowering for these
bool vaEndIsNop() { return true; }
bool vaCopyIsMemcpy() { return true; }
Expand Down Expand Up @@ -153,6 +156,11 @@ class ExpandVariadics : public ModulePass {

bool rewriteABI() { return Mode == ExpandVariadicsMode::Lowering; }

template <typename T> bool isValidCallingConv(T *F) {
return F->getCallingConv() == CallingConv::C ||
F->getCallingConv() == CallingConv::SPIR_FUNC;
}

bool runOnModule(Module &M) override;

bool runOnFunction(Module &M, IRBuilder<> &Builder, Function *F);
Expand Down Expand Up @@ -230,7 +238,10 @@ class ExpandVariadics : public ModulePass {
F->hasFnAttribute(Attribute::Naked))
return false;

if (F->getCallingConv() != CallingConv::C)
if (ABI->ignoreFunction(F))
return false;

if (!isValidCallingConv(F))
return false;

if (rewriteABI())
Expand All @@ -249,7 +260,7 @@ class ExpandVariadics : public ModulePass {
return false;
}

if (CI->getCallingConv() != CallingConv::C)
if (!isValidCallingConv(CI))
return false;

return true;
Expand Down Expand Up @@ -609,6 +620,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");
Expand Down Expand Up @@ -940,6 +954,39 @@ 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};
}

// The SPIR-V backend has special handling for SPIR-V mangled printf
// functions.
bool ignoreFunction(Function *F) override {
return F->getName().starts_with('_') && F->getName().contains("printf");
}
};

struct Wasm final : public VariadicABIInfo {

bool enableForTarget() override {
Expand Down Expand Up @@ -995,6 +1042,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 {};
}
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/CodeGen/SPIRV/function/vararg.ll
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_function_pointers < %s 2>&1 | FileCheck %s
; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-vulkan --spirv-ext=+SPV_INTEL_function_pointers < %s 2>&1 | FileCheck %s

define void @bar() {
entry:
call spir_func void (i32, ...) @_Z3fooiz(i32 5, i32 3)
ret void
}

; CHECK:error: {{.*}} in function bar void (): SPIR-V does not support variadic functions
; CHECK:error: {{.*}} in function bar void (): SPIR-V shaders do not support variadic functions
declare spir_func void @_Z3fooiz(i32, ...)
Loading