From a466503cd36027e2b97aab8131479725706eda7d Mon Sep 17 00:00:00 2001 From: Momchil Velikov Date: Tue, 14 Nov 2017 10:36:52 +0000 Subject: [PATCH] [ARM] Fix incorrect conversion of a tail call to an ordinary call When we emit a tail call for Armv8-M, but then discover that the caller needs to save/restore `LR`, we convert the tail call to an ordinary one, since restoring `LR` takes extra instructions, which may negate the benefits of the tail call. If the callee, however, takes stack arguments, this conversion is incorrect, since nothing has been done to pass the stack arguments. Thus the patch reverts https://reviews.llvm.org/rL294000 Also, we improve the instruction sequence for popping `LR` in the case when we couldn't immediately find a scratch low register, but we can use as a temporary one of the callee-saved low registers and restore `LR` before popping other callee-saves. Differential Revision: https://reviews.llvm.org/D39599 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@318143 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/ARM/ARMSubtarget.cpp | 4 +- lib/Target/ARM/Thumb1FrameLowering.cpp | 106 +++++++++++++++++++--------- test/CodeGen/ARM/thumb1_return_sequence.ll | 41 ++++------- test/CodeGen/ARM/v8m-tail-call.ll | 58 ++++++++++----- test/CodeGen/Thumb/thumb-shrink-wrapping.ll | 9 ++- 5 files changed, 133 insertions(+), 85 deletions(-) diff --git a/lib/Target/ARM/ARMSubtarget.cpp b/lib/Target/ARM/ARMSubtarget.cpp index 7e107e4f8fd..e3855cc9a91 100644 --- a/lib/Target/ARM/ARMSubtarget.cpp +++ b/lib/Target/ARM/ARMSubtarget.cpp @@ -221,8 +221,8 @@ void ARMSubtarget::initSubtargetFeatures(StringRef CPU, StringRef FS) { // baseline, since the LDM/POP instruction on Thumb doesn't take LR. This // means if we need to reload LR, it takes extra instructions, which outweighs // the value of the tail call; but here we don't know yet whether LR is going - // to be used. We generate the tail call here and turn it back into CALL/RET - // in emitEpilogue if LR is used. + // to be used. We take the optimistic approach of generating the tail call and + // perhaps taking a hit if we need to restore the LR. // Thumb1 PIC calls to external symbols use BX, so they can be tail calls, // but we need to make sure there are enough registers; the only valid diff --git a/lib/Target/ARM/Thumb1FrameLowering.cpp b/lib/Target/ARM/Thumb1FrameLowering.cpp index f6996f098c0..31aae92ec59 100644 --- a/lib/Target/ARM/Thumb1FrameLowering.cpp +++ b/lib/Target/ARM/Thumb1FrameLowering.cpp @@ -512,6 +512,26 @@ bool Thumb1FrameLowering::needPopSpecialFixUp(const MachineFunction &MF) const { return false; } +static void findTemporariesForLR(const BitVector &GPRsNoLRSP, + const BitVector &PopFriendly, + const LivePhysRegs &UsedRegs, unsigned &PopReg, + unsigned &TmpReg) { + PopReg = TmpReg = 0; + for (auto Reg : GPRsNoLRSP.set_bits()) { + if (!UsedRegs.contains(Reg)) { + // Remember the first pop-friendly register and exit. + if (PopFriendly.test(Reg)) { + PopReg = Reg; + TmpReg = 0; + break; + } + // Otherwise, remember that the register will be available to + // save a pop-friendly register. + TmpReg = Reg; + } + } +} + bool Thumb1FrameLowering::emitPopSpecialFixUp(MachineBasicBlock &MBB, bool DoIt) const { MachineFunction &MF = *MBB.getParent(); @@ -600,17 +620,19 @@ bool Thumb1FrameLowering::emitPopSpecialFixUp(MachineBasicBlock &MBB, GPRsNoLRSP.reset(ARM::LR); GPRsNoLRSP.reset(ARM::SP); GPRsNoLRSP.reset(ARM::PC); - for (unsigned Register : GPRsNoLRSP.set_bits()) { - if (!UsedRegs.contains(Register)) { - // Remember the first pop-friendly register and exit. - if (PopFriendly.test(Register)) { - PopReg = Register; - TemporaryReg = 0; - break; - } - // Otherwise, remember that the register will be available to - // save a pop-friendly register. - TemporaryReg = Register; + findTemporariesForLR(GPRsNoLRSP, PopFriendly, UsedRegs, PopReg, TemporaryReg); + + // If we couldn't find a pop-friendly register, restore LR before popping the + // other callee-saved registers, so we can use one of them as a temporary. + bool UseLDRSP = false; + if (!PopReg && MBBI != MBB.begin()) { + auto PrevMBBI = MBBI; + PrevMBBI--; + if (PrevMBBI->getOpcode() == ARM::tPOP) { + MBBI = PrevMBBI; + UsedRegs.stepBackward(*MBBI); + findTemporariesForLR(GPRsNoLRSP, PopFriendly, UsedRegs, PopReg, TemporaryReg); + UseLDRSP = true; } } @@ -619,6 +641,26 @@ bool Thumb1FrameLowering::emitPopSpecialFixUp(MachineBasicBlock &MBB, assert((PopReg || TemporaryReg) && "Cannot get LR"); + if (UseLDRSP) { + assert(PopReg && "Do not know how to get LR"); + // Load the LR via LDR tmp, [SP, #off] + BuildMI(MBB, MBBI, dl, TII.get(ARM::tLDRspi)) + .addReg(PopReg, RegState::Define) + .addReg(ARM::SP) + .addImm(MBBI->getNumOperands() - 3) + .add(predOps(ARMCC::AL)); + // Move from the temporary register to the LR. + BuildMI(MBB, MBBI, dl, TII.get(ARM::tMOVr)) + .addReg(ARM::LR, RegState::Define) + .addReg(PopReg, RegState::Kill) + .add(predOps(ARMCC::AL)); + // Advance past the pop instruction. + MBBI++; + // Increment the SP. + emitSPUpdate(MBB, MBBI, TII, dl, *RegInfo, ArgRegsSaveSize + 4); + return true; + } + if (TemporaryReg) { assert(!PopReg && "Unnecessary MOV is about to be inserted"); PopReg = PopFriendly.find_first(); @@ -911,33 +953,29 @@ restoreCalleeSavedRegisters(MachineBasicBlock &MBB, if (Reg == ARM::LR) { Info.setRestored(false); - if (MBB.succ_empty()) { - // Special epilogue for vararg functions. See emitEpilogue - if (isVarArg) - continue; - // ARMv4T requires BX, see emitEpilogue - if (!STI.hasV5TOps()) - continue; - // Tailcall optimization failed; change TCRETURN to a tBL - if (MI->getOpcode() == ARM::TCRETURNdi || - MI->getOpcode() == ARM::TCRETURNri) { - unsigned Opcode = MI->getOpcode() == ARM::TCRETURNdi - ? ARM::tBL : ARM::tBLXr; - MachineInstrBuilder BL = BuildMI(MF, DL, TII.get(Opcode)); - BL.add(predOps(ARMCC::AL)); - BL.add(MI->getOperand(0)); - MBB.insert(MI, &*BL); - } - Reg = ARM::PC; - (*MIB).setDesc(TII.get(ARM::tPOP_RET)); - if (MI != MBB.end()) - MIB.copyImplicitOps(*MI); - MI = MBB.erase(MI); - } else + if (!MBB.succ_empty() || + MI->getOpcode() == ARM::TCRETURNdi || + MI->getOpcode() == ARM::TCRETURNri) // LR may only be popped into PC, as part of return sequence. // If this isn't the return sequence, we'll need emitPopSpecialFixUp // to restore LR the hard way. + // FIXME: if we don't pass any stack arguments it would be actually + // advantageous *and* correct to do the conversion to an ordinary call + // instruction here. continue; + // Special epilogue for vararg functions. See emitEpilogue + if (isVarArg) + continue; + // ARMv4T requires BX, see emitEpilogue + if (!STI.hasV5TOps()) + continue; + + // Pop LR into PC. + Reg = ARM::PC; + (*MIB).setDesc(TII.get(ARM::tPOP_RET)); + if (MI != MBB.end()) + MIB.copyImplicitOps(*MI); + MI = MBB.erase(MI); } MIB.addReg(Reg, getDefRegState(true)); NeedsPop = true; diff --git a/test/CodeGen/ARM/thumb1_return_sequence.ll b/test/CodeGen/ARM/thumb1_return_sequence.ll index c54712efb39..c8ab6b5042e 100644 --- a/test/CodeGen/ARM/thumb1_return_sequence.ll +++ b/test/CodeGen/ARM/thumb1_return_sequence.ll @@ -25,23 +25,14 @@ entry: ; -------- ; Stack realignment means sp is restored from frame pointer ; CHECK-V4T: mov sp +; CHECK-V4T-NEXT: ldr [[POP:r[4567]]], [sp, #{{.*}}] +; CHECK-V4T-NEXT: mov lr, [[POP]] ; CHECK-V4T-NEXT: pop {[[SAVED]]} +; CHECK-V4T-NEXT add sp, sp, #4 ; The ISA for v4 does not support pop pc, so make sure we do not emit ; one even when we do not need to update SP. ; CHECK-V4T-NOT: pop {pc} -; We may only use lo register to pop, but in that case, all the scratch -; ones are used. -; r12 is the only register we are allowed to clobber for AAPCS. -; Use it to save a lo register. -; CHECK-V4T-NEXT: mov [[TEMP_REG:r12]], [[POP_REG:r[0-7]]] -; Pop the value of LR. -; CHECK-V4T-NEXT: pop {[[POP_REG]]} -; Copy the value of LR in the right register. -; CHECK-V4T-NEXT: mov lr, [[POP_REG]] -; Restore the value that was in the register we used to pop the value of LR. -; CHECK-V4T-NEXT: mov [[POP_REG]], [[TEMP_REG]] -; Return. -; CHECK-V4T-NEXT: bx lr +; CHECK-V4T: bx lr ; CHECK-V5T: pop {[[SAVED]], pc} } @@ -66,22 +57,18 @@ entry: ; Epilogue ; -------- -; CHECK-V4T: pop {[[SAVED]]} -; CHECK-V4T-NEXT: mov r12, [[POP_REG:r[0-7]]] -; CHECK-V4T-NEXT: pop {[[POP_REG]]} -; CHECK-V4T-NEXT: add sp, -; CHECK-V4T-NEXT: mov lr, [[POP_REG]] -; CHECK-V4T-NEXT: mov [[POP_REG]], r12 -; CHECK-V4T: bx lr +; CHECK-V4T: ldr [[POP:r[4567]]], [sp, #{{.*}}] +; CHECK-V4T-NEXT: mov lr, [[POP]] +; CHECK-V4T-NEXT: pop {[[SAVED]]} +; CHECK-V4T-NEXT: add sp, #16 +; CHECK-V4T-NEXT: bx lr ; CHECK-V5T: lsls r4 ; CHECK-V5T-NEXT: mov sp, r4 -; CHECK-V5T: pop {[[SAVED]]} -; CHECK-V5T-NEXT: mov r12, [[POP_REG:r[0-7]]] -; CHECK-V5T-NEXT: pop {[[POP_REG]]} -; CHECK-V5T-NEXT: add sp, -; CHECK-V5T-NEXT: mov lr, [[POP_REG]] -; CHECK-V5T-NEXT: mov [[POP_REG]], r12 -; CHECK-V5T-NEXT: bx lr +; CHECK-V5T: ldr [[POP:r[4567]]], [sp, #{{.*}}] +; CHECK-V5T-NEXT: mov lr, [[POP]] +; CHECK-V5T-NEXT: pop {[[SAVED]]} +; CHECK-V5T-NEXT: add sp, #16 +; CHECK-V5T-NEXT: bx lr } ; CHECK-V4T-LABEL: simpleframe diff --git a/test/CodeGen/ARM/v8m-tail-call.ll b/test/CodeGen/ARM/v8m-tail-call.ll index 2c2c795838f..74a6c20ac54 100644 --- a/test/CodeGen/ARM/v8m-tail-call.ll +++ b/test/CodeGen/ARM/v8m-tail-call.ll @@ -1,23 +1,47 @@ ; RUN: llc %s -o - -mtriple=thumbv8m.base | FileCheck %s -define void @test() { -; CHECK-LABEL: test: -entry: - %call = tail call i32 @foo() - %tail = tail call i32 @foo() - ret void -; CHECK: bl foo -; CHECK: bl foo -; CHECK-NOT: b foo +declare i32 @g(...) + +declare i32 @h0(i32, i32, i32, i32) +define hidden i32 @f0() { + %1 = tail call i32 bitcast (i32 (...)* @g to i32 ()*)() + %2 = tail call i32 @h0(i32 %1, i32 1, i32 2, i32 3) + ret i32 %2 +; CHECK-LABEL: f0 +; CHECK: ldr [[POP:r[4567]]], [sp +; CHECK-NEXT: mov lr, [[POP]] +; CHECK-NEXT: pop {{.*}}[[POP]] +; CHECK-NEXT: add sp, #4 +; CHECK-NEXT: b h0 } -define void @test2() { -; CHECK-LABEL: test2: -entry: - %tail = tail call i32 @foo() - ret void -; CHECK: b foo -; CHECK-NOT: bl foo +declare i32 @h1(i32) +define hidden i32 @f1() { + %1 = tail call i32 bitcast (i32 (...)* @g to i32 ()*)() + %2 = tail call i32 @h1(i32 %1) + ret i32 %2 +; CHECK-LABEL: f1 +; CHECK: pop {r7} +; CHECK: pop {r1} +; CHECK: mov lr, r1 +; CHECK: b h1 } -declare i32 @foo() +declare i32 @h2(i32, i32, i32, i32, i32) +define hidden i32 @f2(i32, i32, i32, i32, i32) { + %6 = tail call i32 bitcast (i32 (...)* @g to i32 ()*)() + %7 = icmp eq i32 %6, 0 + br i1 %7, label %10, label %8 + + %9 = tail call i32 @h2(i32 %6, i32 %1, i32 %2, i32 %3, i32 %4) + br label %10 + + %11 = phi i32 [ %9, %8 ], [ -1, %5 ] + ret i32 %11 +; CHECK-LABEL: f2 +; CHECK: ldr [[POP:r[4567]]], [sp +; CHECK-NEXT: mov lr, [[POP]] +; CHECK-NEXT: pop {{.*}}[[POP]] +; CHECK-NEXT: add sp, #4 +; CHECK-NEXT: b h2 +} diff --git a/test/CodeGen/Thumb/thumb-shrink-wrapping.ll b/test/CodeGen/Thumb/thumb-shrink-wrapping.ll index c571e351a1e..6bb5c81485b 100644 --- a/test/CodeGen/Thumb/thumb-shrink-wrapping.ll +++ b/test/CodeGen/Thumb/thumb-shrink-wrapping.ll @@ -647,11 +647,10 @@ define i1 @beq_to_bx(i32* %y, i32 %head) { ; ENABLE: push {r4, lr} ; CHECK: tst r3, r4 -; ENABLE-NEXT: pop {r4} -; ENABLE-NEXT: mov r12, r{{.*}} -; ENABLE-NEXT: pop {r0} -; ENABLE-NEXT: mov lr, r0 -; ENABLE-NEXT: mov r0, r12 +; ENABLE-NEXT: ldr [[POP:r[4567]]], [sp, #8] +; ENABLE-NEXT: mov lr, [[POP]] +; ENABLE-NEXT: pop {[[POP]]} +; ENABLE-NEXT: add sp, #4 ; CHECK-NEXT: beq [[EXIT_LABEL]] ; CHECK: str r1, [r2] -- 2.11.0