diff --git a/llvm/lib/Target/M68k/M68kInstrFormats.td b/llvm/lib/Target/M68k/M68kInstrFormats.td --- a/llvm/lib/Target/M68k/M68kInstrFormats.td +++ b/llvm/lib/Target/M68k/M68kInstrFormats.td @@ -243,7 +243,8 @@ def MxEncEAi : MxEncEA, MxBead2Bits<0b11>, MxBead1Bit<1>>; class MxEncBriefExt { + 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 @@ ), 0b0, // Displacement - (operand "$"#disp_opnd, 8) + (operand "$"#disp_opnd, 8, (encoder disp_encoder)) ); } +class MxEncAddrMode_r : MxEncMemOp { + let EA = (descend /*MODE without the last bit*/0b00, + /*REGISTER with D/A bit*/(operand "$"#reg_opnd, 4)); +} + class MxEncAddrMode_k : MxEncMemOp { let EA = (descend /*MODE*/0b111, - /*REGISTER*/0b011); + /*REGISTER*/0b011); let Supplement = MxEncBriefExt.Value; + /*W/L*/true, /*SCALE*/1, + "encodePCRelImm<8>">.Value; } class MxEncAddrMode_q : MxEncMemOp { @@ -275,7 +282,8 @@ /*REGISTER*/0b010); // 16-bit Displacement - let Supplement = (operand "$"#opnd_name, 16); + let Supplement = (operand "$"#opnd_name, 16, + (encoder "encodePCRelImm<16>")); } class MxEncAddrMode_p : MxEncMemOp { @@ -283,7 +291,8 @@ /*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 : MxEncMemOp { @@ -291,7 +300,8 @@ /*REGISTER*/(operand "$"#opnd_name#".reg", 3)); let Supplement = MxEncBriefExt.Value; + /*W/L*/true, /*SCALE*/1, + "encodeRelocImm<8>">.Value; } class MxEncAddrMode_j : MxEncMemOp { @@ -322,10 +332,9 @@ // 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 --- 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 using namespace llvm; @@ -48,6 +49,16 @@ SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; + template + void encodeRelocImm(const MCInst &MI, unsigned OpIdx, unsigned InsertPos, + APInt &Value, SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + template + void encodePCRelImm(const MCInst &MI, unsigned OpIdx, unsigned InsertPos, + APInt &Value, SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + public: M68kMCCodeEmitter(const MCInstrInfo &mcii, MCContext &ctx) : MCII(mcii), Ctx(ctx) {} @@ -83,6 +94,122 @@ #include "M68kGenMCCodeEmitter.inc" +// Select the proper unsigned integer type from a bit size. +template struct select_uint_t { + using type = typename std::conditional< + Size == 8, uint8_t, + typename std::conditional< + Size == 16, uint16_t, + typename std::conditional::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 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 static unsigned getBytePosition(unsigned BitPos) { + if (Size % 16) { + return static_cast(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 +void M68kMCCodeEmitter::encodeRelocImm(const MCInst &MI, unsigned OpIdx, + unsigned InsertPos, APInt &Value, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + using value_t = typename select_uint_t::type; + const MCOperand &MCO = MI.getOperand(OpIdx); + if (MCO.isImm()) { + Value |= swapWord(static_cast(MCO.getImm())); + } else if (MCO.isExpr()) { + const MCExpr *Expr = MCO.getExpr(); + + // Absolute address + int64_t Addr; + if (Expr->evaluateAsAbsolute(Addr)) { + Value |= swapWord(static_cast(Addr)); + return; + } + + // Relocatable address + unsigned InsertByte = getBytePosition(InsertPos); + Fixups.push_back(MCFixup::create(InsertByte, Expr, + getFixupForSize(Size, /*IsPCRel=*/false), + MI.getLoc())); + } +} + +template +void M68kMCCodeEmitter::encodePCRelImm(const MCInst &MI, unsigned OpIdx, + unsigned InsertPos, APInt &Value, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MCO = MI.getOperand(OpIdx); + if (MCO.isImm()) { + using value_t = typename select_uint_t::type; + Value |= swapWord(static_cast(MCO.getImm())); + } else if (MCO.isExpr()) { + const MCExpr *Expr = MCO.getExpr(); + unsigned InsertByte = getBytePosition(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 &Fixups, @@ -98,6 +225,13 @@ } else if (Op.isImm()) { // Immediate Value |= static_cast(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(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 --- /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 +