Skip to content

Commit

Permalink
[RISCV] Add support for disassembly
Browse files Browse the repository at this point in the history
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
  • Loading branch information
asb committed Sep 17, 2017
1 parent 6758ecb commit 8ab4a96
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 1 deletion.
3 changes: 3 additions & 0 deletions llvm/lib/Target/RISCV/CMakeLists.txt
Expand Up @@ -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)

Expand All @@ -13,6 +15,7 @@ add_llvm_target(RISCVCodeGen
)

add_subdirectory(AsmParser)
add_subdirectory(Disassembler)
add_subdirectory(InstPrinter)
add_subdirectory(MCTargetDesc)
add_subdirectory(TargetInfo)
3 changes: 3 additions & 0 deletions llvm/lib/Target/RISCV/Disassembler/CMakeLists.txt
@@ -0,0 +1,3 @@
add_llvm_library(LLVMRISCVDisassembler
RISCVDisassembler.cpp
)
24 changes: 24 additions & 0 deletions 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

135 changes: 135 additions & 0 deletions 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<uint8_t> 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 <unsigned N>
static DecodeStatus decodeUImmOperand(MCInst &Inst, uint64_t Imm,
int64_t Address, const void *Decoder) {
assert(isUInt<N>(Imm) && "Invalid immediate");
Inst.addOperand(MCOperand::createImm(Imm));
return MCDisassembler::Success;
}

template <unsigned N>
static DecodeStatus decodeSImmOperand(MCInst &Inst, uint64_t Imm,
int64_t Address, const void *Decoder) {
assert(isUInt<N>(Imm) && "Invalid immediate");
// Sign-extend the number in the bottom N bits of Imm
Inst.addOperand(MCOperand::createImm(SignExtend64<N>(Imm)));
return MCDisassembler::Success;
}

template <unsigned N>
static DecodeStatus decodeSImmOperandAndLsl1(MCInst &Inst, uint64_t Imm,
int64_t Address,
const void *Decoder) {
assert(isUInt<N>(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<N>(Imm << 1)));
return MCDisassembler::Success;
}

#include "RISCVGenDisassemblerTables.inc"

DecodeStatus RISCVDisassembler::getInstruction(MCInst &MI, uint64_t &Size,
ArrayRef<uint8_t> 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);
}
3 changes: 2 additions & 1 deletion llvm/lib/Target/RISCV/LLVMBuild.txt
Expand Up @@ -16,14 +16,15 @@
;===------------------------------------------------------------------------===;

[common]
subdirectories = AsmParser InstPrinter TargetInfo MCTargetDesc
subdirectories = AsmParser Disassembler InstPrinter TargetInfo MCTargetDesc

[component_0]
type = TargetGroup
name = RISCV
parent = Target
has_asmparser = 1
has_asmprinter = 1
has_disassembler = 1

[component_1]
type = Library
Expand Down
20 changes: 20 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
Expand Up @@ -59,6 +59,9 @@ class RISCVMCCodeEmitter : public MCCodeEmitter {
unsigned getImmOpValueAsr1(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;
unsigned getImmOpValue(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;
};
} // end anonymous namespace

Expand Down Expand Up @@ -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<MCFixup> &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"
4 changes: 4 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
Expand Up @@ -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() {
Expand Down Expand Up @@ -64,5 +67,6 @@ extern "C" void LLVMInitializeRISCVTargetMC() {
TargetRegistry::RegisterMCAsmBackend(*T, createRISCVAsmBackend);
TargetRegistry::RegisterMCCodeEmitter(*T, createRISCVMCCodeEmitter);
TargetRegistry::RegisterMCInstPrinter(*T, createRISCVMCInstPrinter);
TargetRegistry::RegisterMCSubtargetInfo(*T, createRISCVMCSubtargetInfoImpl);
}
}
3 changes: 3 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h
Expand Up @@ -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
5 changes: 5 additions & 0 deletions llvm/lib/Target/RISCV/RISCVInstrFormats.td
Expand Up @@ -28,6 +28,11 @@
class RISCVInst<dag outs, dag ins, string asmstr, list<dag> 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;
Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/Target/RISCV/RISCVInstrInfo.td
Expand Up @@ -36,34 +36,42 @@ def FenceArg : AsmOperandClass {
def fencearg : Operand<i32> {
let ParserMatchClass = FenceArg;
let PrintMethod = "printFenceArg";
let DecoderMethod = "decodeUImmOperand<4>";
}

def uimm5 : Operand<i32> {
let ParserMatchClass = UImmAsmOperand<5>;
let DecoderMethod = "decodeUImmOperand<5>";
}

def simm12 : Operand<i32> {
let ParserMatchClass = SImmAsmOperand<12>;
let DecoderMethod = "decodeSImmOperand<12>";
}

def uimm12 : Operand<i32> {
let ParserMatchClass = UImmAsmOperand<12>;
let DecoderMethod = "decodeUImmOperand<12>";
}

// A 13-bit signed immediate where the least significant bit is zero.
def simm13_lsb0 : Operand<i32> {
let ParserMatchClass = SImmAsmOperand<13, "Lsb0">;
let EncoderMethod = "getImmOpValueAsr1";
let DecoderMethod = "decodeSImmOperandAndLsl1<13>";
}

def uimm20 : Operand<i32> {
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<i32> {
let ParserMatchClass = SImmAsmOperand<21, "Lsb0">;
let EncoderMethod = "getImmOpValueAsr1";
let DecoderMethod = "decodeSImmOperandAndLsl1<21>";
}

// As noted in RISCVRegisterInfo.td, the hope is that support for
Expand Down
4 changes: 4 additions & 0 deletions llvm/test/MC/RISCV/rv32i-valid.s
Expand Up @@ -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]
Expand Down

0 comments on commit 8ab4a96

Please sign in to comment.