wasmer_compiler_cranelift/
compiler.rs

1//! Support for compiling with Cranelift.
2
3#[cfg(feature = "unwind")]
4use crate::dwarf::WriterRelocate;
5
6use crate::{
7    address_map::get_function_address_map,
8    config::Cranelift,
9    func_environ::{get_function_name, FuncEnvironment},
10    trampoline::{
11        make_trampoline_dynamic_function, make_trampoline_function_call, FunctionBuilderContext,
12    },
13    translator::{
14        compiled_function_unwind_info, irlibcall_to_libcall, irreloc_to_relocationkind,
15        signature_to_cranelift_ir, CraneliftUnwindInfo, FuncTranslator,
16    },
17};
18use cranelift_codegen::{
19    ir::{self, ExternalName, UserFuncName},
20    Context, FinalizedMachReloc, FinalizedRelocTarget, MachTrap,
21};
22
23#[cfg(feature = "unwind")]
24use gimli::write::{Address, EhFrame, FrameTable};
25
26#[cfg(feature = "rayon")]
27use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
28use std::sync::Arc;
29
30use wasmer_compiler::{
31    types::{
32        function::{Compilation, CompiledFunction, CompiledFunctionFrameInfo, Dwarf, FunctionBody},
33        module::CompileModuleInfo,
34        relocation::{Relocation, RelocationTarget},
35        section::SectionIndex,
36        target::{CallingConvention, Target},
37        unwind::CompiledFunctionUnwindInfo,
38    },
39    Compiler, FunctionBinaryReader, FunctionBodyData, MiddlewareBinaryReader, ModuleMiddleware,
40    ModuleMiddlewareChain, ModuleTranslationState,
41};
42use wasmer_types::entity::{EntityRef, PrimaryMap};
43use wasmer_types::{
44    CompileError, FunctionIndex, LocalFunctionIndex, ModuleInfo, SignatureIndex, TrapCode,
45    TrapInformation,
46};
47
48/// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR,
49/// optimizing it and then translating to assembly.
50pub struct CraneliftCompiler {
51    config: Cranelift,
52}
53
54impl CraneliftCompiler {
55    /// Creates a new Cranelift compiler
56    pub fn new(config: Cranelift) -> Self {
57        Self { config }
58    }
59
60    /// Gets the WebAssembly features for this Compiler
61    pub fn config(&self) -> &Cranelift {
62        &self.config
63    }
64}
65
66impl Compiler for CraneliftCompiler {
67    fn name(&self) -> &str {
68        "cranelift"
69    }
70
71    /// Get the middlewares for this compiler
72    fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
73        &self.config.middlewares
74    }
75
76    /// Compile the module using Cranelift, producing a compilation result with
77    /// associated relocations.
78    fn compile_module(
79        &self,
80        target: &Target,
81        compile_info: &CompileModuleInfo,
82        module_translation_state: &ModuleTranslationState,
83        function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
84    ) -> Result<Compilation, CompileError> {
85        let isa = self
86            .config()
87            .isa(target)
88            .map_err(|error| CompileError::Codegen(error.to_string()))?;
89        let frontend_config = isa.frontend_config();
90        let memory_styles = &compile_info.memory_styles;
91        let table_styles = &compile_info.table_styles;
92        let module = &compile_info.module;
93        let signatures = module
94            .signatures
95            .iter()
96            .map(|(_sig_index, func_type)| signature_to_cranelift_ir(func_type, frontend_config))
97            .collect::<PrimaryMap<SignatureIndex, ir::Signature>>();
98
99        // Generate the frametable
100        #[cfg(feature = "unwind")]
101        let dwarf_frametable = if function_body_inputs.is_empty() {
102            // If we have no function body inputs, we don't need to
103            // construct the `FrameTable`. Constructing it, with empty
104            // FDEs will cause some issues in Linux.
105            None
106        } else {
107            match target.triple().default_calling_convention() {
108                Ok(CallingConvention::SystemV) => {
109                    match isa.create_systemv_cie() {
110                        Some(cie) => {
111                            let mut dwarf_frametable = FrameTable::default();
112                            let cie_id = dwarf_frametable.add_cie(cie);
113                            Some((dwarf_frametable, cie_id))
114                        }
115                        // Even though we are in a SystemV system, Cranelift doesn't support it
116                        None => None,
117                    }
118                }
119                _ => None,
120            }
121        };
122
123        let mut custom_sections = PrimaryMap::new();
124
125        #[cfg(not(feature = "rayon"))]
126        let mut func_translator = FuncTranslator::new();
127        #[cfg(not(feature = "rayon"))]
128        let (functions, fdes): (Vec<CompiledFunction>, Vec<_>) = function_body_inputs
129            .iter()
130            .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
131            .into_iter()
132            .map(|(i, input)| {
133                let func_index = module.func_index(i);
134                let mut context = Context::new();
135                let mut func_env = FuncEnvironment::new(
136                    isa.frontend_config(),
137                    module,
138                    &signatures,
139                    &memory_styles,
140                    table_styles,
141                );
142                context.func.name = match get_function_name(func_index) {
143                    ExternalName::User(nameref) => {
144                        if context.func.params.user_named_funcs().is_valid(nameref) {
145                            let name = &context.func.params.user_named_funcs()[nameref];
146                            UserFuncName::User(name.clone())
147                        } else {
148                            UserFuncName::default()
149                        }
150                    }
151                    ExternalName::TestCase(testcase) => UserFuncName::Testcase(testcase),
152                    _ => UserFuncName::default(),
153                };
154                context.func.signature = signatures[module.functions[func_index]].clone();
155                // if generate_debug_info {
156                //     context.func.collect_debug_info();
157                // }
158                let mut reader =
159                    MiddlewareBinaryReader::new_with_offset(input.data, input.module_offset);
160                reader.set_middleware_chain(
161                    self.config
162                        .middlewares
163                        .generate_function_middleware_chain(i),
164                );
165
166                func_translator.translate(
167                    module_translation_state,
168                    &mut reader,
169                    &mut context.func,
170                    &mut func_env,
171                    i,
172                )?;
173
174                let mut code_buf: Vec<u8> = Vec::new();
175                context
176                    .compile_and_emit(&*isa, &mut code_buf, &mut Default::default())
177                    .map_err(|error| CompileError::Codegen(error.inner.to_string()))?;
178
179                let result = context.compiled_code().unwrap();
180                let func_relocs = result
181                    .buffer
182                    .relocs()
183                    .into_iter()
184                    .map(|r| mach_reloc_to_reloc(module, r))
185                    .collect::<Vec<_>>();
186
187                let traps = result
188                    .buffer
189                    .traps()
190                    .into_iter()
191                    .map(mach_trap_to_trap)
192                    .collect::<Vec<_>>();
193
194                let (unwind_info, fde) = match compiled_function_unwind_info(&*isa, &context)? {
195                    #[cfg(feature = "unwind")]
196                    CraneliftUnwindInfo::Fde(fde) => {
197                        if dwarf_frametable.is_some() {
198                            let fde = fde.to_fde(Address::Symbol {
199                                // The symbol is the kind of relocation.
200                                // "0" is used for functions
201                                symbol: WriterRelocate::FUNCTION_SYMBOL,
202                                // We use the addend as a way to specify the
203                                // function index
204                                addend: i.index() as _,
205                            });
206                            // The unwind information is inserted into the dwarf section
207                            (Some(CompiledFunctionUnwindInfo::Dwarf), Some(fde))
208                        } else {
209                            (None, None)
210                        }
211                    }
212                    #[cfg(feature = "unwind")]
213                    other => (other.maybe_into_to_windows_unwind(), None),
214
215                    // This is a bit hacky, but necessary since gimli is not
216                    // available when the "unwind" feature is disabled.
217                    #[cfg(not(feature = "unwind"))]
218                    other => (other.maybe_into_to_windows_unwind(), None::<()>),
219                };
220
221                let range = reader.range();
222                let address_map = get_function_address_map(&context, range, code_buf.len());
223
224                Ok((
225                    CompiledFunction {
226                        body: FunctionBody {
227                            body: code_buf,
228                            unwind_info,
229                        },
230                        relocations: func_relocs,
231                        frame_info: CompiledFunctionFrameInfo { address_map, traps },
232                    },
233                    fde,
234                ))
235            })
236            .collect::<Result<Vec<_>, CompileError>>()?
237            .into_iter()
238            .unzip();
239        #[cfg(feature = "rayon")]
240        let (functions, fdes): (Vec<CompiledFunction>, Vec<_>) = function_body_inputs
241            .iter()
242            .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
243            .par_iter()
244            .map_init(FuncTranslator::new, |func_translator, (i, input)| {
245                let func_index = module.func_index(*i);
246                let mut context = Context::new();
247                let mut func_env = FuncEnvironment::new(
248                    isa.frontend_config(),
249                    module,
250                    &signatures,
251                    memory_styles,
252                    table_styles,
253                );
254                context.func.name = match get_function_name(func_index) {
255                    ExternalName::User(nameref) => {
256                        if context.func.params.user_named_funcs().is_valid(nameref) {
257                            let name = &context.func.params.user_named_funcs()[nameref];
258                            UserFuncName::User(name.clone())
259                        } else {
260                            UserFuncName::default()
261                        }
262                    }
263                    ExternalName::TestCase(testcase) => UserFuncName::Testcase(testcase),
264                    _ => UserFuncName::default(),
265                };
266                context.func.signature = signatures[module.functions[func_index]].clone();
267                // if generate_debug_info {
268                //     context.func.collect_debug_info();
269                // }
270
271                let mut reader =
272                    MiddlewareBinaryReader::new_with_offset(input.data, input.module_offset);
273                reader.set_middleware_chain(
274                    self.config
275                        .middlewares
276                        .generate_function_middleware_chain(*i),
277                );
278
279                func_translator.translate(
280                    module_translation_state,
281                    &mut reader,
282                    &mut context.func,
283                    &mut func_env,
284                    *i,
285                )?;
286
287                let mut code_buf: Vec<u8> = Vec::new();
288                context
289                    .compile_and_emit(&*isa, &mut code_buf, &mut Default::default())
290                    .map_err(|error| CompileError::Codegen(format!("{error:#?}")))?;
291
292                let result = context.compiled_code().unwrap();
293                let func_relocs = result
294                    .buffer
295                    .relocs()
296                    .iter()
297                    .map(|r| mach_reloc_to_reloc(module, r))
298                    .collect::<Vec<_>>();
299
300                let traps = result
301                    .buffer
302                    .traps()
303                    .iter()
304                    .map(mach_trap_to_trap)
305                    .collect::<Vec<_>>();
306
307                let (unwind_info, fde) = match compiled_function_unwind_info(&*isa, &context)? {
308                    #[cfg(feature = "unwind")]
309                    CraneliftUnwindInfo::Fde(fde) => {
310                        if dwarf_frametable.is_some() {
311                            let fde = fde.to_fde(Address::Symbol {
312                                // The symbol is the kind of relocation.
313                                // "0" is used for functions
314                                symbol: WriterRelocate::FUNCTION_SYMBOL,
315                                // We use the addend as a way to specify the
316                                // function index
317                                addend: i.index() as _,
318                            });
319                            // The unwind information is inserted into the dwarf section
320                            (Some(CompiledFunctionUnwindInfo::Dwarf), Some(fde))
321                        } else {
322                            (None, None)
323                        }
324                    }
325                    #[cfg(feature = "unwind")]
326                    other => (other.maybe_into_to_windows_unwind(), None),
327
328                    // This is a bit hacky, but necessary since gimli is not
329                    // available when the "unwind" feature is disabled.
330                    #[cfg(not(feature = "unwind"))]
331                    other => (other.maybe_into_to_windows_unwind(), None::<()>),
332                };
333
334                let range = reader.range();
335                let address_map = get_function_address_map(&context, range, code_buf.len());
336
337                Ok((
338                    CompiledFunction {
339                        body: FunctionBody {
340                            body: code_buf,
341                            unwind_info,
342                        },
343                        relocations: func_relocs,
344                        frame_info: CompiledFunctionFrameInfo { address_map, traps },
345                    },
346                    fde,
347                ))
348            })
349            .collect::<Result<Vec<_>, CompileError>>()?
350            .into_iter()
351            .unzip();
352
353        #[cfg(feature = "unwind")]
354        let dwarf = if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
355            for fde in fdes.into_iter().flatten() {
356                dwarf_frametable.add_fde(cie_id, fde);
357            }
358            let mut eh_frame = EhFrame(WriterRelocate::new(target.triple().endianness().ok()));
359            dwarf_frametable.write_eh_frame(&mut eh_frame).unwrap();
360
361            let eh_frame_section = eh_frame.0.into_section();
362            custom_sections.push(eh_frame_section);
363            Some(Dwarf::new(SectionIndex::new(custom_sections.len() - 1)))
364        } else {
365            None
366        };
367        #[cfg(not(feature = "unwind"))]
368        let dwarf = None;
369
370        // function call trampolines (only for local functions, by signature)
371        #[cfg(not(feature = "rayon"))]
372        let mut cx = FunctionBuilderContext::new();
373        #[cfg(not(feature = "rayon"))]
374        let function_call_trampolines = module
375            .signatures
376            .values()
377            .collect::<Vec<_>>()
378            .into_iter()
379            .map(|sig| make_trampoline_function_call(&*isa, &mut cx, sig))
380            .collect::<Result<Vec<FunctionBody>, CompileError>>()?
381            .into_iter()
382            .collect::<PrimaryMap<SignatureIndex, FunctionBody>>();
383        #[cfg(feature = "rayon")]
384        let function_call_trampolines = module
385            .signatures
386            .values()
387            .collect::<Vec<_>>()
388            .par_iter()
389            .map_init(FunctionBuilderContext::new, |cx, sig| {
390                make_trampoline_function_call(&*isa, cx, sig)
391            })
392            .collect::<Result<Vec<FunctionBody>, CompileError>>()?
393            .into_iter()
394            .collect::<PrimaryMap<SignatureIndex, FunctionBody>>();
395
396        use wasmer_types::VMOffsets;
397        let offsets = VMOffsets::new_for_trampolines(frontend_config.pointer_bytes());
398        // dynamic function trampolines (only for imported functions)
399        #[cfg(not(feature = "rayon"))]
400        let mut cx = FunctionBuilderContext::new();
401        #[cfg(not(feature = "rayon"))]
402        let dynamic_function_trampolines = module
403            .imported_function_types()
404            .collect::<Vec<_>>()
405            .into_iter()
406            .map(|func_type| make_trampoline_dynamic_function(&*isa, &offsets, &mut cx, &func_type))
407            .collect::<Result<Vec<_>, CompileError>>()?
408            .into_iter()
409            .collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
410        #[cfg(feature = "rayon")]
411        let dynamic_function_trampolines = module
412            .imported_function_types()
413            .collect::<Vec<_>>()
414            .par_iter()
415            .map_init(FunctionBuilderContext::new, |cx, func_type| {
416                make_trampoline_dynamic_function(&*isa, &offsets, cx, func_type)
417            })
418            .collect::<Result<Vec<_>, CompileError>>()?
419            .into_iter()
420            .collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
421
422        Ok(Compilation {
423            functions: functions.into_iter().collect(),
424            custom_sections,
425            function_call_trampolines,
426            dynamic_function_trampolines,
427            debug: dwarf,
428        })
429    }
430}
431
432fn mach_reloc_to_reloc(module: &ModuleInfo, reloc: &FinalizedMachReloc) -> Relocation {
433    let FinalizedMachReloc {
434        offset,
435        kind,
436        addend,
437        target,
438    } = &reloc;
439    let name = match target {
440        FinalizedRelocTarget::ExternalName(external_name) => external_name,
441        FinalizedRelocTarget::Func(_) => {
442            unimplemented!("relocations to offset in the same function are not yet supported")
443        }
444    };
445    let reloc_target: RelocationTarget = if let ExternalName::User(extname_ref) = name {
446        //debug_assert_eq!(namespace, 0);
447        RelocationTarget::LocalFunc(
448            module
449                .local_func_index(FunctionIndex::from_u32(extname_ref.as_u32()))
450                .expect("The provided function should be local"),
451        )
452    } else if let ExternalName::LibCall(libcall) = name {
453        RelocationTarget::LibCall(irlibcall_to_libcall(*libcall))
454    } else {
455        panic!("unrecognized external target")
456    };
457    Relocation {
458        kind: irreloc_to_relocationkind(*kind),
459        reloc_target,
460        offset: *offset,
461        addend: *addend,
462    }
463}
464
465fn mach_trap_to_trap(trap: &MachTrap) -> TrapInformation {
466    let &MachTrap { offset, code } = trap;
467    TrapInformation {
468        code_offset: offset,
469        trap_code: translate_ir_trapcode(code),
470    }
471}
472
473/// Translates the Cranelift IR TrapCode into generic Trap Code
474fn translate_ir_trapcode(trap: ir::TrapCode) -> TrapCode {
475    match trap {
476        ir::TrapCode::StackOverflow => TrapCode::StackOverflow,
477        ir::TrapCode::HeapOutOfBounds => TrapCode::HeapAccessOutOfBounds,
478        ir::TrapCode::HeapMisaligned => TrapCode::UnalignedAtomic,
479        ir::TrapCode::TableOutOfBounds => TrapCode::TableAccessOutOfBounds,
480        ir::TrapCode::IndirectCallToNull => TrapCode::IndirectCallToNull,
481        ir::TrapCode::BadSignature => TrapCode::BadSignature,
482        ir::TrapCode::IntegerOverflow => TrapCode::IntegerOverflow,
483        ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
484        ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger,
485        ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached,
486        ir::TrapCode::Interrupt => unimplemented!("Interrupts not supported"),
487        ir::TrapCode::NullReference | ir::TrapCode::NullI31Ref => {
488            unimplemented!("Null reference not supported")
489        }
490        ir::TrapCode::User(_user_code) => unimplemented!("User trap code not supported"),
491        // ir::TrapCode::Interrupt => TrapCode::Interrupt,
492        // ir::TrapCode::User(user_code) => TrapCode::User(user_code),
493    }
494}