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