[llvm] 40614e1 - [ARM] Save and restore CPSR around tMOVimm32

Oliver Stannard via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 24 06:15:40 PDT 2023


Author: Oliver Stannard
Date: 2023-08-24T14:15:02+01:00
New Revision: 40614e1c1483c698a228c3d038621e834d94086c

URL: https://github.com/llvm/llvm-project/commit/40614e1c1483c698a228c3d038621e834d94086c
DIFF: https://github.com/llvm/llvm-project/commit/40614e1c1483c698a228c3d038621e834d94086c.diff

LOG: [ARM] Save and restore CPSR around tMOVimm32

When resolving a frame index with a large offset for v6M execute-only,
we emit a tMOVimm32 pseudo-instruction, which later gets lowered to a
sequence of instructions, all of which are flag-setting. However, a
frame index may be generated for a register spill or reload instruction,
which can be inserted at a point where CPSR is live. This patch inserts
MRS and MSR instructions around the tMOVimm32 to save and restore the
value of CPSR, if CPSR is live at that point.

This may need up to two virtual registers (one to build the immediate
value, one to save CPSR) during frame index lowering, which happens
after register allocation, so we need to ensure two spill slots are
avilable to the register scavenger to ensure it can free up enough
registers for this.

There is no test for the emission (or not) of the MRS/MSR pair, because
it requires a spill or reload to be inserted at a point where CPSR is
live, which requires a large, complex function and is fragile enough
that any optimisation changes will break the test. This bug was easily
found by csmith with -verify-machineinstrs, which I now run regularly on
v6M execute-only (and many other combinations).

Patch by John Brawn and myself.

Reviewed By: stuij

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

Added: 
    llvm/test/CodeGen/ARM/execute-only-save-cpsr.mir

Modified: 
    llvm/lib/Target/ARM/ARMFrameLowering.cpp
    llvm/lib/Target/ARM/ThumbRegisterInfo.cpp
    llvm/test/CodeGen/ARM/large-stack.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/ARM/ARMFrameLowering.cpp b/llvm/lib/Target/ARM/ARMFrameLowering.cpp
index 7bcf95efa8f704..387ec508341b2c 100644
--- a/llvm/lib/Target/ARM/ARMFrameLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMFrameLowering.cpp
@@ -2238,18 +2238,20 @@ static bool requiresAAPCSFrameRecord(const MachineFunction &MF) {
          (Subtarget.createAAPCSFrameChain() && MF.getFrameInfo().hasCalls());
 }
 
