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