1 //! Common support compiling to either 32- or 64-bit Pulley bytecode. 2 3 mod abi; 4 mod inst; 5 mod lower; 6 mod settings; 7 8 use self::inst::EmitInfo; 9 use super::{Builder as IsaBuilder, FunctionAlignment}; 10 use crate::{ 11 dominator_tree::DominatorTree, 12 ir, 13 isa::{self, OwnedTargetIsa, TargetIsa}, 14 machinst::{self, CompiledCodeStencil, MachInst, SigSet, VCode}, 15 result::CodegenResult, 16 settings::{self as shared_settings, Flags}, 17 MachTextSectionBuilder, TextSectionBuilder, 18 }; 19 use alloc::boxed::Box; 20 use alloc::vec::Vec; 21 use core::fmt::Debug; 22 use core::marker::PhantomData; 23 use cranelift_control::ControlPlane; 24 use target_lexicon::{Architecture, Triple}; 25 26 pub use settings::Flags as PulleyFlags; 27 28 /// A trait to abstract over the different kinds of Pulley targets that exist 29 /// (32- vs 64-bit). 30 pub trait PulleyTargetKind: 'static + Clone + Debug + Default + Send + Sync { 31 // Required types and methods. 32 33 fn pointer_width() -> PointerWidth; 34 35 // Provided methods. Don't overwrite. 36 37 fn name() -> &'static str { 38 match Self::pointer_width() { 39 PointerWidth::PointerWidth32 => "pulley32", 40 PointerWidth::PointerWidth64 => "pulley64", 41 } 42 } 43 } 44 45 pub enum PointerWidth { 46 PointerWidth32, 47 PointerWidth64, 48 } 49 50 impl PointerWidth { 51 pub fn bits(self) -> u8 { 52 match self { 53 PointerWidth::PointerWidth32 => 32, 54 PointerWidth::PointerWidth64 => 64, 55 } 56 } 57 } 58 59 /// A Pulley backend. 60 pub struct PulleyBackend<P> 61 where 62 P: PulleyTargetKind, 63 { 64 pulley_target: PhantomData<P>, 65 triple: Triple, 66 flags: Flags, 67 isa_flags: PulleyFlags, 68 } 69 70 impl<P> core::fmt::Debug for PulleyBackend<P> 71 where 72 P: PulleyTargetKind, 73 { 74 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 75 let PulleyBackend { 76 pulley_target: _, 77 triple, 78 flags: _, 79 isa_flags: _, 80 } = self; 81 f.debug_struct("PulleyBackend") 82 .field("triple", triple) 83 .finish_non_exhaustive() 84 } 85 } 86 87 impl<P> core::fmt::Display for PulleyBackend<P> 88 where 89 P: PulleyTargetKind, 90 { 91 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 92 core::fmt::Debug::fmt(self, f) 93 } 94 } 95 96 impl<P> PulleyBackend<P> 97 where 98 P: PulleyTargetKind, 99 { 100 /// Create a new pulley backend with the given (shared) flags. 101 pub fn new_with_flags( 102 triple: Triple, 103 flags: shared_settings::Flags, 104 isa_flags: PulleyFlags, 105 ) -> Self { 106 PulleyBackend { 107 pulley_target: PhantomData, 108 triple, 109 flags, 110 isa_flags, 111 } 112 } 113 114 /// This performs lowering to VCode, register-allocates the code, computes block layout and 115 /// finalizes branches. The result is ready for binary emission. 116 fn compile_vcode( 117 &self, 118 func: &ir::Function, 119 domtree: &DominatorTree, 120 ctrl_plane: &mut ControlPlane, 121 ) -> CodegenResult<(VCode<inst::InstAndKind<P>>, regalloc2::Output)> { 122 let emit_info = EmitInfo::new(self.flags.clone(), self.isa_flags.clone()); 123 let sigs = SigSet::new::<abi::PulleyMachineDeps<P>>(func, &self.flags)?; 124 let abi = abi::PulleyCallee::new(func, self, &self.isa_flags, &sigs)?; 125 machinst::compile::<Self>(func, domtree, self, abi, emit_info, sigs, ctrl_plane) 126 } 127 } 128 129 impl<P> TargetIsa for PulleyBackend<P> 130 where 131 P: PulleyTargetKind, 132 { 133 fn name(&self) -> &'static str { 134 P::name() 135 } 136 137 fn triple(&self) -> &Triple { 138 &self.triple 139 } 140 141 fn flags(&self) -> &Flags { 142 &self.flags 143 } 144 145 fn isa_flags(&self) -> Vec<shared_settings::Value> { 146 self.isa_flags.iter().collect() 147 } 148 149 fn dynamic_vector_bytes(&self, _dynamic_ty: ir::Type) -> u32 { 150 512 151 } 152 153 fn page_size_align_log2(&self) -> u8 { 154 // Claim 64KiB pages to be conservative. 155 16 156 } 157 158 fn compile_function( 159 &self, 160 func: &ir::Function, 161 domtree: &DominatorTree, 162 want_disasm: bool, 163 ctrl_plane: &mut cranelift_control::ControlPlane, 164 ) -> CodegenResult<CompiledCodeStencil> { 165 let (vcode, regalloc_result) = self.compile_vcode(func, domtree, ctrl_plane)?; 166 167 let want_disasm = 168 want_disasm || (cfg!(feature = "trace-log") && log::log_enabled!(log::Level::Debug)); 169 let emit_result = vcode.emit(®alloc_result, want_disasm, &self.flags, ctrl_plane); 170 let frame_size = emit_result.frame_size; 171 let value_labels_ranges = emit_result.value_labels_ranges; 172 let buffer = emit_result.buffer; 173 let sized_stackslot_offsets = emit_result.sized_stackslot_offsets; 174 let dynamic_stackslot_offsets = emit_result.dynamic_stackslot_offsets; 175 176 if let Some(disasm) = emit_result.disasm.as_ref() { 177 log::debug!("disassembly:\n{}", disasm); 178 } 179 180 Ok(CompiledCodeStencil { 181 buffer, 182 frame_size, 183 vcode: emit_result.disasm, 184 value_labels_ranges, 185 sized_stackslot_offsets, 186 dynamic_stackslot_offsets, 187 bb_starts: emit_result.bb_offsets, 188 bb_edges: emit_result.bb_edges, 189 }) 190 } 191 192 fn emit_unwind_info( 193 &self, 194 _result: &crate::CompiledCode, 195 _kind: super::unwind::UnwindInfoKind, 196 ) -> CodegenResult<Option<isa::unwind::UnwindInfo>> { 197 // TODO: actually support unwind info? 198 Ok(None) 199 } 200 201 fn text_section_builder( 202 &self, 203 num_labeled_funcs: usize, 204 ) -> alloc::boxed::Box<dyn TextSectionBuilder> { 205 Box::new(MachTextSectionBuilder::<inst::InstAndKind<P>>::new( 206 num_labeled_funcs, 207 )) 208 } 209 210 fn function_alignment(&self) -> FunctionAlignment { 211 inst::InstAndKind::<P>::function_alignment() 212 } 213 214 fn has_native_fma(&self) -> bool { 215 false 216 } 217 218 fn has_x86_blendv_lowering(&self, _ty: ir::Type) -> bool { 219 false 220 } 221 222 fn has_x86_pshufb_lowering(&self) -> bool { 223 false 224 } 225 226 fn has_x86_pmulhrsw_lowering(&self) -> bool { 227 false 228 } 229 230 fn has_x86_pmaddubsw_lowering(&self) -> bool { 231 false 232 } 233 } 234 235 /// Create a new Pulley ISA builder. 236 pub fn isa_builder(triple: Triple) -> IsaBuilder { 237 assert!(matches!( 238 triple.architecture, 239 Architecture::Pulley32 | Architecture::Pulley64 240 )); 241 let constructor = match triple.architecture { 242 Architecture::Pulley32 => isa_constructor_32, 243 Architecture::Pulley64 => isa_constructor_64, 244 _ => unreachable!(), 245 }; 246 IsaBuilder { 247 triple, 248 setup: self::settings::builder(), 249 constructor, 250 } 251 } 252 253 fn isa_constructor_32( 254 triple: Triple, 255 shared_flags: Flags, 256 builder: &shared_settings::Builder, 257 ) -> CodegenResult<OwnedTargetIsa> { 258 use crate::settings::Configurable; 259 let mut builder = builder.clone(); 260 builder.set("pointer_width", "pointer32").unwrap(); 261 let isa_flags = PulleyFlags::new(&shared_flags, &builder); 262 263 let backend = 264 PulleyBackend::<super::pulley32::Pulley32>::new_with_flags(triple, shared_flags, isa_flags); 265 Ok(backend.wrapped()) 266 } 267 268 fn isa_constructor_64( 269 triple: Triple, 270 shared_flags: Flags, 271 builder: &shared_settings::Builder, 272 ) -> CodegenResult<OwnedTargetIsa> { 273 use crate::settings::Configurable; 274 let mut builder = builder.clone(); 275 builder.set("pointer_width", "pointer64").unwrap(); 276 let isa_flags = PulleyFlags::new(&shared_flags, &builder); 277 278 let backend = 279 PulleyBackend::<super::pulley64::Pulley64>::new_with_flags(triple, shared_flags, isa_flags); 280 Ok(backend.wrapped()) 281 } 282