Fix frame index elimination to correctly handle thumb-2 addressing modes that don't allow negative offsets. During frame elimination convert *i12 opcode to a *i8 when necessary due to a negative offset.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@76883 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Target/ARM/ARMBaseInstrInfo.h b/lib/Target/ARM/ARMBaseInstrInfo.h
index 63e08da..be952d8 100644
--- a/lib/Target/ARM/ARMBaseInstrInfo.h
+++ b/lib/Target/ARM/ARMBaseInstrInfo.h
@@ -215,6 +215,13 @@
   // Return the opcode that implements 'Op', or 0 if no opcode
   virtual unsigned getOpcode(ARMII::Op Op) const =0;
 
+  // If 'opcode' is an instruction with an unsigned offset that also
+  // has a version with a signed offset, return the opcode for the
+  // version with the signed offset. In 'NumBits' return the number of
+  // bits for the signed offset.
+  virtual unsigned unsignedOffsetOpcodeToSigned(unsigned opcode,
+                                                unsigned *NumBits) const = 0;
+
   // Return true if the block does not fall through.
   virtual bool BlockHasNoFallThrough(const MachineBasicBlock &MBB) const =0;
 
diff --git a/lib/Target/ARM/ARMBaseRegisterInfo.cpp b/lib/Target/ARM/ARMBaseRegisterInfo.cpp
index 9e21f3c..ec5e89f 100644
--- a/lib/Target/ARM/ARMBaseRegisterInfo.cpp
+++ b/lib/Target/ARM/ARMBaseRegisterInfo.cpp
@@ -142,6 +142,11 @@
   return TII.getOpcode((ARMII::Op)Op);
 }
 
