1 //! Contains traits that a user of this assembler must implement.
2 
3 use crate::gpr;
4 use crate::xmm;
5 use crate::{Amode, DeferredTarget, GprMem, XmmMem};
6 use alloc::string::String;
7 use alloc::vec::Vec;
8 use core::fmt;
9 use core::num::NonZeroU8;
10 
11 /// Describe how an instruction is emitted into a code buffer.
12 pub trait CodeSink {
13     /// Add 1 byte to the code section.
put1(&mut self, _: u8)14     fn put1(&mut self, _: u8);
15 
16     /// Add 2 bytes to the code section.
put2(&mut self, _: u16)17     fn put2(&mut self, _: u16);
18 
19     /// Add 4 bytes to the code section.
put4(&mut self, _: u32)20     fn put4(&mut self, _: u32);
21 
22     /// Add 8 bytes to the code section.
put8(&mut self, _: u64)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.
add_trap(&mut self, code: TrapCode)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.
use_target(&mut self, target: DeferredTarget)34     fn use_target(&mut self, target: DeferredTarget);
35 
36     /// Resolves a `KnownOffset` value to the actual signed offset.
known_offset(&self, offset: KnownOffset) -> i3237     fn known_offset(&self, offset: KnownOffset) -> i32;
38 }
39 
40 /// Provide a convenient implementation for testing.
41 impl CodeSink for Vec<u8> {
put1(&mut self, v: u8)42     fn put1(&mut self, v: u8) {
43         self.extend_from_slice(&[v]);
44     }
45 
put2(&mut self, v: u16)46     fn put2(&mut self, v: u16) {
47         self.extend_from_slice(&v.to_le_bytes());
48     }
49 
put4(&mut self, v: u32)50     fn put4(&mut self, v: u32) {
51         self.extend_from_slice(&v.to_le_bytes());
52     }
53 
put8(&mut self, v: u64)54     fn put8(&mut self, v: u64) {
55         self.extend_from_slice(&v.to_le_bytes());
56     }
57 
add_trap(&mut self, _: TrapCode)58     fn add_trap(&mut self, _: TrapCode) {}
59 
use_target(&mut self, _: DeferredTarget)60     fn use_target(&mut self, _: DeferredTarget) {}
61 
known_offset(&self, offset: KnownOffset) -> i3262     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))]
70 pub 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))]
75 pub 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))]
80 pub struct TrapCode(pub NonZeroU8);
81 
82 impl fmt::Display for TrapCode {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result83     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.
90 pub 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.
97 pub 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.
118 pub 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.
new(enc: u8) -> Self123     fn new(enc: u8) -> Self;
124 
125     /// Return the register's hardware encoding; e.g., `0` for `%rax`.
enc(&self) -> u8126     fn enc(&self) -> u8;
127 
128     /// Return the register name.
to_string(&self, size: Option<gpr::Size>) -> String129     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.
138 impl AsReg for u8 {
new(enc: u8) -> Self139     fn new(enc: u8) -> Self {
140         enc
141     }
enc(&self) -> u8142     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.
153 pub trait RegisterVisitor<R: Registers> {
154     /// Visit a read-only register.
read_gpr(&mut self, reg: &mut R::ReadGpr)155     fn read_gpr(&mut self, reg: &mut R::ReadGpr);
156     /// Visit a read-write register.
read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr)157     fn read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr);
158     /// Visit a write-only register.
write_gpr(&mut self, reg: &mut R::WriteGpr)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`.
fixed_read_gpr(&mut self, reg: &mut R::ReadGpr, enc: u8)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`.
fixed_read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr, enc: u8)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`.
fixed_write_gpr(&mut self, reg: &mut R::WriteGpr, enc: u8)169     fn fixed_write_gpr(&mut self, reg: &mut R::WriteGpr, enc: u8);
170 
171     /// Visit a read-only SSE register.
read_xmm(&mut self, reg: &mut R::ReadXmm)172     fn read_xmm(&mut self, reg: &mut R::ReadXmm);
173     /// Visit a read-write SSE register.
read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm)174     fn read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm);
175     /// Visit a write-only SSE register.
write_xmm(&mut self, reg: &mut R::WriteXmm)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`.
fixed_read_xmm(&mut self, reg: &mut R::ReadXmm, enc: u8)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`.
fixed_read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm, enc: u8)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`.
fixed_write_xmm(&mut self, reg: &mut R::WriteXmm, enc: u8)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.
read_amode(&mut self, amode: &mut Amode<R::ReadGpr>)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.
read_write_gpr_mem(&mut self, op: &mut GprMem<R::ReadWriteGpr, R::ReadGpr>)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.
write_gpr_mem(&mut self, op: &mut GprMem<R::WriteGpr, R::ReadGpr>)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.
read_gpr_mem(&mut self, op: &mut GprMem<R::ReadGpr, R::ReadGpr>)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.
read_xmm_mem(&mut self, op: &mut XmmMem<R::ReadXmm, R::ReadGpr>)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.
write_xmm_mem(&mut self, op: &mut XmmMem<R::WriteXmm, R::ReadGpr>)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 }
246