`sret` arguments are always going to reside in the stack/`alloca` address space, which makes the current formulation where their AS is derived from the pointee somewhat quaint. This patch ensures that `sret` ends up pointing to the `alloca` AS in IR function signatures, and also guards agains trying to pass a casted `alloca`d pointer to a `sret` arg, which can happen for most languages, when compiled for targets that have a non-zero `alloca` AS (e.g. AMDGCN) / map `LangAS::default` to a non-zero value (SPIR-V). A target could still choose to do something different here, by e.g. overriding `classifyReturnType` behaviour. In a broader sense, this patch extends non-aliased indirect args to also carry an AS, which leads to changing the `getIndirect()` interface. At the moment we're only using this for (indirect) returns, but it allows for future handling of indirect args themselves. We default to using the AllocaAS as that matches what Clang is currently doing, however if, in the future, a target would opt for e.g. placing indirect returns in some other storage, with another AS, this will require revisiting. --------- Co-authored-by: Matt Arsenault <arsenm2@gmail.com> Co-authored-by: Matt Arsenault <Matthew.Arsenault@amd.com>
176 lines
6.2 KiB
C++
176 lines
6.2 KiB
C++
//===- CSKY.cpp -----------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ABIInfoImpl.h"
|
|
#include "TargetInfo.h"
|
|
|
|
using namespace clang;
|
|
using namespace clang::CodeGen;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// CSKY ABI Implementation
|
|
//===----------------------------------------------------------------------===//
|
|
namespace {
|
|
class CSKYABIInfo : public DefaultABIInfo {
|
|
static const int NumArgGPRs = 4;
|
|
static const int NumArgFPRs = 4;
|
|
|
|
static const unsigned XLen = 32;
|
|
unsigned FLen;
|
|
|
|
public:
|
|
CSKYABIInfo(CodeGen::CodeGenTypes &CGT, unsigned FLen)
|
|
: DefaultABIInfo(CGT), FLen(FLen) {}
|
|
|
|
void computeInfo(CGFunctionInfo &FI) const override;
|
|
ABIArgInfo classifyArgumentType(QualType Ty, int &ArgGPRsLeft,
|
|
int &ArgFPRsLeft,
|
|
bool isReturnType = false) const;
|
|
ABIArgInfo classifyReturnType(QualType RetTy) const;
|
|
|
|
RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
|
|
AggValueSlot Slot) const override;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
void CSKYABIInfo::computeInfo(CGFunctionInfo &FI) const {
|
|
QualType RetTy = FI.getReturnType();
|
|
if (!getCXXABI().classifyReturnType(FI))
|
|
FI.getReturnInfo() = classifyReturnType(RetTy);
|
|
|
|
bool IsRetIndirect = FI.getReturnInfo().getKind() == ABIArgInfo::Indirect;
|
|
|
|
// We must track the number of GPRs used in order to conform to the CSKY
|
|
// ABI, as integer scalars passed in registers should have signext/zeroext
|
|
// when promoted.
|
|
int ArgGPRsLeft = IsRetIndirect ? NumArgGPRs - 1 : NumArgGPRs;
|
|
int ArgFPRsLeft = FLen ? NumArgFPRs : 0;
|
|
|
|
for (auto &ArgInfo : FI.arguments()) {
|
|
ArgInfo.info = classifyArgumentType(ArgInfo.type, ArgGPRsLeft, ArgFPRsLeft);
|
|
}
|
|
}
|
|
|
|
RValue CSKYABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
|
|
QualType Ty, AggValueSlot Slot) const {
|
|
CharUnits SlotSize = CharUnits::fromQuantity(XLen / 8);
|
|
|
|
// Empty records are ignored for parameter passing purposes.
|
|
if (isEmptyRecord(getContext(), Ty, true))
|
|
return Slot.asRValue();
|
|
|
|
auto TInfo = getContext().getTypeInfoInChars(Ty);
|
|
|
|
return emitVoidPtrVAArg(CGF, VAListAddr, Ty, false, TInfo, SlotSize,
|
|
/*AllowHigherAlign=*/true, Slot);
|
|
}
|
|
|
|
ABIArgInfo CSKYABIInfo::classifyArgumentType(QualType Ty, int &ArgGPRsLeft,
|
|
int &ArgFPRsLeft,
|
|
bool isReturnType) const {
|
|
assert(ArgGPRsLeft <= NumArgGPRs && "Arg GPR tracking underflow");
|
|
Ty = useFirstFieldIfTransparentUnion(Ty);
|
|
|
|
// Structures with either a non-trivial destructor or a non-trivial
|
|
// copy constructor are always passed indirectly.
|
|
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) {
|
|
if (ArgGPRsLeft)
|
|
ArgGPRsLeft -= 1;
|
|
return getNaturalAlignIndirect(
|
|
Ty, /*AddrSpace=*/getDataLayout().getAllocaAddrSpace(),
|
|
/*ByVal=*/RAA == CGCXXABI::RAA_DirectInMemory);
|
|
}
|
|
|
|
// Ignore empty structs/unions.
|
|
if (isEmptyRecord(getContext(), Ty, true))
|
|
return ABIArgInfo::getIgnore();
|
|
|
|
if (!Ty->getAsUnionType())
|
|
if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
|
|
return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
|
|
|
|
uint64_t Size = getContext().getTypeSize(Ty);
|
|
// Pass floating point values via FPRs if possible.
|
|
if (Ty->isFloatingType() && !Ty->isComplexType() && FLen >= Size &&
|
|
ArgFPRsLeft) {
|
|
ArgFPRsLeft--;
|
|
return ABIArgInfo::getDirect();
|
|
}
|
|
|
|
// Complex types for the hard float ABI must be passed direct rather than
|
|
// using CoerceAndExpand.
|
|
if (Ty->isComplexType() && FLen && !isReturnType) {
|
|
QualType EltTy = Ty->castAs<ComplexType>()->getElementType();
|
|
if (getContext().getTypeSize(EltTy) <= FLen) {
|
|
ArgFPRsLeft -= 2;
|
|
return ABIArgInfo::getDirect();
|
|
}
|
|
}
|
|
|
|
if (!isAggregateTypeForABI(Ty)) {
|
|
// Treat an enum type as its underlying type.
|
|
if (const EnumType *EnumTy = Ty->getAs<EnumType>())
|
|
Ty = EnumTy->getDecl()->getIntegerType();
|
|
|
|
// All integral types are promoted to XLen width, unless passed on the
|
|
// stack.
|
|
if (Size < XLen && Ty->isIntegralOrEnumerationType())
|
|
return ABIArgInfo::getExtend(Ty);
|
|
|
|
if (const auto *EIT = Ty->getAs<BitIntType>()) {
|
|
if (EIT->getNumBits() < XLen)
|
|
return ABIArgInfo::getExtend(Ty);
|
|
}
|
|
|
|
return ABIArgInfo::getDirect();
|
|
}
|
|
|
|
// For argument type, the first 4*XLen parts of aggregate will be passed
|
|
// in registers, and the rest will be passed in stack.
|
|
// So we can coerce to integers directly and let backend handle it correctly.
|
|
// For return type, aggregate which <= 2*XLen will be returned in registers.
|
|
// Otherwise, aggregate will be returned indirectly.
|
|
if (!isReturnType || (isReturnType && Size <= 2 * XLen)) {
|
|
if (Size <= XLen) {
|
|
return ABIArgInfo::getDirect(
|
|
llvm::IntegerType::get(getVMContext(), XLen));
|
|
} else {
|
|
return ABIArgInfo::getDirect(llvm::ArrayType::get(
|
|
llvm::IntegerType::get(getVMContext(), XLen), (Size + 31) / XLen));
|
|
}
|
|
}
|
|
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
|
|
/*ByVal=*/false);
|
|
}
|
|
|
|
ABIArgInfo CSKYABIInfo::classifyReturnType(QualType RetTy) const {
|
|
if (RetTy->isVoidType())
|
|
return ABIArgInfo::getIgnore();
|
|
|
|
int ArgGPRsLeft = 2;
|
|
int ArgFPRsLeft = FLen ? 1 : 0;
|
|
|
|
// The rules for return and argument types are the same, so defer to
|
|
// classifyArgumentType.
|
|
return classifyArgumentType(RetTy, ArgGPRsLeft, ArgFPRsLeft, true);
|
|
}
|
|
|
|
namespace {
|
|
class CSKYTargetCodeGenInfo : public TargetCodeGenInfo {
|
|
public:
|
|
CSKYTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, unsigned FLen)
|
|
: TargetCodeGenInfo(std::make_unique<CSKYABIInfo>(CGT, FLen)) {}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
std::unique_ptr<TargetCodeGenInfo>
|
|
CodeGen::createCSKYTargetCodeGenInfo(CodeGenModule &CGM, unsigned FLen) {
|
|
return std::make_unique<CSKYTargetCodeGenInfo>(CGM.getTypes(), FLen);
|
|
}
|