-// Thumb1 may require a spill when storing to a frame index through FP, for
-// cases where FP is a high register (R11). This scans the function for cases
-// where this may happen.
+// Thumb1 may require a spill when storing to a frame index through FP (or any
+// access with execute-only), for cases where FP is a high register (R11). This
+// scans the function for cases where this may happen.
 static bool canSpillOnFrameIndexAccess(const MachineFunction &MF,
                                        const TargetFrameLowering &TFI) {
   const ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>();
   if (!AFI->isThumb1OnlyFunction())
     return false;
 
+  const ARMSubtarget &STI = MF.getSubtarget<ARMSubtarget>();
   for (const auto &MBB : MF)
     for (const auto &MI : MBB)
-      if (MI.getOpcode() == ARM::tSTRspi || MI.getOpcode() == ARM::tSTRi)
+      if (MI.getOpcode() == ARM::tSTRspi || MI.getOpcode() == ARM::tSTRi ||
+          STI.genExecuteOnly())
         for (const auto &Op : MI.operands())
           if (Op.isFI()) {
             Register Reg;
@@ -2532,18 +2534,19 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF,
         CS1Spilled = true;
     }
 
-    // This is true when we inserted a spill for a callee-save GPR which is
-    // not otherwise used by the function. This guaranteees it is possible
-    // to scavenge a register to hold the address of a stack slot. On Thumb1,
-    // the register must be a valid operand to tSTRi, i.e. r4-r7. For other
-    // subtargets, this is any GPR, i.e. r4-r11 or lr.
+    // This is the number of extra spills inserted for callee-save GPRs which
+    // would not otherwise be used by the function. When greater than zero it
+    // guaranteees that it is possible to scavenge a register to hold the
+    // address of a stack slot. On Thumb1, the register must be a valid operand
+    // to tSTRi, i.e. r4-r7. For other subtargets, this is any GPR, i.e. r4-r11
+    // or lr.
     //
     // If we don't insert a spill, we instead allocate an emergency spill
     // slot, which can be used by scavenging to spill an arbitrary register.
     //
     // We currently don't try to figure out whether any specific instruction
     // requires scavening an additional register.
-    bool ExtraCSSpill = false;
+    unsigned NumExtraCSSpill = 0;
 
     if (AFI->isThumb1OnlyFunction()) {
       // For Thumb1-only targets, we need some low registers when we save and
@@ -2652,7 +2655,7 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF,
         CS1Spilled = true;
         assert(!MRI.isReserved(Reg) && "Should not be reserved");
         if (Reg != ARM::LR && !MRI.isPhysRegUsed(Reg))
-          ExtraCSSpill = true;
+          NumExtraCSSpill++;
         UnspilledCS1GPRs.erase(llvm::find(UnspilledCS1GPRs, Reg));
         if (Reg == ARM::LR)
           LRSpilled = true;
@@ -2678,7 +2681,7 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF,
       ForceLRSpill = false;
       if (!MRI.isReserved(ARM::LR) && !MRI.isPhysRegUsed(ARM::LR) &&
           !AFI->isThumb1OnlyFunction())
-        ExtraCSSpill = true;
+        NumExtraCSSpill++;
     }
 
     // If stack and double are 8-byte aligned and we are spilling an odd number
@@ -2701,7 +2704,7 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF,
                               << " to make up alignment\n");
             if (!MRI.isReserved(Reg) && !MRI.isPhysRegUsed(Reg) &&
                 !(Reg == ARM::LR && AFI->isThumb1OnlyFunction()))
-              ExtraCSSpill = true;
+              NumExtraCSSpill++;
             break;
           }
         }
@@ -2711,18 +2714,26 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF,
         LLVM_DEBUG(dbgs() << "Spilling " << printReg(Reg, TRI)
                           << " to make up alignment\n");
         if (!MRI.isReserved(Reg) && !MRI.isPhysRegUsed(Reg))
-          ExtraCSSpill = true;
+          NumExtraCSSpill++;
       }
     }
 
-    // Estimate if we might need to scavenge a register at some point in order
+    // Estimate if we might need to scavenge registers at some point in order
     // to materialize a stack offset. If so, either spill one additional
     // callee-saved register or reserve a special spill slot to facilitate
     // register scavenging. Thumb1 needs a spill slot for stack pointer
     // adjustments and for frame index accesses when FP is high register,
     // even when the frame itself is small.
-    if (!ExtraCSSpill &&
-        (BigFrameOffsets || canSpillOnFrameIndexAccess(MF, *this))) {
+    unsigned RegsNeeded = 0;
+    if (BigFrameOffsets || canSpillOnFrameIndexAccess(MF, *this)) {
+      RegsNeeded++;
+      // With thumb1 execute-only we may need an additional register for saving
+      // and restoring the CPSR.
+      if (AFI->isThumb1OnlyFunction() && STI.genExecuteOnly() && !STI.useMovt())
+        RegsNeeded++;
+    }
+
+    if (RegsNeeded > NumExtraCSSpill) {
       // If any non-reserved CS register isn't spilled, just spill one or two
       // extra. That should take care of it!
       unsigned NumExtras = TargetAlign.value() / 4;
@@ -2749,10 +2760,10 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF,
         for (unsigned Reg : Extras) {
           SavedRegs.set(Reg);
           if (!MRI.isPhysRegUsed(Reg))
-            ExtraCSSpill = true;
+            NumExtraCSSpill++;
         }
       }
-      if (!ExtraCSSpill && RS) {
+      while ((RegsNeeded > NumExtraCSSpill) && RS) {
         // Reserve a slot closest to SP or frame pointer.
         LLVM_DEBUG(dbgs() << "Reserving emergency spill slot\n");
         const TargetRegisterClass &RC = ARM::GPRRegClass;
@@ -2760,6 +2771,7 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF,
         Align Alignment = TRI->getSpillAlign(RC);
         RS->addScavengingFrameIndex(
             MFI.CreateStackObject(Size, Alignment, false));
+        --RegsNeeded;
       }
     }
   }

