use crate::binemit::{Addend, CodeInfo, CodeOffset, Reloc};
use crate::ir::{
self, DynamicStackSlot, Endianness, RelSourceLoc, StackSlot, TrapCode, Type,
function::FunctionParameters,
};
use crate::isa::FunctionAlignment;
use crate::result::CodegenResult;
use crate::settings;
use crate::settings::Flags;
use crate::value_label::ValueLabelsRanges;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use core::fmt::Debug;
use core::num::NonZeroU8;
use cranelift_control::ControlPlane;
use cranelift_entity::PrimaryMap;
use regalloc2::VReg;
use smallvec::{SmallVec, smallvec};
#[cfg(feature = "enable-serde")]
use serde_derive::{Deserialize, Serialize};
const BIT_ALIGNED: u16 = 1 << 0;
const BIT_READONLY: u16 = 1 << 1;
const BIT_LITTLE_ENDIAN: u16 = 1 << 2;
const BIT_BIG_ENDIAN: u16 = 1 << 3;
const MASK_TRAP_CODE: u16 = ((1 << TRAP_CODE_BITS) - 1) << TRAP_CODE_OFFSET;
const TRAP_CODE_BITS: u16 = 8;
const TRAP_CODE_OFFSET: u16 = 7;
const BIT_CAN_MOVE: u16 = 1 << 15;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct MachMemFlags {
bits: u16,
}
impl MachMemFlags {
pub const fn new() -> Self {
Self { bits: 0 }.with_trap_code(Some(TrapCode::HEAP_OUT_OF_BOUNDS))
}
pub const fn trusted() -> Self {
Self::new().with_notrap().with_aligned()
}
const fn read_bit(self, bit: u16) -> bool {
self.bits & bit != 0
}
const fn with_bit(mut self, bit: u16) -> Self {
self.bits |= bit;
self
}
pub const fn endianness(self, native_endianness: Endianness) -> Endianness {
if self.read_bit(BIT_LITTLE_ENDIAN) {
Endianness::Little
} else if self.read_bit(BIT_BIG_ENDIAN) {
Endianness::Big
} else {
native_endianness
}
}
pub const fn explicit_endianness(self) -> Option<Endianness> {
if self.read_bit(BIT_LITTLE_ENDIAN) {
Some(Endianness::Little)
} else if self.read_bit(BIT_BIG_ENDIAN) {
Some(Endianness::Big)
} else {
None
}
}
pub const fn with_endianness(self, endianness: Endianness) -> Self {
let res = match endianness {
Endianness::Little => self.with_bit(BIT_LITTLE_ENDIAN),
Endianness::Big => self.with_bit(BIT_BIG_ENDIAN),
};
assert!(!(res.read_bit(BIT_LITTLE_ENDIAN) && res.read_bit(BIT_BIG_ENDIAN)));
res
}
pub const fn notrap(self) -> bool {
self.trap_code().is_none()
}
pub const fn with_notrap(self) -> Self {
self.with_trap_code(None)
}
pub const fn can_move(self) -> bool {
self.read_bit(BIT_CAN_MOVE)
}
pub const fn with_can_move(self) -> Self {
self.with_bit(BIT_CAN_MOVE)
}
pub const fn aligned(self) -> bool {
self.read_bit(BIT_ALIGNED)
}
pub const fn with_aligned(self) -> Self {
self.with_bit(BIT_ALIGNED)
}
pub const fn readonly(self) -> bool {
self.read_bit(BIT_READONLY)
}
pub const fn with_readonly(self) -> Self {
self.with_bit(BIT_READONLY)
}
pub const fn trap_code(self) -> Option<TrapCode> {
let byte = ((self.bits & MASK_TRAP_CODE) >> TRAP_CODE_OFFSET) as u8;
match NonZeroU8::new(byte) {
Some(code) => Some(TrapCode::from_raw(code)),
None => None,
}
}
pub const fn with_trap_code(mut self, code: Option<TrapCode>) -> Self {
let bits = match code {
Some(code) => code.as_raw().get() as u16,
None => 0,
};
self.bits &= !MASK_TRAP_CODE;
self.bits |= bits << TRAP_CODE_OFFSET;
self
}
}
impl fmt::Display for MachMemFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.trap_code() {
None => write!(f, " notrap")?,
Some(TrapCode::HEAP_OUT_OF_BOUNDS) => {}
Some(t) => write!(f, " {t}")?,
}
if self.aligned() {
write!(f, " aligned")?;
}
if self.readonly() {
write!(f, " readonly")?;
}
if self.can_move() {
write!(f, " can_move")?;
}
if self.read_bit(BIT_BIG_ENDIAN) {
write!(f, " big")?;
}
if self.read_bit(BIT_LITTLE_ENDIAN) {
write!(f, " little")?;
}
Ok(())
}
}
#[macro_use]
pub mod isle;
pub mod lower;
pub use lower::*;
pub mod vcode;
pub use vcode::*;
pub mod compile;
pub use compile::*;
pub mod blockorder;
pub use blockorder::*;
pub mod abi;
pub use abi::*;
pub mod buffer;
pub use buffer::*;
pub mod helpers;
pub use helpers::*;
pub mod valueregs;
pub use reg::*;
pub use valueregs::*;
pub mod reg;
pub trait MachInst: Clone + Debug {
type ABIMachineSpec: ABIMachineSpec<I = Self>;
fn get_operands(&mut self, collector: &mut impl OperandVisitor);
fn is_move(&self) -> Option<(Writable<Reg>, Reg)>;
fn is_term(&self) -> MachTerminator;
fn is_trap(&self) -> bool;
fn is_args(&self) -> bool;
fn call_type(&self) -> CallType;
fn is_included_in_clobbers(&self) -> bool;
fn is_mem_access(&self) -> bool;
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Self;
fn gen_dummy_use(reg: Reg) -> Self;
fn rc_for_type(ty: Type) -> CodegenResult<(&'static [RegClass], &'static [Type])>;
fn canonical_type_for_rc(rc: RegClass) -> Type;
fn gen_jump(target: MachLabel) -> Self;
fn gen_imm_u64(_value: u64, _dst: Writable<Reg>) -> Option<Self> {
None
}
fn gen_imm_f64(_value: f64, _tmp: Writable<Reg>, _dst: Writable<Reg>) -> SmallVec<[Self; 2]> {
SmallVec::new()
}
fn gen_nop(preferred_size: usize) -> Self;
fn gen_nop_units() -> Vec<Vec<u8>>;
fn align_basic_block(offset: CodeOffset) -> CodeOffset {
offset
}
fn worst_case_size() -> CodeOffset;
fn worst_case_island_growth() -> CodeOffset;
fn ref_type_regclass(_flags: &Flags) -> RegClass;
fn is_safepoint(&self) -> bool;
fn gen_block_start(
_is_indirect_branch_target: bool,
_is_forward_edge_cfi_enabled: bool,
) -> Option<Self> {
None
}
fn function_alignment() -> FunctionAlignment;
fn is_low_level_branch(&self) -> bool {
false
}
type LabelUse: MachInstLabelUse;
const TRAP_OPCODE: &'static [u8];
}
pub trait MachInstLabelUse: Clone + Copy + Debug + Eq {
const ALIGN: CodeOffset;
fn max_pos_range(self) -> CodeOffset;
fn max_neg_range(self) -> CodeOffset;
fn patch_size(self) -> CodeOffset;
fn patch(self, buffer: &mut [u8], use_offset: CodeOffset, label_offset: CodeOffset);
fn supports_veneer(self) -> bool;
fn veneer_size(self) -> CodeOffset;
fn worst_case_veneer_size() -> CodeOffset;
fn generate_veneer(self, buffer: &mut [u8], veneer_offset: CodeOffset) -> (CodeOffset, Self);
fn from_reloc(reloc: Reloc, addend: Addend) -> Option<Self>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CallType {
None,
Regular,
TailCall,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum FunctionCalls {
#[default]
None,
TailOnly,
Regular,
}
impl FunctionCalls {
pub fn update(&mut self, call_type: CallType) {
*self = match (*self, call_type) {
(current, CallType::None) => current,
(_, CallType::Regular) => FunctionCalls::Regular,
(FunctionCalls::None, CallType::TailCall) => FunctionCalls::TailOnly,
(current, CallType::TailCall) => current,
};
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MachTerminator {
None,
Ret,
RetCall,
Branch,
}
pub trait MachInstEmit: MachInst {
type State: MachInstEmitState<Self>;
type Info;
fn emit(&self, code: &mut MachBuffer<Self>, info: &Self::Info, state: &mut Self::State);
fn pretty_print_inst(&self, state: &mut Self::State) -> String;
}
pub trait MachInstEmitState<I: VCodeInst>: Default + Clone + Debug {
fn new(abi: &Callee<I::ABIMachineSpec>, ctrl_plane: ControlPlane) -> Self;
fn pre_safepoint(&mut self, user_stack_map: Option<ir::UserStackMap>);
fn ctrl_plane_mut(&mut self) -> &mut ControlPlane;
fn take_ctrl_plane(self) -> ControlPlane;
fn on_new_block(&mut self) {}
fn frame_layout(&self) -> &FrameLayout;
}
#[derive(PartialEq, Debug, Clone)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct CompiledCodeBase<T: CompilePhase> {
pub buffer: MachBufferFinalized<T>,
pub vcode: Option<String>,
pub value_labels_ranges: ValueLabelsRanges,
pub bb_starts: Vec<CodeOffset>,
pub bb_edges: Vec<(CodeOffset, CodeOffset)>,
}
impl CompiledCodeStencil {
pub fn apply_params(self, params: &FunctionParameters) -> CompiledCode {
CompiledCode {
buffer: self.buffer.apply_base_srcloc(params.base_srcloc()),
vcode: self.vcode,
value_labels_ranges: self.value_labels_ranges,
bb_starts: self.bb_starts,
bb_edges: self.bb_edges,
}
}
}
impl<T: CompilePhase> CompiledCodeBase<T> {
pub fn code_info(&self) -> CodeInfo {
CodeInfo {
total_size: self.buffer.total_size(),
}
}
pub fn code_buffer(&self) -> &[u8] {
self.buffer.data()
}
#[cfg(feature = "disas")]
pub fn disassemble(
&self,
params: Option<&crate::ir::function::FunctionParameters>,
cs: &capstone::Capstone,
) -> Result<String, anyhow::Error> {
use core::fmt::Write;
let mut buf = String::new();
let relocs = self.buffer.relocs();
let traps = self.buffer.traps();
let mut patchables = self.buffer.patchable_call_sites().peekable();
let mut block_starts = Vec::new();
if self.bb_starts.first().copied() != Some(0) {
block_starts.push(0);
}
block_starts.extend_from_slice(&self.bb_starts);
block_starts.push(self.buffer.data().len() as u32);
for (n, (&start, &end)) in block_starts
.iter()
.zip(block_starts.iter().skip(1))
.enumerate()
{
writeln!(buf, "block{n}: ; offset 0x{start:x}")?;
let buffer = &self.buffer.data()[start as usize..end as usize];
let insns = cs.disasm_all(buffer, start as u64).map_err(map_caperr)?;
for i in insns.iter() {
write!(buf, " ")?;
let op_str = i.op_str().unwrap_or("");
if let Some(s) = i.mnemonic() {
write!(buf, "{s}")?;
if !op_str.is_empty() {
write!(buf, " ")?;
}
}
write!(buf, "{op_str}")?;
let end = i.address() + i.bytes().len() as u64;
let contains = |off| i.address() <= off && off < end;
for reloc in relocs.iter().filter(|reloc| contains(reloc.offset as u64)) {
write!(
buf,
" ; reloc_external {} {} {}",
reloc.kind,
reloc.target.display(params),
reloc.addend,
)?;
}
if let Some(trap) = traps.iter().find(|trap| contains(trap.offset as u64)) {
write!(buf, " ; trap: {}", trap.code)?;
}
if let Some(patchable) = patchables.peek()
&& patchable.ret_addr == end as u32
{
write!(
buf,
" ; patchable call: NOP out last {} bytes",
patchable.len
)?;
patchables.next();
}
writeln!(buf)?;
}
}
return Ok(buf);
fn map_caperr(err: capstone::Error) -> anyhow::Error {
anyhow::format_err!("{err}")
}
}
}
pub type CompiledCodeStencil = CompiledCodeBase<Stencil>;
pub type CompiledCode = CompiledCodeBase<Final>;
impl CompiledCode {
pub fn get_code_bb_layout(&self) -> (Vec<usize>, Vec<(usize, usize)>) {
(
self.bb_starts.iter().map(|&off| off as usize).collect(),
self.bb_edges
.iter()
.map(|&(from, to)| (from as usize, to as usize))
.collect(),
)
}
#[cfg(feature = "unwind")]
pub fn create_unwind_info(
&self,
isa: &dyn crate::isa::TargetIsa,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
use crate::isa::unwind::UnwindInfoKind;
let unwind_info_kind = match isa.triple().operating_system {
target_lexicon::OperatingSystem::Windows => UnwindInfoKind::Windows,
_ => UnwindInfoKind::SystemV,
};
self.create_unwind_info_of_kind(isa, unwind_info_kind)
}
#[cfg(feature = "unwind")]
pub fn create_unwind_info_of_kind(
&self,
isa: &dyn crate::isa::TargetIsa,
unwind_info_kind: crate::isa::unwind::UnwindInfoKind,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
isa.emit_unwind_info(self, unwind_info_kind)
}
}
pub trait TextSectionBuilder {
fn append(
&mut self,
labeled: bool,
data: &[u8],
align: u32,
ctrl_plane: &mut ControlPlane,
) -> u64;
fn resolve_reloc(&mut self, offset: u64, reloc: Reloc, addend: Addend, target: usize) -> bool;
fn force_veneers(&mut self);
fn write(&mut self, offset: u64, data: &[u8]);
fn finish(&mut self, ctrl_plane: &mut ControlPlane) -> Vec<u8>;
}