+unsigned ARMBaseRegisterInfo::
+unsignedOffsetOpcodeToSigned(unsigned opcode, unsigned *NumBits) const {
+  return TII.unsignedOffsetOpcodeToSigned(opcode, NumBits);
+}
+
 const unsigned*
 ARMBaseRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
   static const unsigned CalleeSavedRegs[] = {
@@ -1109,6 +1114,8 @@
     int InstrOffs = 0;
     unsigned NumBits = 0;
     unsigned Scale = 1;
+    bool encodedOffset = true;
+    bool HandlesNeg = true;
     switch (AddrMode) {
     case ARMII::AddrMode2: {
       ImmIdx = i+2;
@@ -1139,17 +1146,21 @@
       ImmIdx = i+1;
       InstrOffs = MI.getOperand(ImmIdx).getImm();
       NumBits = 12;
+      encodedOffset = false;
+      HandlesNeg = false;
       break;
     }
     case ARMII::AddrModeT2_i8: {
       ImmIdx = i+1;
       InstrOffs = MI.getOperand(ImmIdx).getImm();
       NumBits = 8;
+      encodedOffset = false;
       break;
     }
     case ARMII::AddrModeT2_so: {
       ImmIdx = i+2;
       InstrOffs = MI.getOperand(ImmIdx).getImm();
+      encodedOffset = false;
       break;
     }
     default:
@@ -1160,29 +1171,55 @@
     Offset += InstrOffs * Scale;
     assert((Offset & (Scale-1)) == 0 && "Can't encode this offset!");
     if (Offset < 0) {
+      // For addrmodes that cannot handle negative offsets, convert to
+      // an opcode that can, or set NumBits == 0 to avoid folding
+      // address computation
+      if (!HandlesNeg) {
+        unsigned usop = unsignedOffsetOpcodeToSigned(Opcode, &NumBits);
+        if (usop != 0) {
+          MI.setDesc(TII.get(usop));
+          HandlesNeg = true;
+          Opcode = usop;
+        }
+        else {
+          NumBits = 0;
+        }
+      }
+
       Offset = -Offset;
       isSub = true;
     }
 
-    // Common case: small offset, fits into instruction.
-    MachineOperand &ImmOp = MI.getOperand(ImmIdx);
-    int ImmedOffset = Offset / Scale;
-    unsigned Mask = (1 << NumBits) - 1;
-    if ((unsigned)Offset <= Mask * Scale) {
-      // Replace the FrameIndex with sp
-      MI.getOperand(i).ChangeToRegister(FrameReg, false);
-      if (isSub)
-        ImmedOffset |= 1 << NumBits;
+    // Attempt to fold address comp. if opcode has offset bits
+    if (NumBits > 0) {
+      // Common case: small offset, fits into instruction.
+      MachineOperand &ImmOp = MI.getOperand(ImmIdx);
+      int ImmedOffset = Offset / Scale;
+      unsigned Mask = (1 << NumBits) - 1;
+      if ((unsigned)Offset <= Mask * Scale) {
+        // Replace the FrameIndex with sp
+        MI.getOperand(i).ChangeToRegister(FrameReg, false);
+        if (isSub) {
+          if (encodedOffset)
+            ImmedOffset |= 1 << NumBits;
+          else
+            ImmedOffset = -ImmedOffset;
+        }
+        ImmOp.ChangeToImmediate(ImmedOffset);
+        return;
+      }
+      
+      // Otherwise, it didn't fit. Pull in what we can to simplify the immed.
+      ImmedOffset = ImmedOffset & Mask;
+      if (isSub) {
+          if (encodedOffset)
+            ImmedOffset |= 1 << NumBits;
+          else
+            ImmedOffset = -ImmedOffset;
+      }
       ImmOp.ChangeToImmediate(ImmedOffset);
-      return;
+      Offset &= ~(Mask*Scale);
     }
-
-    // Otherwise, it didn't fit. Pull in what we can to simplify the immed.
-    ImmedOffset = ImmedOffset & Mask;
-    if (isSub)
-      ImmedOffset |= 1 << NumBits;
-    ImmOp.ChangeToImmediate(ImmedOffset);
-    Offset &= ~(Mask*Scale);
   }
 
   // If we get here, the immediate doesn't fit into the instruction.  We folded
diff --git a/lib/Target/ARM/ARMBaseRegisterInfo.h b/lib/Target/ARM/ARMBaseRegisterInfo.h
index 9165bbc..ac5e6b6 100644
--- a/lib/Target/ARM/ARMBaseRegisterInfo.h
+++ b/lib/Target/ARM/ARMBaseRegisterInfo.h
@@ -59,6 +59,13 @@
   // Return the opcode that implements 'Op', or 0 if no opcode
   unsigned getOpcode(int Op) const;
 
+  // If 'opcode' is an instruction with an unsigned offset that also
+  // has a version with a signed offset, return the opcode for the
+  // version with the signed offset. In 'NumBits' return the number of
+  // bits for the signed offset.
+  unsigned unsignedOffsetOpcodeToSigned(unsigned opcode,
+                                        unsigned *NumBits) const;
+
 public:
   /// getRegisterNumbering - Given the enum value for some register, e.g.
   /// ARM::LR, return the number that it corresponds to (e.g. 14). It
diff --git a/lib/Target/ARM/ARMInstrInfo.cpp b/lib/Target/ARM/ARMInstrInfo.cpp
index 688dc31..45b77c8 100644
--- a/lib/Target/ARM/ARMInstrInfo.cpp
+++ b/lib/Target/ARM/ARMInstrInfo.cpp
@@ -30,6 +30,11 @@
 }
 
 unsigned ARMInstrInfo::
+unsignedOffsetOpcodeToSigned(unsigned opcode, unsigned *NumBits) const {
+  return 0;
+}
+
+unsigned ARMInstrInfo::
 getUnindexedOpcode(unsigned Opc) const {
   switch (Opc) {
   default: break;
diff --git a/lib/Target/ARM/ARMInstrInfo.h b/lib/Target/ARM/ARMInstrInfo.h
index 3e9f020..8ff0912 100644
--- a/lib/Target/ARM/ARMInstrInfo.h
+++ b/lib/Target/ARM/ARMInstrInfo.h
@@ -35,6 +35,13 @@
   // Return the opcode that implements 'Op', or 0 if no opcode
   unsigned getOpcode(ARMII::Op Op) const;
 
+  // If 'opcode' is an instruction with an unsigned offset that also
+  // has a version with a signed offset, return the opcode for the
+  // version with the signed offset. In 'NumBits' return the number of
+  // bits for the signed offset.
+  unsigned unsignedOffsetOpcodeToSigned(unsigned opcode,
+                                        unsigned *NumBits) const;
+
   // Return true if the block does not fall through.
   bool BlockHasNoFallThrough(const MachineBasicBlock &MBB) const;
 
diff --git a/lib/Target/ARM/ARMInstrThumb2.td b/lib/Target/ARM/ARMInstrThumb2.td
index 5361bb5..80b0d68 100644
--- a/lib/Target/ARM/ARMInstrThumb2.td
+++ b/lib/Target/ARM/ARMInstrThumb2.td
@@ -109,7 +109,7 @@
   let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm);
 }
 
-// t2addrmode_imm8  := reg - imm8
+// t2addrmode_imm8  := reg +/- imm8
 def t2addrmode_imm8 : Operand<i32>,
                       ComplexPattern<i32, 2, "SelectT2AddrModeImm8", []> {
   let PrintMethod = "printT2AddrModeImm8Operand";
diff --git a/lib/Target/ARM/Thumb1InstrInfo.cpp b/lib/Target/ARM/Thumb1InstrInfo.cpp
index ddc6e0d..7bec736 100644
--- a/lib/Target/ARM/Thumb1InstrInfo.cpp
+++ b/lib/Target/ARM/Thumb1InstrInfo.cpp
@@ -30,6 +30,12 @@
   return 0;
 }
 
+unsigned
+Thumb1InstrInfo::unsignedOffsetOpcodeToSigned(unsigned opcode,
+                                              unsigned *NumBits) const {
+  return 0;
+}
+
 unsigned Thumb1InstrInfo::getOpcode(ARMII::Op Op) const {
   switch (Op) {
   case ARMII::ADDri: return ARM::tADDi8;
diff --git a/lib/Target/ARM/Thumb1InstrInfo.h b/lib/Target/ARM/Thumb1InstrInfo.h
index 67b78fb..a1c9f04 100644
--- a/lib/Target/ARM/Thumb1InstrInfo.h
+++ b/lib/Target/ARM/Thumb1InstrInfo.h
@@ -34,6 +34,13 @@
   // Return the opcode that implements 'Op', or 0 if no opcode
   unsigned getOpcode(ARMII::Op Op) const;
 
+  // If 'opcode' is an instruction with an unsigned offset that also
+  // has a version with a signed offset, return the opcode for the
+  // version with the signed offset. In 'NumBits' return the number of
+  // bits for the signed offset.
+  unsigned unsignedOffsetOpcodeToSigned(unsigned opcode,
+                                        unsigned *NumBits) const;
+
   // Return true if the block does not fall through.
   bool BlockHasNoFallThrough(const MachineBasicBlock &MBB) const;
 
diff --git a/lib/Target/ARM/Thumb2InstrInfo.cpp b/lib/Target/ARM/Thumb2InstrInfo.cpp
index 081cf4f..d92856c 100644
--- a/lib/Target/ARM/Thumb2InstrInfo.cpp
+++ b/lib/Target/ARM/Thumb2InstrInfo.cpp
@@ -88,6 +88,29 @@
   return false;
 }
 
+unsigned
+Thumb2InstrInfo::unsignedOffsetOpcodeToSigned(unsigned opcode,
+                                              unsigned *NumBits) const
+{
+  if (NumBits != NULL)
+    *NumBits = 8;
+
+  switch (opcode) {
+  case ARM::t2LDRi12:   return ARM::t2LDRi8;
+  case ARM::t2LDRHi12:  return ARM::t2LDRHi8;
+  case ARM::t2LDRBi12:  return ARM::t2LDRBi8;
+  case ARM::t2LDRSHi12: return ARM::t2LDRSHi8;
+  case ARM::t2LDRSBi12: return ARM::t2LDRSBi8;
+  case ARM::t2STRi12:   return ARM::t2STRi8;
+  case ARM::t2STRBi12:  return ARM::t2STRBi8;
+  case ARM::t2STRHi12:  return ARM::t2STRHi8;
+  default:
+    break;
+  }
+
+  return 0;
+}
+
 bool
 Thumb2InstrInfo::copyRegToReg(MachineBasicBlock &MBB,
                               MachineBasicBlock::iterator I,
diff --git a/lib/Target/ARM/Thumb2InstrInfo.h b/lib/Target/ARM/Thumb2InstrInfo.h
index ac31707..43ea56e 100644
--- a/lib/Target/ARM/Thumb2InstrInfo.h
+++ b/lib/Target/ARM/Thumb2InstrInfo.h
@@ -34,6 +34,13 @@
   // Return the opcode that implements 'Op', or 0 if no opcode
   unsigned getOpcode(ARMII::Op Op) const;
 
+  // If 'opcode' is an instruction with an unsigned offset that also
+  // has a version with a signed offset, return the opcode for the
+  // version with the signed offset. In 'NumBits' return the number of
+  // bits for the signed offset.
+  unsigned unsignedOffsetOpcodeToSigned(unsigned opcode,
+                                        unsigned *NumBits) const;
+
   // Return true if the block does not fall through.
   bool BlockHasNoFallThrough(const MachineBasicBlock &MBB) const;