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(®alloc_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