#include "AArch64CallLowering.h"
#include "AArch64ISelLowering.h"
#include "AArch64MachineFunctionInfo.h"
#include "AArch64Subtarget.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/ObjCARCUtil.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/CallingConvLower.h"
#include "llvm/CodeGen/FunctionLoweringInfo.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/GlobalISel/Utils.h"
#include "llvm/CodeGen/LowLevelType.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/CodeGen/ValueTypes.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/MachineValueType.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <iterator>
#define DEBUG_TYPE "aarch64-call-lowering"
using namespace llvm;
AArch64CallLowering::AArch64CallLowering(const AArch64TargetLowering &TLI)
: CallLowering(&TLI) {}
static void applyStackPassedSmallTypeDAGHack(EVT OrigVT, MVT &ValVT,
MVT &LocVT) {
if (OrigVT == MVT::i1 || OrigVT == MVT::i8)
ValVT = LocVT = MVT::i8;
else if (OrigVT == MVT::i16)
ValVT = LocVT = MVT::i16;
}
static LLT getStackValueStoreTypeHack(const CCValAssign &VA) {
const MVT ValVT = VA.getValVT();
return (ValVT == MVT::i8 || ValVT == MVT::i16) ? LLT(ValVT)
: LLT(VA.getLocVT());
}
namespace {
struct AArch64IncomingValueAssigner
: public CallLowering::IncomingValueAssigner {
AArch64IncomingValueAssigner(CCAssignFn *AssignFn_,
CCAssignFn *AssignFnVarArg_)
: IncomingValueAssigner(AssignFn_, AssignFnVarArg_) {}
bool assignArg(unsigned ValNo, EVT OrigVT, MVT ValVT, MVT LocVT,
CCValAssign::LocInfo LocInfo,
const CallLowering::ArgInfo &Info, ISD::ArgFlagsTy Flags,
CCState &State) override {
applyStackPassedSmallTypeDAGHack(OrigVT, ValVT, LocVT);
return IncomingValueAssigner::assignArg(ValNo, OrigVT, ValVT, LocVT,
LocInfo, Info, Flags, State);
}
};
struct AArch64OutgoingValueAssigner
: public CallLowering::OutgoingValueAssigner {
const AArch64Subtarget &Subtarget;
bool IsReturn;
AArch64OutgoingValueAssigner(CCAssignFn *AssignFn_,
CCAssignFn *AssignFnVarArg_,
const AArch64Subtarget &Subtarget_,
bool IsReturn)
: OutgoingValueAssigner(AssignFn_, AssignFnVarArg_),
Subtarget(Subtarget_), IsReturn(IsReturn) {}
bool assignArg(unsigned ValNo, EVT OrigVT, MVT ValVT, MVT LocVT,
CCValAssign::LocInfo LocInfo,
const CallLowering::ArgInfo &Info, ISD::ArgFlagsTy Flags,
CCState &State) override {
bool IsCalleeWin = Subtarget.isCallingConvWin64(State.getCallingConv());
bool UseVarArgsCCForFixed = IsCalleeWin && State.isVarArg();
if (!State.isVarArg() && !UseVarArgsCCForFixed && !IsReturn)
applyStackPassedSmallTypeDAGHack(OrigVT, ValVT, LocVT);
bool Res;
if (Info.IsFixed && !UseVarArgsCCForFixed)
Res = AssignFn(ValNo, ValVT, LocVT, LocInfo, Flags, State);
else
Res = AssignFnVarArg(ValNo, ValVT, LocVT, LocInfo, Flags, State);
StackOffset = State.getNextStackOffset();
return Res;
}
};
struct IncomingArgHandler : public CallLowering::IncomingValueHandler {
IncomingArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI)
: IncomingValueHandler(MIRBuilder, MRI) {}
Register getStackAddress(uint64_t Size, int64_t Offset,
MachinePointerInfo &MPO,
ISD::ArgFlagsTy Flags) override {
auto &MFI = MIRBuilder.getMF().getFrameInfo();
const bool IsImmutable = !Flags.isByVal();
int FI = MFI.CreateFixedObject(Size, Offset, IsImmutable);
MPO = MachinePointerInfo::getFixedStack(MIRBuilder.getMF(), FI);
auto AddrReg = MIRBuilder.buildFrameIndex(LLT::pointer(0, 64), FI);
return AddrReg.getReg(0);
}
LLT getStackValueStoreType(const DataLayout &DL, const CCValAssign &VA,
ISD::ArgFlagsTy Flags) const override {
if (Flags.isPointer())
return CallLowering::ValueHandler::getStackValueStoreType(DL, VA, Flags);
return getStackValueStoreTypeHack(VA);
}
void assignValueToReg(Register ValVReg, Register PhysReg,
CCValAssign VA) override {
markPhysRegUsed(PhysReg);
IncomingValueHandler::assignValueToReg(ValVReg, PhysReg, VA);
}
void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy,
MachinePointerInfo &MPO, CCValAssign &VA) override {
MachineFunction &MF = MIRBuilder.getMF();
LLT ValTy(VA.getValVT());
LLT LocTy(VA.getLocVT());
if (VA.getValVT() == MVT::i8 || VA.getValVT() == MVT::i16)
std::swap(ValTy, LocTy);
else {
assert(LocTy.getSizeInBits() == MemTy.getSizeInBits());
LocTy = MemTy;
}
auto MMO = MF.getMachineMemOperand(
MPO, MachineMemOperand::MOLoad | MachineMemOperand::MOInvariant, LocTy,
inferAlignFromPtrInfo(MF, MPO));
switch (VA.getLocInfo()) {
case CCValAssign::LocInfo::ZExt:
MIRBuilder.buildLoadInstr(TargetOpcode::G_ZEXTLOAD, ValVReg, Addr, *MMO);
return;
case CCValAssign::LocInfo::SExt:
MIRBuilder.buildLoadInstr(TargetOpcode::G_SEXTLOAD, ValVReg, Addr, *MMO);
return;
default:
MIRBuilder.buildLoad(ValVReg, Addr, *MMO);
return;
}
}
virtual void markPhysRegUsed(MCRegister PhysReg) = 0;
};
struct FormalArgHandler : public IncomingArgHandler {
FormalArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI)
: IncomingArgHandler(MIRBuilder, MRI) {}
void markPhysRegUsed(MCRegister PhysReg) override {
MIRBuilder.getMRI()->addLiveIn(PhysReg);
MIRBuilder.getMBB().addLiveIn(PhysReg);
}
};
struct CallReturnHandler : public IncomingArgHandler {
CallReturnHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI,
MachineInstrBuilder MIB)
: IncomingArgHandler(MIRBuilder, MRI), MIB(MIB) {}
void markPhysRegUsed(MCRegister PhysReg) override {
MIB.addDef(PhysReg, RegState::Implicit);
}
MachineInstrBuilder MIB;
};
struct ReturnedArgCallReturnHandler : public CallReturnHandler {
ReturnedArgCallReturnHandler(MachineIRBuilder &MIRBuilder,
MachineRegisterInfo &MRI,
MachineInstrBuilder MIB)
: CallReturnHandler(MIRBuilder, MRI, MIB) {}
void markPhysRegUsed(MCRegister PhysReg) override {}
};
struct OutgoingArgHandler : public CallLowering::OutgoingValueHandler {
OutgoingArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI,
MachineInstrBuilder MIB, bool IsTailCall = false,
int FPDiff = 0)
: OutgoingValueHandler(MIRBuilder, MRI), MIB(MIB), IsTailCall(IsTailCall),
FPDiff(FPDiff),
Subtarget(MIRBuilder.getMF().getSubtarget<AArch64Subtarget>()) {}
Register getStackAddress(uint64_t Size, int64_t Offset,
MachinePointerInfo &MPO,
ISD::ArgFlagsTy Flags) override {
MachineFunction &MF = MIRBuilder.getMF();
LLT p0 = LLT::pointer(0, 64);
LLT s64 = LLT::scalar(64);
if (IsTailCall) {
assert(!Flags.isByVal() && "byval unhandled with tail calls");
Offset += FPDiff;
int FI = MF.getFrameInfo().CreateFixedObject(Size, Offset, true);
auto FIReg = MIRBuilder.buildFrameIndex(p0, FI);
MPO = MachinePointerInfo::getFixedStack(MF, FI);
return FIReg.getReg(0);
}
if (!SPReg)
SPReg = MIRBuilder.buildCopy(p0, Register(AArch64::SP)).getReg(0);
auto OffsetReg = MIRBuilder.buildConstant(s64, Offset);
auto AddrReg = MIRBuilder.buildPtrAdd(p0, SPReg, OffsetReg);
MPO = MachinePointerInfo::getStack(MF, Offset);
return AddrReg.getReg(0);
}
LLT getStackValueStoreType(const DataLayout &DL, const CCValAssign &VA,
ISD::ArgFlagsTy Flags) const override {
if (Flags.isPointer())
return CallLowering::ValueHandler::getStackValueStoreType(DL, VA, Flags);
return getStackValueStoreTypeHack(VA);
}
void assignValueToReg(Register ValVReg, Register PhysReg,
CCValAssign VA) override {
MIB.addUse(PhysReg, RegState::Implicit);
Register ExtReg = extendRegister(ValVReg, VA);
MIRBuilder.buildCopy(PhysReg, ExtReg);
}
void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy,
MachinePointerInfo &MPO, CCValAssign &VA) override {
MachineFunction &MF = MIRBuilder.getMF();
auto MMO = MF.getMachineMemOperand(MPO, MachineMemOperand::MOStore, MemTy,
inferAlignFromPtrInfo(MF, MPO));
MIRBuilder.buildStore(ValVReg, Addr, *MMO);
}
void assignValueToAddress(const CallLowering::ArgInfo &Arg, unsigned RegIndex,
Register Addr, LLT MemTy, MachinePointerInfo &MPO,
CCValAssign &VA) override {
unsigned MaxSize = MemTy.getSizeInBytes() * 8;
if (!Arg.IsFixed)
MaxSize = 0;
Register ValVReg = Arg.Regs[RegIndex];
if (VA.getLocInfo() != CCValAssign::LocInfo::FPExt) {
MVT LocVT = VA.getLocVT();
MVT ValVT = VA.getValVT();
if (VA.getValVT() == MVT::i8 || VA.getValVT() == MVT::i16) {
std::swap(ValVT, LocVT);
MemTy = LLT(VA.getValVT());
}
ValVReg = extendRegister(ValVReg, VA, MaxSize);
} else {
MemTy = LLT(VA.getValVT());
}
assignValueToAddress(ValVReg, Addr, MemTy, MPO, VA);
}
MachineInstrBuilder MIB;
bool IsTailCall;
int FPDiff;
Register SPReg;
const AArch64Subtarget &Subtarget;
};
}
static bool doesCalleeRestoreStack(CallingConv::ID CallConv, bool TailCallOpt) {
return (CallConv == CallingConv::Fast && TailCallOpt) ||
CallConv == CallingConv::Tail || CallConv == CallingConv::SwiftTail;
}
bool AArch64CallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
const Value *Val,
ArrayRef<Register> VRegs,
FunctionLoweringInfo &FLI,
Register SwiftErrorVReg) const {
auto MIB = MIRBuilder.buildInstrNoInsert(AArch64::RET_ReallyLR);
assert(((Val && !VRegs.empty()) || (!Val && VRegs.empty())) &&
"Return value without a vreg");
bool Success = true;
if (!FLI.CanLowerReturn) {
insertSRetStores(MIRBuilder, Val->getType(), VRegs, FLI.DemoteRegister);
} else if (!VRegs.empty()) {
MachineFunction &MF = MIRBuilder.getMF();
const Function &F = MF.getFunction();
const AArch64Subtarget &Subtarget = MF.getSubtarget<AArch64Subtarget>();
MachineRegisterInfo &MRI = MF.getRegInfo();
const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>();
CCAssignFn *AssignFn = TLI.CCAssignFnForReturn(F.getCallingConv());
auto &DL = F.getParent()->getDataLayout();
LLVMContext &Ctx = Val->getType()->getContext();
SmallVector<EVT, 4> SplitEVTs;
ComputeValueVTs(TLI, DL, Val->getType(), SplitEVTs);
assert(VRegs.size() == SplitEVTs.size() &&
"For each split Type there should be exactly one VReg.");
SmallVector<ArgInfo, 8> SplitArgs;
CallingConv::ID CC = F.getCallingConv();
for (unsigned i = 0; i < SplitEVTs.size(); ++i) {
Register CurVReg = VRegs[i];
ArgInfo CurArgInfo = ArgInfo{CurVReg, SplitEVTs[i].getTypeForEVT(Ctx), 0};
setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, F);
if (MRI.getType(CurVReg).getSizeInBits() == 1) {
CurVReg = MIRBuilder.buildZExt(LLT::scalar(8), CurVReg).getReg(0);
} else if (TLI.getNumRegistersForCallingConv(Ctx, CC, SplitEVTs[i]) ==
1) {
MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CC, SplitEVTs[i]);
if (EVT(NewVT) != SplitEVTs[i]) {
unsigned ExtendOp = TargetOpcode::G_ANYEXT;
if (F.getAttributes().hasRetAttr(Attribute::SExt))
ExtendOp = TargetOpcode::G_SEXT;
else if (F.getAttributes().hasRetAttr(Attribute::ZExt))
ExtendOp = TargetOpcode::G_ZEXT;
LLT NewLLT(NewVT);
LLT OldLLT(MVT::getVT(CurArgInfo.Ty));
CurArgInfo.Ty = EVT(NewVT).getTypeForEVT(Ctx);
if (NewVT.isVector()) {
if (OldLLT.isVector()) {
if (NewLLT.getNumElements() > OldLLT.getNumElements()) {
if (NewLLT.getNumElements() != OldLLT.getNumElements() * 2) {
LLVM_DEBUG(dbgs() << "Outgoing vector ret has too many elts");
return false;
}
auto Undef = MIRBuilder.buildUndef({OldLLT});
CurVReg =
MIRBuilder.buildMerge({NewLLT}, {CurVReg, Undef}).getReg(0);
} else {
CurVReg = MIRBuilder.buildInstr(ExtendOp, {NewLLT}, {CurVReg})
.getReg(0);
}
} else if (NewLLT.getNumElements() == 2) {
auto Undef = MIRBuilder.buildUndef({OldLLT});
CurVReg =
MIRBuilder
.buildBuildVector({NewLLT}, {CurVReg, Undef.getReg(0)})
.getReg(0);
} else {
LLVM_DEBUG(dbgs() << "Could not handle ret ty\n");
return false;
}
} else {
if (NewLLT != MRI.getType(CurVReg)) {
CurVReg = MIRBuilder.buildInstr(ExtendOp, {NewLLT}, {CurVReg})
.getReg(0);
}
}
}
}
if (CurVReg != CurArgInfo.Regs[0]) {
CurArgInfo.Regs[0] = CurVReg;
setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, F);
}
splitToValueTypes(CurArgInfo, SplitArgs, DL, CC);
}
AArch64OutgoingValueAssigner Assigner(AssignFn, AssignFn, Subtarget,
true);
OutgoingArgHandler Handler(MIRBuilder, MRI, MIB);
Success = determineAndHandleAssignments(Handler, Assigner, SplitArgs,
MIRBuilder, CC, F.isVarArg());
}
if (SwiftErrorVReg) {
MIB.addUse(AArch64::X21, RegState::Implicit);
MIRBuilder.buildCopy(AArch64::X21, SwiftErrorVReg);
}
MIRBuilder.insertInstr(MIB);
return Success;
}
bool AArch64CallLowering::canLowerReturn(MachineFunction &MF,
CallingConv::ID CallConv,
SmallVectorImpl<BaseArgInfo> &Outs,
bool IsVarArg) const {
SmallVector<CCValAssign, 16> ArgLocs;
const auto &TLI = *getTLI<AArch64TargetLowering>();
CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs,
MF.getFunction().getContext());
return checkReturn(CCInfo, Outs, TLI.CCAssignFnForReturn(CallConv));
}
static void handleMustTailForwardedRegisters(MachineIRBuilder &MIRBuilder,
CCAssignFn *AssignFn) {
MachineBasicBlock &MBB = MIRBuilder.getMBB();
MachineFunction &MF = MIRBuilder.getMF();
MachineFrameInfo &MFI = MF.getFrameInfo();
if (!MFI.hasMustTailInVarArgFunc())
return;
AArch64FunctionInfo *FuncInfo = MF.getInfo<AArch64FunctionInfo>();
const Function &F = MF.getFunction();
assert(F.isVarArg() && "Expected F to be vararg?");
SmallVector<CCValAssign, 16> ArgLocs;
CCState CCInfo(F.getCallingConv(), true, MF, ArgLocs,
F.getContext());
SmallVector<MVT, 2> RegParmTypes;
RegParmTypes.push_back(MVT::i64);
RegParmTypes.push_back(MVT::f128);
SmallVectorImpl<ForwardedRegister> &Forwards =
FuncInfo->getForwardedMustTailRegParms();
CCInfo.analyzeMustTailForwardedRegisters(Forwards, RegParmTypes, AssignFn);
if (!CCInfo.isAllocated(AArch64::X8)) {
Register X8VReg = MF.addLiveIn(AArch64::X8, &AArch64::GPR64RegClass);
Forwards.push_back(ForwardedRegister(X8VReg, AArch64::X8, MVT::i64));
}
for (const auto &F : Forwards) {
MBB.addLiveIn(F.PReg);
MIRBuilder.buildCopy(Register(F.VReg), Register(F.PReg));
}
}
bool AArch64CallLowering::fallBackToDAGISel(const MachineFunction &MF) const {
auto &F = MF.getFunction();
if (isa<ScalableVectorType>(F.getReturnType()))
return true;
if (llvm::any_of(F.args(), [](const Argument &A) {
return isa<ScalableVectorType>(A.getType());
}))
return true;
const auto &ST = MF.getSubtarget<AArch64Subtarget>();
if (!ST.hasNEON() || !ST.hasFPARMv8()) {
LLVM_DEBUG(dbgs() << "Falling back to SDAG because we don't support no-NEON\n");
return true;
}
return false;
}
bool AArch64CallLowering::lowerFormalArguments(
MachineIRBuilder &MIRBuilder, const Function &F,
ArrayRef<ArrayRef<Register>> VRegs, FunctionLoweringInfo &FLI) const {
MachineFunction &MF = MIRBuilder.getMF();
MachineBasicBlock &MBB = MIRBuilder.getMBB();
MachineRegisterInfo &MRI = MF.getRegInfo();
auto &DL = F.getParent()->getDataLayout();
SmallVector<ArgInfo, 8> SplitArgs;
SmallVector<std::pair<Register, Register>> BoolArgs;
if (!FLI.CanLowerReturn)
insertSRetIncomingArgument(F, SplitArgs, FLI.DemoteRegister, MRI, DL);
unsigned i = 0;
for (auto &Arg : F.args()) {
if (DL.getTypeStoreSize(Arg.getType()).isZero())
continue;
ArgInfo OrigArg{VRegs[i], Arg, i};
setArgFlags(OrigArg, i + AttributeList::FirstArgIndex, DL, F);
if (OrigArg.Ty->isIntegerTy(1)) {
assert(OrigArg.Regs.size() == 1 &&
MRI.getType(OrigArg.Regs[0]).getSizeInBits() == 1 &&
"Unexpected registers used for i1 arg");
if (!OrigArg.Flags[0].isZExt()) {
Register OrigReg = OrigArg.Regs[0];
Register WideReg = MRI.createGenericVirtualRegister(LLT::scalar(8));
OrigArg.Regs[0] = WideReg;
BoolArgs.push_back({OrigReg, WideReg});
}
}
if (Arg.hasAttribute(Attribute::SwiftAsync))
MF.getInfo<AArch64FunctionInfo>()->setHasSwiftAsyncContext(true);
splitToValueTypes(OrigArg, SplitArgs, DL, F.getCallingConv());
++i;
}
if (!MBB.empty())
MIRBuilder.setInstr(*MBB.begin());
const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>();
CCAssignFn *AssignFn =
TLI.CCAssignFnForCall(F.getCallingConv(), false);
AArch64IncomingValueAssigner Assigner(AssignFn, AssignFn);
FormalArgHandler Handler(MIRBuilder, MRI);
if (!determineAndHandleAssignments(Handler, Assigner, SplitArgs, MIRBuilder,
F.getCallingConv(), F.isVarArg()))
return false;
if (!BoolArgs.empty()) {
for (auto &KV : BoolArgs) {
Register OrigReg = KV.first;
Register WideReg = KV.second;
LLT WideTy = MRI.getType(WideReg);
assert(MRI.getType(OrigReg).getScalarSizeInBits() == 1 &&
"Unexpected bit size of a bool arg");
MIRBuilder.buildTrunc(
OrigReg, MIRBuilder.buildAssertZExt(WideTy, WideReg, 1).getReg(0));
}
}
AArch64FunctionInfo *FuncInfo = MF.getInfo<AArch64FunctionInfo>();
uint64_t StackOffset = Assigner.StackOffset;
if (F.isVarArg()) {
auto &Subtarget = MF.getSubtarget<AArch64Subtarget>();
if (!Subtarget.isTargetDarwin()) {
return false;
}
StackOffset =
alignTo(Assigner.StackOffset, Subtarget.isTargetILP32() ? 4 : 8);
auto &MFI = MIRBuilder.getMF().getFrameInfo();
FuncInfo->setVarArgsStackIndex(MFI.CreateFixedObject(4, StackOffset, true));
}
if (doesCalleeRestoreStack(F.getCallingConv(),
MF.getTarget().Options.GuaranteedTailCallOpt)) {
StackOffset = alignTo(StackOffset, 16);
FuncInfo->setArgumentStackToRestore(StackOffset);
}
FuncInfo->setBytesInStackArgArea(StackOffset);
auto &Subtarget = MF.getSubtarget<AArch64Subtarget>();
if (Subtarget.hasCustomCallingConv())
Subtarget.getRegisterInfo()->UpdateCustomCalleeSavedRegs(MF);
handleMustTailForwardedRegisters(MIRBuilder, AssignFn);
MIRBuilder.setMBB(MBB);
return true;
}
static bool canGuaranteeTCO(CallingConv::ID CC, bool GuaranteeTailCalls) {
return (CC == CallingConv::Fast && GuaranteeTailCalls) ||
CC == CallingConv::Tail || CC == CallingConv::SwiftTail;
}
static bool mayTailCallThisCC(CallingConv::ID CC) {
switch (CC) {
case CallingConv::C:
case CallingConv::PreserveMost:
case CallingConv::Swift:
case CallingConv::SwiftTail:
case CallingConv::Tail:
case CallingConv::Fast:
return true;
default:
return false;
}
}
static std::pair<CCAssignFn *, CCAssignFn *>
getAssignFnsForCC(CallingConv::ID CC, const AArch64TargetLowering &TLI) {
return {TLI.CCAssignFnForCall(CC, false), TLI.CCAssignFnForCall(CC, true)};
}
bool AArch64CallLowering::doCallerAndCalleePassArgsTheSameWay(
CallLoweringInfo &Info, MachineFunction &MF,
SmallVectorImpl<ArgInfo> &InArgs) const {
const Function &CallerF = MF.getFunction();
CallingConv::ID CalleeCC = Info.CallConv;
CallingConv::ID CallerCC = CallerF.getCallingConv();
if (CalleeCC == CallerCC)
return true;
const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>();
CCAssignFn *CalleeAssignFnFixed;
CCAssignFn *CalleeAssignFnVarArg;
std::tie(CalleeAssignFnFixed, CalleeAssignFnVarArg) =
getAssignFnsForCC(CalleeCC, TLI);
CCAssignFn *CallerAssignFnFixed;
CCAssignFn *CallerAssignFnVarArg;
std::tie(CallerAssignFnFixed, CallerAssignFnVarArg) =
getAssignFnsForCC(CallerCC, TLI);
AArch64IncomingValueAssigner CalleeAssigner(CalleeAssignFnFixed,
CalleeAssignFnVarArg);
AArch64IncomingValueAssigner CallerAssigner(CallerAssignFnFixed,
CallerAssignFnVarArg);
if (!resultsCompatible(Info, MF, InArgs, CalleeAssigner, CallerAssigner))
return false;
auto TRI = MF.getSubtarget<AArch64Subtarget>().getRegisterInfo();
const uint32_t *CallerPreserved = TRI->getCallPreservedMask(MF, CallerCC);
const uint32_t *CalleePreserved = TRI->getCallPreservedMask(MF, CalleeCC);
if (MF.getSubtarget<AArch64Subtarget>().hasCustomCallingConv()) {
TRI->UpdateCustomCallPreservedMask(MF, &CallerPreserved);
TRI->UpdateCustomCallPreservedMask(MF, &CalleePreserved);
}
return TRI->regmaskSubsetEqual(CallerPreserved, CalleePreserved);
}
bool AArch64CallLowering::areCalleeOutgoingArgsTailCallable(
CallLoweringInfo &Info, MachineFunction &MF,
SmallVectorImpl<ArgInfo> &OutArgs) const {
if (OutArgs.empty())
return true;
const Function &CallerF = MF.getFunction();
LLVMContext &Ctx = CallerF.getContext();
CallingConv::ID CalleeCC = Info.CallConv;
CallingConv::ID CallerCC = CallerF.getCallingConv();
const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>();
const AArch64Subtarget &Subtarget = MF.getSubtarget<AArch64Subtarget>();
CCAssignFn *AssignFnFixed;
CCAssignFn *AssignFnVarArg;
std::tie(AssignFnFixed, AssignFnVarArg) = getAssignFnsForCC(CalleeCC, TLI);
SmallVector<CCValAssign, 16> OutLocs;
CCState OutInfo(CalleeCC, false, MF, OutLocs, Ctx);
AArch64OutgoingValueAssigner CalleeAssigner(AssignFnFixed, AssignFnVarArg,
Subtarget, false);
if (!determineAssignments(CalleeAssigner, OutArgs, OutInfo)) {
LLVM_DEBUG(dbgs() << "... Could not analyze call operands.\n");
return false;
}
const AArch64FunctionInfo *FuncInfo = MF.getInfo<AArch64FunctionInfo>();
if (OutInfo.getNextStackOffset() > FuncInfo->getBytesInStackArgArea()) {
LLVM_DEBUG(dbgs() << "... Cannot fit call operands on caller's stack.\n");
return false;
}
auto TRI = MF.getSubtarget<AArch64Subtarget>().getRegisterInfo();
const uint32_t *CallerPreservedMask = TRI->getCallPreservedMask(MF, CallerCC);
MachineRegisterInfo &MRI = MF.getRegInfo();
if (Info.IsVarArg) {
for (unsigned i = 0; i < OutLocs.size(); ++i) {
auto &ArgLoc = OutLocs[i];
if (ArgLoc.isRegLoc())
continue;
LLVM_DEBUG(
dbgs()
<< "... Cannot tail call vararg function with stack arguments\n");
return false;
}
}
return parametersInCSRMatch(MRI, CallerPreservedMask, OutLocs, OutArgs);
}
bool AArch64CallLowering::isEligibleForTailCallOptimization(
MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info,
SmallVectorImpl<ArgInfo> &InArgs,
SmallVectorImpl<ArgInfo> &OutArgs) const {
if (!Info.IsTailCall)
return false;
CallingConv::ID CalleeCC = Info.CallConv;
MachineFunction &MF = MIRBuilder.getMF();
const Function &CallerF = MF.getFunction();
LLVM_DEBUG(dbgs() << "Attempting to lower call as tail call\n");
if (Info.SwiftErrorVReg) {
LLVM_DEBUG(dbgs() << "... Cannot handle tail calls with swifterror yet.\n");
return false;
}
if (!mayTailCallThisCC(CalleeCC)) {
LLVM_DEBUG(dbgs() << "... Calling convention cannot be tail called.\n");
return false;
}
if (any_of(CallerF.args(), [](const Argument &A) {
return A.hasByValAttr() || A.hasInRegAttr() || A.hasSwiftErrorAttr();
})) {
LLVM_DEBUG(dbgs() << "... Cannot tail call from callers with byval, "
"inreg, or swifterror arguments\n");
return false;
}
if (Info.Callee.isGlobal()) {
const GlobalValue *GV = Info.Callee.getGlobal();
const Triple &TT = MF.getTarget().getTargetTriple();
if (GV->hasExternalWeakLinkage() &&
(!TT.isOSWindows() || TT.isOSBinFormatELF() ||
TT.isOSBinFormatMachO())) {
LLVM_DEBUG(dbgs() << "... Cannot tail call externally-defined function "
"with weak linkage for this OS.\n");
return false;
}
}
if (canGuaranteeTCO(CalleeCC, MF.getTarget().Options.GuaranteedTailCallOpt))
return CalleeCC == CallerF.getCallingConv();
assert((!Info.IsVarArg || CalleeCC == CallingConv::C) &&
"Unexpected variadic calling convention");
if (!doCallerAndCalleePassArgsTheSameWay(Info, MF, InArgs)) {
LLVM_DEBUG(
dbgs()
<< "... Caller and callee have incompatible calling conventions.\n");
return false;
}
if (!areCalleeOutgoingArgsTailCallable(Info, MF, OutArgs))
return false;
LLVM_DEBUG(
dbgs() << "... Call is eligible for tail call optimization.\n");
return true;
}
static unsigned getCallOpcode(const MachineFunction &CallerF, bool IsIndirect,
bool IsTailCall) {
if (!IsTailCall)
return IsIndirect ? getBLRCallOpcode(CallerF) : (unsigned)AArch64::BL;
if (!IsIndirect)
return AArch64::TCRETURNdi;
if (CallerF.getInfo<AArch64FunctionInfo>()->branchTargetEnforcement())
return AArch64::TCRETURNriBTI;
return AArch64::TCRETURNri;
}
static const uint32_t *
getMaskForArgs(SmallVectorImpl<AArch64CallLowering::ArgInfo> &OutArgs,
AArch64CallLowering::CallLoweringInfo &Info,
const AArch64RegisterInfo &TRI, MachineFunction &MF) {
const uint32_t *Mask;
if (!OutArgs.empty() && OutArgs[0].Flags[0].isReturned()) {
Mask = TRI.getThisReturnPreservedMask(MF, Info.CallConv);
if (!Mask) {
OutArgs[0].Flags[0].setReturned(false);
Mask = TRI.getCallPreservedMask(MF, Info.CallConv);
}
} else {
Mask = TRI.getCallPreservedMask(MF, Info.CallConv);
}
return Mask;
}
bool AArch64CallLowering::lowerTailCall(
MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info,
SmallVectorImpl<ArgInfo> &OutArgs) const {
MachineFunction &MF = MIRBuilder.getMF();
const Function &F = MF.getFunction();
MachineRegisterInfo &MRI = MF.getRegInfo();
const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>();
AArch64FunctionInfo *FuncInfo = MF.getInfo<AArch64FunctionInfo>();
bool IsSibCall = !MF.getTarget().Options.GuaranteedTailCallOpt &&
Info.CallConv != CallingConv::Tail &&
Info.CallConv != CallingConv::SwiftTail;
if (MF.getInfo<AArch64FunctionInfo>()->branchTargetEnforcement()) {
LLVM_DEBUG(
dbgs() << "Cannot lower indirect tail calls with BTI enabled yet.\n");
return false;
}
CallingConv::ID CalleeCC = Info.CallConv;
CCAssignFn *AssignFnFixed;
CCAssignFn *AssignFnVarArg;
std::tie(AssignFnFixed, AssignFnVarArg) = getAssignFnsForCC(CalleeCC, TLI);
MachineInstrBuilder CallSeqStart;
if (!IsSibCall)
CallSeqStart = MIRBuilder.buildInstr(AArch64::ADJCALLSTACKDOWN);
unsigned Opc = getCallOpcode(MF, Info.Callee.isReg(), true);
auto MIB = MIRBuilder.buildInstrNoInsert(Opc);
MIB.add(Info.Callee);
MIB.addImm(0);
const AArch64Subtarget &Subtarget = MF.getSubtarget<AArch64Subtarget>();
auto TRI = Subtarget.getRegisterInfo();
const uint32_t *Mask = TRI->getCallPreservedMask(MF, CalleeCC);
if (Subtarget.hasCustomCallingConv())
TRI->UpdateCustomCallPreservedMask(MF, &Mask);
MIB.addRegMask(Mask);
if (TRI->isAnyArgRegReserved(MF))
TRI->emitReservedArgRegCallError(MF);
int FPDiff = 0;
unsigned NumBytes = 0;
if (!IsSibCall) {
unsigned NumReusableBytes = FuncInfo->getBytesInStackArgArea();
SmallVector<CCValAssign, 16> OutLocs;
CCState OutInfo(CalleeCC, false, MF, OutLocs, F.getContext());
AArch64OutgoingValueAssigner CalleeAssigner(AssignFnFixed, AssignFnVarArg,
Subtarget, false);
if (!determineAssignments(CalleeAssigner, OutArgs, OutInfo))
return false;
NumBytes = alignTo(OutInfo.getNextStackOffset(), 16);
FPDiff = NumReusableBytes - NumBytes;
if (FPDiff < 0 && FuncInfo->getTailCallReservedStack() < (unsigned)-FPDiff)
FuncInfo->setTailCallReservedStack(-FPDiff);
assert(FPDiff % 16 == 0 && "unaligned stack on tail call");
}
const auto &Forwards = FuncInfo->getForwardedMustTailRegParms();
AArch64OutgoingValueAssigner Assigner(AssignFnFixed, AssignFnVarArg,
Subtarget, false);
OutgoingArgHandler Handler(MIRBuilder, MRI, MIB,
true, FPDiff);
if (!determineAndHandleAssignments(Handler, Assigner, OutArgs, MIRBuilder,
CalleeCC, Info.IsVarArg))
return false;
Mask = getMaskForArgs(OutArgs, Info, *TRI, MF);
if (Info.IsVarArg && Info.IsMustTailCall) {
for (const auto &F : Forwards) {
Register ForwardedReg = F.PReg;
if (any_of(MIB->uses(), [&ForwardedReg, &TRI](const MachineOperand &Use) {
if (!Use.isReg())
return false;
return TRI->regsOverlap(Use.getReg(), ForwardedReg);
}))
continue;
MIRBuilder.buildCopy(ForwardedReg, Register(F.VReg));
MIB.addReg(ForwardedReg, RegState::Implicit);
}
}
if (!IsSibCall) {
MIB->getOperand(1).setImm(FPDiff);
CallSeqStart.addImm(0).addImm(0);
MIRBuilder.buildInstr(AArch64::ADJCALLSTACKUP).addImm(0).addImm(0);
}
MIRBuilder.insertInstr(MIB);
if (MIB->getOperand(0).isReg())
constrainOperandRegClass(MF, *TRI, MRI, *MF.getSubtarget().getInstrInfo(),
*MF.getSubtarget().getRegBankInfo(), *MIB,
MIB->getDesc(), MIB->getOperand(0), 0);
MF.getFrameInfo().setHasTailCall();
Info.LoweredTailCall = true;
return true;
}
bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
CallLoweringInfo &Info) const {
MachineFunction &MF = MIRBuilder.getMF();
const Function &F = MF.getFunction();
MachineRegisterInfo &MRI = MF.getRegInfo();
auto &DL = F.getParent()->getDataLayout();
const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>();
SmallVector<ArgInfo, 8> OutArgs;
for (auto &OrigArg : Info.OrigArgs) {
splitToValueTypes(OrigArg, OutArgs, DL, Info.CallConv);
if (OrigArg.Ty->isIntegerTy(1)) {
ArgInfo &OutArg = OutArgs.back();
assert(OutArg.Regs.size() == 1 &&
MRI.getType(OutArg.Regs[0]).getSizeInBits() == 1 &&
"Unexpected registers used for i1 arg");
OutArg.Regs[0] =
MIRBuilder.buildZExt(LLT::scalar(8), OutArg.Regs[0]).getReg(0);
LLVMContext &Ctx = MF.getFunction().getContext();
OutArg.Ty = Type::getInt8Ty(Ctx);
}
}
SmallVector<ArgInfo, 8> InArgs;
if (!Info.OrigRet.Ty->isVoidTy())
splitToValueTypes(Info.OrigRet, InArgs, DL, Info.CallConv);
bool CanTailCallOpt =
isEligibleForTailCallOptimization(MIRBuilder, Info, InArgs, OutArgs);
if (Info.IsMustTailCall && !CanTailCallOpt) {
LLVM_DEBUG(dbgs() << "Failed to lower musttail call as tail call\n");
return false;
}
Info.IsTailCall = CanTailCallOpt;
if (CanTailCallOpt)
return lowerTailCall(MIRBuilder, Info, OutArgs);
CCAssignFn *AssignFnFixed;
CCAssignFn *AssignFnVarArg;
std::tie(AssignFnFixed, AssignFnVarArg) =
getAssignFnsForCC(Info.CallConv, TLI);
MachineInstrBuilder CallSeqStart;
CallSeqStart = MIRBuilder.buildInstr(AArch64::ADJCALLSTACKDOWN);
const AArch64Subtarget &Subtarget = MF.getSubtarget<AArch64Subtarget>();
unsigned Opc = 0;
if (Info.CB && objcarc::hasAttachedCallOpBundle(Info.CB))
Opc = AArch64::BLR_RVMARKER;
else if (Info.CB &&
Info.CB->getAttributes().hasFnAttr(Attribute::ReturnsTwice) &&
!Subtarget.noBTIAtReturnTwice() &&
MF.getInfo<AArch64FunctionInfo>()->branchTargetEnforcement())
Opc = AArch64::BLR_BTI;
else
Opc = getCallOpcode(MF, Info.Callee.isReg(), false);
auto MIB = MIRBuilder.buildInstrNoInsert(Opc);
unsigned CalleeOpNo = 0;
if (Opc == AArch64::BLR_RVMARKER) {
Function *ARCFn = *objcarc::getAttachedARCFunction(Info.CB);
MIB.addGlobalAddress(ARCFn);
++CalleeOpNo;
}
MIB.add(Info.Callee);
const uint32_t *Mask;
const auto *TRI = Subtarget.getRegisterInfo();
AArch64OutgoingValueAssigner Assigner(AssignFnFixed, AssignFnVarArg,
Subtarget, false);
OutgoingArgHandler Handler(MIRBuilder, MRI, MIB, false);
if (!determineAndHandleAssignments(Handler, Assigner, OutArgs, MIRBuilder,
Info.CallConv, Info.IsVarArg))
return false;
Mask = getMaskForArgs(OutArgs, Info, *TRI, MF);
if (MF.getSubtarget<AArch64Subtarget>().hasCustomCallingConv())
TRI->UpdateCustomCallPreservedMask(MF, &Mask);
MIB.addRegMask(Mask);
if (TRI->isAnyArgRegReserved(MF))
TRI->emitReservedArgRegCallError(MF);
MIRBuilder.insertInstr(MIB);
if (MIB->getOperand(CalleeOpNo).isReg())
constrainOperandRegClass(MF, *TRI, MRI, *Subtarget.getInstrInfo(),
*Subtarget.getRegBankInfo(), *MIB, MIB->getDesc(),
MIB->getOperand(CalleeOpNo), CalleeOpNo);
if (Info.CanLowerReturn && !Info.OrigRet.Ty->isVoidTy()) {
CCAssignFn *RetAssignFn = TLI.CCAssignFnForReturn(Info.CallConv);
CallReturnHandler Handler(MIRBuilder, MRI, MIB);
bool UsingReturnedArg =
!OutArgs.empty() && OutArgs[0].Flags[0].isReturned();
AArch64OutgoingValueAssigner Assigner(RetAssignFn, RetAssignFn, Subtarget,
false);
ReturnedArgCallReturnHandler ReturnedArgHandler(MIRBuilder, MRI, MIB);
if (!determineAndHandleAssignments(
UsingReturnedArg ? ReturnedArgHandler : Handler, Assigner, InArgs,
MIRBuilder, Info.CallConv, Info.IsVarArg,
UsingReturnedArg ? makeArrayRef(OutArgs[0].Regs) : None))
return false;
}
if (Info.SwiftErrorVReg) {
MIB.addDef(AArch64::X21, RegState::Implicit);
MIRBuilder.buildCopy(Info.SwiftErrorVReg, Register(AArch64::X21));
}
uint64_t CalleePopBytes =
doesCalleeRestoreStack(Info.CallConv,
MF.getTarget().Options.GuaranteedTailCallOpt)
? alignTo(Assigner.StackOffset, 16)
: 0;
CallSeqStart.addImm(Assigner.StackOffset).addImm(0);
MIRBuilder.buildInstr(AArch64::ADJCALLSTACKUP)
.addImm(Assigner.StackOffset)
.addImm(CalleePopBytes);
if (!Info.CanLowerReturn) {
insertSRetLoads(MIRBuilder, Info.OrigRet.Ty, Info.OrigRet.Regs,
Info.DemoteRegister, Info.DemoteStackIndex);
}
return true;
}
bool AArch64CallLowering::isTypeIsValidForThisReturn(EVT Ty) const {
return Ty.getSizeInBits() == 64;
}