Skip to content

[CIR][CodeGen] Basic skeleton of SPIRV64 target support #671

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 20, 2024
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
6 changes: 3 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ CIRGenFunction::AutoVarEmission
CIRGenFunction::buildAutoVarAlloca(const VarDecl &D,
mlir::OpBuilder::InsertPoint ip) {
QualType Ty = D.getType();
// TODO: (|| Ty.getAddressSpace() == LangAS::opencl_private &&
// getLangOpts().OpenCL))
assert(!MissingFeatures::openCL());
assert(Ty.getAddressSpace() == LangAS::Default);
assert(
Ty.getAddressSpace() == LangAS::Default ||
(Ty.getAddressSpace() == LangAS::opencl_private && getLangOpts().OpenCL));
assert(!D.hasAttr<AnnotateAttr>() && "not implemented");

auto loc = getLoc(D.getSourceRange());
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,8 @@ void CIRGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
llvm_unreachable("NYI");

if (FD && getLangOpts().OpenCL) {
llvm_unreachable("NYI");
// TODO(cir): Emit OpenCL kernel metadata
assert(!MissingFeatures::openCL());
}

// If we are checking function types, emit a function type signature as
Expand Down
10 changes: 8 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,14 @@ using namespace clang;
using namespace cir;

unsigned CIRGenTypes::ClangCallConvToCIRCallConv(clang::CallingConv CC) {
assert(CC == CC_C && "No other calling conventions implemented.");
return cir::CallingConv::C;
switch (CC) {
case CC_C:
return cir::CallingConv::C;
case CC_OpenCLKernel:
return CGM.getTargetCIRGenInfo().getOpenCLKernelCallingConv();
default:
llvm_unreachable("No other calling conventions implemented.");
}
}

CIRGenTypes::CIRGenTypes(CIRGenModule &cgm)
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/CallingConv.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ enum {
/// with typical C calling conventions, the callee/caller have to tolerate
/// certain amounts of prototype mismatch.
C = 0,

/// Used for SPIR kernel functions. Inherits the restrictions of SPIR_FUNC,
/// except it cannot have non-void return values, it cannot have variable
/// arguments, it can also be called by the host or it is externally
/// visible.
SPIR_KERNEL = 76,
};

} // namespace CallingConv
Expand Down
133 changes: 126 additions & 7 deletions clang/lib/CIR/CodeGen/TargetInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,68 @@ static bool testIfIsVoidTy(QualType Ty) {
return k == BuiltinType::Void;
}

static bool isAggregateTypeForABI(QualType T) {
return !CIRGenFunction::hasScalarEvaluationKind(T) ||
T->isMemberFunctionPointerType();
}

/// Pass transparent unions as if they were the type of the first element. Sema
/// should ensure that all elements of the union have the same "machine type".
static QualType useFirstFieldIfTransparentUnion(QualType Ty) {
assert(!Ty->getAsUnionType() && "NYI");
return Ty;
}

namespace {

/// The default implementation for ABI specific
/// details. This implementation provides information which results in
/// self-consistent and sensible LLVM IR generation, but does not
/// conform to any particular ABI.
class DefaultABIInfo : public ABIInfo {
public:
DefaultABIInfo(CIRGenTypes &CGT) : ABIInfo(CGT) {}

virtual ~DefaultABIInfo() = default;

ABIArgInfo classifyReturnType(QualType RetTy) const {
if (RetTy->isVoidType())
return ABIArgInfo::getIgnore();

llvm_unreachable("Non-void return type NYI");
}

ABIArgInfo classifyArgumentType(QualType Ty) const {
Ty = useFirstFieldIfTransparentUnion(Ty);

if (isAggregateTypeForABI(Ty)) {
llvm_unreachable("NYI");
}

// Treat an enum type as its underlying type.
if (const EnumType *EnumTy = Ty->getAs<EnumType>())
llvm_unreachable("NYI");

ASTContext &Context = getContext();
if (const auto *EIT = Ty->getAs<BitIntType>())
llvm_unreachable("NYI");

if (isPromotableIntegerTypeForABI(Ty)) {
llvm_unreachable("ArgInfo integer extend NYI");
} else {
return ABIArgInfo::getDirect();
}
}

void computeInfo(CIRGenFunctionInfo &FI) const override {
if (!getCXXABI().classifyReturnType(FI))
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
for (auto &I : FI.arguments())
I.info = classifyArgumentType(I.type);
}
};
} // namespace

