Skip to content

Commit ab92127

Browse files
seven-milesitio-couto
authored andcommitted
[CIR][CodeGen] Basic skeleton of SPIRV64 target support (#671)
* SPIRV64 TargetInfo * Calling convention `SPIR_KERNEL` * Minimal ABI with Direct/Extend for arguments and Ignore for return --------- Co-authored-by: Vinicius Couto Espindola <[email protected]>
1 parent 7e367e4 commit ab92127

File tree

8 files changed

+214
-13
lines changed

8 files changed

+214
-13
lines changed

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ CIRGenFunction::AutoVarEmission
3636
CIRGenFunction::buildAutoVarAlloca(const VarDecl &D,
3737
mlir::OpBuilder::InsertPoint ip) {
3838
QualType Ty = D.getType();
39-
// TODO: (|| Ty.getAddressSpace() == LangAS::opencl_private &&
40-
// getLangOpts().OpenCL))
4139
assert(!MissingFeatures::openCL());
42-
assert(Ty.getAddressSpace() == LangAS::Default);
40+
assert(
41+
Ty.getAddressSpace() == LangAS::Default ||
42+
(Ty.getAddressSpace() == LangAS::opencl_private && getLangOpts().OpenCL));
4343
assert(!D.hasAttr<AnnotateAttr>() && "not implemented");
4444

4545
auto loc = getLoc(D.getSourceRange());

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,8 @@ void CIRGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
993993
llvm_unreachable("NYI");
994994

995995
if (FD && getLangOpts().OpenCL) {
996-
llvm_unreachable("NYI");
996+
// TODO(cir): Emit OpenCL kernel metadata
997+
assert(!MissingFeatures::openCL());
997998
}
998999

9991000
// If we are checking function types, emit a function type signature as

clang/lib/CIR/CodeGen/CIRGenTypes.cpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,14 @@ using namespace clang;
2525
using namespace cir;
2626

2727
unsigned CIRGenTypes::ClangCallConvToCIRCallConv(clang::CallingConv CC) {
28-
assert(CC == CC_C && "No other calling conventions implemented.");
29-
return cir::CallingConv::C;
28+
switch (CC) {
29+
case CC_C:
30+
return cir::CallingConv::C;
31+
case CC_OpenCLKernel:
32+
return CGM.getTargetCIRGenInfo().getOpenCLKernelCallingConv();
33+
default:
34+
llvm_unreachable("No other calling conventions implemented.");
35+
}
3036
}
3137

3238
CIRGenTypes::CIRGenTypes(CIRGenModule &cgm)

clang/lib/CIR/CodeGen/CallingConv.h

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ enum {
3434
/// with typical C calling conventions, the callee/caller have to tolerate
3535
/// certain amounts of prototype mismatch.
3636
C = 0,
37+
38+
/// Used for SPIR kernel functions. Inherits the restrictions of SPIR_FUNC,
39+
/// except it cannot have non-void return values, it cannot have variable
40+
/// arguments, it can also be called by the host or it is externally
41+
/// visible.
42+
SPIR_KERNEL = 76,
3743
};
3844

3945
} // namespace CallingConv

clang/lib/CIR/CodeGen/TargetInfo.cpp

+126-7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,68 @@ static bool testIfIsVoidTy(QualType Ty) {
2020
return k == BuiltinType::Void;
2121
}
2222

23+
static bool isAggregateTypeForABI(QualType T) {
24+
return !CIRGenFunction::hasScalarEvaluationKind(T) ||
25+
T->isMemberFunctionPointerType();
26+
}
27+
28+
/// Pass transparent unions as if they were the type of the first element. Sema
29+
/// should ensure that all elements of the union have the same "machine type".
30+
static QualType useFirstFieldIfTransparentUnion(QualType Ty) {
31+
assert(!Ty->getAsUnionType() && "NYI");
32+
return Ty;
33+
}
34+
35+
namespace {
36+
37+
/// The default implementation for ABI specific
38+
/// details. This implementation provides information which results in
39+
/// self-consistent and sensible LLVM IR generation, but does not
40+
/// conform to any particular ABI.
41+
class DefaultABIInfo : public ABIInfo {
42+
public:
43+
DefaultABIInfo(CIRGenTypes &CGT) : ABIInfo(CGT) {}
44+
45+
virtual ~DefaultABIInfo() = default;
46+
47+
ABIArgInfo classifyReturnType(QualType RetTy) const {
48+
if (RetTy->isVoidType())
49+
return ABIArgInfo::getIgnore();
50+
51+
llvm_unreachable("Non-void return type NYI");
52+
}
53+
54+
ABIArgInfo classifyArgumentType(QualType Ty) const {
55+
Ty = useFirstFieldIfTransparentUnion(Ty);
56+
57+
if (isAggregateTypeForABI(Ty)) {
58+
llvm_unreachable("NYI");
59+
}
60+
61+
// Treat an enum type as its underlying type.
62+
if (const EnumType *EnumTy = Ty->getAs<EnumType>())
63+
llvm_unreachable("NYI");
64+
65+
ASTContext &Context = getContext();
66+
if (const auto *EIT = Ty->getAs<BitIntType>())
67+
llvm_unreachable("NYI");
68+
69+
if (isPromotableIntegerTypeForABI(Ty)) {
70+
llvm_unreachable("ArgInfo integer extend NYI");
71+
} else {
72+
return ABIArgInfo::getDirect();
73+
}
74+
}
75+
76+
void computeInfo(CIRGenFunctionInfo &FI) const override {
77+
if (!getCXXABI().classifyReturnType(FI))
78+
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
79+
for (auto &I : FI.arguments())
80+
I.info = classifyArgumentType(I.type);
81+
}
82+
};
83+
} // namespace
84+
2385
//===----------------------------------------------------------------------===//
2486
// AArch64 ABI Implementation
2587
//===----------------------------------------------------------------------===//
@@ -151,6 +213,66 @@ class X86_64TargetCIRGenInfo : public TargetCIRGenInfo {
151213
};
152214
} // namespace
153215

