| //===- 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/MCDecoderOps.h" | |
| #include "llvm/MC/MCDisassembler/MCDisassembler.h" | |
| #include "llvm/MC/MCInst.h" | |
| #include "llvm/MC/MCInstrInfo.h" | |
| #include "llvm/MC/MCSubtargetInfo.h" | |
| #include "llvm/MC/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<MCInstrInfo const> const MCII; | |
| ARCDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, | |
| MCInstrInfo const *MCII) | |
| : MCDisassembler(STI, Ctx), MCII(MCII) {} | |
| DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, | |
| ArrayRef<uint8_t> Bytes, uint64_t Address, | |
| raw_ostream &CStream) const override; | |
| }; | |
| } // end anonymous namespace | |
| static bool readInstruction32(ArrayRef<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> Bytes, uint64_t Address, | |
| uint64_t &Size, uint32_t &Insn) { | |
| Size = 2; | |
| Insn = (Bytes[0] << 0) | (Bytes[1] << 8); | |
| return true; | |
| } | |
| template <unsigned B> | |
| static DecodeStatus | |
| DecodeSignedOperand(MCInst &Inst, unsigned InsnS, uint64_t Address = 0, | |
| const MCDisassembler *Decoder = nullptr); | |
| template <unsigned B> | |
| static DecodeStatus | |
| DecodeFromCyclicRange(MCInst &Inst, unsigned InsnS, uint64_t Address = 0, | |
| const MCDisassembler *Decoder = nullptr); | |
| template <unsigned B> | |
| static DecodeStatus DecodeBranchTargetS(MCInst &Inst, unsigned InsnS, | |
| uint64_t Address, | |
| const MCDisassembler *Decoder); | |
| static DecodeStatus DecodeMEMrs9(MCInst &, unsigned, uint64_t, | |
| const MCDisassembler *); | |
| static DecodeStatus DecodeLdLImmInstruction(MCInst &, uint64_t, uint64_t, | |
| const MCDisassembler *); | |
| static DecodeStatus DecodeStLImmInstruction(MCInst &, uint64_t, uint64_t, | |
| const MCDisassembler *); | |
| static DecodeStatus DecodeLdRLImmInstruction(MCInst &, uint64_t, uint64_t, | |
| const MCDisassembler *); | |
| static DecodeStatus DecodeSOPwithRS12(MCInst &, uint64_t, uint64_t, | |
| const MCDisassembler *); | |
| static DecodeStatus DecodeSOPwithRU6(MCInst &, uint64_t, uint64_t, | |
| const MCDisassembler *); | |
| static DecodeStatus DecodeCCRU6Instruction(MCInst &, uint64_t, uint64_t, | |
| const MCDisassembler *); | |
| static DecodeStatus DecodeMoveHRegInstruction(MCInst &Inst, uint64_t, uint64_t, | |
| const MCDisassembler *); | |
| 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 MCDisassembler *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 MCDisassembler *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 MCDisassembler *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 MCDisassembler *Decoder) { | |
| static const uint64_t AtLeast = 2; | |
| return (nullptr != Decoder && Decoder->tryAddingSymbolicOperand( | |
| Inst, Value, Address, true, 0, AtLeast, 0)); | |
| } | |
| static void DecodeSymbolicOperandOff(MCInst &Inst, uint64_t Address, | |
| uint64_t Offset, | |
| const MCDisassembler *Decoder) { | |
| uint64_t NextAddress = Address + Offset; | |
| if (!DecodeSymbolicOperand(Inst, Address, NextAddress, Decoder)) | |
| Inst.addOperand(MCOperand::createImm(Offset)); | |
| } | |
| template <unsigned B> | |
| static DecodeStatus DecodeBranchTargetS(MCInst &Inst, unsigned InsnS, | |
| uint64_t Address, | |
| const MCDisassembler *Decoder) { | |
| static_assert(B > 0, "field is empty"); | |
| DecodeSymbolicOperandOff(Inst, Address, SignExtend32<B>(InsnS), Decoder); | |
| return MCDisassembler::Success; | |
| } | |
| template <unsigned B> | |
| static DecodeStatus DecodeSignedOperand(MCInst &Inst, unsigned InsnS, | |
| uint64_t /*Address*/, | |
| const MCDisassembler * /*Decoder*/) { | |
| static_assert(B > 0, "field is empty"); | |
| Inst.addOperand(MCOperand::createImm( | |
| SignExtend32<B>(maskTrailingOnes<decltype(InsnS)>(B) & InsnS))); | |
| return MCDisassembler::Success; | |
| } | |
| template <unsigned B> | |
| static DecodeStatus DecodeFromCyclicRange(MCInst &Inst, unsigned InsnS, | |
| uint64_t /*Address*/, | |
| const MCDisassembler * /*Decoder*/) { | |
| static_assert(B > 0, "field is empty"); | |
| const unsigned max = (1u << B) - 1; | |
| Inst.addOperand( | |
| MCOperand::createImm(InsnS < max ? static_cast<int>(InsnS) : -1)); | |
| return MCDisassembler::Success; | |
| } | |
| static DecodeStatus DecodeStLImmInstruction(MCInst &Inst, uint64_t Insn, | |
| uint64_t Address, | |
| const MCDisassembler *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 MCDisassembler *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 MCDisassembler *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 MCDisassembler *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 MCDisassembler *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, 6); | |
| Inst.addOperand(MCOperand::createImm(U6Field)); | |
| Field CCField = fieldFromInstruction(Insn, 0, 4); | |
| Inst.addOperand(MCOperand::createImm(CCField)); | |
| return MCDisassembler::Success; | |
| } | |
| static DecodeStatus DecodeSOPwithRU6(MCInst &Inst, uint64_t Insn, | |
| uint64_t Address, | |
| const MCDisassembler *Decoder) { | |
| unsigned DstB = decodeBField(Insn); | |
| DecodeGPR32RegisterClass(Inst, DstB, Address, Decoder); | |
| using Field = decltype(Insn); | |
| Field U6 = fieldFromInstruction(Insn, 6, 6); | |
| Inst.addOperand(MCOperand::createImm(U6)); | |
| return MCDisassembler::Success; | |
| } | |
| static DecodeStatus DecodeSOPwithRS12(MCInst &Inst, uint64_t Insn, | |
| uint64_t Address, | |
| const MCDisassembler *Decoder) { | |
| unsigned DstB = decodeBField(Insn); | |
| DecodeGPR32RegisterClass(Inst, DstB, Address, Decoder); | |
| using Field = decltype(Insn); | |
| Field Lower = fieldFromInstruction(Insn, 6, 6); | |
| Field Upper = fieldFromInstruction(Insn, 0, 5); | |
| Field Sign = fieldFromInstruction(Insn, 5, 1) ? -1 : 1; | |
| Field Result = Sign * ((Upper << 6) + Lower); | |
| Inst.addOperand(MCOperand::createImm(Result)); | |
| return MCDisassembler::Success; | |
| } | |
| DecodeStatus ARCDisassembler::getInstruction(MCInst &Instr, uint64_t &Size, | |
| ArrayRef<uint8_t> 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); | |
| } |