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::string::String;
21 use alloc::vec::Vec;
22 use core::fmt::Debug;
23 use core::marker::PhantomData;
24 use cranelift_control::ControlPlane;
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 
pointer_width() -> PointerWidth34     fn pointer_width() -> PointerWidth;
35 
36     // Provided methods. Don't overwrite.
37 
name() -> &'static str38     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 {
bits(self) -> u852     pub fn bits(self) -> u8 {
53         match self {
54             PointerWidth::PointerWidth32 => 32,
55             PointerWidth::PointerWidth64 => 64,
56         }
57     }
58 
bytes(self) -> u859     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 {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result79     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 {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result96     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.
new_with_flags( triple: Triple, flags: shared_settings::Flags, isa_flags: PulleyFlags, ) -> Self106     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.
compile_vcode( &self, func: &ir::Function, domtree: &DominatorTree, ctrl_plane: &mut ControlPlane, ) -> CodegenResult<(VCode<inst::InstAndKind<P>>, regalloc2::Output)>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 {
name(&self) -> &'static str142     fn name(&self) -> &'static str {
143         P::name()
144     }
145 
triple(&self) -> &Triple146     fn triple(&self) -> &Triple {
147         &self.triple
148     }
149 
flags(&self) -> &Flags150     fn flags(&self) -> &Flags {
151         &self.flags
152     }
153 
isa_flags(&self) -> Vec<shared_settings::Value>154     fn isa_flags(&self) -> Vec<shared_settings::Value> {
155         self.isa_flags.iter().collect()
156     }
157 
isa_flags_hash_key(&self) -> IsaFlagsHashKey<'_>158     fn isa_flags_hash_key(&self) -> IsaFlagsHashKey<'_> {
159         IsaFlagsHashKey(self.isa_flags.hash_key())
160     }
161 
dynamic_vector_bytes(&self, _dynamic_ty: ir::Type) -> u32162     fn dynamic_vector_bytes(&self, _dynamic_ty: ir::Type) -> u32 {
163         512
164     }
165 
page_size_align_log2(&self) -> u8166     fn page_size_align_log2(&self) -> u8 {
167         // Claim 64KiB pages to be conservative.
168         16
169     }
170 
compile_function( &self, func: &ir::Function, domtree: &DominatorTree, want_disasm: bool, ctrl_plane: &mut cranelift_control::ControlPlane, ) -> CodegenResult<CompiledCodeStencil>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(&regalloc_result, want_disasm, &self.flags, ctrl_plane);
183         let value_labels_ranges = emit_result.value_labels_ranges;
184         let buffer = emit_result.buffer;
185 
186         if let Some(disasm) = emit_result.disasm.as_ref() {
187             log::debug!("disassembly:\n{disasm}");
188         }
189 
190         Ok(CompiledCodeStencil {
191             buffer,
192             vcode: emit_result.disasm,
193             value_labels_ranges,
194             bb_starts: emit_result.bb_offsets,
195             bb_edges: emit_result.bb_edges,
196         })
197     }
198 
emit_unwind_info( &self, _result: &crate::CompiledCode, _kind: super::unwind::UnwindInfoKind, ) -> CodegenResult<Option<isa::unwind::UnwindInfo>>199     fn emit_unwind_info(
200         &self,
201         _result: &crate::CompiledCode,
202         _kind: super::unwind::UnwindInfoKind,
203     ) -> CodegenResult<Option<isa::unwind::UnwindInfo>> {
204         // TODO: actually support unwind info?
205         Ok(None)
206     }
207 
text_section_builder( &self, num_labeled_funcs: usize, ) -> alloc::boxed::Box<dyn TextSectionBuilder>208     fn text_section_builder(
209         &self,
210         num_labeled_funcs: usize,
211     ) -> alloc::boxed::Box<dyn TextSectionBuilder> {
212         Box::new(MachTextSectionBuilder::<inst::InstAndKind<P>>::new(
213             num_labeled_funcs,
214         ))
215     }
216 
function_alignment(&self) -> FunctionAlignment217     fn function_alignment(&self) -> FunctionAlignment {
218         inst::InstAndKind::<P>::function_alignment()
219     }
220 
pretty_print_reg(&self, reg: crate::Reg, _size: u8) -> String221     fn pretty_print_reg(&self, reg: crate::Reg, _size: u8) -> String {
222         format!("{reg:?}")
223     }
224 
has_native_fma(&self) -> bool225     fn has_native_fma(&self) -> bool {
226         // The pulley interpreter does have fma opcodes.
227         true
228     }
229 
has_round(&self) -> bool230     fn has_round(&self) -> bool {
231         // The pulley interpreter does have rounding opcodes.
232         true
233     }
234 
has_blendv_lowering(&self, _ty: ir::Type) -> bool235     fn has_blendv_lowering(&self, _ty: ir::Type) -> bool {
236         false
237     }
238 
has_x86_pshufb_lowering(&self) -> bool239     fn has_x86_pshufb_lowering(&self) -> bool {
240         false
241     }
242 
has_x86_pmulhrsw_lowering(&self) -> bool243     fn has_x86_pmulhrsw_lowering(&self) -> bool {
244         false
245     }
246 
has_x86_pmaddubsw_lowering(&self) -> bool247     fn has_x86_pmaddubsw_lowering(&self) -> bool {
248         false
249     }
250 
default_argument_extension(&self) -> ir::ArgumentExtension251     fn default_argument_extension(&self) -> ir::ArgumentExtension {
252         ir::ArgumentExtension::None
253     }
254 }
255 
256 /// Create a new Pulley ISA builder.
isa_builder(triple: Triple) -> IsaBuilder257 pub fn isa_builder(triple: Triple) -> IsaBuilder {
258     let constructor = match triple.architecture {
259         Architecture::Pulley32 | Architecture::Pulley32be => isa_constructor_32,
260         Architecture::Pulley64 | Architecture::Pulley64be => isa_constructor_64,
261         other => panic!("unexpected architecture {other:?}"),
262     };
263     IsaBuilder {
264         triple,
265         setup: self::settings::builder(),
266         constructor,
267     }
268 }
269 
isa_constructor_32( triple: Triple, shared_flags: Flags, builder: &shared_settings::Builder, ) -> CodegenResult<OwnedTargetIsa>270 fn isa_constructor_32(
271     triple: Triple,
272     shared_flags: Flags,
273     builder: &shared_settings::Builder,
274 ) -> CodegenResult<OwnedTargetIsa> {
275     use crate::settings::Configurable;
276     let mut builder = builder.clone();
277     builder.set("pointer_width", "pointer32").unwrap();
278     if triple.endianness().unwrap() == target_lexicon::Endianness::Big {
279         builder.enable("big_endian").unwrap();
280     }
281     let isa_flags = PulleyFlags::new(&shared_flags, &builder);
282 
283     let backend =
284         PulleyBackend::<super::pulley32::Pulley32>::new_with_flags(triple, shared_flags, isa_flags);
285     Ok(backend.wrapped())
286 }
287 
isa_constructor_64( triple: Triple, shared_flags: Flags, builder: &shared_settings::Builder, ) -> CodegenResult<OwnedTargetIsa>288 fn isa_constructor_64(
289     triple: Triple,
290     shared_flags: Flags,
291     builder: &shared_settings::Builder,
292 ) -> CodegenResult<OwnedTargetIsa> {
293     use crate::settings::Configurable;
294     let mut builder = builder.clone();
295     builder.set("pointer_width", "pointer64").unwrap();
296     if triple.endianness().unwrap() == target_lexicon::Endianness::Big {
297         builder.enable("big_endian").unwrap();
298     }
299     let isa_flags = PulleyFlags::new(&shared_flags, &builder);
300 
301     let backend =
302         PulleyBackend::<super::pulley64::Pulley64>::new_with_flags(triple, shared_flags, isa_flags);
303     Ok(backend.wrapped())
304 }
305 
306 impl PulleyFlags {
endianness(&self) -> ir::Endianness307     fn endianness(&self) -> ir::Endianness {
308         if self.big_endian() {
309             ir::Endianness::Big
310         } else {
311             ir::Endianness::Little
312         }
313     }
314 }
315