<div dir="ltr">Reverted in 283938</div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Oct 11, 2016 at 1:39 PM, Reid Kleckner <span dir="ltr"><<a href="mailto:rnk@google.com" target="_blank">rnk@google.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">This change causes the test suite to time out on 32-bit Windows:<div><a href="http://lab.llvm.org:8011/builders/clang-x86-windows-msvc2015/builds/1040" target="_blank">http://lab.llvm.org:8011/<wbr>builders/clang-x86-windows-<wbr>msvc2015/builds/1040</a><br></div><div><br></div>I'm trying to reproduce locally, and will revert or fix depending on the complexity.</div><div class="HOEnZb"><div class="h5"><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Oct 11, 2016 at 3:12 AM, Oliver Stannard via llvm-commits <span dir="ltr"><<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: olista01<br>
Date: Tue Oct 11 05:12:25 2016<br>
New Revision: 283867<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=283867&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject?rev=283867&view=rev</a><br>
Log:<br>
[Thumb] Save/restore high registers in Thumb1 pro/epilogues<br>
<br>
The high registers are not allocatable in Thumb1 functions, but they<br>
could still be used by inline assembly, so we need to save and restore<br>
the callee-saved high registers (r8-r11) in the prologue and epilogue.<br>
<br>
This is complicated by the fact that the Thumb1 push and pop<br>
instructions cannot access these registers. Therefore, we have to move<br>
them down into low registers before pushing, and move them back after<br>
popping into low registers.<br>
<br>
In most functions, we will have low registers that are also being<br>
pushed/popped, which we can use as the temporary registers for<br>
saving/restoring the high registers. However, this is not guaranteed, so<br>
we may need to push some extra low registers to ensure that the high<br>
registers can be saved/restored. For correctness, it would be sufficient<br>
to use just one low register, but if we have enough low registers<br>
available then we only need one push/pop instruction, rather than one<br>
per high register.<br>
<br>
We can also use the argument/return registers when they are not live,<br>
and the link register when saving (but not restoring), reducing the<br>
number of extra registers we need to push.<br>
<br>
There are still a few extreme edge cases where we need two push/pop<br>
instructions, because not enough low registers can be made live in the<br>
prologue or epilogue.<br>
<br>
In addition to the regression tests included here, I've also tested this<br>
using a script to generate functions which clobber different<br>
combinations of registers, have different numbers of argument and return<br>
registers (including variadic arguments), allocate different fixed sized<br>
objects on the stack, and do or don't use variable sized allocas and the<br>
__builtin_return_address intrinsic (all of which affect the available<br>
registers in the prologue and epilogue). I ran these functions in a test<br>
harness which verifies that all of the callee-saved registers are<br>
correctly preserved.<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D24228" rel="noreferrer" target="_blank">https://reviews.llvm.org/D2422<wbr>8</a><br>
<br>
<br>
Added:<br>
    llvm/trunk/test/CodeGen/Thumb/<wbr>callee_save.ll<br>
Modified:<br>
    llvm/trunk/lib/Target/ARM/ARMF<wbr>rameLowering.cpp<br>
    llvm/trunk/lib/Target/ARM/ARMS<wbr>ubtarget.h<br>
    llvm/trunk/lib/Target/ARM/Thum<wbr>b1FrameLowering.cpp<br>
    llvm/trunk/test/CodeGen/Thumb/<wbr>large-stack.ll<br>
    llvm/trunk/test/CodeGen/Thumb2<wbr>/frame-pointer.ll<br>
<br>
Modified: llvm/trunk/lib/Target/ARM/ARMF<wbr>rameLowering.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/ARM/ARMFrameLowering.cpp?rev=283867&r1=283866&r2=283867&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/llvm/trunk/lib/Target/AR<wbr>M/ARMFrameLowering.cpp?rev=283<wbr>867&r1=283866&r2=283867&view=<wbr>diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- llvm/trunk/lib/Target/ARM/ARMF<wbr>rameLowering.cpp (original)<br>
+++ llvm/trunk/lib/Target/ARM/ARMF<wbr>rameLowering.cpp Tue Oct 11 05:12:25 2016<br>
@@ -30,6 +30,8 @@<br>
 #include "llvm/Support/CommandLine.h"<br>
 #include "llvm/Target/TargetOptions.h"<br>
<br>
+#define DEBUG_TYPE "arm-frame-lowering"<br>
+<br>
 using namespace llvm;<br>
<br>
 static cl::opt<bool><br>
@@ -1485,6 +1487,7 @@ void ARMFrameLowering::determineCal<wbr>leeSa<br>
   ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>();<br>
   MachineFrameInfo &MFI = MF.getFrameInfo();<br>
   MachineRegisterInfo &MRI = MF.getRegInfo();<br>
+  const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterI<wbr>nfo();<br>
   unsigned FramePtr = RegInfo->getFrameRegister(MF);<br>
<br>
   // Spill R4 if Thumb2 function requires stack realignment - it will be used as<br>
@@ -1640,6 +1643,9 @@ void ARMFrameLowering::determineCal<wbr>leeSa<br>
         SavedRegs.set(ARM::LR);<br>
         LRSpilled = true;<br>
         NumGPRSpills++;<br>
+        auto LRPos = find(UnspilledCS1GPRs, ARM::LR);<br>
+        if (LRPos != UnspilledCS1GPRs.end())<br>
+          UnspilledCS1GPRs.erase(LRPos);<br>
       }<br>
       auto FPPos = find(UnspilledCS1GPRs, FramePtr);<br>
       if (FPPos != UnspilledCS1GPRs.end())<br>
@@ -1649,6 +1655,116 @@ void ARMFrameLowering::determineCal<wbr>leeSa<br>
         CS1Spilled = true;<br>
     }<br>
