Path: blob/main/cranelift/assembler-x64/src/api.rs
3050 views
//! Contains traits that a user of this assembler must implement.12use crate::gpr;3use crate::xmm;4use crate::{Amode, DeferredTarget, GprMem, XmmMem};5use alloc::string::String;6use alloc::vec::Vec;7use core::fmt;8use core::num::NonZeroU8;910/// Describe how an instruction is emitted into a code buffer.11pub trait CodeSink {12/// Add 1 byte to the code section.13fn put1(&mut self, _: u8);1415/// Add 2 bytes to the code section.16fn put2(&mut self, _: u16);1718/// Add 4 bytes to the code section.19fn put4(&mut self, _: u32);2021/// Add 8 bytes to the code section.22fn put8(&mut self, _: u64);2324/// Inform the code buffer of a possible trap at the current location;25/// required for assembling memory accesses.26fn add_trap(&mut self, code: TrapCode);2728/// Inform the code buffer that a use of `target` is about to happen at the29/// current offset.30///31/// After this method is called the bytes of the target are then expected to32/// be placed using one of the above `put*` methods.33fn use_target(&mut self, target: DeferredTarget);3435/// Resolves a `KnownOffset` value to the actual signed offset.36fn known_offset(&self, offset: KnownOffset) -> i32;37}3839/// Provide a convenient implementation for testing.40impl CodeSink for Vec<u8> {41fn put1(&mut self, v: u8) {42self.extend_from_slice(&[v]);43}4445fn put2(&mut self, v: u16) {46self.extend_from_slice(&v.to_le_bytes());47}4849fn put4(&mut self, v: u32) {50self.extend_from_slice(&v.to_le_bytes());51}5253fn put8(&mut self, v: u64) {54self.extend_from_slice(&v.to_le_bytes());55}5657fn add_trap(&mut self, _: TrapCode) {}5859fn use_target(&mut self, _: DeferredTarget) {}6061fn known_offset(&self, offset: KnownOffset) -> i32 {62panic!("unknown offset {offset:?}")63}64}6566/// Wrap [`CodeSink`]-specific labels.67#[derive(Debug, Copy, Clone, PartialEq)]68#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]69pub struct Label(pub u32);7071/// Wrap [`CodeSink`]-specific constant keys.72#[derive(Debug, Copy, Clone, PartialEq)]73#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]74pub struct Constant(pub u32);7576/// Wrap [`CodeSink`]-specific trap codes.77#[derive(Debug, Clone, Copy, PartialEq)]78#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]79pub struct TrapCode(pub NonZeroU8);8081impl fmt::Display for TrapCode {82fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {83write!(f, "trap={}", self.0)84}85}8687/// A `KnownOffset` is a unique identifier for a specific offset known only at88/// emission time.89pub type KnownOffset = u8;9091/// A type set fixing the register types used in the assembler.92///93/// This assembler is parameterizable over register types; this allows the94/// assembler users (e.g., Cranelift) to define their own register types95/// independent of this crate.96pub trait Registers {97/// An x64 general purpose register that may be read.98type ReadGpr: AsReg;99100/// An x64 general purpose register that may be read and written.101type ReadWriteGpr: AsReg;102103/// An x64 general purpose register that may be written.104type WriteGpr: AsReg;105106/// An x64 SSE register that may be read.107type ReadXmm: AsReg;108109/// An x64 SSE register that may be read and written.110type ReadWriteXmm: AsReg;111112/// An x64 SSE register that may be written.113type WriteXmm: AsReg;114}115116/// Describe how to interact with an external register type.117pub trait AsReg: Copy + Clone + core::fmt::Debug + PartialEq {118/// Create a register from its hardware encoding.119///120/// This is primarily useful for fuzzing, though it is also useful for121/// generating fixed registers.122fn new(enc: u8) -> Self;123124/// Return the register's hardware encoding; e.g., `0` for `%rax`.125fn enc(&self) -> u8;126127/// Return the register name.128fn to_string(&self, size: Option<gpr::Size>) -> String {129match size {130Some(size) => gpr::enc::to_string(self.enc(), size).into(),131None => xmm::enc::to_string(self.enc()).into(),132}133}134}135136/// Provide a convenient implementation for testing.137impl AsReg for u8 {138fn new(enc: u8) -> Self {139enc140}141fn enc(&self) -> u8 {142*self143}144}145146/// Describe a visitor for the register operands of an instruction.147///148/// Due to how Cranelift's register allocation works, we allow the visitor to149/// modify the register operands in place. This allows Cranelift to convert150/// virtual registers (`[128..N)`) to physical registers (`[0..16)`) without151/// re-allocating the entire instruction object.152pub trait RegisterVisitor<R: Registers> {153/// Visit a read-only register.154fn read_gpr(&mut self, reg: &mut R::ReadGpr);155/// Visit a read-write register.156fn read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr);157/// Visit a write-only register.158fn write_gpr(&mut self, reg: &mut R::WriteGpr);159160/// Visit a read-only fixed register; this register can be modified in-place161/// but must emit as the hardware encoding `enc`.162fn fixed_read_gpr(&mut self, reg: &mut R::ReadGpr, enc: u8);163/// Visit a read-write fixed register; this register can be modified164/// in-place but must emit as the hardware encoding `enc`.165fn fixed_read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr, enc: u8);166/// Visit a write-only fixed register; this register can be modified167/// in-place but must emit as the hardware encoding `enc`.168fn fixed_write_gpr(&mut self, reg: &mut R::WriteGpr, enc: u8);169170/// Visit a read-only SSE register.171fn read_xmm(&mut self, reg: &mut R::ReadXmm);172/// Visit a read-write SSE register.173fn read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm);174/// Visit a write-only SSE register.175fn write_xmm(&mut self, reg: &mut R::WriteXmm);176177/// Visit a read-only fixed SSE register; this register can be modified178/// in-place but must emit as the hardware encoding `enc`.179fn fixed_read_xmm(&mut self, reg: &mut R::ReadXmm, enc: u8);180/// Visit a read-write fixed SSE register; this register can be modified181/// in-place but must emit as the hardware encoding `enc`.182fn fixed_read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm, enc: u8);183/// Visit a read-only fixed SSE register; this register can be modified184/// in-place but must emit as the hardware encoding `enc`.185fn fixed_write_xmm(&mut self, reg: &mut R::WriteXmm, enc: u8);186187/// Visit the registers in an [`Amode`].188///189/// This is helpful for generated code: it allows capturing the `R::ReadGpr`190/// type (which an `Amode` method cannot) and simplifies the code to be191/// generated.192fn read_amode(&mut self, amode: &mut Amode<R::ReadGpr>) {193match amode {194Amode::ImmReg { base, .. } => {195self.read_gpr(base);196}197Amode::ImmRegRegShift { base, index, .. } => {198self.read_gpr(base);199self.read_gpr(index.as_mut());200}201Amode::RipRelative { .. } => {}202}203}204205/// Helper method to handle a read/write [`GprMem`] operand.206fn read_write_gpr_mem(&mut self, op: &mut GprMem<R::ReadWriteGpr, R::ReadGpr>) {207match op {208GprMem::Gpr(r) => self.read_write_gpr(r),209GprMem::Mem(m) => self.read_amode(m),210}211}212213/// Helper method to handle a write [`GprMem`] operand.214fn write_gpr_mem(&mut self, op: &mut GprMem<R::WriteGpr, R::ReadGpr>) {215match op {216GprMem::Gpr(r) => self.write_gpr(r),217GprMem::Mem(m) => self.read_amode(m),218}219}220221/// Helper method to handle a read-only [`GprMem`] operand.222fn read_gpr_mem(&mut self, op: &mut GprMem<R::ReadGpr, R::ReadGpr>) {223match op {224GprMem::Gpr(r) => self.read_gpr(r),225GprMem::Mem(m) => self.read_amode(m),226}227}228229/// Helper method to handle a read-only [`XmmMem`] operand.230fn read_xmm_mem(&mut self, op: &mut XmmMem<R::ReadXmm, R::ReadGpr>) {231match op {232XmmMem::Xmm(r) => self.read_xmm(r),233XmmMem::Mem(m) => self.read_amode(m),234}235}236237/// Helper method to handle a write [`XmmMem`] operand.238fn write_xmm_mem(&mut self, op: &mut XmmMem<R::WriteXmm, R::ReadGpr>) {239match op {240XmmMem::Xmm(r) => self.write_xmm(r),241XmmMem::Mem(m) => self.read_amode(m),242}243}244}245246247