//===----------------------------------------------------------------------===//
// AArch64 ABI Implementation
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -159,6 +221,66 @@ class X86_64TargetCIRGenInfo : public TargetCIRGenInfo {
};
} // namespace

//===----------------------------------------------------------------------===//
// Base ABI and target codegen info implementation common between SPIR and
// SPIR-V.
//===----------------------------------------------------------------------===//

namespace {
class CommonSPIRABIInfo : public DefaultABIInfo {
public:
CommonSPIRABIInfo(CIRGenTypes &CGT) : DefaultABIInfo(CGT) {}
};

class SPIRVABIInfo : public CommonSPIRABIInfo {
public:
SPIRVABIInfo(CIRGenTypes &CGT) : CommonSPIRABIInfo(CGT) {}
void computeInfo(CIRGenFunctionInfo &FI) const override {
// The logic is same as in DefaultABIInfo with an exception on the kernel
// arguments handling.
llvm::CallingConv::ID CC = FI.getCallingConvention();

bool cxxabiHit = getCXXABI().classifyReturnType(FI);
assert(!cxxabiHit && "C++ ABI not considered");

FI.getReturnInfo() = classifyReturnType(FI.getReturnType());

for (auto &I : FI.arguments()) {
if (CC == llvm::CallingConv::SPIR_KERNEL) {
I.info = classifyKernelArgumentType(I.type);
} else {
I.info = classifyArgumentType(I.type);
}
}
}

private:
ABIArgInfo classifyKernelArgumentType(QualType Ty) const {
assert(!getContext().getLangOpts().CUDAIsDevice && "NYI");
return classifyArgumentType(Ty);
}
};
} // namespace
namespace {

class CommonSPIRTargetCIRGenInfo : public TargetCIRGenInfo {
public:
CommonSPIRTargetCIRGenInfo(std::unique_ptr<ABIInfo> ABIInfo)
: TargetCIRGenInfo(std::move(ABIInfo)) {}

unsigned getOpenCLKernelCallingConv() const override {
return llvm::CallingConv::SPIR_KERNEL;
}
};

class SPIRVTargetCIRGenInfo : public CommonSPIRTargetCIRGenInfo {
public:
SPIRVTargetCIRGenInfo(CIRGenTypes &CGT)
: CommonSPIRTargetCIRGenInfo(std::make_unique<SPIRVABIInfo>(CGT)) {}
};

} // namespace

// TODO(cir): remove the attribute once this gets used.
LLVM_ATTRIBUTE_UNUSED
static bool classifyReturnType(const CIRGenCXXABI &CXXABI,
Expand Down Expand Up @@ -197,13 +319,6 @@ void X86_64ABIInfo::computeInfo(CIRGenFunctionInfo &FI) const {
FI.getReturnInfo() = ABIArgInfo::getDirect(CGT.ConvertType(RetTy));
}

/// Pass transparent unions as if they were the type of the first element. Sema
/// should ensure that all elements of the union have the same "machine type".
static QualType useFirstFieldIfTransparentUnion(QualType Ty) {
assert(!Ty->getAsUnionType() && "NYI");
return Ty;
}

/// GetINTEGERTypeAtOffset - The ABI specifies that a value should be passed in
/// an 8-byte GPR. This means that we either have a scalar or we are talking
/// about the high or low part of an up-to-16-byte struct. This routine picks
Expand Down Expand Up @@ -467,5 +582,9 @@ const TargetCIRGenInfo &CIRGenModule::getTargetCIRGenInfo() {
return SetCIRGenInfo(new X86_64TargetCIRGenInfo(genTypes, AVXLevel));
}
}

