ARM LDM encoding for the mode (ia, ib, da, db) operand.
[oota-llvm.git] / lib / Target / ARM / ARMMCCodeEmitter.cpp
index efa9677194b081552d79ee63e434db8207a5f47d..38f5c7970460cdd35c12522b837a10eab79a33ab 100644 (file)
@@ -14,6 +14,7 @@
 #define DEBUG_TYPE "arm-emitter"
 #include "ARM.h"
 #include "ARMAddressingModes.h"
+#include "ARMFixupKinds.h"
 #include "ARMInstrInfo.h"
 #include "llvm/MC/MCCodeEmitter.h"
 #include "llvm/MC/MCExpr.h"
@@ -22,7 +23,8 @@
 #include "llvm/Support/raw_ostream.h"
 using namespace llvm;
 
-STATISTIC(MCNumEmitted, "Number of MC instructions emitted");
+STATISTIC(MCNumEmitted, "Number of MC instructions emitted.");
+STATISTIC(MCNumCPRelocations, "Number of constant pool relocations created.");
 
 namespace {
 class ARMMCCodeEmitter : public MCCodeEmitter {
@@ -39,6 +41,21 @@ public:
 
   ~ARMMCCodeEmitter() {}
 
+  unsigned getNumFixupKinds() const { return 2; }
+
+  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const {
+    const static MCFixupKindInfo Infos[] = {
+      { "fixup_arm_pcrel_12", 2, 12, MCFixupKindInfo::FKF_IsPCRel },
+      { "fixup_arm_vfp_pcrel_12", 3, 8, MCFixupKindInfo::FKF_IsPCRel },
+    };
+
+    if (Kind < FirstTargetFixupKind)
+      return MCCodeEmitter::getFixupKindInfo(Kind);
+
+    assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() &&
+           "Invalid kind!");
+    return Infos[Kind - FirstTargetFixupKind];
+  }
   unsigned getMachineSoImmOpValue(unsigned SoImm) const;
 
   // getBinaryCodeForInstr - TableGen'erated function for getting the
@@ -60,6 +77,24 @@ public:
   uint32_t getAddrModeImm12OpValue(const MCInst &MI, unsigned OpIdx,
                                    SmallVectorImpl<MCFixup> &Fixups) const;
 
+  /// getLdStSORegOpValue - Return encoding info for 'reg +/- reg shop imm'
+  /// operand as needed by load/store instructions.
+  uint32_t getLdStSORegOpValue(const MCInst &MI, unsigned OpIdx,
+                               SmallVectorImpl<MCFixup> &Fixups) const;
+
+  /// getLdStmModeOpValue - Return encoding for load/store multiple mode.
+  uint32_t getLdStmModeOpValue(const MCInst &MI, unsigned OpIdx,
+                               SmallVectorImpl<MCFixup> &Fixups) const {
+    ARM_AM::AMSubMode Mode = (ARM_AM::AMSubMode)MI.getOperand(OpIdx).getImm();
+    switch (Mode) {
+    default: assert(0 && "Unknown addressing sub-mode!");
+    case ARM_AM::da: return 0;
+    case ARM_AM::ia: return 1;
+    case ARM_AM::db: return 2;
+    case ARM_AM::ib: return 3;
+    }
+  }
+
   /// getAddrMode5OpValue - Return encoding info for 'reg +/- imm8' operand.
   uint32_t getAddrMode5OpValue(const MCInst &MI, unsigned OpIdx,
                                SmallVectorImpl<MCFixup> &Fixups) const;
@@ -123,27 +158,14 @@ public:
   unsigned getAddrMode6OffsetOpValue(const MCInst &MI, unsigned Op,
                                      SmallVectorImpl<MCFixup> &Fixups) const;
 
-  unsigned getNumFixupKinds() const {
-    assert(0 && "ARMMCCodeEmitter::getNumFixupKinds() not yet implemented.");
-    return 0;
-  }
-
-  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const {
-    static MCFixupKindInfo rtn;
-    assert(0 && "ARMMCCodeEmitter::getFixupKindInfo() not yet implemented.");
-    return rtn;
-  }
-
-  void EmitByte(unsigned char C, unsigned &CurByte, raw_ostream &OS) const {
+  void EmitByte(unsigned char C, raw_ostream &OS) const {
     OS << (char)C;
-    ++CurByte;
   }
 
-  void EmitConstant(uint64_t Val, unsigned Size, unsigned &CurByte,
-                    raw_ostream &OS) const {
+  void EmitConstant(uint64_t Val, unsigned Size, raw_ostream &OS) const {
     // Output the constant in little endian byte order.
     for (unsigned i = 0; i != Size; ++i) {
-      EmitByte(Val & 255, CurByte, OS);
+      EmitByte(Val & 255, OS);
       Val >>= 8;
     }
   }
@@ -199,14 +221,6 @@ EncodeAddrModeOpValues(const MCInst &MI, unsigned OpIdx, unsigned &Reg,
   const MCOperand &MO  = MI.getOperand(OpIdx);
   const MCOperand &MO1 = MI.getOperand(OpIdx + 1);
 
-  // If The first operand isn't a register, we have a label reference.
-  if (!MO.isReg()) {
-    Reg = ARM::PC;              // Rn is PC.
-    Imm = 0;
-    // FIXME: Add a fixup referencing the label.
-    return true;
-  }
-
   Reg = getARMRegisterNumbering(MO.getReg());
 
   int32_t SImm = MO1.getImm();
@@ -234,10 +248,21 @@ getAddrModeImm12OpValue(const MCInst &MI, unsigned OpIdx,
   // {12}    = (U)nsigned (add == '1', sub == '0')
   // {11-0}  = imm12
   unsigned Reg, Imm12;
-  bool isAdd = EncodeAddrModeOpValues(MI, OpIdx, Reg, Imm12, Fixups);
+  bool isAdd = true;
+  // If The first operand isn't a register, we have a label reference.
+  const MCOperand &MO = MI.getOperand(OpIdx);
+  if (!MO.isReg()) {
+    Reg = getARMRegisterNumbering(ARM::PC);   // Rn is PC.
+    Imm12 = 0;
 
-  if (Reg == ARM::PC)
-    return ARM::PC << 13;       // Rn is PC;
+    assert(MO.isExpr() && "Unexpected machine operand type!");
+    const MCExpr *Expr = MO.getExpr();
+    MCFixupKind Kind = MCFixupKind(ARM::fixup_arm_pcrel_12);
+    Fixups.push_back(MCFixup::Create(0, Expr, Kind));
+
+    ++MCNumCPRelocations;
+  } else
+    isAdd = EncodeAddrModeOpValues(MI, OpIdx, Reg, Imm12, Fixups);
 
   uint32_t Binary = Imm12 & 0xfff;
   // Immediate is always encoded as positive. The 'U' bit controls add vs sub.
@@ -247,6 +272,49 @@ getAddrModeImm12OpValue(const MCInst &MI, unsigned OpIdx,
   return Binary;
 }
 
+uint32_t ARMMCCodeEmitter::
+getLdStSORegOpValue(const MCInst &MI, unsigned OpIdx,
+                    SmallVectorImpl<MCFixup> &Fixups) const {
+  const MCOperand &MO = MI.getOperand(OpIdx);
+  const MCOperand &MO1 = MI.getOperand(OpIdx+1);
+  const MCOperand &MO2 = MI.getOperand(OpIdx+2);
+  unsigned Rn = getARMRegisterNumbering(MO.getReg());
+  unsigned Rm = getARMRegisterNumbering(MO1.getReg());
+  ARM_AM::ShiftOpc ShOp = ARM_AM::getAM2ShiftOpc(MO2.getImm());
+  unsigned ShImm = ARM_AM::getAM2Offset(MO2.getImm());
+  bool isAdd = ARM_AM::getAM2Op(MO2.getImm()) == ARM_AM::add;
+  unsigned SBits;
+  // LSL - 00
+  // LSR - 01
+  // ASR - 10
+  // ROR - 11
+  switch (ShOp) {
+  default: llvm_unreachable("Unknown shift opc!");
+  case ARM_AM::no_shift:
+    assert(ShImm == 0 && "Non-zero shift amount with no shift type!");
+    // fall through
+  case ARM_AM::lsl: SBits = 0x0; break;
+  case ARM_AM::lsr: SBits = 0x1; break;
+  case ARM_AM::asr: SBits = 0x2; break;
+  case ARM_AM::ror: SBits = 0x3; break;
+  }
+
+  // {16-13} = Rn
+  // {12}    = isAdd
+  // {11-0}  = shifter
+  //  {3-0}  = Rm
+  //  {4}    = 0
+  //  {6-5}  = type
+  //  {11-7} = imm
+  int64_t Binary = Rm;
+  Binary |= Rn << 13;
+  Binary |= SBits << 5;
+  Binary |= ShImm << 7;
+  if (isAdd)
+    Binary |= 1 << 12;
+  return Binary;
+}
+
 /// getAddrMode5OpValue - Return encoding info for 'reg +/- imm12' operand.
 uint32_t ARMMCCodeEmitter::
 getAddrMode5OpValue(const MCInst &MI, unsigned OpIdx,
@@ -255,10 +323,20 @@ getAddrMode5OpValue(const MCInst &MI, unsigned OpIdx,
   // {8}    = (U)nsigned (add == '1', sub == '0')
   // {7-0}  = imm8
   unsigned Reg, Imm8;
-  EncodeAddrModeOpValues(MI, OpIdx, Reg, Imm8, Fixups);
+  // If The first operand isn't a register, we have a label reference.
+  const MCOperand &MO = MI.getOperand(OpIdx);
+  if (!MO.isReg()) {
+    Reg = getARMRegisterNumbering(ARM::PC);   // Rn is PC.
+    Imm8 = 0;
+
+    assert(MO.isExpr() && "Unexpected machine operand type!");
+    const MCExpr *Expr = MO.getExpr();
+    MCFixupKind Kind = MCFixupKind(ARM::fixup_arm_vfp_pcrel_12);
+    Fixups.push_back(MCFixup::Create(0, Expr, Kind));
 
-  if (Reg == ARM::PC)
-    return ARM::PC << 9;        // Rn is PC;
+    ++MCNumCPRelocations;
+  } else
+    EncodeAddrModeOpValues(MI, OpIdx, Reg, Imm8, Fixups);
 
   uint32_t Binary = ARM_AM::getAM5Offset(Imm8);
   // Immediate is always encoded as positive. The 'U' bit controls add vs sub.
@@ -403,9 +481,7 @@ EncodeInstruction(const MCInst &MI, raw_ostream &OS,
   if ((Desc.TSFlags & ARMII::FormMask) == ARMII::Pseudo)
     return;
 
-  // Keep track of the current byte being emitted.
-  unsigned CurByte = 0;
-  EmitConstant(getBinaryCodeForInstr(MI, Fixups), 4, CurByte, OS);
+  EmitConstant(getBinaryCodeForInstr(MI, Fixups), 4, OS);
   ++MCNumEmitted;  // Keep track of the # of mi's emitted.
 }