[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
This commit is contained in:
Min-Yih Hsu 2021-12-17 10:06:19 +08:00
parent 920146316d
commit 53a2bf8ac7
3 changed files with 175 additions and 10 deletions
llvm
lib/Target/M68k
test/MC/M68k

View File

@ -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>"))
);
}

View File

@ -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 @@ public:
#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");
}

View File

@ -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