Skip to content

[SYCL] Add support for __regcall calling convention to spir targets. #4533

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 3 commits into from
Mar 17, 2022
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
13 changes: 10 additions & 3 deletions clang/lib/Basic/Targets/SPIR.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,13 @@ class LLVM_LIBRARY_VISIBILITY BaseSPIRTargetInfo : public TargetInfo {
}

CallingConvCheckResult checkCallingConvention(CallingConv CC) const override {
return (CC == CC_SpirFunction || CC == CC_OpenCLKernel) ? CCCR_OK
: CCCR_Warning;
return (CC == CC_SpirFunction || CC == CC_OpenCLKernel ||
// Permit CC_X86RegCall which is used to mark external functions
// with explicit simd or structure type arguments to pass them via
// registers.
CC == CC_X86RegCall)
? CCCR_OK
: CCCR_Warning;
}

CallingConv getDefaultCallingConv() const override {
Expand Down Expand Up @@ -286,8 +291,10 @@ class LLVM_LIBRARY_VISIBILITY WindowsX86_64_SPIR64TargetInfo
}

CallingConvCheckResult checkCallingConvention(CallingConv CC) const override {
if (CC == CC_X86VectorCall)
if (CC == CC_X86VectorCall || CC == CC_X86RegCall)
// Permit CC_X86VectorCall which is used in Microsoft headers
// Permit CC_X86RegCall which is used to mark external functions with
// explicit simd or structure type arguments to pass them via registers.
return CCCR_OK;
return (CC == CC_SpirFunction || CC == CC_OpenCLKernel) ? CCCR_OK
: CCCR_Warning;
Expand Down
108 changes: 105 additions & 3 deletions clang/lib/CodeGen/TargetInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10286,6 +10286,11 @@ class CommonSPIRABIInfo : public DefaultABIInfo {

ABIArgInfo classifyKernelArgumentType(QualType Ty) const;

// Add new functions rather than overload existing so that these public APIs
// can't be blindly misused with wrong calling convention.
ABIArgInfo classifyRegcallReturnType(QualType RetTy) const;
ABIArgInfo classifyRegcallArgumentType(QualType RetTy) const;

void computeInfo(CGFunctionInfo &FI) const override;

private:
Expand All @@ -10305,17 +10310,114 @@ ABIArgInfo CommonSPIRABIInfo::classifyKernelArgumentType(QualType Ty) const {

void CommonSPIRABIInfo::computeInfo(CGFunctionInfo &FI) const {
llvm::CallingConv::ID CC = FI.getCallingConvention();
bool IsRegCall = CC == llvm::CallingConv::X86_RegCall;

if (!getCXXABI().classifyReturnType(FI))
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
if (!getCXXABI().classifyReturnType(FI)) {
CanQualType RetT = FI.getReturnType();
FI.getReturnInfo() =
IsRegCall ? classifyRegcallReturnType(RetT) : classifyReturnType(RetT);
}

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

// The two functions below are based on AMDGPUABIInfo, but without any
// restriction on the maximum number of arguments passed via registers.
// SPIRV BEs are expected to further adjust the calling convention as
// needed (use stack or byval-like passing) for some of the arguments.

ABIArgInfo CommonSPIRABIInfo::classifyRegcallReturnType(QualType RetTy) const {
if (isAggregateTypeForABI(RetTy)) {
// Records with non-trivial destructors/copy-constructors should not be
// returned by value.
if (!getRecordArgABI(RetTy, getCXXABI())) {
// Ignore empty structs/unions.
if (isEmptyRecord(getContext(), RetTy, true))
return ABIArgInfo::getIgnore();

// Lower single-element structs to just return a regular value.
if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext()))
return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));

if (const RecordType *RT = RetTy->getAs<RecordType>()) {
const RecordDecl *RD = RT->getDecl();
if (RD->hasFlexibleArrayMember())
return classifyReturnType(RetTy);
}

// Pack aggregates <= 8 bytes into a single vector register or pair.
// TODO make this parameterizeable/adjustable depending on spir target
// triple abi component.
uint64_t Size = getContext().getTypeSize(RetTy);
if (Size <= 16)
return ABIArgInfo::getDirect(llvm::Type::getInt16Ty(getVMContext()));

if (Size <= 32)
return ABIArgInfo::getDirect(llvm::Type::getInt32Ty(getVMContext()));

if (Size <= 64) {
llvm::Type *I32Ty = llvm::Type::getInt32Ty(getVMContext());
return ABIArgInfo::getDirect(llvm::ArrayType::get(I32Ty, 2));
}
return ABIArgInfo::getDirect();
}
}
// Otherwise just do the default thing.
return classifyReturnType(RetTy);
}

ABIArgInfo CommonSPIRABIInfo::classifyRegcallArgumentType(QualType Ty) const {
Ty = useFirstFieldIfTransparentUnion(Ty);

if (isAggregateTypeForABI(Ty)) {
// Records with non-trivial destructors/copy-constructors should not be
// passed by value.
if (auto RAA = getRecordArgABI(Ty, getCXXABI()))
return getNaturalAlignIndirect(Ty, RAA == CGCXXABI::RAA_DirectInMemory);

// Ignore empty structs/unions.
if (isEmptyRecord(getContext(), Ty, true))
return ABIArgInfo::getIgnore();

// Lower single-element structs to just pass a regular value. TODO: We
// could do reasonable-size multiple-element structs too, using getExpand(),
// though watch out for things like bitfields.
if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));

if (const RecordType *RT = Ty->getAs<RecordType>()) {
const RecordDecl *RD = RT->getDecl();
if (RD->hasFlexibleArrayMember())
return classifyArgumentType(Ty);
}

// Pack aggregates <= 8 bytes into single vector register or pair.
// TODO make this parameterizeable/adjustable depending on spir target
// triple abi component.
uint64_t Size = getContext().getTypeSize(Ty);
if (Size <= 64) {
if (Size <= 16)
return ABIArgInfo::getDirect(llvm::Type::getInt16Ty(getVMContext()));

if (Size <= 32)
return ABIArgInfo::getDirect(llvm::Type::getInt32Ty(getVMContext()));

// XXX: Should this be i64 instead, and should the limit increase?
llvm::Type *I32Ty = llvm::Type::getInt32Ty(getVMContext());
return ABIArgInfo::getDirect(llvm::ArrayType::get(I32Ty, 2));
}
return ABIArgInfo::getDirect();
}

// Otherwise just do the default thing.
return classifyArgumentType(Ty);
}

class SPIRVABIInfo : public CommonSPIRABIInfo {
Expand Down
Loading