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}