//===- ARCDisassembler.cpp - Disassembler for ARC ---------------*- 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 is part of the ARC Disassembler. /// //===----------------------------------------------------------------------===// #include "ARC.h" #include "ARCRegisterInfo.h" #include "MCTargetDesc/ARCMCTargetDesc.h" #include "TargetInfo/ARCTargetInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/MC/MCFixedLenDisassembler.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/TargetRegistry.h" using namespace llvm; #define DEBUG_TYPE "arc-disassembler" using DecodeStatus = MCDisassembler::DecodeStatus; namespace { /// A disassembler class for ARC. class ARCDisassembler : public MCDisassembler { public: std::unique_ptr const MCII; ARCDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, MCInstrInfo const *MCII) : MCDisassembler(STI, Ctx), MCII(MCII) {} DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, ArrayRef Bytes, uint64_t Address, raw_ostream &CStream) const override; }; } // end anonymous namespace static bool readInstruction32(ArrayRef Bytes, uint64_t Address, uint64_t &Size, uint32_t &Insn) { Size = 4; // Read 2 16-bit values, but swap hi/lo parts. Insn = (Bytes[0] << 16) | (Bytes[1] << 24) | (Bytes[2] << 0) | (Bytes[3] << 8); return true; } static bool readInstruction64(ArrayRef Bytes, uint64_t Address, uint64_t &Size, uint64_t &Insn) { Size = 8; Insn = ((uint64_t)Bytes[0] << 16) | ((uint64_t)Bytes[1] << 24) | ((uint64_t)Bytes[2] << 0) | ((uint64_t)Bytes[3] << 8) | ((uint64_t)Bytes[4] << 48) | ((uint64_t)Bytes[5] << 56) | ((uint64_t)Bytes[6] << 32) | ((uint64_t)Bytes[7] << 40); return true; } static bool readInstruction48(ArrayRef Bytes, uint64_t Address, uint64_t &Size, uint64_t &Insn) { Size = 6; Insn = ((uint64_t)Bytes[0] << 0) | ((uint64_t)Bytes[1] << 8) | ((uint64_t)Bytes[2] << 32) | ((uint64_t)Bytes[3] << 40) | ((uint64_t)Bytes[4] << 16) | ((uint64_t)Bytes[5] << 24); return true; } static bool readInstruction16(ArrayRef Bytes, uint64_t Address, uint64_t &Size, uint32_t &Insn) { Size = 2; Insn = (Bytes[0] << 0) | (Bytes[1] << 8); return true; } template static DecodeStatus DecodeSignedOperand(MCInst &Inst, unsigned InsnS, uint64_t Address = 0, const void *Decoder = nullptr); template static DecodeStatus DecodeFromCyclicRange(MCInst &Inst, unsigned InsnS, uint64_t Address = 0, const void *Decoder = nullptr); template static DecodeStatus DecodeBranchTargetS(MCInst &Inst, unsigned InsnS, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMEMrs9(MCInst &, unsigned, uint64_t, const void *); static DecodeStatus DecodeLdLImmInstruction(MCInst &, uint64_t, uint64_t, const void *); static DecodeStatus DecodeStLImmInstruction(MCInst &, uint64_t, uint64_t, const void *); static DecodeStatus DecodeLdRLImmInstruction(MCInst &, uint64_t, uint64_t, const void *); static DecodeStatus DecodeCCRU6Instruction(MCInst &, uint64_t, uint64_t, const void *); static DecodeStatus DecodeMoveHRegInstruction(MCInst &Inst, uint64_t, uint64_t, const void *); static const uint16_t GPR32DecoderTable[] = { ARC::R0, ARC::R1, ARC::R2, ARC::R3, ARC::R4, ARC::R5, ARC::R6, ARC::R7, ARC::R8, ARC::R9, ARC::R10, ARC::R11, ARC::R12, ARC::R13, ARC::R14, ARC::R15, ARC::R16, ARC::R17, ARC::R18, ARC::R19, ARC::R20, ARC::R21, ARC::R22, ARC::R23, ARC::R24, ARC::R25, ARC::GP, ARC::FP, ARC::SP, ARC::ILINK, ARC::R30, ARC::BLINK}; static DecodeStatus DecodeGPR32RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo >= 32) { LLVM_DEBUG(dbgs() << "Not a GPR32 register."); return MCDisassembler::Fail; } unsigned Reg = GPR32DecoderTable[RegNo]; Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeGBR32ShortRegister(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { // Enumerates registers from ranges [r0-r3],[r12-r15]. if (RegNo > 3) RegNo += 8; // 4 for r12, etc... return DecodeGPR32RegisterClass(Inst, RegNo, Address, Decoder); } #include "ARCGenDisassemblerTables.inc" static unsigned decodeCField(unsigned Insn) { return fieldFromInstruction(Insn, 6, 6); } static unsigned decodeBField(unsigned Insn) { return (fieldFromInstruction(Insn, 12, 3) << 3) | fieldFromInstruction(Insn, 24, 3); } static unsigned decodeAField(unsigned Insn) { return fieldFromInstruction(Insn, 0, 6); } static DecodeStatus DecodeMEMrs9(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Dec) { // We have the 9-bit immediate in the low bits, 6-bit register in high bits. unsigned S9 = Insn & 0x1ff; unsigned R = (Insn & (0x7fff & ~0x1ff)) >> 9; DecodeGPR32RegisterClass(Inst, R, Address, Dec); Inst.addOperand(MCOperand::createImm(SignExtend32<9>(S9))); return MCDisassembler::Success; } static bool DecodeSymbolicOperand(MCInst &Inst, uint64_t Address, uint64_t Value, const void *Decoder) { static const uint64_t AtLeast = 2; // TODO: Try to force emitter to use MCDisassembler* instead of void*. auto Disassembler = static_cast(Decoder); return (nullptr != Disassembler && Disassembler->tryAddingSymbolicOperand(Inst, Value, Address, true, 0, AtLeast)); } static void DecodeSymbolicOperandOff(MCInst &Inst, uint64_t Address, uint64_t Offset, const void *Decoder) { uint64_t NextAddress = Address + Offset; if (!DecodeSymbolicOperand(Inst, Address, NextAddress, Decoder)) Inst.addOperand(MCOperand::createImm(Offset)); } template static DecodeStatus DecodeBranchTargetS(MCInst &Inst, unsigned InsnS, uint64_t Address, const void *Decoder) { static_assert(B > 0, "field is empty"); DecodeSymbolicOperandOff(Inst, Address, SignExtend32(InsnS), Decoder); return MCDisassembler::Success; } template static DecodeStatus DecodeSignedOperand(MCInst &Inst, unsigned InsnS, uint64_t /*Address*/, const void * /*Decoder*/) { static_assert(B > 0, "field is empty"); Inst.addOperand(MCOperand::createImm( SignExtend32(maskTrailingOnes(B) & InsnS))); return MCDisassembler::Success; } template static DecodeStatus DecodeFromCyclicRange(MCInst &Inst, unsigned InsnS, uint64_t /*Address*/, const void * /*Decoder*/) { static_assert(B > 0, "field is empty"); const unsigned max = (1u << B) - 1; Inst.addOperand( MCOperand::createImm(InsnS < max ? static_cast(InsnS) : -1)); return MCDisassembler::Success; } static DecodeStatus DecodeStLImmInstruction(MCInst &Inst, uint64_t Insn, uint64_t Address, const void *Decoder) { unsigned SrcC, DstB, LImm; DstB = decodeBField(Insn); if (DstB != 62) { LLVM_DEBUG(dbgs() << "Decoding StLImm found non-limm register."); return MCDisassembler::Fail; } SrcC = decodeCField(Insn); DecodeGPR32RegisterClass(Inst, SrcC, Address, Decoder); LImm = (Insn >> 32); Inst.addOperand(MCOperand::createImm(LImm)); Inst.addOperand(MCOperand::createImm(0)); return MCDisassembler::Success; } static DecodeStatus DecodeLdLImmInstruction(MCInst &Inst, uint64_t Insn, uint64_t Address, const void *Decoder) { unsigned DstA, SrcB, LImm; LLVM_DEBUG(dbgs() << "Decoding LdLImm:\n"); SrcB = decodeBField(Insn); if (SrcB != 62) { LLVM_DEBUG(dbgs() << "Decoding LdLImm found non-limm register."); return MCDisassembler::Fail; } DstA = decodeAField(Insn); DecodeGPR32RegisterClass(Inst, DstA, Address, Decoder); LImm = (Insn >> 32); Inst.addOperand(MCOperand::createImm(LImm)); Inst.addOperand(MCOperand::createImm(0)); return MCDisassembler::Success; } static DecodeStatus DecodeLdRLImmInstruction(MCInst &Inst, uint64_t Insn, uint64_t Address, const void *Decoder) { unsigned DstA, SrcB; LLVM_DEBUG(dbgs() << "Decoding LdRLimm\n"); DstA = decodeAField(Insn); DecodeGPR32RegisterClass(Inst, DstA, Address, Decoder); SrcB = decodeBField(Insn); DecodeGPR32RegisterClass(Inst, SrcB, Address, Decoder); if (decodeCField(Insn) != 62) { LLVM_DEBUG(dbgs() << "Decoding LdRLimm found non-limm register."); return MCDisassembler::Fail; } Inst.addOperand(MCOperand::createImm((uint32_t)(Insn >> 32))); return MCDisassembler::Success; } static DecodeStatus DecodeMoveHRegInstruction(MCInst &Inst, uint64_t Insn, uint64_t Address, const void *Decoder) { LLVM_DEBUG(dbgs() << "Decoding MOV_S h-register\n"); using Field = decltype(Insn); Field H = fieldFromInstruction(Insn, 5, 3) | (fieldFromInstruction(Insn, 0, 2) << 3); Field G = fieldFromInstruction(Insn, 8, 3) | (fieldFromInstruction(Insn, 3, 2) << 3); auto DecodeRegisterOrImm = [&Inst, Address, Decoder](Field RegNum, Field Value) { if (30 == RegNum) { Inst.addOperand(MCOperand::createImm(Value)); return MCDisassembler::Success; } return DecodeGPR32RegisterClass(Inst, RegNum, Address, Decoder); }; if (MCDisassembler::Success != DecodeRegisterOrImm(G, 0)) return MCDisassembler::Fail; return DecodeRegisterOrImm(H, Insn >> 16u); } static DecodeStatus DecodeCCRU6Instruction(MCInst &Inst, uint64_t Insn, uint64_t Address, const void *Decoder) { unsigned DstB; LLVM_DEBUG(dbgs() << "Decoding CCRU6 instruction:\n"); DstB = decodeBField(Insn); DecodeGPR32RegisterClass(Inst, DstB, Address, Decoder); using Field = decltype(Insn); Field U6Field = fieldFromInstruction(Insn, 6, 11); Inst.addOperand(MCOperand::createImm(U6Field)); Field CCField = fieldFromInstruction(Insn, 0, 4); Inst.addOperand(MCOperand::createImm(CCField)); return MCDisassembler::Success; } DecodeStatus ARCDisassembler::getInstruction(MCInst &Instr, uint64_t &Size, ArrayRef Bytes, uint64_t Address, raw_ostream &cStream) const { MCDisassembler::DecodeStatus Result; if (Bytes.size() < 2) { Size = 0; return Fail; } uint8_t DecodeByte = (Bytes[1] & 0xF7) >> 3; // 0x00 -> 0x07 are 32-bit instructions. // 0x08 -> 0x1F are 16-bit instructions. if (DecodeByte < 0x08) { // 32-bit instruction. if (Bytes.size() < 4) { // Did we decode garbage? Size = 0; return Fail; } if (Bytes.size() >= 8) { // Attempt to decode 64-bit instruction. uint64_t Insn64; if (!readInstruction64(Bytes, Address, Size, Insn64)) return Fail; Result = decodeInstruction(DecoderTable64, Instr, Insn64, Address, this, STI); if (Success == Result) { LLVM_DEBUG(dbgs() << "Successfully decoded 64-bit instruction."); return Result; } LLVM_DEBUG(dbgs() << "Not a 64-bit instruction, falling back to 32-bit."); } uint32_t Insn32; if (!readInstruction32(Bytes, Address, Size, Insn32)) { return Fail; } // Calling the auto-generated decoder function. return decodeInstruction(DecoderTable32, Instr, Insn32, Address, this, STI); } else { if (Bytes.size() >= 6) { // Attempt to treat as instr. with limm data. uint64_t Insn48; if (!readInstruction48(Bytes, Address, Size, Insn48)) return Fail; Result = decodeInstruction(DecoderTable48, Instr, Insn48, Address, this, STI); if (Success == Result) { LLVM_DEBUG( dbgs() << "Successfully decoded 16-bit instruction with limm."); return Result; } LLVM_DEBUG( dbgs() << "Not a 16-bit instruction with limm, try without it."); } uint32_t Insn16; if (!readInstruction16(Bytes, Address, Size, Insn16)) return Fail; // Calling the auto-generated decoder function. return decodeInstruction(DecoderTable16, Instr, Insn16, Address, this, STI); } } static MCDisassembler *createARCDisassembler(const Target &T, const MCSubtargetInfo &STI, MCContext &Ctx) { return new ARCDisassembler(STI, Ctx, T.createMCInstrInfo()); } extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeARCDisassembler() { // Register the disassembler. TargetRegistry::RegisterMCDisassembler(getTheARCTarget(), createARCDisassembler); }