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, GprMem, XmmMem};
6use std::fmt;
7use std::{num::NonZeroU8, ops::Index, 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    /// Return the byte offset of the current location in the code buffer;
28    /// required for assembling RIP-relative memory accesses.
29    fn current_offset(&self) -> u32;
30
31    /// Inform the code buffer of a use of `label` at `offset`; required for
32    /// assembling RIP-relative memory accesses.
33    fn use_label_at_offset(&mut self, offset: u32, label: Label);
34
35    /// Return the label for a constant `id`; required for assembling
36    /// RIP-relative memory accesses of constants.
37    fn get_label_for_constant(&mut self, id: Constant) -> Label;
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 current_offset(&self) -> u32 {
61        self.len().try_into().unwrap()
62    }
63
64    fn use_label_at_offset(&mut self, _: u32, _: Label) {}
65
66    fn get_label_for_constant(&mut self, c: Constant) -> Label {
67        Label(c.0)
68    }
69}
70
71/// Wrap [`CodeSink`]-specific labels.
72#[derive(Debug, Copy, Clone)]
73#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
74pub struct Label(pub u32);
75
76/// Wrap [`CodeSink`]-specific constant keys.
77#[derive(Debug, Copy, Clone)]
78#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
79pub struct Constant(pub u32);
80
81/// Wrap [`CodeSink`]-specific trap codes.
82#[derive(Debug, Clone, Copy)]
83#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
84pub struct TrapCode(pub NonZeroU8);
85
86impl fmt::Display for TrapCode {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        write!(f, "trap={}", self.0)
89    }
90}
91
92/// A table mapping `KnownOffset` identifiers to their `i32` offset values.
93///
94/// When encoding instructions, Cranelift may not know all of the information
95/// needed to construct an immediate. Specifically, addressing modes that
96/// require knowing the size of the tail arguments or outgoing arguments (see
97/// `SyntheticAmode::finalize`) will not know these sizes until emission.
98///
99/// This table allows up to do a "late" look up of these values by their
100/// `KnownOffset`.
101pub trait KnownOffsetTable: Index<usize, Output = i32> {}
102impl KnownOffsetTable for Vec<i32> {}
103/// Provide a convenient implementation for testing.
104impl KnownOffsetTable for [i32; 2] {}
105
106/// A `KnownOffset` is a unique identifier for a specific offset known only at
107/// emission time.
108pub type KnownOffset = u8;
109
110/// A type set fixing the register types used in the assembler.
111///
112/// This assembler is parameterizable over register types; this allows the
113/// assembler users (e.g., Cranelift) to define their own register types
114/// independent of this crate.
115pub trait Registers {
116    /// An x64 general purpose register that may be read.
117    type ReadGpr: AsReg;
118
119    /// An x64 general purpose register that may be read and written.
120    type ReadWriteGpr: AsReg;
121
122    /// An x64 general purpose register that may be written.
123    type WriteGpr: AsReg;
124
125    /// An x64 SSE register that may be read.
126    type ReadXmm: AsReg;
127
128    /// An x64 SSE register that may be read and written.
129    type ReadWriteXmm: AsReg;
130
131    /// An x64 SSE register that may be written.
132    type WriteXmm: AsReg;
133}
134
135/// Describe how to interact with an external register type.
136pub trait AsReg: Copy + Clone + std::fmt::Debug + PartialEq {
137    /// Create a register from its hardware encoding.
138    ///
139    /// This is primarily useful for fuzzing, though it is also useful for
140    /// generating fixed registers.
141    fn new(enc: u8) -> Self;
142
143    /// Return the register's hardware encoding; e.g., `0` for `%rax`.
144    fn enc(&self) -> u8;
145
146    /// Return the register name.
147    fn to_string(&self, size: Option<gpr::Size>) -> String {
148        match size {
149            Some(size) => gpr::enc::to_string(self.enc(), size).into(),
150            None => xmm::enc::to_string(self.enc()).into(),
151        }
152    }
153}
154
155/// Provide a convenient implementation for testing.
156impl AsReg for u8 {
157    fn new(enc: u8) -> Self {
158        enc
159    }
160    fn enc(&self) -> u8 {
161        *self
162    }
163}
164
165/// Describe a visitor for the register operands of an instruction.
166///
167/// Due to how Cranelift's register allocation works, we allow the visitor to
168/// modify the register operands in place. This allows Cranelift to convert
169/// virtual registers (`[128..N)`) to physical registers (`[0..16)`) without
170/// re-allocating the entire instruction object.
171pub trait RegisterVisitor<R: Registers> {
172    /// Visit a read-only register.
173    fn read_gpr(&mut self, reg: &mut R::ReadGpr);
174    /// Visit a read-write register.
175    fn read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr);
176    /// Visit a write-only register.
177    fn write_gpr(&mut self, reg: &mut R::WriteGpr);
178
179    /// Visit a read-only fixed register; this register can be modified in-place
180    /// but must emit as the hardware encoding `enc`.
181    fn fixed_read_gpr(&mut self, reg: &mut R::ReadGpr, enc: u8);
182    /// Visit a read-write fixed register; this register can be modified
183    /// in-place but must emit as the hardware encoding `enc`.
184    fn fixed_read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr, enc: u8);
185    /// Visit a write-only fixed register; this register can be modified
186    /// in-place but must emit as the hardware encoding `enc`.
187    fn fixed_write_gpr(&mut self, reg: &mut R::WriteGpr, enc: u8);
188
189    /// Visit a read-only SSE register.
190    fn read_xmm(&mut self, reg: &mut R::ReadXmm);
191    /// Visit a read-write SSE register.
192    fn read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm);
193    /// Visit a write-only SSE register.
194    fn write_xmm(&mut self, reg: &mut R::WriteXmm);
195
196    /// Visit a read-only fixed SSE register; this register can be modified
197    /// in-place but must emit as the hardware encoding `enc`.
198    fn fixed_read_xmm(&mut self, reg: &mut R::ReadXmm, enc: u8);
199    /// Visit a read-write fixed SSE register; this register can be modified
200    /// in-place but must emit as the hardware encoding `enc`.
201    fn fixed_read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm, enc: u8);
202    /// Visit a read-only fixed SSE register; this register can be modified
203    /// in-place but must emit as the hardware encoding `enc`.
204    fn fixed_write_xmm(&mut self, reg: &mut R::WriteXmm, enc: u8);
205
206    /// Visit the registers in an [`Amode`].
207    ///
208    /// This is helpful for generated code: it allows capturing the `R::ReadGpr`
209    /// type (which an `Amode` method cannot) and simplifies the code to be
210    /// generated.
211    fn read_amode(&mut self, amode: &mut Amode<R::ReadGpr>) {
212        match amode {
213            Amode::ImmReg { base, .. } => {
214                self.read_gpr(base);
215            }
216            Amode::ImmRegRegShift { base, index, .. } => {
217                self.read_gpr(base);
218                self.read_gpr(index.as_mut());
219            }
220            Amode::RipRelative { .. } => {}
221        }
222    }
223
224    /// Helper method to handle a read/write [`GprMem`] operand.
225    fn read_write_gpr_mem(&mut self, op: &mut GprMem<R::ReadWriteGpr, R::ReadGpr>) {
226        match op {
227            GprMem::Gpr(r) => self.read_write_gpr(r),
228            GprMem::Mem(m) => self.read_amode(m),
229        }
230    }
231
232    /// Helper method to handle a write [`GprMem`] operand.
233    fn write_gpr_mem(&mut self, op: &mut GprMem<R::WriteGpr, R::ReadGpr>) {
234        match op {
235            GprMem::Gpr(r) => self.write_gpr(r),
236            GprMem::Mem(m) => self.read_amode(m),
237        }
238    }
239
240    /// Helper method to handle a read-only [`GprMem`] operand.
241    fn read_gpr_mem(&mut self, op: &mut GprMem<R::ReadGpr, R::ReadGpr>) {
242        match op {
243            GprMem::Gpr(r) => self.read_gpr(r),
244            GprMem::Mem(m) => self.read_amode(m),
245        }
246    }
247
248    /// Helper method to handle a read-only [`XmmMem`] operand.
249    fn read_xmm_mem(&mut self, op: &mut XmmMem<R::ReadXmm, R::ReadGpr>) {
250        match op {
251            XmmMem::Xmm(r) => self.read_xmm(r),
252            XmmMem::Mem(m) => self.read_amode(m),
253        }
254    }
255
256    /// Helper method to handle a write [`XmmMem`] operand.
257    fn write_xmm_mem(&mut self, op: &mut XmmMem<R::WriteXmm, R::ReadGpr>) {
258        match op {
259            XmmMem::Xmm(r) => self.write_xmm(r),
260            XmmMem::Mem(m) => self.read_amode(m),
261        }
262    }
263}