216+
//===----------------------------------------------------------------------===//
217+
// Base ABI and target codegen info implementation common between SPIR and
218+
// SPIR-V.
219+
//===----------------------------------------------------------------------===//
220+
221+
namespace {
222+
class CommonSPIRABIInfo : public DefaultABIInfo {
223+
public:
224+
CommonSPIRABIInfo(CIRGenTypes &CGT) : DefaultABIInfo(CGT) {}
225+
};
226+
227+
class SPIRVABIInfo : public CommonSPIRABIInfo {
228+
public:
229+
SPIRVABIInfo(CIRGenTypes &CGT) : CommonSPIRABIInfo(CGT) {}
230+
void computeInfo(CIRGenFunctionInfo &FI) const override {
231+
// The logic is same as in DefaultABIInfo with an exception on the kernel
232+
// arguments handling.
233+
llvm::CallingConv::ID CC = FI.getCallingConvention();
234+
235+
bool cxxabiHit = getCXXABI().classifyReturnType(FI);
236+
assert(!cxxabiHit && "C++ ABI not considered");
237+
238+
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
239+
240+
for (auto &I : FI.arguments()) {
241+
if (CC == llvm::CallingConv::SPIR_KERNEL) {
242+
I.info = classifyKernelArgumentType(I.type);
243+
} else {
244+
I.info = classifyArgumentType(I.type);
245+
}
246+
}
247+
}
248+
249+
private:
250+
ABIArgInfo classifyKernelArgumentType(QualType Ty) const {
251+
assert(!getContext().getLangOpts().CUDAIsDevice && "NYI");
252+
return classifyArgumentType(Ty);
253+
}
254+
};
255+
} // namespace
256+
namespace {
257+
258+
class CommonSPIRTargetCIRGenInfo : public TargetCIRGenInfo {
259+
public:
260+
CommonSPIRTargetCIRGenInfo(std::unique_ptr<ABIInfo> ABIInfo)
261+
: TargetCIRGenInfo(std::move(ABIInfo)) {}
262+
263+
unsigned getOpenCLKernelCallingConv() const override {
264+
return llvm::CallingConv::SPIR_KERNEL;
265+
}
266+
};
267+
268+
class SPIRVTargetCIRGenInfo : public CommonSPIRTargetCIRGenInfo {
269+
public:
270+
SPIRVTargetCIRGenInfo(CIRGenTypes &CGT)
271+
: CommonSPIRTargetCIRGenInfo(std::make_unique<SPIRVABIInfo>(CGT)) {}
272+
};
273+
274+
} // namespace
275+
154276
// TODO(cir): remove the attribute once this gets used.
155277
LLVM_ATTRIBUTE_UNUSED
156278
static bool classifyReturnType(const CIRGenCXXABI &CXXABI,
@@ -189,13 +311,6 @@ void X86_64ABIInfo::computeInfo(CIRGenFunctionInfo &FI) const {
189311
FI.getReturnInfo() = ABIArgInfo::getDirect(CGT.ConvertType(RetTy));
190312
}
191313

192-
/// Pass transparent unions as if they were the type of the first element. Sema
193-
/// should ensure that all elements of the union have the same "machine type".
194-
static QualType useFirstFieldIfTransparentUnion(QualType Ty) {
195-
assert(!Ty->getAsUnionType() && "NYI");
196-
return Ty;
197-
}
198-
199314
/// GetINTEGERTypeAtOffset - The ABI specifies that a value should be passed in
200315
/// an 8-byte GPR. This means that we either have a scalar or we are talking
201316
/// about the high or low part of an up-to-16-byte struct. This routine picks
@@ -459,5 +574,9 @@ const TargetCIRGenInfo &CIRGenModule::getTargetCIRGenInfo() {
459574
return SetCIRGenInfo(new X86_64TargetCIRGenInfo(genTypes, AVXLevel));
460575
}
461576
}
577+
578+
case llvm::Triple::spirv64: {
579+
return SetCIRGenInfo(new SPIRVTargetCIRGenInfo(genTypes));
580+
}
462581
}
463582
}

