[ARM][MachineOutliner] Emit more CFI instructions
authorMomchil Velikov <momchil.velikov@arm.com>
Mon, 9 Nov 2020 11:48:32 +0000 (11:48 +0000)
committerMomchil Velikov <momchil.velikov@arm.com>
Mon, 9 Nov 2020 15:26:18 +0000 (15:26 +0000)
This patch make the outliner emit CFI instructions in a few more
places:

  * after LR is restored, but before the return in an outlined
  function

  * around save/restore of LR to/from a register at calls to outlined
  functions

  * around save/restore of LR to/from the stack at calls to outlined
  functions

The latter two only when the function does NOT spill LR. If the
function spills LR, then outliner generated saves/restores around
calls are not considered interesting for unwinding the frame.

Differential Revision: https://reviews.llvm.org/D89483

llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
llvm/lib/Target/ARM/ARMBaseInstrInfo.h
llvm/test/CodeGen/ARM/machine-outliner-calls.mir
llvm/test/CodeGen/ARM/machine-outliner-cfi-1.ll [new file with mode: 0644]
llvm/test/CodeGen/ARM/machine-outliner-cfi-2.ll [new file with mode: 0644]
llvm/test/CodeGen/ARM/machine-outliner-cfi-3.ll [new file with mode: 0644]

