blob: 9708abaadf981ef6418b317ed34e8c7eeaaf144b [file] [log] [blame]
//===-- 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);
}