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}