Skip to content

Commit 4072557

Browse files
kbobrovsAaronBallmanpremanandrao
authored
[SYCL] Add support for __regcall calling convention to spir targets. (#4533)
* [SYCL] Add support for __regcall calling convention to spir targets. This calling convention makes compiler return values and function arguments passed as values (through virtual registers) in most cases. The implementation is basically customizing SPIRABIInfo for CGFunctionInfo with the X86_RegCall calling convention. Code generation is mostly borrowed from AMDGPUABIInfo, but with some of the restrictions for passing by value removed. Signed-off-by: kbobrovs <[email protected]> Co-authored-by: Aaron Ballman <[email protected]> Co-authored-by: premanandrao <[email protected]>
1 parent 25d1a9d commit 4072557

File tree

4 files changed

+581
-7
lines changed

4 files changed

+581
-7
lines changed

clang/lib/Basic/Targets/SPIR.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,13 @@ class LLVM_LIBRARY_VISIBILITY BaseSPIRTargetInfo : public TargetInfo {
128128
}
129129

130130
CallingConvCheckResult checkCallingConvention(CallingConv CC) const override {
131-
return (CC == CC_SpirFunction || CC == CC_OpenCLKernel) ? CCCR_OK
132-
: CCCR_Warning;
131+
return (CC == CC_SpirFunction || CC == CC_OpenCLKernel ||
132+
// Permit CC_X86RegCall which is used to mark external functions
133+
// with explicit simd or structure type arguments to pass them via
134+
// registers.
135+
CC == CC_X86RegCall)
136+
? CCCR_OK
137+
: CCCR_Warning;
133138
}
134139

135140
CallingConv getDefaultCallingConv() const override {
@@ -286,8 +291,10 @@ class LLVM_LIBRARY_VISIBILITY WindowsX86_64_SPIR64TargetInfo
286291
}
287292

288293
CallingConvCheckResult checkCallingConvention(CallingConv CC) const override {
289-
if (CC == CC_X86VectorCall)
294+
if (CC == CC_X86VectorCall || CC == CC_X86RegCall)
290295
// Permit CC_X86VectorCall which is used in Microsoft headers
296+
// Permit CC_X86RegCall which is used to mark external functions with
297+
// explicit simd or structure type arguments to pass them via registers.
291298
return CCCR_OK;
292299
return (CC == CC_SpirFunction || CC == CC_OpenCLKernel) ? CCCR_OK
293300
: CCCR_Warning;

clang/lib/CodeGen/TargetInfo.cpp

Lines changed: 105 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10286,6 +10286,11 @@ class CommonSPIRABIInfo : public DefaultABIInfo {
1028610286

1028710287
ABIArgInfo classifyKernelArgumentType(QualType Ty) const;
1028810288

10289+
// Add new functions rather than overload existing so that these public APIs
10290+
// can't be blindly misused with wrong calling convention.
10291+
ABIArgInfo classifyRegcallReturnType(QualType RetTy) const;
10292+
ABIArgInfo classifyRegcallArgumentType(QualType RetTy) const;
10293+
1028910294
void computeInfo(CGFunctionInfo &FI) const override;
1029010295

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

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

10309-
if (!getCXXABI().classifyReturnType(FI))
10310-
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
10315+
if (!getCXXABI().classifyReturnType(FI)) {
10316+
CanQualType RetT = FI.getReturnType();
10317+
FI.getReturnInfo() =
10318+
IsRegCall ? classifyRegcallReturnType(RetT) : classifyReturnType(RetT);
10319+
}
1031110320

1031210321
for (auto &Arg : FI.arguments()) {
1031310322
if (CC == llvm::CallingConv::SPIR_KERNEL) {
1031410323
Arg.info = classifyKernelArgumentType(Arg.type);
1031510324
} else {
10316-
Arg.info = classifyArgumentType(Arg.type);
10325+
Arg.info = IsRegCall ? classifyRegcallArgumentType(Arg.type)
10326+
: classifyArgumentType(Arg.type);
10327+
}
10328+
}
10329+
}
10330+
10331+
// The two functions below are based on AMDGPUABIInfo, but without any
10332+
// restriction on the maximum number of arguments passed via registers.
10333+
// SPIRV BEs are expected to further adjust the calling convention as
10334+
// needed (use stack or byval-like passing) for some of the arguments.
10335+
10336+
ABIArgInfo CommonSPIRABIInfo::classifyRegcallReturnType(QualType RetTy) const {
10337+
if (isAggregateTypeForABI(RetTy)) {
10338+
// Records with non-trivial destructors/copy-constructors should not be
10339+
// returned by value.
10340+
if (!getRecordArgABI(RetTy, getCXXABI())) {
10341+
// Ignore empty structs/unions.
10342+
if (isEmptyRecord(getContext(), RetTy, true))
10343+
return ABIArgInfo::getIgnore();
10344+
10345+
// Lower single-element structs to just return a regular value.
10346+
if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext()))
10347+
return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
10348+
10349+
if (const RecordType *RT = RetTy->getAs<RecordType>()) {
10350+
const RecordDecl *RD = RT->getDecl();
10351+
if (RD->hasFlexibleArrayMember())
10352+
return classifyReturnType(RetTy);
10353+
}
10354+
10355+
// Pack aggregates <= 8 bytes into a single vector register or pair.
10356+
// TODO make this parameterizeable/adjustable depending on spir target
10357+
// triple abi component.
10358+
uint64_t Size = getContext().getTypeSize(RetTy);
10359+
if (Size <= 16)
10360+
return ABIArgInfo::getDirect(llvm::Type::getInt16Ty(getVMContext()));
10361+
10362+
if (Size <= 32)
10363+
return ABIArgInfo::getDirect(llvm::Type::getInt32Ty(getVMContext()));
10364+
10365+
if (Size <= 64) {
10366+
llvm::Type *I32Ty = llvm::Type::getInt32Ty(getVMContext());
10367+
return ABIArgInfo::getDirect(llvm::ArrayType::get(I32Ty, 2));
10368+
}
10369+
return ABIArgInfo::getDirect();
1031710370
}
1031810371
}
10372+
// Otherwise just do the default thing.
10373+
return classifyReturnType(RetTy);
10374+
}
10375+
10376+
ABIArgInfo CommonSPIRABIInfo::classifyRegcallArgumentType(QualType Ty) const {
10377+
Ty = useFirstFieldIfTransparentUnion(Ty);
10378+
10379+
if (isAggregateTypeForABI(Ty)) {
10380+
// Records with non-trivial destructors/copy-constructors should not be
10381+
// passed by value.
10382+
if (auto RAA = getRecordArgABI(Ty, getCXXABI()))
10383+
return getNaturalAlignIndirect(Ty, RAA == CGCXXABI::RAA_DirectInMemory);
10384+
10385+
// Ignore empty structs/unions.
10386+
if (isEmptyRecord(getContext(), Ty, true))
10387+
return ABIArgInfo::getIgnore();
10388+
10389+
// Lower single-element structs to just pass a regular value. TODO: We
10390+
// could do reasonable-size multiple-element structs too, using getExpand(),
10391+
// though watch out for things like bitfields.
10392+
if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
10393+
return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
10394+
10395+
if (const RecordType *RT = Ty->getAs<RecordType>()) {
10396+
const RecordDecl *RD = RT->getDecl();
10397+
if (RD->hasFlexibleArrayMember())
10398+
return classifyArgumentType(Ty);
10399+
}
10400+
10401+
// Pack aggregates <= 8 bytes into single vector register or pair.
10402+
// TODO make this parameterizeable/adjustable depending on spir target
10403+
// triple abi component.
10404+
uint64_t Size = getContext().getTypeSize(Ty);
10405+
if (Size <= 64) {
10406+
if (Size <= 16)
10407+
return ABIArgInfo::getDirect(llvm::Type::getInt16Ty(getVMContext()));
10408+
10409+
if (Size <= 32)
10410+
return ABIArgInfo::getDirect(llvm::Type::getInt32Ty(getVMContext()));
10411+
10412+
// XXX: Should this be i64 instead, and should the limit increase?
10413+
llvm::Type *I32Ty = llvm::Type::getInt32Ty(getVMContext());
10414+
return ABIArgInfo::getDirect(llvm::ArrayType::get(I32Ty, 2));
10415+
}
10416+
return ABIArgInfo::getDirect();
10417+
}
10418+
10419+
// Otherwise just do the default thing.
10420+
return classifyArgumentType(Ty);
1031910421
}
1032010422

1032110423
class SPIRVABIInfo : public CommonSPIRABIInfo {

0 commit comments

Comments
 (0)