Skip to main content

asmkit/core/
rwinfo.rs

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