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 std::fmt;
7 use std::{num::NonZeroU8, vec::Vec};
8 
9 /// Describe how an instruction is emitted into a code buffer.
10 pub 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.
39 impl 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))]
68 pub 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))]
73 pub 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))]
78 pub struct TrapCode(pub NonZeroU8);
79 
80 impl 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.
88 pub 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.
95 pub 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.
116 pub 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.
136 impl 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.
151 pub 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 }
244