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 /// A second immediate, for instructions with two.
43 pub(crate) imm2: i32,
44 /// For vector instructions, whether the operation is masked by `v0`.
45 pub(crate) masked: bool,
46 /// Whether floating-point operands are in the integer registers (Zfinx).
47 pub(crate) fp_in_x: bool,
48}
49
50impl Instruction {
51 pub(crate) fn new(isa: Isa, pc: u64, inst: u64, op: &'static Opcode) -> Self {
52 // Reserved lengths are shown one 16-bit parcel at a time so that
53 // disassembly can continue.
54 let len = crate::inst_length(inst as u16).unwrap_or(2) as u8;
55 Instruction {
56 isa,
57 pc,
58 inst,
59 len,
60 op,
61 decoded: op,
62 rd: 0,
63 rs1: 0,
64 rs2: 0,
65 rs3: 0,
66 rm: 0,
67 pred: 0,
68 succ: 0,
69 aq: false,
70 rl: false,
71 imm: 0,
72 imm2: 0,
73 masked: false,
74 fp_in_x: false,
75 }
76 }
77
78 /// The base ISA the instruction was decoded for.
79 pub fn isa(&self) -> Isa {
80 self.isa
81 }
82
83 /// The address of the instruction.
84 pub fn pc(&self) -> u64 {
85 self.pc
86 }
87
88 /// The instruction encoding, in the low [`length`](Self::length) bytes.
89 pub fn raw(&self) -> u64 {
90 self.inst
91 }
92
93 /// The length of the encoding in bytes: 2, 4, 6 or 8.
94 ///
95 /// Encodings with a reserved length (10 bytes or more) are decoded as a
96 /// 2-byte illegal instruction.
97 pub fn length(&self) -> usize {
98 self.len as usize
99 }
100
101 /// The mnemonic, such as `"addi"`, `"li"` or `"amoadd.w"`.
102 ///
103 /// An encoding that is not a valid instruction is shown as objdump shows
104 /// it, as an `.insn` directive (`.insn 4, 0x0911003b`), or as a `.2byte`
105 /// directive for a parcel of a reserved-length encoding. Use
106 /// [`is_illegal`](Self::is_illegal) to check for these.
107 ///
108 /// Compressed instructions are shown as the instruction they expand to,
109 /// and aliases (pseudoinstructions) are used where objdump uses them.
110 /// [`without_aliases`](Self::without_aliases) gives the instruction as
111 /// encoded. The mnemonic does not include the `.aq`/`.rl` ordering
112 /// suffixes of atomic instructions, which [`Display`](core::fmt::Display)
113 /// adds.
114 pub fn mnemonic(&self) -> &'static str {
115 self.op.name
116 }
117
118 /// The operands, without the mnemonic, such as `a0,8(sp)`.
119 pub fn operands(&self) -> Operands<'_> {
120 Operands(self)
121 }
122
123 /// Whether the encoding is not a valid instruction. This includes the
124 /// all-zeros 16-bit encoding, which the ISA defines to be illegal and
125 /// which is shown as `unimp`.
126 pub fn is_illegal(&self) -> bool {
127 self.op.codec == Codec::Illegal || *self.decoded == C_UNIMP
128 }
129
130 /// The same instruction, shown as encoded: compressed instructions keep
131 /// their `c.` names and pseudoinstructions are not used. This matches
132 /// `objdump -M no-aliases`.
133 pub fn without_aliases(&self) -> Instruction {
134 Instruction {
135 op: self.decoded,
136 ..*self
137 }
138 }
139
140 /// The target address of a PC-relative operand, wrapped to the
141 /// register width on RV32. Addresses are 64 bits, so on RV128 it is the
142 /// low 64 bits of the target.
143 pub(crate) fn target(&self) -> u64 {
144 let target = self.pc.wrapping_add(self.imm as i64 as u64);
145 match self.isa {
146 Isa::Rv32 => target & 0xffff_ffff,
147 Isa::Rv64 | Isa::Rv128 => target,
148 }
149 }
150}