[llvm] 53a2bf8 - [M68k][VarLenCodeEmitter] Support reloc & pc-rel immediate values

Min-Yih Hsu via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 15 20:45:02 PST 2022


Author: Min-Yih Hsu
Date: 2022-02-15T20:41:33-08:00
New Revision: 53a2bf8ac7c2d631f5a32157df0b701843e12de6

URL: https://github.com/llvm/llvm-project/commit/53a2bf8ac7c2d631f5a32157df0b701843e12de6
DIFF: https://github.com/llvm/llvm-project/commit/53a2bf8ac7c2d631f5a32157df0b701843e12de6.diff

LOG: [M68k][VarLenCodeEmitter] Support reloc & pc-rel immediate values

Supporting relocatable and pc-relative immediate values for the new code
emitter.

Differential Revision: https://reviews.llvm.org/D119101

Added: 
    llvm/test/MC/M68k/pc-rel.s

Modified: 
    llvm/lib/Target/M68k/M68kInstrFormats.td
    llvm/lib/Target/M68k/MCTargetDesc/M68kMCCodeEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/M68k/M68kInstrFormats.td b/llvm/lib/Target/M68k/M68kInstrFormats.td
index b3c4fdfe2f530..0518faa77a283 100644
--- a/llvm/lib/Target/M68k/M68kInstrFormats.td
+++ b/llvm/lib/Target/M68k/M68kInstrFormats.td
@@ -243,7 +243,8 @@ def MxEncEAk : MxEncEA<MxBead3Bits<0b011>, MxBead2Bits<0b11>, MxBead1Bit<1>>;
 def MxEncEAi : MxEncEA<MxBead3Bits<0b100>, MxBead2Bits<0b11>, MxBead1Bit<1>>;
 
 class MxEncBriefExt<string reg_opnd, string disp_opnd,
