[ARM] Fix unwind info for Thumb1 functions that save high registers.
authorEli Friedman <efriedma@quicinc.com>
Tue, 2 Jul 2019 21:35:15 +0000 (21:35 +0000)
committerEli Friedman <efriedma@quicinc.com>
Tue, 2 Jul 2019 21:35:15 +0000 (21:35 +0000)
There were two issues here: one, some of the relevant instructions were
missing the expected "FrameSetup" flag, and two,
ARMAsmPrinter::EmitUnwindingInstruction wasn't expecting "mov"
instructions in the prologue.

I'm sticking the additional state into ARMFunctionInfo so it's obvious
it only applies to the current function.

I considered a few alternative approaches where we would compute the
correct unwind information as part of the prologue/epilogue lowering,
but it seems like a lot of work to introduce pseudo-instructions, and
the current code seems to be reliable enough.

Fixes https://bugs.llvm.org/show_bug.cgi?id=42408.

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

llvm-svn: 364970

llvm/lib/Target/ARM/ARMAsmPrinter.cpp
llvm/lib/Target/ARM/ARMMachineFunctionInfo.h
llvm/lib/Target/ARM/Thumb1FrameLowering.cpp
llvm/test/CodeGen/Thumb/callee_save.ll

index d217cda..e290772 100644 (file)
@@ -1071,7 +1071,6 @@ void ARMAsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) {
   const TargetRegisterInfo *TargetRegInfo =
     MF.getSubtarget().getRegisterInfo();
   const MachineRegisterInfo &MachineRegInfo = MF.getRegInfo();
-  const ARMFunctionInfo &AFI = *MF.getInfo<ARMFunctionInfo>();
 
   unsigned FramePtr = TargetRegInfo->getFrameRegister(MF);
   unsigned Opc = MI->getOpcode();
@@ -1135,7 +1134,12 @@ void ARMAsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) {
           Pad += Width;
           continue;
         }
-        RegList.push_back(MO.getReg());
+        // Check for registers that are remapped (for a Thumb1 prologue that
+        // saves high registers).
+        unsigned Reg = MO.getReg();
+        if (unsigned RemappedReg = AFI->EHPrologueRemappedRegs.lookup(Reg))
+          Reg = RemappedReg;
+        RegList.push_back(Reg);
       }
       break;
     case ARM::STR_PRE_IMM:
@@ -1185,7 +1189,7 @@ void ARMAsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) {
         unsigned CPI = MI->getOperand(1).getIndex();
         const MachineConstantPool *MCP = MF.getConstantPool();
         if (CPI >= MCP->getConstants().size())
-          CPI = AFI.getOriginalCPIdx(CPI);
+          CPI = AFI->getOriginalCPIdx(CPI);
         assert(CPI != -1U && "Invalid constpool index");
 
         // Derive the actual offset.
@@ -1215,8 +1219,12 @@ void ARMAsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) {
     } else if (DstReg == ARM::SP) {
       MI->print(errs());
       llvm_unreachable("Unsupported opcode for unwinding information");
-    }
-    else {
+    } else if (Opc == ARM::tMOVr) {
+      // If a Thumb1 function spills r8-r11, we copy the values to low
+      // registers before pushing them. Record the copy so we can emit the
+      // correct ".save" later.
+      AFI->EHPrologueRemappedRegs[DstReg] = SrcReg;
+    } else {
       MI->print(errs());
       llvm_unreachable("Unsupported opcode for unwinding information");
     }
index f96124e..90d794c 100644 (file)
@@ -245,6 +245,8 @@ public:
   void setPromotedConstpoolIncrease(int Sz) {
     PromotedGlobalsIncrease = Sz;
   }
+
+  DenseMap<unsigned, unsigned> EHPrologueRemappedRegs;
 };
 
 } // end namespace llvm
