Pass and return `long double`s indirectly, as specified in the psABI. This continues the patch at https://reviews.llvm.org/D89130. This should fix the issue at https://github.com/llvm/llvm-project/issues/41838.
423 lines
14 KiB
C++
423 lines
14 KiB
C++
//===- Sparc.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"
|
|
#include <algorithm>
|
|
|
|
using namespace clang;
|
|
using namespace clang::CodeGen;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// SPARC v8 ABI Implementation.
|
|
// Based on the SPARC Compliance Definition version 2.4.1.
|
|
//
|
|
// Ensures that complex values are passed in registers.
|
|
//
|
|
namespace {
|
|
class SparcV8ABIInfo : public DefaultABIInfo {
|
|
public:
|
|
SparcV8ABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) {}
|
|
|
|
private:
|
|
ABIArgInfo classifyReturnType(QualType RetTy) const;
|
|
ABIArgInfo classifyArgumentType(QualType Ty) const;
|
|
void computeInfo(CGFunctionInfo &FI) const override;
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
ABIArgInfo SparcV8ABIInfo::classifyReturnType(QualType Ty) const {
|
|
const auto *CT = Ty->getAs<ComplexType>();
|
|
const auto *BT = Ty->getAs<BuiltinType>();
|
|
if (CT)
|
|
BT = CT->getElementType()->getAs<BuiltinType>();
|
|
bool IsLongDouble = BT && BT->getKind() == BuiltinType::LongDouble;
|
|
|
|
// long double _Complex is special in that it should be marked as inreg.
|
|
if (CT)
|
|
return IsLongDouble ? ABIArgInfo::getDirectInReg()
|
|
: ABIArgInfo::getDirect();
|
|
|
|
if (IsLongDouble)
|
|
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
|
|
/*ByVal=*/false);
|
|
|
|
return DefaultABIInfo::classifyReturnType(Ty);
|
|
}
|
|
|
|
ABIArgInfo SparcV8ABIInfo::classifyArgumentType(QualType Ty) const {
|
|
if (const auto *BT = Ty->getAs<BuiltinType>();
|
|
BT && BT->getKind() == BuiltinType::LongDouble)
|
|
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
|
|
|
|
return DefaultABIInfo::classifyArgumentType(Ty);
|
|
}
|
|
|
|
void SparcV8ABIInfo::computeInfo(CGFunctionInfo &FI) const {
|
|
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
|
|
for (auto &Arg : FI.arguments())
|
|
Arg.info = classifyArgumentType(Arg.type);
|
|
}
|
|
|
|
namespace {
|
|
class SparcV8TargetCodeGenInfo : public TargetCodeGenInfo {
|
|
public:
|
|
SparcV8TargetCodeGenInfo(CodeGenTypes &CGT)
|
|
: TargetCodeGenInfo(std::make_unique<SparcV8ABIInfo>(CGT)) {}
|
|
|
|
llvm::Value *decodeReturnAddress(CodeGen::CodeGenFunction &CGF,
|
|
llvm::Value *Address) const override {
|
|
int Offset;
|
|
if (isAggregateTypeForABI(CGF.CurFnInfo->getReturnType()))
|
|
Offset = 12;
|
|
else
|
|
Offset = 8;
|
|
return CGF.Builder.CreateGEP(CGF.Int8Ty, Address,
|
|
llvm::ConstantInt::get(CGF.Int32Ty, Offset));
|
|
}
|
|
|
|
llvm::Value *encodeReturnAddress(CodeGen::CodeGenFunction &CGF,
|
|
llvm::Value *Address) const override {
|
|
int Offset;
|
|
if (isAggregateTypeForABI(CGF.CurFnInfo->getReturnType()))
|
|
Offset = -12;
|
|
else
|
|
Offset = -8;
|
|
return CGF.Builder.CreateGEP(CGF.Int8Ty, Address,
|
|
llvm::ConstantInt::get(CGF.Int32Ty, Offset));
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// SPARC v9 ABI Implementation.
|
|
// Based on the SPARC Compliance Definition version 2.4.1.
|
|
//
|
|
// Function arguments a mapped to a nominal "parameter array" and promoted to
|
|
// registers depending on their type. Each argument occupies 8 or 16 bytes in
|
|
// the array, structs larger than 16 bytes are passed indirectly.
|
|
//
|
|
// One case requires special care:
|
|
//
|
|
// struct mixed {
|
|
// int i;
|
|
// float f;
|
|
// };
|
|
//
|
|
// When a struct mixed is passed by value, it only occupies 8 bytes in the
|
|
// parameter array, but the int is passed in an integer register, and the float
|
|
// is passed in a floating point register. This is represented as two arguments
|
|
// with the LLVM IR inreg attribute:
|
|
//
|
|
// declare void f(i32 inreg %i, float inreg %f)
|
|
//
|
|
// The code generator will only allocate 4 bytes from the parameter array for
|
|
// the inreg arguments. All other arguments are allocated a multiple of 8
|
|
// bytes.
|
|
//
|
|
namespace {
|
|
class SparcV9ABIInfo : public ABIInfo {
|
|
public:
|
|
SparcV9ABIInfo(CodeGenTypes &CGT) : ABIInfo(CGT) {}
|
|
|
|
private:
|
|
ABIArgInfo classifyType(QualType RetTy, unsigned SizeLimit,
|
|
unsigned &RegOffset) const;
|
|
void computeInfo(CGFunctionInfo &FI) const override;
|
|
RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
|
|
AggValueSlot Slot) const override;
|
|
|
|
// Coercion type builder for structs passed in registers. The coercion type
|
|
// serves two purposes:
|
|
//
|
|
// 1. Pad structs to a multiple of 64 bits, so they are passed 'left-aligned'
|
|
// in registers.
|
|
// 2. Expose aligned floating point elements as first-level elements, so the
|
|
// code generator knows to pass them in floating point registers.
|
|
//
|
|
// We also compute the InReg flag which indicates that the struct contains
|
|
// aligned 32-bit floats.
|
|
//
|
|
struct CoerceBuilder {
|
|
llvm::LLVMContext &Context;
|
|
const llvm::DataLayout &DL;
|
|
SmallVector<llvm::Type*, 8> Elems;
|
|
uint64_t Size;
|
|
bool InReg;
|
|
|
|
CoerceBuilder(llvm::LLVMContext &c, const llvm::DataLayout &dl)
|
|
: Context(c), DL(dl), Size(0), InReg(false) {}
|
|
|
|
// Pad Elems with integers until Size is ToSize.
|
|
void pad(uint64_t ToSize) {
|
|
assert(ToSize >= Size && "Cannot remove elements");
|
|
if (ToSize == Size)
|
|
return;
|
|
|
|
// Finish the current 64-bit word.
|
|
uint64_t Aligned = llvm::alignTo(Size, 64);
|
|
if (Aligned > Size && Aligned <= ToSize) {
|
|
Elems.push_back(llvm::IntegerType::get(Context, Aligned - Size));
|
|
Size = Aligned;
|
|
}
|
|
|
|
// Add whole 64-bit words.
|
|
while (Size + 64 <= ToSize) {
|
|
Elems.push_back(llvm::Type::getInt64Ty(Context));
|
|
Size += 64;
|
|
}
|
|
|
|
// Final in-word padding.
|
|
if (Size < ToSize) {
|
|
Elems.push_back(llvm::IntegerType::get(Context, ToSize - Size));
|
|
Size = ToSize;
|
|
}
|
|
}
|
|
|
|
// Add a floating point element at Offset.
|
|
void addFloat(uint64_t Offset, llvm::Type *Ty, unsigned Bits) {
|
|
// Unaligned floats are treated as integers.
|
|
if (Offset % Bits)
|
|
return;
|
|
// The InReg flag is only required if there are any floats < 64 bits.
|
|
if (Bits < 64)
|
|
InReg = true;
|
|
pad(Offset);
|
|
Elems.push_back(Ty);
|
|
Size = Offset + Bits;
|
|
}
|
|
|
|
// Add a struct type to the coercion type, starting at Offset (in bits).
|
|
void addStruct(uint64_t Offset, llvm::StructType *StrTy) {
|
|
const llvm::StructLayout *Layout = DL.getStructLayout(StrTy);
|
|
for (unsigned i = 0, e = StrTy->getNumElements(); i != e; ++i) {
|
|
llvm::Type *ElemTy = StrTy->getElementType(i);
|
|
uint64_t ElemOffset = Offset + Layout->getElementOffsetInBits(i);
|
|
switch (ElemTy->getTypeID()) {
|
|
case llvm::Type::StructTyID:
|
|
addStruct(ElemOffset, cast<llvm::StructType>(ElemTy));
|
|
break;
|
|
case llvm::Type::FloatTyID:
|
|
addFloat(ElemOffset, ElemTy, 32);
|
|
break;
|
|
case llvm::Type::DoubleTyID:
|
|
addFloat(ElemOffset, ElemTy, 64);
|
|
break;
|
|
case llvm::Type::FP128TyID:
|
|
addFloat(ElemOffset, ElemTy, 128);
|
|
break;
|
|
case llvm::Type::PointerTyID:
|
|
if (ElemOffset % 64 == 0) {
|
|
pad(ElemOffset);
|
|
Elems.push_back(ElemTy);
|
|
Size += 64;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if Ty is a usable substitute for the coercion type.
|
|
bool isUsableType(llvm::StructType *Ty) const {
|
|
return llvm::ArrayRef(Elems) == Ty->elements();
|
|
}
|
|
|
|
// Get the coercion type as a literal struct type.
|
|
llvm::Type *getType() const {
|
|
if (Elems.size() == 1)
|
|
return Elems.front();
|
|
else
|
|
return llvm::StructType::get(Context, Elems);
|
|
}
|
|
};
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit,
|
|
unsigned &RegOffset) const {
|
|
if (Ty->isVoidType())
|
|
return ABIArgInfo::getIgnore();
|
|
|
|
auto &Context = getContext();
|
|
auto &VMContext = getVMContext();
|
|
|
|
uint64_t Size = Context.getTypeSize(Ty);
|
|
unsigned Alignment = Context.getTypeAlign(Ty);
|
|
bool NeedPadding = (Alignment > 64) && (RegOffset % 2 != 0);
|
|
|
|
// Anything too big to fit in registers is passed with an explicit indirect
|
|
// pointer / sret pointer.
|
|
if (Size > SizeLimit) {
|
|
RegOffset += 1;
|
|
return getNaturalAlignIndirect(
|
|
Ty, /*AddrSpace=*/getDataLayout().getAllocaAddrSpace(),
|
|
/*ByVal=*/false);
|
|
}
|
|
|
|
// Treat an enum type as its underlying type.
|
|
if (const auto *ED = Ty->getAsEnumDecl())
|
|
Ty = ED->getIntegerType();
|
|
|
|
// Integer types smaller than a register are extended.
|
|
if (Size < 64 && Ty->isIntegerType()) {
|
|
RegOffset += 1;
|
|
return ABIArgInfo::getExtend(Ty);
|
|
}
|
|
|
|
if (const auto *EIT = Ty->getAs<BitIntType>())
|
|
if (EIT->getNumBits() < 64) {
|
|
RegOffset += 1;
|
|
return ABIArgInfo::getExtend(Ty);
|
|
}
|
|
|
|
// Other non-aggregates go in registers.
|
|
if (!isAggregateTypeForABI(Ty)) {
|
|
RegOffset += Size / 64;
|
|
return ABIArgInfo::getDirect();
|
|
}
|
|
|
|
// If a C++ object has either a non-trivial copy constructor or a non-trivial
|
|
// destructor, it is passed with an explicit indirect pointer / sret pointer.
|
|
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) {
|
|
RegOffset += 1;
|
|
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
|
|
RAA == CGCXXABI::RAA_DirectInMemory);
|
|
}
|
|
|
|
// This is a small aggregate type that should be passed in registers.
|
|
// Build a coercion type from the LLVM struct type.
|
|
llvm::StructType *StrTy = dyn_cast<llvm::StructType>(CGT.ConvertType(Ty));
|
|
if (!StrTy) {
|
|
RegOffset += Size / 64;
|
|
return ABIArgInfo::getDirect();
|
|
}
|
|
|
|
CoerceBuilder CB(VMContext, getDataLayout());
|
|
CB.addStruct(0, StrTy);
|
|
// All structs, even empty ones, should take up a register argument slot,
|
|
// so pin the minimum struct size to one bit.
|
|
CB.pad(llvm::alignTo(
|
|
std::max(CB.DL.getTypeSizeInBits(StrTy).getKnownMinValue(), uint64_t(1)),
|
|
64));
|
|
RegOffset += CB.Size / 64;
|
|
|
|
// If we're dealing with overaligned structs we may need to add a padding in
|
|
// the front, to preserve the correct register-memory mapping.
|
|
//
|
|
// See SCD 2.4.1, pages 3P-11 and 3P-12.
|
|
llvm::Type *Padding =
|
|
NeedPadding ? llvm::Type::getInt64Ty(VMContext) : nullptr;
|
|
RegOffset += NeedPadding ? 1 : 0;
|
|
|
|
// Try to use the original type for coercion.
|
|
llvm::Type *CoerceTy = CB.isUsableType(StrTy) ? StrTy : CB.getType();
|
|
|
|
ABIArgInfo AAI = ABIArgInfo::getDirect(CoerceTy, 0, Padding);
|
|
AAI.setInReg(CB.InReg);
|
|
return AAI;
|
|
}
|
|
|
|
RValue SparcV9ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
|
|
QualType Ty, AggValueSlot Slot) const {
|
|
CharUnits SlotSize = CharUnits::fromQuantity(8);
|
|
auto TInfo = getContext().getTypeInfoInChars(Ty);
|
|
|
|
// Zero-sized types have a width of one byte for parameter passing purposes.
|
|
TInfo.Width = std::max(TInfo.Width, CharUnits::fromQuantity(1));
|
|
|
|
// Arguments bigger than 2*SlotSize bytes are passed indirectly.
|
|
return emitVoidPtrVAArg(CGF, VAListAddr, Ty,
|
|
/*IsIndirect=*/TInfo.Width > 2 * SlotSize, TInfo,
|
|
SlotSize,
|
|
/*AllowHigherAlign=*/true, Slot);
|
|
}
|
|
|
|
void SparcV9ABIInfo::computeInfo(CGFunctionInfo &FI) const {
|
|
unsigned RetOffset = 0;
|
|
ABIArgInfo RetType = classifyType(FI.getReturnType(), 32 * 8, RetOffset);
|
|
FI.getReturnInfo() = RetType;
|
|
|
|
// Indirect returns will have its pointer passed as an argument.
|
|
unsigned ArgOffset = RetType.isIndirect() ? RetOffset : 0;
|
|
for (auto &I : FI.arguments())
|
|
I.info = classifyType(I.type, 16 * 8, ArgOffset);
|
|
}
|
|
|
|
namespace {
|
|
class SparcV9TargetCodeGenInfo : public TargetCodeGenInfo {
|
|
public:
|
|
SparcV9TargetCodeGenInfo(CodeGenTypes &CGT)
|
|
: TargetCodeGenInfo(std::make_unique<SparcV9ABIInfo>(CGT)) {}
|
|
|
|
int getDwarfEHStackPointer(CodeGen::CodeGenModule &M) const override {
|
|
return 14;
|
|
}
|
|
|
|
bool initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF,
|
|
llvm::Value *Address) const override;
|
|
|
|
llvm::Value *decodeReturnAddress(CodeGen::CodeGenFunction &CGF,
|
|
llvm::Value *Address) const override {
|
|
return CGF.Builder.CreateGEP(CGF.Int8Ty, Address,
|
|
llvm::ConstantInt::get(CGF.Int32Ty, 8));
|
|
}
|
|
|
|
llvm::Value *encodeReturnAddress(CodeGen::CodeGenFunction &CGF,
|
|
llvm::Value *Address) const override {
|
|
return CGF.Builder.CreateGEP(CGF.Int8Ty, Address,
|
|
llvm::ConstantInt::get(CGF.Int32Ty, -8));
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
bool
|
|
SparcV9TargetCodeGenInfo::initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF,
|
|
llvm::Value *Address) const {
|
|
// This is calculated from the LLVM and GCC tables and verified
|
|
// against gcc output. AFAIK all ABIs use the same encoding.
|
|
|
|
CodeGen::CGBuilderTy &Builder = CGF.Builder;
|
|
|
|
llvm::IntegerType *i8 = CGF.Int8Ty;
|
|
llvm::Value *Four8 = llvm::ConstantInt::get(i8, 4);
|
|
llvm::Value *Eight8 = llvm::ConstantInt::get(i8, 8);
|
|
|
|
// 0-31: the 8-byte general-purpose registers
|
|
AssignToArrayRange(Builder, Address, Eight8, 0, 31);
|
|
|
|
// 32-63: f0-31, the 4-byte floating-point registers
|
|
AssignToArrayRange(Builder, Address, Four8, 32, 63);
|
|
|
|
// Y = 64
|
|
// PSR = 65
|
|
// WIM = 66
|
|
// TBR = 67
|
|
// PC = 68
|
|
// NPC = 69
|
|
// FSR = 70
|
|
// CSR = 71
|
|
AssignToArrayRange(Builder, Address, Eight8, 64, 71);
|
|
|
|
// 72-87: d0-15, the 8-byte floating-point registers
|
|
AssignToArrayRange(Builder, Address, Eight8, 72, 87);
|
|
|
|
return false;
|
|
}
|
|
|
|
std::unique_ptr<TargetCodeGenInfo>
|
|
CodeGen::createSparcV8TargetCodeGenInfo(CodeGenModule &CGM) {
|
|
return std::make_unique<SparcV8TargetCodeGenInfo>(CGM.getTypes());
|
|
}
|
|
|
|
std::unique_ptr<TargetCodeGenInfo>
|
|
CodeGen::createSparcV9TargetCodeGenInfo(CodeGenModule &CGM) {
|
|
return std::make_unique<SparcV9TargetCodeGenInfo>(CGM.getTypes());
|
|
}
|