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