case llvm::Triple::spirv64: {
return SetCIRGenInfo(new SPIRVTargetCIRGenInfo(genTypes));
}
}
}
15 changes: 15 additions & 0 deletions clang/lib/CIR/CodeGen/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,21 @@ class TargetCIRGenInfo {
mlir::Type DestTy,
bool IsNonNull = false) const;

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

virtual ~TargetCIRGenInfo() {}
};

Expand Down
25 changes: 25 additions & 0 deletions clang/test/CIR/CodeGen/OpenCL/addrspace-alloca.cl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// RUN: %clang_cc1 -cl-std=CL3.0 -O0 -fclangir -emit-cir -triple spirv64-unknown-unknown %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
// RUN: %clang_cc1 -cl-std=CL3.0 -O0 -fclangir -S -emit-llvm -triple spirv64-unknown-unknown %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM

// CIR: cir.func @func(%arg0: !cir.ptr<!s32i, addrspace(3)>
// LLVM: @func(ptr addrspace(3)
kernel void func(local int *p) {
// CIR-NEXT: %[[#ALLOCA_P:]] = cir.alloca !cir.ptr<!s32i, addrspace(3)>, !cir.ptr<!cir.ptr<!s32i, addrspace(3)>>, ["p", init] {alignment = 8 : i64}
// LLVM-NEXT: %[[#ALLOCA_P:]] = alloca ptr addrspace(3), i64 1, align 8

int x;
// CIR-NEXT: %[[#ALLOCA_X:]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x"] {alignment = 4 : i64}
// LLVM-NEXT: %[[#ALLOCA_X:]] = alloca i32, i64 1, align 4

global char *b;
// CIR-NEXT: %[[#ALLOCA_B:]] = cir.alloca !cir.ptr<!s8i, addrspace(1)>, !cir.ptr<!cir.ptr<!s8i, addrspace(1)>>, ["b"] {alignment = 8 : i64}
// LLVM-NEXT: %[[#ALLOCA_B:]] = alloca ptr addrspace(1), i64 1, align 8

// Store of the argument `p`
// CIR-NEXT: cir.store %arg0, %[[#ALLOCA_P]] : !cir.ptr<!s32i, addrspace(3)>, !cir.ptr<!cir.ptr<!s32i, addrspace(3)>>
// LLVM-NEXT: store ptr addrspace(3) %{{[0-9]+}}, ptr %[[#ALLOCA_P]], align 8

return;
}
29 changes: 29 additions & 0 deletions clang/test/CIR/CodeGen/OpenCL/spirv-target.cl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// See also: clang/test/CodeGenOpenCL/spirv_target.cl
// RUN: %clang_cc1 -cl-std=CL3.0 -fclangir -emit-cir -triple spirv64-unknown-unknown %s -o %t_64.cir
// RUN: FileCheck --input-file=%t_64.cir %s --check-prefix=CIR-SPIRV64
// RUN: %clang_cc1 -cl-std=CL3.0 -fclangir -S -emit-llvm -triple spirv64-unknown-unknown %s -o %t_64.ll
// RUN: FileCheck --input-file=%t_64.ll %s --check-prefix=LLVM-SPIRV64

// CIR-SPIRV64: cir.triple = "spirv64-unknown-unknown"
// LLVM-SPIRV64: target triple = "spirv64-unknown-unknown"

typedef struct {
char c;
void *v;
void *v2;
} my_st;

// CIR-SPIRV64: cir.func @func(
// LLVM-SPIRV64: @func(
kernel void func(global long *arg) {
int res1[sizeof(my_st) == 24 ? 1 : -1]; // expected-no-diagnostics
int res2[sizeof(void *) == 8 ? 1 : -1]; // expected-no-diagnostics
int res3[sizeof(arg) == 8 ? 1 : -1]; // expected-no-diagnostics

my_st *tmp = 0;

// LLVM-SPIRV64: store i64 8, ptr addrspace(1)
arg[0] = (long)(&tmp->v);
// LLVM-SPIRV64: store i64 16, ptr addrspace(1)
arg[1] = (long)(&tmp->v2);
}
Loading