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