index 6844b02..12a04c5 100644 (file)
@@ -6071,7 +6071,7 @@ ARMBaseInstrInfo::getOutliningType(MachineBasicBlock::iterator &MIT,
 }
 
 void ARMBaseInstrInfo::saveLROnStack(MachineBasicBlock &MBB,
-                                     MachineBasicBlock::iterator &It) const {
+                                     MachineBasicBlock::iterator It) const {
   unsigned Opc = Subtarget.isThumb() ? ARM::t2STR_PRE : ARM::STR_PRE_IMM;
   int Align = -Subtarget.getStackAlignment().value();
   BuildMI(MBB, It, DebugLoc(), get(Opc), ARM::SP)
@@ -6081,8 +6081,45 @@ void ARMBaseInstrInfo::saveLROnStack(MachineBasicBlock &MBB,
     .add(predOps(ARMCC::AL));
 }
 
+void ARMBaseInstrInfo::emitCFIForLRSaveOnStack(
+    MachineBasicBlock &MBB, MachineBasicBlock::iterator It) const {
+  MachineFunction &MF = *MBB.getParent();
+  const MCRegisterInfo *MRI = Subtarget.getRegisterInfo();
+  unsigned DwarfLR = MRI->getDwarfRegNum(ARM::LR, true);
+  int Align = Subtarget.getStackAlignment().value();
+  // Add a CFI saying the stack was moved down.
+  int64_t StackPosEntry =
+      MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, Align));
+  BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION))
+      .addCFIIndex(StackPosEntry)
+      .setMIFlags(MachineInstr::FrameSetup);
+
+  // Add a CFI saying that the LR that we want to find is now higher than
+  // before.
+  int64_t LRPosEntry =
+      MF.addFrameInst(MCCFIInstruction::createOffset(nullptr, DwarfLR, -Align));
+  BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION))
+      .addCFIIndex(LRPosEntry)
+      .setMIFlags(MachineInstr::FrameSetup);
+}
+
+void ARMBaseInstrInfo::emitCFIForLRSaveToReg(MachineBasicBlock &MBB,
+                                             MachineBasicBlock::iterator It,
+                                             Register Reg) const {
+  MachineFunction &MF = *MBB.getParent();
+  const MCRegisterInfo *MRI = Subtarget.getRegisterInfo();
+  unsigned DwarfLR = MRI->getDwarfRegNum(ARM::LR, true);
+  unsigned DwarfReg = MRI->getDwarfRegNum(Reg, true);
+
+  int64_t LRPosEntry = MF.addFrameInst(
+      MCCFIInstruction::createRegister(nullptr, DwarfLR, DwarfReg));
+  BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION))
+      .addCFIIndex(LRPosEntry)
+      .setMIFlags(MachineInstr::FrameSetup);
+}
+
 void ARMBaseInstrInfo::restoreLRFromStack(
-  MachineBasicBlock &MBB, MachineBasicBlock::iterator &It) const {
+    MachineBasicBlock &MBB, MachineBasicBlock::iterator It) const {
   unsigned Opc = Subtarget.isThumb() ? ARM::t2LDR_POST : ARM::LDR_POST_IMM;
   MachineInstrBuilder MIB = BuildMI(MBB, It, DebugLoc(), get(Opc), ARM::LR)
     .addReg(ARM::SP, RegState::Define)
@@ -6092,6 +6129,39 @@ void ARMBaseInstrInfo::restoreLRFromStack(
   MIB.addImm(Subtarget.getStackAlignment().value()).add(predOps(ARMCC::AL));
 }
 
+void ARMBaseInstrInfo::emitCFIForLRRestoreFromStack(
+    MachineBasicBlock &MBB, MachineBasicBlock::iterator It) const {
+  // Now stack has moved back up...
+  MachineFunction &MF = *MBB.getParent();
+  const MCRegisterInfo *MRI = Subtarget.getRegisterInfo();
+  unsigned DwarfLR = MRI->getDwarfRegNum(ARM::LR, true);
+  int64_t StackPosEntry =
+      MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, 0));
+  BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION))
+      .addCFIIndex(StackPosEntry)
+      .setMIFlags(MachineInstr::FrameDestroy);
+
+  // ... and we have restored LR.
+  int64_t LRPosEntry =
+      MF.addFrameInst(MCCFIInstruction::createRestore(nullptr, DwarfLR));
+  BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION))
+      .addCFIIndex(LRPosEntry)
+      .setMIFlags(MachineInstr::FrameDestroy);
+}
+
+void ARMBaseInstrInfo::emitCFIForLRRestoreFromReg(
+    MachineBasicBlock &MBB, MachineBasicBlock::iterator It) const {
+  MachineFunction &MF = *MBB.getParent();
+  const MCRegisterInfo *MRI = Subtarget.getRegisterInfo();
+  unsigned DwarfLR = MRI->getDwarfRegNum(ARM::LR, true);
+
+  int64_t LRPosEntry =
+      MF.addFrameInst(MCCFIInstruction::createRestore(nullptr, DwarfLR));
+  BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION))
+      .addCFIIndex(LRPosEntry)
+      .setMIFlags(MachineInstr::FrameDestroy);
+}
+
 void ARMBaseInstrInfo::buildOutlinedFrame(
     MachineBasicBlock &MBB, MachineFunction &MF,
     const outliner::OutlinedFunction &OF) const {
@@ -6133,28 +6203,11 @@ void ARMBaseInstrInfo::buildOutlinedFrame(
 
     // Insert a save before the outlined region
     saveLROnStack(MBB, It);
-
-    unsigned StackAlignment = Subtarget.getStackAlignment().value();
-    const TargetSubtargetInfo &STI = MF.getSubtarget();
-    const MCRegisterInfo *MRI = STI.getRegisterInfo();
-    unsigned DwarfReg = MRI->getDwarfRegNum(ARM::LR, true);
-    // Add a CFI saying the stack was moved down.
-    int64_t StackPosEntry = MF.addFrameInst(
-        MCCFIInstruction::cfiDefCfaOffset(nullptr, StackAlignment));
-    BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION))
-        .addCFIIndex(StackPosEntry)
-        .setMIFlags(MachineInstr::FrameSetup);
-
-    // Add a CFI saying that the LR that we want to find is now higher than
-    // before.
-    int64_t LRPosEntry = MF.addFrameInst(
-        MCCFIInstruction::createOffset(nullptr, DwarfReg, StackAlignment));
-    BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION))
-        .addCFIIndex(LRPosEntry)
-        .setMIFlags(MachineInstr::FrameSetup);
+    emitCFIForLRSaveOnStack(MBB, It);
 
     // Insert a restore before the terminator for the function.  Restore LR.
     restoreLRFromStack(MBB, Et);
+    emitCFIForLRRestoreFromStack(MBB, Et);
   }
 
   // If this is a tail call outlined function, then there's already a return.
@@ -6204,6 +6257,7 @@ MachineBasicBlock::iterator ARMBaseInstrInfo::insertOutlinedCall(
     return It;
   }
 
