forked from OSchip/llvm-project
[M68k][Disassembler] Adopt the new variable length decoder
This is an example usage of D120958. After these patches are landed, we can strip off the codebeads officially. Reviewed By: myhsu Differential Revision: https://reviews.llvm.org/D120960
This commit is contained in:
parent
c644488a8b
commit
cf0b6df6db
|
@ -14,6 +14,7 @@ tablegen(LLVM M68kGenDAGISel.inc -gen-dag-isel)
|
|||
tablegen(LLVM M68kGenCallingConv.inc -gen-callingconv)
|
||||
tablegen(LLVM M68kGenAsmWriter.inc -gen-asm-writer)
|
||||
tablegen(LLVM M68kGenAsmMatcher.inc -gen-asm-matcher)
|
||||
tablegen(LLVM M68kGenDisassemblerTable.inc -gen-disassembler)
|
||||
|
||||
add_public_tablegen_target(M68kCommonTableGen)
|
||||
|
||||
|
|
|
@ -20,584 +20,118 @@
|
|||
#include "llvm/MC/MCAsmInfo.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
|
||||
#include "llvm/MC/MCDecoderOps.h"
|
||||
#include "llvm/MC/MCInst.h"
|
||||
#include "llvm/MC/TargetRegistry.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "m68k-disassembler"
|
||||
|
||||
typedef MCDisassembler::DecodeStatus DecodeStatus;
|
||||
const unsigned MaxInstrWord = 11;
|
||||
|
||||
namespace {
|
||||
constexpr unsigned MaxInstructionWords = 11;
|
||||
|
||||
class M68kInstructionBuffer {
|
||||
typedef SmallVector<uint16_t, MaxInstructionWords> BufferType;
|
||||
BufferType Buffer;
|
||||
|
||||
public:
|
||||
M68kInstructionBuffer() {}
|
||||
|
||||
template <typename TIt>
|
||||
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<uint8_t> Bytes);
|
||||
static const unsigned RegisterDecode[] = {
|
||||
M68k::D0, M68k::D1, M68k::D2, M68k::D3, M68k::D4, M68k::D5,
|
||||
M68k::D6, M68k::D7, M68k::A0, M68k::A1, M68k::A2, M68k::A3,
|
||||
M68k::A4, M68k::A5, M68k::A6, M68k::SP,
|
||||
};
|
||||
|
||||
class M68kInstructionReader {
|
||||
M68kInstructionBuffer Buffer;
|
||||
unsigned NumRead;
|
||||
static DecodeStatus DecodeRegisterClass(MCInst &Inst, uint64_t RegNo,
|
||||
uint64_t Address, const void *Decoder) {
|
||||
if (RegNo >= 16)
|
||||
return DecodeStatus::Fail;
|
||||
Inst.addOperand(MCOperand::createReg(RegisterDecode[RegNo]));
|
||||
return DecodeStatus::Success;
|
||||
}
|
||||
|
||||
public:
|
||||
M68kInstructionReader(M68kInstructionBuffer Buf) : Buffer(Buf), NumRead(0) {}
|
||||
static DecodeStatus DecodeDR32RegisterClass(MCInst &Inst, uint64_t RegNo,
|
||||
uint64_t Address,
|
||||
const void *Decoder) {
|
||||
return DecodeRegisterClass(Inst, RegNo, Address, Decoder);
|
||||
}
|
||||
|
||||
unsigned size() const { return (Buffer.size() * 16) - NumRead; }
|
||||
static DecodeStatus DecodeDR16RegisterClass(MCInst &Inst, uint64_t RegNo,
|
||||
uint64_t Address,
|
||||
const void *Decoder) {
|
||||
return DecodeRegisterClass(Inst, RegNo, Address, Decoder);
|
||||
}
|
||||
|
||||
uint64_t readBits(unsigned NumBits);
|
||||
};
|
||||
static DecodeStatus DecodeDR8RegisterClass(MCInst &Inst, uint64_t RegNo,
|
||||
uint64_t Address,
|
||||
const void *Decoder) {
|
||||
return DecodeRegisterClass(Inst, RegNo, Address, Decoder);
|
||||
}
|
||||
|
||||
struct M68kInstructionLookup {
|
||||
unsigned OpCode;
|
||||
M68kInstructionBuffer Mask;
|
||||
M68kInstructionBuffer Value;
|
||||
static DecodeStatus DecodeAR32RegisterClass(MCInst &Inst, uint64_t RegNo,
|
||||
uint64_t Address,
|
||||
const void *Decoder) {
|
||||
return DecodeRegisterClass(Inst, RegNo | 8ULL, Address, Decoder);
|
||||
}
|
||||
|
||||
unsigned size() const { return Mask.size(); }
|
||||
static DecodeStatus DecodeAR16RegisterClass(MCInst &Inst, uint64_t RegNo,
|
||||
uint64_t Address,
|
||||
const void *Decoder) {
|
||||
return DecodeRegisterClass(Inst, RegNo | 8ULL, Address, Decoder);
|
||||
}
|
||||
|
||||
// Check whether this instruction could possibly match the given bytes.
|
||||
bool matches(const M68kInstructionBuffer &Test) const;
|
||||
void dump() const;
|
||||
};
|
||||
static DecodeStatus DecodeXR32RegisterClass(MCInst &Inst, uint64_t RegNo,
|
||||
uint64_t Address,
|
||||
const void *Decoder) {
|
||||
return DecodeRegisterClass(Inst, RegNo, Address, Decoder);
|
||||
}
|
||||
|
||||
class M68kInstructionLookupBuilder {
|
||||
std::array<uint16_t, MaxInstructionWords> Mask;
|
||||
std::array<uint16_t, MaxInstructionWords> Value;
|
||||
unsigned NumWritten;
|
||||
static DecodeStatus DecodeXR16RegisterClass(MCInst &Inst, uint64_t RegNo,
|
||||
uint64_t Address,
|
||||
const void *Decoder) {
|
||||
return DecodeRegisterClass(Inst, RegNo, Address, Decoder);
|
||||
}
|
||||
|
||||
public:
|
||||
M68kInstructionLookupBuilder() : NumWritten(0) {
|
||||
Mask.fill(0);
|
||||
Value.fill(0);
|
||||
}
|
||||
static DecodeStatus DecodeCCRCRegisterClass(MCInst &Inst, APInt &Insn,
|
||||
uint64_t Address,
|
||||
const void *Decoder) {
|
||||
llvm_unreachable("unimplemented");
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
#include "M68kGenDisassemblerTable.inc"
|
||||
|
||||
/// A disassembler class for M68k.
|
||||
class M68kDisassembler : public MCDisassembler {
|
||||
MCInstrInfo *MCII;
|
||||
std::vector<M68kInstructionLookup> Lookups;
|
||||
|
||||
public:
|
||||
M68kDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx,
|
||||
MCInstrInfo *MCII)
|
||||
: MCDisassembler(STI, Ctx), MCII(MCII) {
|
||||
buildBeadTable();
|
||||
}
|
||||
: MCDisassembler(STI, Ctx), MCII(MCII) {}
|
||||
virtual ~M68kDisassembler() {}
|
||||
|
||||
void buildBeadTable();
|
||||
DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size,
|
||||
ArrayRef<uint8_t> 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,
|
||||
};
|
||||
|
||||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
||||
LLVM_DUMP_METHOD
|
||||
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";
|
||||
}
|
||||
#endif
|
||||
|
||||
M68kInstructionBuffer M68kInstructionBuffer::fill(ArrayRef<uint8_t> Bytes) {
|
||||
SmallVector<uint16_t, MaxInstructionWords> 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;
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
||||
LLVM_DUMP_METHOD
|
||||
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";
|
||||
}
|
||||
#endif
|
||||
|
||||
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 = (NumToRead < 32) ? (Scratch << NumToRead) : 0;
|
||||
Scratch |= Reader.readBits(NumToRead);
|
||||
}
|
||||
|
||||
DecodeStatus M68kDisassembler::getInstruction(MCInst &Instr, uint64_t &Size,
|
||||
ArrayRef<uint8_t> 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,
|
||||
DecodeStatus Result;
|
||||
auto MakeUp = [&](APInt &Insn, unsigned InstrBits) {
|
||||
unsigned Idx = Insn.getBitWidth() >> 3;
|
||||
unsigned RoundUp = alignTo(InstrBits, Align(16));
|
||||
Insn = Insn.zextOrSelf(RoundUp);
|
||||
RoundUp = RoundUp >> 3;
|
||||
for (; Idx < RoundUp; Idx += 2) {
|
||||
Insn.insertBits(support::endian::read16be(&Bytes[Idx]), Idx * 8, 16);
|
||||
}
|
||||
};
|
||||
|
||||
SmallVector<OperandType, 6> OpType(NumOperands, OperandType::Invalid);
|
||||
SmallVector<unsigned, 6> 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;
|
||||
APInt Insn(16, support::endian::read16be(Bytes.data()));
|
||||
Result = decodeInstruction(DecoderTable80, Instr, Insn, Address, this, STI,
|
||||
MakeUp);
|
||||
if (Result == DecodeStatus::Success)
|
||||
Size = InstrLenTable[Instr.getOpcode()] >> 3;
|
||||
return Result;
|
||||
}
|
||||
|
||||
static MCDisassembler *createM68kDisassembler(const Target &T,
|
||||
|
|
|
@ -1,13 +1,131 @@
|
|||
# FIXME: The first two tests are disabled due to decoding conflict
|
||||
# RUN: llvm-mc -disassemble -triple m68k %s | not FileCheck -check-prefix=CHECK-CONFLICT %s
|
||||
# RUN: llvm-mc -disassemble -triple m68k %s | FileCheck %s
|
||||
# Disable this particular test until migration to the new code emitter is
|
||||
# finished.
|
||||
# XFAIL: *
|
||||
|
||||
# CHECK: adda.l %a0, %a1
|
||||
# CHECK-CONFLICT: adda.l %a0, %a1
|
||||
0xd3 0xc8
|
||||
# CHECK: sub.w %d3, %d1
|
||||
# CHECK-CONFLICT: sub.w %d3, %d1
|
||||
0x92 0x43
|
||||
|
||||
# CHECK: add.w (56,%a4,%d3), %d2
|
||||
0xd4 0x74 0x38 0x38
|
||||
|
||||
# CHECK: add.w #35172, %d1
|
||||
0xd2 0x7c 0x89 0x64
|
||||
|
||||
# CHECK: add.w (%a5), %d3
|
||||
0xd6 0x55
|
||||
|
||||
# CHECK: add.w (102,%pc,%a4), %d7
|
||||
0xde 0x7b 0xc8 0x66
|
||||
|
||||
# CHECK: add.w (30600,%a4), %d5
|
||||
0xda 0x6c 0x77 0x88
|
||||
|
||||
# CHECK: add.w (5190,%pc), %d1
|
||||
0xd2 0x7a 0x14 0x46
|
||||
|
||||
# CHECK: add.w %d2, (20,%a6,%d5)
|
||||
0xd5 0x76 0x58 0x14
|
||||
|
||||
# CHECK: add.w #5205, (49,%a3,%d7)
|
||||
0x06 0x73 0x14 0x55 0x78 0x31
|
||||
|
||||
# CHECK: add.w %d2, (%a6)
|
||||
0xd5 0x56
|
||||
|
||||
# CHECK: add.w #5378, (%a6)
|
||||
0x06 0x56 0x15, 0x02
|
||||
|
||||
# CHECK: add.w %d2, (5380,%a2)
|
||||
0xd5 0x6a 0x15 0x04
|
||||
|
||||
# CHECK: adda.l (18,%a4,%d1), %a3
|
||||
0xd7 0xf4 0x18 0x12
|
||||
|
||||
# CHECK: adda.l #50402580, %a1
|
||||
0xd3 0xfc 0x03 0x01 0x15 0x14
|
||||
|
||||
# CHECK: adda.l (%a5), %a1
|
||||
0xd3 0xd5
|
||||
|
||||
# CHECK: adda.l (0,%pc,%sp), %sp
|
||||
0xdf 0xfb 0xf8 0x00
|
||||
|
||||
# CHECK: adda.l (5401,%pc), %a5
|
||||
0xdb 0xfa 0x15 0x19
|
||||
|
||||
# CHECK: adda.l %a5, %a4
|
||||
0xd9 0xcd
|
||||
|
||||
# CHECK: add.l (4660,%sp), %d5
|
||||
0xda 0xaf 0x12 0x34
|
||||
|
||||
# CHECK: cmpi.w #769, $3012022
|
||||
0x0c 0x79 0x03 0x01 0x20 0x22 0x03 0x01
|
||||
|
||||
# CHECK: cmpi.w #5416, %d7
|
||||
0x0c 0x47 0x15 0x28
|
||||
|
||||
# CHECK: cmp.w (69,%a2,%a1), %d3
|
||||
0xb6 0x72 0x98 0x45
|
||||
|
||||
# CHECK: cmpi.l #50402614, (51,%a5,%d7)
|
||||
0x0c 0xb5 0x03 0x01 0x15 0x36 0x78 0x33
|
||||
|
||||
# CHECK: cmpi.l #50403411, $1
|
||||
0x0c 0xb9 0x03 0x01 0x18 0x53 0x00 0x01 0x00 0x00
|
||||
|
||||
# CHECK: cmpi.b #64, $15400301
|
||||
0x0c 0x39 0x00 0x40 0x03 0x01 0x15 0x40
|
||||
|
||||
# CHECK: cmp.b %d5, %d7
|
||||
0xbe 0x05
|
||||
|
||||
# CHECK: cmp.w %d1, %d0
|
||||
0xb0 0x41
|
||||
|
||||
# CHECK: neg.w %d0
|
||||
0x44 0x40
|
||||
|
||||
# CHECK: negx.l %a2
|
||||
0x40 0x8a
|
||||
|
||||
# CHECK: or.w (18,%a4,%a0), %d3
|
||||
0x86 0x74 0x88 0x12
|
||||
|
||||
# CHECK: or.w #4149, %d2
|
||||
0x84 0x7c 0x10 0x35
|
||||
|
||||
# CHECK: or.l (%a3), %d4
|
||||
0x88 0x93
|
||||
|
||||
# CHECK: or.l (55,%pc,%a1), %d6
|
||||
0x8c 0xbb 0x98 0x37
|
||||
|
||||
# CHECK: or.l (38967,%a3), %d6
|
||||
0x8c 0xab 0x98 0x37
|
||||
|
||||
# CHECK: or.l %d6, (48,%a4,%a2)
|
||||
0x8d 0xb4 0xa8 0x30
|
||||
|
||||
# CHECK: or.b %a2, %d7
|
||||
0x8e 0x0a
|
||||
|
||||
# CHECK: or.b (4163,%a1), %d0
|
||||
0x80 0x29 0x10 0x43
|
||||
|
||||
# CHECK: or.b #16, (3,%a3,%d4)
|
||||
0x00 0x33 0x00 0x10 0x48 0x03
|
||||
|
||||
# CHECK: sub.w %d5, (16,%a3,%a2)
|
||||
0x9b 0x73 0xa8 0x10
|
||||
|
||||
# CHECK: suba.l (85,%a4,%a2), %a0
|
||||
0x91 0xf4 0xa8 0x55
|
||||
|
||||
# CHECK: suba.l %d4, %a1
|
||||
0x93 0xc4
|
||||
|
||||
# CHECK: sub.l #16843009, %d3
|
||||
0x96 0xbc 0x01 0x01 0x01 0x01
|
|
@ -1,7 +1,22 @@
|
|||
# RUN: llvm-mc -disassemble -triple m68k %s | FileCheck %s
|
||||
# Disable this particular test until migration to the new code emitter is
|
||||
# finished.
|
||||
# XFAIL: *
|
||||
|
||||
# CHECK: btst #0, %d3
|
||||
0x08 0x03 0x00 0x00
|
||||
# CHECK: btst %d3, %a0
|
||||
0x07 0x08
|
||||
|
||||
# CHECK: btst %d5, -(%a2)
|
||||
0x0b 0x22
|
||||
|
||||
# CHECK: btst #20, -(%a4)
|
||||
0x08 0x24 0x00 0x14
|
||||
|
||||
# CHECK: btst %d5, (79,%sp,%a0)
|
||||
0x0b 0x37 0x88 0x4f
|
||||
|
||||
# CHECK: btst %d4, (%a2)+
|
||||
0x09 0x1a
|
||||
|
||||
# CHECK: btst %d6, (8217,%pc)
|
||||
0x0d 0x3a 0x20 0x19
|
||||
|
||||
# CHECK: btst #20, %d3
|
||||
0x08 0x03 0x00 0x14
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# RUN: llvm-mc -disassemble -triple m68k %s | FileCheck %s
|
||||
# XFAIL: *
|
||||
|
||||
# CHECK: bra $0
|
||||
0x60 0x00 0x00 0x00
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
# RUN: llvm-mc -disassemble -triple m68k %s | FileCheck %s
|
||||
# Disable this particular test until migration to the new code emitter is
|
||||
# finished.
|
||||
# XFAIL: *
|
||||
|
||||
# CHECK: move.l %a1, %a0
|
||||
0x20 0x49
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
# RUN: llvm-mc -disassemble -triple m68k %s | FileCheck %s
|
||||
# Disable this particular test until migration to the new code emitter is
|
||||
# finished.
|
||||
# XFAIL: *
|
||||
|
||||
# CHECK: lsl.l #5, %d1
|
||||
0xeb 0x89
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
; RUN: llvm-mc -triple=m68k -show-encoding %s | FileCheck %s
|
||||
; TODO: We negates the second test since it needs disassembler support
|
||||
; Revert it back when we have that.
|
||||
; RUN: llvm-mc -triple=m68k -filetype=obj < %s | \
|
||||
; RUN: llvm-objdump -d - | not FileCheck --check-prefix=CHECK-OBJ %s
|
||||
; RUN: llvm-objdump -d - | FileCheck --check-prefix=CHECK-OBJ %s
|
||||
|
||||
; CHECK-LABEL: BACKWARD:
|
||||
BACKWARD:
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
; RUN: llvm-mc -triple=m68k -motorola-integers -filetype=obj < %s \
|
||||
; RUN: | llvm-objdump -d - | FileCheck %s
|
||||
; XFAIL: *
|
||||
|
||||
; CHECK-LABEL: <TIGHT>:
|
||||
TIGHT:
|
||||
|
|
Loading…
Reference in New Issue