diff options
author | Jessica Paquette <jpaquette@apple.com> | 2019-09-05 20:18:34 +0000 |
---|---|---|
committer | Jessica Paquette <jpaquette@apple.com> | 2019-09-05 20:18:34 +0000 |
commit | 20e866709882dbad73d9cbb99f056cd6ea113498 (patch) | |
tree | 95c22040ef9077d06762aeb46975ae32732913b1 /llvm/lib | |
parent | 31817731167135870259ef1e7387746345b96a2f (diff) | |
download | bcm5719-llvm-20e866709882dbad73d9cbb99f056cd6ea113498.tar.gz bcm5719-llvm-20e866709882dbad73d9cbb99f056cd6ea113498.zip |
Recommit "[AArch64][GlobalISel] Teach AArch64CallLowering to handle basic sibling calls"
Recommit basic sibling call lowering (https://reviews.llvm.org/D67189)
The issue was that if you have a return type other than void, call lowering
will emit COPYs to get the return value after the call.
Disallow sibling calls other than ones that return void for now. Also
proactively disable swifterror tail calls for now, since there's a similar issue
with COPYs there.
Update call-translator-tail-call.ll to include test cases for each of these
things.
llvm-svn: 371114
Diffstat (limited to 'llvm/lib')
-rw-r--r-- | llvm/lib/CodeGen/GlobalISel/CallLowering.cpp | 4 | ||||
-rw-r--r-- | llvm/lib/Target/AArch64/AArch64CallLowering.cpp | 193 | ||||
-rw-r--r-- | llvm/lib/Target/AArch64/AArch64CallLowering.h | 4 |
3 files changed, 193 insertions, 8 deletions
diff --git a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp index d433155160b..1c8e4541817 100644 --- a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp @@ -64,7 +64,9 @@ bool CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, ImmutableCallSite CS, Info.CallConv = CS.getCallingConv(); Info.SwiftErrorVReg = SwiftErrorVReg; Info.IsMustTailCall = CS.isMustTailCall(); - + Info.IsTailCall = CS.isTailCall() && + isInTailCallPosition(CS, MIRBuilder.getMF().getTarget()); + Info.IsVarArg = CS.getFunctionType()->isVarArg(); return lowerCall(MIRBuilder, Info); } diff --git a/llvm/lib/Target/AArch64/AArch64CallLowering.cpp b/llvm/lib/Target/AArch64/AArch64CallLowering.cpp index 9ace33b3985..e97b329985a 100644 --- a/llvm/lib/Target/AArch64/AArch64CallLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64CallLowering.cpp @@ -233,6 +233,17 @@ bool AArch64CallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val, ArrayRef<Register> VRegs, Register SwiftErrorVReg) const { + + // Check if a tail call was lowered in this block. If so, we already handled + // the terminator. + MachineFunction &MF = MIRBuilder.getMF(); + if (MF.getFrameInfo().hasTailCall()) { + MachineBasicBlock &MBB = MIRBuilder.getMBB(); + auto FirstTerm = MBB.getFirstTerminator(); + if (FirstTerm != MBB.end() && FirstTerm->isCall()) + return true; + } + auto MIB = MIRBuilder.buildInstrNoInsert(AArch64::RET_ReallyLR); assert(((Val && !VRegs.empty()) || (!Val && VRegs.empty())) && "Return value without a vreg"); @@ -403,6 +414,146 @@ bool AArch64CallLowering::lowerFormalArguments( return true; } +/// Return true if the calling convention is one that we can guarantee TCO for. +static bool canGuaranteeTCO(CallingConv::ID CC) { + return CC == CallingConv::Fast; +} + +/// Return true if we might ever do TCO for calls with this calling convention. +static bool mayTailCallThisCC(CallingConv::ID CC) { + switch (CC) { + case CallingConv::C: + case CallingConv::PreserveMost: + case CallingConv::Swift: + return true; + default: + return canGuaranteeTCO(CC); + } +} + +bool AArch64CallLowering::isEligibleForTailCallOptimization( + MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info) const { + CallingConv::ID CalleeCC = Info.CallConv; + MachineFunction &MF = MIRBuilder.getMF(); + const Function &CallerF = MF.getFunction(); + CallingConv::ID CallerCC = CallerF.getCallingConv(); + bool CCMatch = CallerCC == CalleeCC; + + LLVM_DEBUG(dbgs() << "Attempting to lower call as tail call\n"); + + if (Info.SwiftErrorVReg) { + // TODO: We should handle this. + // Note that this is also handled by the check for no outgoing arguments. + // Proactively disabling this though, because the swifterror handling in + // lowerCall inserts a COPY *after* the location of the call. + LLVM_DEBUG(dbgs() << "... Cannot handle tail calls with swifterror yet.\n"); + return false; + } + + if (!Info.OrigRet.Ty->isVoidTy()) { + // TODO: lowerCall will insert COPYs to handle the call's return value. + // This needs some refactoring to avoid this with tail call returns. For + // now, just don't handle that case. + LLVM_DEBUG(dbgs() << "... Cannot handle non-void return types yet.\n"); + return false; + } + + if (!mayTailCallThisCC(CalleeCC)) { + LLVM_DEBUG(dbgs() << "... Calling convention cannot be tail called.\n"); + return false; + } + + if (Info.IsVarArg) { + LLVM_DEBUG(dbgs() << "... Tail calling varargs not supported yet.\n"); + return false; + } + + // Byval parameters hand the function a pointer directly into the stack area + // we want to reuse during a tail call. Working around this *is* possible (see + // X86). + // + // FIXME: In AArch64ISelLowering, this isn't worked around. Can/should we try + // it? + // + // On Windows, "inreg" attributes signify non-aggregate indirect returns. + // In this case, it is necessary to save/restore X0 in the callee. Tail + // call opt interferes with this. So we disable tail call opt when the + // caller has an argument with "inreg" attribute. + // + // FIXME: Check whether the callee also has an "inreg" argument. + if (any_of(CallerF.args(), [](const Argument &A) { + return A.hasByValAttr() || A.hasInRegAttr(); + })) { + LLVM_DEBUG(dbgs() << "... Cannot tail call from callers with byval or " + "inreg arguments.\n"); + return false; + } + + // Externally-defined functions with weak linkage should not be + // tail-called on AArch64 when the OS does not support dynamic + // pre-emption of symbols, as the AAELF spec requires normal calls + // to undefined weak functions to be replaced with a NOP or jump to the + // next instruction. The behaviour of branch instructions in this + // situation (as used for tail calls) is implementation-defined, so we + // cannot rely on the linker replacing the tail call with a return. + 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 we have -tailcallopt and matching CCs, at this point, we could return + // true. However, we don't have full tail call support yet. So, continue + // checking. We want to emit a sibling call. + + // I want anyone implementing a new calling convention to think long and hard + // about this assert. + assert((!Info.IsVarArg || CalleeCC == CallingConv::C) && + "Unexpected variadic calling convention"); + + // For now, only support the case where the calling conventions match. + if (!CCMatch) { + LLVM_DEBUG( + dbgs() + << "... Cannot tail call with mismatched calling conventions yet.\n"); + return false; + } + + // For now, only handle callees that take no arguments. + if (!Info.OrigArgs.empty()) { + LLVM_DEBUG( + dbgs() + << "... Cannot tail call callees with outgoing arguments yet.\n"); + return false; + } + + LLVM_DEBUG( + dbgs() << "... Call is eligible for tail call optimization.\n"); + return true; +} + +static unsigned getCallOpcode(const Function &CallerF, bool IsIndirect, + bool IsTailCall) { + if (!IsTailCall) + return IsIndirect ? AArch64::BLR : AArch64::BL; + + if (!IsIndirect) + return AArch64::TCRETURNdi; + + // When BTI is enabled, we need to use TCRETURNriBTI to make sure that we use + // x16 or x17. + if (CallerF.hasFnAttribute("branch-target-enforcement")) + return AArch64::TCRETURNriBTI; + + return AArch64::TCRETURNri; +} + bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info) const { MachineFunction &MF = MIRBuilder.getMF(); @@ -411,6 +562,7 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, auto &DL = F.getParent()->getDataLayout(); if (Info.IsMustTailCall) { + // TODO: Until we lower all tail calls, we should fall back on this. LLVM_DEBUG(dbgs() << "Cannot lower musttail calls yet.\n"); return false; } @@ -423,6 +575,11 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, SplitArgs.back().Flags[0].setZExt(); } + bool IsSibCall = + Info.IsTailCall && isEligibleForTailCallOptimization(MIRBuilder, Info); + if (IsSibCall) + MF.getFrameInfo().setHasTailCall(); + // Find out which ABI gets to decide where things go. const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>(); CCAssignFn *AssignFnFixed = @@ -430,14 +587,33 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, CCAssignFn *AssignFnVarArg = TLI.CCAssignFnForCall(Info.CallConv, /*IsVarArg=*/true); - auto CallSeqStart = MIRBuilder.buildInstr(AArch64::ADJCALLSTACKDOWN); + // If we have a sibling call, then we don't have to adjust the stack. + // Otherwise, we need to adjust it. + MachineInstrBuilder CallSeqStart; + if (!IsSibCall) + CallSeqStart = MIRBuilder.buildInstr(AArch64::ADJCALLSTACKDOWN); // Create a temporarily-floating call instruction so we can add the implicit // uses of arg registers. - auto MIB = MIRBuilder.buildInstrNoInsert(Info.Callee.isReg() ? AArch64::BLR - : AArch64::BL); + unsigned Opc = getCallOpcode(F, Info.Callee.isReg(), IsSibCall); + + // TODO: Right now, regbankselect doesn't know how to handle the rtcGPR64 + // register class. Until we can do that, we should fall back here. + if (Opc == AArch64::TCRETURNriBTI) { + LLVM_DEBUG( + dbgs() << "Cannot lower indirect tail calls with BTI enabled yet.\n"); + return false; + } + + auto MIB = MIRBuilder.buildInstrNoInsert(Opc); MIB.add(Info.Callee); + // Add the byte offset for the tail call. We only have sibling calls, so this + // is always 0. + // TODO: Handle tail calls where we will have a different value here. + if (IsSibCall) + MIB.addImm(0); + // Tell the call which registers are clobbered. auto TRI = MF.getSubtarget<AArch64Subtarget>().getRegisterInfo(); const uint32_t *Mask = TRI->getCallPreservedMask(MF, F.getCallingConv()); @@ -486,10 +662,13 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, MIRBuilder.buildCopy(Info.SwiftErrorVReg, Register(AArch64::X21)); } - CallSeqStart.addImm(Handler.StackSize).addImm(0); - MIRBuilder.buildInstr(AArch64::ADJCALLSTACKUP) - .addImm(Handler.StackSize) - .addImm(0); + if (!IsSibCall) { + // If we aren't sibcalling, we need to move the stack. + CallSeqStart.addImm(Handler.StackSize).addImm(0); + MIRBuilder.buildInstr(AArch64::ADJCALLSTACKUP) + .addImm(Handler.StackSize) + .addImm(0); + } return true; } diff --git a/llvm/lib/Target/AArch64/AArch64CallLowering.h b/llvm/lib/Target/AArch64/AArch64CallLowering.h index 5da72286a5d..0bf250b85a3 100644 --- a/llvm/lib/Target/AArch64/AArch64CallLowering.h +++ b/llvm/lib/Target/AArch64/AArch64CallLowering.h @@ -43,6 +43,10 @@ public: bool lowerCall(MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info) const override; + /// Returns true if the call can be lowered as a tail call. + bool isEligibleForTailCallOptimization(MachineIRBuilder &MIRBuilder, + CallLoweringInfo &Info) const; + bool supportSwiftError() const override { return true; } private: |