forked from OSchip/llvm-project
388 lines
12 KiB
C++
388 lines
12 KiB
C++
//===-- M68kMCCodeEmitter.cpp - Convert M68k code emitter ---*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// This file contains defintions for M68k code emitter.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MCTargetDesc/M68kMCCodeEmitter.h"
|
|
#include "MCTargetDesc/M68kBaseInfo.h"
|
|
#include "MCTargetDesc/M68kFixupKinds.h"
|
|
#include "MCTargetDesc/M68kMCTargetDesc.h"
|
|
|
|
#include "llvm/MC/MCCodeEmitter.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCExpr.h"
|
|
#include "llvm/MC/MCInst.h"
|
|
#include "llvm/MC/MCInstrInfo.h"
|
|
#include "llvm/MC/MCRegisterInfo.h"
|
|
#include "llvm/MC/MCSubtargetInfo.h"
|
|
#include "llvm/MC/MCSymbol.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/EndianStream.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "m68k-mccodeemitter"
|
|
|
|
namespace {
|
|
class M68kMCCodeEmitter : public MCCodeEmitter {
|
|
M68kMCCodeEmitter(const M68kMCCodeEmitter &) = delete;
|
|
void operator=(const M68kMCCodeEmitter &) = delete;
|
|
const MCInstrInfo &MCII;
|
|
MCContext &Ctx;
|
|
|
|
public:
|
|
M68kMCCodeEmitter(const MCInstrInfo &mcii, MCContext &ctx)
|
|
: MCII(mcii), Ctx(ctx) {}
|
|
|
|
~M68kMCCodeEmitter() override {}
|
|
|
|
// TableGen'erated function
|
|
const uint8_t *getGenInstrBeads(const MCInst &MI) const {
|
|
return M68k::getMCInstrBeads(MI.getOpcode());
|
|
}
|
|
|
|
unsigned encodeBits(unsigned ThisByte, uint8_t Bead, const MCInst &MI,
|
|
const MCInstrDesc &Desc, uint64_t &Buffer,
|
|
unsigned Offset, SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const;
|
|
|
|
unsigned encodeReg(unsigned ThisByte, uint8_t Bead, const MCInst &MI,
|
|
const MCInstrDesc &Desc, uint64_t &Buffer, unsigned Offset,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const;
|
|
|
|
unsigned encodeImm(unsigned ThisByte, uint8_t Bead, const MCInst &MI,
|
|
const MCInstrDesc &Desc, uint64_t &Buffer, unsigned Offset,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const;
|
|
|
|
void encodeInstruction(const MCInst &MI, raw_ostream &OS,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const override;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
unsigned M68kMCCodeEmitter::encodeBits(unsigned ThisByte, uint8_t Bead,
|
|
const MCInst &MI,
|
|
const MCInstrDesc &Desc,
|
|
uint64_t &Buffer, unsigned Offset,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const {
|
|
unsigned Num = 0;
|
|
switch (Bead & 0xF) {
|
|
case M68kBeads::Bits1:
|
|
Num = 1;
|
|
break;
|
|
case M68kBeads::Bits2:
|
|
Num = 2;
|
|
break;
|
|
case M68kBeads::Bits3:
|
|
Num = 3;
|
|
break;
|
|
case M68kBeads::Bits4:
|
|
Num = 4;
|
|
break;
|
|
}
|
|
unsigned char Val = (Bead & 0xF0) >> 4;
|
|
|
|
LLVM_DEBUG(dbgs() << "\tEncodeBits"
|
|
<< " Num: " << Num << " Val: 0x");
|
|
LLVM_DEBUG(dbgs().write_hex(Val) << "\n");
|
|
|
|
Buffer |= (Val << Offset);
|
|
|
|
return Num;
|
|
}
|
|
|
|
unsigned M68kMCCodeEmitter::encodeReg(unsigned ThisByte, uint8_t Bead,
|
|
const MCInst &MI, const MCInstrDesc &Desc,
|
|
uint64_t &Buffer, unsigned Offset,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const {
|
|
bool DA, Reg;
|
|
switch (Bead & 0xF) {
|
|
default:
|
|
llvm_unreachable("Unrecognized Bead code for register type");
|
|
case M68kBeads::DAReg:
|
|
Reg = true;
|
|
DA = true;
|
|
break;
|
|
case M68kBeads::DA:
|
|
Reg = false;
|
|
DA = true;
|
|
break;
|
|
case M68kBeads::DReg:
|
|
case M68kBeads::Reg:
|
|
Reg = true;
|
|
DA = false;
|
|
break;
|
|
}
|
|
|
|
unsigned Op = (Bead & 0x70) >> 4;
|
|
bool Alt = (Bead & 0x80);
|
|
LLVM_DEBUG(dbgs() << "\tEncodeReg"
|
|
<< " Op: " << Op << ", DA: " << DA << ", Reg: " << Reg
|
|
<< ", Alt: " << Alt << "\n");
|
|
|
|
auto MIOpIdx = M68k::getLogicalOperandIdx(MI.getOpcode(), Op);
|
|
bool IsPCRel = Desc.OpInfo[MIOpIdx].OperandType == MCOI::OPERAND_PCREL;
|
|
|
|
MCOperand MCO;
|
|
if (M68kII::hasMultiMIOperands(MI.getOpcode(), Op)) {
|
|
if (IsPCRel) {
|
|
assert(Alt &&
|
|
"PCRel addresses use Alt bead register encoding by default");
|
|
MCO = MI.getOperand(MIOpIdx + M68k::PCRelIndex);
|
|
} else {
|
|
MCO = MI.getOperand(MIOpIdx + (Alt ? M68k::MemIndex : M68k::MemBase));
|
|
}
|
|
} else {
|
|
assert(!Alt && "You cannot use Alt register with a simple operand");
|
|
MCO = MI.getOperand(MIOpIdx);
|
|
}
|
|
|
|
unsigned RegNum = MCO.getReg();
|
|
auto RI = Ctx.getRegisterInfo();
|
|
|
|
unsigned Written = 0;
|
|
if (Reg) {
|
|
uint32_t Val = RI->getEncodingValue(RegNum);
|
|
Buffer |= (Val & 7) << Offset;
|
|
Offset += 3;
|
|
Written += 3;
|
|
}
|
|
|
|
if (DA) {
|
|
Buffer |= (uint64_t)M68kII::isAddressRegister(RegNum) << Offset;
|
|
Written++;
|
|
}
|
|
|
|
return Written;
|
|
}
|
|
|
|
static unsigned EmitConstant(uint64_t Val, unsigned Size, unsigned Pad,
|
|
uint64_t &Buffer, unsigned Offset) {
|
|
assert(Size + Offset <= 64 && isUIntN(Size, Val) && "Value does not fit");
|
|
|
|
// Writing Value in host's endianness
|
|
Buffer |= (Val & ((1ULL << Size) - 1)) << Offset;
|
|
return Size + Pad;
|
|
}
|
|
|
|
unsigned M68kMCCodeEmitter::encodeImm(unsigned ThisByte, uint8_t Bead,
|
|
const MCInst &MI, const MCInstrDesc &Desc,
|
|
uint64_t &Buffer, unsigned Offset,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const {
|
|
unsigned ThisWord = ThisByte / 2;
|
|
unsigned Size = 0;
|
|
unsigned Pad = 0;
|
|
unsigned FixOffset = 0;
|
|
int64_t Addendum = 0;
|
|
bool NoExpr = false;
|
|
|
|
unsigned Type = Bead & 0xF;
|
|
unsigned Op = (Bead & 0x70) >> 4;
|
|
bool Alt = (Bead & 0x80);
|
|
|
|
auto MIOpIdx = M68k::getLogicalOperandIdx(MI.getOpcode(), Op);
|
|
bool IsPCRel = Desc.OpInfo[MIOpIdx].OperandType == MCOI::OPERAND_PCREL;
|
|
|
|
// The PC value upon instruction reading of a short jump will point to the
|
|
// next instruction, thus we need to compensate 2 bytes, which is the diff
|
|
// between the patch point and the PC.
|
|
if (IsPCRel && ThisWord == 0)
|
|
Addendum -= 2;
|
|
|
|
switch (Type) {
|
|
// ??? what happens if it is not byte aligned
|
|
// ??? is it even possible
|
|
case M68kBeads::Disp8:
|
|
Size = 8;
|
|
Pad = 0;
|
|
FixOffset = ThisByte + 1;
|
|
Addendum += 1;
|
|
break;
|
|
case M68kBeads::Imm8:
|
|
Size = 8;
|
|
Pad = 8;
|
|
FixOffset = ThisByte;
|
|
break;
|
|
case M68kBeads::Imm16:
|
|
Size = 16;
|
|
Pad = 0;
|
|
FixOffset = ThisByte;
|
|
break;
|
|
case M68kBeads::Imm32:
|
|
Size = 32;
|
|
Pad = 0;
|
|
FixOffset = ThisByte;
|
|
break;
|
|
case M68kBeads::Imm3:
|
|
Size = 3;
|
|
Pad = 0;
|
|
NoExpr = true;
|
|
break;
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << "\tEncodeImm"
|
|
<< " Op: " << Op << ", Size: " << Size << ", Alt: " << Alt
|
|
<< "\n");
|
|
|
|
MCOperand MCO;
|
|
if (M68kII::hasMultiMIOperands(MI.getOpcode(), Op)) {
|
|
|
|
if (IsPCRel) {
|
|
assert(!Alt && "You cannot use ALT operand with PCRel");
|
|
MCO = MI.getOperand(MIOpIdx + M68k::PCRelDisp);
|
|
} else {
|
|
MCO = MI.getOperand(MIOpIdx + (Alt ? M68k::MemOuter : M68k::MemDisp));
|
|
}
|
|
|
|
if (MCO.isExpr()) {
|
|
assert(!NoExpr && "Cannot use expression here");
|
|
const MCExpr *Expr = MCO.getExpr();
|
|
|
|
// This only makes sense for PCRel instructions since PC points to the
|
|
// extension word and Disp8 for example is right justified and requires
|
|
// correction. E.g. R_68K_PC32 is calculated as S + A - P, P for Disp8
|
|
// will be EXTENSION_WORD + 1 thus we need to have A equal to 1 to
|
|
// compensate.
|
|
// TODO count extension words
|
|
if (IsPCRel && Addendum != 0) {
|
|
Expr = MCBinaryExpr::createAdd(
|
|
Expr, MCConstantExpr::create(Addendum, Ctx), Ctx);
|
|
}
|
|
|
|
Fixups.push_back(MCFixup::create(
|
|
FixOffset, Expr, getFixupForSize(Size, IsPCRel), MI.getLoc()));
|
|
// Write zeros
|
|
return EmitConstant(0, Size, Pad, Buffer, Offset);
|
|
}
|
|
|
|
} else {
|
|
MCO = MI.getOperand(MIOpIdx);
|
|
if (MCO.isExpr()) {
|
|
assert(!NoExpr && "Cannot use expression here");
|
|
const MCExpr *Expr = MCO.getExpr();
|
|
|
|
if (Addendum != 0) {
|
|
Expr = MCBinaryExpr::createAdd(
|
|
Expr, MCConstantExpr::create(Addendum, Ctx), Ctx);
|
|
}
|
|
|
|
Fixups.push_back(MCFixup::create(
|
|
FixOffset, Expr, getFixupForSize(Size, IsPCRel), MI.getLoc()));
|
|
// Write zeros
|
|
return EmitConstant(0, Size, Pad, Buffer, Offset);
|
|
}
|
|
}
|
|
|
|
int64_t I = MCO.getImm();
|
|
|
|
// Store 8 as 0, thus making range 1-8
|
|
if (Type == M68kBeads::Imm3 && Alt) {
|
|
assert(I && "Cannot encode Alt Imm3 zero value");
|
|
I %= 8;
|
|
} else {
|
|
assert(isIntN(Size, I));
|
|
}
|
|
|
|
uint64_t Imm = I;
|
|
|
|
// 32 bit Imm requires HI16 first then LO16
|
|
if (Size == 32) {
|
|
Offset += EmitConstant((Imm >> 16) & 0xFFFF, 16, Pad, Buffer, Offset);
|
|
EmitConstant(Imm & 0xFFFF, 16, Pad, Buffer, Offset);
|
|
return Size;
|
|
}
|
|
|
|
return EmitConstant(Imm & ((1ULL << Size) - 1), Size, Pad, Buffer, Offset);
|
|
}
|
|
|
|
#include "M68kGenMCCodeBeads.inc"
|
|
|
|
void M68kMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const {
|
|
unsigned Opcode = MI.getOpcode();
|
|
const MCInstrDesc &Desc = MCII.get(Opcode);
|
|
|
|
LLVM_DEBUG(dbgs() << "EncodeInstruction: " << MCII.getName(Opcode) << "("
|
|
<< Opcode << ")\n");
|
|
|
|
const uint8_t *Beads = getGenInstrBeads(MI);
|
|
if (!Beads || !*Beads) {
|
|
llvm_unreachable("*** Instruction does not have Beads defined");
|
|
}
|
|
|
|
uint64_t Buffer = 0;
|
|
unsigned Offset = 0;
|
|
unsigned ThisByte = 0;
|
|
|
|
for (uint8_t Bead = *Beads; Bead; Bead = *++Beads) {
|
|
// Check for control beads
|
|
if (!(Bead & 0xF)) {
|
|
switch (Bead >> 4) {
|
|
case M68kBeads::Ignore:
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch (Bead & 0xF) {
|
|
default:
|
|
llvm_unreachable("Unknown Bead code");
|
|
break;
|
|
case M68kBeads::Bits1:
|
|
case M68kBeads::Bits2:
|
|
case M68kBeads::Bits3:
|
|
case M68kBeads::Bits4:
|
|
Offset +=
|
|
encodeBits(ThisByte, Bead, MI, Desc, Buffer, Offset, Fixups, STI);
|
|
break;
|
|
case M68kBeads::DAReg:
|
|
case M68kBeads::DA:
|
|
case M68kBeads::DReg:
|
|
case M68kBeads::Reg:
|
|
Offset +=
|
|
encodeReg(ThisByte, Bead, MI, Desc, Buffer, Offset, Fixups, STI);
|
|
break;
|
|
case M68kBeads::Disp8:
|
|
case M68kBeads::Imm8:
|
|
case M68kBeads::Imm16:
|
|
case M68kBeads::Imm32:
|
|
case M68kBeads::Imm3:
|
|
Offset +=
|
|
encodeImm(ThisByte, Bead, MI, Desc, Buffer, Offset, Fixups, STI);
|
|
break;
|
|
}
|
|
|
|
// Since M68k is Big Endian we need to rotate each instruction word
|
|
while (Offset / 16) {
|
|
support::endian::write<uint16_t>(OS, Buffer, support::big);
|
|
Buffer >>= 16;
|
|
Offset -= 16;
|
|
ThisByte += 2;
|
|
}
|
|
}
|
|
|
|
assert(Offset == 0 && "M68k Instructions are % 2 bytes");
|
|
assert((ThisByte && !(ThisByte % 2)) && "M68k Instructions are % 2 bytes");
|
|
}
|
|
|
|
MCCodeEmitter *llvm::createM68kMCCodeEmitter(const MCInstrInfo &MCII,
|
|
const MCRegisterInfo &MRI,
|
|
MCContext &Ctx) {
|
|
return new M68kMCCodeEmitter(MCII, Ctx);
|
|
}
|