Enable ARM base register reuse to local stack slot allocation. Whenever a new
frame index reference to an object in the local block is seen, check if
it's near enough to any previously allocaated base register to re-use.

rdar://8277890



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@111443 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/CodeGen/LocalStackSlotAllocation.cpp b/lib/CodeGen/LocalStackSlotAllocation.cpp
index 219b845..61d6b10 100644
--- a/lib/CodeGen/LocalStackSlotAllocation.cpp
+++ b/lib/CodeGen/LocalStackSlotAllocation.cpp
@@ -43,8 +43,11 @@
 
 namespace {
   class LocalStackSlotPass: public MachineFunctionPass {
-    void calculateFrameObjectOffsets(MachineFunction &Fn);
+    SmallVector<int64_t,16> LocalOffsets;
 
+    void AdjustStackOffset(MachineFrameInfo *MFI, int FrameIdx, int64_t &Offset,
+                           unsigned &MaxAlign);
+    void calculateFrameObjectOffsets(MachineFunction &Fn);
     void insertFrameReferenceRegisters(MachineFunction &Fn);
   public:
     static char ID; // Pass identification, replacement for typeid
@@ -70,18 +73,29 @@
 }
 
 bool LocalStackSlotPass::runOnMachineFunction(MachineFunction &MF) {
+  MachineFrameInfo *MFI = MF.getFrameInfo();
+  unsigned LocalObjectCount = MFI->getObjectIndexEnd();
+
+  // Early exit if there are no locals to consider
+  if (!LocalObjectCount)
+    return true;
+
+  // Make sure we have enough space to store the local offsets.
+  LocalOffsets.resize(MFI->getObjectIndexEnd());
+
   // Lay out the local blob.
   calculateFrameObjectOffsets(MF);
 
   // Insert virtual base registers to resolve frame index references.
   insertFrameReferenceRegisters(MF);
+
   return true;
 }
 
 /// AdjustStackOffset - Helper function used to adjust the stack frame offset.
-static inline void
-AdjustStackOffset(MachineFrameInfo *MFI, int FrameIdx, int64_t &Offset,
-                  unsigned &MaxAlign) {
+void LocalStackSlotPass::AdjustStackOffset(MachineFrameInfo *MFI,
+                                           int FrameIdx, int64_t &Offset,
+                                           unsigned &MaxAlign) {
   unsigned Align = MFI->getObjectAlignment(FrameIdx);
 
   // If the alignment of this object is greater than that of the stack, then
@@ -93,6 +107,9 @@
 
   DEBUG(dbgs() << "Allocate FI(" << FrameIdx << ") to local offset "
         << Offset << "\n");
+  // Keep the offset available for base register allocation
+  LocalOffsets[FrameIdx] = Offset;
+  // And tell MFI about it for PEI to use later
   MFI->mapLocalFrameObject(FrameIdx, Offset);
   Offset += MFI->getObjectSize(FrameIdx);
 
@@ -149,12 +166,16 @@
 static inline bool
 lookupCandidateBaseReg(const SmallVector<std::pair<unsigned, int64_t>, 8> &Regs,
                        std::pair<unsigned, int64_t> &RegOffset,
+                       int64_t LocalFrameOffset,
                        const MachineInstr *MI,
                        const TargetRegisterInfo *TRI) {
   unsigned e = Regs.size();
   for (unsigned i = 0; i < e; ++i) {
     RegOffset = Regs[i];
-    if (TRI->isBaseRegInRange(MI, RegOffset.first, RegOffset.second))
+    // Check if the relative offset from the where the base register references
+    // to the target address is in range for the instruction.
+    int64_t Offset = LocalFrameOffset - RegOffset.second;
+    if (TRI->isBaseRegInRange(MI, RegOffset.first, Offset))
       return true;
   }
   return false;
@@ -173,6 +194,9 @@
 
   for (MachineFunction::iterator BB = Fn.begin(),
          E = Fn.end(); BB != E; ++BB) {
+    // A base register definition is a register+offset pair.
+    SmallVector<std::pair<unsigned, int64_t>, 8> BaseRegisters;
+
     for (MachineBasicBlock::iterator I = BB->begin(); I != BB->end(); ++I) {
       MachineInstr *MI = I;
       // Debug value instructions can't be out of range, so they don't need
@@ -183,9 +207,6 @@
       if (MI->isDebugValue())
         continue;
 
-      // A base register definition is a register+offset pair.
-      SmallVector<std::pair<unsigned, int64_t>, 8> BaseRegisters;
-
       // For now, allocate the base register(s) within the basic block
       // where they're used, and don't try to keep them around outside
       // of that. It may be beneficial to try sharing them more broadly
@@ -212,10 +233,12 @@
             // create a new one.
 
             std::pair<unsigned, int64_t> RegOffset;
-            if (lookupCandidateBaseReg(BaseRegisters, RegOffset, MI, TRI)) {
+            if (lookupCandidateBaseReg(BaseRegisters, RegOffset,
+                                       LocalOffsets[FrameIdx], MI, TRI)) {
+              DEBUG(dbgs() << "  Reusing base register " << RegOffset.first);
               // We found a register to reuse.
               BaseReg = RegOffset.first;
-              Offset = RegOffset.second;
+              Offset = LocalOffsets[FrameIdx] - RegOffset.second;
             } else {
               // No previously defined register was in range, so create a
               // new one.
diff --git a/lib/Target/ARM/ARMBaseRegisterInfo.cpp b/lib/Target/ARM/ARMBaseRegisterInfo.cpp
index b498075..0330fac 100644
--- a/lib/Target/ARM/ARMBaseRegisterInfo.cpp
+++ b/lib/Target/ARM/ARMBaseRegisterInfo.cpp
@@ -1453,6 +1453,75 @@
 
 bool ARMBaseRegisterInfo::isBaseRegInRange(const MachineInstr *MI,
                                            unsigned Reg, int64_t Offset) const {
+  const TargetInstrDesc &Desc = MI->getDesc();
+  unsigned AddrMode = (Desc.TSFlags & ARMII::AddrModeMask);
+  unsigned i = 0;
+
+  while (!MI->getOperand(i).isFI()) {
+    ++i;
+    assert(i < MI->getNumOperands() &&"Instr doesn't have FrameIndex operand!");
+  }
+
+  // AddrMode4 and AddrMode6 cannot handle any offset.
+  if (AddrMode == ARMII::AddrMode4 || AddrMode == ARMII::AddrMode6)
+    return Offset == 0;
+
+  unsigned NumBits = 0;
+  unsigned Scale = 1;
+  unsigned ImmIdx = 0;
+  int InstrOffs;
+  switch(AddrMode) {
+  case ARMII::AddrModeT2_i8:
+  case ARMII::AddrModeT2_i12:
+    // i8 supports only negative, and i12 supports only positive, so
+    // based on Offset sign, consider the appropriate instruction
+    Offset += MI->getOperand(i+1).getImm();
+    if (Offset < 0) {
+      NumBits = 8;
+      Offset = -Offset;
+    } else {
+      NumBits = 12;
+    }
+    break;
+  case ARMII::AddrMode5: {
+    // VFP address mode.
+    const MachineOperand &OffOp = MI->getOperand(i+1);
+    int InstrOffs = ARM_AM::getAM5Offset(OffOp.getImm());
+    if (ARM_AM::getAM5Op(OffOp.getImm()) == ARM_AM::sub)
+      InstrOffs = -InstrOffs;
+    NumBits = 8;
+    Scale = 4;
+    break;
+  }
+  case ARMII::AddrMode2: {
+    ImmIdx = i+2;
+    InstrOffs = ARM_AM::getAM2Offset(MI->getOperand(ImmIdx).getImm());
+    if (ARM_AM::getAM2Op(MI->getOperand(ImmIdx).getImm()) == ARM_AM::sub)
+      InstrOffs = -InstrOffs;
+    NumBits = 12;
+    break;
+  }
+  case ARMII::AddrMode3: {
+    ImmIdx = i+2;
+    InstrOffs = ARM_AM::getAM3Offset(MI->getOperand(ImmIdx).getImm());
+    if (ARM_AM::getAM3Op(MI->getOperand(ImmIdx).getImm()) == ARM_AM::sub)
+      InstrOffs = -InstrOffs;
+    NumBits = 8;
+    break;
+  }
+  default:
+    llvm_unreachable("Unsupported addressing mode!");
+    break;
+  }
+
+  Offset += InstrOffs * Scale;
+  assert((Offset & (Scale-1)) == 0 && "Can't encode this offset!");
+  if (Offset < 0)
+    Offset = -Offset;
+
+  unsigned Mask = (1 << NumBits) - 1;
+  if ((unsigned)Offset <= Mask * Scale)
+    return true;
 
   return false;
 }