424 lines
16 KiB
C++
424 lines
16 KiB
C++
|
//===- Hexagon.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;
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Hexagon ABI Implementation
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
class HexagonABIInfo : public DefaultABIInfo {
|
||
|
public:
|
||
|
HexagonABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) {}
|
||
|
|
||
|
private:
|
||
|
ABIArgInfo classifyReturnType(QualType RetTy) const;
|
||
|
ABIArgInfo classifyArgumentType(QualType RetTy) const;
|
||
|
ABIArgInfo classifyArgumentType(QualType RetTy, unsigned *RegsLeft) const;
|
||
|
|
||
|
void computeInfo(CGFunctionInfo &FI) const override;
|
||
|
|
||
|
Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
|
||
|
QualType Ty) const override;
|
||
|
Address EmitVAArgFromMemory(CodeGenFunction &CFG, Address VAListAddr,
|
||
|
QualType Ty) const;
|
||
|
Address EmitVAArgForHexagon(CodeGenFunction &CFG, Address VAListAddr,
|
||
|
QualType Ty) const;
|
||
|
Address EmitVAArgForHexagonLinux(CodeGenFunction &CFG, Address VAListAddr,
|
||
|
QualType Ty) const;
|
||
|
};
|
||
|
|
||
|
class HexagonTargetCodeGenInfo : public TargetCodeGenInfo {
|
||
|
public:
|
||
|
HexagonTargetCodeGenInfo(CodeGenTypes &CGT)
|
||
|
: TargetCodeGenInfo(std::make_unique<HexagonABIInfo>(CGT)) {}
|
||
|
|
||
|
int getDwarfEHStackPointer(CodeGen::CodeGenModule &M) const override {
|
||
|
return 29;
|
||
|
}
|
||
|
|
||
|
void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
|
||
|
CodeGen::CodeGenModule &GCM) const override {
|
||
|
if (GV->isDeclaration())
|
||
|
return;
|
||
|
const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
|
||
|
if (!FD)
|
||
|
return;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
void HexagonABIInfo::computeInfo(CGFunctionInfo &FI) const {
|
||
|
unsigned RegsLeft = 6;
|
||
|
if (!getCXXABI().classifyReturnType(FI))
|
||
|
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
|
||
|
for (auto &I : FI.arguments())
|
||
|
I.info = classifyArgumentType(I.type, &RegsLeft);
|
||
|
}
|
||
|
|
||
|
static bool HexagonAdjustRegsLeft(uint64_t Size, unsigned *RegsLeft) {
|
||
|
assert(Size <= 64 && "Not expecting to pass arguments larger than 64 bits"
|
||
|
" through registers");
|
||
|
|
||
|
if (*RegsLeft == 0)
|
||
|
return false;
|
||
|
|
||
|
if (Size <= 32) {
|
||
|
(*RegsLeft)--;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (2 <= (*RegsLeft & (~1U))) {
|
||
|
*RegsLeft = (*RegsLeft & (~1U)) - 2;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Next available register was r5 but candidate was greater than 32-bits so it
|
||
|
// has to go on the stack. However we still consume r5
|
||
|
if (*RegsLeft == 1)
|
||
|
*RegsLeft = 0;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
ABIArgInfo HexagonABIInfo::classifyArgumentType(QualType Ty,
|
||
|
unsigned *RegsLeft) const {
|
||
|
if (!isAggregateTypeForABI(Ty)) {
|
||
|
// Treat an enum type as its underlying type.
|
||
|
if (const EnumType *EnumTy = Ty->getAs<EnumType>())
|
||
|
Ty = EnumTy->getDecl()->getIntegerType();
|
||
|
|
||
|
uint64_t Size = getContext().getTypeSize(Ty);
|
||
|
if (Size <= 64)
|
||
|
HexagonAdjustRegsLeft(Size, RegsLeft);
|
||
|
|
||
|
if (Size > 64 && Ty->isBitIntType())
|
||
|
return getNaturalAlignIndirect(Ty, /*ByVal=*/true);
|
||
|
|
||
|
return isPromotableIntegerTypeForABI(Ty) ? ABIArgInfo::getExtend(Ty)
|
||
|
: ABIArgInfo::getDirect();
|
||
|
}
|
||
|
|
||
|
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI()))
|
||
|
return getNaturalAlignIndirect(Ty, RAA == CGCXXABI::RAA_DirectInMemory);
|
||
|
|
||
|
// Ignore empty records.
|
||
|
if (isEmptyRecord(getContext(), Ty, true))
|
||
|
return ABIArgInfo::getIgnore();
|
||
|
|
||
|
uint64_t Size = getContext().getTypeSize(Ty);
|
||
|
unsigned Align = getContext().getTypeAlign(Ty);
|
||
|
|
||
|
if (Size > 64)
|
||
|
return getNaturalAlignIndirect(Ty, /*ByVal=*/true);
|
||
|
|
||
|
if (HexagonAdjustRegsLeft(Size, RegsLeft))
|
||
|
Align = Size <= 32 ? 32 : 64;
|
||
|
if (Size <= Align) {
|
||
|
// Pass in the smallest viable integer type.
|
||
|
Size = llvm::bit_ceil(Size);
|
||
|
return ABIArgInfo::getDirect(llvm::Type::getIntNTy(getVMContext(), Size));
|
||
|
}
|
||
|
return DefaultABIInfo::classifyArgumentType(Ty);
|
||
|
}
|
||
|
|
||
|
ABIArgInfo HexagonABIInfo::classifyReturnType(QualType RetTy) const {
|
||
|
if (RetTy->isVoidType())
|
||
|
return ABIArgInfo::getIgnore();
|
||
|
|
||
|
const TargetInfo &T = CGT.getTarget();
|
||
|
uint64_t Size = getContext().getTypeSize(RetTy);
|
||
|
|
||
|
if (RetTy->getAs<VectorType>()) {
|
||
|
// HVX vectors are returned in vector registers or register pairs.
|
||
|
if (T.hasFeature("hvx")) {
|
||
|
assert(T.hasFeature("hvx-length64b") || T.hasFeature("hvx-length128b"));
|
||
|
uint64_t VecSize = T.hasFeature("hvx-length64b") ? 64*8 : 128*8;
|
||
|
if (Size == VecSize || Size == 2*VecSize)
|
||
|
return ABIArgInfo::getDirectInReg();
|
||
|
}
|
||
|
// Large vector types should be returned via memory.
|
||
|
if (Size > 64)
|
||
|
return getNaturalAlignIndirect(RetTy);
|
||
|
}
|
||
|
|
||
|
if (!isAggregateTypeForABI(RetTy)) {
|
||
|
// Treat an enum type as its underlying type.
|
||
|
if (const EnumType *EnumTy = RetTy->getAs<EnumType>())
|
||
|
RetTy = EnumTy->getDecl()->getIntegerType();
|
||
|
|
||
|
if (Size > 64 && RetTy->isBitIntType())
|
||
|
return getNaturalAlignIndirect(RetTy, /*ByVal=*/false);
|
||
|
|
||
|
return isPromotableIntegerTypeForABI(RetTy) ? ABIArgInfo::getExtend(RetTy)
|
||
|
: ABIArgInfo::getDirect();
|
||
|
}
|
||
|
|
||
|
if (isEmptyRecord(getContext(), RetTy, true))
|
||
|
return ABIArgInfo::getIgnore();
|
||
|
|
||
|
// Aggregates <= 8 bytes are returned in registers, other aggregates
|
||
|
// are returned indirectly.
|
||
|
if (Size <= 64) {
|
||
|
// Return in the smallest viable integer type.
|
||
|
Size = llvm::bit_ceil(Size);
|
||
|
return ABIArgInfo::getDirect(llvm::Type::getIntNTy(getVMContext(), Size));
|
||
|
}
|
||
|
return getNaturalAlignIndirect(RetTy, /*ByVal=*/true);
|
||
|
}
|
||
|
|
||
|
Address HexagonABIInfo::EmitVAArgFromMemory(CodeGenFunction &CGF,
|
||
|
Address VAListAddr,
|
||
|
QualType Ty) const {
|
||
|
// Load the overflow area pointer.
|
||
|
Address __overflow_area_pointer_p =
|
||
|
CGF.Builder.CreateStructGEP(VAListAddr, 2, "__overflow_area_pointer_p");
|
||
|
llvm::Value *__overflow_area_pointer = CGF.Builder.CreateLoad(
|
||
|
__overflow_area_pointer_p, "__overflow_area_pointer");
|
||
|
|
||
|
uint64_t Align = CGF.getContext().getTypeAlign(Ty) / 8;
|
||
|
if (Align > 4) {
|
||
|
// Alignment should be a power of 2.
|
||
|
assert((Align & (Align - 1)) == 0 && "Alignment is not power of 2!");
|
||
|
|
||
|
// overflow_arg_area = (overflow_arg_area + align - 1) & -align;
|
||
|
llvm::Value *Offset = llvm::ConstantInt::get(CGF.Int64Ty, Align - 1);
|
||
|
|
||
|
// Add offset to the current pointer to access the argument.
|
||
|
__overflow_area_pointer =
|
||
|
CGF.Builder.CreateGEP(CGF.Int8Ty, __overflow_area_pointer, Offset);
|
||
|
llvm::Value *AsInt =
|
||
|
CGF.Builder.CreatePtrToInt(__overflow_area_pointer, CGF.Int32Ty);
|
||
|
|
||
|
// Create a mask which should be "AND"ed
|
||
|
// with (overflow_arg_area + align - 1)
|
||
|
llvm::Value *Mask = llvm::ConstantInt::get(CGF.Int32Ty, -(int)Align);
|
||
|
__overflow_area_pointer = CGF.Builder.CreateIntToPtr(
|
||
|
CGF.Builder.CreateAnd(AsInt, Mask), __overflow_area_pointer->getType(),
|
||
|
"__overflow_area_pointer.align");
|
||
|
}
|
||
|
|
||
|
// Get the type of the argument from memory and bitcast
|
||
|
// overflow area pointer to the argument type.
|
||
|
llvm::Type *PTy = CGF.ConvertTypeForMem(Ty);
|
||
|
Address AddrTyped =
|
||
|
Address(__overflow_area_pointer, PTy, CharUnits::fromQuantity(Align));
|
||
|
|
||
|
// Round up to the minimum stack alignment for varargs which is 4 bytes.
|
||
|
uint64_t Offset = llvm::alignTo(CGF.getContext().getTypeSize(Ty) / 8, 4);
|
||
|
|
||
|
__overflow_area_pointer = CGF.Builder.CreateGEP(
|
||
|
CGF.Int8Ty, __overflow_area_pointer,
|
||
|
llvm::ConstantInt::get(CGF.Int32Ty, Offset),
|
||
|
"__overflow_area_pointer.next");
|
||
|
CGF.Builder.CreateStore(__overflow_area_pointer, __overflow_area_pointer_p);
|
||
|
|
||
|
return AddrTyped;
|
||
|
}
|
||
|
|
||
|
Address HexagonABIInfo::EmitVAArgForHexagon(CodeGenFunction &CGF,
|
||
|
Address VAListAddr,
|
||
|
QualType Ty) const {
|
||
|
// FIXME: Need to handle alignment
|
||
|
llvm::Type *BP = CGF.Int8PtrTy;
|
||
|
CGBuilderTy &Builder = CGF.Builder;
|
||
|
Address VAListAddrAsBPP = VAListAddr.withElementType(BP);
|
||
|
llvm::Value *Addr = Builder.CreateLoad(VAListAddrAsBPP, "ap.cur");
|
||
|
// Handle address alignment for type alignment > 32 bits
|
||
|
uint64_t TyAlign = CGF.getContext().getTypeAlign(Ty) / 8;
|
||
|
if (TyAlign > 4) {
|
||
|
assert((TyAlign & (TyAlign - 1)) == 0 && "Alignment is not power of 2!");
|
||
|
llvm::Value *AddrAsInt = Builder.CreatePtrToInt(Addr, CGF.Int32Ty);
|
||
|
AddrAsInt = Builder.CreateAdd(AddrAsInt, Builder.getInt32(TyAlign - 1));
|
||
|
AddrAsInt = Builder.CreateAnd(AddrAsInt, Builder.getInt32(~(TyAlign - 1)));
|
||
|
Addr = Builder.CreateIntToPtr(AddrAsInt, BP);
|
||
|
}
|
||
|
Address AddrTyped =
|
||
|
Address(Addr, CGF.ConvertType(Ty), CharUnits::fromQuantity(TyAlign));
|
||
|
|
||
|
uint64_t Offset = llvm::alignTo(CGF.getContext().getTypeSize(Ty) / 8, 4);
|
||
|
llvm::Value *NextAddr = Builder.CreateGEP(
|
||
|
CGF.Int8Ty, Addr, llvm::ConstantInt::get(CGF.Int32Ty, Offset), "ap.next");
|
||
|
Builder.CreateStore(NextAddr, VAListAddrAsBPP);
|
||
|
|
||
|
return AddrTyped;
|
||
|
}
|
||
|
|
||
|
Address HexagonABIInfo::EmitVAArgForHexagonLinux(CodeGenFunction &CGF,
|
||
|
Address VAListAddr,
|
||
|
QualType Ty) const {
|
||
|
int ArgSize = CGF.getContext().getTypeSize(Ty) / 8;
|
||
|
|
||
|
if (ArgSize > 8)
|
||
|
return EmitVAArgFromMemory(CGF, VAListAddr, Ty);
|
||
|
|
||
|
// Here we have check if the argument is in register area or
|
||
|
// in overflow area.
|
||
|
// If the saved register area pointer + argsize rounded up to alignment >
|
||
|
// saved register area end pointer, argument is in overflow area.
|
||
|
unsigned RegsLeft = 6;
|
||
|
Ty = CGF.getContext().getCanonicalType(Ty);
|
||
|
(void)classifyArgumentType(Ty, &RegsLeft);
|
||
|
|
||
|
llvm::BasicBlock *MaybeRegBlock = CGF.createBasicBlock("vaarg.maybe_reg");
|
||
|
llvm::BasicBlock *InRegBlock = CGF.createBasicBlock("vaarg.in_reg");
|
||
|
llvm::BasicBlock *OnStackBlock = CGF.createBasicBlock("vaarg.on_stack");
|
||
|
llvm::BasicBlock *ContBlock = CGF.createBasicBlock("vaarg.end");
|
||
|
|
||
|
// Get rounded size of the argument.GCC does not allow vararg of
|
||
|
// size < 4 bytes. We follow the same logic here.
|
||
|
ArgSize = (CGF.getContext().getTypeSize(Ty) <= 32) ? 4 : 8;
|
||
|
int ArgAlign = (CGF.getContext().getTypeSize(Ty) <= 32) ? 4 : 8;
|
||
|
|
||
|
// Argument may be in saved register area
|
||
|
CGF.EmitBlock(MaybeRegBlock);
|
||
|
|
||
|
// Load the current saved register area pointer.
|
||
|
Address __current_saved_reg_area_pointer_p = CGF.Builder.CreateStructGEP(
|
||
|
VAListAddr, 0, "__current_saved_reg_area_pointer_p");
|
||
|
llvm::Value *__current_saved_reg_area_pointer = CGF.Builder.CreateLoad(
|
||
|
__current_saved_reg_area_pointer_p, "__current_saved_reg_area_pointer");
|
||
|
|
||
|
// Load the saved register area end pointer.
|
||
|
Address __saved_reg_area_end_pointer_p = CGF.Builder.CreateStructGEP(
|
||
|
VAListAddr, 1, "__saved_reg_area_end_pointer_p");
|
||
|
llvm::Value *__saved_reg_area_end_pointer = CGF.Builder.CreateLoad(
|
||
|
__saved_reg_area_end_pointer_p, "__saved_reg_area_end_pointer");
|
||
|
|
||
|
// If the size of argument is > 4 bytes, check if the stack
|
||
|
// location is aligned to 8 bytes
|
||
|
if (ArgAlign > 4) {
|
||
|
|
||
|
llvm::Value *__current_saved_reg_area_pointer_int =
|
||
|
CGF.Builder.CreatePtrToInt(__current_saved_reg_area_pointer,
|
||
|
CGF.Int32Ty);
|
||
|
|
||
|
__current_saved_reg_area_pointer_int = CGF.Builder.CreateAdd(
|
||
|
__current_saved_reg_area_pointer_int,
|
||
|
llvm::ConstantInt::get(CGF.Int32Ty, (ArgAlign - 1)),
|
||
|
"align_current_saved_reg_area_pointer");
|
||
|
|
||
|
__current_saved_reg_area_pointer_int =
|
||
|
CGF.Builder.CreateAnd(__current_saved_reg_area_pointer_int,
|
||
|
llvm::ConstantInt::get(CGF.Int32Ty, -ArgAlign),
|
||
|
"align_current_saved_reg_area_pointer");
|
||
|
|
||
|
__current_saved_reg_area_pointer =
|
||
|
CGF.Builder.CreateIntToPtr(__current_saved_reg_area_pointer_int,
|
||
|
__current_saved_reg_area_pointer->getType(),
|
||
|
"align_current_saved_reg_area_pointer");
|
||
|
}
|
||
|
|
||
|
llvm::Value *__new_saved_reg_area_pointer =
|
||
|
CGF.Builder.CreateGEP(CGF.Int8Ty, __current_saved_reg_area_pointer,
|
||
|
llvm::ConstantInt::get(CGF.Int32Ty, ArgSize),
|
||
|
"__new_saved_reg_area_pointer");
|
||
|
|
||
|
llvm::Value *UsingStack = nullptr;
|
||
|
UsingStack = CGF.Builder.CreateICmpSGT(__new_saved_reg_area_pointer,
|
||
|
__saved_reg_area_end_pointer);
|
||
|
|
||
|
CGF.Builder.CreateCondBr(UsingStack, OnStackBlock, InRegBlock);
|
||
|
|
||
|
// Argument in saved register area
|
||
|
// Implement the block where argument is in register saved area
|
||
|
CGF.EmitBlock(InRegBlock);
|
||
|
|
||
|
llvm::Type *PTy = CGF.ConvertType(Ty);
|
||
|
llvm::Value *__saved_reg_area_p = CGF.Builder.CreateBitCast(
|
||
|
__current_saved_reg_area_pointer, llvm::PointerType::getUnqual(PTy));
|
||
|
|
||
|
CGF.Builder.CreateStore(__new_saved_reg_area_pointer,
|
||
|
__current_saved_reg_area_pointer_p);
|
||
|
|
||
|
CGF.EmitBranch(ContBlock);
|
||
|
|
||
|
// Argument in overflow area
|
||
|
// Implement the block where the argument is in overflow area.
|
||
|
CGF.EmitBlock(OnStackBlock);
|
||
|
|
||
|
// Load the overflow area pointer
|
||
|
Address __overflow_area_pointer_p =
|
||
|
CGF.Builder.CreateStructGEP(VAListAddr, 2, "__overflow_area_pointer_p");
|
||
|
llvm::Value *__overflow_area_pointer = CGF.Builder.CreateLoad(
|
||
|
__overflow_area_pointer_p, "__overflow_area_pointer");
|
||
|
|
||
|
// Align the overflow area pointer according to the alignment of the argument
|
||
|
if (ArgAlign > 4) {
|
||
|
llvm::Value *__overflow_area_pointer_int =
|
||
|
CGF.Builder.CreatePtrToInt(__overflow_area_pointer, CGF.Int32Ty);
|
||
|
|
||
|
__overflow_area_pointer_int =
|
||
|
CGF.Builder.CreateAdd(__overflow_area_pointer_int,
|
||
|
llvm::ConstantInt::get(CGF.Int32Ty, ArgAlign - 1),
|
||
|
"align_overflow_area_pointer");
|
||
|
|
||
|
__overflow_area_pointer_int =
|
||
|
CGF.Builder.CreateAnd(__overflow_area_pointer_int,
|
||
|
llvm::ConstantInt::get(CGF.Int32Ty, -ArgAlign),
|
||
|
"align_overflow_area_pointer");
|
||
|
|
||
|
__overflow_area_pointer = CGF.Builder.CreateIntToPtr(
|
||
|
__overflow_area_pointer_int, __overflow_area_pointer->getType(),
|
||
|
"align_overflow_area_pointer");
|
||
|
}
|
||
|
|
||
|
// Get the pointer for next argument in overflow area and store it
|
||
|
// to overflow area pointer.
|
||
|
llvm::Value *__new_overflow_area_pointer = CGF.Builder.CreateGEP(
|
||
|
CGF.Int8Ty, __overflow_area_pointer,
|
||
|
llvm::ConstantInt::get(CGF.Int32Ty, ArgSize),
|
||
|
"__overflow_area_pointer.next");
|
||
|
|
||
|
CGF.Builder.CreateStore(__new_overflow_area_pointer,
|
||
|
__overflow_area_pointer_p);
|
||
|
|
||
|
CGF.Builder.CreateStore(__new_overflow_area_pointer,
|
||
|
__current_saved_reg_area_pointer_p);
|
||
|
|
||
|
// Bitcast the overflow area pointer to the type of argument.
|
||
|
llvm::Type *OverflowPTy = CGF.ConvertTypeForMem(Ty);
|
||
|
llvm::Value *__overflow_area_p = CGF.Builder.CreateBitCast(
|
||
|
__overflow_area_pointer, llvm::PointerType::getUnqual(OverflowPTy));
|
||
|
|
||
|
CGF.EmitBranch(ContBlock);
|
||
|
|
||
|
// Get the correct pointer to load the variable argument
|
||
|
// Implement the ContBlock
|
||
|
CGF.EmitBlock(ContBlock);
|
||
|
|
||
|
llvm::Type *MemTy = CGF.ConvertTypeForMem(Ty);
|
||
|
llvm::Type *MemPTy = llvm::PointerType::getUnqual(MemTy);
|
||
|
llvm::PHINode *ArgAddr = CGF.Builder.CreatePHI(MemPTy, 2, "vaarg.addr");
|
||
|
ArgAddr->addIncoming(__saved_reg_area_p, InRegBlock);
|
||
|
ArgAddr->addIncoming(__overflow_area_p, OnStackBlock);
|
||
|
|
||
|
return Address(ArgAddr, MemTy, CharUnits::fromQuantity(ArgAlign));
|
||
|
}
|
||
|
|
||
|
Address HexagonABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
|
||
|
QualType Ty) const {
|
||
|
|
||
|
if (getTarget().getTriple().isMusl())
|
||
|
return EmitVAArgForHexagonLinux(CGF, VAListAddr, Ty);
|
||
|
|
||
|
return EmitVAArgForHexagon(CGF, VAListAddr, Ty);
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<TargetCodeGenInfo>
|
||
|
CodeGen::createHexagonTargetCodeGenInfo(CodeGenModule &CGM) {
|
||
|
return std::make_unique<HexagonTargetCodeGenInfo>(CGM.getTypes());
|
||
|
}
|