-                    bit size_w_l = false, int scale = 1> {
+                    bit size_w_l = false, int scale = 1,
+                    string disp_encoder = ""> {
   dag Value = (descend
     // D/A + REGISTER
     (operand "$"#reg_opnd, 4),
@@ -258,16 +259,22 @@ class MxEncBriefExt<string reg_opnd, string disp_opnd,
     ),
     0b0,
     // Displacement
-    (operand "$"#disp_opnd, 8)
+    (operand "$"#disp_opnd, 8, (encoder disp_encoder))
   );
 }
 
+class MxEncAddrMode_r<string reg_opnd> : MxEncMemOp {
+  let EA = (descend /*MODE without the last bit*/0b00,
+                    /*REGISTER with D/A bit*/(operand "$"#reg_opnd, 4));
+}
+
 class MxEncAddrMode_k<string opnd_name> : MxEncMemOp {
   let EA = (descend /*MODE*/0b111,
-                     /*REGISTER*/0b011);
+                    /*REGISTER*/0b011);
 
   let Supplement = MxEncBriefExt<opnd_name#".index", opnd_name#".disp",
-                                 /*W/L*/true>.Value;
+                                 /*W/L*/true, /*SCALE*/1,
+                                 "encodePCRelImm<8>">.Value;
 }
 
 class MxEncAddrMode_q<string opnd_name> : MxEncMemOp {
@@ -275,7 +282,8 @@ class MxEncAddrMode_q<string opnd_name> : MxEncMemOp {
                      /*REGISTER*/0b010);
 
   // 16-bit Displacement
-  let Supplement = (operand "$"#opnd_name, 16);
+  let Supplement = (operand "$"#opnd_name, 16,
+                            (encoder "encodePCRelImm<16>"));
 }
 
 class MxEncAddrMode_p<string opnd_name> : MxEncMemOp {
@@ -283,7 +291,8 @@ class MxEncAddrMode_p<string opnd_name> : MxEncMemOp {
                      /*REGISTER*/(operand "$"#opnd_name#".reg", 3));
 
   // 16-bit Displacement
-  let Supplement = (operand "$"#opnd_name#".disp", 16);
+  let Supplement = (operand "$"#opnd_name#".disp", 16,
+                            (encoder "encodeRelocImm<16>"));
 }
 
 class MxEncAddrMode_f<string opnd_name> : MxEncMemOp {
@@ -291,7 +300,8 @@ class MxEncAddrMode_f<string opnd_name> : MxEncMemOp {
                      /*REGISTER*/(operand "$"#opnd_name#".reg", 3));
 
   let Supplement = MxEncBriefExt<opnd_name#".index", opnd_name#".disp",
-                                 /*W/L*/true>.Value;
+                                 /*W/L*/true, /*SCALE*/1,
+                                 "encodeRelocImm<8>">.Value;
 }
 
 class MxEncAddrMode_j<string reg_opnd> : MxEncMemOp {
@@ -322,10 +332,9 @@ class MxEncAddrMode_abs<string opnd_name, bit size_w_l = false> : MxEncMemOp {
   // Absolute address
   let Supplement = !if(size_w_l,
     // abs.L
-    (ascend (slice "$"#opnd_name, 31, 16),
-            (slice "$"#opnd_name, 15, 0)),
+    (operand "$"#opnd_name, 32, (encoder "encodeRelocImm<32>")),
     // abs.W
-    (operand "$"#opnd_name, 16)
+    (operand "$"#opnd_name, 16, (encoder "encodeRelocImm<16>"))
   );
 }
 

diff  --git a/llvm/lib/Target/M68k/MCTargetDesc/M68kMCCodeEmitter.cpp b/llvm/lib/Target/M68k/MCTargetDesc/M68kMCCodeEmitter.cpp
index c90126e52db0d..64c3c3324f3a3 100644
--- a/llvm/lib/Target/M68k/MCTargetDesc/M68kMCCodeEmitter.cpp
+++ b/llvm/lib/Target/M68k/MCTargetDesc/M68kMCCodeEmitter.cpp
@@ -27,6 +27,7 @@
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/EndianStream.h"
 #include "llvm/Support/raw_ostream.h"
+#include <type_traits>
 
 using namespace llvm;
 
@@ -48,6 +49,16 @@ class M68kMCCodeEmitter : public MCCodeEmitter {
                          SmallVectorImpl<MCFixup> &Fixups,
                          const MCSubtargetInfo &STI) const;
 
+  template <unsigned Size>
+  void encodeRelocImm(const MCInst &MI, unsigned OpIdx, unsigned InsertPos,
+                      APInt &Value, SmallVectorImpl<MCFixup> &Fixups,
+                      const MCSubtargetInfo &STI) const;
+
+  template <unsigned Size>
+  void encodePCRelImm(const MCInst &MI, unsigned OpIdx, unsigned InsertPos,
+                      APInt &Value, SmallVectorImpl<MCFixup> &Fixups,
+                      const MCSubtargetInfo &STI) const;
+
 public:
   M68kMCCodeEmitter(const MCInstrInfo &mcii, MCContext &ctx)
       : MCII(mcii), Ctx(ctx) {}
@@ -83,6 +94,122 @@ class M68kMCCodeEmitter : public MCCodeEmitter {
 
 #include "M68kGenMCCodeEmitter.inc"
 
+// Select the proper unsigned integer type from a bit size.
+template <unsigned Size> struct select_uint_t {
+  using type = typename std::conditional<
+      Size == 8, uint8_t,
+      typename std::conditional<
+          Size == 16, uint16_t,
+          typename std::conditional<Size == 32, uint32_t,
+                                    uint64_t>::type>::type>::type;
+};
+
+// On a LE host:
+// MSB                   LSB    MSB                   LSB
+// | 0x12 0x34 | 0xAB 0xCD | -> | 0xAB 0xCD | 0x12 0x34 |
+// (On a BE host nothing changes)
+template <typename value_t> static value_t swapWord(value_t Val) {
+  const unsigned NumWords = sizeof(Val) / 2;
+  if (NumWords <= 1)
+    return Val;
+  Val = support::endian::byte_swap(Val, support::big);
+  value_t NewVal = 0;
+  for (unsigned i = 0U; i != NumWords; ++i) {
+    uint16_t Part = (Val >> (i * 16)) & 0xFFFF;
+    Part = support::endian::byte_swap(Part, support::big);
+    NewVal |= (Part << (i * 16));
+  }
+  return NewVal;
+}
+
+// Figure out which byte we're at in big endian mode.
+template <unsigned Size> static unsigned getBytePosition(unsigned BitPos) {
+  if (Size % 16) {
+    return static_cast<unsigned>(BitPos / 8 + ((BitPos & 0b1111) < 8 ? 1 : -1));
+  } else {
+    assert(!(BitPos & 0b1111) && "Not aligned to word boundary?");
+    return BitPos / 8;
+  }
+}
+
+// We need special handlings for relocatable & pc-relative operands that are
+// larger than a word.
+// A M68k instruction is aligned by word (16 bits). That means, 32-bit
+// (& 64-bit) immediate values are separated into hi & lo words and placed
+// at lower & higher addresses, respectively. For immediate values that can
+// be easily expressed in TG, we explicitly rotate the word ordering like
+// this:
+// ```
+// (ascend (slice "$imm", 31, 16), (slice "$imm", 15, 0))
+// ```
+// For operands that call into encoder functions, we need to use the `swapWord`
+// function to assure the correct word ordering on LE host. Note that
+// M68kMCCodeEmitter does massage _byte_ ordering of the final encoded
+// instruction but it assumes everything aligns on word boundaries. So things
+// will go wrong if we don't take care of the _word_ ordering here.
+template <unsigned Size>
+void M68kMCCodeEmitter::encodeRelocImm(const MCInst &MI, unsigned OpIdx,
+                                       unsigned InsertPos, APInt &Value,
+                                       SmallVectorImpl<MCFixup> &Fixups,
+                                       const MCSubtargetInfo &STI) const {
+  using value_t = typename select_uint_t<Size>::type;
+  const MCOperand &MCO = MI.getOperand(OpIdx);
+  if (MCO.isImm()) {
+    Value |= swapWord<value_t>(static_cast<value_t>(MCO.getImm()));
+  } else if (MCO.isExpr()) {
+    const MCExpr *Expr = MCO.getExpr();
+
+    // Absolute address
+    int64_t Addr;
+    if (Expr->evaluateAsAbsolute(Addr)) {
+      Value |= swapWord<value_t>(static_cast<value_t>(Addr));
+      return;
+    }
+
+    // Relocatable address
+    unsigned InsertByte = getBytePosition<Size>(InsertPos);
+    Fixups.push_back(MCFixup::create(InsertByte, Expr,
+                                     getFixupForSize(Size, /*IsPCRel=*/false),
+                                     MI.getLoc()));
+  }
+}
+
+template <unsigned Size>
+void M68kMCCodeEmitter::encodePCRelImm(const MCInst &MI, unsigned OpIdx,
+                                       unsigned InsertPos, APInt &Value,
+                                       SmallVectorImpl<MCFixup> &Fixups,
+                                       const MCSubtargetInfo &STI) const {
+  const MCOperand &MCO = MI.getOperand(OpIdx);
+  if (MCO.isImm()) {
+    using value_t = typename select_uint_t<Size>::type;
+    Value |= swapWord<value_t>(static_cast<value_t>(MCO.getImm()));
+  } else if (MCO.isExpr()) {
+    const MCExpr *Expr = MCO.getExpr();
+    unsigned InsertByte = getBytePosition<Size>(InsertPos);
+
+    // Special handlings for sizes smaller than a word.
+    if (Size < 16) {
+      int LabelOffset = 0;
+      if (InsertPos < 16)
+        // If the patch point is at the first word, PC is pointing at the
+        // next word.
+        LabelOffset = InsertByte - 2;
+      else if (InsertByte % 2)
+        // Otherwise the PC is pointing at the first byte of this word.
+        // So we need to consider the offset between PC and the fixup byte.
+        LabelOffset = 1;
+
+      if (LabelOffset)
+        Expr = MCBinaryExpr::createAdd(
+            Expr, MCConstantExpr::create(LabelOffset, Ctx), Ctx);
+    }
+
+    Fixups.push_back(MCFixup::create(InsertByte, Expr,
+                                     getFixupForSize(Size, /*IsPCRel=*/true),
+                                     MI.getLoc()));
+  }
+}
+
 void M68kMCCodeEmitter::getMachineOpValue(const MCInst &MI, const MCOperand &Op,
                                           unsigned InsertPos, APInt &Value,
                                           SmallVectorImpl<MCFixup> &Fixups,
@@ -98,6 +225,13 @@ void M68kMCCodeEmitter::getMachineOpValue(const MCInst &MI, const MCOperand &Op,
   } else if (Op.isImm()) {
     // Immediate
     Value |= static_cast<uint64_t>(Op.getImm());
+  } else if (Op.isExpr()) {
+    // Absolute address
+    int64_t Addr;
+    if (!Op.getExpr()->evaluateAsAbsolute(Addr))
+      report_fatal_error("Unsupported asm expression. Only absolute address "
+                         "can be placed here.");
+    Value |= static_cast<uint64_t>(Addr);
   } else {
     llvm_unreachable("Unsupported operand type");
   }

diff  --git a/llvm/test/MC/M68k/pc-rel.s b/llvm/test/MC/M68k/pc-rel.s
new file mode 100644
index 0000000000000..a4d76d341bb05
--- /dev/null
+++ b/llvm/test/MC/M68k/pc-rel.s
@@ -0,0 +1,22 @@
+; RUN: llvm-mc -triple=m68k -show-encoding %s | FileCheck %s
+
+        ; A fixup whose size is multiple of a word.
+	; CHECK:      cmpi.l  #87, (.LBB0_1,%pc)
+	; CHECK-SAME: encoding: [0x0c,0xba,0x00,0x00,0x00,0x57,A,A]
+        ; CHECK:      fixup A - offset: 6, value: .LBB0_1, kind: FK_PCRel_2
+	cmpi.l  #87, (.LBB0_1,%pc)
+
+        ; A fixup that is smaller than a word.
+        ; For cases where the fixup is located in the first word, they are
+        ; tested by `Control/branch-pc-rel.s`.
+	; CHECK:      cmpi.l  #94, (.LBB0_2,%pc,%a0)
+	; CHECK-SAME: encoding: [0x0c,0xbb,0x00,0x00,0x00,0x5e,0x88,A]
+        ; CHECK:      fixup A - offset: 7, value: .LBB0_2+1, kind: FK_PCRel_1
+	cmpi.l  #94, (.LBB0_2,%pc,%a0)
+.LBB0_1:
+	add.l	#0, %d0
+	rts
+.LBB0_2:
+	add.l	#1, %d0
+	rts
+


        


More information about the llvm-commits mailing list