1 //! Contains traits that a user of this assembler must implement. 2 3 use crate::gpr; 4 use crate::xmm; 5 use crate::{Amode, GprMem, XmmMem}; 6 use std::fmt; 7 use std::{num::NonZeroU8, ops::Index, 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 /// 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. 41 impl 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))] 74 pub 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))] 79 pub 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))] 84 pub struct TrapCode(pub NonZeroU8); 85 86 impl 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`. 101 pub trait KnownOffsetTable: Index<usize, Output = i32> {} 102 impl KnownOffsetTable for Vec<i32> {} 103 /// Provide a convenient implementation for testing. 104 impl KnownOffsetTable for [i32; 2] {} 105 106 /// A `KnownOffset` is a unique identifier for a specific offset known only at 107 /// emission time. 108 pub 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. 115 pub 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. 136 pub 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. 156 impl 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. 171 pub 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 } 264