diff --git a/llvm/lib/Target/M68k/CMakeLists.txt b/llvm/lib/Target/M68k/CMakeLists.txt index b6d7810d87a36..9511cd8195afd 100644 --- a/llvm/lib/Target/M68k/CMakeLists.txt +++ b/llvm/lib/Target/M68k/CMakeLists.txt @@ -48,3 +48,4 @@ add_llvm_target(M68kCodeGen add_subdirectory(TargetInfo) add_subdirectory(MCTargetDesc) add_subdirectory(AsmParser) +add_subdirectory(Disassembler) diff --git a/llvm/lib/Target/M68k/Disassembler/CMakeLists.txt b/llvm/lib/Target/M68k/Disassembler/CMakeLists.txt new file mode 100644 index 0000000000000..69f9e8be24b87 --- /dev/null +++ b/llvm/lib/Target/M68k/Disassembler/CMakeLists.txt @@ -0,0 +1,12 @@ +add_llvm_component_library(LLVMM68kDisassembler + M68kDisassembler.cpp + + LINK_COMPONENTS + M68kInfo + MCDisassembler + Support + + ADD_TO_COMPONENT + M68k +) + diff --git a/llvm/lib/Target/M68k/Disassembler/M68kDisassembler.cpp b/llvm/lib/Target/M68k/Disassembler/M68kDisassembler.cpp new file mode 100644 index 0000000000000..8045251c30ef8 --- /dev/null +++ b/llvm/lib/Target/M68k/Disassembler/M68kDisassembler.cpp @@ -0,0 +1,606 @@ +//===- M68kDisassembler.cpp - Disassembler for M68k -------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file is part of the M68k Disassembler. +// +//===----------------------------------------------------------------------===// + +#include "M68k.h" +#include "M68kRegisterInfo.h" +#include "M68kSubtarget.h" +#include "MCTargetDesc/M68kMCCodeEmitter.h" +#include "MCTargetDesc/M68kMCTargetDesc.h" +#include "TargetInfo/M68kTargetInfo.h" + +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define DEBUG_TYPE "m68k-disassembler" + +typedef MCDisassembler::DecodeStatus DecodeStatus; + +namespace { +constexpr unsigned MaxInstructionWords = 11; + +class M68kInstructionBuffer { + typedef SmallVector BufferType; + BufferType Buffer; + +public: + M68kInstructionBuffer() {} + + template + M68kInstructionBuffer(TIt Start, TIt End) : Buffer(Start, End) {} + + unsigned size() const { return Buffer.size(); } + + BufferType::const_iterator begin() const { return Buffer.begin(); } + BufferType::const_iterator end() const { return Buffer.end(); } + + uint16_t operator[](unsigned Index) const { + assert((Index < Buffer.size()) && "tried to read out of bounds word"); + return Buffer[Index]; + } + + void truncate(unsigned NewLength) { + assert((NewLength <= Buffer.size()) && + "instruction buffer too short to truncate"); + Buffer.resize(NewLength); + } + + void dump() const; + + static M68kInstructionBuffer fill(ArrayRef Bytes); +}; + +class M68kInstructionReader { + M68kInstructionBuffer Buffer; + unsigned NumRead; + +public: + M68kInstructionReader(M68kInstructionBuffer Buf) : Buffer(Buf), NumRead(0) {} + + unsigned size() const { return (Buffer.size() * 16) - NumRead; } + + uint64_t readBits(unsigned NumBits); +}; + +struct M68kInstructionLookup { + unsigned OpCode; + M68kInstructionBuffer Mask; + M68kInstructionBuffer Value; + + unsigned size() const { return Mask.size(); } + + // Check whether this instruction could possibly match the given bytes. + bool matches(const M68kInstructionBuffer &Test) const; + void dump() const; +}; + +class M68kInstructionLookupBuilder { + std::array Mask; + std::array Value; + unsigned NumWritten; + +public: + M68kInstructionLookupBuilder() : NumWritten(0) { + Mask.fill(0); + Value.fill(0); + } + + unsigned numWords() const { + assert(!(NumWritten & 0xf) && "instructions must be whole words"); + return NumWritten >> 4; + } + + bool isValid() const; + M68kInstructionLookup build(unsigned OpCode); + void addBits(unsigned N, uint64_t Bits); + void skipBits(unsigned N); +}; + +/// A disassembler class for M68k. +class M68kDisassembler : public MCDisassembler { + MCInstrInfo *MCII; + std::vector Lookups; + +public: + M68kDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, + MCInstrInfo *MCII) + : MCDisassembler(STI, Ctx), MCII(MCII) { + buildBeadTable(); + } + virtual ~M68kDisassembler() {} + + void buildBeadTable(); + DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, uint64_t Address, + raw_ostream &CStream) const override; + void decodeReg(MCInst &Instr, unsigned int Bead, + M68kInstructionReader &Reader, unsigned &Scratch) const; + void decodeImm(MCInst &Instr, unsigned int Bead, + M68kInstructionReader &Reader, unsigned &Scratch) const; + unsigned int getRegOperandIndex(MCInst &Instr, unsigned int Bead) const; + unsigned int getImmOperandIndex(MCInst &Instr, unsigned int Bead) const; +}; +} // namespace + +static unsigned RegisterDecode[] = { + M68k::A0, M68k::A1, M68k::A2, M68k::A3, M68k::A4, M68k::A5, + M68k::A6, M68k::SP, M68k::D0, M68k::D1, M68k::D2, M68k::D3, + M68k::D4, M68k::D5, M68k::D6, M68k::D7, +}; + +void M68kInstructionBuffer::dump() const { + for (auto Word : Buffer) { + for (unsigned B = 0; B < 16; ++B) { + uint16_t Bit = (1 << (16 - B - 1)); + unsigned IsClear = !(Word & Bit); + + if (B == 8) + dbgs() << " "; + + char Ch = IsClear ? '0' : '1'; + dbgs() << Ch; + } + + dbgs() << " "; + } + + dbgs() << "\n"; +} + +M68kInstructionBuffer M68kInstructionBuffer::fill(ArrayRef Bytes) { + SmallVector Buffer; + Buffer.resize(std::min(Bytes.size() / 2, Buffer.max_size())); + + for (unsigned I = 0, E = Buffer.size(); I < E; ++I) { + unsigned Offset = I * 2; + uint64_t Hi = Bytes[Offset]; + uint64_t Lo = Bytes[Offset + 1]; + uint64_t Word = (Hi << 8) | Lo; + Buffer[I] = Word; + + LLVM_DEBUG( + errs() << format("Read word %x (%d)\n", (unsigned)Word, Buffer.size())); + } + + return M68kInstructionBuffer(Buffer.begin(), Buffer.end()); +} + +uint64_t M68kInstructionReader::readBits(unsigned NumBits) { + assert((size() >= NumBits) && "not enough bits to read"); + + // We have to read the bits in 16-bit chunks because we read them as + // 16-bit words but they're actually written in big-endian. If a read + // crosses a word boundary we have to be careful. + + uint64_t Value = 0; + unsigned BitsRead = 0; + + while (BitsRead < NumBits) { + unsigned AvailableThisWord = 16 - (NumRead & 0xf); + unsigned ToRead = std::min(NumBits, AvailableThisWord); + + unsigned WordIndex = NumRead >> 4; + uint64_t ThisWord = Buffer[WordIndex] >> (NumRead & 0xf); + uint64_t Mask = (1 << ToRead) - 1; + Value |= (ThisWord & Mask) << BitsRead; + NumRead += ToRead; + BitsRead += ToRead; + } + return Value; +} + +bool M68kInstructionLookup::matches(const M68kInstructionBuffer &Test) const { + if (Test.size() < Value.size()) + return false; + + for (unsigned I = 0, E = Value.size(); I < E; ++I) { + uint16_t Have = Test[I]; + uint16_t Need = Value[I]; + uint16_t WordMask = Mask[I]; + + if ((Have & WordMask) != Need) + return false; + } + + return true; +} + +void M68kInstructionLookup::dump() const { + dbgs() << "M68kInstructionLookup " << OpCode << " "; + + for (unsigned I = 0, E = Mask.size(); I < E; ++I) { + uint16_t WordMask = Mask[I]; + uint16_t WordValue = Value[I]; + + for (unsigned B = 0; B < 16; ++B) { + uint16_t Bit = (1 << (15 - B)); + unsigned IsMasked = !(WordMask & Bit); + unsigned IsClear = !(WordValue & Bit); + + if (B == 8) + dbgs() << " "; + + char Ch = IsMasked ? '?' : (IsClear ? '0' : '1'); + dbgs() << Ch; + } + + dbgs() << " "; + } + + dbgs() << "\n"; +} + +bool M68kInstructionLookupBuilder::isValid() const { + for (unsigned I = 0, E = numWords(); I < E; ++I) + if (Mask[I]) + return true; + + return false; +} + +M68kInstructionLookup M68kInstructionLookupBuilder::build(unsigned OpCode) { + unsigned NumWords = numWords(); + M68kInstructionBuffer MaskBuffer(Mask.begin(), Mask.begin() + NumWords); + M68kInstructionBuffer ValueBuffer(Value.begin(), Value.begin() + NumWords); + M68kInstructionLookup Ret; + Ret.OpCode = OpCode; + Ret.Mask = MaskBuffer; + Ret.Value = ValueBuffer; + return Ret; +} + +void M68kInstructionLookupBuilder::addBits(unsigned N, uint64_t Bits) { + while (N > 0) { + unsigned WordIndex = NumWritten >> 4; + unsigned WordOffset = NumWritten & 0xf; + unsigned AvailableThisWord = 16 - WordOffset; + unsigned ToWrite = std::min(AvailableThisWord, N); + + uint16_t WordMask = (1 << ToWrite) - 1; + uint16_t BitsToWrite = Bits & WordMask; + + Value[WordIndex] |= (BitsToWrite << WordOffset); + Mask[WordIndex] |= (WordMask << WordOffset); + + Bits >>= ToWrite; + N -= ToWrite; + NumWritten += ToWrite; + } +} + +void M68kInstructionLookupBuilder::skipBits(unsigned N) { NumWritten += N; } + +// This is a bit of a hack: we can't generate this table at table-gen time +// because some of the definitions are in our platform. +void M68kDisassembler::buildBeadTable() { + const unsigned NumInstr = M68k::INSTRUCTION_LIST_END; + Lookups.reserve(NumInstr); + + for (unsigned I = 0; I < NumInstr; ++I) { + M68kInstructionLookupBuilder Builder; + + for (const uint8_t *PartPtr = M68k::getMCInstrBeads(I); *PartPtr; + ++PartPtr) { + uint8_t Bead = *PartPtr; + unsigned Ext = Bead >> 4; + unsigned Op = Bead & 0xf; + + switch (Op) { + case M68kBeads::Ctrl: + // Term will have already been skipped by the loop. + assert((Ext == M68kBeads::Ignore) && "unexpected command bead"); + break; + + case M68kBeads::Bits1: + Builder.addBits(1, Ext); + break; + + case M68kBeads::Bits2: + Builder.addBits(2, Ext); + break; + + case M68kBeads::Bits3: + Builder.addBits(3, Ext); + break; + + case M68kBeads::Bits4: + Builder.addBits(4, Ext); + break; + + case M68kBeads::DAReg: + case M68kBeads::DA: + case M68kBeads::DReg: + case M68kBeads::Reg: + if (Op != M68kBeads::DA) + Builder.skipBits(3); + + if (Op != M68kBeads::Reg && Op != M68kBeads::DReg) + Builder.skipBits(1); + + break; + + case M68kBeads::Disp8: + Builder.skipBits(8); + break; + + case M68kBeads::Imm8: + case M68kBeads::Imm16: + Builder.skipBits(16); + break; + + case M68kBeads::Imm32: + Builder.skipBits(32); + break; + + case M68kBeads::Imm3: + Builder.skipBits(3); + break; + + default: + llvm_unreachable("unhandled bead type"); + } + } + + // Ignore instructions which are unmatchable (usually pseudo instructions). + if (!Builder.isValid()) + continue; + + Lookups.push_back(Builder.build(I)); + } +} + +unsigned M68kDisassembler::getRegOperandIndex(MCInst &Instr, + unsigned Bead) const { + unsigned Ext = Bead >> 4; + + const MCInstrDesc &Desc = MCII->get(Instr.getOpcode()); + auto MIOpIdx = M68k::getLogicalOperandIdx(Instr.getOpcode(), Ext & 7); + + if (M68kII::hasMultiMIOperands(Instr.getOpcode(), Ext & 7)) { + bool IsPCRel = Desc.OpInfo[MIOpIdx].OperandType == MCOI::OPERAND_PCREL; + if (IsPCRel) + MIOpIdx += M68k::PCRelIndex; + else if (Ext & 8) + MIOpIdx += M68k::MemIndex; + else + MIOpIdx += M68k::MemBase; + } + + return MIOpIdx; +} + +unsigned M68kDisassembler::getImmOperandIndex(MCInst &Instr, + unsigned Bead) const { + unsigned Ext = Bead >> 4; + + const MCInstrDesc &Desc = MCII->get(Instr.getOpcode()); + auto MIOpIdx = M68k::getLogicalOperandIdx(Instr.getOpcode(), Ext & 7); + + if (M68kII::hasMultiMIOperands(Instr.getOpcode(), Ext & 7)) { + bool IsPCRel = Desc.OpInfo[MIOpIdx].OperandType == MCOI::OPERAND_PCREL; + if (IsPCRel) + MIOpIdx += M68k::PCRelDisp; + else if (Ext & 8) + MIOpIdx += M68k::MemOuter; + else + MIOpIdx += M68k::MemDisp; + } + + return MIOpIdx; +} + +void M68kDisassembler::decodeReg(MCInst &Instr, unsigned Bead, + M68kInstructionReader &Reader, + unsigned &Scratch) const { + unsigned Op = Bead & 0xf; + LLVM_DEBUG(errs() << format("decodeReg %x\n", Bead)); + + if (Op != M68kBeads::DA) + Scratch = (Scratch & ~7) | Reader.readBits(3); + + if (Op != M68kBeads::Reg) { + bool DA = (Op != M68kBeads::DReg) && Reader.readBits(1); + if (!DA) + Scratch |= 8; + else + Scratch &= ~8; + } +} + +void M68kDisassembler::decodeImm(MCInst &Instr, unsigned Bead, + M68kInstructionReader &Reader, + unsigned &Scratch) const { + unsigned Op = Bead & 0xf; + LLVM_DEBUG(errs() << format("decodeImm %x\n", Bead)); + + unsigned NumToRead; + switch (Op) { + case M68kBeads::Disp8: + NumToRead = 8; + break; + case M68kBeads::Imm8: + case M68kBeads::Imm16: + NumToRead = 16; + break; + case M68kBeads::Imm32: + NumToRead = 32; + break; + case M68kBeads::Imm3: + NumToRead = 3; + break; + default: + llvm_unreachable("invalid imm"); + } + + Scratch = (Scratch << NumToRead) | Reader.readBits(NumToRead); +} + +DecodeStatus M68kDisassembler::getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, + uint64_t Address, + raw_ostream &CStream) const { + // Read and shift the input (fetch as much as we can for now). + auto Buffer = M68kInstructionBuffer::fill(Bytes); + if (Buffer.size() == 0) + return Fail; + + // Check through our lookup table. + bool Found = false; + for (unsigned I = 0, E = Lookups.size(); I < E; ++I) { + const M68kInstructionLookup &Lookup = Lookups[I]; + if (!Lookup.matches(Buffer)) + continue; + + Found = true; + Size = Lookup.size() * 2; + Buffer.truncate(Lookup.size()); + Instr.setOpcode(Lookup.OpCode); + LLVM_DEBUG(errs() << "decoding instruction " << MCII->getName(Lookup.OpCode) + << "\n"); + break; + } + + if (!Found) + return Fail; + + M68kInstructionReader Reader(Buffer); + const MCInstrDesc &Desc = MCII->get(Instr.getOpcode()); + unsigned NumOperands = Desc.NumOperands; + + // Now use the beads to decode the operands. + enum class OperandType { + Invalid, + Reg, + Imm, + }; + + SmallVector OpType(NumOperands, OperandType::Invalid); + SmallVector Scratch(NumOperands, 0); + for (const uint8_t *PartPtr = M68k::getMCInstrBeads(Instr.getOpcode()); + *PartPtr; ++PartPtr) { + uint8_t Bead = *PartPtr; + unsigned Ext = Bead >> 4; + unsigned Op = Bead & 0xf; + unsigned MIOpIdx; + + switch (Op) { + case M68kBeads::Ctrl: + // Term will have already been skipped by the loop. + assert((Ext == M68kBeads::Ignore) && "unexpected command bead"); + break; + + // These bits are constant - if we're here we've already matched them. + case M68kBeads::Bits1: + Reader.readBits(1); + break; + case M68kBeads::Bits2: + Reader.readBits(2); + break; + case M68kBeads::Bits3: + Reader.readBits(3); + break; + case M68kBeads::Bits4: + Reader.readBits(4); + break; + + case M68kBeads::DAReg: + case M68kBeads::DA: + case M68kBeads::DReg: + case M68kBeads::Reg: + MIOpIdx = getRegOperandIndex(Instr, Bead); + assert(((OpType[MIOpIdx] == OperandType::Invalid) || + (OpType[MIOpIdx] == OperandType::Reg)) && + "operands cannot change type"); + OpType[MIOpIdx] = OperandType::Reg; + decodeReg(Instr, Bead, Reader, Scratch[MIOpIdx]); + break; + + case M68kBeads::Disp8: + case M68kBeads::Imm8: + case M68kBeads::Imm16: + case M68kBeads::Imm32: + case M68kBeads::Imm3: + MIOpIdx = getImmOperandIndex(Instr, Bead); + assert(((OpType[MIOpIdx] == OperandType::Invalid) || + (OpType[MIOpIdx] == OperandType::Imm)) && + "operands cannot change type"); + OpType[MIOpIdx] = OperandType::Imm; + decodeImm(Instr, Bead, Reader, Scratch[MIOpIdx]); + break; + + default: + llvm_unreachable("unhandled bead type"); + } + } + + // Copy constrained operands. + for (unsigned DstMIOpIdx = 0; DstMIOpIdx < NumOperands; ++DstMIOpIdx) { + int TiedTo = Desc.getOperandConstraint(DstMIOpIdx, MCOI::TIED_TO); + if (TiedTo < 0) + continue; + + unsigned SrcMIOpIdx = TiedTo; + + unsigned OpCount = 0; + for (unsigned I = 0;; ++I) { + unsigned Offset = M68k::getLogicalOperandIdx(Instr.getOpcode(), I); + assert(Offset <= SrcMIOpIdx && "missing logical operand"); + if (Offset == SrcMIOpIdx) { + OpCount = M68k::getLogicalOperandSize(Instr.getOpcode(), I); + break; + } + } + assert(OpCount != 0 && "operand count not found"); + + for (unsigned I = 0; I < OpCount; ++I) { + assert(OpType[DstMIOpIdx + I] == OperandType::Invalid && + "tried to stomp over operand whilst applying constraints"); + OpType[DstMIOpIdx + I] = OpType[SrcMIOpIdx + I]; + Scratch[DstMIOpIdx + I] = Scratch[SrcMIOpIdx + I]; + } + } + + // Create the operands from our scratch space. + for (unsigned O = 0; O < NumOperands; ++O) { + switch (OpType[O]) { + case OperandType::Invalid: + assert(false && "operand not parsed"); + + case OperandType::Imm: + Instr.addOperand(MCOperand::createImm(Scratch[O])); + break; + + case OperandType::Reg: + Instr.addOperand(MCOperand::createReg(RegisterDecode[Scratch[O]])); + break; + } + } + + assert((Reader.size() == 0) && "wrong number of bits consumed"); + return Success; +} + +static MCDisassembler *createM68kDisassembler(const Target &T, + const MCSubtargetInfo &STI, + MCContext &Ctx) { + return new M68kDisassembler(STI, Ctx, T.createMCInstrInfo()); +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeM68kDisassembler() { + // Register the disassembler. + TargetRegistry::RegisterMCDisassembler(getTheM68kTarget(), + createM68kDisassembler); +} diff --git a/llvm/test/MC/Disassembler/M68k/instructions.txt b/llvm/test/MC/Disassembler/M68k/instructions.txt new file mode 100644 index 0000000000000..20fe80315afbe --- /dev/null +++ b/llvm/test/MC/Disassembler/M68k/instructions.txt @@ -0,0 +1,38 @@ +# RUN: llvm-mc -disassemble -triple m68k %s | FileCheck %s + +# CHECK: move.l %a1, %a0 +0x20 0x49 +# CHECK: add.l %a0, %a1 +0xd3 0xc8 +# CHECK: sub.w %d3, %d1 +0x92 0x43 +# CHECK: cmp.w %d1, %d0 +0xb0 0x41 +# CHECK: neg.w %d0 +0x44 0x40 +# CHECK: btst #0, %d3 +0x08 0x03 0x00 0x00 +# CHECK: bra $0 +0x60 0x00 0x00 0x00 +# CHECK: jsr $0 +0x4e 0xb9 0x00 0x00 0x00 0x00 +# CHECK: seq %d0 +0x57 0xc0 +# CHECK: sgt %d0 +0x5e 0xc0 +# CHECK: lea (50,%a0), %a1 +0x43 0xe8 0x00 0x32 +# CHECK: lsl.l #5, %d1 +0xeb 0x89 +# CHECK: lsr.l #5, %d1 +0xea 0x89 +# CHECK: asr.l #5, %d1 +0xea 0x81 +# CHECK: rol.l #5, %d1 +0xeb 0x99 +# CHECK: ror.l #5, %d1 +0xea 0x99 +# CHECK: nop +0x4e 0x71 +# CHECK: rts +0x4e 0x75 diff --git a/llvm/test/MC/Disassembler/M68k/lit.local.cfg b/llvm/test/MC/Disassembler/M68k/lit.local.cfg new file mode 100644 index 0000000000000..a0faa52ae52ba --- /dev/null +++ b/llvm/test/MC/Disassembler/M68k/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'M68k' in config.root.targets: + config.unsupported = True