diff  --git a/llvm/lib/Target/ARM/ThumbRegisterInfo.cpp b/llvm/lib/Target/ARM/ThumbRegisterInfo.cpp
index d8eb6c36c2745a..6e88c9378e9bd4 100644
--- a/llvm/lib/Target/ARM/ThumbRegisterInfo.cpp
+++ b/llvm/lib/Target/ARM/ThumbRegisterInfo.cpp
@@ -173,9 +173,60 @@ static void emitThumbRegPlusImmInReg(
         .addReg(LdReg, RegState::Kill)
         .setMIFlags(MIFlags);
   } else if (ST.genExecuteOnly()) {
-    unsigned XOInstr = ST.useMovt() ? ARM::t2MOVi32imm : ARM::tMOVi32imm;
-    BuildMI(MBB, MBBI, dl, TII.get(XOInstr), LdReg)
-      .addImm(NumBytes).setMIFlags(MIFlags);
+    if (ST.useMovt()) {
+      BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MOVi32imm ), LdReg)
+          .addImm(NumBytes)
+          .setMIFlags(MIFlags);
+    } else if (!CanChangeCC) {
+      // tMOVi32imm is lowered to a sequence of flag-setting instructions, so
+      // if CPSR is live we need to save and restore CPSR around it.
+      // TODO Try inserting the tMOVi32imm at an earlier point, where CPSR is
+      // dead.
+      bool LiveCpsr = false, CpsrWrite = false;
+      auto isCpsr = [](auto &MO) { return MO.getReg() == ARM::CPSR; };
+      for (auto Iter = MBBI; Iter != MBB.instr_end(); ++Iter) {
+        // If CPSR is used after this instruction (and there's not a def before
+        // that) then CPSR is live.
+        if (any_of(Iter->all_uses(), isCpsr)) {
+          LiveCpsr = true;
+          break;
+        }
+        if (any_of(Iter->all_defs(), isCpsr)) {
+          CpsrWrite = true;
+          break;
+        }
+      }
+      // If there's no use or def of CPSR then it may be live if it's a
+      // live-out value.
+      auto liveOutIsCpsr = [](auto &Out) { return Out.PhysReg == ARM::CPSR; };
+      if (!LiveCpsr && !CpsrWrite)
+        LiveCpsr = any_of(MBB.liveouts(), liveOutIsCpsr);
+
+      Register CPSRSaveReg;
+      unsigned APSREncoding;
+      if (LiveCpsr) {
+        CPSRSaveReg = MF.getRegInfo().createVirtualRegister(&ARM::tGPRRegClass);
+        APSREncoding =
+            ARMSysReg::lookupMClassSysRegByName("apsr_nzcvq")->Encoding;
+        BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MRS_M), CPSRSaveReg)
+            .addImm(APSREncoding)
+            .add(predOps(ARMCC::AL))
+            .addReg(ARM::CPSR, RegState::Implicit);
+      }
+      BuildMI(MBB, MBBI, dl, TII.get(ARM::tMOVi32imm), LdReg)
+          .addImm(NumBytes)
+          .setMIFlags(MIFlags);
+      if (LiveCpsr) {
+        BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MSR_M))
+            .addImm(APSREncoding)
+            .addReg(CPSRSaveReg, RegState::Kill)
+            .add(predOps(ARMCC::AL));
+      }
+    } else {
+      BuildMI(MBB, MBBI, dl, TII.get(ARM::tMOVi32imm), LdReg)
+          .addImm(NumBytes)
+          .setMIFlags(MIFlags);
+    }
   } else
     MRI.emitLoadConstPool(MBB, MBBI, dl, LdReg, 0, NumBytes, ARMCC::AL, 0,
                           MIFlags);

