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