clang/lib/CIR/CodeGen/TargetInfo.h

+15
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,21 @@ class TargetCIRGenInfo {
7878
mlir::Type DestTy,
7979
bool IsNonNull = false) const;
8080

81+
/// Get LLVM calling convention for OpenCL kernel.
82+
virtual unsigned getOpenCLKernelCallingConv() const {
83+
// OpenCL kernels are called via an explicit runtime API with arguments
84+
// set with clSetKernelArg(), not as normal sub-functions.
85+
// Return SPIR_KERNEL by default as the kernel calling convention to
86+
// ensure the fingerprint is fixed such way that each OpenCL argument
87+
// gets one matching argument in the produced kernel function argument
88+
// list to enable feasible implementation of clSetKernelArg() with
89+
// aggregates etc. In case we would use the default C calling conv here,
90+
// clSetKernelArg() might break depending on the target-specific
91+
// conventions; different targets might split structs passed as values
92+
// to multiple function arguments etc.
93+
return llvm::CallingConv::SPIR_KERNEL;
94+
}
95+
8196
virtual ~TargetCIRGenInfo() {}
8297
};
8398

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %clang_cc1 -cl-std=CL3.0 -O0 -fclangir -emit-cir -triple spirv64-unknown-unknown %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
3+
// RUN: %clang_cc1 -cl-std=CL3.0 -O0 -fclangir -emit-llvm -triple spirv64-unknown-unknown %s -o %t.ll
4+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
5+
6+
// CIR: cir.func @func(%arg0: !cir.ptr<!s32i, addrspace(3)>
7+
// LLVM: @func(ptr addrspace(3)
8+
kernel void func(local int *p) {
9+
// CIR-NEXT: %[[#ALLOCA_P:]] = cir.alloca !cir.ptr<!s32i, addrspace(3)>, !cir.ptr<!cir.ptr<!s32i, addrspace(3)>>, ["p", init] {alignment = 8 : i64}
10+
// LLVM-NEXT: %[[#ALLOCA_P:]] = alloca ptr addrspace(3), i64 1, align 8
11+
12+
int x;
13+
// CIR-NEXT: %[[#ALLOCA_X:]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x"] {alignment = 4 : i64}
14+
// LLVM-NEXT: %[[#ALLOCA_X:]] = alloca i32, i64 1, align 4
15+
16+
global char *b;
17+
// CIR-NEXT: %[[#ALLOCA_B:]] = cir.alloca !cir.ptr<!s8i, addrspace(1)>, !cir.ptr<!cir.ptr<!s8i, addrspace(1)>>, ["b"] {alignment = 8 : i64}
18+
// LLVM-NEXT: %[[#ALLOCA_B:]] = alloca ptr addrspace(1), i64 1, align 8
19+
20+
// Store of the argument `p`
21+
// CIR-NEXT: cir.store %arg0, %[[#ALLOCA_P]] : !cir.ptr<!s32i, addrspace(3)>, !cir.ptr<!cir.ptr<!s32i, addrspace(3)>>
22+
// LLVM-NEXT: store ptr addrspace(3) %{{[0-9]+}}, ptr %[[#ALLOCA_P]], align 8
23+
24+
return;
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// See also: clang/test/CodeGenOpenCL/spirv_target.cl
2+
// RUN: %clang_cc1 -cl-std=CL3.0 -fclangir -emit-cir -triple spirv64-unknown-unknown %s -o %t_64.cir
3+
// RUN: FileCheck --input-file=%t_64.cir %s --check-prefix=CIR-SPIRV64
4+
// RUN: %clang_cc1 -cl-std=CL3.0 -fclangir -emit-llvm -triple spirv64-unknown-unknown %s -o %t_64.ll
5+
// RUN: FileCheck --input-file=%t_64.ll %s --check-prefix=LLVM-SPIRV64
6+
7+
// CIR-SPIRV64: cir.triple = "spirv64-unknown-unknown"
8+
// LLVM-SPIRV64: target triple = "spirv64-unknown-unknown"
9+
10+
typedef struct {
11+
char c;
12+
void *v;
13+
void *v2;
14+
} my_st;
15+
16+
// CIR-SPIRV64: cir.func @func(
17+
// LLVM-SPIRV64: @func(
18+
kernel void func(global long *arg) {
19+
int res1[sizeof(my_st) == 24 ? 1 : -1]; // expected-no-diagnostics
20+
int res2[sizeof(void *) == 8 ? 1 : -1]; // expected-no-diagnostics
21+
int res3[sizeof(arg) == 8 ? 1 : -1]; // expected-no-diagnostics
22+
23+
my_st *tmp = 0;
24+
25+
// LLVM-SPIRV64: store i64 8, ptr addrspace(1)
26+
arg[0] = (long)(&tmp->v);
27+
// LLVM-SPIRV64: store i64 16, ptr addrspace(1)
28+
arg[1] = (long)(&tmp->v2);
29+
}

0 commit comments

Comments
 (0)