Path: blob/main/cranelift/assembler-x64/src/api.rs
1692 views
//! Contains traits that a user of this assembler must implement.12use crate::gpr;3use crate::xmm;4use crate::{Amode, DeferredTarget, GprMem, XmmMem};5use std::fmt;6use std::{num::NonZeroU8, vec::Vec};78/// Describe how an instruction is emitted into a code buffer.9pub trait CodeSink {10/// Add 1 byte to the code section.11fn put1(&mut self, _: u8);1213/// Add 2 bytes to the code section.14fn put2(&mut self, _: u16);1516/// Add 4 bytes to the code section.17fn put4(&mut self, _: u32);1819/// Add 8 bytes to the code section.20fn put8(&mut self, _: u64);2122/// Inform the code buffer of a possible trap at the current location;23/// required for assembling memory accesses.24fn add_trap(&mut self, code: TrapCode);2526/// Inform the code buffer that a use of `target` is about to happen at the27/// current offset.28///29/// After this method is called the bytes of the target are then expected to30/// be placed using one of the above `put*` methods.31fn use_target(&mut self, target: DeferredTarget);3233/// Resolves a `KnownOffset` value to the actual signed offset.34fn known_offset(&self, offset: KnownOffset) -> i32;35}3637/// Provide a convenient implementation for testing.38impl CodeSink for Vec<u8> {39fn put1(&mut self, v: u8) {40self.extend_from_slice(&[v]);41}4243fn put2(&mut self, v: u16) {44self.extend_from_slice(&v.to_le_bytes());45}4647fn put4(&mut self, v: u32) {48self.extend_from_slice(&v.to_le_bytes());49}5051fn put8(&mut self, v: u64) {52self.extend_from_slice(&v.to_le_bytes());53}5455fn add_trap(&mut self, _: TrapCode) {}5657fn use_target(&mut self, _: DeferredTarget) {}5859fn known_offset(&self, offset: KnownOffset) -> i32 {60panic!("unknown offset {offset:?}")61}62}6364/// Wrap [`CodeSink`]-specific labels.65#[derive(Debug, Copy, Clone, PartialEq)]66#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]67pub struct Label(pub u32);6869/// Wrap [`CodeSink`]-specific constant keys.70#[derive(Debug, Copy, Clone, PartialEq)]71#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]72pub struct Constant(pub u32);7374/// Wrap [`CodeSink`]-specific trap codes.75#[derive(Debug, Clone, Copy, PartialEq)]76#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]77pub struct TrapCode(pub NonZeroU8);7879impl fmt::Display for TrapCode {80fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {81write!(f, "trap={}", self.0)82}83}8485/// A `KnownOffset` is a unique identifier for a specific offset known only at86/// emission time.87pub type KnownOffset = u8;8889/// A type set fixing the register types used in the assembler.90///91/// This assembler is parameterizable over register types; this allows the92/// assembler users (e.g., Cranelift) to define their own register types93/// independent of this crate.94pub trait Registers {95/// An x64 general purpose register that may be read.96type ReadGpr: AsReg;9798/// An x64 general purpose register that may be read and written.99type ReadWriteGpr: AsReg;100101/// An x64 general purpose register that may be written.102type WriteGpr: AsReg;103104/// An x64 SSE register that may be read.105type ReadXmm: AsReg;106107/// An x64 SSE register that may be read and written.108type ReadWriteXmm: AsReg;109110/// An x64 SSE register that may be written.111type WriteXmm: AsReg;112}113114/// Describe how to interact with an external register type.115pub trait AsReg: Copy + Clone + std::fmt::Debug + PartialEq {116/// Create a register from its hardware encoding.117///118/// This is primarily useful for fuzzing, though it is also useful for119/// generating fixed registers.120fn new(enc: u8) -> Self;121122/// Return the register's hardware encoding; e.g., `0` for `%rax`.123fn enc(&self) -> u8;124125/// Return the register name.126fn to_string(&self, size: Option<gpr::Size>) -> String {127match size {128Some(size) => gpr::enc::to_string(self.enc(), size).into(),129None => xmm::enc::to_string(self.enc()).into(),130}131}132}133134/// Provide a convenient implementation for testing.135impl AsReg for u8 {136fn new(enc: u8) -> Self {137enc138}139fn enc(&self) -> u8 {140*self141}142}143144/// Describe a visitor for the register operands of an instruction.145///146/// Due to how Cranelift's register allocation works, we allow the visitor to147/// modify the register operands in place. This allows Cranelift to convert148/// virtual registers (`[128..N)`) to physical registers (`[0..16)`) without149/// re-allocating the entire instruction object.150pub trait RegisterVisitor<R: Registers> {151/// Visit a read-only register.152fn read_gpr(&mut self, reg: &mut R::ReadGpr);153/// Visit a read-write register.154fn read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr);155/// Visit a write-only register.156fn write_gpr(&mut self, reg: &mut R::WriteGpr);157158/// Visit a read-only fixed register; this register can be modified in-place159/// but must emit as the hardware encoding `enc`.160fn fixed_read_gpr(&mut self, reg: &mut R::ReadGpr, enc: u8);161/// Visit a read-write fixed register; this register can be modified162/// in-place but must emit as the hardware encoding `enc`.163fn fixed_read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr, enc: u8);164/// Visit a write-only fixed register; this register can be modified165/// in-place but must emit as the hardware encoding `enc`.166fn fixed_write_gpr(&mut self, reg: &mut R::WriteGpr, enc: u8);167168/// Visit a read-only SSE register.169fn read_xmm(&mut self, reg: &mut R::ReadXmm);170/// Visit a read-write SSE register.171fn read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm);172/// Visit a write-only SSE register.173fn write_xmm(&mut self, reg: &mut R::WriteXmm);174175/// Visit a read-only fixed SSE register; this register can be modified176/// in-place but must emit as the hardware encoding `enc`.177fn fixed_read_xmm(&mut self, reg: &mut R::ReadXmm, enc: u8);178/// Visit a read-write fixed SSE register; this register can be modified179/// in-place but must emit as the hardware encoding `enc`.180fn fixed_read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm, enc: u8);181/// Visit a read-only fixed SSE register; this register can be modified182/// in-place but must emit as the hardware encoding `enc`.183fn fixed_write_xmm(&mut self, reg: &mut R::WriteXmm, enc: u8);184185/// Visit the registers in an [`Amode`].186///187/// This is helpful for generated code: it allows capturing the `R::ReadGpr`188/// type (which an `Amode` method cannot) and simplifies the code to be189/// generated.190fn read_amode(&mut self, amode: &mut Amode<R::ReadGpr>) {191match amode {192Amode::ImmReg { base, .. } => {193self.read_gpr(base);194}195Amode::ImmRegRegShift { base, index, .. } => {196self.read_gpr(base);197self.read_gpr(index.as_mut());198}199Amode::RipRelative { .. } => {}200}201}202203/// Helper method to handle a read/write [`GprMem`] operand.204fn read_write_gpr_mem(&mut self, op: &mut GprMem<R::ReadWriteGpr, R::ReadGpr>) {205match op {206GprMem::Gpr(r) => self.read_write_gpr(r),207GprMem::Mem(m) => self.read_amode(m),208}209}210211/// Helper method to handle a write [`GprMem`] operand.212fn write_gpr_mem(&mut self, op: &mut GprMem<R::WriteGpr, R::ReadGpr>) {213match op {214GprMem::Gpr(r) => self.write_gpr(r),215GprMem::Mem(m) => self.read_amode(m),216}217}218219/// Helper method to handle a read-only [`GprMem`] operand.220fn read_gpr_mem(&mut self, op: &mut GprMem<R::ReadGpr, R::ReadGpr>) {221match op {222GprMem::Gpr(r) => self.read_gpr(r),223GprMem::Mem(m) => self.read_amode(m),224}225}226227/// Helper method to handle a read-only [`XmmMem`] operand.228fn read_xmm_mem(&mut self, op: &mut XmmMem<R::ReadXmm, R::ReadGpr>) {229match op {230XmmMem::Xmm(r) => self.read_xmm(r),231XmmMem::Mem(m) => self.read_amode(m),232}233}234235/// Helper method to handle a write [`XmmMem`] operand.236fn write_xmm_mem(&mut self, op: &mut XmmMem<R::WriteXmm, R::ReadGpr>) {237match op {238XmmMem::Xmm(r) => self.write_xmm(r),239XmmMem::Mem(m) => self.read_amode(m),240}241}242}243244245