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, 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 dynamic_vector_bytes(&self, _dynamic_ty: ir::Type) -> u32 { 159 512 160 } 161 162 fn page_size_align_log2(&self) -> u8 { 163 // Claim 64KiB pages to be conservative. 164 16 165 } 166 167 fn compile_function( 168 &self, 169 func: &ir::Function, 170 domtree: &DominatorTree, 171 want_disasm: bool, 172 ctrl_plane: &mut cranelift_control::ControlPlane, 173 ) -> CodegenResult<CompiledCodeStencil> { 174 let (vcode, regalloc_result) = self.compile_vcode(func, domtree, ctrl_plane)?; 175 176 let want_disasm = 177 want_disasm || (cfg!(feature = "trace-log") && log::log_enabled!(log::Level::Debug)); 178 let emit_result = vcode.emit(®alloc_result, want_disasm, &self.flags, ctrl_plane); 179 let frame_size = emit_result.frame_size; 180 let value_labels_ranges = emit_result.value_labels_ranges; 181 let buffer = emit_result.buffer; 182 let sized_stackslot_offsets = emit_result.sized_stackslot_offsets; 183 let dynamic_stackslot_offsets = emit_result.dynamic_stackslot_offsets; 184 185 if let Some(disasm) = emit_result.disasm.as_ref() { 186 log::debug!("disassembly:\n{}", disasm); 187 } 188 189 Ok(CompiledCodeStencil { 190 buffer, 191 frame_size, 192 vcode: emit_result.disasm, 193 value_labels_ranges, 194 sized_stackslot_offsets, 195 dynamic_stackslot_offsets, 196 bb_starts: emit_result.bb_offsets, 197 bb_edges: emit_result.bb_edges, 198 }) 199 } 200 201 fn emit_unwind_info( 202 &self, 203 _result: &crate::CompiledCode, 204 _kind: super::unwind::UnwindInfoKind, 205 ) -> CodegenResult<Option<isa::unwind::UnwindInfo>> { 206 // TODO: actually support unwind info? 207 Ok(None) 208 } 209 210 fn text_section_builder( 211 &self, 212 num_labeled_funcs: usize, 213 ) -> alloc::boxed::Box<dyn TextSectionBuilder> { 214 Box::new(MachTextSectionBuilder::<inst::InstAndKind<P>>::new( 215 num_labeled_funcs, 216 )) 217 } 218 219 fn function_alignment(&self) -> FunctionAlignment { 220 inst::InstAndKind::<P>::function_alignment() 221 } 222 223 fn pretty_print_reg(&self, reg: crate::Reg, _size: u8) -> String { 224 format!("{reg:?}") 225 } 226 227 fn has_native_fma(&self) -> bool { 228 // The pulley interpreter does have fma opcodes. 229 true 230 } 231 232 fn has_round(&self) -> bool { 233 // The pulley interpreter does have rounding opcodes. 234 true 235 } 236 237 fn has_x86_blendv_lowering(&self, _ty: ir::Type) -> bool { 238 false 239 } 240 241 fn has_x86_pshufb_lowering(&self) -> bool { 242 false 243 } 244 245 fn has_x86_pmulhrsw_lowering(&self) -> bool { 246 false 247 } 248 249 fn has_x86_pmaddubsw_lowering(&self) -> bool { 250 false 251 } 252 253 fn default_argument_extension(&self) -> ir::ArgumentExtension { 254 ir::ArgumentExtension::None 255 } 256 } 257 258 /// Create a new Pulley ISA builder. 259 pub fn isa_builder(triple: Triple) -> IsaBuilder { 260 let constructor = match triple.architecture { 261 Architecture::Pulley32 | Architecture::Pulley32be => isa_constructor_32, 262 Architecture::Pulley64 | Architecture::Pulley64be => isa_constructor_64, 263 other => panic!("unexpected architecture {other:?}"), 264 }; 265 IsaBuilder { 266 triple, 267 setup: self::settings::builder(), 268 constructor, 269 } 270 } 271 272 fn isa_constructor_32( 273 triple: Triple, 274 shared_flags: Flags, 275 builder: &shared_settings::Builder, 276 ) -> CodegenResult<OwnedTargetIsa> { 277 use crate::settings::Configurable; 278 let mut builder = builder.clone(); 279 builder.set("pointer_width", "pointer32").unwrap(); 280 if triple.endianness().unwrap() == target_lexicon::Endianness::Big { 281 builder.enable("big_endian").unwrap(); 282 } 283 let isa_flags = PulleyFlags::new(&shared_flags, &builder); 284 285 let backend = 286 PulleyBackend::<super::pulley32::Pulley32>::new_with_flags(triple, shared_flags, isa_flags); 287 Ok(backend.wrapped()) 288 } 289 290 fn isa_constructor_64( 291 triple: Triple, 292 shared_flags: Flags, 293 builder: &shared_settings::Builder, 294 ) -> CodegenResult<OwnedTargetIsa> { 295 use crate::settings::Configurable; 296 let mut builder = builder.clone(); 297 builder.set("pointer_width", "pointer64").unwrap(); 298 if triple.endianness().unwrap() == target_lexicon::Endianness::Big { 299 builder.enable("big_endian").unwrap(); 300 } 301 let isa_flags = PulleyFlags::new(&shared_flags, &builder); 302 303 let backend = 304 PulleyBackend::<super::pulley64::Pulley64>::new_with_flags(triple, shared_flags, isa_flags); 305 Ok(backend.wrapped()) 306 } 307 308 impl PulleyFlags { 309 fn endianness(&self) -> ir::Endianness { 310 if self.big_endian() { 311 ir::Endianness::Big 312 } else { 313 ir::Endianness::Little 314 } 315 } 316 } 317