+  const ARMFunctionInfo &AFI = *C.getMF()->getInfo<ARMFunctionInfo>();
   // Can we save to a register?
   if (C.CallConstructionID == MachineOutlinerRegSave) {
     unsigned Reg = findRegisterToSaveLRTo(C);
@@ -6211,15 +6265,23 @@ MachineBasicBlock::iterator ARMBaseInstrInfo::insertOutlinedCall(
 
     // Save and restore LR from that register.
     copyPhysReg(MBB, It, DebugLoc(), Reg, ARM::LR, true);
+    if (!AFI.isLRSpilled())
+      emitCFIForLRSaveToReg(MBB, It, Reg);
     CallPt = MBB.insert(It, CallMIB);
     copyPhysReg(MBB, It, DebugLoc(), ARM::LR, Reg, true);
+    if (!AFI.isLRSpilled())
+      emitCFIForLRRestoreFromReg(MBB, It);
     It--;
     return CallPt;
   }
   // We have the default case. Save and restore from SP.
   saveLROnStack(MBB, It);
+  if (!AFI.isLRSpilled())
+    emitCFIForLRSaveOnStack(MBB, It);
   CallPt = MBB.insert(It, CallMIB);
   restoreLRFromStack(MBB, It);
+  if (!AFI.isLRSpilled())
+    emitCFIForLRRestoreFromStack(MBB, It);
   It--;
   return CallPt;
 }
index 4132906..bfcbe8e 100644 (file)
@@ -368,12 +368,33 @@ private:
   // Adds an instruction which saves the link register on top of the stack into
   /// the MachineBasicBlock \p MBB at position \p It.
   void saveLROnStack(MachineBasicBlock &MBB,
-                     MachineBasicBlock::iterator &It) const;
+                     MachineBasicBlock::iterator It) const;
 
   /// Adds an instruction which restores the link register from the top the
   /// stack into the MachineBasicBlock \p MBB at position \p It.
   void restoreLRFromStack(MachineBasicBlock &MBB,
-                          MachineBasicBlock::iterator &It) const;
+                          MachineBasicBlock::iterator It) const;
+
+  /// Emit CFI instructions into the MachineBasicBlock \p MBB at position \p It,
+  /// for the case when the LR is saved on the stack.
+  void emitCFIForLRSaveOnStack(MachineBasicBlock &MBB,
+                               MachineBasicBlock::iterator It) const;
+
+  /// Emit CFI instructions into the MachineBasicBlock \p MBB at position \p It,
+  /// for the case when the LR is saved in the register \p Reg.
+  void emitCFIForLRSaveToReg(MachineBasicBlock &MBB,
+                             MachineBasicBlock::iterator It,
+                             Register Reg) const;
+
+  /// Emit CFI instructions into the MachineBasicBlock \p MBB at position \p It,
+  /// after the LR is was restored from the stack.
+  void emitCFIForLRRestoreFromStack(MachineBasicBlock &MBB,
+                                    MachineBasicBlock::iterator It) const;
+
+  /// Emit CFI instructions into the MachineBasicBlock \p MBB at position \p It,
+  /// after the LR is was restored from a register.
+  void emitCFIForLRRestoreFromReg(MachineBasicBlock &MBB,
+                                  MachineBasicBlock::iterator It) const;
 
   unsigned getInstBundleLength(const MachineInstr &MI) const;
 
index 7880ddf..a9a2a13 100644 (file)
@@ -295,7 +295,7 @@ body:             |
   ; CHECK:   liveins: $r11, $r10, $r9, $r8, $r7, $r6, $r5, $d15, $d14, $d13, $d12, $d11, $d10, $d9, $d8, $lr
   ; CHECK:   early-clobber $sp = STR_PRE_IMM killed $lr, $sp, -8, 14 /* CC::al */, $noreg
   ; CHECK:   frame-setup CFI_INSTRUCTION def_cfa_offset 8
-  ; CHECK:   frame-setup CFI_INSTRUCTION offset $lr, 8
+  ; CHECK:   frame-setup CFI_INSTRUCTION offset $lr, -8
   ; CHECK:   BL @bar, implicit-def dead $lr, implicit $sp
   ; CHECK:   $r0 = MOVi 1, 14 /* CC::al */, $noreg, $noreg
   ; CHECK:   $r1 = MOVi 1, 14 /* CC::al */, $noreg, $noreg
@@ -320,7 +320,7 @@ body:             |
   ; CHECK:   liveins: $r11, $r10, $r9, $r8, $r7, $r6, $r5, $d15, $d14, $d13, $d12, $d11, $d10, $d9, $d8, $lr
   ; CHECK:   early-clobber $sp = STR_PRE_IMM killed $lr, $sp, -8, 14 /* CC::al */, $noreg
   ; CHECK:   frame-setup CFI_INSTRUCTION def_cfa_offset 8
