[ARM][MachineOutliner] Add stack fixup feature
authorYvan Roux <yvan.roux@linaro.org>
Tue, 19 Jan 2021 09:07:56 +0000 (10:07 +0100)
committerYvan Roux <yvan.roux@linaro.org>
Tue, 19 Jan 2021 09:59:09 +0000 (10:59 +0100)
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

llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
llvm/lib/Target/ARM/ARMBaseInstrInfo.h
llvm/test/CodeGen/ARM/machine-outliner-default.mir
llvm/test/CodeGen/ARM/machine-outliner-no-lr-save.mir
llvm/test/CodeGen/ARM/machine-outliner-stack-fixup-arm.mir [new file with mode: 0644]
llvm/test/CodeGen/ARM/machine-outliner-stack-fixup-thumb.mir [new file with mode: 0644]
llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/arm_generated_funcs.ll
llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/arm_generated_funcs.ll.generated.expected
llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/arm_generated_funcs.ll.nogenerated.expected

index 143bf66..112eb59 100644 (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);
index b14f7e4..1b843c4 100644 (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;
 
index 9db4207..fa5119c 100644 (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:
index 678633d..71a7d4d 100644 (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:
diff --git a/llvm/test/CodeGen/ARM/machine-outliner-stack-fixup-arm.mir b/llvm/test/CodeGen/ARM/machine-outliner-stack-fixup-arm.mir
new file mode 100644 (file)
index 0000000..1f8745c
--- /dev/null
@@ -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
diff --git a/llvm/test/CodeGen/ARM/machine-outliner-stack-fixup-thumb.mir b/llvm/test/CodeGen/ARM/machine-outliner-stack-fixup-thumb.mir
new file mode 100644 (file)
index 0000000..7d9b195
--- /dev/null
@@ -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
index cdc7d3b..bae66d4 100644 (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 {
index ddf87ee..de5571f 100644 (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
index 316e774..4f62338 100644 (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