//! Common support compiling to either 32- or 64-bit Pulley bytecode.
mod abi;
mod inst;
mod lower;
mod settings;
use self::inst::EmitInfo;
use super::{Builder as IsaBuilder, FunctionAlignment};
use crate::{
dominator_tree::DominatorTree,
ir,
isa::{self, OwnedTargetIsa, TargetIsa},
machinst::{self, CompiledCodeStencil, MachInst, SigSet, VCode},
result::CodegenResult,
settings::{self as shared_settings, Flags},
MachTextSectionBuilder, TextSectionBuilder,
};
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::fmt::Debug;
use core::marker::PhantomData;
use cranelift_control::ControlPlane;
use target_lexicon::{Architecture, Triple};
pub use settings::Flags as PulleyFlags;
/// A trait to abstract over the different kinds of Pulley targets that exist
/// (32- vs 64-bit).
pub trait PulleyTargetKind: 'static + Clone + Debug + Default + Send + Sync {
// Required types and methods.
fn pointer_width() -> PointerWidth;
// Provided methods. Don't overwrite.
fn name() -> &'static str {
match Self::pointer_width() {
PointerWidth::PointerWidth32 => "pulley32",
PointerWidth::PointerWidth64 => "pulley64",
}
}
}
pub enum PointerWidth {
PointerWidth32,
PointerWidth64,
}
impl PointerWidth {
pub fn bits(self) -> u8 {
match self {
PointerWidth::PointerWidth32 => 32,
PointerWidth::PointerWidth64 => 64,
}
}
pub fn bytes(self) -> u8 {
self.bits() / 8
}
}
/// A Pulley backend.
pub struct PulleyBackend
where
P: PulleyTargetKind,
{
pulley_target: PhantomData
,
triple: Triple,
flags: Flags,
isa_flags: PulleyFlags,
}
impl
core::fmt::Debug for PulleyBackend
where
P: PulleyTargetKind,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let PulleyBackend {
pulley_target: _,
triple,
flags: _,
isa_flags: _,
} = self;
f.debug_struct("PulleyBackend")
.field("triple", triple)
.finish_non_exhaustive()
}
}
impl
core::fmt::Display for PulleyBackend
where
P: PulleyTargetKind,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Debug::fmt(self, f)
}
}
impl
PulleyBackend
where
P: PulleyTargetKind,
{
/// Create a new pulley backend with the given (shared) flags.
pub fn new_with_flags(
triple: Triple,
flags: shared_settings::Flags,
isa_flags: PulleyFlags,
) -> Self {
PulleyBackend {
pulley_target: PhantomData,
triple,
flags,
isa_flags,
}
}
/// This performs lowering to VCode, register-allocates the code, computes block layout and
/// finalizes branches. The result is ready for binary emission.
fn compile_vcode(
&self,
func: &ir::Function,
domtree: &DominatorTree,
ctrl_plane: &mut ControlPlane,
) -> CodegenResult<(VCode>, regalloc2::Output)> {
let emit_info = EmitInfo::new(self.flags.clone(), self.isa_flags.clone());
let sigs = SigSet::new::>(func, &self.flags)?;
let abi = abi::PulleyCallee::new(func, self, &self.isa_flags, &sigs)?;
machinst::compile::(func, domtree, self, abi, emit_info, sigs, ctrl_plane)
}
}
impl TargetIsa for PulleyBackend
where
P: PulleyTargetKind,
{
fn name(&self) -> &'static str {
P::name()
}
fn triple(&self) -> &Triple {
&self.triple
}
fn flags(&self) -> &Flags {
&self.flags
}
fn isa_flags(&self) -> Vec {
self.isa_flags.iter().collect()
}
fn dynamic_vector_bytes(&self, _dynamic_ty: ir::Type) -> u32 {
512
}
fn page_size_align_log2(&self) -> u8 {
// Claim 64KiB pages to be conservative.
16
}
fn compile_function(
&self,
func: &ir::Function,
domtree: &DominatorTree,
want_disasm: bool,
ctrl_plane: &mut cranelift_control::ControlPlane,
) -> CodegenResult {
let (vcode, regalloc_result) = self.compile_vcode(func, domtree, ctrl_plane)?;
let want_disasm =
want_disasm || (cfg!(feature = "trace-log") && log::log_enabled!(log::Level::Debug));
let emit_result = vcode.emit(®alloc_result, want_disasm, &self.flags, ctrl_plane);
let frame_size = emit_result.frame_size;
let value_labels_ranges = emit_result.value_labels_ranges;
let buffer = emit_result.buffer;
let sized_stackslot_offsets = emit_result.sized_stackslot_offsets;
let dynamic_stackslot_offsets = emit_result.dynamic_stackslot_offsets;
if let Some(disasm) = emit_result.disasm.as_ref() {
log::debug!("disassembly:\n{}", disasm);
}
Ok(CompiledCodeStencil {
buffer,
frame_size,
vcode: emit_result.disasm,
value_labels_ranges,
sized_stackslot_offsets,
dynamic_stackslot_offsets,
bb_starts: emit_result.bb_offsets,
bb_edges: emit_result.bb_edges,
})
}
fn emit_unwind_info(
&self,
_result: &crate::CompiledCode,
_kind: super::unwind::UnwindInfoKind,
) -> CodegenResult> {
// TODO: actually support unwind info?
Ok(None)
}
fn text_section_builder(
&self,
num_labeled_funcs: usize,
) -> alloc::boxed::Box {
Box::new(MachTextSectionBuilder::>::new(
num_labeled_funcs,
))
}
fn function_alignment(&self) -> FunctionAlignment {
inst::InstAndKind::::function_alignment()
}
fn has_native_fma(&self) -> bool {
false
}
fn has_x86_blendv_lowering(&self, _ty: ir::Type) -> bool {
false
}
fn has_x86_pshufb_lowering(&self) -> bool {
false
}
fn has_x86_pmulhrsw_lowering(&self) -> bool {
false
}
fn has_x86_pmaddubsw_lowering(&self) -> bool {
false
}
}
/// Create a new Pulley ISA builder.
pub fn isa_builder(triple: Triple) -> IsaBuilder {
let constructor = match triple.architecture {
Architecture::Pulley32 | Architecture::Pulley32be => isa_constructor_32,
Architecture::Pulley64 | Architecture::Pulley64be => isa_constructor_64,
other => panic!("unexpected architecture {other:?}"),
};
IsaBuilder {
triple,
setup: self::settings::builder(),
constructor,
}
}
fn isa_constructor_32(
triple: Triple,
shared_flags: Flags,
builder: &shared_settings::Builder,
) -> CodegenResult {
use crate::settings::Configurable;
let mut builder = builder.clone();
builder.set("pointer_width", "pointer32").unwrap();
if triple.endianness().unwrap() == target_lexicon::Endianness::Big {
builder.enable("big_endian").unwrap();
}
let isa_flags = PulleyFlags::new(&shared_flags, &builder);
let backend =
PulleyBackend::::new_with_flags(triple, shared_flags, isa_flags);
Ok(backend.wrapped())
}
fn isa_constructor_64(
triple: Triple,
shared_flags: Flags,
builder: &shared_settings::Builder,
) -> CodegenResult {
use crate::settings::Configurable;
let mut builder = builder.clone();
builder.set("pointer_width", "pointer64").unwrap();
if triple.endianness().unwrap() == target_lexicon::Endianness::Big {
builder.enable("big_endian").unwrap();
}
let isa_flags = PulleyFlags::new(&shared_flags, &builder);
let backend =
PulleyBackend::::new_with_flags(triple, shared_flags, isa_flags);
Ok(backend.wrapped())
}