llvm-project/llvm/lib/Target/M68k/Disassembler/M68kDisassembler.cpp
Sergei Barannikov ed3597e2f7
[TableGen][Decoder] Decode operands with zero width or all bits known (#156358)
There are two classes of operands that DecoderEmitter cannot currently
handle:
1. Operands that do not participate in instruction encoding.
2. Operands whose encoding contains only 1s and 0s.

Because of this, targets developed various workarounds. Some targets
insert missing operands after an instruction has been (incompletely)
decoded, other take into account the missing operands when printing the
instruction. Some targets do neither of that and fail to correctly
disassemble some instructions.

This patch makes it possible to decode both classes of operands and
allows to remove existing workarounds.

For the case of operand with no contribution to instruction encoding,
one should now add `bits<0> OpName` field to instruction encoding
record. This will make DecoderEmitter generate a call to the decoder
function specified by the operand's DecoderMethod. The function has a
signature different from the usual one and looks like this:

```
static DecodeStatus DecodeImm42Operand(MCInst &Inst, const MCDisassembler *Decoder) {
  Inst.addOperand(MCOperand::createImm(42));
  return DecodeStatus::Success;
}
```

Notably, encoding bits are not passed to it (since there are none).

There is nothing special about the second case, the operand bits are
passed as usual. The difference is that before this change, the function
was not called if all the bits of the operand were known (no '?' in the
operand encoding).

There are two options controlling the behavior. Passing an option
enables the old behavior. They exist to allow smooth transition to the
new behavior. They are temporary (yeah, I know) and will be removed once
all targets migrate, possibly giving some more time to downstream
targets.

Subsequent patches in the stack enable the new behavior on some in-tree
targets.
2025-09-04 14:48:36 +00:00

184 lines
7.3 KiB
C++

//===-- 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/MCDecoder.h"
#include "llvm/MC/MCDecoderOps.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ErrorHandling.h"
using namespace llvm;
using namespace llvm::MCD;
#define DEBUG_TYPE "m68k-disassembler"
typedef MCDisassembler::DecodeStatus DecodeStatus;
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, M68k::FP0, M68k::FP1,
M68k::FP2, M68k::FP3, M68k::FP4, M68k::FP5, M68k::FP6, M68k::FP7,
M68k::FPIAR, M68k::FPS, M68k::FPC};
static DecodeStatus DecodeRegisterClass(MCInst &Inst, uint64_t RegNo,
uint64_t Address, const void *Decoder) {
if (RegNo >= 24)
return DecodeStatus::Fail;
Inst.addOperand(MCOperand::createReg(RegisterDecode[RegNo]));
return DecodeStatus::Success;
}
static DecodeStatus DecodeDR32RegisterClass(MCInst &Inst, uint64_t RegNo,
uint64_t Address,
const void *Decoder) {
return DecodeRegisterClass(Inst, RegNo, Address, Decoder);
}
static DecodeStatus DecodeDR16RegisterClass(MCInst &Inst, uint64_t RegNo,
uint64_t Address,
const void *Decoder) {
return DecodeRegisterClass(Inst, RegNo, Address, Decoder);
}
static DecodeStatus DecodeDR8RegisterClass(MCInst &Inst, uint64_t RegNo,
uint64_t Address,
const void *Decoder) {
return DecodeRegisterClass(Inst, RegNo, Address, Decoder);
}
static DecodeStatus DecodeAR32RegisterClass(MCInst &Inst, uint64_t RegNo,
uint64_t Address,
const void *Decoder) {
return DecodeRegisterClass(Inst, RegNo | 8ULL, Address, Decoder);
}
static DecodeStatus DecodeAR16RegisterClass(MCInst &Inst, uint64_t RegNo,
uint64_t Address,
const void *Decoder) {
return DecodeRegisterClass(Inst, RegNo | 8ULL, Address, Decoder);
}
static DecodeStatus DecodeXR32RegisterClass(MCInst &Inst, uint64_t RegNo,
uint64_t Address,
const void *Decoder) {
return DecodeRegisterClass(Inst, RegNo, Address, Decoder);
}
static DecodeStatus DecodeXR16RegisterClass(MCInst &Inst, uint64_t RegNo,
uint64_t Address,
const void *Decoder) {
return DecodeRegisterClass(Inst, RegNo, Address, Decoder);
}
static DecodeStatus DecodeFPDRRegisterClass(MCInst &Inst, uint64_t RegNo,
uint64_t Address,
const void *Decoder) {
return DecodeRegisterClass(Inst, RegNo | 16ULL, Address, Decoder);
}
#define DecodeFPDR32RegisterClass DecodeFPDRRegisterClass
#define DecodeFPDR64RegisterClass DecodeFPDRRegisterClass
#define DecodeFPDR80RegisterClass DecodeFPDRRegisterClass
static DecodeStatus DecodeFPCSCRegisterClass(MCInst &Inst, uint64_t RegNo,
uint64_t Address,
const void *Decoder) {
return DecodeRegisterClass(Inst, (RegNo >> 1) + 24, Address, Decoder);
}
#define DecodeFPICRegisterClass DecodeFPCSCRegisterClass
static DecodeStatus DecodeCCRCRegisterClass(MCInst &Inst,
const MCDisassembler *Decoder) {
Inst.addOperand(MCOperand::createReg(M68k::CCR));
return DecodeStatus::Success;
}
static DecodeStatus DecodeSRCRegisterClass(MCInst &Inst,
const MCDisassembler *Decoder) {
Inst.addOperand(MCOperand::createReg(M68k::SR));
return DecodeStatus::Success;
}
static DecodeStatus DecodeImm32(MCInst &Inst, uint64_t Imm, uint64_t Address,
const void *Decoder) {
Inst.addOperand(MCOperand::createImm(M68k::swapWord<uint32_t>(Imm)));
return DecodeStatus::Success;
}
#include "M68kGenDisassemblerTables.inc"
#undef DecodeFPDR32RegisterClass
#undef DecodeFPDR64RegisterClass
#undef DecodeFPDR80RegisterClass
#undef DecodeFPICRegisterClass
/// A disassembler class for M68k.
struct M68kDisassembler : public MCDisassembler {
M68kDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx)
: MCDisassembler(STI, Ctx) {}
virtual ~M68kDisassembler() {}
DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size,
ArrayRef<uint8_t> Bytes, uint64_t Address,
raw_ostream &CStream) const override;
};
DecodeStatus M68kDisassembler::getInstruction(MCInst &Instr, uint64_t &Size,
ArrayRef<uint8_t> Bytes,
uint64_t Address,
raw_ostream &CStream) const {
DecodeStatus Result;
auto MakeUp = [&](APInt &Insn, unsigned InstrBits) {
unsigned Idx = Insn.getBitWidth() >> 3;
unsigned RoundUp = alignTo(InstrBits, Align(16));
if (RoundUp > Insn.getBitWidth())
Insn = Insn.zext(RoundUp);
RoundUp = RoundUp >> 3;
for (; Idx < RoundUp; Idx += 2) {
Insn.insertBits(support::endian::read16be(&Bytes[Idx]), Idx * 8, 16);
}
};
APInt Insn(16, support::endian::read16be(Bytes.data()));
// 2 bytes of data are consumed, so set Size to 2
// If we don't do this, disassembler may generate result even
// the encoding is invalid. We need to let it fail correctly.
Size = 2;
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,
const MCSubtargetInfo &STI,
MCContext &Ctx) {
return new M68kDisassembler(STI, Ctx);
}
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeM68kDisassembler() {
// Register the disassembler.
TargetRegistry::RegisterMCDisassembler(getTheM68kTarget(),
createM68kDisassembler);
}