-  ; CHECK:   frame-setup CFI_INSTRUCTION offset $lr, 8
+  ; CHECK:   frame-setup CFI_INSTRUCTION offset $lr, -8
   ; CHECK:   BL @bar, implicit-def dead $lr, implicit $sp
   ; CHECK:   $r0 = MOVi 2, 14 /* CC::al */, $noreg, $noreg
   ; CHECK:   $r1 = MOVi 2, 14 /* CC::al */, $noreg, $noreg
@@ -335,7 +335,7 @@ body:             |
   ; CHECK:   liveins: $r11, $r10, $r9, $r8, $r6, $r5, $r4, $d15, $d14, $d13, $d12, $d11, $d10, $d9, $d8, $lr
   ; CHECK:   early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg
   ; CHECK:   frame-setup CFI_INSTRUCTION def_cfa_offset 8
-  ; CHECK:   frame-setup CFI_INSTRUCTION offset $lr, 8
+  ; CHECK:   frame-setup CFI_INSTRUCTION offset $lr, -8
   ; CHECK:   tBL 14 /* CC::al */, $noreg, @bar, implicit-def dead $lr, implicit $sp
   ; CHECK:   $r0 = t2MOVi 1, 14 /* CC::al */, $noreg, $noreg
   ; CHECK:   $r1 = t2MOVi 1, 14 /* CC::al */, $noreg, $noreg
@@ -348,7 +348,7 @@ body:             |
   ; CHECK:   liveins: $r11, $r10, $r9, $r8, $r6, $r5, $r4, $d15, $d14, $d13, $d12, $d11, $d10, $d9, $d8, $lr
   ; CHECK:   early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg
   ; CHECK:   frame-setup CFI_INSTRUCTION def_cfa_offset 8
-  ; CHECK:   frame-setup CFI_INSTRUCTION offset $lr, 8
+  ; CHECK:   frame-setup CFI_INSTRUCTION offset $lr, -8
   ; CHECK:   tBL 14 /* CC::al */, $noreg, @bar, implicit-def dead $lr, implicit $sp
   ; CHECK:   $r0 = t2MOVi 2, 14 /* CC::al */, $noreg, $noreg
   ; CHECK:   $r1 = t2MOVi 2, 14 /* CC::al */, $noreg, $noreg
