From 8ab4a9696a7753c546caff4fd21826ea72370648 Mon Sep 17 00:00:00 2001 From: Alex Bradbury Date: Sun, 17 Sep 2017 14:36:28 +0000 Subject: [PATCH] [RISCV] Add support for disassembly This Disassembly support allows for 'round-trip' testing, and rv32i-valid.s has been updated appropriately. Differential Revision: https://reviews.llvm.org/D23567 llvm-svn: 313486 --- llvm/lib/Target/RISCV/CMakeLists.txt | 3 + .../Target/RISCV/Disassembler/CMakeLists.txt | 3 + .../Target/RISCV/Disassembler/LLVMBuild.txt | 24 ++++ .../RISCV/Disassembler/RISCVDisassembler.cpp | 135 ++++++++++++++++++ llvm/lib/Target/RISCV/LLVMBuild.txt | 3 +- .../RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp | 20 +++ .../RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp | 4 + .../RISCV/MCTargetDesc/RISCVMCTargetDesc.h | 3 + llvm/lib/Target/RISCV/RISCVInstrFormats.td | 5 + llvm/lib/Target/RISCV/RISCVInstrInfo.td | 8 ++ llvm/test/MC/RISCV/rv32i-valid.s | 4 + 11 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 llvm/lib/Target/RISCV/Disassembler/CMakeLists.txt create mode 100644 llvm/lib/Target/RISCV/Disassembler/LLVMBuild.txt create mode 100644 llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp diff --git a/llvm/lib/Target/RISCV/CMakeLists.txt b/llvm/lib/Target/RISCV/CMakeLists.txt index 6cc55fd3a175..b9f3fc110c74 100644 --- a/llvm/lib/Target/RISCV/CMakeLists.txt +++ b/llvm/lib/Target/RISCV/CMakeLists.txt @@ -5,6 +5,8 @@ tablegen(LLVM RISCVGenInstrInfo.inc -gen-instr-info) tablegen(LLVM RISCVGenMCCodeEmitter.inc -gen-emitter) tablegen(LLVM RISCVGenAsmMatcher.inc -gen-asm-matcher) tablegen(LLVM RISCVGenAsmWriter.inc -gen-asm-writer) +tablegen(LLVM RISCVGenSubtargetInfo.inc -gen-subtarget) +tablegen(LLVM RISCVGenDisassemblerTables.inc -gen-disassembler) add_public_tablegen_target(RISCVCommonTableGen) @@ -13,6 +15,7 @@ add_llvm_target(RISCVCodeGen ) add_subdirectory(AsmParser) +add_subdirectory(Disassembler) add_subdirectory(InstPrinter) add_subdirectory(MCTargetDesc) add_subdirectory(TargetInfo) diff --git a/llvm/lib/Target/RISCV/Disassembler/CMakeLists.txt b/llvm/lib/Target/RISCV/Disassembler/CMakeLists.txt new file mode 100644 index 000000000000..39bd2b7669f5 --- /dev/null +++ b/llvm/lib/Target/RISCV/Disassembler/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMRISCVDisassembler + RISCVDisassembler.cpp + ) diff --git a/llvm/lib/Target/RISCV/Disassembler/LLVMBuild.txt b/llvm/lib/Target/RISCV/Disassembler/LLVMBuild.txt new file mode 100644 index 000000000000..340e89d4cf35 --- /dev/null +++ b/llvm/lib/Target/RISCV/Disassembler/LLVMBuild.txt @@ -0,0 +1,24 @@ +;===- ./lib/Target/RISCV/Disassembler/LLVMBuild.txt ------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = RISCVDisassembler +parent = RISCV +required_libraries = MCDisassembler RISCVInfo Support +add_to_library_groups = RISCV + diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp new file mode 100644 index 000000000000..e64d875a567f --- /dev/null +++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp @@ -0,0 +1,135 @@ +//===-- RISCVDisassembler.cpp - Disassembler for RISCV --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the RISCVDisassembler class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/RISCVMCTargetDesc.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/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define DEBUG_TYPE "riscv-disassembler" + +typedef MCDisassembler::DecodeStatus DecodeStatus; + +namespace { +class RISCVDisassembler : public MCDisassembler { + +public: + RISCVDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx) + : MCDisassembler(STI, Ctx) {} + + DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, uint64_t Address, + raw_ostream &VStream, + raw_ostream &CStream) const override; +}; +} // end anonymous namespace + +static MCDisassembler *createRISCVDisassembler(const Target &T, + const MCSubtargetInfo &STI, + MCContext &Ctx) { + return new RISCVDisassembler(STI, Ctx); +} + +extern "C" void LLVMInitializeRISCVDisassembler() { + // Register the disassembler for each target. + TargetRegistry::RegisterMCDisassembler(getTheRISCV32Target(), + createRISCVDisassembler); + TargetRegistry::RegisterMCDisassembler(getTheRISCV64Target(), + createRISCVDisassembler); +} + +static const unsigned GPRDecoderTable[] = { + RISCV::X0_32, RISCV::X1_32, RISCV::X2_32, RISCV::X3_32, + RISCV::X4_32, RISCV::X5_32, RISCV::X6_32, RISCV::X7_32, + RISCV::X8_32, RISCV::X9_32, RISCV::X10_32, RISCV::X11_32, + RISCV::X12_32, RISCV::X13_32, RISCV::X14_32, RISCV::X15_32, + RISCV::X16_32, RISCV::X17_32, RISCV::X18_32, RISCV::X19_32, + RISCV::X20_32, RISCV::X21_32, RISCV::X22_32, RISCV::X23_32, + RISCV::X24_32, RISCV::X25_32, RISCV::X26_32, RISCV::X27_32, + RISCV::X28_32, RISCV::X29_32, RISCV::X30_32, RISCV::X31_32 +}; + +static DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > sizeof(GPRDecoderTable)) { + return MCDisassembler::Fail; + } + + // We must define our own mapping from RegNo to register identifier. + // Accessing index RegNo in the register class will work in the case that + // registers were added in ascending order, but not in general. + unsigned Reg = GPRDecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +template +static DecodeStatus decodeUImmOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + +template +static DecodeStatus decodeSImmOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt(Imm) && "Invalid immediate"); + // Sign-extend the number in the bottom N bits of Imm + Inst.addOperand(MCOperand::createImm(SignExtend64(Imm))); + return MCDisassembler::Success; +} + +template +static DecodeStatus decodeSImmOperandAndLsl1(MCInst &Inst, uint64_t Imm, + int64_t Address, + const void *Decoder) { + assert(isUInt(Imm) && "Invalid immediate"); + // Sign-extend the number in the bottom N bits of Imm after accounting for + // the fact that the N bit immediate is stored in N-1 bits (the LSB is + // always zero) + Inst.addOperand(MCOperand::createImm(SignExtend64(Imm << 1))); + return MCDisassembler::Success; +} + +#include "RISCVGenDisassemblerTables.inc" + +DecodeStatus RISCVDisassembler::getInstruction(MCInst &MI, uint64_t &Size, + ArrayRef Bytes, + uint64_t Address, + raw_ostream &OS, + raw_ostream &CS) const { + // TODO: although assuming 4-byte instructions is sufficient for RV32 and + // RV64, this will need modification when supporting the compressed + // instruction set extension (RVC) which uses 16-bit instructions. Other + // instruction set extensions have the option of defining instructions up to + // 176 bits wide. + Size = 4; + if (Bytes.size() < 4) { + Size = 0; + return MCDisassembler::Fail; + } + + // Get the four bytes of the instruction. + uint32_t Inst = support::endian::read32le(Bytes.data()); + + return decodeInstruction(DecoderTable32, MI, Inst, Address, this, STI); +} diff --git a/llvm/lib/Target/RISCV/LLVMBuild.txt b/llvm/lib/Target/RISCV/LLVMBuild.txt index 933fed1856b4..e15963b5bd7b 100644 --- a/llvm/lib/Target/RISCV/LLVMBuild.txt +++ b/llvm/lib/Target/RISCV/LLVMBuild.txt @@ -16,7 +16,7 @@ ;===------------------------------------------------------------------------===; [common] -subdirectories = AsmParser InstPrinter TargetInfo MCTargetDesc +subdirectories = AsmParser Disassembler InstPrinter TargetInfo MCTargetDesc [component_0] type = TargetGroup @@ -24,6 +24,7 @@ name = RISCV parent = Target has_asmparser = 1 has_asmprinter = 1 +has_disassembler = 1 [component_1] type = Library diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp index eb0beb028ad2..f4eaf06f622d 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -59,6 +59,9 @@ public: unsigned getImmOpValueAsr1(const MCInst &MI, unsigned OpNo, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; + unsigned getImmOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; }; } // end anonymous namespace @@ -105,6 +108,23 @@ RISCVMCCodeEmitter::getImmOpValueAsr1(const MCInst &MI, unsigned OpNo, } llvm_unreachable("Unhandled expression!"); + + return 0; +} + +unsigned RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + + const MCOperand &MO = MI.getOperand(OpNo); + + // If the destination is an immediate, there is nothing to do + if (MO.isImm()) + return MO.getImm(); + + llvm_unreachable("Unhandled expression!"); + + return 0; } #include "RISCVGenMCCodeEmitter.inc" diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp index 0f68dd7aa055..2b35eab577bf 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp @@ -29,6 +29,9 @@ #define GET_REGINFO_MC_DESC #include "RISCVGenRegisterInfo.inc" +#define GET_SUBTARGETINFO_MC_DESC +#include "RISCVGenSubtargetInfo.inc" + using namespace llvm; static MCInstrInfo *createRISCVMCInstrInfo() { @@ -64,5 +67,6 @@ extern "C" void LLVMInitializeRISCVTargetMC() { TargetRegistry::RegisterMCAsmBackend(*T, createRISCVAsmBackend); TargetRegistry::RegisterMCCodeEmitter(*T, createRISCVMCCodeEmitter); TargetRegistry::RegisterMCInstPrinter(*T, createRISCVMCInstPrinter); + TargetRegistry::RegisterMCSubtargetInfo(*T, createRISCVMCSubtargetInfoImpl); } } diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h index 7c98b1c8f321..9891fd52b2f4 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h @@ -55,4 +55,7 @@ MCObjectWriter *createRISCVELFObjectWriter(raw_pwrite_stream &OS, uint8_t OSABI, #define GET_INSTRINFO_ENUM #include "RISCVGenInstrInfo.inc" +#define GET_SUBTARGETINFO_ENUM +#include "RISCVGenSubtargetInfo.inc" + #endif diff --git a/llvm/lib/Target/RISCV/RISCVInstrFormats.td b/llvm/lib/Target/RISCV/RISCVInstrFormats.td index 3fab7122f6f1..d77f3874c7df 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrFormats.td +++ b/llvm/lib/Target/RISCV/RISCVInstrFormats.td @@ -28,6 +28,11 @@ class RISCVInst pattern> : Instruction { field bits<32> Inst; + // SoftFail is a field the disassembler can use to provide a way for + // instructions to not match without killing the whole decode process. It is + // mainly used for ARM, but Tablegen expects this field to exist or it fails + // to build the decode table. + field bits<32> SoftFail = 0; let Size = 4; bits<7> Opcode = 0; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td index 78b1c907f339..e84e4dabb942 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -36,34 +36,42 @@ def FenceArg : AsmOperandClass { def fencearg : Operand { let ParserMatchClass = FenceArg; let PrintMethod = "printFenceArg"; + let DecoderMethod = "decodeUImmOperand<4>"; } def uimm5 : Operand { let ParserMatchClass = UImmAsmOperand<5>; + let DecoderMethod = "decodeUImmOperand<5>"; } def simm12 : Operand { let ParserMatchClass = SImmAsmOperand<12>; + let DecoderMethod = "decodeSImmOperand<12>"; } def uimm12 : Operand { let ParserMatchClass = UImmAsmOperand<12>; + let DecoderMethod = "decodeUImmOperand<12>"; } // A 13-bit signed immediate where the least significant bit is zero. def simm13_lsb0 : Operand { let ParserMatchClass = SImmAsmOperand<13, "Lsb0">; let EncoderMethod = "getImmOpValueAsr1"; + let DecoderMethod = "decodeSImmOperandAndLsl1<13>"; } def uimm20 : Operand { let ParserMatchClass = UImmAsmOperand<20>; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeUImmOperand<20>"; } // A 21-bit signed immediate where the least significant bit is zero. def simm21_lsb0 : Operand { let ParserMatchClass = SImmAsmOperand<21, "Lsb0">; let EncoderMethod = "getImmOpValueAsr1"; + let DecoderMethod = "decodeSImmOperandAndLsl1<21>"; } // As noted in RISCVRegisterInfo.td, the hope is that support for diff --git a/llvm/test/MC/RISCV/rv32i-valid.s b/llvm/test/MC/RISCV/rv32i-valid.s index 4c883e9a0ae1..95d9da4bb51d 100644 --- a/llvm/test/MC/RISCV/rv32i-valid.s +++ b/llvm/test/MC/RISCV/rv32i-valid.s @@ -2,6 +2,10 @@ # RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s # RUN: llvm-mc %s -triple=riscv64 -show-encoding \ # RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s +# RUN: llvm-mc -filetype=obj -triple riscv32 < %s \ +# RUN: | llvm-objdump -d - | FileCheck -check-prefix=CHECK-INST %s +# RUN: llvm-mc -filetype=obj -triple riscv64 < %s \ +# RUN: | llvm-objdump -d - | FileCheck -check-prefix=CHECK-INST %s # CHECK-INST: lui a0, 2 # CHECK: encoding: [0x37,0x25,0x00,0x00]