The MC code couldn't handle ARM LDR instructions with negative offsets:

    vldr.64 d1, [r0, #-32]

The problem was with how the addressing mode 5 encodes the offsets. This change
makes sure that the way offsets are handled in addressing mode 5 is consistent
throughout the MC code. It involves re-refactoring the "getAddrModeImmOpValue"
method into an "Imm12" and "addressing mode 5" version. But not to worry! The
majority of the duplicated code has been unified.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@118144 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
index 4552374..f5dc38b 100644
--- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
+++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
@@ -8,6 +8,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "ARM.h"
+#include "ARMAddressingModes.h"
 #include "ARMSubtarget.h"
 #include "llvm/MC/MCParser/MCAsmLexer.h"
 #include "llvm/MC/MCParser/MCAsmParser.h"
@@ -260,16 +261,25 @@
 
     Inst.addOperand(MCOperand::CreateReg(Mem.BaseRegNum));
     assert(!Mem.OffsetIsReg && "invalid mode 5 operand");
+
     // FIXME: #-0 is encoded differently than #0. Does the parser preserve
     // the difference?
     if (Mem.Offset) {
       const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Mem.Offset);
-      assert(CE && "non-constant mode 5 offset operand!");
+      assert(CE && "Non-constant mode 5 offset operand!");
+
       // The MCInst offset operand doesn't include the low two bits (like
       // the instruction encoding).
-      Inst.addOperand(MCOperand::CreateImm(CE->getValue() / 4));
-    } else
+      int64_t Offset = CE->getValue() / 4;
+      if (Offset >= 0)
+        Inst.addOperand(MCOperand::CreateImm(ARM_AM::getAM5Opc(ARM_AM::add,
+                                                               Offset)));
+      else
+        Inst.addOperand(MCOperand::CreateImm(ARM_AM::getAM5Opc(ARM_AM::sub,
+                                                               -Offset)));
+    } else {
       Inst.addOperand(MCOperand::CreateImm(0));
+    }
   }
 
   virtual void dump(raw_ostream &OS) const;