Skip to main content

asmkit/core/
rwinfo.rs

1//! Read/write information describing how an instruction accesses its operands and CPU flags.
2//!
3//! This is the asmkit port of AsmJit's effects model (`OpRWFlags`, `CpuRWFlags`, `OpRWInfo`,
4//! `InstRWInfo`, `InstControlFlow`, `InstSameRegHint` — see `core/inst.h` in AsmJit). The data
5//! model is shaped so that generated per-architecture tables (produced by `meta/`) are plain
6//! `static` arrays and a future register-allocation pass can consume them uniformly across
7//! architectures.
8
9use bitflags::bitflags;
10
11use super::globals::MAX_OP_COUNT;
12
13/// Physical register id used when an operand is not tied to any specific physical register.
14pub const INVALID_PHYS_ID: u8 = 0xFF;
15
16bitflags! {
17    /// CPU read/write flags used by [`InstRwInfo`].
18    ///
19    /// Common flags occupy the low byte. Architecture-specific aliases share the same bits
20    /// where architectures do not overlap (X86 EFLAGS and AArch64 NZCV reuse the common bits).
21    #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
22    pub struct CpuRwFlags: u32 {
23        /// No flags.
24        const NONE = 0x0000_0000;
25
26        /// Signed overflow flag.
27        const OF = 0x0000_0001;
28        /// Carry flag.
29        const CF = 0x0000_0002;
30        /// Zero and/or equality flag (1 if zero/equal).
31        const ZF = 0x0000_0004;
32        /// Sign flag (negative/sign, if set).
33        const SF = 0x0000_0008;
34
35        /// Carry flag (X86/X64).
36        const X86_CF = Self::CF.bits();
37        /// Overflow flag (X86/X64).
38        const X86_OF = Self::OF.bits();
39        /// Sign flag (X86/X64).
40        const X86_SF = Self::SF.bits();
41        /// Zero flag (X86/X64).
42        const X86_ZF = Self::ZF.bits();
43
44        /// Adjust flag (X86/X64).
45        const X86_AF = 0x0000_0100;
46        /// Parity flag (X86/X64).
47        const X86_PF = 0x0000_0200;
48        /// Direction flag (X86/X64).
49        const X86_DF = 0x0000_0400;
50        /// Interrupt enable flag (X86/X64).
51        const X86_IF = 0x0000_0800;
52        /// Alignment check flag (X86/X64).
53        const X86_AC = 0x0000_1000;
54
55        /// FPU C0 status flag (X86/X64).
56        const X86_C0 = 0x0001_0000;
57        /// FPU C1 status flag (X86/X64).
58        const X86_C1 = 0x0002_0000;
59        /// FPU C2 status flag (X86/X64).
60        const X86_C2 = 0x0004_0000;
61        /// FPU C3 status flag (X86/X64).
62        const X86_C3 = 0x0008_0000;
63
64        /// Overflow flag (AArch64 PSTATE.V).
65        const A64_V = Self::OF.bits();
66        /// Carry flag (AArch64 PSTATE.C).
67        const A64_C = Self::CF.bits();
68        /// Zero flag (AArch64 PSTATE.Z).
69        const A64_Z = Self::ZF.bits();
70        /// Negative flag (AArch64 PSTATE.N).
71        const A64_N = Self::SF.bits();
72        /// Cumulative saturation flag (AArch64 PSTATE.Q).
73        const A64_Q = 0x0000_0100;
74        /// Greater-than-or-equal flags (AArch64 PSTATE.GE).
75        const A64_GE = 0x0000_0200;
76
77        /// Floating-point accrued exception flags (RISC-V `fflags` CSR field).
78        const RISCV_FFLAGS = 0x0000_0100;
79        /// Floating-point rounding mode (RISC-V `frm` CSR field).
80        const RISCV_FRM = 0x0000_0200;
81        /// Fixed-point saturation flag (RISC-V `vxsat` CSR field).
82        const RISCV_VXSAT = 0x0000_0400;
83    }
84}
85
86bitflags! {
87    /// Operand read/write flags describe how the operand is accessed and additional features.
88    #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
89    pub struct OpRwFlags: u32 {
90        /// No flags.
91        const NONE = 0;
92
93        /// Operand is read.
94        const READ = 0x0000_0001;
95        /// Operand is written.
96        const WRITE = 0x0000_0002;
97        /// Operand is both read and written.
98        const RW = Self::READ.bits() | Self::WRITE.bits();
99
100        /// Register operand can be replaced by a memory operand.
101        const REG_MEM = 0x0000_0004;
102
103        /// The register must be allocated to the index of the previous register + 1.
104        ///
105        /// Used by instructions that use consecutive registers, where only the first one is
106        /// encoded in the instruction, and the others are a sequence starting at the first
107        /// one (X86 VP2INTERSECT*, AArch64 multi-vector loads/stores).
108        const CONSECUTIVE = 0x0000_0008;
109
110        /// The `extend_byte_mask` represents a zero extension.
111        const ZEXT = 0x0000_0010;
112
113        /// The register must have assigned a unique physical id, which cannot be assigned to
114        /// any other register.
115        const UNIQUE = 0x0000_0080;
116
117        /// Register operand must use [`OpRwInfo::phys_id`].
118        const REG_PHYS_ID = 0x0000_0100;
119        /// Base register of a memory operand must use [`OpRwInfo::phys_id`].
120        const MEM_PHYS_ID = 0x0000_0200;
121
122        /// This memory operand is only used to encode registers and doesn't access memory.
123        ///
124        /// X86 specific: BNDLDX, BNDSTX, and LEA.
125        const MEM_FAKE = 0x0000_0400;
126
127        /// Base register of the memory operand will be read.
128        const MEM_BASE_READ = 0x0000_1000;
129        /// Base register of the memory operand will be written.
130        const MEM_BASE_WRITE = 0x0000_2000;
131        /// Base register of the memory operand will be read & written.
132        const MEM_BASE_RW = Self::MEM_BASE_READ.bits() | Self::MEM_BASE_WRITE.bits();
133
134        /// Index register of the memory operand will be read.
135        const MEM_INDEX_READ = 0x0000_4000;
136        /// Index register of the memory operand will be written.
137        const MEM_INDEX_WRITE = 0x0000_8000;
138        /// Index register of the memory operand will be read & written.
139        const MEM_INDEX_RW = Self::MEM_INDEX_READ.bits() | Self::MEM_INDEX_WRITE.bits();
140
141        /// Base register of the memory operand will be modified before the operation.
142        const MEM_BASE_PRE_MODIFY = 0x0001_0000;
143        /// Base register of the memory operand will be modified after the operation.
144        const MEM_BASE_POST_MODIFY = 0x0002_0000;
145    }
146}
147
148bitflags! {
149    /// Flags used by [`InstRwInfo`].
150    #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
151    pub struct InstRwFlags: u32 {
152        /// No flags.
153        const NONE = 0x0000_0000;
154
155        /// Describes a move operation.
156        ///
157        /// Used by register allocation to eliminate moves that are guaranteed to be moves
158        /// only.
159        const MOV_OP = 0x0000_0001;
160    }
161}
162
163/// Read/write information related to a single operand, used by [`InstRwInfo`].
164///
165/// `phys_id` is always [`INVALID_PHYS_ID`] unless a physical register is required — unlike
166/// AsmJit (where value-initialization leaves it 0), asmkit keeps one "no phys id" sentinel.
167#[derive(Clone, Copy, PartialEq, Eq, Debug)]
168pub struct OpRwInfo {
169    /// Read/write flags.
170    pub op_flags: OpRwFlags,
171    /// Physical register index, if required ([`INVALID_PHYS_ID`] otherwise).
172    pub phys_id: u8,
173    /// Size of a possible memory operand that can replace a register operand.
174    pub rm_size: u8,
175    /// If non-zero, this is a consecutive lead register and the value describes how many
176    /// registers follow.
177    pub consecutive_lead_count: u8,
178    /// Read bit-mask where each bit represents one byte read from Reg/Mem.
179    pub read_byte_mask: u64,
180    /// Write bit-mask where each bit represents one byte written to Reg/Mem.
181    pub write_byte_mask: u64,
182    /// Zero/sign extend bit-mask where each bit represents one byte written to Reg/Mem.
183    pub extend_byte_mask: u64,
184}
185
186impl Default for OpRwInfo {
187    fn default() -> Self {
188        Self::new()
189    }
190}
191
192impl OpRwInfo {
193    /// Returns this operand info with all members reset to zero.
194    pub const fn new() -> Self {
195        Self {
196            op_flags: OpRwFlags::NONE,
197            phys_id: INVALID_PHYS_ID,
198            rm_size: 0,
199            consecutive_lead_count: 0,
200            read_byte_mask: 0,
201            write_byte_mask: 0,
202            extend_byte_mask: 0,
203        }
204    }
205
206    /// Resets this operand info to `op_flags`, `register_size`, and possibly `phys_id`,
207    /// computing full byte masks from the flags (mirrors AsmJit's `OpRWInfo::reset`).
208    pub fn reset(&mut self, op_flags: OpRwFlags, register_size: u32, phys_id: u8) {
209        self.op_flags = op_flags;
210        self.phys_id = phys_id;
211        self.rm_size = if op_flags.contains(OpRwFlags::REG_MEM) {
212            register_size as u8
213        } else {
214            0
215        };
216        self.consecutive_lead_count = 0;
217
218        let mask = u64::MAX >> (64 - register_size.min(64));
219        self.read_byte_mask = if op_flags.contains(OpRwFlags::READ) {
220            mask
221        } else {
222            0
223        };
224        self.write_byte_mask = if op_flags.contains(OpRwFlags::WRITE) {
225            mask
226        } else {
227            0
228        };
229        self.extend_byte_mask = 0;
230    }
231
232    /// Tests whether this operand is read from.
233    pub const fn is_read(&self) -> bool {
234        self.op_flags.contains(OpRwFlags::READ)
235    }
236
237    /// Tests whether this operand is written to.
238    pub const fn is_write(&self) -> bool {
239        self.op_flags.contains(OpRwFlags::WRITE)
240    }
241
242    /// Tests whether this operand is both read and written.
243    pub const fn is_read_write(&self) -> bool {
244        self.op_flags.contains(OpRwFlags::RW)
245    }
246
247    /// Tests whether this operand is read-only.
248    pub const fn is_read_only(&self) -> bool {
249        !self.is_write()
250    }
251
252    /// Tests whether this operand is write-only.
253    pub const fn is_write_only(&self) -> bool {
254        !self.is_read()
255    }
256}
257
258/// Read/write information of an instruction.
259#[derive(Clone, Copy, Debug)]
260pub struct InstRwInfo {
261    /// Instruction flags.
262    pub inst_flags: InstRwFlags,
263    /// CPU flags read.
264    pub read_flags: CpuRwFlags,
265    /// CPU flags written.
266    pub write_flags: CpuRwFlags,
267    /// Count of operands.
268    pub op_count: u8,
269    /// CPU feature required for replacing register operand with memory operand
270    /// (architecture-specific feature id, 0 = none).
271    pub rm_feature: u8,
272    /// Read/write info of the extra register (rep{} or {k} selector).
273    pub extra_reg: OpRwInfo,
274    /// Read/write info of instruction operands.
275    pub operands: [OpRwInfo; MAX_OP_COUNT],
276}
277
278impl Default for InstRwInfo {
279    fn default() -> Self {
280        Self::new()
281    }
282}
283
284impl InstRwInfo {
285    /// Returns this instruction info with all members reset to zero.
286    pub const fn new() -> Self {
287        Self {
288            inst_flags: InstRwFlags::NONE,
289            read_flags: CpuRwFlags::NONE,
290            write_flags: CpuRwFlags::NONE,
291            op_count: 0,
292            rm_feature: 0,
293            extra_reg: OpRwInfo::new(),
294            operands: [OpRwInfo::new(); MAX_OP_COUNT],
295        }
296    }
297
298    /// Tests whether this instruction is a move operation (see [`InstRwFlags::MOV_OP`]).
299    pub const fn is_mov_op(&self) -> bool {
300        self.inst_flags.contains(InstRwFlags::MOV_OP)
301    }
302
303    /// Returns read/write info of operands as a slice, truncated to `op_count`.
304    pub fn operands(&self) -> &[OpRwInfo] {
305        &self.operands[..self.op_count as usize]
306    }
307
308    /// Returns read/write info of operands as a mutable slice, truncated to `op_count`.
309    pub fn operands_mut(&mut self) -> &mut [OpRwInfo] {
310        &mut self.operands[..self.op_count as usize]
311    }
312
313    /// Returns read/write info of the operand at `index`, or `None` if out of range.
314    pub fn operand(&self, index: usize) -> Option<&OpRwInfo> {
315        self.operands().get(index)
316    }
317}
318
319/// Instruction control flow.
320#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
321#[repr(u8)]
322pub enum InstControlFlow {
323    /// Regular instruction.
324    #[default]
325    Regular = 0,
326    /// Unconditional jump.
327    Jump = 1,
328    /// Conditional jump (branch).
329    Branch = 2,
330    /// Function call.
331    Call = 3,
332    /// Function return.
333    Return = 4,
334}
335
336/// Hint used when two or more operands of an instruction are the same register.
337///
338/// Influences the RW operations query: instructions such as `xor x, x` produce write-only
339/// semantics, `and x, x` read-only.
340#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
341#[repr(u8)]
342pub enum InstSameRegHint {
343    /// No special handling.
344    #[default]
345    None = 0,
346    /// Operands become read-only, the operation doesn't change the content (`X & X` and
347    /// similar).
348    RO = 1,
349    /// Operands become write-only, the content of the input(s) doesn't matter (`X ^ X`,
350    /// `X - X`, and similar).
351    WO = 2,
352}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357
358    #[test]
359    fn op_rw_flags_layout() {
360        // Must stay compatible with AsmJit: RA code depends on these exact values.
361        assert_eq!(OpRwFlags::READ.bits(), 0x1);
362        assert_eq!(OpRwFlags::WRITE.bits(), 0x2);
363        assert_eq!(OpRwFlags::RW.bits(), 0x3);
364        assert_eq!(OpRwFlags::REG_MEM.bits(), 0x4);
365    }
366
367    #[test]
368    fn reset_computes_masks() {
369        let mut info = OpRwInfo::new();
370        info.reset(OpRwFlags::RW, 8, INVALID_PHYS_ID);
371        assert!(info.is_read_write());
372        assert_eq!(info.read_byte_mask, 0xFF);
373        assert_eq!(info.write_byte_mask, 0xFF);
374        assert_eq!(info.extend_byte_mask, 0);
375
376        info.reset(OpRwFlags::WRITE | OpRwFlags::REG_MEM, 16, 3);
377        assert!(info.is_write_only());
378        assert_eq!(info.rm_size, 16);
379        assert_eq!(info.phys_id, 3);
380        assert_eq!(info.write_byte_mask, 0xFFFF);
381        assert_eq!(info.read_byte_mask, 0);
382    }
383}