diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp --- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp @@ -5560,8 +5560,32 @@ /// | Frame overhead in Bytes | 0 | 0 | /// | Stack fixup required | No | No | /// +-------------------------+--------+-----+ +/// +/// \p MachineOutlinerNoLRSave implies that the function should be called using +/// a BL instruction, but doesn't require LR to be saved and restored. This +/// happens when LR is known to be dead. +/// +/// That is, +/// +/// I1 OUTLINED_FUNCTION: +/// I2 --> BL OUTLINED_FUNCTION I1 +/// I3 I2 +/// I3 +/// BX LR +/// +/// +-------------------------+--------+-----+ +/// | | Thumb2 | ARM | +/// +-------------------------+--------+-----+ +/// | Call overhead in Bytes | 4 | 4 | +/// | Frame overhead in Bytes | 4 | 4 | +/// | Stack fixup required | No | No | +/// +-------------------------+--------+-----+ -enum MachineOutlinerClass { MachineOutlinerTailCall, MachineOutlinerThunk }; +enum MachineOutlinerClass { + MachineOutlinerTailCall, + MachineOutlinerThunk, + MachineOutlinerNoLRSave +}; enum MachineOutlinerMBBFlags { LRUnavailableSomewhere = 0x2, @@ -5574,12 +5598,16 @@ const int FrameTailCall; const int CallThunk; const int FrameThunk; + const int CallNoLRSave; + const int FrameNoLRSave; OutlinerCosts(const ARMSubtarget &target) : CallTailCall(target.isThumb() ? 4 : 4), FrameTailCall(target.isThumb() ? 0 : 0), CallThunk(target.isThumb() ? 4 : 4), - FrameThunk(target.isThumb() ? 0 : 0) {} + FrameThunk(target.isThumb() ? 0 : 0), + CallNoLRSave(target.isThumb() ? 4 : 4), + FrameNoLRSave(target.isThumb() ? 4 : 4) {} }; outliner::OutlinedFunction ARMBaseInstrInfo::getOutliningCandidateInfo( @@ -5665,8 +5693,29 @@ FrameID = MachineOutlinerThunk; NumBytesToCreateFrame = Costs.FrameThunk; SetCandidateCallInfo(MachineOutlinerThunk, Costs.CallThunk); - } else - return outliner::OutlinedFunction(); + } else { + // We need to decide how to emit calls + frames. We can always emit the same + // frame if we don't need to save to the stack. + unsigned NumBytesNoStackCalls = 0; + std::vector CandidatesWithoutStackFixups; + + for (outliner::Candidate &C : RepeatedSequenceLocs) { + C.initLRU(TRI); + + // Is LR available? If so, we don't need a save. + if (C.LRU.available(ARM::LR)) { + FrameID = MachineOutlinerNoLRSave; + NumBytesNoStackCalls += Costs.CallNoLRSave; + C.setCallInfo(MachineOutlinerNoLRSave, Costs.CallNoLRSave); + CandidatesWithoutStackFixups.push_back(C); + } + } + + if (!CandidatesWithoutStackFixups.empty()) { + RepeatedSequenceLocs = CandidatesWithoutStackFixups; + } else + return outliner::OutlinedFunction(); + } return outliner::OutlinedFunction(RepeatedSequenceLocs, SequenceSize, NumBytesToCreateFrame, FrameID); @@ -5820,6 +5869,25 @@ if (MI.modifiesRegister(ARM::LR, TRI) || MI.modifiesRegister(ARM::PC, TRI)) return outliner::InstrType::Illegal; + // Does this use the stack? + if (MI.modifiesRegister(ARM::SP, TRI) || MI.readsRegister(ARM::SP, TRI)) { + // True if there is no chance that any outlined candidate from this range + // could require stack fixups. That is, both + // * LR is available in the range (No save/restore around call) + // * The range doesn't include calls (No save/restore in outlined frame) + // are true. + // FIXME: This is very restrictive; the flags check the whole block, + // not just the bit we will try to outline. + bool MightNeedStackFixUp = + (Flags & (MachineOutlinerMBBFlags::LRUnavailableSomewhere | + MachineOutlinerMBBFlags::HasCalls)); + + if (!MightNeedStackFixUp) + return outliner::InstrType::Legal; + + return outliner::InstrType::Illegal; + } + // Be conservative with IT blocks. if (MI.readsRegister(ARM::ITSTATE, TRI) || MI.modifiesRegister(ARM::ITSTATE, TRI)) @@ -5835,6 +5903,10 @@ void ARMBaseInstrInfo::buildOutlinedFrame( MachineBasicBlock &MBB, MachineFunction &MF, const outliner::OutlinedFunction &OF) const { + // Nothing is needed for tail-calls. + if (OF.FrameConstructionID == MachineOutlinerTailCall) + return; + // For thunk outlining, rewrite the last instruction from a call to a // tail-call. if (OF.FrameConstructionID == MachineOutlinerThunk) { @@ -5851,7 +5923,13 @@ if (isThumb && !Call->getOperand(FuncOp).isReg()) MIB.add(predOps(ARMCC::AL)); Call->eraseFromParent(); + return; } + + // Here we have to insert the return ourselves. Get the correct opcode from + // current feature set. + BuildMI(MBB, MBB.end(), DebugLoc(), get(Subtarget.getReturnOpcode())) + .add(predOps(ARMCC::AL)); } MachineBasicBlock::iterator ARMBaseInstrInfo::insertOutlinedCall( diff --git a/llvm/test/CodeGen/ARM/machine-outliner-no-lr-save.mir b/llvm/test/CodeGen/ARM/machine-outliner-no-lr-save.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/ARM/machine-outliner-no-lr-save.mir @@ -0,0 +1,139 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -mtriple=arm-- -run-pass=machine-outliner -verify-machineinstrs \ +# RUN: %s -o - | FileCheck %s + +--- | + define void @outline_no_save_ok_arm() #0 { ret void } + define void @outline_no_save_ko_arm() #0 { ret void } + define void @outline_no_save_ok_thumb() #1 { ret void } + define void @outline_no_save_ko_thumb() #1 { ret void } + + declare void @foo() + + attributes #0 = { minsize optsize } + attributes #1 = { minsize optsize "target-features"="+armv7-a,+thumb-mode" } +... +--- + +name: outline_no_save_ok_arm +tracksRegLiveness: true +body: | + ; CHECK-LABEL: name: outline_no_save_ok_arm + ; CHECK: bb.0: + ; CHECK: BL @OUTLINED_FUNCTION_1 + ; CHECK: bb.1: + ; CHECK: BL @OUTLINED_FUNCTION_1 + ; CHECK: bb.2: + ; CHECK: BX_RET 14 /* CC::al */, $noreg + bb.0: + $r2 = MOVi 1, 14, $noreg, $noreg + $r2 = MOVi 1, 14, $noreg, $noreg + $r2 = MOVi 1, 14, $noreg, $noreg + $r2 = MOVi 1, 14, $noreg, $noreg + $r3 = LDRi12 $sp, 8, 14, $noreg + bb.1: + $r2 = MOVi 1, 14, $noreg, $noreg + $r2 = MOVi 1, 14, $noreg, $noreg + $r2 = MOVi 1, 14, $noreg, $noreg + $r2 = MOVi 1, 14, $noreg, $noreg + $r3 = LDRi12 $sp, 8, 14, $noreg + bb.2: + BX_RET 14, $noreg +... +--- + +name: outline_no_save_ko_arm +tracksRegLiveness: true +body: | + ; CHECK-LABEL: name: outline_no_save_ko_arm + ; CHECK-NOT: OUTLINED_FUNCTION + bb.0: + liveins: $lr + $r2 = MOVi 2, 14, $noreg, $noreg + $r2 = MOVi 2, 14, $noreg, $noreg + $r2 = MOVi 2, 14, $noreg, $noreg + $r2 = MOVi 2, 14, $noreg, $noreg + $r3 = LDRi12 $sp, 8, 14, $noreg + $r2 = MOVr $lr, 14, $noreg, $noreg + bb.1: + $r2 = MOVi 2, 14, $noreg, $noreg + $r2 = MOVi 2, 14, $noreg, $noreg + $r2 = MOVi 2, 14, $noreg, $noreg + $r2 = MOVi 2, 14, $noreg, $noreg + $r3 = LDRi12 $sp, 8, 14, $noreg + $r4 = MOVi 4, 14, $noreg, $noreg + BL @foo + bb.2: + liveins: $lr + BX_RET 14, $noreg +... +--- + +name: outline_no_save_ok_thumb +tracksRegLiveness: true +body: | + ; CHECK-LABEL: name: outline_no_save_ok_thumb + ; CHECK: bb.0: + ; CHECK: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_0 + ; CHECK: bb.1: + ; CHECK: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_0 + ; CHECK: bb.2: + ; CHECK: tBX_RET 14 /* CC::al */, $noreg + bb.0: + $r2 = t2MOVi 1, 14, $noreg, $noreg + $r2 = t2MOVi 1, 14, $noreg, $noreg + $r2 = t2MOVi 1, 14, $noreg, $noreg + $r2 = t2MOVi 1, 14, $noreg, $noreg + t2STRi12 $r2, $sp, 0, 14, $noreg + bb.1: + $r2 = t2MOVi 1, 14, $noreg, $noreg + $r2 = t2MOVi 1, 14, $noreg, $noreg + $r2 = t2MOVi 1, 14, $noreg, $noreg + $r2 = t2MOVi 1, 14, $noreg, $noreg + t2STRi12 $r2, $sp, 0, 14, $noreg + bb.2: + tBX_RET 14, $noreg +... +--- + +name: outline_no_save_ko_thumb +tracksRegLiveness: true +body: | + ; CHECK-LABEL: name: outline_no_save_ko_thumb + ; CHECK-NOT: OUTLINED_FUNCTION + bb.0: + liveins: $lr + $r2 = t2MOVi 2, 14, $noreg, $noreg + $r2 = t2MOVi 2, 14, $noreg, $noreg + $r2 = t2MOVi 2, 14, $noreg, $noreg + $r2 = t2MOVi 2, 14, $noreg, $noreg + t2STRi12 $r2, $sp, 0, 14, $noreg + $r2 = tMOVr $lr, 14, $noreg + bb.1: + $r2 = t2MOVi 2, 14, $noreg, $noreg + $r2 = t2MOVi 2, 14, $noreg, $noreg + $r2 = t2MOVi 2, 14, $noreg, $noreg + $r2 = t2MOVi 2, 14, $noreg, $noreg + t2STRi12 $r2, $sp, 0, 14, $noreg + $r4 = t2MOVi 3, 14, $noreg, $noreg + tBL 14, $noreg, @foo + bb.2: + tBX_RET 14, $noreg + + ; CHECK-LABEL: name: OUTLINED_FUNCTION_0 + ; CHECK: bb.0: + ; CHECK: $r2 = t2MOVi 1, 14 /* CC::al */, $noreg, $noreg + ; CHECK: $r2 = t2MOVi 1, 14 /* CC::al */, $noreg, $noreg + ; CHECK: $r2 = t2MOVi 1, 14 /* CC::al */, $noreg, $noreg + ; CHECK: $r2 = t2MOVi 1, 14 /* CC::al */, $noreg, $noreg + ; CHECK: t2STRi12 $r2, $sp, 0, 14 /* CC::al */, $noreg + ; CHECK: tBX_RET 14 /* CC::al */, $noreg + + ; CHECK-LABEL: name: OUTLINED_FUNCTION_1 + ; CHECK: bb.0: + ; CHECK: $r2 = MOVi 1, 14 /* CC::al */, $noreg, $noreg + ; CHECK: $r2 = MOVi 1, 14 /* CC::al */, $noreg, $noreg + ; CHECK: $r2 = MOVi 1, 14 /* CC::al */, $noreg, $noreg + ; CHECK: $r2 = MOVi 1, 14 /* CC::al */, $noreg, $noreg + ; CHECK: $r3 = LDRi12 $sp, 8, 14 /* CC::al */, $noreg + ; CHECK: MOVPCLR 14 /* CC::al */, $noreg