cranelift_assembler_x64/
api.rs

1//! Contains traits that a user of this assembler must implement.
2
3use crate::gpr;
4use crate::xmm;
5use crate::{Amode, DeferredTarget, GprMem, XmmMem};
6use std::fmt;
7use std::{num::NonZeroU8, vec::Vec};
8
9/// Describe how an instruction is emitted into a code buffer.
10pub trait CodeSink {
11    /// Add 1 byte to the code section.
12    fn put1(&mut self, _: u8);
13
14    /// Add 2 bytes to the code section.
15    fn put2(&mut self, _: u16);
16
17    /// Add 4 bytes to the code section.
18    fn put4(&mut self, _: u32);
19
20    /// Add 8 bytes to the code section.
21    fn put8(&mut self, _: u64);
22
23    /// Inform the code buffer of a possible trap at the current location;
24    /// required for assembling memory accesses.
25    fn add_trap(&mut self, code: TrapCode);
26
27    /// Inform the code buffer that a use of `target` is about to happen at the
28    /// current offset.
29    ///
30    /// After this method is called the bytes of the target are then expected to
31    /// be placed using one of the above `put*` methods.
32    fn use_target(&mut self, target: DeferredTarget);
33
34    /// Resolves a `KnownOffset` value to the actual signed offset.
35    fn known_offset(&self, offset: KnownOffset) -> i32;
36}
37
38/// Provide a convenient implementation for testing.
39impl CodeSink for Vec<u8> {
40    fn put1(&mut self, v: u8) {
41        self.extend_from_slice(&[v]);
42    }
43
44    fn put2(&mut self, v: u16) {
45        self.extend_from_slice(&v.to_le_bytes());
46    }
47
48    fn put4(&mut self, v: u32) {
49        self.extend_from_slice(&v.to_le_bytes());
50    }
51
52    fn put8(&mut self, v: u64) {
53        self.extend_from_slice(&v.to_le_bytes());
54    }
55
56    fn add_trap(&mut self, _: TrapCode) {}
57
58    fn use_target(&mut self, _: DeferredTarget) {}
59
60    fn known_offset(&self, offset: KnownOffset) -> i32 {
61        panic!("unknown offset {offset:?}")
62    }
63}
64
65/// Wrap [`CodeSink`]-specific labels.
66#[derive(Debug, Copy, Clone, PartialEq)]
67#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
68pub struct Label(pub u32);
69
70/// Wrap [`CodeSink`]-specific constant keys.
71#[derive(Debug, Copy, Clone, PartialEq)]
72#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
73pub struct Constant(pub u32);
74
75/// Wrap [`CodeSink`]-specific trap codes.
76#[derive(Debug, Clone, Copy, PartialEq)]
77#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
78pub struct TrapCode(pub NonZeroU8);
79
80impl fmt::Display for TrapCode {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        write!(f, "trap={}", self.0)
83    }
84}
85
86/// A `KnownOffset` is a unique identifier for a specific offset known only at
87/// emission time.
88pub type KnownOffset = u8;
89
90/// A type set fixing the register types used in the assembler.
91///
92/// This assembler is parameterizable over register types; this allows the
93/// assembler users (e.g., Cranelift) to define their own register types
94/// independent of this crate.
95pub trait Registers {
96    /// An x64 general purpose register that may be read.
97    type ReadGpr: AsReg;
98
99    /// An x64 general purpose register that may be read and written.
100    type ReadWriteGpr: AsReg;
101
102    /// An x64 general purpose register that may be written.
103    type WriteGpr: AsReg;
104
105    /// An x64 SSE register that may be read.
106    type ReadXmm: AsReg;
107
108    /// An x64 SSE register that may be read and written.
109    type ReadWriteXmm: AsReg;
110
111    /// An x64 SSE register that may be written.
112    type WriteXmm: AsReg;
113}
114
115/// Describe how to interact with an external register type.
116pub trait AsReg: Copy + Clone + std::fmt::Debug + PartialEq {
117    /// Create a register from its hardware encoding.
118    ///
119    /// This is primarily useful for fuzzing, though it is also useful for
120    /// generating fixed registers.
121    fn new(enc: u8) -> Self;
122
123    /// Return the register's hardware encoding; e.g., `0` for `%rax`.
124    fn enc(&self) -> u8;
125
126    /// Return the register name.
127    fn to_string(&self, size: Option<gpr::Size>) -> String {
128        match size {
129            Some(size) => gpr::enc::to_string(self.enc(), size).into(),
130            None => xmm::enc::to_string(self.enc()).into(),
131        }
132    }
133}
134
135/// Provide a convenient implementation for testing.
136impl AsReg for u8 {
137    fn new(enc: u8) -> Self {
138        enc
139    }
140    fn enc(&self) -> u8 {
141        *self
142    }
143}
144
145/// Describe a visitor for the register operands of an instruction.
146///
147/// Due to how Cranelift's register allocation works, we allow the visitor to
148/// modify the register operands in place. This allows Cranelift to convert
149/// virtual registers (`[128..N)`) to physical registers (`[0..16)`) without
150/// re-allocating the entire instruction object.
151pub trait RegisterVisitor<R: Registers> {
152    /// Visit a read-only register.
153    fn read_gpr(&mut self, reg: &mut R::ReadGpr);
154    /// Visit a read-write register.
155    fn read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr);
156    /// Visit a write-only register.
157    fn write_gpr(&mut self, reg: &mut R::WriteGpr);
158
159    /// Visit a read-only fixed register; this register can be modified in-place
160    /// but must emit as the hardware encoding `enc`.
161    fn fixed_read_gpr(&mut self, reg: &mut R::ReadGpr, enc: u8);
162    /// Visit a read-write fixed register; this register can be modified
163    /// in-place but must emit as the hardware encoding `enc`.
164    fn fixed_read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr, enc: u8);
165    /// Visit a write-only fixed register; this register can be modified
166    /// in-place but must emit as the hardware encoding `enc`.
167    fn fixed_write_gpr(&mut self, reg: &mut R::WriteGpr, enc: u8);
168
169    /// Visit a read-only SSE register.
170    fn read_xmm(&mut self, reg: &mut R::ReadXmm);
171    /// Visit a read-write SSE register.
172    fn read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm);
173    /// Visit a write-only SSE register.
174    fn write_xmm(&mut self, reg: &mut R::WriteXmm);
175
176    /// Visit a read-only fixed SSE register; this register can be modified
177    /// in-place but must emit as the hardware encoding `enc`.
178    fn fixed_read_xmm(&mut self, reg: &mut R::ReadXmm, enc: u8);
179    /// Visit a read-write fixed SSE register; this register can be modified
180    /// in-place but must emit as the hardware encoding `enc`.
181    fn fixed_read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm, enc: u8);
182    /// Visit a read-only fixed SSE register; this register can be modified
183    /// in-place but must emit as the hardware encoding `enc`.
184    fn fixed_write_xmm(&mut self, reg: &mut R::WriteXmm, enc: u8);
185
186    /// Visit the registers in an [`Amode`].
187    ///
188    /// This is helpful for generated code: it allows capturing the `R::ReadGpr`
189    /// type (which an `Amode` method cannot) and simplifies the code to be
190    /// generated.
191    fn read_amode(&mut self, amode: &mut Amode<R::ReadGpr>) {
192        match amode {
193            Amode::ImmReg { base, .. } => {
194                self.read_gpr(base);
195            }
196            Amode::ImmRegRegShift { base, index, .. } => {
197                self.read_gpr(base);
198                self.read_gpr(index.as_mut());
199            }
200            Amode::RipRelative { .. } => {}
201        }
202    }
203
204    /// Helper method to handle a read/write [`GprMem`] operand.
205    fn read_write_gpr_mem(&mut self, op: &mut GprMem<R::ReadWriteGpr, R::ReadGpr>) {
206        match op {
207            GprMem::Gpr(r) => self.read_write_gpr(r),
208            GprMem::Mem(m) => self.read_amode(m),
209        }
210    }
211
212    /// Helper method to handle a write [`GprMem`] operand.
213    fn write_gpr_mem(&mut self, op: &mut GprMem<R::WriteGpr, R::ReadGpr>) {
214        match op {
215            GprMem::Gpr(r) => self.write_gpr(r),
216            GprMem::Mem(m) => self.read_amode(m),
217        }
218    }
219
220    /// Helper method to handle a read-only [`GprMem`] operand.
221    fn read_gpr_mem(&mut self, op: &mut GprMem<R::ReadGpr, R::ReadGpr>) {
222        match op {
223            GprMem::Gpr(r) => self.read_gpr(r),
224            GprMem::Mem(m) => self.read_amode(m),
225        }
226    }
227
228    /// Helper method to handle a read-only [`XmmMem`] operand.
229    fn read_xmm_mem(&mut self, op: &mut XmmMem<R::ReadXmm, R::ReadGpr>) {
230        match op {
231            XmmMem::Xmm(r) => self.read_xmm(r),
232            XmmMem::Mem(m) => self.read_amode(m),
233        }
234    }
235
236    /// Helper method to handle a write [`XmmMem`] operand.
237    fn write_xmm_mem(&mut self, op: &mut XmmMem<R::WriteXmm, R::ReadGpr>) {
238        match op {
239            XmmMem::Xmm(r) => self.write_xmm(r),
240            XmmMem::Mem(m) => self.read_amode(m),
241        }
242    }
243}