diff  --git a/llvm/test/CodeGen/ARM/execute-only-save-cpsr.mir b/llvm/test/CodeGen/ARM/execute-only-save-cpsr.mir
new file mode 100644
index 00000000000000..67e05218a4f197
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/execute-only-save-cpsr.mir
@@ -0,0 +1,441 @@
+# RUN: llc -run-pass=prologepilog %s -o - | FileCheck %s
+# Tests to check that CPSR is saved and restored if live when we emit tMOVi32imm
+# to resolve a frame offset.
+
+--- |
+  target triple = "thumbv6m-arm-none-eabi"
+
+  define void @test_def_in_block(i32 %x) #0 {
+  entry:
+    %var = alloca i32, align 4
+    %dummy = alloca [2048 x i8], align 1
+    %cmp = icmp eq i32 %x, 0
+    store i32 %x, ptr %var, align 4
+    br i1 %cmp, label %if.then, label %if.end
+
+  if.then:
+    br label %if.end
+
+  if.end:
+    ret void
+  }
+
+  define void @test_live_in(i32 %x) #0 {
+  entry:
+    %var = alloca i32, align 4
+    %dummy = alloca [2048 x i8], align 1
+    %cmp = icmp eq i32 %x, 0
+    br i1 %cmp, label %if.then, label %if.end
+
+  if.then:
+    store i32 %x, ptr %var, align 4
+    %cmp1 = icmp slt i32 %x, 0
+    br i1 %cmp1, label %if.then2, label %if.end
+
+  if.then2:
+    br label %if.end
+
+  if.end:
+    ret void
+  }
+
+  define void @test_live_out(i32 %x) #0 {
+  entry:
+    %var = alloca i32, align 4
+    %dummy = alloca [2048 x i8], align 1
+    %cmp = icmp eq i32 %x, 0
+    store i32 %x, ptr %var, align 4
+    br label %if.then
+
+  if.then:
+    br i1 %cmp, label %if.then2, label %if.end
+
+  if.then2:
+    br label %if.end
+
+  if.end:
+    ret void
+  }
+
+  define void @test_live_out_def_after_mov(i32 %x) #0 {
+  entry:
+    %var = alloca i32, align 4
+    %dummy = alloca [2048 x i8], align 1
+    store i32 %x, ptr %var, align 4
+    %cmp = icmp eq i32 %x, 0
+    br label %if.then
+
+  if.then:
+    br i1 %cmp, label %if.then2, label %if.end
+
+  if.then2:
+    br label %if.end
+
+  if.end:
+    ret void
+  }
+
+  attributes #0 = { "target-features"="+execute-only" }
+...
+---
+name:            test_def_in_block
+alignment:       2
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: false
+registers:       []
+liveins:
+  - { reg: '$r0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       0
+  offsetAdjustment: 0
+  maxAlignment:    4
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  functionContext: ''
+  maxCallFrameSize: 0
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  localFrameSize:  2052
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:
+  - { id: 0, name: var, type: default, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -4, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+  - { id: 1, name: dummy, type: default, offset: 0, size: 2048, alignment: 1,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -2052, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.0.entry:
+    successors: %bb.1(0x40000000), %bb.2(0x40000000)
+    liveins: $r0
+
+    ; CHECK-LABEL: name: test_def_in_block
+    ; CHECK-LABEL: bb.0.entry:
+    ; CHECK: tCMPi8 renamable $r0, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    ; CHECK-NEXT: $r2 = t2MRS_M 2048, 14 /* CC::al */, $noreg, implicit $cpsr
+    ; CHECK-NEXT: $r1 = tMOVi32imm 2048, implicit-def $cpsr
+    ; CHECK-NEXT: t2MSR_M 2048, killed $r2, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    ; CHECK-NEXT: $r1 = tADDhirr $r1, killed $sp, 14 /* CC::al */, $noreg
+    ; CHECK-NEXT: tSTRi renamable $r0, killed $r1, 0, 14 /* CC::al */, $noreg :: (store (s32) into %ir.var)
+
+    tCMPi8 renamable $r0, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    tSTRspi renamable $r0, %stack.0.var, 0, 14 /* CC::al */, $noreg :: (store (s32) into %ir.var)
+    tBcc %bb.1, 1 /* CC::ne */, killed $cpsr
+    tB %bb.2, 14 /* CC::al */, $noreg
+
+  bb.1.if.then:
+    successors: %bb.2(0x80000000)
+
+    tB %bb.2, 14 /* CC::al */, $noreg
+
+  bb.2.if.end:
+    tBX_RET 14 /* CC::al */, $noreg
+
+...
+---
+name:            test_live_in
+alignment:       2
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: false
+registers:       []
+liveins:
+  - { reg: '$r0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       0
+  offsetAdjustment: 0
+  maxAlignment:    4
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  functionContext: ''
+  maxCallFrameSize: 0
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  localFrameSize:  2052
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:
+  - { id: 0, name: var, type: default, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -4, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+  - { id: 1, name: dummy, type: default, offset: 0, size: 2048, alignment: 1,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -2052, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.0.entry:
+    successors: %bb.1(0x40000000), %bb.3(0x40000000)
+    liveins: $r0
+
+    tCMPi8 renamable $r0, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    tBcc %bb.3, 1 /* CC::ne */, $cpsr
+    tB %bb.1, 14 /* CC::al */, $noreg
+
+  bb.1.if.then:
+    successors: %bb.2(0x40000000), %bb.3(0x40000000)
+    liveins: $r0, $cpsr
+
+    ; CHECK-LABEL: name: test_live_in
+    ; CHECK-LABEL: bb.1.if.then:
+    ; CHECK: $r2 = t2MRS_M 2048, 14 /* CC::al */, $noreg, implicit $cpsr
+    ; CHECK-NEXT: $r1 = tMOVi32imm 2048, implicit-def $cpsr
+    ; CHECK-NEXT: t2MSR_M 2048, killed $r2, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    ; CHECK-NEXT: $r1 = tADDhirr $r1, killed $sp, 14 /* CC::al */, $noreg
+    ; CHECK-NEXT: tSTRi renamable $r0, killed $r1, 0, 14 /* CC::al */, $noreg :: (store (s32) into %ir.var)
+
+    tSTRspi renamable $r0, %stack.0.var, 0, 14 /* CC::al */, $noreg :: (store (s32) into %ir.var)
+    tBcc %bb.3, 5 /* CC::pl */, killed $cpsr
+    tB %bb.2, 14 /* CC::al */, $noreg
+
+  bb.2.if.then2:
+    successors: %bb.3(0x80000000)
+
+    tB %bb.3, 14 /* CC::al */, $noreg
+
+  bb.3.if.end:
+    tBX_RET 14 /* CC::al */, $noreg
+
+...
+---
+name:            test_live_out
+alignment:       2
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: false
+registers:       []
+liveins:
+  - { reg: '$r0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       0
+  offsetAdjustment: 0
+  maxAlignment:    4
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  functionContext: ''
+  maxCallFrameSize: 0
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  localFrameSize:  2052
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:
+  - { id: 0, name: var, type: default, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -4, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+  - { id: 1, name: dummy, type: default, offset: 0, size: 2048, alignment: 1,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -2052, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.0.entry:
+    successors: %bb.1(0x40000000)
+    liveins: $r0
+
+    ; CHECK-LABEL: name: test_live_out
+    ; CHECK-LABEL: bb.0.entry:
+    ; CHECK: tCMPi8 renamable $r0, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    ; CHECK-NEXT: $r2 = t2MRS_M 2048, 14 /* CC::al */, $noreg, implicit $cpsr
+    ; CHECK-NEXT: $r1 = tMOVi32imm 2048, implicit-def $cpsr
+    ; CHECK-NEXT: t2MSR_M 2048, killed $r2, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    ; CHECK-NEXT: $r1 = tADDhirr $r1, killed $sp, 14 /* CC::al */, $noreg
+    ; CHECK-NEXT: tSTRi renamable $r0, killed $r1, 0, 14 /* CC::al */, $noreg :: (store (s32) into %ir.var)
+
+    tCMPi8 renamable $r0, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    tSTRspi renamable $r0, %stack.0.var, 0, 14 /* CC::al */, $noreg :: (store (s32) into %ir.var)
+    tB %bb.1, 14 /* CC::al */, $noreg
+
+  bb.1.if.then:
+    successors: %bb.2(0x40000000), %bb.3(0x40000000)
+    liveins: $cpsr
+
+    tBcc %bb.3, 5 /* CC::pl */, killed $cpsr
+    tB %bb.2, 14 /* CC::al */, $noreg
+
+  bb.2.if.then2:
+    successors: %bb.3(0x80000000)
+
+    tB %bb.3, 14 /* CC::al */, $noreg
+
+  bb.3.if.end:
+    tBX_RET 14 /* CC::al */, $noreg
+
+...
+---
+name:            test_live_out_def_after_mov
+alignment:       2
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: false
+registers:       []
+liveins:
+  - { reg: '$r0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       0
+  offsetAdjustment: 0
+  maxAlignment:    4
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  functionContext: ''
+  maxCallFrameSize: 0
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  localFrameSize:  2052
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:
+  - { id: 0, name: var, type: default, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -4, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+  - { id: 1, name: dummy, type: default, offset: 0, size: 2048, alignment: 1,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -2052, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.0.entry:
+    successors: %bb.1(0x40000000)
+    liveins: $r0
+
+    ; Here the live-out cpsr is defined after the tMOVi32imm, so cpsr doesn't
+    ; need to be saved.
+    ; CHECK-LABEL: name: test_live_out
+    ; CHECK-LABEL: bb.0.entry:
+    ; CHECK: $r1 = tMOVi32imm 2048, implicit-def $cpsr
+    ; CHECK-NEXT: $r1 = tADDhirr $r1, killed $sp, 14 /* CC::al */, $noreg
+    ; CHECK-NEXT: tSTRi renamable $r0, killed $r1, 0, 14 /* CC::al */, $noreg :: (store (s32) into %ir.var)
+    ; CHECK-NEXT: tCMPi8 renamable $r0, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+
+    tSTRspi renamable $r0, %stack.0.var, 0, 14 /* CC::al */, $noreg :: (store (s32) into %ir.var)
+    tCMPi8 renamable $r0, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    tB %bb.1, 14 /* CC::al */, $noreg
+
+  bb.1.if.then:
+    successors: %bb.2(0x40000000), %bb.3(0x40000000)
+    liveins: $cpsr
+
+    tBcc %bb.3, 5 /* CC::pl */, killed $cpsr
+    tB %bb.2, 14 /* CC::al */, $noreg
+
+  bb.2.if.then2:
+    successors: %bb.3(0x80000000)
+
+    tB %bb.3, 14 /* CC::al */, $noreg
+
+  bb.3.if.end:
+    tBX_RET 14 /* CC::al */, $noreg
+
+...

diff  --git a/llvm/test/CodeGen/ARM/large-stack.ll b/llvm/test/CodeGen/ARM/large-stack.ll
index e33f0646c5b40a..501925ebf81e5d 100644
--- a/llvm/test/CodeGen/ARM/large-stack.ll
+++ b/llvm/test/CodeGen/ARM/large-stack.ll
@@ -36,7 +36,7 @@ define i32 @test3() {
 ; CHECK-NEXT: lsls    [[REG]], [[REG]], #0x8
 ; CHECK-NEXT: adds    [[REG]], #0xff
 ; CHECK-NEXT: lsls    [[REG]], [[REG]], #0x8
-; CHECK-NEXT: adds    [[REG]], #0xf0
+; CHECK-NEXT: adds    [[REG]], #0xf4
 	%retval = alloca i32, align 4
 	%tmp = alloca i32, align 4
 	%a = alloca [u0x30000001 x i8], align 16
@@ -45,7 +45,7 @@ define i32 @test3() {
 ; CHECK:      movs    [[REG:r[0-9]+]], #0x30
 ; CHECK-NEXT: lsls    [[REG]], [[REG]], #0x18
 ; CHECK-NEXT: add     [[REG]], sp
-; CHECK-NEXT: str     {{r[0-9]+}}, [[[REG]], #0x8]
+; CHECK-NEXT: str     {{r[0-9]+}}, [[[REG]], #0x4]
 	%tmp1 = load i32, ptr %tmp
         ret i32 %tmp1
 }


        


More information about the llvm-commits mailing list