/// | 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,
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(
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<outliner::Candidate> 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);
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))
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) {
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(
--- /dev/null
+# 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