1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 20:51:52 +01:00

[ARM][MachineOutliner] Add stack fixup feature

This patch handles cases where we have to save/restore the link register
into the stack and and load/store instruction which use the stack are
part of the outlined region. It checks that there will be no overflow
introduced by the new offset and fixup these instructions accordingly.

Differential Revision: https://reviews.llvm.org/D92934
This commit is contained in:
Yvan Roux 2021-01-19 10:07:56 +01:00
parent d6f7a6374a
commit 178bc607c9
9 changed files with 593 additions and 210 deletions

View File

@ -5914,6 +5914,112 @@ outliner::OutlinedFunction ARMBaseInstrInfo::getOutliningCandidateInfo(
NumBytesToCreateFrame, FrameID);
}
bool ARMBaseInstrInfo::checkAndUpdateStackOffset(MachineInstr *MI,
int64_t Fixup,
bool Updt) const {
int SPIdx = MI->findRegisterUseOperandIdx(ARM::SP);
unsigned AddrMode = (MI->getDesc().TSFlags & ARMII::AddrModeMask);
if (SPIdx < 0)
// No SP operand
return true;
else if (SPIdx != 1 && (AddrMode != ARMII::AddrModeT2_i8s4 || SPIdx != 2))
// If SP is not the base register we can't do much
return false;
// Stack might be involved but addressing mode doesn't handle any offset.
// Rq: AddrModeT1_[1|2|4] don't operate on SP
if (AddrMode == ARMII::AddrMode1 // Arithmetic instructions
|| AddrMode == ARMII::AddrMode4 // Load/Store Multiple
|| AddrMode == ARMII::AddrMode6 // Neon Load/Store Multiple
|| AddrMode == ARMII::AddrModeT2_so // SP can't be used as based register
|| AddrMode == ARMII::AddrModeT2_pc // PCrel access
|| AddrMode == ARMII::AddrMode2 // Used by PRE and POST indexed LD/ST
|| AddrMode == ARMII::AddrModeNone)
return false;
unsigned NumOps = MI->getDesc().getNumOperands();
unsigned ImmIdx = NumOps - 3;
const MachineOperand &Offset = MI->getOperand(ImmIdx);
assert(Offset.isImm() && "Is not an immediate");
int64_t OffVal = Offset.getImm();
if (OffVal < 0)
// Don't override data if the are below SP.
return false;
unsigned NumBits = 0;
unsigned Scale = 1;
switch (AddrMode) {
case ARMII::AddrMode3:
if (ARM_AM::getAM3Op(OffVal) == ARM_AM::sub)
return false;
OffVal = ARM_AM::getAM3Offset(OffVal);
NumBits = 8;
break;
case ARMII::AddrMode5:
if (ARM_AM::getAM5Op(OffVal) == ARM_AM::sub)
return false;
OffVal = ARM_AM::getAM5Offset(OffVal);
NumBits = 8;
Scale = 4;
break;
case ARMII::AddrMode5FP16:
if (ARM_AM::getAM5FP16Op(OffVal) == ARM_AM::sub)
return false;
OffVal = ARM_AM::getAM5FP16Offset(OffVal);
NumBits = 8;
Scale = 2;
break;
case ARMII::AddrModeT2_i8:
NumBits = 8;
break;
case ARMII::AddrModeT2_i8s4:
case ARMII::AddrModeT2_ldrex:
NumBits = 8;
Scale = 4;
break;
case ARMII::AddrModeT2_i12:
case ARMII::AddrMode_i12:
NumBits = 12;
break;
case ARMII::AddrModeT2_i7:
NumBits = 7;
break;
case ARMII::AddrModeT2_i7s2:
NumBits = 7;
Scale = 2;
break;
case ARMII::AddrModeT2_i7s4:
NumBits = 7;
Scale = 4;
break;
case ARMII::AddrModeT1_s: // SP-relative LD/ST
NumBits = 8;
Scale = 4;
break;
default:
llvm_unreachable("Unsupported addressing mode!");
}
// Make sure the offset is encodable for instructions that scale the
// immediate.
if (((OffVal * Scale + Fixup) & (Scale - 1)) != 0)
return false;
OffVal += Fixup / Scale;
unsigned Mask = (1 << NumBits) - 1;
if (OffVal <= Mask) {
if (Updt)
MI->getOperand(ImmIdx).setImm(OffVal);
return true;
}
return false;
}
bool ARMBaseInstrInfo::isFunctionSafeToOutlineFrom(
MachineFunction &MF, bool OutlineFromLinkOnceODRs) const {
const Function &F = MF.getFunction();
@ -6125,6 +6231,19 @@ ARMBaseInstrInfo::getOutliningType(MachineBasicBlock::iterator &MIT,
if (!MightNeedStackFixUp)
return outliner::InstrType::Legal;
// Any modification of SP will break our code to save/restore LR.
// FIXME: We could handle some instructions which add a constant offset to
// SP, with a bit more work.
if (MI.modifiesRegister(ARM::SP, TRI))
return outliner::InstrType::Illegal;
// At this point, we have a stack instruction that we might need to fix up.
// up. We'll handle it if it's a load or store.
if (checkAndUpdateStackOffset(&MI, Subtarget.getStackAlignment().value(),
false))
return outliner::InstrType::Legal;
// We can't fix it up, so don't outline it.
return outliner::InstrType::Illegal;
}
@ -6140,6 +6259,12 @@ ARMBaseInstrInfo::getOutliningType(MachineBasicBlock::iterator &MIT,
return outliner::InstrType::Legal;
}
void ARMBaseInstrInfo::fixupPostOutline(MachineBasicBlock &MBB) const {
for (MachineInstr &MI : MBB) {
checkAndUpdateStackOffset(&MI, Subtarget.getStackAlignment().value(), true);
}
}
void ARMBaseInstrInfo::saveLROnStack(MachineBasicBlock &MBB,
MachineBasicBlock::iterator It) const {
unsigned Opc = Subtarget.isThumb() ? ARM::t2STR_PRE : ARM::STR_PRE_IMM;
@ -6275,6 +6400,12 @@ void ARMBaseInstrInfo::buildOutlinedFrame(
saveLROnStack(MBB, It);
emitCFIForLRSaveOnStack(MBB, It);
// Fix up the instructions in the range, since we're going to modify the
// stack.
assert(OF.FrameConstructionID != MachineOutlinerDefault &&
"Can only fix up stack references once");
fixupPostOutline(MBB);
// Insert a restore before the terminator for the function. Restore LR.
restoreLRFromStack(MBB, Et);
emitCFIForLRRestoreFromStack(MBB, Et);
@ -6289,6 +6420,15 @@ void ARMBaseInstrInfo::buildOutlinedFrame(
// current feature set.
BuildMI(MBB, MBB.end(), DebugLoc(), get(Subtarget.getReturnOpcode()))
.add(predOps(ARMCC::AL));
// Did we have to modify the stack by saving the link register?
if (OF.FrameConstructionID != MachineOutlinerDefault &&
OF.Candidates[0].CallConstructionID != MachineOutlinerDefault)
return;
// We modified the stack.
// Walk over the basic block and fix up all the stack accesses.
fixupPostOutline(MBB);
}
MachineBasicBlock::iterator ARMBaseInstrInfo::insertOutlinedCall(
@ -6345,6 +6485,8 @@ MachineBasicBlock::iterator ARMBaseInstrInfo::insertOutlinedCall(
return CallPt;
}
// We have the default case. Save and restore from SP.
if (!MBB.isLiveIn(ARM::LR))
MBB.addLiveIn(ARM::LR);
saveLROnStack(MBB, It);
if (!AFI.isLRSpilled())
emitCFIForLRSaveOnStack(MBB, It);

View File

@ -404,6 +404,16 @@ private:
/// after the LR is was restored from a register.
void emitCFIForLRRestoreFromReg(MachineBasicBlock &MBB,
MachineBasicBlock::iterator It) const;
/// \brief Sets the offsets on outlined instructions in \p MBB which use SP
/// so that they will be valid post-outlining.
///
/// \param MBB A \p MachineBasicBlock in an outlined function.
void fixupPostOutline(MachineBasicBlock &MBB) const;
/// Returns true if the machine instruction offset can handle the stack fixup
/// and updates it if requested.
bool checkAndUpdateStackOffset(MachineInstr *MI, int64_t Fixup,
bool Updt) const;
unsigned getInstBundleLength(const MachineInstr &MI) const;

View File

@ -5,8 +5,6 @@
--- |
define void @outline_default_arm() #0 { ret void }
define void @outline_default_thumb() #1 { ret void }
define void @outline_default_KO_stack_arm() #0 { ret void }
define void @outline_default_KO_stack_thumb() #0 { ret void }
declare void @bar()
attributes #0 = { minsize optsize }
@ -113,121 +111,6 @@ body: |
liveins: $lr, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11
$r2 = tMOVr $lr, 14, $noreg
tBX_RET 14, $noreg
...
---
name: outline_default_KO_stack_arm
tracksRegLiveness: true
body: |
; CHECK-LABEL: name: outline_default_KO_stack_arm
; CHECK: bb.0:
; CHECK: liveins: $lr
; CHECK: $r0 = LDRi12 $sp, 0, 14 /* CC::al */, $noreg
; CHECK: $r1 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r2 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r3 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r4 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r5 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: bb.1:
; CHECK: liveins: $lr, $r6, $r7, $r8, $r9, $r10, $r11
; CHECK: $r0 = LDRi12 $sp, 0, 14 /* CC::al */, $noreg
; CHECK: $r1 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r2 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r3 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r4 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r5 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: bb.2:
; CHECK: liveins: $lr, $r5, $r6, $r7, $r8, $r9, $r10, $r11
; CHECK: $r0 = LDRi12 $sp, 0, 14 /* CC::al */, $noreg
; CHECK: $r1 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r2 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r3 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r4 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r5 = MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: bb.3:
; CHECK: liveins: $lr, $r5, $r6, $r7, $r8, $r9, $r10, $r11
; CHECK: $r2 = MOVr $lr, 14 /* CC::al */, $noreg, $noreg
; CHECK: BX_RET 14 /* CC::al */, $noreg
bb.0:
liveins: $lr
$r0 = LDRi12 $sp, 0, 14, $noreg
$r1 = MOVi 3, 14, $noreg, $noreg
$r2 = MOVi 3, 14, $noreg, $noreg
$r3 = MOVi 3, 14, $noreg, $noreg
$r4 = MOVi 3, 14, $noreg, $noreg
$r5 = MOVi 3, 14, $noreg, $noreg
bb.1:
liveins: $lr, $r6, $r7, $r8, $r9, $r10, $r11
$r0 = LDRi12 $sp, 0, 14, $noreg
$r1 = MOVi 3, 14, $noreg, $noreg
$r2 = MOVi 3, 14, $noreg, $noreg
$r3 = MOVi 3, 14, $noreg, $noreg
$r4 = MOVi 3, 14, $noreg, $noreg
$r5 = MOVi 3, 14, $noreg, $noreg
bb.2:
liveins: $lr, $r5, $r6, $r7, $r8, $r9, $r10, $r11
$r0 = LDRi12 $sp, 0, 14, $noreg
$r1 = MOVi 3, 14, $noreg, $noreg
$r2 = MOVi 3, 14, $noreg, $noreg
$r3 = MOVi 3, 14, $noreg, $noreg
$r4 = MOVi 3, 14, $noreg, $noreg
$r5 = MOVi 3, 14, $noreg, $noreg
bb.3:
liveins: $lr, $r5, $r6, $r7, $r8, $r9, $r10, $r11
$r2 = MOVr $lr, 14, $noreg, $noreg
BX_RET 14, $noreg
...
---
name: outline_default_KO_stack_thumb
tracksRegLiveness: true
body: |
; CHECK-LABEL: name: outline_default_KO_stack_thumb
; CHECK: bb.0:
; CHECK: liveins: $lr
; CHECK: $r0 = t2LDRi12 $sp, 0, 14 /* CC::al */, $noreg
; CHECK: $r1 = t2MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r2 = t2MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r3 = t2MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: bb.1:
; CHECK: liveins: $lr, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11
; CHECK: $r0 = t2LDRi12 $sp, 0, 14 /* CC::al */, $noreg
; CHECK: $r1 = t2MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r2 = t2MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r3 = t2MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: bb.2:
; CHECK: liveins: $lr, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11
; CHECK: $r0 = t2LDRi12 $sp, 0, 14 /* CC::al */, $noreg
; CHECK: $r1 = t2MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r2 = t2MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: $r3 = t2MOVi 3, 14 /* CC::al */, $noreg, $noreg
; CHECK: bb.3:
; CHECK: liveins: $lr, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11
; CHECK: $r2 = tMOVr $lr, 14 /* CC::al */, $noreg
; CHECK: tBX_RET 14 /* CC::al */, $noreg
bb.0:
liveins: $lr
$r0 = t2LDRi12 $sp, 0, 14, $noreg
$r1 = t2MOVi 3, 14, $noreg, $noreg
$r2 = t2MOVi 3, 14, $noreg, $noreg
$r3 = t2MOVi 3, 14, $noreg, $noreg
bb.1:
liveins: $lr, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11
$r0 = t2LDRi12 $sp, 0, 14, $noreg
$r1 = t2MOVi 3, 14, $noreg, $noreg
$r2 = t2MOVi 3, 14, $noreg, $noreg
$r3 = t2MOVi 3, 14, $noreg, $noreg
bb.2:
liveins: $lr, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11
$r0 = t2LDRi12 $sp, 0, 14, $noreg
$r1 = t2MOVi 3, 14, $noreg, $noreg
$r2 = t2MOVi 3, 14, $noreg, $noreg
$r3 = t2MOVi 3, 14, $noreg, $noreg
bb.3:
liveins: $lr, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11
$r2 = tMOVr $lr, 14, $noreg
tBX_RET 14, $noreg
; CHECK-LABEL: name: OUTLINED_FUNCTION_0
; CHECK: bb.0:

View File

@ -4,9 +4,7 @@
--- |
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()
@ -42,33 +40,6 @@ body: |
...
---
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: |
@ -93,33 +64,6 @@ body: |
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:
liveins: $lr, $r0, $r6, $r7, $r8, $r9, $r10, $r11
tBX_RET 14, $noreg
; CHECK-LABEL: name: OUTLINED_FUNCTION_0
; CHECK: bb.0:

View File

@ -0,0 +1,186 @@
# RUN: llc -mtriple=armv7-- -run-pass=prologepilog -run-pass=machine-outliner \
# RUN: -verify-machineinstrs %s -o - | FileCheck %s
--- |
define void @CheckAddrMode_i12() { ret void }
define void @CheckAddrMode3() { ret void }
define void @CheckAddrMode5() { ret void }
define void @CheckAddrMode5FP16() { ret void }
define void @foo() { ret void }
...
---
name: CheckAddrMode_i12
tracksRegLiveness: true
body: |
bb.0:
liveins: $r0
; CHECK-LABEL: name: CheckAddrMode_i12
; CHECK: $r1 = MOVr killed $r0, 14 /* CC::al */, $noreg, $noreg
; CHECK-NEXT: BL @OUTLINED_FUNCTION_[[I12:[0-9]+]]
; CHECK-NEXT: $r6 = LDRi12 $sp, 4088, 14 /* CC::al */, $noreg
$r1 = MOVr killed $r0, 14, $noreg, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$r1 = LDRi12 $sp, 0, 14, $noreg
$r2 = LDRi12 $sp, 8, 14, $noreg
$r5 = LDRi12 $sp, 4086, 14, $noreg
$r6 = LDRi12 $sp, 4088, 14, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$r1 = LDRi12 $sp, 0, 14, $noreg
$r2 = LDRi12 $sp, 8, 14, $noreg
$r5 = LDRi12 $sp, 4086, 14, $noreg
$r6 = LDRi12 $sp, 4088, 14, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$r1 = LDRi12 $sp, 0, 14, $noreg
$r2 = LDRi12 $sp, 8, 14, $noreg
$r5 = LDRi12 $sp, 4086, 14, $noreg
$r6 = LDRi12 $sp, 4088, 14, $noreg
BX_RET 14, $noreg
...
---
name: CheckAddrMode3
tracksRegLiveness: true
body: |
bb.0:
liveins: $r1
; CHECK-LABEL: name: CheckAddrMode3
; CHECK: $r0 = MOVr killed $r1, 14 /* CC::al */, $noreg, $noreg
; CHECK-NEXT: BL @OUTLINED_FUNCTION_[[I3:[0-9]+]]
; CHECK-NEXT: $r6 = LDRSH $sp, $noreg, 248, 14 /* CC::al */, $noreg
$r0 = MOVr killed $r1, 14, $noreg, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$r1 = LDRSH $sp, $noreg, 0, 14, $noreg
$r2 = LDRSH $sp, $noreg, 8, 14, $noreg
$r5 = LDRSH $sp, $noreg, 247, 14, $noreg
$r6 = LDRSH $sp, $noreg, 248, 14, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$r1 = LDRSH $sp, $noreg, 0, 14, $noreg
$r2 = LDRSH $sp, $noreg, 8, 14, $noreg
$r5 = LDRSH $sp, $noreg, 247, 14, $noreg
$r6 = LDRSH $sp, $noreg, 248, 14, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$r1 = LDRSH $sp, $noreg, 0, 14, $noreg
$r2 = LDRSH $sp, $noreg, 8, 14, $noreg
$r5 = LDRSH $sp, $noreg, 247, 14, $noreg
$r6 = LDRSH $sp, $noreg, 248, 14, $noreg
BX_RET 14, $noreg
...
---
name: CheckAddrMode5
tracksRegLiveness: true
body: |
bb.0:
liveins: $r2
; CHECK-LABEL: name: CheckAddrMode5
; CHECK: $r0 = MOVr killed $r2, 14 /* CC::al */, $noreg, $noreg
; CHECK-NEXT: BL @OUTLINED_FUNCTION_[[I5:[0-9]+]]
; CHECK-NEXT: $d5 = VLDRD $sp, 254, 14 /* CC::al */, $noreg
$r0 = MOVr killed $r2, 14, $noreg, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$d0 = VLDRD $sp, 0, 14, $noreg
$d1 = VLDRD $sp, 8, 14, $noreg
$d4 = VLDRD $sp, 253, 14, $noreg
$d5 = VLDRD $sp, 254, 14, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$d0 = VLDRD $sp, 0, 14, $noreg
$d1 = VLDRD $sp, 8, 14, $noreg
$d4 = VLDRD $sp, 253, 14, $noreg
$d5 = VLDRD $sp, 254, 14, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$d0 = VLDRD $sp, 0, 14, $noreg
$d1 = VLDRD $sp, 8, 14, $noreg
$d4 = VLDRD $sp, 253, 14, $noreg
$d5 = VLDRD $sp, 254, 14, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$d0 = VLDRD $sp, 0, 14, $noreg
$d1 = VLDRD $sp, 8, 14, $noreg
$d4 = VLDRD $sp, 253, 14, $noreg
$d5 = VLDRD $sp, 254, 14, $noreg
BX_RET 14, $noreg
...
---
name: CheckAddrMode5FP16
tracksRegLiveness: true
body: |
bb.0:
liveins: $r3
; CHECK-LABEL: name: CheckAddrMode5FP16
; CHECK: $r0 = MOVr killed $r3, 14 /* CC::al */, $noreg, $noreg
; CHECK-NEXT: BL @OUTLINED_FUNCTION_[[I5FP16:[0-9]+]]
; CHECK-NEXT: $s6 = VLDRH $sp, 252, 14, $noreg
$r0 = MOVr killed $r3, 14, $noreg, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$s1 = VLDRH $sp, 0, 14, $noreg
$s2 = VLDRH $sp, 8, 14, $noreg
$s5 = VLDRH $sp, 240, 14, $noreg
$s6 = VLDRH $sp, 252, 14, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$s1 = VLDRH $sp, 0, 14, $noreg
$s2 = VLDRH $sp, 8, 14, $noreg
$s5 = VLDRH $sp, 240, 14, $noreg
$s6 = VLDRH $sp, 252, 14, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$s1 = VLDRH $sp, 0, 14, $noreg
$s2 = VLDRH $sp, 8, 14, $noreg
$s5 = VLDRH $sp, 240, 14, $noreg
$s6 = VLDRH $sp, 252, 14, $noreg
BL @foo, implicit-def dead $lr, implicit $sp
$s1 = VLDRH $sp, 0, 14, $noreg
$s2 = VLDRH $sp, 8, 14, $noreg
$s5 = VLDRH $sp, 240, 14, $noreg
$s6 = VLDRH $sp, 252, 14, $noreg
BX_RET 14, $noreg
...
---
name: foo
tracksRegLiveness: true
body: |
bb.0:
liveins: $lr
BX_RET 14, $noreg
;CHECK: name: OUTLINED_FUNCTION_[[I5]]
;CHECK: early-clobber $sp = STR_PRE_IMM killed $lr, $sp, -8, 14 /* CC::al */, $noreg
;CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8
;CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8
;CHECK-NEXT: BL @foo, implicit-def dead $lr, implicit $sp
;CHECK-NEXT: $d0 = VLDRD $sp, 2, 14 /* CC::al */, $noreg
;CHECK-NEXT: $d1 = VLDRD $sp, 10, 14 /* CC::al */, $noreg
;CHECK-NEXT: $d4 = VLDRD $sp, 255, 14 /* CC::al */, $noreg
;CHECK-NEXT: $lr, $sp = LDR_POST_IMM $sp, $noreg, 8, 14 /* CC::al */, $noreg
;CHECK: name: OUTLINED_FUNCTION_[[I5FP16]]
;CHECK: early-clobber $sp = STR_PRE_IMM killed $lr, $sp, -8, 14 /* CC::al */, $noreg
;CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8
;CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8
;CHECK-NEXT: BL @foo, implicit-def dead $lr, implicit $sp
;CHECK-NEXT: $s1 = VLDRH $sp, 4, 14, $noreg
;CHECK-NEXT: $s2 = VLDRH $sp, 12, 14, $noreg
;CHECK-NEXT: $s5 = VLDRH $sp, 244, 14, $noreg
;CHECK-NEXT: $lr, $sp = LDR_POST_IMM $sp, $noreg, 8, 14 /* CC::al */, $noreg
;CHECK: name: OUTLINED_FUNCTION_[[I12]]
;CHECK: early-clobber $sp = STR_PRE_IMM killed $lr, $sp, -8, 14 /* CC::al */, $noreg
;CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8
;CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8
;CHECK-NEXT: BL @foo, implicit-def dead $lr, implicit $sp
;CHECK-NEXT: $r1 = LDRi12 $sp, 8, 14 /* CC::al */, $noreg
;CHECK-NEXT: $r2 = LDRi12 $sp, 16, 14 /* CC::al */, $noreg
;CHECK-NEXT: $r5 = LDRi12 $sp, 4094, 14 /* CC::al */, $noreg
;CHECK-NEXT: $lr, $sp = LDR_POST_IMM $sp, $noreg, 8, 14 /* CC::al */, $noreg
;CHECK: name: OUTLINED_FUNCTION_[[I3]]
;CHECK: early-clobber $sp = STR_PRE_IMM killed $lr, $sp, -8, 14 /* CC::al */, $noreg
;CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8
;CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8
;CHECK-NEXT: BL @foo, implicit-def dead $lr, implicit $sp
;CHECK-NEXT: $r1 = LDRSH $sp, $noreg, 8, 14 /* CC::al */, $noreg
;CHECK-NEXT: $r2 = LDRSH $sp, $noreg, 16, 14 /* CC::al */, $noreg
;CHECK-NEXT: $r5 = LDRSH $sp, $noreg, 255, 14 /* CC::al */, $noreg
;CHECK-NEXT: $lr, $sp = LDR_POST_IMM $sp, $noreg, 8, 14 /* CC::al */, $noreg

View File

@ -0,0 +1,231 @@
# RUN: llc -mtriple=thumbv7-- -run-pass=prologepilog \
# RUN: -run-pass=machine-outliner %s -o - | FileCheck %s
--- |
define void @CheckAddrModeT2_i12() { ret void }
define void @CheckAddrModeT2_i8() { ret void }
define void @CheckAddrModeT2_i8s4() { ret void }
define void @CheckAddrModeT2_ldrex() { ret void }
define void @CheckAddrModeT1_s() { ret void }
define void @foo() { ret void }
...
---
name: CheckAddrModeT2_i12
tracksRegLiveness: true
body: |
bb.0:
liveins: $r1
;CHECK-LABEL: name: CheckAddrModeT2_i12
;CHECK: $r0 = tMOVr killed $r1, 14 /* CC::al */, $noreg
;CHECK-NEXT: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_[[I12:[0-9]+]]
;CHECK-NEXT: $r0 = t2LDRi12 $sp, 4088, 14 /* CC::al */, $noreg
$r0 = tMOVr killed $r1, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
$r0 = t2LDRi12 $sp, 0, 14, $noreg
$r0 = t2LDRi12 $sp, 4, 14, $noreg
$r0 = t2LDRi12 $sp, 4086, 14, $noreg
$r0 = t2LDRi12 $sp, 4088, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
$r0 = t2LDRi12 $sp, 0, 14, $noreg
$r0 = t2LDRi12 $sp, 4, 14, $noreg
$r0 = t2LDRi12 $sp, 4086, 14, $noreg
$r0 = t2LDRi12 $sp, 4088, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
$r0 = t2LDRi12 $sp, 0, 14, $noreg
$r0 = t2LDRi12 $sp, 4, 14, $noreg
$r0 = t2LDRi12 $sp, 4086, 14, $noreg
$r0 = t2LDRi12 $sp, 4088, 14, $noreg
BX_RET 14, $noreg
...
---
name: CheckAddrModeT2_i8
tracksRegLiveness: true
body: |
bb.0:
liveins: $r1
;CHECK-LABEL: name: CheckAddrModeT2_i8
;CHECK: $r0 = tMOVr $r1, 14 /* CC::al */, $noreg
;CHECK-NEXT: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_[[I8:[0-9]+]]
;CHECK-NEXT: t2STRHi8 $r0, $sp, 248, 14 /* CC::al */, $noreg
$r0 = tMOVr $r1, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
t2STRHi8 $r0, $sp, 0, 14, $noreg
t2STRHi8 $r0, $sp, 4, 14, $noreg
t2STRHi8 $r0, $sp, 247, 14, $noreg
t2STRHi8 $r0, $sp, 248, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
t2STRHi8 $r0, $sp, 0, 14, $noreg
t2STRHi8 $r0, $sp, 4, 14, $noreg
t2STRHi8 $r0, $sp, 247, 14, $noreg
t2STRHi8 $r0, $sp, 248, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
t2STRHi8 $r0, $sp, 0, 14, $noreg
t2STRHi8 $r0, $sp, 4, 14, $noreg
t2STRHi8 $r0, $sp, 247, 14, $noreg
t2STRHi8 $r0, $sp, 248, 14, $noreg
BX_RET 14, $noreg
...
---
name: CheckAddrModeT2_i8s4
tracksRegLiveness: true
body: |
bb.0:
liveins: $r1
;CHECK-LABEL: name: CheckAddrModeT2_i8s4
;CHECK: $r0 = tMOVr $r1, 14 /* CC::al */, $noreg
;CHECK-NEXT: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_[[I8S4:[0-9]+]]
;CHECK-NEXT: t2STRDi8 $r0, $r1, $sp, 254, 14 /* CC::al */, $noreg
$r0 = tMOVr $r1, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
t2STRDi8 $r0, $r1, $sp, 0, 14, $noreg
t2STRDi8 $r0, $r1, $sp, 8, 14, $noreg
t2STRDi8 $r0, $r1, $sp, 253, 14, $noreg
t2STRDi8 $r0, $r1, $sp, 254, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
t2STRDi8 $r0, $r1, $sp, 0, 14, $noreg
t2STRDi8 $r0, $r1, $sp, 8, 14, $noreg
t2STRDi8 $r0, $r1, $sp, 253, 14, $noreg
t2STRDi8 $r0, $r1, $sp, 254, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
t2STRDi8 $r0, $r1, $sp, 0, 14, $noreg
t2STRDi8 $r0, $r1, $sp, 8, 14, $noreg
t2STRDi8 $r0, $r1, $sp, 253, 14, $noreg
t2STRDi8 $r0, $r1, $sp, 254, 14, $noreg
BX_RET 14, $noreg
...
---
name: CheckAddrModeT2_ldrex
tracksRegLiveness: true
body: |
bb.0:
liveins: $r1
;CHECK-LABEL: name: CheckAddrModeT2_ldrex
;CHECK: $r0 = tMOVr $r1, 14 /* CC::al */, $noreg
;CHECK-NEXT: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_[[LDREX:[0-9]+]]
;CHECK-NEXT: $r1 = t2LDREX $sp, 254, 14 /* CC::al */, $noreg
$r0 = tMOVr $r1, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
$r1 = t2LDREX $sp, 0, 14, $noreg
$r1 = t2LDREX $sp, 8, 14, $noreg
$r1 = t2LDREX $sp, 253, 14, $noreg
$r1 = t2LDREX $sp, 254, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
$r1 = t2LDREX $sp, 0, 14, $noreg
$r1 = t2LDREX $sp, 8, 14, $noreg
$r1 = t2LDREX $sp, 253, 14, $noreg
$r1 = t2LDREX $sp, 254, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
$r1 = t2LDREX $sp, 0, 14, $noreg
$r1 = t2LDREX $sp, 8, 14, $noreg
$r1 = t2LDREX $sp, 253, 14, $noreg
$r1 = t2LDREX $sp, 254, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
$r1 = t2LDREX $sp, 0, 14, $noreg
$r1 = t2LDREX $sp, 8, 14, $noreg
$r1 = t2LDREX $sp, 253, 14, $noreg
$r1 = t2LDREX $sp, 254, 14, $noreg
BX_RET 14, $noreg
...
---
name: CheckAddrModeT1_s
tracksRegLiveness: true
body: |
bb.0:
liveins: $r0, $r1
;CHECK-LABEL: name: CheckAddrModeT1_s
;CHECK: $r0 = tMOVr $r1, 14 /* CC::al */, $noreg
;CHECK-NEXT: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_[[T1_S:[0-9]+]]
;CHECK-NEXT: tSTRspi $r0, $sp, 254, 14 /* CC::al */, $noreg
$r0 = tMOVr $r1, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
tSTRspi $r0, $sp, 0, 14, $noreg
tSTRspi $r0, $sp, 4, 14, $noreg
tSTRspi $r0, $sp, 253, 14, $noreg
tSTRspi $r0, $sp, 254, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
tSTRspi $r0, $sp, 0, 14, $noreg
tSTRspi $r0, $sp, 4, 14, $noreg
tSTRspi $r0, $sp, 253, 14, $noreg
tSTRspi $r0, $sp, 254, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
tSTRspi $r0, $sp, 0, 14, $noreg
tSTRspi $r0, $sp, 4, 14, $noreg
tSTRspi $r0, $sp, 253, 14, $noreg
tSTRspi $r0, $sp, 254, 14, $noreg
tBL 14, $noreg, @foo, implicit-def dead $lr, implicit $sp
tSTRspi $r0, $sp, 0, 14, $noreg
tSTRspi $r0, $sp, 4, 14, $noreg
tSTRspi $r0, $sp, 253, 14, $noreg
tSTRspi $r0, $sp, 254, 14, $noreg
BX_RET 14, $noreg
...
---
name: foo
tracksRegLiveness: true
body: |
bb.0:
liveins: $lr
BX_RET 14, $noreg
;CHECK: name: OUTLINED_FUNCTION_[[LDREX]]
;CHECK: early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg
;CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8
;CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8
;CHECK-NEXT: tBL 14 /* CC::al */, $noreg, @foo, implicit-def dead $lr, implicit $sp
;CHECK-NEXT: $r1 = t2LDREX $sp, 2, 14 /* CC::al */, $noreg
;CHECK-NEXT: $r1 = t2LDREX $sp, 10, 14 /* CC::al */, $noreg
;CHECK-NEXT: $r1 = t2LDREX $sp, 255, 14 /* CC::al */, $noreg
;CHECK-NEXT: $lr, $sp = t2LDR_POST $sp, 8, 14 /* CC::al */, $noreg
;CHECK: name: OUTLINED_FUNCTION_[[I8]]
;CHECK: early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg
;CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8
;CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8
;CHECK-NEXT: tBL 14 /* CC::al */, $noreg, @foo, implicit-def dead $lr, implicit $sp
;CHECK-NEXT: t2STRHi8 $r0, $sp, 8, 14 /* CC::al */, $noreg
;CHECK-NEXT: t2STRHi8 $r0, $sp, 12, 14 /* CC::al */, $noreg
;CHECK-NEXT: t2STRHi8 $r0, $sp, 255, 14 /* CC::al */, $noreg
;CHECK-NEXT: $lr, $sp = t2LDR_POST $sp, 8, 14 /* CC::al */, $noreg
;CHECK: name: OUTLINED_FUNCTION_[[I8S4]]
;CHECK: early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg
;CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8
;CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8
;CHECK-NEXT: tBL 14 /* CC::al */, $noreg, @foo, implicit-def dead $lr, implicit $sp
;CHECK-NEXT: t2STRDi8 $r0, $r1, $sp, 2, 14 /* CC::al */, $noreg
;CHECK-NEXT: t2STRDi8 $r0, $r1, $sp, 10, 14 /* CC::al */, $noreg
;CHECK-NEXT: t2STRDi8 $r0, $r1, $sp, 255, 14 /* CC::al */, $noreg
;CHECK-NEXT: $lr, $sp = t2LDR_POST $sp, 8, 14 /* CC::al */, $noreg
;CHECK: name: OUTLINED_FUNCTION_[[I12]]
;CHECK: early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg
;CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8
;CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8
;CHECK-NEXT: tBL 14 /* CC::al */, $noreg, @foo, implicit-def dead $lr, implicit $sp
;CHECK-NEXT: $r0 = t2LDRi12 $sp, 8, 14 /* CC::al */, $noreg
;CHECK-NEXT: $r0 = t2LDRi12 $sp, 12, 14 /* CC::al */, $noreg
;CHECK-NEXT: $r0 = t2LDRi12 $sp, 4094, 14 /* CC::al */, $noreg
;CHECK-NEXT: $lr, $sp = t2LDR_POST $sp, 8, 14 /* CC::al */, $noreg
;CHECK: name: OUTLINED_FUNCTION_[[T1_S]]
;CHECK: early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg
;CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8
;CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8
;CHECK-NEXT: tBL 14 /* CC::al */, $noreg, @foo, implicit-def dead $lr, implicit $sp
;CHECK-NEXT: tSTRspi $r0, $sp, 2, 14 /* CC::al */, $noreg
;CHECK-NEXT: tSTRspi $r0, $sp, 6, 14 /* CC::al */, $noreg
;CHECK-NEXT: tSTRspi $r0, $sp, 255, 14 /* CC::al */, $noreg
;CHECK-NEXT: $lr, $sp = t2LDR_POST $sp, 8, 14 /* CC::al */, $noreg

View File

@ -1,6 +1,4 @@
; RUN: llc -enable-machine-outliner -mtriple=arm-unknown-linux < %s | FileCheck %s
;
; NOTE: Machine outliner runs, but doesn't do anything.
@x = global i32 0, align 4
define dso_local i32 @check_boundaries() #0 {

View File

@ -1,6 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --include-generated-funcs
; RUN: llc -enable-machine-outliner -mtriple=arm-unknown-linux < %s | FileCheck %s
; NOTE: Machine outliner runs, but doesn't do anything.
@x = global i32 0, align 4
define dso_local i32 @check_boundaries() #0 {
@ -76,14 +75,9 @@ attributes #0 = { noredzone nounwind ssp uwtable "frame-pointer"="all" }
; CHECK-NEXT: str r0, [sp, #4]
; CHECK-NEXT: b .LBB0_3
; CHECK-NEXT: .LBB0_2:
; CHECK-NEXT: mov r0, #2
; CHECK-NEXT: str r0, [sp, #8]
; CHECK-NEXT: mov r0, #1
; CHECK-NEXT: str r0, [sp, #12]
; CHECK-NEXT: mov r0, #3
; CHECK-NEXT: str r0, [sp, #4]
; CHECK-NEXT: mov r0, #4
; CHECK-NEXT: str r0, [sp]
; CHECK-NEXT: mov r1, lr
; CHECK-NEXT: bl OUTLINED_FUNCTION_0
; CHECK-NEXT: mov lr, r1
; CHECK-NEXT: .LBB0_3:
; CHECK-NEXT: ldr r0, [sp, #12]
; CHECK-NEXT: cmp r0, #0
@ -93,14 +87,9 @@ attributes #0 = { noredzone nounwind ssp uwtable "frame-pointer"="all" }
; CHECK-NEXT: str r0, [sp, #4]
; CHECK-NEXT: b .LBB0_6
; CHECK-NEXT: .LBB0_5:
; CHECK-NEXT: mov r0, #2
; CHECK-NEXT: str r0, [sp, #8]
; CHECK-NEXT: mov r0, #1
; CHECK-NEXT: str r0, [sp, #12]
; CHECK-NEXT: mov r0, #3
; CHECK-NEXT: str r0, [sp, #4]
; CHECK-NEXT: mov r0, #4
; CHECK-NEXT: str r0, [sp]
; CHECK-NEXT: mov r1, lr
; CHECK-NEXT: bl OUTLINED_FUNCTION_0
; CHECK-NEXT: mov lr, r1
; CHECK-NEXT: .LBB0_6:
; CHECK-NEXT: mov r0, #0
; CHECK-NEXT: add sp, sp, #20
@ -134,3 +123,15 @@ attributes #0 = { noredzone nounwind ssp uwtable "frame-pointer"="all" }
; CHECK-NEXT: @ %bb.1:
; CHECK-NEXT: .LCPI1_0:
; CHECK-NEXT: .long x
;
; CHECK-LABEL: OUTLINED_FUNCTION_0:
; CHECK: @ %bb.0:
; CHECK-NEXT: mov r0, #2
; CHECK-NEXT: str r0, [sp, #8]
; CHECK-NEXT: mov r0, #1
; CHECK-NEXT: str r0, [sp, #12]
; CHECK-NEXT: mov r0, #3
; CHECK-NEXT: str r0, [sp, #4]
; CHECK-NEXT: mov r0, #4
; CHECK-NEXT: str r0, [sp]
; CHECK-NEXT: mov pc, lr

View File

@ -1,7 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -enable-machine-outliner -mtriple=arm-unknown-linux < %s | FileCheck %s
;
; NOTE: Machine outliner runs, but doesn't do anything.
@x = global i32 0, align 4
define dso_local i32 @check_boundaries() #0 {
@ -18,14 +16,9 @@ define dso_local i32 @check_boundaries() #0 {
; CHECK-NEXT: str r0, [sp, #4]
; CHECK-NEXT: b .LBB0_3
; CHECK-NEXT: .LBB0_2:
; CHECK-NEXT: mov r0, #2
; CHECK-NEXT: str r0, [sp, #8]
; CHECK-NEXT: mov r0, #1
; CHECK-NEXT: str r0, [sp, #12]
; CHECK-NEXT: mov r0, #3
; CHECK-NEXT: str r0, [sp, #4]
; CHECK-NEXT: mov r0, #4
; CHECK-NEXT: str r0, [sp]
; CHECK-NEXT: mov r1, lr
; CHECK-NEXT: bl OUTLINED_FUNCTION_0
; CHECK-NEXT: mov lr, r1
; CHECK-NEXT: .LBB0_3:
; CHECK-NEXT: ldr r0, [sp, #12]
; CHECK-NEXT: cmp r0, #0
@ -35,14 +28,9 @@ define dso_local i32 @check_boundaries() #0 {
; CHECK-NEXT: str r0, [sp, #4]
; CHECK-NEXT: b .LBB0_6
; CHECK-NEXT: .LBB0_5:
; CHECK-NEXT: mov r0, #2
; CHECK-NEXT: str r0, [sp, #8]
; CHECK-NEXT: mov r0, #1
; CHECK-NEXT: str r0, [sp, #12]
; CHECK-NEXT: mov r0, #3
; CHECK-NEXT: str r0, [sp, #4]
; CHECK-NEXT: mov r0, #4
; CHECK-NEXT: str r0, [sp]
; CHECK-NEXT: mov r1, lr
; CHECK-NEXT: bl OUTLINED_FUNCTION_0
; CHECK-NEXT: mov lr, r1
; CHECK-NEXT: .LBB0_6:
; CHECK-NEXT: mov r0, #0
; CHECK-NEXT: add sp, sp, #20