// function has the optnone attribute.
bool EnableOpts = false;
+ /// True when the block contains a tail call. This allows the IRTranslator to
+ /// stop translating such blocks early.
+ bool HasTailCall = false;
+
/// Switch analysis and optimization.
class GISelSwitchLowering : public SwitchCG::SwitchLowering {
public:
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/StackProtector.h"
#include "llvm/CodeGen/TargetFrameLowering.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
CLI->lowerCall(MIRBuilder, CS, Res, Args, SwiftErrorVReg,
[&]() { return getOrCreateVReg(*CS.getCalledValue()); });
+ // Check if we just inserted a tail call.
+ if (Success) {
+ assert(!HasTailCall && "Can't tail call return twice from block?");
+ const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo();
+ HasTailCall = TII->isTailCall(*std::prev(MIRBuilder.getInsertPt()));
+ }
+
return Success;
}
// Set the insertion point of all the following translations to
// the end of this basic block.
CurBuilder->setMBB(MBB);
-
+ HasTailCall = false;
for (const Instruction &Inst : *BB) {
+ // If we translated a tail call in the last step, then we know
+ // everything after the call is either a return, or something that is
+ // handled by the call itself. (E.g. a lifetime marker or assume
+ // intrinsic.) In this case, we should stop translating the block and
+ // move on.
+ if (HasTailCall)
+ break;
#ifndef NDEBUG
Verifier.setCurrentInst(&Inst);
#endif // ifndef NDEBUG
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");
tail call fastcc void @fast_fn()
ret void
}
+
+; Verify that lifetime markers and llvm.assume don't impact tail calling.
+declare void @llvm.assume(i1)
+define void @test_assume() local_unnamed_addr {
+ ; COMMON-LABEL: name: test_assume
+ ; COMMON: bb.1.entry:
+ ; COMMON: TCRETURNdi @nonvoid_ret, 0, csr_aarch64_aapcs, implicit $sp
+entry:
+ %x = tail call i32 @nonvoid_ret()
+ %y = icmp ne i32 %x, 0
+ tail call void @llvm.assume(i1 %y)
+ ret void
+}
+
+declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
+declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
+define void @test_lifetime() local_unnamed_addr {
+ ; COMMON-LABEL: name: test_lifetime
+ ; COMMON: bb.1.entry:
+ ; COMMON: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.t
+ ; COMMON: LIFETIME_START %stack.0.t
+ ; COMMON: TCRETURNdi @nonvoid_ret, 0, csr_aarch64_aapcs, implicit $sp
+entry:
+ %t = alloca i8, align 1
+ call void @llvm.lifetime.start.p0i8(i64 1, i8* %t)
+ %x = tail call i32 @nonvoid_ret()
+ %y = icmp ne i32 %x, 0
+ tail call void @llvm.lifetime.end.p0i8(i64 1, i8* %t)
+ ret void
+}