<br>
+    if (AFI->isThumb1OnlyFunction()) {<br>
+      // For Thumb1-only targets, we need some low registers when we save and<br>
+      // restore the high registers (which aren't allocatable, but could be<br>
+      // used by inline assembly) because the push/pop instructions can not<br>
+      // access high registers. If necessary, we might need to push more low<br>
+      // registers to ensure that there is at least one free that can be used<br>
+      // for the saving & restoring, and preferably we should ensure that as<br>
+      // many as are needed are available so that fewer push/pop instructions<br>
+      // are required.<br>
+<br>
+      // Low registers which are not currently pushed, but could be (r4-r7).<br>
+      SmallVector<unsigned, 4> AvailableRegs;<br>
+<br>
+      // Unused argument registers (r0-r3) can be clobbered in the prologue for<br>
+      // free.<br>
+      int EntryRegDeficit = 0;<br>
+      for (unsigned Reg : {ARM::R0, ARM::R1, ARM::R2, ARM::R3}) {<br>
+        if (!MF.getRegInfo().isLiveIn(Reg<wbr>)) {<br>
+          --EntryRegDeficit;<br>
+          DEBUG(dbgs() << PrintReg(Reg, TRI)<br>
+                       << " is unused argument register, EntryRegDeficit = "<br>
+                       << EntryRegDeficit << "\n");<br>
+        }<br>
+      }<br>
+<br>
+      // Unused return registers can be clobbered in the epilogue for free.<br>
+      int ExitRegDeficit = AFI->getReturnRegsCount() - 4;<br>
+      DEBUG(dbgs() << AFI->getReturnRegsCount()<br>
+                   << " return regs used, ExitRegDeficit = " << ExitRegDeficit<br>
+                   << "\n");<br>
+<br>
+      int RegDeficit = std::max(EntryRegDeficit, ExitRegDeficit);<br>
+      DEBUG(dbgs() << "RegDeficit = " << RegDeficit << "\n");<br>
+<br>
+      // r4-r6 can be used in the prologue if they are pushed by the first push<br>
+      // instruction.<br>
+      for (unsigned Reg : {ARM::R4, ARM::R5, ARM::R6}) {<br>
+        if (SavedRegs.test(Reg)) {<br>
+          --RegDeficit;<br>
+          DEBUG(dbgs() << PrintReg(Reg, TRI)<br>
+                       << " is saved low register, RegDeficit = " << RegDeficit<br>
+                       << "\n");<br>
+        } else {<br>
+          AvailableRegs.push_back(Reg);<br>
+          DEBUG(dbgs()<br>
+                << PrintReg(Reg, TRI)<br>
+                << " is non-saved low register, adding to AvailableRegs\n");<br>
+        }<br>
+      }<br>
+<br>
+      // r7 can be used if it is not being used as the frame pointer.<br>
+      if (!hasFP(MF)) {<br>
+        if (SavedRegs.test(ARM::R7)) {<br>
+          --RegDeficit;<br>
+          DEBUG(dbgs() << "%R7 is saved low register, RegDeficit = "<br>
+                       << RegDeficit << "\n");<br>
+        } else {<br>
+          AvailableRegs.push_back(ARM::R<wbr>7);<br>
+          DEBUG(dbgs()<br>
+                << "%R7 is non-saved low register, adding to AvailableRegs\n");<br>
+        }<br>
+      }<br>
+<br>
+      // Each of r8-r11 needs to be copied to a low register, then pushed.<br>
+      for (unsigned Reg : {ARM::R8, ARM::R9, ARM::R10, ARM::R11}) {<br>
+        if (SavedRegs.test(Reg)) {<br>
+          ++RegDeficit;<br>
+          DEBUG(dbgs() << PrintReg(Reg, TRI)<br>
+                       << " is saved high register, RegDeficit = " << RegDeficit<br>
+                       << "\n");<br>
+        }<br>
+      }<br>
+<br>
+      // LR can only be used by PUSH, not POP, and can't be used at all if the<br>
+      // llvm.returnaddress intrinsic is used. This is only worth doing if we<br>
+      // are more limited at function entry than exit.<br>
+      if ((EntryRegDeficit > ExitRegDeficit) &&<br>
+          !(MF.getRegInfo().isLiveIn(ARM<wbr>::LR) &&<br>
+            MF.getFrameInfo().isReturnAddr<wbr>essTaken())) {<br>
+        if (SavedRegs.test(ARM::LR)) {<br>
+          --RegDeficit;<br>
+          DEBUG(dbgs() << "%LR is saved register, RegDeficit = " << RegDeficit<br>
+                       << "\n");<br>
+        } else {<br>
+          AvailableRegs.push_back(ARM::L<wbr>R);<br>
+          DEBUG(dbgs() << "%LR is not saved, adding to AvailableRegs\n");<br>
+        }<br>
+      }<br>
+<br>
+      // If there are more high registers that need pushing than low registers<br>
+      // available, push some more low registers so that we can use fewer push<br>
+      // instructions. This might not reduce RegDeficit all the way to zero,<br>
+      // because we can only guarantee that r4-r6 are available, but r8-r11 may<br>
+      // need saving.<br>
+      DEBUG(dbgs() << "Final RegDeficit = " << RegDeficit << "\n");<br>
+      for (; RegDeficit > 0 && !AvailableRegs.empty(); --RegDeficit) {<br>
+        unsigned Reg = AvailableRegs.pop_back_val();<br>
+        DEBUG(dbgs() << "Spilling " << PrintReg(Reg, TRI)<br>
+                     << " to make up reg deficit\n");<br>
+        SavedRegs.set(Reg);<br>
+        NumGPRSpills++;<br>
+        CS1Spilled = true;<br>
+        ExtraCSSpill = true;<br>
+        UnspilledCS1GPRs.erase(find(Un<wbr>spilledCS1GPRs, Reg));<br>
+        if (Reg == ARM::LR)<br>
+          LRSpilled = true;<br>
+      }<br>
+      DEBUG(dbgs() << "After adding spills, RegDeficit = " << RegDeficit << "\n");<br>
+    }<br>
+<br>
     // If LR is not spilled, but at least one of R4, R5, R6, and R7 is spilled.<br>
     // Spill LR as well so we can fold BX_RET to the registers restore (LDM).<br>
     if (!LRSpilled && CS1Spilled) {<br>
@@ -1666,6 +1782,7 @@ void ARMFrameLowering::determineCal<wbr>leeSa<br>
     // If stack and double are 8-byte aligned and we are spilling an odd number<br>
     // of GPRs, spill one extra callee save GPR so we won't have to pad between<br>
     // the integer and double callee save areas.<br>
+    DEBUG(dbgs() << "NumGPRSpills = " << NumGPRSpills << "\n");<br>
     unsigned TargetAlign = getStackAlignment();<br>
     if (TargetAlign >= 8 && (NumGPRSpills & 1)) {<br>
       if (CS1Spilled && !UnspilledCS1GPRs.empty()) {<br>
@@ -1677,6 +1794,8 @@ void ARMFrameLowering::determineCal<wbr>leeSa<br>
               (STI.isTargetWindows() && Reg == ARM::R11) ||<br>
               isARMLowRegister(Reg) || Reg == ARM::LR) {<br>
             SavedRegs.set(Reg);<br>
+            DEBUG(dbgs() << "Spilling " << PrintReg(Reg, TRI)<br>
+                         << " to make up alignment\n");<br>
             if (!MRI.isReserved(Reg))<br>
               ExtraCSSpill = true;<br>
             break;<br>
@@ -1685,6 +1804,8 @@ void ARMFrameLowering::determineCal<wbr>leeSa<br>
       } else if (!UnspilledCS2GPRs.empty() && !AFI->isThumb1OnlyFunction()) {<br>
         unsigned Reg = UnspilledCS2GPRs.front();<br>
         SavedRegs.set(Reg);<br>
+        DEBUG(dbgs() << "Spilling " << PrintReg(Reg, TRI)<br>
+                     << " to make up alignment\n");<br>
         if (!MRI.isReserved(Reg))<br>
           ExtraCSSpill = true;<br>
       }<br>
<br>
Modified: llvm/trunk/lib/Target/ARM/ARMS<wbr>ubtarget.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/ARM/ARMSubtarget.h?rev=283867&r1=283866&r2=283867&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/llvm/trunk/lib/Target/AR<wbr>M/ARMSubtarget.h?rev=283867&r1<wbr>=283866&r2=283867&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- llvm/trunk/lib/Target/ARM/ARMS<wbr>ubtarget.h (original)<br>
+++ llvm/trunk/lib/Target/ARM/ARMS<wbr>ubtarget.h Tue Oct 11 05:12:25 2016<br>
@@ -568,10 +568,12 @@ public:<br>
   }<br>
   /// Returns true if the frame setup is split into two separate pushes (first<br>
   /// r0-r7,lr then r8-r11), principally so that the frame pointer is adjacent<br>
-  /// to lr.<br>
+  /// to lr. This is always required on Thumb1-only targets, as the push and<br>
+  /// pop instructions can't access the high registers.<br>
   bool splitFramePushPop(const MachineFunction &MF) const {<br>
-    return useR7AsFramePointer() &&<br>
-           MF.getTarget().Options.Disabl<wbr>eFramePointerElim(MF);<br>
+    return (useR7AsFramePointer() &&<br>
+            MF.getTarget().Options.Disable<wbr>FramePointerElim(MF)) ||<br>
+           isThumb1Only();<br>
   }<br>
<br>
   bool useStride4VFPs(const MachineFunction &MF) const;<br>
<br>
Modified: llvm/trunk/lib/Target/ARM/Thum<wbr>b1FrameLowering.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/ARM/Thumb1FrameLowering.cpp?rev=283867&r1=283866&r2=283867&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/llvm/trunk/lib/Target/AR<wbr>M/Thumb1FrameLowering.cpp?rev=<wbr>283867&r1=283866&r2=283867&<wbr>view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- llvm/trunk/lib/Target/ARM/Thum<wbr>b1FrameLowering.cpp (original)<br>
+++ llvm/trunk/lib/Target/ARM/Thum<wbr>b1FrameLowering.cpp Tue Oct 11 05:12:25 2016<br>
@@ -188,7 +188,8 @@ void Thumb1FrameLowering::emitProlo<wbr>gue(M<br>
<br>
   int FramePtrOffsetInBlock = 0;<br>
   unsigned adjustedGPRCS1Size = GPRCS1Size;<br>
-  if (tryFoldSPUpdateIntoPushPop(ST<wbr>I, MF, &*std::prev(MBBI), NumBytes)) {<br>
+  if (GPRCS1Size > 0 && GPRCS2Size == 0 &&<br>
+      tryFoldSPUpdateIntoPushPop(STI<wbr>, MF, &*std::prev(MBBI), NumBytes)) {<br>
     FramePtrOffsetInBlock = NumBytes;<br>
     adjustedGPRCS1Size += NumBytes;<br>
     NumBytes = 0;<br>
@@ -261,6 +262,48 @@ void Thumb1FrameLowering::emitProlo<wbr>gue(M<br>
       AFI-><wbr>setShouldRestoreSPFromFP(true)<wbr>;<br>
   }<br>
<br>
+  // Skip past the spilling of r8-r11, which could consist of multiple tPUSH<br>
+  // and tMOVr instructions. We don't need to add any call frame information<br>
+  // in-between these instructions, because they do not modify the high<br>
+  // registers.<br>
+  while (true) {<br>
+    MachineBasicBlock::iterator OldMBBI = MBBI;<br>
+    // Skip a run of tMOVr instructions<br>
+    while (MBBI != MBB.end() && MBBI->getOpcode() == ARM::tMOVr)<br>
+      MBBI++;<br>
+    if (MBBI != MBB.end() && MBBI->getOpcode() == ARM::tPUSH) {<br>
+      MBBI++;<br>
+    } else {<br>
+      // We have reached an instruction which is not a push, so the previous<br>
+      // run of tMOVr instructions (which may have been empty) was not part of<br>
+      // the prologue. Reset MBBI back to the last PUSH of the prologue.<br>
+      MBBI = OldMBBI;<br>
+      break;<br>
+    }<br>
+  }<br>
+<br>
+  // Emit call frame information for the callee-saved high registers.<br>
+  for (auto &I : CSI) {<br>
+    unsigned Reg = I.getReg();<br>
+    int FI = I.getFrameIdx();<br>
+    switch (Reg) {<br>
+    case ARM::R8:<br>
+    case ARM::R9:<br>
+    case ARM::R10:<br>
+    case ARM::R11:<br>
+    case ARM::R12: {<br>
+      unsigned CFIIndex = MMI.addFrameInst(MCCFIInstruct<wbr>ion::createOffset(<br>
+          nullptr, MRI->getDwarfRegNum(Reg, true), MFI.getObjectOffset(FI)));<br>
+      BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INST<wbr>RUCTION))<br>
+          .addCFIIndex(CFIIndex)<br>
+          .setMIFlags(MachineInstr::Fram<wbr>eSetup);<br>
+      break;<br>
+    }<br>
+    default:<br>
+      break;<br>
+    }<br>
+  }<br>
+<br>
   if (NumBytes) {<br>
     // Insert it after all the callee-save spills.<br>
     emitSPUpdate(MBB, MBBI, TII, dl, *RegInfo, -NumBytes,<br>
@@ -308,12 +351,12 @@ static bool isCSRestore(MachineInstr &MI<br>
       isCalleeSavedRegister(MI.getO<wbr>perand(0).getReg(), CSRegs))<br>
     return true;<br>
   else if (MI.getOpcode() == ARM::tPOP) {<br>
-    // The first two operands are predicates. The last two are<br>
-    // imp-def and imp-use of SP. Check everything in between.<br>
-    for (int i = 2, e = MI.getNumOperands() - 2; i != e; ++i)<br>
-      if (!isCalleeSavedRegister(MI.get<wbr>Operand(i).getReg(), CSRegs))<br>
-        return false;<br>
     return true;<br>
+  } else if (MI.getOpcode() == ARM::tMOVr) {<br>
+    unsigned Dst = MI.getOperand(0).getReg();<br>
+    unsigned Src = MI.getOperand(1).getReg();<br>
+    return ((ARM::tGPRRegClass.contains(S<wbr>rc) || Src == ARM::LR) &&<br>
+            ARM::hGPRRegClass.contains(Dst<wbr>));<br>
   }<br>
   return false;<br>
 }<br>
@@ -568,6 +611,19 @@ bool Thumb1FrameLowering::emitPopSp<wbr>ecial<br>
   return true;<br>
 }<br>
<br>
+// Return the first iteraror after CurrentReg which is present in EnabledRegs,<br>
+// or OrderEnd if no further registers are in that set. This does not advance<br>
+// the iterator fiorst, so returns CurrentReg if it is in EnabledRegs.<br>
+template <unsigned SetSize><br>
+static ArrayRef<unsigned>::const_iter<wbr>ator<br>
+findNextOrderedReg(ArrayRef<u<wbr>nsigned>::const_iterator CurrentReg,<br>
+                   SmallSet<unsigned, SetSize> &EnabledRegs,<br>
+                   ArrayRef<unsigned>::const_ite<wbr>rator OrderEnd) {<br>
+  while (CurrentReg != OrderEnd && !EnabledRegs.count(*CurrentReg<wbr>))<br>
+    ++CurrentReg;<br>
+  return CurrentReg;<br>
+}<br>
+<br>
 bool Thumb1FrameLowering::<br>
 spillCalleeSavedRegisters(Mac<wbr>hineBasicBlock &MBB,<br>
                           MachineBasicBlock::iterator MI,<br>
@@ -578,29 +634,111 @@ spillCalleeSavedRegisters(Mach<wbr>ineBasicBl<br>
<br>
   DebugLoc DL;<br>
   const TargetInstrInfo &TII = *STI.getInstrInfo();<br>
+  MachineFunction &MF = *MBB.getParent();<br>
+  const ARMBaseRegisterInfo *RegInfo = static_cast<const ARMBaseRegisterInfo *>(<br>
+      MF.getSubtarget().getRegisterI<wbr>nfo());<br>
+<br>
+  SmallSet<unsigned, 9> LoRegsToSave; // r0-r7, lr<br>
+  SmallSet<unsigned, 4> HiRegsToSave; // r8-r11<br>
+  SmallSet<unsigned, 9> CopyRegs; // Registers which can be used after pushing<br>
+                           // LoRegs for saving HiRegs.<br>
<br>
-  MachineInstrBuilder MIB = BuildMI(MBB, MI, DL, TII.get(ARM::tPUSH));<br>
-  AddDefaultPred(MIB);<br>
   for (unsigned i = CSI.size(); i != 0; --i) {<br>
     unsigned Reg = CSI[i-1].getReg();<br>
-    bool isKill = true;<br>
<br>
-    // Add the callee-saved register as live-in unless it's LR and<br>
-    // @llvm.returnaddress is called. If LR is returned for @llvm.returnaddress<br>
-    // then it's already added to the function and entry block live-in sets.<br>
-    if (Reg == ARM::LR) {<br>
-      MachineFunction &MF = *MBB.getParent();<br>
-      if (MF.getFrameInfo().isReturnAdd<wbr>ressTaken() &&<br>
-          MF.getRegInfo().isLiveIn(Reg))<br>
-        isKill = false;<br>
+    if (ARM::tGPRRegClass.contains(Re<wbr>g) || Reg == ARM::LR) {<br>
+      LoRegsToSave.insert(Reg);<br>
+    } else if (ARM::hGPRRegClass.contains(Re<wbr>g) && Reg != ARM::LR) {<br>
+      HiRegsToSave.insert(Reg);<br>
+    } else {<br>
+      llvm_unreachable("callee-saved register of unexpected class");<br>
     }<br>
<br>
-    if (isKill)<br>
-      MBB.addLiveIn(Reg);<br>
+    if ((ARM::tGPRRegClass.contains(R<wbr>eg) || Reg == ARM::LR) &&<br>
+        !MF.getRegInfo().isLiveIn(Reg) &&<br>
+        !(hasFP(MF) && Reg == RegInfo->getFrameRegister(MF))<wbr>)<br>
+      CopyRegs.insert(Reg);<br>
+  }<br>
+<br>
+  // Unused argument registers can be used for the high register saving.<br>
+  for (unsigned ArgReg : {ARM::R0, ARM::R1, ARM::R2, ARM::R3})<br>
+    if (!MF.getRegInfo().isLiveIn(Arg<wbr>Reg))<br>
+      CopyRegs.insert(ArgReg);<br>
+<br>
+  // Push the low registers and lr<br>
+  if (!LoRegsToSave.empty()) {<br>
+    MachineInstrBuilder MIB = BuildMI(MBB, MI, DL, TII.get(ARM::tPUSH));<br>
+    AddDefaultPred(MIB);<br>
+    for (unsigned Reg : {ARM::R4, ARM::R5, ARM::R6, ARM::R7, ARM::LR}) {<br>
+      if (LoRegsToSave.count(Reg)) {<br>
+        bool isKill = !MF.getRegInfo().isLiveIn(Reg)<wbr>;<br>
+        if (isKill)<br>
+          MBB.addLiveIn(Reg);<br>
<br>
-    MIB.addReg(Reg, getKillRegState(isKill));<br>
+        MIB.addReg(Reg, getKillRegState(isKill));<br>
+      }<br>
+    }<br>
+    MIB.setMIFlags(MachineInstr::F<wbr>rameSetup);<br>
   }<br>
-  MIB.setMIFlags(MachineInstr::F<wbr>rameSetup);<br>
+<br>
+  // Push the high registers. There are no store instructions that can access<br>
+  // these registers directly, so we have to move them to low registers, and<br>
+  // push them. This might take multiple pushes, as it is possible for there to<br>
+  // be fewer low registers available than high registers which need saving.<br>
+<br>
+  // These are in reverse order so that in the case where we need to use<br>
+  // multiple PUSH instructions, the order of the registers on the stack still<br>
+  // matches the unwind info. They need to be swicthed back to ascending order<br>
+  // before adding to the PUSH instruction.<br>
+  ArrayRef<unsigned> AllCopyRegs({ARM::LR,<br>
+                                  ARM::R7, ARM::R6, ARM::R5, ARM::R4,<br>
+                                  ARM::R3, ARM::R2, ARM::R1, ARM::R0});<br>
+  ArrayRef<unsigned> AllHighRegs({ARM::R11, ARM::R10, ARM::R9, ARM::R8});<br>
+<br>
+  // Find the first register to save.<br>
+  auto HiRegToSave =<br>
+      findNextOrderedReg(AllHighRegs<wbr>.begin(), HiRegsToSave, AllHighRegs.end());<br>
+<br>
+  while (HiRegToSave != AllHighRegs.end()) {<br>
+    // Find the first low register to use.<br>
+    auto CopyReg =<br>
+        findNextOrderedReg(AllCopyRegs<wbr>.begin(), CopyRegs, AllCopyRegs.end());<br>
+<br>
+    // Create the PUSH, but don't insert it yet (the MOVs need to come first).<br>
+    MachineInstrBuilder PushMIB = BuildMI(MF, DL, TII.get(ARM::tPUSH));<br>
+    AddDefaultPred(PushMIB);<br>
+<br>
+    SmallVector<unsigned, 4> RegsToPush;<br>
+    while (HiRegToSave != AllHighRegs.end() && CopyReg != AllCopyRegs.end()) {<br>
+      if (HiRegsToSave.count(*HiRegToSa<wbr>ve)) {<br>
+        bool isKill = !MF.getRegInfo().isLiveIn(*HiR<wbr>egToSave);<br>
+        if (isKill)<br>
+          MBB.addLiveIn(*HiRegToSave);<br>
+<br>
+        // Emit a MOV from the high reg to the low reg.<br>
+        MachineInstrBuilder MIB =<br>
+            BuildMI(MBB, MI, DL, TII.get(ARM::tMOVr));<br>
+        MIB.addReg(*CopyReg, RegState::Define);<br>
+        MIB.addReg(*HiRegToSave, getKillRegState(isKill));<br>
+        AddDefaultPred(MIB);<br>
+<br>
+        // Record the register that must be added to the PUSH.<br>
+        RegsToPush.push_back(*CopyReg)<wbr>;<br>
+<br>
+        CopyReg = findNextOrderedReg(++CopyReg, CopyRegs, AllCopyRegs.end());<br>
+        HiRegToSave =<br>
+            findNextOrderedReg(++HiRegToSa<wbr>ve, HiRegsToSave, AllHighRegs.end());<br>
+      }<br>
+    }<br>
+<br>
+    // Add the low registers to the PUSH, in ascending order.<br>
+    for (unsigned Reg : reverse(RegsToPush))<br>
+      PushMIB.addReg(Reg, RegState::Kill);<br>
+<br>
+    // Insert the PUSH instruction after the MOVs.<br>
+    MBB.insert(MI, PushMIB);<br>
+  }<br>
+<br>
   return true;<br>
 }<br>
<br>
@@ -615,15 +753,98 @@ restoreCalleeSavedRegisters(Ma<wbr>chineBasic<br>
   MachineFunction &MF = *MBB.getParent();<br>
   ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>();<br>
   const TargetInstrInfo &TII = *STI.getInstrInfo();<br>
+  const ARMBaseRegisterInfo *RegInfo = static_cast<const ARMBaseRegisterInfo *>(<br>
+      MF.getSubtarget().getRegisterI<wbr>nfo());<br>
<br>
   bool isVarArg = AFI->getArgRegsSaveSize() > 0;<br>
   DebugLoc DL = MI != MBB.end() ? MI->getDebugLoc() : DebugLoc();<br>
+<br>
+  SmallSet<unsigned, 9> LoRegsToRestore;<br>
+  SmallSet<unsigned, 4> HiRegsToRestore;<br>
+  // Low registers (r0-r7) which can be used to restore the high registers.<br>
+  SmallSet<unsigned, 9> CopyRegs;<br>
+<br>
+  for (CalleeSavedInfo I : CSI) {<br>
+    unsigned Reg = I.getReg();<br>
+<br>
+    if (ARM::tGPRRegClass.contains(Re<wbr>g) || Reg == ARM::LR) {<br>
+      LoRegsToRestore.insert(Reg);<br>
+    } else if (ARM::hGPRRegClass.contains(Re<wbr>g) && Reg != ARM::LR) {<br>
+      HiRegsToRestore.insert(Reg);<br>
+    } else {<br>
+      llvm_unreachable("callee-saved register of unexpected class");<br>
+    }<br>
+<br>
+    // If this is a low register not used as the frame pointer, we may want to<br>
+    // use it for restoring the high registers.<br>
+    if ((ARM::tGPRRegClass.contains(R<wbr>eg)) &&<br>
+        !(hasFP(MF) && Reg == RegInfo->getFrameRegister(MF))<wbr>)<br>
+      CopyRegs.insert(Reg);<br>
+  }<br>
+<br>
+  // If this is a return block, we may be able to use some unused return value<br>
+  // registers for restoring the high regs.<br>
+  auto Terminator = MBB.getFirstTerminator();<br>
+  if (Terminator != MBB.end() && Terminator->getOpcode() == ARM::tBX_RET) {<br>
+    CopyRegs.insert(ARM::R0);<br>
+    CopyRegs.insert(ARM::R1);<br>
+    CopyRegs.insert(ARM::R2);<br>
+    CopyRegs.insert(ARM::R3);<br>
+    for (auto Op : Terminator->implicit_operands(<wbr>)) {<br>
+      if (Op.isReg())<br>
+        CopyRegs.erase(Op.getReg());<br>
+    }<br>
+  }<br>
+<br>
+  ArrayRef<unsigned> AllCopyRegs({ARM::R0, ARM::R1, ARM::R2, ARM::R3,<br>
+                                  ARM::R4, ARM::R5, ARM::R6, ARM::R7});<br>
+  ArrayRef<unsigned> AllHighRegs({ARM::R8, ARM::R9, ARM::R10, ARM::R11});<br>
+<br>
+  // Find the first register to restore.<br>
+  auto HiRegToRestore = findNextOrderedReg(AllHighRegs<wbr>.begin(), HiRegsToRestore,<br>
+                                           AllHighRegs.end());<br>
+<br>
+  while (HiRegToRestore != AllHighRegs.end()) {<br>
+    assert(!CopyRegs.empty());<br>
+    // Find the first low register to use.<br>
+    auto CopyReg =<br>
+        findNextOrderedReg(AllCopyRegs<wbr>.begin(), CopyRegs, AllCopyRegs.end());<br>
+<br>
+    // Create the POP instruction.<br>
+    MachineInstrBuilder PopMIB = BuildMI(MBB, MI, DL, TII.get(ARM::tPOP));<br>
+    AddDefaultPred(PopMIB);<br>
+<br>
+    while (HiRegToRestore != AllHighRegs.end() && CopyReg != AllCopyRegs.end()) {<br>
+      // Add the low register to the POP.<br>
+      PopMIB.addReg(*CopyReg, RegState::Define);<br>
+<br>
+      // Create the MOV from low to high register.<br>
+      MachineInstrBuilder MIB =<br>
+          BuildMI(MBB, MI, DL, TII.get(ARM::tMOVr));<br>
+      MIB.addReg(*HiRegToRestore, RegState::Define);<br>
+      MIB.addReg(*CopyReg, RegState::Kill);<br>
+      AddDefaultPred(MIB);<br>
+<br>
+      CopyReg = findNextOrderedReg(++CopyReg, CopyRegs, AllCopyRegs.end());<br>
+      HiRegToRestore = findNextOrderedReg(++HiRegToRe<wbr>store, HiRegsToRestore,<br>
+                                          AllHighRegs.end());<br>
+    }<br>
+  }<br>
+<br>
+<br>
+<br>
+<br>
   MachineInstrBuilder MIB = BuildMI(MF, DL, TII.get(ARM::tPOP));<br>
   AddDefaultPred(MIB);<br>
<br>
   bool NeedsPop = false;<br>
   for (unsigned i = CSI.size(); i != 0; --i) {<br>
     unsigned Reg = CSI[i-1].getReg();<br>
+<br>
+    // High registers (excluding lr) have already been dealt with<br>
+    if (!(ARM::tGPRRegClass.contains(<wbr>Reg) || Reg == ARM::LR))<br>
+      continue;<br>
+<br>
     if (Reg == ARM::LR) {<br>
       if (MBB.succ_empty()) {<br>
         // Special epilogue for vararg functions. See emitEpilogue<br>
<br>
Added: llvm/trunk/test/CodeGen/Thumb/<wbr>callee_save.ll<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/Thumb/callee_save.ll?rev=283867&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/llvm/trunk/test/CodeGen/<wbr>Thumb/callee_save.ll?rev=<wbr>283867&view=auto</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- llvm/trunk/test/CodeGen/Thumb/<wbr>callee_save.ll (added)<br>
+++ llvm/trunk/test/CodeGen/Thumb/<wbr>callee_save.ll Tue Oct 11 05:12:25 2016<br>
@@ -0,0 +1,236 @@<br>
+; RUN: llc -mtriple=thumbv6m-none-eabi < %s | FileCheck %s<br>
+<br>
+declare i8* @llvm.returnaddress(i32)<br>
+<br>
+; We don't allocate high registers, so any function not using inline asm will<br>
+; only need to save the low registers.<br>
+define void @low_regs_only() {<br>
+; CHECK-LABEL: low_regs_only:<br>
+entry:<br>
+; CHECK: push {r4, r5, r6, r7, lr}<br>
+  tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7}"()<br>
+; CHECK: pop {r4, r5, r6, r7, pc}<br>
+  ret void<br>
+}<br>
+<br>
+; One high reg clobbered, but no low regs, args or returns. We can use an<br>
+; argument/return register to help save/restore it.<br>
+define void @one_high() {<br>
+; CHECK-LABEL: one_high:<br>
+entry:<br>
+; CHECK: mov [[SAVEREG:r[0-3]]], r8<br>
+; CHECK: push {[[SAVEREG]]}<br>
+  tail call void asm sideeffect "", "~{r8}"()<br>
+; CHECK: pop {[[RESTOREREG:r[0-3]]]}<br>
+; CHECK: mov r8, [[RESTOREREG]]<br>
+  ret void<br>
+}<br>
+<br>
+; 4 high regs clobbered, but still no low regs, args or returns. We can use all<br>
+; 4 arg/return regs for the save/restore.<br>
+define void @four_high() {<br>
+; CHECK-LABEL: four_high:<br>
+entry:<br>
+; CHECK: mov r3, r11<br>
+; CHECK: mov r2, r10<br>
+; CHECK: mov r1, r9<br>
+; CHECK: mov r0, r8<br>
+; CHECK: push {r0, r1, r2, r3}<br>
+  tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"()<br>
+; CHECK: pop {r0, r1, r2, r3}<br>
+; CHECK: mov r8, r0<br>
+; CHECK: mov r9, r1<br>
+; CHECK: mov r10, r2<br>
+; CHECK: mov r11, r3<br>
+  ret void<br>
+}<br>
+<br>
+; One high and one low register clobbered. lr also gets pushed to simplify the<br>
+; return, and r7 to keep the stack aligned. Here, we could use r0-r3, r4, r7 or<br>
+; lr to save/restore r8.<br>
+define void @one_high_one_low() {<br>
+; CHECK-LABEL: one_high_one_low:<br>
+entry:<br>
+; CHECK: push {r4, r7, lr}<br>
+; CHECK: mov [[SAVEREG:r0|r1|r2|r3|r4|r7|lr<wbr>]], r8<br>
+; CHECK: push {[[SAVEREG]]}<br>
+  tail call void asm sideeffect "", "~{r4},~{r8}"()<br>
+; CHECK: pop {[[RESTOREREG:r0|r1|r2|r3|r4|r<wbr>7]]}<br>
+; CHECK: mov r8, [[RESTOREREG]]<br>
+; CHECK: pop {r4, r7, pc}<br>
+  ret void<br>
+}<br>
+<br>
+; All callee-saved registers clobbered, r4-r7 and lr are not live after the<br>
+; first push so can be used for pushing the high registers.<br>
+define void @four_high_four_low() {<br>
+; CHECK-LABEL: four_high_four_low:<br>
+entry:<br>
+; CHECK: push {r4, r5, r6, r7, lr}<br>
+; CHECK: mov lr, r11<br>
+; CHECK: mov r7, r10<br>
+; CHECK: mov r6, r9<br>
+; CHECK: mov r5, r8<br>
+; CHECK: push {r5, r6, r7, lr}<br>
+  tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8}<wbr>,~{r9},~{r10},~{r11}"()<br>
+; CHECK: pop {r0, r1, r2, r3}<br>
+; CHECK: mov r8, r0<br>
+; CHECK: mov r9, r1<br>
+; CHECK: mov r10, r2<br>
+; CHECK: mov r11, r3<br>
+; CHECK: pop {r4, r5, r6, r7, pc}<br>
+  ret void<br>
+}<br>
+<br>
+<br>
+; All callee-saved registers clobbered, and frame pointer is requested. r7 now<br>
+; cannot be used while saving/restoring the high regs.<br>
+define void @four_high_four_low_frame_ptr(<wbr>) "no-frame-pointer-elim"="true" {<br>
+; CHECK-LABEL: four_high_four_low_frame_ptr:<br>
+entry:<br>
+; CHECK: push {r4, r5, r6, r7, lr}<br>
+; CHECK: add r7, sp, #12<br>
+; CHECK: mov lr, r11<br>
+; CHECK: mov r6, r10<br>
+; CHECK: mov r5, r9<br>
+; CHECK: mov r4, r8<br>
+; CHECK: push {r4, r5, r6, lr}<br>
+  tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8}<wbr>,~{r9},~{r10},~{r11}"()<br>
+; CHECK: pop {r0, r1, r2, r3}<br>
+; CHECK: mov r8, r0<br>
+; CHECK: mov r9, r1<br>
+; CHECK: mov r10, r2<br>
+; CHECK: mov r11, r3<br>
+; CHECK: pop {r4, r5, r6, r7, pc}<br>
+  ret void<br>
+}<br>
+<br>
+; All callee-saved registers clobbered, frame pointer is requested and<br>
+; llvm.returnaddress used. r7 and lr now cannot be used while saving/restoring<br>
+; the high regs.<br>
+define void @four_high_four_low_frame_ptr_<wbr>ret_addr() "no-frame-pointer-elim"="true" {<br>
+; CHECK-LABEL: four_high_four_low_frame_ptr_r<wbr>et_addr:<br>
+entry:<br>
+; CHECK: push {r4, r5, r6, r7, lr}<br>
+; CHECK: mov r6, r11<br>
+; CHECK: mov r5, r10<br>
+; CHECK: mov r4, r9<br>
+; CHECK: mov r3, r8<br>
+; CHECK: push {r3, r4, r5, r6}<br>
+  %a = tail call i8* @llvm.returnaddress(i32 0)<br>
+  tail call void asm sideeffect "", "r,~{r4},~{r5},~{r6},~{r7},~{r<wbr>8},~{r9},~{r10},~{r11}"(i8* %a)<br>
+; CHECK: pop {r0, r1, r2, r3}<br>
+; CHECK: mov r8, r0<br>
+; CHECK: mov r9, r1<br>
+; CHECK: mov r10, r2<br>
+; CHECK: mov r11, r3<br>
+; CHECK: pop {r4, r5, r6, r7, pc}<br>
+  ret void<br>
+}<br>
+<br>
+; 4 high regs clobbered, all 4 argument registers used. We push an extra 4 low<br>
+; registers, so that we can use them for saving the high regs.<br>
+define void @four_high_four_arg(i32 %a, i32 %b, i32 %c, i32 %d) {<br>
+; CHECK-LABEL: four_high_four_arg:<br>
+entry:<br>
+; CHECK: push    {r5, r6, r7, lr}<br>
+; CHECK: mov     lr, r11<br>
+; CHECK: mov     r7, r10<br>
+; CHECK: mov     r6, r9<br>
+; CHECK: mov     r5, r8<br>
+; CHECK: push    {r5, r6, r7, lr}<br>
+  tail call void asm sideeffect "", "r,r,r,r,~{r8},~{r9},~{r10},~{<wbr>r11}"(i32 %a, i32 %b, i32 %c, i32 %d)<br>
+; CHECK: pop     {r0, r1, r2, r3}<br>
+; CHECK: mov     r8, r0<br>
+; CHECK: mov     r9, r1<br>
+; CHECK: mov     r10, r2<br>
+; CHECK: mov     r11, r3<br>
+; CHECK: pop     {r5, r6, r7, pc}<br>
+  ret void<br>
+}<br>
+<br>
+; 4 high regs clobbered, all 4 return registers used. We push an extra 4 low<br>
+; registers, so that we can use them for restoring the high regs.<br>
+define <4 x i32> @four_high_four_return() {<br>
+; CHECK-LABEL: four_high_four_return:<br>
+entry:<br>
+; CHECK: push    {r4, r5, r6, r7, lr}<br>
+; CHECK: mov     lr, r11<br>
+; CHECK: mov     r7, r10<br>
+; CHECK: mov     r6, r9<br>
+; CHECK: mov     r5, r8<br>
+; CHECK: push    {r5, r6, r7, lr}<br>
+  tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"()<br>
+  %vecinit = insertelement <4 x i32> undef, i32 1, i32 0<br>
+  %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1<br>
+  %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2<br>
+  %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3<br>
+; CHECK: pop     {r4, r5, r6, r7}<br>
+; CHECK: mov     r8, r4<br>
+; CHECK: mov     r9, r5<br>
+; CHECK: mov     r10, r6<br>
+; CHECK: mov     r11, r7<br>
+; CHECK: pop     {r4, r5, r6, r7, pc}<br>
+  ret <4 x i32> %vecinit13<br>
+}<br>
+<br>
+; 4 high regs clobbered, all args & returns used, frame pointer requested and<br>
+; llvm.returnaddress called. This leaves us with 3 low registers available (r4,<br>
+; r5, r6), with which to save 4 high registers, so we have to use two pushes<br>
+; and pops.<br>
+define <4 x i32> @all_of_the_above(i32 %a, i32 %b, i32 %c, i32 %d) "no-frame-pointer-elim"="true" {<br>
+; CHECK-LABEL: all_of_the_above<br>
+entry:<br>
+; CHECK: push    {r4, r5, r6, r7, lr}<br>
+; CHECK: add     r7, sp, #12<br>
+; CHECK: mov     r6, r11<br>
+; CHECK: mov     r5, r10<br>
+; CHECK: mov     r4, r9<br>
+; CHECK: push    {r4, r5, r6}<br>
+; CHECK: mov     r6, r8<br>
+; CHECK: push    {r6}<br>
+  tail call void asm sideeffect "", "r,r,r,r,~{r4},~{r5},~{r6},~{r<wbr>7},~{r8},~{r9},~{r10},~{r11}"(<wbr>i32 %a, i32 %b, i32 %c, i32 %d)<br>
+  %e = tail call i8* @llvm.returnaddress(i32 0)<br>
+  %f = ptrtoint i8* %e to i32<br>
+  %vecinit = insertelement <4 x i32> undef, i32 %f, i32 0<br>
+  %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1<br>
+  %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2<br>
+  %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3<br>
+; CHECK: pop     {r4, r5, r6}<br>
+; CHECK: mov     r8, r4<br>
+; CHECK: mov     r9, r5<br>
+; CHECK: mov     r10, r6<br>
+; CHECK: pop     {r4}<br>
+; CHECK: mov     r11, r4<br>
+; CHECK: pop     {r4, r5, r6, r7, pc}<br>
+  ret <4 x i32> %vecinit13<br>
+}<br>
+<br>
+; When a base pointer is being used, we can safely use it for saving/restoring<br>
+; the high regs because it is set after the last push, and not used at all in the<br>
+; epliogue. We can also use r4 for restoring the registers despite it also being<br>
+; used when restoring sp from fp, as that happens before the first pop.<br>
+define <4 x i32> @base_pointer(i32 %a) {<br>
+; CHECK-LABEL: base_pointer:<br>
+entry:<br>
+; CHECK: push    {r4, r6, r7, lr}<br>
+; CHECK: add     r7, sp, #8<br>
+; CHECK: mov     lr, r9<br>
+; CHECK: mov     r6, r8<br>
+; CHECK: push    {r6, lr}<br>
+; CHECK: mov     r6, sp<br>
+  %b = alloca i32, i32 %a<br>
+  call void asm sideeffect "", "r,~{r8},~{r9}"(i32* %b)<br>
+  %vecinit = insertelement <4 x i32> undef, i32 1, i32 0<br>
+  %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1<br>
+  %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2<br>
+  %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3<br>
+; CHECK: subs    r4, r7, #7<br>
+; CHECK: subs    r4, #9<br>
+; CHECK: mov     sp, r4<br>
+; CHECK: pop     {r4, r6}<br>
+; CHECK: mov     r8, r4<br>
+; CHECK: mov     r9, r6<br>
+; CHECK: pop     {r4, r6, r7, pc}<br>
+  ret <4 x i32> %vecinit13<br>
+}<br>
<br>
Modified: llvm/trunk/test/CodeGen/Thumb/<wbr>large-stack.ll<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/Thumb/large-stack.ll?rev=283867&r1=283866&r2=283867&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/llvm/trunk/test/CodeGen/<wbr>Thumb/large-stack.ll?rev=<wbr>283867&r1=283866&r2=283867&<wbr>view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- llvm/trunk/test/CodeGen/Thumb/<wbr>large-stack.ll (original)<br>
+++ llvm/trunk/test/CodeGen/Thumb/<wbr>large-stack.ll Tue Oct 11 05:12:25 2016<br>
@@ -1,9 +1,9 @@<br>
-; RUN: llc < %s -mtriple=thumb-apple-ios | FileCheck %s<br>
-; RUN: llc < %s -mtriple=thumb-none-eabi | FileCheck %s<br>
+; RUN: llc < %s -mtriple=thumb-apple-ios | FileCheck %s --check-prefix=CHECK --check-prefix=ALIGN4<br>
+; RUN: llc < %s -mtriple=thumb-none-eabi | FileCheck %s --check-prefix=CHECK --check-prefix=ALIGN8<br>
 ; RUN: llc < %s -o %t -filetype=obj -mtriple=thumbv6-apple-ios<br>
-; RUN: llvm-objdump -triple=thumbv6-apple-ios -d %t | FileCheck %s<br>
+; RUN: llvm-objdump -triple=thumbv6-apple-ios -d %t | FileCheck %s --check-prefix=CHECK --check-prefix=ALIGN4<br>
 ; RUN: llc < %s -o %t -filetype=obj -mtriple=thumbv6-none-eabi<br>
-; RUN: llvm-objdump -triple=thumbv6-none-eabi -d %t | FileCheck %s<br>
+; RUN: llvm-objdump -triple=thumbv6-none-eabi -d %t | FileCheck %s --check-prefix=CHECK --check-prefix=ALIGN8<br>
<br>
 ; Largest stack for which a single tADDspi/tSUBspi is enough<br>
 define void @test1() {<br>
@@ -33,7 +33,9 @@ define void @test100_nofpelim() "no-fram<br>
 ; CHECK: sub sp, #508<br>
 ; CHECK: sub sp, #508<br>
 ; CHECK: sub sp, #508<br>
-; CHECK: subs r4, r7, #4<br>
+; ALIGN4: subs r4, r7, #4<br>
+; ALIGN8: subs r4, r7, #7<br>
+; ALIGN8: subs r4, #1<br>
 ; CHECK: mov sp, r4<br>
     %tmp = alloca [ 1524 x i8 ] , align 4<br>
     ret void<br>
@@ -55,7 +57,9 @@ define void @test2_nofpelim() "no-frame-<br>
 ; CHECK-LABEL: test2_nofpelim:<br>
 ; CHECK: ldr [[TEMP:r[0-7]]],<br>
 ; CHECK: add sp, [[TEMP]]<br>
-; CHECK: subs r4, r7, #4<br>
+; ALIGN4: subs r4, r7, #4<br>
+; ALIGN8: subs r4, r7, #7<br>
+; ALIGN8: subs r4, #1<br>
 ; CHECK: mov sp, r4<br>
     %tmp = alloca [ 1528 x i8 ] , align 4<br>
     ret void<br>
<br>
Modified: llvm/trunk/test/CodeGen/Thumb2<wbr>/frame-pointer.ll<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/Thumb2/frame-pointer.ll?rev=283867&r1=283866&r2=283867&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/llvm/trunk/test/CodeGen/<wbr>Thumb2/frame-pointer.ll?rev=<wbr>283867&r1=283866&r2=283867&<wbr>view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- llvm/trunk/test/CodeGen/Thumb2<wbr>/frame-pointer.ll (original)<br>
+++ llvm/trunk/test/CodeGen/Thumb2<wbr>/frame-pointer.ll Tue Oct 11 05:12:25 2016<br>
@@ -27,9 +27,9 @@ define void @leaf_nofpelim() "no-frame-p<br>
 ; need to use a frame pointer.<br>
 define void @leaf_lowreg_nofpelim() "no-frame-pointer-elim"="true" {<br>
 ; CHECK-LABEL: leaf_lowreg_nofpelim:<br>
-; CHECK: push {r4, r7, lr}<br>
-; CHECK: add r7, sp, #4<br>
-; CHECK: pop {r4, r7, pc}<br>
+; CHECK: push {r4, r6, r7, lr}<br>
+; CHECK: add r7, sp, #8<br>
+; CHECK: pop {r4, r6, r7, pc}<br>
   call void asm sideeffect "", "~{r4}" ()<br>
   ret void<br>
 }<br>
@@ -40,11 +40,11 @@ define void @leaf_lowreg_nofpelim() "no-<br>
 ; the stack.<br>
 define void @leaf_highreg_nofpelim() "no-frame-pointer-elim"="true" {<br>
 ; CHECK-LABEL: leaf_highreg_nofpelim:<br>
-; CHECK: push {r7, lr}<br>
-; CHECK: mov r7, sp<br>
+; CHECK: push {r6, r7, lr}<br>
+; CHECK: add r7, sp, #4<br>
 ; CHECK: str r8, [sp, #-4]!<br>
 ; CHECK: ldr r8, [sp], #4<br>
-; CHECK: pop {r7, pc}<br>
+; CHECK: pop {r6, r7, pc}<br>
   call void asm sideeffect "", "~{r8}" ()<br>
   ret void<br>
 }<br>
<br>
<br>
______________________________<wbr>_________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/<wbr>mailman/listinfo/llvm-commits</a><br>
</blockquote></div><br></div>
</div></div></blockquote></div><br></div>