diff --git a/llvm/test/CodeGen/ARM/machine-outliner-cfi-1.ll b/llvm/test/CodeGen/ARM/machine-outliner-cfi-1.ll
new file mode 100644 (file)
index 0000000..67c859c
--- /dev/null
@@ -0,0 +1,78 @@
+; RUN: llc --verify-machineinstrs --force-dwarf-frame-section %s -o - | FileCheck %s
+target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
+target triple = "thumbv7m-unknown-unknown-eabi"
+
+; Derived from
+; volatile int a, b, c, d, e, f, g, h;
+;
+; int x() {
+;   int r = (a + b) / (c + d) + e + f;
+;   return r + 1;
+; }
+;
+;
+; int y() {
+;   int r = (a + b) / (c + d) + e + f;
+;   return r + 2;
+; }
+; Checks that CFI instruction are emitted around saves/restores of
+; the LR on stack.
+
+@a = dso_local global i32 0, align 4
+@b = dso_local global i32 0, align 4
+@c = dso_local global i32 0, align 4
+@d = dso_local global i32 0, align 4
+@e = dso_local global i32 0, align 4
+@f = dso_local global i32 0, align 4
+
+define dso_local i32 @x() local_unnamed_addr #0 {
+entry:
+  %0 = load volatile i32, i32* @a, align 4
+  %1 = load volatile i32, i32* @b, align 4
+  %add = add nsw i32 %1, %0
+  %2 = load volatile i32, i32* @c, align 4
+  %3 = load volatile i32, i32* @d, align 4
+  %add1 = add nsw i32 %3, %2
+  %div = sdiv i32 %add, %add1
+  %4 = load volatile i32, i32* @e, align 4
+  %5 = load volatile i32, i32* @f, align 4
+  %add2 = add i32 %div, 1
+  %add3 = add i32 %add2, %4
+  %add4 = add i32 %add3, %5
+  ret i32 %add4
+}
+; CHECK-LABEL: x:
+; CHECK:       str lr, [sp, #-8]!
+; CHECK-NEXT:  .cfi_def_cfa_offset 8
+; CHECK-NEXT:  .cfi_offset lr, -8
+; CHECK-NEXT:  bl  OUTLINED_FUNCTION_0
+; CHECK-NEXT:  ldr lr, [sp], #8
+; CHECK-NEXT:  .cfi_def_cfa_offset 0
+; CHECK-NEXT:  .cfi_restore lr
+
+define dso_local i32 @y() local_unnamed_addr #0 {
+entry:
+  %0 = load volatile i32, i32* @a, align 4
+  %1 = load volatile i32, i32* @b, align 4
+  %add = add nsw i32 %1, %0
+  %2 = load volatile i32, i32* @c, align 4
+  %3 = load volatile i32, i32* @d, align 4
+  %add1 = add nsw i32 %3, %2
+  %div = sdiv i32 %add, %add1
+  %4 = load volatile i32, i32* @e, align 4
+  %5 = load volatile i32, i32* @f, align 4
+  %add2 = add i32 %div, 2
+  %add3 = add i32 %add2, %4
+  %add4 = add i32 %add3, %5
+  ret i32 %add4
+}
+; CHECK-LABEL: y:
+; CHECK:       str lr, [sp, #-8]!
+; CHECK-NEXT:  .cfi_def_cfa_offset 8
+; CHECK-NEXT:  .cfi_offset lr, -8
+; CHECK-NEXT:  bl  OUTLINED_FUNCTION_0
+; CHECK-NEXT:  ldr lr, [sp], #8
+; CHECK-NEXT:  .cfi_def_cfa_offset 0
+; CHECK-NEXT:  .cfi_restore lr
+
+attributes #0 = { minsize nofree norecurse nounwind optsize }
diff --git a/llvm/test/CodeGen/ARM/machine-outliner-cfi-2.ll b/llvm/test/CodeGen/ARM/machine-outliner-cfi-2.ll
new file mode 100644 (file)
index 0000000..e5dee8f
--- /dev/null
@@ -0,0 +1,73 @@
+; RUN: llc --verify-machineinstrs --force-dwarf-frame-section %s -o - | FileCheck %s
+target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
+target triple = "thumbv7m-unknown-unknown-eabi"
+
+; Derived from
+; volatile int a, b, c, d, e, f, g, h;
+;
+; int x() {
+;   int r = a + b + c + d + e + f;
+;   return r + 1;
+; }
+;
+;
+; int y() {
+;   int r = a + b + c + d + e + f;
+;   return r + 2;
+; }
+; Check CFI instructions when LR is saved/restored to/from a register.
+
+@a = dso_local global i32 0, align 4
+@b = dso_local global i32 0, align 4
+@c = dso_local global i32 0, align 4
+@d = dso_local global i32 0, align 4
+@e = dso_local global i32 0, align 4
+@f = dso_local global i32 0, align 4
+
+define dso_local i32 @x() local_unnamed_addr #0 {
+entry:
+  %0 = load volatile i32, i32* @a, align 4
+  %1 = load volatile i32, i32* @b, align 4
+  %2 = load volatile i32, i32* @c, align 4
+  %3 = load volatile i32, i32* @d, align 4
+  %4 = load volatile i32, i32* @e, align 4
+  %5 = load volatile i32, i32* @f, align 4
+  %add = add i32 %0, 1
+  %add1 = add i32 %add, %1
+  %add2 = add i32 %add1, %2
+  %add3 = add i32 %add2, %3
+  %add4 = add i32 %add3, %4
+  %add5 = add i32 %add4, %5
+  ret i32 %add5
+}
+; CHECK-LABEL: x:
+; CHECK:      mov r3, lr
+; CHECK-NEXT: .cfi_register lr, r3
+; CHECK-NEXT: bl  OUTLINED_FUNCTION_0
+; CHECK-NEXT: mov lr, r3
+; CHECK-NEXT: .cfi_restore lr
+
+define dso_local i32 @y() local_unnamed_addr #0 {
+entry:
+  %0 = load volatile i32, i32* @a, align 4
+  %1 = load volatile i32, i32* @b, align 4
+  %2 = load volatile i32, i32* @c, align 4
+  %3 = load volatile i32, i32* @d, align 4
+  %4 = load volatile i32, i32* @e, align 4
+  %5 = load volatile i32, i32* @f, align 4
+  %add = add i32 %0, 2
+  %add1 = add i32 %add, %1
+  %add2 = add i32 %add1, %2
+  %add3 = add i32 %add2, %3
+  %add4 = add i32 %add3, %4
+  %add5 = add i32 %add4, %5
+  ret i32 %add5
+}
+; CHECK-LABEL: y:
+; CHECK:      mov r3, lr
+; CHECK-NEXT: .cfi_register lr, r3
+; CHECK-NEXT: bl  OUTLINED_FUNCTION_0
+; CHECK-NEXT: mov lr, r3
+; CHECK-NEXT: .cfi_restore lr
+
+attributes #0 = { minsize nofree norecurse nounwind optsize  }
diff --git a/llvm/test/CodeGen/ARM/machine-outliner-cfi-3.ll b/llvm/test/CodeGen/ARM/machine-outliner-cfi-3.ll
new file mode 100644 (file)
index 0000000..13c055f
--- /dev/null
@@ -0,0 +1,78 @@
+; RUN: llc --verify-machineinstrs --force-dwarf-frame-section %s -o - | FileCheck %s
+target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
+target triple = "thumbv7m-unknown-unknown-eabi"
+
+; Derived from
+; __attribute__((noinline)) int h(int a, int b) { return a + b; }
+;
+; int f(int a, int b, int c, int d) {
+;     if (a < 0)
+;         return -1;
+;     a = h(a, b);
+;     return 2 + a * (a + b) / (c + d);
+; }
+;
+; int g(int a, int b, int c, int d) {
+;     if (a < 0)
+;         return -1;
+;     a = h(a, b);
+;     return 1 + a * (a + b) / (c + d);
+; }
+; Check CFI instructions inside the outlined function.
+
+define dso_local i32 @h(i32 %a, i32 %b) local_unnamed_addr #0 {
+entry:
+  %add = add nsw i32 %b, %a
+  ret i32 %add
+}
+
+define dso_local i32 @f(i32 %a, i32 %b, i32 %c, i32 %d) local_unnamed_addr #1 {
+entry:
+  %cmp = icmp slt i32 %a, 0
+  br i1 %cmp, label %return, label %if.end
+
+if.end:                                           ; preds = %entry
+  %call = tail call i32 @h(i32 %a, i32 %b) #2
+  %add = add nsw i32 %call, %b
+  %mul = mul nsw i32 %add, %call
+  %add1 = add nsw i32 %d, %c
+  %div = sdiv i32 %mul, %add1
+  %add2 = add nsw i32 %div, 2
+  br label %return
+
+return:                                           ; preds = %entry, %if.end
+  %retval.0 = phi i32 [ %add2, %if.end ], [ -1, %entry ]
+  ret i32 %retval.0
+}
+
+define dso_local i32 @g(i32 %a, i32 %b, i32 %c, i32 %d) local_unnamed_addr #1 {
+entry:
+  %cmp = icmp slt i32 %a, 0
+  br i1 %cmp, label %return, label %if.end
+
+if.end:                                           ; preds = %entry
+  %call = tail call i32 @h(i32 %a, i32 %b) #2
+  %add = add nsw i32 %call, %b
+  %mul = mul nsw i32 %add, %call
+  %add1 = add nsw i32 %d, %c
+  %div = sdiv i32 %mul, %add1
+  %add2 = add nsw i32 %div, 1
+  br label %return
+
+return:                                           ; preds = %entry, %if.end
+  %retval.0 = phi i32 [ %add2, %if.end ], [ -1, %entry ]
+  ret i32 %retval.0
+}
+
+; CHECK-LABEL: OUTLINED_FUNCTION_0:
+; CHECK:      str lr, [sp, #-8]!
+; CHECK-NEXT: .cfi_def_cfa_offset 8
+; CHECK-NEXT: .cfi_offset lr, -8
+; CHECK:      ldr lr, [sp], #8
+; CHECK-NEXT: .cfi_def_cfa_offset 0
+; CHECK-NEXT: .cfi_restore lr
+; CHECK-NEXT: bx  lr
+
+attributes #0 = { minsize noinline norecurse nounwind optsize readnone }
+attributes #1 = { minsize norecurse nounwind optsize readnone }
+attributes #2 = { minsize optsize }