blob: 08ceed45949a7b148d470b79f6480a53bedf7d56 [file]
// Copyright 2025 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
//! Code for parsing and formatting RISC-V instructions.
//
//! Based on the RISC-V specification. See
//! https://github.com/riscv/riscv-isa-manual/ for more information.
pub mod call_graph;
mod compression;
mod elf_mem;
use core::fmt::{Debug, Display};
use bitfield_struct::bitfield;
pub use elf_mem::ElfMem;
/// Risc-V instruction with address
#[derive(Clone, Copy)]
pub struct InstrA {
pub addr: u32,
pub instr: Instr,
}
impl InstrA {
#[must_use]
pub fn new(addr: u32, instr: Instr) -> Self {
Self { addr, instr }
}
/// Returns true if this instruction is a 16-bit compressed instruction.
#[must_use]
pub fn is_compressed(&self) -> bool {
self.instr.is_compressed()
}
/// Decodes the instruction
#[must_use]
pub fn decode(&self) -> DecodedInstr {
self.instr.decode()
}
/// If the instruction contains a relative address, this function will
/// return the absolute address (relative to self.addr).
#[must_use]
pub fn absolute_imm(&self) -> u32 {
match self.instr.decode().ty() {
InstrType::R(_) => self.addr,
InstrType::I(i) => self.addr.wrapping_add_signed(i.imm()),
InstrType::IS(i) => self.addr.wrapping_add_signed(i.imm()),
InstrType::IB(_) => self.addr,
InstrType::S(i) => self.addr.wrapping_add_signed(i.imm()),
InstrType::B(i) => self.addr.wrapping_add_signed(i.imm()),
InstrType::U(i) => self.addr.wrapping_add_signed(i.imm()),
InstrType::J(i) => self.addr.wrapping_add_signed(i.imm()),
InstrType::Const(_) => self.addr,
InstrType::Unknown(_) => self.addr,
}
}
}
impl Display for InstrA {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:08x} {}", self.addr, self.instr.decode())
}
}
impl Debug for InstrA {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"InstrA{{addr: 0x{:08x}, \"{}\"}}",
self.addr,
self.instr.decode()
)
}
}
/// A raw risc-v instruction. If this instruction is 16-bit, the upper half-word
/// is ignored.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct Instr(u32);
impl Instr {
/// Decodes the instruction
#[inline(always)]
#[must_use]
pub fn decode(self) -> DecodedInstr {
if self.is_compressed() {
// The instr is stored in the lower 16-bits.
let instr: u16 = (self.0 & 0xffff) as u16;
match compression::decompress_instr(instr) {
Some(instr) => instr.decode32(),
None => DecodedInstr::Unknown(instr.into()),
}
} else {
self.decode32()
}
}
#[inline(always)]
fn decode32(self) -> DecodedInstr {
match Op7::from_instr(self) {
Op7::LUI => return DecodedInstr::Lui(self.0.into()),
Op7::AUIPC => return DecodedInstr::Auipc(self.0.into()),
Op7::JAL => return DecodedInstr::Jal(self.0.into()),
_ => {}
}
match Op10::from_instr(self) {
Op10::JALR => return DecodedInstr::Jalr(self.0.into()),
Op10::BEQ => return DecodedInstr::Beq(self.0.into()),
Op10::BNE => return DecodedInstr::Bne(self.0.into()),
Op10::BLT => return DecodedInstr::Blt(self.0.into()),
Op10::BGE => return DecodedInstr::Bge(self.0.into()),
Op10::BLTU => return DecodedInstr::Bltu(self.0.into()),
Op10::BGEU => return DecodedInstr::Bgeu(self.0.into()),
Op10::LOAD_B => return DecodedInstr::Lb(self.0.into()),
Op10::LOAD_H => return DecodedInstr::Lh(self.0.into()),
Op10::LOAD_W => return DecodedInstr::Lw(self.0.into()),
Op10::LOAD_BU => return DecodedInstr::Lbu(self.0.into()),
Op10::LOAD_HU => return DecodedInstr::Lhu(self.0.into()),
Op10::STORE_B => return DecodedInstr::Sb(self.0.into()),
Op10::STORE_H => return DecodedInstr::Sh(self.0.into()),
Op10::STORE_W => return DecodedInstr::Sw(self.0.into()),
Op10::ADDI => return DecodedInstr::Addi(self.0.into()),
Op10::SLTI => return DecodedInstr::Slti(self.0.into()),
Op10::SLTIU => return DecodedInstr::Sltiu(self.0.into()),
Op10::XORI => return DecodedInstr::Xori(self.0.into()),
Op10::ORI => return DecodedInstr::Ori(self.0.into()),
Op10::ANDI => return DecodedInstr::Andi(self.0.into()),
_ => {}
}
match Op17::from_instr(self) {
Op17::SLLI => return DecodedInstr::Slli(self.0.into()),
Op17::SRLI => return DecodedInstr::Srli(self.0.into()),
Op17::SRAI => return DecodedInstr::Srai(self.0.into()),
Op17::ADD => return DecodedInstr::Add(self.0.into()),
Op17::SUB => return DecodedInstr::Sub(self.0.into()),
Op17::SLL => return DecodedInstr::Sll(self.0.into()),
Op17::SLT => return DecodedInstr::Slt(self.0.into()),
Op17::SLTU => return DecodedInstr::Sltu(self.0.into()),
Op17::XOR => return DecodedInstr::Xor(self.0.into()),
Op17::SRL => return DecodedInstr::Srl(self.0.into()),
Op17::SRA => return DecodedInstr::Sra(self.0.into()),
Op17::OR => return DecodedInstr::Or(self.0.into()),
Op17::AND => return DecodedInstr::And(self.0.into()),
Op17::MUL => return DecodedInstr::Mul(self.0.into()),
Op17::MULH => return DecodedInstr::Mulh(self.0.into()),
Op17::MULHSU => return DecodedInstr::Mulhsu(self.0.into()),
Op17::MULHU => return DecodedInstr::Mulhu(self.0.into()),
Op17::DIV => return DecodedInstr::Div(self.0.into()),
Op17::DIVU => return DecodedInstr::Divu(self.0.into()),
Op17::REM => return DecodedInstr::Rem(self.0.into()),
Op17::REMU => return DecodedInstr::Remu(self.0.into()),
Op17::ANDN => return DecodedInstr::Andn(self.0.into()),
Op17::BCLR => return DecodedInstr::Bclr(self.0.into()),
Op17::BEXT => return DecodedInstr::Bext(self.0.into()),
Op17::BINV => return DecodedInstr::Binv(self.0.into()),
Op17::BSET => return DecodedInstr::Bset(self.0.into()),
Op17::CLMUL => return DecodedInstr::Clmul(self.0.into()),
Op17::CLMULH => return DecodedInstr::Clmulh(self.0.into()),
Op17::CLMULR => return DecodedInstr::Clmulr(self.0.into()),
Op17::MAX => return DecodedInstr::Max(self.0.into()),
Op17::MAXU => return DecodedInstr::Maxu(self.0.into()),
Op17::MIN => return DecodedInstr::Min(self.0.into()),
Op17::MINU => return DecodedInstr::Minu(self.0.into()),
Op17::ORN => return DecodedInstr::Orn(self.0.into()),
Op17::ROL => return DecodedInstr::Rol(self.0.into()),
Op17::ROR => return DecodedInstr::Ror(self.0.into()),
Op17::SH1ADD => return DecodedInstr::Sh1add(self.0.into()),
Op17::SH2ADD => return DecodedInstr::Sh2add(self.0.into()),
Op17::SH3ADD => return DecodedInstr::Sh3add(self.0.into()),
Op17::XNOR => return DecodedInstr::Xnor(self.0.into()),
Op17::BCLRI => return DecodedInstr::Bclri(self.0.into()),
Op17::BEXTI => return DecodedInstr::Bexti(self.0.into()),
Op17::BINVI => return DecodedInstr::Binvi(self.0.into()),
Op17::BSETI => return DecodedInstr::Bseti(self.0.into()),
Op17::RORI => return DecodedInstr::Rori(self.0.into()),
_ => {}
}
match Op22::from_instr(self) {
Op22::CLZ => return DecodedInstr::Clz(self.0.into()),
Op22::CPOP => return DecodedInstr::Cpop(self.0.into()),
Op22::CTZ => return DecodedInstr::Ctz(self.0.into()),
Op22::ORCB => return DecodedInstr::OrcB(self.0.into()),
Op22::REV8 => return DecodedInstr::Rev8(self.0.into()),
Op22::SEXTB => return DecodedInstr::SextB(self.0.into()),
Op22::SEXTH => return DecodedInstr::SextH(self.0.into()),
Op22::ZEXT_H => return DecodedInstr::ZextH(self.0.into()),
_ => {}
}
DecodedInstr::Unknown(self.0)
}
/// Returns true if this instruction is a 16-bit compressed instruction.
#[must_use]
pub fn is_compressed(self) -> bool {
(self.0 & OP_MASK_32) != OP_MASK_32
}
}
impl From<u32> for Instr {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<u16> for Instr {
fn from(value: u16) -> Self {
Self(value.into())
}
}
const fn sign_extend_i16(val: u16, msb: u16) -> i16 {
let sh = 15 - msb;
((val << sh).cast_signed()) >> sh
}
const fn sign_extend_i32(val: u32, msb: u32) -> i32 {
let sh = 31 - msb;
((val << sh).cast_signed()) >> sh
}
const OP_MASK_32: u32 = 0x3;
#[derive(Debug, Eq, PartialEq)]
struct Opcode(pub u32);
impl Opcode {
#![allow(unused)]
const LOAD: Self = Self(OP_MASK_32);
const LOAD_FP: Self = Self((1 << 2) | OP_MASK_32);
const CUSTOM_0: Self = Self((2 << 2) | OP_MASK_32);
const MISC_MEM: Self = Self((3 << 2) | OP_MASK_32);
const OP_IMM: Self = Self((4 << 2) | OP_MASK_32);
const AUIPC: Self = Self((5 << 2) | OP_MASK_32);
const IMM_32: Self = Self((6 << 2) | OP_MASK_32);
const STORE: Self = Self((8 << 2) | OP_MASK_32);
const STORE_FP: Self = Self((9 << 2) | OP_MASK_32);
const CUSTOM_1: Self = Self((10 << 2) | OP_MASK_32);
const AMO: Self = Self((11 << 2) | OP_MASK_32);
const OP: Self = Self((12 << 2) | OP_MASK_32);
const LUI: Self = Self((13 << 2) | OP_MASK_32);
const OP_32: Self = Self((14 << 2) | OP_MASK_32);
const MADD: Self = Self((16 << 2) | OP_MASK_32);
const MSUB: Self = Self((17 << 2) | OP_MASK_32);
const NMSUB: Self = Self((18 << 2) | OP_MASK_32);
const NMADD: Self = Self((19 << 2) | OP_MASK_32);
const OP_FP: Self = Self((20 << 2) | OP_MASK_32);
const BRANCH: Self = Self((24 << 2) | OP_MASK_32);
const JALR: Self = Self((25 << 2) | OP_MASK_32);
const JAL: Self = Self((27 << 2) | OP_MASK_32);
const SYSTEM: Self = Self((28 << 2) | OP_MASK_32);
pub const fn from_bits(val: u32) -> Self {
Self(val & 0x7f)
}
pub const fn into_bits(self) -> u32 {
self.0
}
pub const fn funct3(self, funct3: u32) -> u32 {
((funct3 & 0x7) << 12) | (self.0 & 0x7f)
}
pub const fn funct10(self, funct10: u32) -> u32 {
((funct10 >> 3) << 25) | ((funct10 & 0x7) << 12) | (self.0 & 0x7f)
}
pub const fn funct15(self, funct15: u32) -> u32 {
((funct15 >> 3) << 20) | ((funct15 & 0x7) << 12) | (self.0 & 0x7f)
}
}
/// An identifier for instructions that don't have any additional constant bits
/// (funct3, etc) beyond opcode.
#[derive(Eq, PartialEq)]
struct Op7(pub u32);
impl Op7 {
pub const fn from_instr(instr: Instr) -> Self {
Self(instr.0 & 0b0111_1111)
}
const LUI: Self = Self(Opcode::LUI.0);
const AUIPC: Self = Self(Opcode::AUIPC.0);
const JAL: Self = Self(Opcode::JAL.0);
}
/// The identifier for instructions that contain funct3 constant bits.
#[derive(Eq, PartialEq)]
struct Op10(pub u32);
impl Op10 {
pub const fn from_instr(instr: Instr) -> Op10 {
Self(instr.0 & 0b111_0000_0111_1111)
}
pub const JALR: Self = Self(Opcode::JALR.funct3(0));
pub const BEQ: Self = Self(Opcode::BRANCH.funct3(FUNCT3_BRANCH_EQ));
pub const BNE: Self = Self(Opcode::BRANCH.funct3(FUNCT3_BRANCH_NE));
pub const BLT: Self = Self(Opcode::BRANCH.funct3(FUNCT3_BRANCH_LT));
pub const BGE: Self = Self(Opcode::BRANCH.funct3(FUNCT3_BRANCH_GE));
pub const BLTU: Self = Self(Opcode::BRANCH.funct3(FUNCT3_BRANCH_LTU));
pub const BGEU: Self = Self(Opcode::BRANCH.funct3(FUNCT3_BRANCH_GEU));
pub const LOAD_B: Self = Self(Opcode::LOAD.funct3(FUNCT3_LOAD_B));
pub const LOAD_H: Self = Self(Opcode::LOAD.funct3(FUNCT3_LOAD_H));
pub const LOAD_W: Self = Self(Opcode::LOAD.funct3(FUNCT3_LOAD_W));
pub const LOAD_BU: Self = Self(Opcode::LOAD.funct3(FUNCT3_LOAD_BU));
pub const LOAD_HU: Self = Self(Opcode::LOAD.funct3(FUNCT3_LOAD_HU));
pub const STORE_B: Self = Self(Opcode::STORE.funct3(FUNCT3_STORE_B));
pub const STORE_H: Self = Self(Opcode::STORE.funct3(FUNCT3_STORE_H));
pub const STORE_W: Self = Self(Opcode::STORE.funct3(FUNCT3_STORE_W));
pub const ADDI: Self = Self(Opcode::OP_IMM.funct3(FUNCT3_OP_ADDI));
pub const SLTI: Self = Self(Opcode::OP_IMM.funct3(FUNCT3_OP_SLTI));
pub const SLTIU: Self = Self(Opcode::OP_IMM.funct3(FUNCT3_OP_SLTIU));
pub const XORI: Self = Self(Opcode::OP_IMM.funct3(FUNCT3_OP_XORI));
pub const ORI: Self = Self(Opcode::OP_IMM.funct3(FUNCT3_OP_ORI));
pub const ANDI: Self = Self(Opcode::OP_IMM.funct3(FUNCT3_OP_ANDI));
}
/// The identifier for instructions that contain funct3 and funct7 constant
/// bits.
#[derive(Eq, PartialEq)]
struct Op17(pub u32);
impl Op17 {
#![allow(clippy::unusual_byte_groupings)]
pub const fn from_instr(instr: Instr) -> Self {
Self(instr.0 & 0b1111_1110_0000_0000_0111_0000_0111_1111)
}
pub const SLLI: Self = Self(Opcode::OP_IMM.funct10(FUNCT10_SLLI));
pub const SRLI: Self = Self(Opcode::OP_IMM.funct10(FUNCT10_SRLI));
pub const SRAI: Self = Self(Opcode::OP_IMM.funct10(FUNCT10_SRAI));
pub const BCLRI: Self = Self(Opcode::OP_IMM.funct10(0b0100100_001));
pub const BEXTI: Self = Self(Opcode::OP_IMM.funct10(0b0100100_101));
pub const BINVI: Self = Self(Opcode::OP_IMM.funct10(0b0110100_001));
pub const BSETI: Self = Self(Opcode::OP_IMM.funct10(0b0010100_001));
pub const RORI: Self = Self(Opcode::OP_IMM.funct10(0b0110000_101));
pub const ADD: Self = Self(Opcode::OP.funct10(FUNCT10_ADD));
pub const SUB: Self = Self(Opcode::OP.funct10(FUNCT10_SUB));
pub const SLL: Self = Self(Opcode::OP.funct10(FUNCT10_SLL));
pub const SLT: Self = Self(Opcode::OP.funct10(FUNCT10_SLT));
pub const SLTU: Self = Self(Opcode::OP.funct10(FUNCT10_SLTU));
pub const XOR: Self = Self(Opcode::OP.funct10(FUNCT10_XOR));
pub const SRL: Self = Self(Opcode::OP.funct10(FUNCT10_SRL));
pub const SRA: Self = Self(Opcode::OP.funct10(FUNCT10_SRA));
pub const OR: Self = Self(Opcode::OP.funct10(FUNCT10_OR));
pub const AND: Self = Self(Opcode::OP.funct10(FUNCT10_AND));
pub const MUL: Self = Self(Opcode::OP.funct10(FUNCT10_MUL));
pub const MULH: Self = Self(Opcode::OP.funct10(FUNCT10_MULH));
pub const MULHSU: Self = Self(Opcode::OP.funct10(FUNCT10_MULHSU));
pub const MULHU: Self = Self(Opcode::OP.funct10(FUNCT10_MULHU));
pub const DIV: Self = Self(Opcode::OP.funct10(FUNCT10_DIV));
pub const DIVU: Self = Self(Opcode::OP.funct10(FUNCT10_DIVU));
pub const REM: Self = Self(Opcode::OP.funct10(FUNCT10_REM));
pub const REMU: Self = Self(Opcode::OP.funct10(FUNCT10_REMU));
pub const ANDN: Self = Self(Opcode::OP.funct10(0b0100000_111));
pub const BCLR: Self = Self(Opcode::OP.funct10(0b0100100_001));
pub const BEXT: Self = Self(Opcode::OP.funct10(0b0100100_101));
pub const BINV: Self = Self(Opcode::OP.funct10(0b0110100_001));
pub const BSET: Self = Self(Opcode::OP.funct10(0b0010100_001));
pub const CLMUL: Self = Self(Opcode::OP.funct10(0b0000101_001));
pub const CLMULH: Self = Self(Opcode::OP.funct10(0b0000101_011));
pub const CLMULR: Self = Self(Opcode::OP.funct10(0b0000101_010));
pub const MAX: Self = Self(Opcode::OP.funct10(0b0000101_110));
pub const MAXU: Self = Self(Opcode::OP.funct10(0b0000101_111));
pub const MIN: Self = Self(Opcode::OP.funct10(0b0000101_100));
pub const MINU: Self = Self(Opcode::OP.funct10(0b0000101_101));
pub const ORN: Self = Self(Opcode::OP.funct10(0b0100000_110));
pub const ROL: Self = Self(Opcode::OP.funct10(0b0110000_001));
pub const ROR: Self = Self(Opcode::OP.funct10(0b0110000_101));
pub const SH1ADD: Self = Self(Opcode::OP.funct10(0b0010000_010));
pub const SH2ADD: Self = Self(Opcode::OP.funct10(0b0010000_100));
pub const SH3ADD: Self = Self(Opcode::OP.funct10(0b0010000_110));
pub const XNOR: Self = Self(Opcode::OP.funct10(0b0100000_100));
}
/// The identifier for instructions that contain 22 constant bits.
#[derive(Eq, PartialEq)]
struct Op22(pub u32);
impl Op22 {
#![allow(clippy::unusual_byte_groupings)]
pub const fn from_instr(instr: Instr) -> Self {
Self(instr.0 & 0b1111_1111_1111_0000_0111_0000_0111_1111)
}
pub const CLZ: Self = Self(Opcode::OP_IMM.funct15(0b0110000_00000_001));
pub const CPOP: Self = Self(Opcode::OP_IMM.funct15(0b0110000_00010_001));
pub const CTZ: Self = Self(Opcode::OP_IMM.funct15(0b0110000_00001_001));
pub const ORCB: Self = Self(Opcode::OP_IMM.funct15(0b0010100_00111_101));
pub const REV8: Self = Self(Opcode::OP_IMM.funct15(0b0110100_11000_101));
pub const SEXTB: Self = Self(Opcode::OP_IMM.funct15(0b0110000_00100_001));
pub const SEXTH: Self = Self(Opcode::OP_IMM.funct15(0b0110000_00101_001));
pub const ZEXT_H: Self = Self(Opcode::OP.funct15(0b0000100_00000_100));
}
#[allow(unused)]
pub enum InstrType {
R(Instr32R),
I(Instr32I),
IS(Instr32IS),
IB(Instr32IB),
S(Instr32S),
B(Instr32B),
U(Instr32U),
J(Instr32J),
Const(u32),
Unknown(u32),
}
impl InstrType {
#[must_use]
pub fn rd(&self) -> Option<Reg> {
match self {
Self::R(i) => Some(i.rd()),
Self::I(i) => Some(i.rd()),
Self::IS(i) => Some(i.rd()),
Self::IB(i) => Some(i.rd()),
Self::S(_) => None,
Self::B(_) => None,
Self::U(i) => Some(i.rd()),
Self::J(i) => Some(i.rd()),
Self::Const(_) => None,
Self::Unknown(_) => None,
}
}
}
#[derive(Clone, Copy)]
pub enum DecodedInstr {
Unknown(u32),
Lui(Instr32U),
Auipc(Instr32U),
Jal(Instr32J),
Jalr(Instr32I),
Beq(Instr32B),
Bne(Instr32B),
Blt(Instr32B),
Bge(Instr32B),
Bltu(Instr32B),
Bgeu(Instr32B),
Lb(Instr32I),
Lh(Instr32I),
Lw(Instr32I),
Lbu(Instr32I),
Lhu(Instr32I),
Sb(Instr32S),
Sh(Instr32S),
Sw(Instr32S),
Addi(Instr32I),
Slti(Instr32I),
Sltiu(Instr32I),
Xori(Instr32I),
Ori(Instr32I),
Andi(Instr32I),
Slli(Instr32IS),
Srli(Instr32IS),
Srai(Instr32IS),
Bclri(Instr32IS),
Bexti(Instr32IS),
Binvi(Instr32IS),
Bseti(Instr32IS),
Rori(Instr32IS),
Clz(Instr32IB),
Cpop(Instr32IB),
Ctz(Instr32IB),
OrcB(Instr32IB),
Rev8(Instr32IB),
SextB(Instr32IB),
SextH(Instr32IB),
ZextH(Instr32IB),
Add(Instr32R),
Sub(Instr32R),
Sll(Instr32R),
Slt(Instr32R),
Sltu(Instr32R),
Xor(Instr32R),
Srl(Instr32R),
Sra(Instr32R),
Or(Instr32R),
And(Instr32R),
Mul(Instr32R),
Mulh(Instr32R),
Mulhsu(Instr32R),
Mulhu(Instr32R),
Div(Instr32R),
Divu(Instr32R),
Rem(Instr32R),
Remu(Instr32R),
Andn(Instr32R),
Bclr(Instr32R),
Bext(Instr32R),
Binv(Instr32R),
Bset(Instr32R),
Clmul(Instr32R),
Clmulh(Instr32R),
Clmulr(Instr32R),
Max(Instr32R),
Maxu(Instr32R),
Min(Instr32R),
Minu(Instr32R),
Orn(Instr32R),
Rol(Instr32R),
Ror(Instr32R),
Sh1add(Instr32R),
Sh2add(Instr32R),
Sh3add(Instr32R),
Xnor(Instr32R),
#[allow(dead_code)]
FenceTso,
#[allow(dead_code)]
Pause,
#[allow(dead_code)]
Ecall,
#[allow(dead_code)]
Ebreak,
}
impl DecodedInstr {
#[must_use]
pub fn ty(&self) -> InstrType {
#![allow(clippy::unusual_byte_groupings)]
match self {
Self::Lui(i) => InstrType::U(*i),
Self::Auipc(i) => InstrType::U(*i),
Self::Jal(i) => InstrType::J(*i),
Self::Jalr(i) => InstrType::I(*i),
Self::Beq(i) => InstrType::B(*i),
Self::Bne(i) => InstrType::B(*i),
Self::Blt(i) => InstrType::B(*i),
Self::Bge(i) => InstrType::B(*i),
Self::Bltu(i) => InstrType::B(*i),
Self::Bgeu(i) => InstrType::B(*i),
Self::Lb(i) => InstrType::I(*i),
Self::Lh(i) => InstrType::I(*i),
Self::Lw(i) => InstrType::I(*i),
Self::Lbu(i) => InstrType::I(*i),
Self::Lhu(i) => InstrType::I(*i),
Self::Sb(i) => InstrType::S(*i),
Self::Sh(i) => InstrType::S(*i),
Self::Sw(i) => InstrType::S(*i),
Self::Addi(i) => InstrType::I(*i),
Self::Slti(i) => InstrType::I(*i),
Self::Sltiu(i) => InstrType::I(*i),
Self::Xori(i) => InstrType::I(*i),
Self::Ori(i) => InstrType::I(*i),
Self::Andi(i) => InstrType::I(*i),
Self::Slli(i) => InstrType::IS(*i),
Self::Srli(i) => InstrType::IS(*i),
Self::Srai(i) => InstrType::IS(*i),
Self::Bclri(i) => InstrType::IS(*i),
Self::Bexti(i) => InstrType::IS(*i),
Self::Binvi(i) => InstrType::IS(*i),
Self::Bseti(i) => InstrType::IS(*i),
Self::Rori(i) => InstrType::IS(*i),
Self::Clz(i) => InstrType::IB(*i),
Self::Cpop(i) => InstrType::IB(*i),
Self::Ctz(i) => InstrType::IB(*i),
Self::OrcB(i) => InstrType::IB(*i),
Self::Rev8(i) => InstrType::IB(*i),
Self::SextB(i) => InstrType::IB(*i),
Self::SextH(i) => InstrType::IB(*i),
Self::ZextH(i) => InstrType::IB(*i),
Self::Add(i) => InstrType::R(*i),
Self::Sub(i) => InstrType::R(*i),
Self::Sll(i) => InstrType::R(*i),
Self::Slt(i) => InstrType::R(*i),
Self::Sltu(i) => InstrType::R(*i),
Self::Xor(i) => InstrType::R(*i),
Self::Srl(i) => InstrType::R(*i),
Self::Sra(i) => InstrType::R(*i),
Self::Or(i) => InstrType::R(*i),
Self::And(i) => InstrType::R(*i),
Self::Mul(i) => InstrType::R(*i),
Self::Mulh(i) => InstrType::R(*i),
Self::Mulhsu(i) => InstrType::R(*i),
Self::Mulhu(i) => InstrType::R(*i),
Self::Div(i) => InstrType::R(*i),
Self::Divu(i) => InstrType::R(*i),
Self::Rem(i) => InstrType::R(*i),
Self::Remu(i) => InstrType::R(*i),
Self::Andn(i) => InstrType::R(*i),
Self::Bclr(i) => InstrType::R(*i),
Self::Bext(i) => InstrType::R(*i),
Self::Binv(i) => InstrType::R(*i),
Self::Bset(i) => InstrType::R(*i),
Self::Clmul(i) => InstrType::R(*i),
Self::Clmulh(i) => InstrType::R(*i),
Self::Clmulr(i) => InstrType::R(*i),
Self::Max(i) => InstrType::R(*i),
Self::Maxu(i) => InstrType::R(*i),
Self::Min(i) => InstrType::R(*i),
Self::Minu(i) => InstrType::R(*i),
Self::Orn(i) => InstrType::R(*i),
Self::Rol(i) => InstrType::R(*i),
Self::Ror(i) => InstrType::R(*i),
Self::Sh1add(i) => InstrType::R(*i),
Self::Sh2add(i) => InstrType::R(*i),
Self::Sh3add(i) => InstrType::R(*i),
Self::Xnor(i) => InstrType::R(*i),
Self::FenceTso => InstrType::Const(0b1000_0011_0011_00000_000_00000_0001111),
Self::Pause => InstrType::Const(0b0000_0001_0000_00000_000_00000_0001111),
Self::Ecall => InstrType::Const(0b000000000000_00000_000_00000_1110011),
Self::Ebreak => InstrType::Const(0b000000000001_00000_000_00000_1110011),
Self::Unknown(i) => InstrType::Unknown(*i),
}
}
#[must_use]
pub fn mnemonic(&self) -> &'static str {
match self {
Self::Lui(_) => "lui",
Self::Auipc(_) => "auipc",
Self::Jal(_) => "jal",
Self::Jalr(_) => "jalr",
Self::Beq(_) => "beq",
Self::Bne(_) => "bne",
Self::Blt(_) => "blt",
Self::Bge(_) => "bge",
Self::Bltu(_) => "bltu",
Self::Bgeu(_) => "bgeu",
Self::Lb(_) => "lb",
Self::Lh(_) => "lh",
Self::Lw(_) => "lw",
Self::Lbu(_) => "lbu",
Self::Lhu(_) => "lhu",
Self::Sb(_) => "sb",
Self::Sh(_) => "sh",
Self::Sw(_) => "sw",
Self::Addi(_) => "addi",
Self::Slti(_) => "slti",
Self::Sltiu(_) => "sltiu",
Self::Xori(_) => "xori",
Self::Ori(_) => "ori",
Self::Andi(_) => "andi",
Self::Slli(_) => "slli",
Self::Srli(_) => "srli",
Self::Srai(_) => "srai",
Self::Bclri(_) => "bclri",
Self::Bexti(_) => "bexti",
Self::Binvi(_) => "binvi",
Self::Bseti(_) => "bseti",
Self::Rori(_) => "rori",
Self::Clz(_) => "clz",
Self::Cpop(_) => "cpop",
Self::Ctz(_) => "ctz",
Self::OrcB(_) => "orc.b",
Self::Rev8(_) => "rev8",
Self::SextB(_) => "sext.b",
Self::SextH(_) => "sext.h",
Self::ZextH(_) => "zext.h",
Self::Add(_) => "add",
Self::Sub(_) => "sub",
Self::Sll(_) => "sll",
Self::Slt(_) => "slt",
Self::Sltu(_) => "sltu",
Self::Xor(_) => "xor",
Self::Srl(_) => "srl",
Self::Sra(_) => "sra",
Self::Or(_) => "or",
Self::And(_) => "and",
Self::Mul(_) => "mul",
Self::Mulh(_) => "mulh",
Self::Mulhsu(_) => "mulhsu",
Self::Mulhu(_) => "mulhu",
Self::Div(_) => "div",
Self::Divu(_) => "divu",
Self::Rem(_) => "rem",
Self::Remu(_) => "remu",
Self::Andn(_) => "andn",
Self::Bclr(_) => "bclr",
Self::Bext(_) => "bext",
Self::Binv(_) => "binv",
Self::Bset(_) => "bset",
Self::Clmul(_) => "clmul",
Self::Clmulh(_) => "clmulh",
Self::Clmulr(_) => "clmulr",
Self::Max(_) => "max",
Self::Maxu(_) => "maxu",
Self::Min(_) => "min",
Self::Minu(_) => "minu",
Self::Orn(_) => "orn",
Self::Rol(_) => "rol",
Self::Ror(_) => "ror",
Self::Sh1add(_) => "sh1add",
Self::Sh2add(_) => "sh2add",
Self::Sh3add(_) => "sh3add",
Self::Xnor(_) => "xnor",
Self::FenceTso => "fence_tso",
Self::Pause => "pause",
Self::Ecall => "ecall",
Self::Ebreak => "ebreak",
Self::Unknown(_) => "",
}
}
}
impl Display for DecodedInstr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self.ty() {
InstrType::B(i) => write!(f, "{:<8} {}", self.mnemonic(), i),
InstrType::I(i) => write!(f, "{:<8} {}", self.mnemonic(), i),
InstrType::IB(i) => write!(f, "{:<8} {}", self.mnemonic(), i),
InstrType::IS(i) => write!(f, "{:<8} {}", self.mnemonic(), i),
InstrType::S(i) => write!(f, "{:<8} {}", self.mnemonic(), i),
InstrType::J(i) => write!(f, "{:<8} {}", self.mnemonic(), i),
InstrType::R(i) => write!(f, "{:<8} {}", self.mnemonic(), i),
InstrType::U(i) => write!(f, "{:<8} {}", self.mnemonic(), i),
InstrType::Const(_) => write!(f, "{:<8}", self.mnemonic()),
InstrType::Unknown(i) => write!(f, "0x{i:x}"),
}
}
}
#[bitfield(u32)]
pub struct Instr32R {
#[bits(7)]
pub opcode: Opcode,
#[bits(5)]
pub rd: Reg,
#[bits(3)]
pub funct10_lower: u32,
#[bits(5)]
pub rs1: Reg,
#[bits(5)]
pub rs2: Reg,
#[bits(7)]
pub funct10_upper: u32,
}
impl Instr32R {
#[must_use]
pub fn funct10(&self) -> u32 {
(self.funct10_upper() << 3) | self.funct10_lower()
}
pub fn set_funct10(&mut self, val: u32) {
self.set_funct10_lower(val & 0x7);
self.set_funct10_upper(val >> 3)
}
}
impl Display for Instr32R {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{},{},{}", self.rd(), self.rs1(), self.rs2())
}
}
#[bitfield(u32)]
pub struct Instr32I {
#[bits(7)]
pub opcode: Opcode,
#[bits(5)]
pub rd: Reg,
#[bits(3)]
pub funct3: u32,
#[bits(5)]
pub rs1: Reg,
#[bits(12)]
pub uimm: u32,
}
impl Instr32I {
#[must_use]
pub fn imm(&self) -> i32 {
sign_extend_i32(self.uimm(), 11)
}
pub fn set_imm(&mut self, val: i32) {
self.set_uimm(val.cast_unsigned() & 0xfff);
}
}
impl Display for Instr32I {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self.opcode() == Opcode::LOAD {
write!(f, "{},{}({})", self.rd(), self.imm(), self.rs1())
} else {
write!(f, "{},{},{}", self.rd(), self.rs1(), self.imm())
}
}
}
#[bitfield(u32)]
pub struct Instr32IS {
#[bits(7)]
opcode: Opcode,
#[bits(5)]
rd: Reg,
#[bits(3)]
funct3: u32,
#[bits(5)]
rs1: Reg,
#[bits(5)]
uimm: u32,
#[bits(7)]
funct7: u32,
}
impl Instr32IS {
#[must_use]
pub const fn funct10(&self) -> u32 {
(self.funct7() << 3) | self.funct3()
}
pub fn set_funct10(&mut self, val: u32) {
self.set_funct3(val & 0x7);
self.set_funct7(val >> 3)
}
#[must_use]
pub fn imm(&self) -> i32 {
sign_extend_i32(self.uimm(), 4)
}
}
impl Display for Instr32IS {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{},{},{}", self.rd(), self.rs1(), self.uimm())
}
}
#[bitfield(u32)]
pub struct Instr32IB {
#[bits(7)]
opcode: Opcode,
#[bits(5)]
rd: Reg,
#[bits(3)]
funct3: u32,
#[bits(5)]
rs1: Reg,
#[bits(12)]
funct12: u32,
}
impl Instr32IB {
#[must_use]
pub const fn funct15(&self) -> u32 {
(self.funct12() << 3) | self.funct3()
}
pub fn set_funct15(&mut self, val: u32) {
self.set_funct3(val & 0x7);
self.set_funct12(val >> 3)
}
}
impl Display for Instr32IB {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{},{}", self.rd(), self.rs1())
}
}
#[bitfield(u32)]
pub struct Instr32S {
#[bits(7)]
pub opcode: Opcode,
#[bits(5)]
pub imm0: u32,
#[bits(3)]
pub funct3: u32,
#[bits(5)]
pub rs1: Reg,
#[bits(5)]
pub rs2: Reg,
#[bits(7)]
pub imm5: u32,
}
impl Instr32S {
#[must_use]
pub const fn uimm(&self) -> u32 {
(self.imm5() << 5) | self.imm0()
}
pub fn set_uimm(&mut self, val: u32) {
self.set_imm0(val & 0x1f);
self.set_imm5(val >> 5)
}
#[must_use]
pub fn imm(&self) -> i32 {
sign_extend_i32(self.uimm(), 11)
}
pub fn set_imm(&mut self, val: i32) {
self.set_uimm(val.cast_unsigned() & 0xfff);
}
}
impl Display for Instr32S {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{},{}({})", self.rs2(), self.imm(), self.rs1())
}
}
#[bitfield(u32)]
pub struct Instr32B {
#[bits(7)]
opcode: Opcode,
#[bits(1)]
imm11: u32,
#[bits(4)]
imm1: u32,
#[bits(3)]
funct3: u32,
#[bits(5)]
rs1: Reg,
#[bits(5)]
rs2: Reg,
#[bits(6)]
imm5: u32,
#[bits(1)]
imm12: u32,
}
impl Instr32B {
fn uimm(&self) -> u32 {
(self.imm1() << 1) | (self.imm5() << 5) | (self.imm11() << 11) | (self.imm12() << 12)
}
fn set_uimm(&mut self, val: u32) {
self.set_imm1((val >> 1) & 0xf);
self.set_imm5((val >> 5) & 0x3f);
self.set_imm11((val >> 11) & 1);
self.set_imm12((val >> 12) & 1);
}
fn imm(&self) -> i32 {
sign_extend_i32(self.uimm(), 11)
}
fn set_imm(&mut self, val: i32) {
self.set_uimm(val.cast_unsigned() & 0xfff);
}
}
impl Display for Instr32B {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{},{},{}", self.rs1(), self.rs2(), self.imm())
}
}
#[bitfield(u32)]
pub struct Instr32J {
#[bits(7)]
opcode: Opcode,
#[bits(5)]
rd: Reg,
#[bits(8)]
imm12: u32,
#[bits(1)]
imm11: u32,
#[bits(10)]
imm1: u32,
#[bits(1)]
imm20: u32,
}
impl Instr32J {
fn imm(&self) -> i32 {
sign_extend_i32(self.uimm(), 20)
}
fn set_imm(&mut self, val: i32) {
self.set_uimm(val.cast_unsigned() & 0x1fffff);
}
fn uimm(&self) -> u32 {
(self.imm1() << 1) | (self.imm11() << 11) | (self.imm12() << 12) | (self.imm20() << 20)
}
fn set_uimm(&mut self, val: u32) {
self.set_imm1((val >> 1) & 0x3ff);
self.set_imm11((val >> 11) & 1);
self.set_imm12((val >> 12) & 0xff);
self.set_imm20(val >> 20);
}
}
impl Display for Instr32J {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{},{}", self.rd(), self.imm())
}
}
#[bitfield(u32)]
pub struct Instr32U {
#[bits(7)]
pub opcode: Opcode,
#[bits(5)]
pub rd: Reg,
#[bits(20)]
pub imm12: u32,
}
impl Instr32U {
#[must_use]
pub fn uimm(&self) -> u32 {
self.imm12() << 12
}
pub fn set_uimm(&mut self, val: u32) {
self.set_imm12(val >> 12);
}
#[must_use]
pub fn imm(&self) -> i32 {
self.uimm().cast_signed()
}
pub fn set_imm(&mut self, val: i32) {
self.set_uimm(val.cast_unsigned())
}
}
impl Display for Instr32U {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{},0x{:x}", self.rd(), self.uimm() >> 12)
}
}
const FUNCT3_LOAD_B: u32 = 0;
const FUNCT3_LOAD_H: u32 = 1;
const FUNCT3_LOAD_W: u32 = 2;
const FUNCT3_LOAD_BU: u32 = 4;
const FUNCT3_LOAD_HU: u32 = 5;
const FUNCT3_STORE_B: u32 = 0;
const FUNCT3_STORE_H: u32 = 1;
const FUNCT3_STORE_W: u32 = 2;
const FUNCT3_OP_ADDI: u32 = 0;
const FUNCT3_OP_SLTI: u32 = 2;
const FUNCT3_OP_SLTIU: u32 = 3;
const FUNCT3_OP_XORI: u32 = 4;
const FUNCT3_OP_ORI: u32 = 6;
const FUNCT3_OP_ANDI: u32 = 7;
const FUNCT3_OP_SLI: u32 = 1;
#[allow(dead_code)]
const FUNCT3_OP_SRI: u32 = 5;
const FUNCT3_BRANCH_EQ: u32 = 0;
const FUNCT3_BRANCH_NE: u32 = 1;
const FUNCT3_BRANCH_LT: u32 = 4;
const FUNCT3_BRANCH_GE: u32 = 5;
const FUNCT3_BRANCH_LTU: u32 = 6;
const FUNCT3_BRANCH_GEU: u32 = 7;
const FUNCT10_SLLI: u32 = 0b00_0000_0001;
const FUNCT10_SRLI: u32 = 0b00_0000_0101;
const FUNCT10_SRAI: u32 = 0b01_0000_0101;
const FUNCT10_ADD: u32 = 0x000;
const FUNCT10_SUB: u32 = 0x100;
const FUNCT10_SLL: u32 = 0x001;
const FUNCT10_SLT: u32 = 0x002;
const FUNCT10_SLTU: u32 = 0x003;
const FUNCT10_XOR: u32 = 0x004;
const FUNCT10_SRL: u32 = 0x005;
const FUNCT10_SRA: u32 = 0x105;
const FUNCT10_OR: u32 = 0x006;
const FUNCT10_AND: u32 = 0x007;
const FUNCT10_MUL: u32 = 0x008;
const FUNCT10_MULH: u32 = 0x009;
const FUNCT10_MULHSU: u32 = 0x00a;
const FUNCT10_MULHU: u32 = 0x00b;
const FUNCT10_DIV: u32 = 0x00c;
const FUNCT10_DIVU: u32 = 0x00d;
const FUNCT10_REM: u32 = 0x00e;
const FUNCT10_REMU: u32 = 0x00f;
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum Reg {
X0 = 0,
X1Ra = 1,
X2Sp = 2,
X3Gp = 3,
X4Tp = 4,
X5T0 = 5,
X6T1 = 6,
X7T2 = 7,
X8S0 = 8,
X9S1 = 9,
X10A0 = 10,
X11A1 = 11,
X12A2 = 12,
X13A3 = 13,
X14A4 = 14,
X15A5 = 15,
X16A6 = 16,
X17A7 = 17,
X18S2 = 18,
X19S3 = 19,
X20S4 = 20,
X21S5 = 21,
X22S6 = 22,
X23S7 = 23,
X24S8 = 24,
X25S9 = 25,
X26S10 = 26,
X27S11 = 27,
X28T3 = 28,
X29T4 = 29,
X30T5 = 30,
X31T6 = 31,
}
impl Reg {
const fn from_bits(bits: u8) -> Self {
match bits & 0x1f {
0 => Self::X0,
1 => Self::X1Ra,
2 => Self::X2Sp,
3 => Self::X3Gp,
4 => Self::X4Tp,
5 => Self::X5T0,
6 => Self::X6T1,
7 => Self::X7T2,
8 => Self::X8S0,
9 => Self::X9S1,
10 => Self::X10A0,
11 => Self::X11A1,
12 => Self::X12A2,
13 => Self::X13A3,
14 => Self::X14A4,
15 => Self::X15A5,
16 => Self::X16A6,
17 => Self::X17A7,
18 => Self::X18S2,
19 => Self::X19S3,
20 => Self::X20S4,
21 => Self::X21S5,
22 => Self::X22S6,
23 => Self::X23S7,
24 => Self::X24S8,
25 => Self::X25S9,
26 => Self::X26S10,
27 => Self::X27S11,
28 => Self::X28T3,
29 => Self::X29T4,
30 => Self::X30T5,
31 => Self::X31T6,
_ => unreachable!(),
}
}
const fn into_bits(self) -> u8 {
self as u8
}
#[must_use]
pub const fn abi_mnemonic(self) -> &'static str {
match self {
Self::X0 => "x0",
Self::X1Ra => "ra",
Self::X2Sp => "sp",
Self::X3Gp => "gp",
Self::X4Tp => "tp",
Self::X5T0 => "t0",
Self::X6T1 => "t1",
Self::X7T2 => "t2",
Self::X8S0 => "s0",
Self::X9S1 => "s1",
Self::X10A0 => "a0",
Self::X11A1 => "a1",
Self::X12A2 => "a2",
Self::X13A3 => "a3",
Self::X14A4 => "a4",
Self::X15A5 => "a5",
Self::X16A6 => "a6",
Self::X17A7 => "a7",
Self::X18S2 => "s2",
Self::X19S3 => "s3",
Self::X20S4 => "s4",
Self::X21S5 => "s5",
Self::X22S6 => "s6",
Self::X23S7 => "s7",
Self::X24S8 => "s8",
Self::X25S9 => "s9",
Self::X26S10 => "s10",
Self::X27S11 => "s11",
Self::X28T3 => "t3",
Self::X29T4 => "t4",
Self::X30T5 => "t5",
Self::X31T6 => "t6",
}
}
}
impl Display for Reg {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.abi_mnemonic())
}
}
impl From<Reg> for usize {
fn from(value: Reg) -> Self {
value as usize
}
}
#[cfg(test)]
mod test {
use crate::riscv::Instr;
#[test]
fn test() {
assert_eq!(Instr(0x02f807b3).decode().to_string(), "mul a5,a6,a5");
assert_eq!(Instr(0x010107b7).decode().to_string(), "lui a5,0x1010");
assert_eq!(Instr(0x10178793).decode().to_string(), "addi a5,a5,257");
assert_eq!(Instr(0x0141).decode().to_string(), "addi sp,sp,16");
assert_eq!(Instr(0x00a92223).decode().to_string(), "sw a0,4(s2)",);
assert_eq!(Instr(0xc31c).decode().to_string(), "sw a5,0(a4)");
assert_eq!(Instr(0x1141).decode().to_string(), "addi sp,sp,-16");
assert_eq!(Instr(0x00e68c63).decode().to_string(), "beq a3,a4,24");
assert_eq!(Instr(0xca81).decode().to_string(), "beq a3,x0,16");
assert_eq!(Instr(0x8082).decode().to_string(), "jalr x0,ra,0");
}
}