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(&regalloc_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