Skip to main content

disc_v/
instruction.rs

1// Copyright (c) 2026 Christopher Swenson
2// Licensed under the Apache-2.0 license.
3//
4// Derived from riscv-disassembler, Copyright (c) 2016-2017 Michael Clark
5// and Copyright (c) 2017-2018 SiFive, Inc., under the MIT license; see NOTICE.
6
7//! The decoded instruction type.
8
9use crate::Isa;
10use crate::format::Operands;
11use crate::opcodes::c::C_UNIMP;
12use crate::opcodes::{Codec, Opcode};
13
14/// A decoded instruction.
15///
16/// Create one with [`decode`](fn@crate::decode),
17/// [`decode_bytes`](crate::decode_bytes), or by iterating over
18/// [`disassemble`](crate::disassemble). Its [`Display`](core::fmt::Display)
19/// output follows GNU objdump's for a raw binary, such as `addi gp,gp,256`
20/// or `beqz a0,0x1040`.
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub struct Instruction {
23    pub(crate) isa: Isa,
24    pub(crate) pc: u64,
25    pub(crate) inst: u64,
26    pub(crate) len: u8,
27    /// The opcode to show: expanded if compressed, and aliased if a
28    /// pseudoinstruction applies.
29    pub(crate) op: &'static Opcode,
30    /// The opcode as encoded, before expansion and aliasing.
31    pub(crate) decoded: &'static Opcode,
32    pub(crate) rd: u8,
33    pub(crate) rs1: u8,
34    pub(crate) rs2: u8,
35    pub(crate) rs3: u8,
36    pub(crate) rm: u8,
37    pub(crate) pred: u8,
38    pub(crate) succ: u8,
39    pub(crate) aq: bool,
40    pub(crate) rl: bool,
41    pub(crate) imm: i32,
42}
43
44impl Instruction {
45    pub(crate) fn new(isa: Isa, pc: u64, inst: u64, op: &'static Opcode) -> Self {
46        // Reserved lengths are shown one 16-bit parcel at a time so that
47        // disassembly can continue.
48        let len = crate::inst_length(inst as u16).unwrap_or(2) as u8;
49        Instruction {
50            isa,
51            pc,
52            inst,
53            len,
54            op,
55            decoded: op,
56            rd: 0,
57            rs1: 0,
58            rs2: 0,
59            rs3: 0,
60            rm: 0,
61            pred: 0,
62            succ: 0,
63            aq: false,
64            rl: false,
65            imm: 0,
66        }
67    }
68
69    /// The base ISA the instruction was decoded for.
70    pub fn isa(&self) -> Isa {
71        self.isa
72    }
73
74    /// The address of the instruction.
75    pub fn pc(&self) -> u64 {
76        self.pc
77    }
78
79    /// The instruction encoding, in the low [`length`](Self::length) bytes.
80    pub fn raw(&self) -> u64 {
81        self.inst
82    }
83
84    /// The length of the encoding in bytes: 2, 4, 6 or 8.
85    ///
86    /// Encodings with a reserved length (10 bytes or more) are decoded as a
87    /// 2-byte illegal instruction.
88    pub fn length(&self) -> usize {
89        self.len as usize
90    }
91
92    /// The mnemonic, such as `"addi"`, `"li"` or `"amoadd.w"`.
93    ///
94    /// An encoding that is not a valid instruction is shown as objdump shows
95    /// it, as an `.insn` directive (`.insn 4, 0x0911003b`), or as a `.2byte`
96    /// directive for a parcel of a reserved-length encoding. Use
97    /// [`is_illegal`](Self::is_illegal) to check for these.
98    ///
99    /// Compressed instructions are shown as the instruction they expand to,
100    /// and aliases (pseudoinstructions) are used where objdump uses them.
101    /// [`without_aliases`](Self::without_aliases) gives the instruction as
102    /// encoded. The mnemonic does not include the `.aq`/`.rl` ordering
103    /// suffixes of atomic instructions, which [`Display`](core::fmt::Display)
104    /// adds.
105    pub fn mnemonic(&self) -> &'static str {
106        self.op.name
107    }
108
109    /// The operands, without the mnemonic, such as `a0,8(sp)`.
110    pub fn operands(&self) -> Operands<'_> {
111        Operands(self)
112    }
113
114    /// Whether the encoding is not a valid instruction. This includes the
115    /// all-zeros 16-bit encoding, which the ISA defines to be illegal and
116    /// which is shown as `unimp`.
117    pub fn is_illegal(&self) -> bool {
118        self.op.codec == Codec::Illegal || *self.decoded == C_UNIMP
119    }
120
121    /// The same instruction, shown as encoded: compressed instructions keep
122    /// their `c.` names and pseudoinstructions are not used. This matches
123    /// `objdump -M no-aliases`.
124    pub fn without_aliases(&self) -> Instruction {
125        Instruction {
126            op: self.decoded,
127            ..*self
128        }
129    }
130
131    /// The target address of a PC-relative operand, wrapped to the
132    /// register width.
133    pub(crate) fn target(&self) -> u64 {
134        let target = self.pc.wrapping_add(self.imm as i64 as u64);
135        match self.isa {
136            Isa::Rv32 => target & 0xffff_ffff,
137            Isa::Rv64 | Isa::Rv128 => target,
138        }
139    }
140}