index 1b32df2..426e9a0 100644 (file)
@@ -889,8 +889,9 @@ spillCalleeSavedRegisters(MachineBasicBlock &MBB,
         findNextOrderedReg(std::begin(AllCopyRegs), CopyRegs, AllCopyRegsEnd);
 
     // Create the PUSH, but don't insert it yet (the MOVs need to come first).
-    MachineInstrBuilder PushMIB =
-        BuildMI(MF, DL, TII.get(ARM::tPUSH)).add(predOps(ARMCC::AL));
+    MachineInstrBuilder PushMIB = BuildMI(MF, DL, TII.get(ARM::tPUSH))
+                                      .add(predOps(ARMCC::AL))
+                                      .setMIFlags(MachineInstr::FrameSetup);
 
     SmallVector<unsigned, 4> RegsToPush;
     while (HiRegToSave != AllHighRegsEnd && CopyReg != AllCopyRegsEnd) {
@@ -903,7 +904,8 @@ spillCalleeSavedRegisters(MachineBasicBlock &MBB,
         BuildMI(MBB, MI, DL, TII.get(ARM::tMOVr))
             .addReg(*CopyReg, RegState::Define)
             .addReg(*HiRegToSave, getKillRegState(isKill))
-            .add(predOps(ARMCC::AL));
+            .add(predOps(ARMCC::AL))
+            .setMIFlags(MachineInstr::FrameSetup);
 
         // Record the register that must be added to the PUSH.
         RegsToPush.push_back(*CopyReg);
index b9216e7..20140e4 100644 (file)
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
 ; RUN: llc -mtriple=thumbv6m-none-eabi < %s | FileCheck %s
 
 declare i8* @llvm.returnaddress(i32)
@@ -6,10 +7,14 @@ declare i8* @llvm.returnaddress(i32)
 ; only need to save the low registers.
 define void @low_regs_only() {
 ; CHECK-LABEL: low_regs_only:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    .save {r4, r5, r6, r7, lr}
+; CHECK-NEXT:    push {r4, r5, r6, r7, lr}
+; CHECK-NEXT:    @APP
+; CHECK-NEXT:    @NO_APP
+; CHECK-NEXT:    pop {r4, r5, r6, r7, pc}
 entry:
-; CHECK: push {r4, r5, r6, r7, lr}
   tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7}"()
-; CHECK: pop {r4, r5, r6, r7, pc}
   ret void
 }
 
@@ -17,12 +22,17 @@ entry:
 ; argument/return register to help save/restore it.
 define void @one_high() {
 ; CHECK-LABEL: one_high:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    mov r3, r8
+; CHECK-NEXT:    .save {r8}
+; CHECK-NEXT:    push {r3}
+; CHECK-NEXT:    @APP
+; CHECK-NEXT:    @NO_APP
+; CHECK-NEXT:    pop {r0}
+; CHECK-NEXT:    mov r8, r0
+; CHECK-NEXT:    bx lr
 entry:
-; CHECK: mov [[SAVEREG:r[0-3]]], r8
-; CHECK: push {[[SAVEREG]]}
   tail call void asm sideeffect "", "~{r8}"()
-; CHECK: pop {[[RESTOREREG:r[0-3]]]}
-; CHECK: mov r8, [[RESTOREREG]]
   ret void
 }
 
@@ -30,18 +40,23 @@ entry:
 ; 4 arg/return regs for the save/restore.
 define void @four_high() {
 ; CHECK-LABEL: four_high:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    mov r3, r11
+; CHECK-NEXT:    mov r2, r10
+; CHECK-NEXT:    mov r1, r9
+; CHECK-NEXT:    mov r0, r8
+; CHECK-NEXT:    .save {r8, r9, r10, r11}
+; CHECK-NEXT:    push {r0, r1, r2, r3}
+; CHECK-NEXT:    @APP
+; CHECK-NEXT:    @NO_APP
+; CHECK-NEXT:    pop {r0, r1, r2, r3}
+; CHECK-NEXT:    mov r8, r0
+; CHECK-NEXT:    mov r9, r1
+; CHECK-NEXT:    mov r10, r2
+; CHECK-NEXT:    mov r11, r3
+; CHECK-NEXT:    bx lr
 entry:
-; CHECK: mov r3, r11
-; CHECK: mov r2, r10
-; CHECK: mov r1, r9
-; CHECK: mov r0, r8
-; CHECK: push {r0, r1, r2, r3}
   tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"()
-; CHECK: pop {r0, r1, r2, r3}
-; CHECK: mov r8, r0
-; CHECK: mov r9, r1
-; CHECK: mov r10, r2
-; CHECK: mov r11, r3
   ret void
 }
 
@@ -50,14 +65,19 @@ entry:
 ; lr to save/restore r8.
 define void @one_high_one_low() {
 ; CHECK-LABEL: one_high_one_low:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    .save {r4, r7, lr}
+; CHECK-NEXT:    push {r4, r7, lr}
+; CHECK-NEXT:    mov lr, r8
+; CHECK-NEXT:    .save {r8}
+; CHECK-NEXT:    push {lr}
+; CHECK-NEXT:    @APP
+; CHECK-NEXT:    @NO_APP
+; CHECK-NEXT:    pop {r0}
+; CHECK-NEXT:    mov r8, r0
+; CHECK-NEXT:    pop {r4, r7, pc}
 entry:
-; CHECK: push {r4, r7, lr}
-; CHECK: mov [[SAVEREG:r0|r1|r2|r3|r4|r7|lr]], r8
-; CHECK: push {[[SAVEREG]]}
   tail call void asm sideeffect "", "~{r4},~{r8}"()
-; CHECK: pop {[[RESTOREREG:r0|r1|r2|r3|r4|r7]]}
-; CHECK: mov r8, [[RESTOREREG]]
-; CHECK: pop {r4, r7, pc}
   ret void
 }
 
@@ -65,20 +85,25 @@ entry:
 ; first push so can be used for pushing the high registers.
 define void @four_high_four_low() {
 ; CHECK-LABEL: four_high_four_low:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    .save {r4, r5, r6, r7, lr}
+; CHECK-NEXT:    push {r4, r5, r6, r7, lr}
+; CHECK-NEXT:    mov lr, r11
+; CHECK-NEXT:    mov r7, r10
+; CHECK-NEXT:    mov r6, r9
+; CHECK-NEXT:    mov r5, r8
+; CHECK-NEXT:    .save {r8, r9, r10, r11}
+; CHECK-NEXT:    push {r5, r6, r7, lr}
+; CHECK-NEXT:    @APP
+; CHECK-NEXT:    @NO_APP
+; CHECK-NEXT:    pop {r0, r1, r2, r3}
+; CHECK-NEXT:    mov r8, r0
+; CHECK-NEXT:    mov r9, r1
+; CHECK-NEXT:    mov r10, r2
+; CHECK-NEXT:    mov r11, r3
+; CHECK-NEXT:    pop {r4, r5, r6, r7, pc}
 entry:
-; CHECK: push {r4, r5, r6, r7, lr}
-; CHECK: mov lr, r11
-; CHECK: mov r7, r10
-; CHECK: mov r6, r9
-; CHECK: mov r5, r8
-; CHECK: push {r5, r6, r7, lr}
   tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"()
-; CHECK: pop {r0, r1, r2, r3}
-; CHECK: mov r8, r0
-; CHECK: mov r9, r1
-; CHECK: mov r10, r2
-; CHECK: mov r11, r3
-; CHECK: pop {r4, r5, r6, r7, pc}
   ret void
 }
 
@@ -87,21 +112,27 @@ entry:
 ; cannot be used while saving/restoring the high regs.
 define void @four_high_four_low_frame_ptr() "no-frame-pointer-elim"="true" {
 ; CHECK-LABEL: four_high_four_low_frame_ptr:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    .save {r4, r5, r6, r7, lr}
+; CHECK-NEXT:    push {r4, r5, r6, r7, lr}
+; CHECK-NEXT:    .setfp r7, sp, #12
+; CHECK-NEXT:    add r7, sp, #12
+; CHECK-NEXT:    mov lr, r11
+; CHECK-NEXT:    mov r6, r10
+; CHECK-NEXT:    mov r5, r9
+; CHECK-NEXT:    mov r4, r8
+; CHECK-NEXT:    .save {r8, r9, r10, r11}
+; CHECK-NEXT:    push {r4, r5, r6, lr}
+; CHECK-NEXT:    @APP
+; CHECK-NEXT:    @NO_APP
+; CHECK-NEXT:    pop {r0, r1, r2, r3}
+; CHECK-NEXT:    mov r8, r0
+; CHECK-NEXT:    mov r9, r1
+; CHECK-NEXT:    mov r10, r2
+; CHECK-NEXT:    mov r11, r3
+; CHECK-NEXT:    pop {r4, r5, r6, r7, pc}
 entry:
-; CHECK: push {r4, r5, r6, r7, lr}
-; CHECK: add r7, sp, #12
-; CHECK: mov lr, r11
-; CHECK: mov r6, r10
-; CHECK: mov r5, r9
-; CHECK: mov r4, r8
-; CHECK: push {r4, r5, r6, lr}
   tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"()
-; CHECK: pop {r0, r1, r2, r3}
-; CHECK: mov r8, r0
-; CHECK: mov r9, r1
-; CHECK: mov r10, r2
-; CHECK: mov r11, r3
-; CHECK: pop {r4, r5, r6, r7, pc}
   ret void
 }
 
@@ -110,21 +141,29 @@ entry:
 ; the high regs.
 define void @four_high_four_low_frame_ptr_ret_addr() "no-frame-pointer-elim"="true" {
 ; CHECK-LABEL: four_high_four_low_frame_ptr_ret_addr:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    .save {r4, r5, r6, r7, lr}
+; CHECK-NEXT:    push {r4, r5, r6, r7, lr}
+; CHECK-NEXT:    .setfp r7, sp, #12
+; CHECK-NEXT:    add r7, sp, #12
+; CHECK-NEXT:    mov r6, r11
+; CHECK-NEXT:    mov r5, r10
+; CHECK-NEXT:    mov r4, r9
+; CHECK-NEXT:    mov r3, r8
+; CHECK-NEXT:    .save {r8, r9, r10, r11}
+; CHECK-NEXT:    push {r3, r4, r5, r6}
+; CHECK-NEXT:    mov r0, lr
+; CHECK-NEXT:    @APP
+; CHECK-NEXT:    @NO_APP
+; CHECK-NEXT:    pop {r0, r1, r2, r3}
+; CHECK-NEXT:    mov r8, r0
+; CHECK-NEXT:    mov r9, r1
+; CHECK-NEXT:    mov r10, r2
+; CHECK-NEXT:    mov r11, r3
+; CHECK-NEXT:    pop {r4, r5, r6, r7, pc}
 entry:
-; CHECK: push {r4, r5, r6, r7, lr}
-; CHECK: mov r6, r11
-; CHECK: mov r5, r10
-; CHECK: mov r4, r9
-; CHECK: mov r3, r8
-; CHECK: push {r3, r4, r5, r6}
   %a = tail call i8* @llvm.returnaddress(i32 0)
   tail call void asm sideeffect "", "r,~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"(i8* %a)
-; CHECK: pop {r0, r1, r2, r3}
-; CHECK: mov r8, r0
-; CHECK: mov r9, r1
-; CHECK: mov r10, r2
-; CHECK: mov r11, r3
-; CHECK: pop {r4, r5, r6, r7, pc}
   ret void
 }
 
@@ -132,20 +171,25 @@ entry:
 ; registers, so that we can use them for saving the high regs.
 define void @four_high_four_arg(i32 %a, i32 %b, i32 %c, i32 %d) {
 ; CHECK-LABEL: four_high_four_arg:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    .save {r5, r6, r7, lr}
+; CHECK-NEXT:    push {r5, r6, r7, lr}
+; CHECK-NEXT:    mov lr, r11
+; CHECK-NEXT:    mov r7, r10
+; CHECK-NEXT:    mov r6, r9
+; CHECK-NEXT:    mov r5, r8
+; CHECK-NEXT:    .save {r8, r9, r10, r11}
+; CHECK-NEXT:    push {r5, r6, r7, lr}
+; CHECK-NEXT:    @APP
+; CHECK-NEXT:    @NO_APP
+; CHECK-NEXT:    pop {r0, r1, r2, r3}
+; CHECK-NEXT:    mov r8, r0
+; CHECK-NEXT:    mov r9, r1
+; CHECK-NEXT:    mov r10, r2
+; CHECK-NEXT:    mov r11, r3
+; CHECK-NEXT:    pop {r5, r6, r7, pc}
 entry:
-; CHECK: push    {r5, r6, r7, lr}
-; CHECK: mov     lr, r11
-; CHECK: mov     r7, r10
-; CHECK: mov     r6, r9
-; CHECK: mov     r5, r8
-; CHECK: push    {r5, r6, r7, lr}
   tail call void asm sideeffect "", "r,r,r,r,~{r8},~{r9},~{r10},~{r11}"(i32 %a, i32 %b, i32 %c, i32 %d)
-; CHECK: pop     {r0, r1, r2, r3}
-; CHECK: mov     r8, r0
-; CHECK: mov     r9, r1
-; CHECK: mov     r10, r2
-; CHECK: mov     r11, r3
-; CHECK: pop     {r5, r6, r7, pc}
   ret void
 }
 
@@ -153,24 +197,33 @@ entry:
 ; registers, so that we can use them for restoring the high regs.
 define <4 x i32> @four_high_four_return() {
 ; CHECK-LABEL: four_high_four_return:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    .save {r4, r5, r6, r7, lr}
+; CHECK-NEXT:    push {r4, r5, r6, r7, lr}
+; CHECK-NEXT:    mov lr, r11
+; CHECK-NEXT:    mov r7, r10
+; CHECK-NEXT:    mov r6, r9
+; CHECK-NEXT:    mov r5, r8
+; CHECK-NEXT:    .save {r8, r9, r10, r11}
+; CHECK-NEXT:    push {r5, r6, r7, lr}
+; CHECK-NEXT:    @APP
+; CHECK-NEXT:    @NO_APP
+; CHECK-NEXT:    movs r0, #1
+; CHECK-NEXT:    movs r1, #2
+; CHECK-NEXT:    movs r2, #3
+; CHECK-NEXT:    movs r3, #4
+; CHECK-NEXT:    pop {r4, r5, r6, r7}
+; CHECK-NEXT:    mov r8, r4
+; CHECK-NEXT:    mov r9, r5
+; CHECK-NEXT:    mov r10, r6
+; CHECK-NEXT:    mov r11, r7
+; CHECK-NEXT:    pop {r4, r5, r6, r7, pc}
 entry:
-; CHECK: push    {r4, r5, r6, r7, lr}
-; CHECK: mov     lr, r11
-; CHECK: mov     r7, r10
-; CHECK: mov     r6, r9
-; CHECK: mov     r5, r8
-; CHECK: push    {r5, r6, r7, lr}
   tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"()
   %vecinit = insertelement <4 x i32> undef, i32 1, i32 0
   %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1
   %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2
   %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3
-; CHECK: pop     {r4, r5, r6, r7}
-; CHECK: mov     r8, r4
-; CHECK: mov     r9, r5
-; CHECK: mov     r10, r6
-; CHECK: mov     r11, r7
-; CHECK: pop     {r4, r5, r6, r7, pc}
   ret <4 x i32> %vecinit13
 }
 
@@ -179,16 +232,39 @@ entry:
 ; r5, r6), with which to save 4 high registers, so we have to use two pushes
 ; and pops.
 define <4 x i32> @all_of_the_above(i32 %a, i32 %b, i32 %c, i32 %d) "no-frame-pointer-elim"="true" {
-; CHECK-LABEL: all_of_the_above
+; CHECK-LABEL: all_of_the_above:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    .save {r4, r5, r6, r7, lr}
+; CHECK-NEXT:    push {r4, r5, r6, r7, lr}
+; CHECK-NEXT:    .setfp r7, sp, #12
+; CHECK-NEXT:    add r7, sp, #12
+; CHECK-NEXT:    mov r6, r11
+; CHECK-NEXT:    mov r5, r10
+; CHECK-NEXT:    mov r4, r9
+; CHECK-NEXT:    .save {r9, r10, r11}
+; CHECK-NEXT:    push {r4, r5, r6}
+; CHECK-NEXT:    mov r6, r8
+; CHECK-NEXT:    .save {r8}
+; CHECK-NEXT:    push {r6}
+; CHECK-NEXT:    .pad #4
+; CHECK-NEXT:    sub sp, #4
+; CHECK-NEXT:    mov r4, lr
+; CHECK-NEXT:    str r4, [sp] @ 4-byte Spill
+; CHECK-NEXT:    @APP
+; CHECK-NEXT:    @NO_APP
+; CHECK-NEXT:    movs r1, #2
+; CHECK-NEXT:    movs r2, #3
+; CHECK-NEXT:    movs r3, #4
+; CHECK-NEXT:    ldr r0, [sp] @ 4-byte Reload
+; CHECK-NEXT:    add sp, #4
+; CHECK-NEXT:    pop {r4, r5, r6}
+; CHECK-NEXT:    mov r8, r4
+; CHECK-NEXT:    mov r9, r5
+; CHECK-NEXT:    mov r10, r6
+; CHECK-NEXT:    pop {r4}
+; CHECK-NEXT:    mov r11, r4
+; CHECK-NEXT:    pop {r4, r5, r6, r7, pc}
 entry:
-; CHECK: push    {r4, r5, r6, r7, lr}
-; CHECK: add     r7, sp, #12
-; CHECK: mov     r6, r11
-; CHECK: mov     r5, r10
-; CHECK: mov     r4, r9
-; CHECK: push    {r4, r5, r6}
-; CHECK: mov     r6, r8
-; CHECK: push    {r6}
   tail call void asm sideeffect "", "r,r,r,r,~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"(i32 %a, i32 %b, i32 %c, i32 %d)
   %e = tail call i8* @llvm.returnaddress(i32 0)
   %f = ptrtoint i8* %e to i32
@@ -196,13 +272,6 @@ entry:
   %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1
   %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2
   %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3
-; CHECK: pop     {r4, r5, r6}
-; CHECK: mov     r8, r4
-; CHECK: mov     r9, r5
-; CHECK: mov     r10, r6
-; CHECK: pop     {r4}
-; CHECK: mov     r11, r4
-; CHECK: pop     {r4, r5, r6, r7, pc}
   ret <4 x i32> %vecinit13
 }
 
@@ -212,25 +281,42 @@ entry:
 ; used when restoring sp from fp, as that happens before the first pop.
 define <4 x i32> @base_pointer(i32 %a) {
 ; CHECK-LABEL: base_pointer:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    .save {r4, r6, r7, lr}
+; CHECK-NEXT:    push {r4, r6, r7, lr}
+; CHECK-NEXT:    .setfp r7, sp, #8
+; CHECK-NEXT:    add r7, sp, #8
+; CHECK-NEXT:    mov lr, r9
+; CHECK-NEXT:    mov r6, r8
+; CHECK-NEXT:    .save {r8, r9}
+; CHECK-NEXT:    push {r6, lr}
+; CHECK-NEXT:    mov r6, sp
+; CHECK-NEXT:    lsls r0, r0, #2
+; CHECK-NEXT:    adds r0, r0, #7
+; CHECK-NEXT:    movs r1, #7
+; CHECK-NEXT:    bics r0, r1
+; CHECK-NEXT:    mov r1, sp
+; CHECK-NEXT:    subs r0, r1, r0
+; CHECK-NEXT:    mov sp, r0
+; CHECK-NEXT:    @APP
+; CHECK-NEXT:    @NO_APP
+; CHECK-NEXT:    movs r0, #1
+; CHECK-NEXT:    movs r1, #2
+; CHECK-NEXT:    movs r2, #3
+; CHECK-NEXT:    movs r3, #4
+; CHECK-NEXT:    subs r4, r7, #7
+; CHECK-NEXT:    subs r4, #9
+; CHECK-NEXT:    mov sp, r4
+; CHECK-NEXT:    pop {r4, r6}
+; CHECK-NEXT:    mov r8, r4
+; CHECK-NEXT:    mov r9, r6
+; CHECK-NEXT:    pop {r4, r6, r7, pc}
 entry:
-; CHECK: push    {r4, r6, r7, lr}
-; CHECK: add     r7, sp, #8
-; CHECK: mov     lr, r9
-; CHECK: mov     r6, r8
-; CHECK: push    {r6, lr}
-; CHECK: mov     r6, sp
   %b = alloca i32, i32 %a
   call void asm sideeffect "", "r,~{r8},~{r9}"(i32* %b)
   %vecinit = insertelement <4 x i32> undef, i32 1, i32 0
   %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1
   %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2
   %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3
-; CHECK: subs    r4, r7, #7
-; CHECK: subs    r4, #9
-; CHECK: mov     sp, r4
-; CHECK: pop     {r4, r6}
-; CHECK: mov     r8, r4
-; CHECK: mov     r9, r6
-; CHECK: pop     {r4, r6, r7, pc}
   ret <4 x i32> %vecinit13
 }