Skip to main content

wasmer_compiler_cranelift/
compiler.rs

1//! Support for compiling with Cranelift.
2
3#[cfg(feature = "unwind")]
4use crate::dwarf::WriterRelocate;
5
6#[cfg(feature = "unwind")]
7use crate::eh::{
8    CompactUnwindEntryData, FunctionLsdaData, build_compact_unwind_section, build_function_lsda,
9    build_lsda_section, build_tag_section, compact_unwind_encoding_aarch64,
10};
11
12#[cfg(feature = "unwind")]
13use crate::translator::CraneliftUnwindInfo;
14use crate::{
15    address_map::get_function_address_map,
16    config::{Cranelift, CraneliftOptLevel},
17    func_environ::{FuncEnvironment, get_function_name},
18    trampoline::{
19        FunctionBuilderContext, make_trampoline_dynamic_function, make_trampoline_function_call,
20    },
21    translator::{
22        FuncTranslator, compiled_function_unwind_info, irlibcall_to_libcall,
23        irreloc_to_relocationkind, signature_to_cranelift_ir,
24    },
25};
26use cranelift_codegen::{
27    Context, FinalizedMachReloc, FinalizedRelocTarget, MachTrap,
28    ir::{self, ExternalName, UserFuncName},
29};
30
31#[cfg(feature = "unwind")]
32use cranelift_codegen::gimli::{
33    constants::DW_EH_PE_absptr,
34    write::{Address, EhFrame, FrameDescriptionEntry, FrameTable, Writer},
35};
36
37use itertools::Itertools;
38use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
39#[cfg(feature = "unwind")]
40use std::collections::HashMap;
41use std::sync::Arc;
42use wasmer_compiler::WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE;
43use wasmer_compiler::elf::{CompileOutput, compile_output_in_memory, compile_output_objects};
44use wasmer_compiler::types::function::Compilation;
45
46use wasmer_compiler::progress::ProgressContext;
47#[cfg(feature = "unwind")]
48use wasmer_compiler::types::{section::SectionIndex, unwind::CompiledFunctionUnwindInfo};
49use wasmer_compiler::{
50    Compiler, FunctionBinaryReader, FunctionBodyData, MiddlewareBinaryReader, ModuleMiddleware,
51    ModuleMiddlewareChain, ModuleTranslationState, WasmSourceMap,
52    types::{
53        function::{
54            CompiledFunction, CompiledFunctionFrameInfo, FunctionBody, RkyvCompilation, UnwindInfo,
55        },
56        module::CompileModuleInfo,
57        relocation::{Relocation, RelocationKind, RelocationTarget},
58        section::{CustomSection, CustomSectionProtection, SectionBody},
59    },
60};
61use wasmer_compiler::{build_function_buckets, translate_function_buckets};
62#[cfg(feature = "unwind")]
63use wasmer_types::LibCall;
64#[cfg(feature = "unwind")]
65use wasmer_types::entity::EntityRef;
66use wasmer_types::entity::PrimaryMap;
67#[cfg(feature = "unwind")]
68use wasmer_types::target::CallingConvention;
69use wasmer_types::target::Target;
70use wasmer_types::{
71    CompilationProgressCallback, CompileError, FunctionIndex, LocalFunctionIndex, ModuleInfo,
72    SignatureIndex, TrapCode, TrapInformation,
73};
74
75pub struct CraneliftCompiledFunction {
76    function: CompiledFunction,
77    #[cfg(feature = "unwind")]
78    fde: Option<FrameDescriptionEntry>,
79    #[cfg(feature = "unwind")]
80    function_lsda: Option<FunctionLsdaData>,
81    #[cfg(feature = "unwind")]
82    compact_unwind_encoding: Option<u32>,
83}
84
85impl wasmer_compiler::CompiledFunction for CraneliftCompiledFunction {}
86
87/// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR,
88/// optimizing it and then translating to assembly.
89#[derive(Debug)]
90pub struct CraneliftCompiler {
91    config: Cranelift,
92}
93
94impl CraneliftCompiler {
95    /// Creates a new Cranelift compiler
96    pub fn new(config: Cranelift) -> Self {
97        Self { config }
98    }
99
100    /// Gets the WebAssembly features for this Compiler
101    pub fn config(&self) -> &Cranelift {
102        &self.config
103    }
104
105    // Helper function to create an easy scope boundary for the thread pool used
106    // in [`Self::compile_module`].
107    fn compile_module_internal(
108        &self,
109        target: &Target,
110        compile_info: &CompileModuleInfo,
111        compile_info_blob: &[u8],
112        module_translation_state: &ModuleTranslationState,
113        function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
114        progress_callback: Option<&CompilationProgressCallback>,
115    ) -> Result<Compilation, CompileError> {
116        wasmer_compiler::validate_module_fixed_table_size(
117            &compile_info.module,
118            self.config.max_table_elements,
119        )?;
120        let function_max_stack_usage = function_body_inputs.iter().map(|_| None).collect();
121        let isa = self
122            .config()
123            .isa(target)
124            .map_err(|error| CompileError::Codegen(error.to_string()))?;
125        let frontend_config = isa.frontend_config();
126        #[cfg(feature = "unwind")]
127        let pointer_bytes = frontend_config.pointer_bytes();
128        #[cfg(feature = "unwind")]
129        let emit_macho_compact_unwind = matches!(
130            target.triple(),
131            target_lexicon::Triple {
132                binary_format: target_lexicon::BinaryFormat::Macho,
133                operating_system: target_lexicon::OperatingSystem::Darwin(_),
134                architecture: target_lexicon::Architecture::Aarch64(_),
135                ..
136            }
137        );
138        let memory_styles = &compile_info.memory_styles;
139        let table_styles = &compile_info.table_styles;
140        let module = &compile_info.module;
141        let source_map = Arc::new(if self.config.experimental_artifact {
142            WasmSourceMap::new(module, module_translation_state, &function_body_inputs)
143                .map_err(CompileError::Codegen)?
144        } else {
145            WasmSourceMap::default()
146        });
147
148        let signatures = module
149            .signatures
150            .iter()
151            .map(|(_sig_index, func_type)| {
152                signature_to_cranelift_ir(func_type, frontend_config, target.triple().architecture)
153            })
154            .collect::<PrimaryMap<SignatureIndex, ir::Signature>>();
155        let signature_hashes = &module.signature_hashes;
156
157        let total_function_call_trampolines = module.signatures.len();
158        let total_dynamic_trampolines = module.num_imported_functions;
159        let total_steps = WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE
160            * ((total_dynamic_trampolines + total_function_call_trampolines) as u64)
161            + function_body_inputs
162                .iter()
163                .map(|(_, body)| body.data.len() as u64)
164                .sum::<u64>();
165        let progress = progress_callback
166            .cloned()
167            .map(|cb| ProgressContext::new(cb, total_steps, "cranelift::functions"));
168
169        // Generate the frametable
170        #[cfg(feature = "unwind")]
171        let dwarf_frametable = if function_body_inputs.is_empty() {
172            // If we have no function body inputs, we don't need to
173            // construct the `FrameTable`. Constructing it, with empty
174            // FDEs will cause some issues in Linux.
175            None
176        } else {
177            match target.triple().default_calling_convention() {
178                Ok(CallingConvention::SystemV) => match isa.create_systemv_cie() {
179                    Some(mut cie) => {
180                        cie.personality = Some((
181                            DW_EH_PE_absptr,
182                            Address::Symbol {
183                                symbol: WriterRelocate::PERSONALITY_SYMBOL,
184                                addend: 0,
185                            },
186                        ));
187                        cie.lsda_encoding = Some(DW_EH_PE_absptr);
188                        let mut dwarf_frametable = FrameTable::default();
189                        let cie_id = dwarf_frametable.add_cie(cie);
190                        Some((dwarf_frametable, cie_id))
191                    }
192                    // Even though we are in a SystemV system, Cranelift doesn't support it
193                    None => None,
194                },
195                _ => None,
196            }
197        };
198
199        // The `compile_function` closure is used for both the sequential and
200        // parallel compilation paths to avoid code duplication.
201        let compile_function = |func_translator: &mut FuncTranslator,
202                                i: &LocalFunctionIndex,
203                                input: &FunctionBodyData|
204         -> Result<
205            CompileOutput<CraneliftCompiledFunction>,
206            CompileError,
207        > {
208            let func_index = module.func_index(*i);
209            let mut context = Context::new();
210            let mut func_env = FuncEnvironment::new(
211                isa.frontend_config(),
212                target.triple().architecture,
213                module,
214                &signatures,
215                signature_hashes,
216                memory_styles,
217                table_styles,
218            )?;
219            context.func.name = match get_function_name(&mut context.func, func_index) {
220                ExternalName::User(nameref) => {
221                    if context.func.params.user_named_funcs().is_valid(nameref) {
222                        let name = &context.func.params.user_named_funcs()[nameref];
223                        UserFuncName::User(name.clone())
224                    } else {
225                        UserFuncName::default()
226                    }
227                }
228                ExternalName::TestCase(testcase) => UserFuncName::Testcase(testcase),
229                _ => UserFuncName::default(),
230            };
231            context.func.signature = signatures[module.functions[func_index]].clone();
232            // if generate_debug_info {
233            //     context.func.collect_debug_info();
234            // }
235
236            let mut reader =
237                MiddlewareBinaryReader::new_with_offset(input.data, input.module_offset);
238            reader.set_middleware_chain(
239                self.config
240                    .middlewares
241                    .generate_function_middleware_chain(*i),
242            );
243
244            func_translator.translate(
245                module_translation_state,
246                &mut reader,
247                &mut context.func,
248                &mut func_env,
249                *i,
250            )?;
251
252            if let Some(callbacks) = self.config.callbacks.as_ref() {
253                use wasmer_compiler::misc::CompiledKind;
254
255                callbacks.preopt_ir(
256                    &CompiledKind::Local(*i, compile_info.module.get_function_name(func_index)),
257                    &compile_info.module.hash_string(),
258                    context.func.display().to_string().as_bytes(),
259                );
260            }
261
262            let mut code_buf: Vec<u8> = Vec::new();
263            let mut ctrl_plane = Default::default();
264            let func_name_map = context.func.params.user_named_funcs().clone();
265            let result = context
266                .compile(&*isa, &mut ctrl_plane)
267                .map_err(|error| CompileError::Codegen(format!("{error:#?}")))?;
268            code_buf.extend_from_slice(result.code_buffer());
269
270            if let Some(callbacks) = self.config.callbacks.as_ref() {
271                use wasmer_compiler::misc::CompiledKind;
272
273                callbacks.obj_memory_buffer(
274                    &CompiledKind::Local(*i, compile_info.module.get_function_name(func_index)),
275                    &compile_info.module.hash_string(),
276                    &code_buf,
277                );
278                callbacks.asm_memory_buffer(
279                    &CompiledKind::Local(*i, compile_info.module.get_function_name(func_index)),
280                    &compile_info.module.hash_string(),
281                    target.triple().architecture,
282                    &code_buf,
283                )?;
284            }
285
286            let func_relocs = result
287                .buffer
288                .relocs()
289                .iter()
290                .map(|r| mach_reloc_to_reloc(module, &func_name_map, r))
291                .collect::<Vec<_>>();
292
293            let traps = result
294                .buffer
295                .traps()
296                .iter()
297                .map(mach_trap_to_trap)
298                .collect::<Vec<_>>();
299
300            #[cfg(feature = "unwind")]
301            let emit_lsda = dwarf_frametable.is_some() || emit_macho_compact_unwind;
302
303            #[cfg(feature = "unwind")]
304            let compact_unwind_encoding = if emit_macho_compact_unwind {
305                Some(
306                    compact_unwind_encoding_aarch64(&result.buffer.unwind_info).map_err(|error| {
307                        CompileError::Codegen(format!(
308                            "failed to encode aarch64 Mach-O compact unwind for function {}: {error}",
309                            i.index()
310                        ))
311                    })?,
312                )
313            } else {
314                None
315            };
316
317            #[cfg(feature = "unwind")]
318            let function_lsda = if emit_lsda {
319                build_function_lsda(
320                    result.buffer.call_sites(),
321                    result.buffer.data().len(),
322                    pointer_bytes,
323                    self.config.experimental_artifact,
324                )
325            } else {
326                None
327            };
328
329            #[allow(unused)]
330            let (unwind_info, fde) = match compiled_function_unwind_info(&*isa, &context)? {
331                #[cfg(feature = "unwind")]
332                CraneliftUnwindInfo::Fde(fde) => {
333                    if dwarf_frametable.is_some() {
334                        // For the ELF artifact format each function's
335                        // `.eh_frame` relocates against its own text symbol,
336                        // so the FDE's initial location must not be shifted.
337                        let addend = if self.config.experimental_artifact {
338                            0
339                        } else {
340                            // We use the addend as a way to specify the
341                            // function index
342                            i.index() as _
343                        };
344                        let fde = fde.to_fde(Address::Symbol {
345                            // The symbol is the kind of relocation.
346                            // "0" is used for functions
347                            symbol: WriterRelocate::FUNCTION_SYMBOL,
348                            addend,
349                        });
350                        // The unwind information is inserted into the dwarf section
351                        (Some(CompiledFunctionUnwindInfo::Dwarf), Some(fde))
352                    } else {
353                        (None, None)
354                    }
355                }
356                #[cfg(feature = "unwind")]
357                other => (other.maybe_into_to_windows_unwind(), None),
358
359                // This is a bit hacky, but necessary since gimli is not
360                // available when the "unwind" feature is disabled.
361                #[cfg(not(feature = "unwind"))]
362                other => (other.maybe_into_to_windows_unwind(), None::<()>),
363            };
364
365            let range = reader.range();
366            let address_map = get_function_address_map(&context, range, code_buf.len());
367
368            let compiled = CraneliftCompiledFunction {
369                function: CompiledFunction {
370                    body: FunctionBody {
371                        body: code_buf,
372                        unwind_info,
373                    },
374                    relocations: func_relocs,
375                    frame_info: CompiledFunctionFrameInfo { address_map, traps },
376                    maximum_stack_usage: None,
377                },
378                #[cfg(feature = "unwind")]
379                fde,
380                #[cfg(feature = "unwind")]
381                function_lsda,
382                #[cfg(feature = "unwind")]
383                compact_unwind_encoding,
384            };
385
386            if self.config.experimental_artifact {
387                let object = crate::elf::emit_local_function(
388                    #[cfg(feature = "unwind")]
389                    &*isa,
390                    target,
391                    *i,
392                    &compile_info.module.get_function_name(func_index),
393                    compile_info.module.name.as_deref(),
394                    &compiled.function,
395                    &source_map,
396                    #[cfg(feature = "unwind")]
397                    compiled.fde,
398                    #[cfg(feature = "unwind")]
399                    compiled.function_lsda,
400                )?;
401                Ok(CompileOutput::Object(object, None))
402            } else {
403                Ok(CompileOutput::InMemory(compiled))
404            }
405        };
406
407        #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
408        let mut custom_sections = PrimaryMap::new();
409
410        let num_threads = self.config.num_threads.get();
411        let pool = rayon::ThreadPoolBuilder::new()
412            .num_threads(num_threads)
413            .build()
414            .unwrap();
415        let results = {
416            use wasmer_compiler::WASM_LARGE_FUNCTION_THRESHOLD;
417
418            let buckets =
419                build_function_buckets(&function_body_inputs, WASM_LARGE_FUNCTION_THRESHOLD / 3);
420            let largest_bucket = buckets.first().map(|b| b.size).unwrap_or_default();
421            tracing::debug!(buckets = buckets.len(), largest_bucket, "buckets built");
422
423            translate_function_buckets(
424                &pool,
425                || FuncTranslator::new(self.config.allow_experimental_unaligned_memory_accesses),
426                |func_translator, i, input| compile_function(func_translator, i, input),
427                progress.clone(),
428                &buckets,
429            )?
430        };
431
432        let module_hash = module.hash_string();
433
434        // function call trampolines (only for local functions, by signature)
435        let function_call_trampoline_outputs = module
436            .signatures
437            .iter()
438            .collect_vec()
439            .par_iter()
440            .map_init(FunctionBuilderContext::new, |cx, (sig_index, sig)| {
441                let kind = wasmer_compiler::misc::CompiledKind::FunctionCallTrampoline(
442                    *sig_index,
443                    (*sig).clone(),
444                );
445                let trampoline = make_trampoline_function_call(
446                    &self.config().callbacks,
447                    &*isa,
448                    target.triple().architecture,
449                    cx,
450                    &kind,
451                    sig,
452                    &module_hash,
453                )?;
454                if let Some(progress) = progress.as_ref() {
455                    progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
456                }
457                if self.config.experimental_artifact {
458                    Ok(CompileOutput::Object(
459                        wasmer_compiler::elf::emit_function_body(target, &kind, &trampoline)?,
460                        None,
461                    ))
462                } else {
463                    Ok(CompileOutput::InMemory(trampoline))
464                }
465            })
466            .collect::<Result<Vec<_>, CompileError>>()?;
467
468        use wasmer_types::VMOffsets;
469        let offsets = VMOffsets::new_for_trampolines(frontend_config.pointer_bytes());
470        // dynamic function trampolines (only for imported functions)
471        let dynamic_function_trampoline_outputs = module
472            .imported_function_types()
473            .enumerate()
474            .collect_vec()
475            .par_iter()
476            .map_init(FunctionBuilderContext::new, |cx, (index, func_type)| {
477                let kind = wasmer_compiler::misc::CompiledKind::DynamicFunctionTrampoline(
478                    FunctionIndex::from_u32(*index as u32),
479                    func_type.clone(),
480                );
481                let trampoline = make_trampoline_dynamic_function(
482                    &self.config().callbacks,
483                    &*isa,
484                    target.triple().architecture,
485                    &offsets,
486                    cx,
487                    &kind,
488                    func_type,
489                    &module_hash,
490                )?;
491                if let Some(progress) = progress.as_ref() {
492                    progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
493                }
494                if self.config.experimental_artifact {
495                    Ok(CompileOutput::Object(
496                        wasmer_compiler::elf::emit_function_body(target, &kind, &trampoline)?,
497                        None,
498                    ))
499                } else {
500                    Ok(CompileOutput::InMemory(trampoline))
501                }
502            })
503            .collect::<Result<Vec<_>, CompileError>>()?;
504
505        if self.config.experimental_artifact {
506            let object_files = compile_output_objects(results);
507            let trampoline_objects = compile_output_objects(function_call_trampoline_outputs);
508            let dynamic_trampoline_objects =
509                compile_output_objects(dynamic_function_trampoline_outputs);
510            return wasmer_compiler::elf::link_module(
511                &pool,
512                target,
513                compile_info_blob,
514                object_files,
515                Vec::new(),
516                trampoline_objects,
517                dynamic_trampoline_objects,
518                self.config
519                    .callbacks
520                    .as_ref()
521                    .map(|callbacks| callbacks.debug_dir().clone()),
522                module.hash().map(|hash| hash.to_string()),
523                function_max_stack_usage,
524            );
525        }
526
527        let results = compile_output_in_memory(results);
528
529        let mut functions = Vec::with_capacity(function_body_inputs.len());
530        #[cfg(feature = "unwind")]
531        let mut fdes = Vec::with_capacity(function_body_inputs.len());
532        #[cfg(feature = "unwind")]
533        let mut lsda_data = Vec::with_capacity(function_body_inputs.len());
534        #[cfg(feature = "unwind")]
535        let mut compact_unwind_entries = Vec::new();
536
537        for compiled in results {
538            let CraneliftCompiledFunction {
539                function,
540                #[cfg(feature = "unwind")]
541                fde,
542                #[cfg(feature = "unwind")]
543                function_lsda,
544                #[cfg(feature = "unwind")]
545                compact_unwind_encoding,
546            } = compiled;
547            #[cfg(feature = "unwind")]
548            let local_function_index = LocalFunctionIndex::new(functions.len());
549            functions.push(function);
550            #[cfg(feature = "unwind")]
551            {
552                fdes.push(fde);
553                lsda_data.push(function_lsda);
554                if let Some(compact_encoding) = compact_unwind_encoding {
555                    let function_length = functions
556                        .last()
557                        .expect("function was just pushed")
558                        .body
559                        .body
560                        .len()
561                        .try_into()
562                        .map_err(|_| {
563                            CompileError::Codegen(
564                                "function body too large for Mach-O compact unwind".into(),
565                            )
566                        })?;
567                    compact_unwind_entries.push((
568                        local_function_index,
569                        function_length,
570                        compact_encoding,
571                    ));
572                }
573            }
574        }
575
576        #[cfg(feature = "unwind")]
577        let (_tag_section_index, lsda_section_index, function_lsda_offsets) =
578            if dwarf_frametable.is_some() || emit_macho_compact_unwind {
579                let mut tag_section_index = None;
580                let mut tag_offsets = HashMap::new();
581                if let Some((tag_section, offsets)) = build_tag_section(&lsda_data) {
582                    custom_sections.push(tag_section);
583                    tag_section_index = Some(SectionIndex::new(custom_sections.len() - 1));
584                    tag_offsets = offsets;
585                }
586                let lsda_vec = lsda_data;
587                let (lsda_section, offsets_per_function) =
588                    build_lsda_section(lsda_vec, pointer_bytes, &tag_offsets, tag_section_index);
589                let mut lsda_section_index = None;
590                if let Some(section) = lsda_section {
591                    custom_sections.push(section);
592                    lsda_section_index = Some(SectionIndex::new(custom_sections.len() - 1));
593                }
594                (tag_section_index, lsda_section_index, offsets_per_function)
595            } else {
596                (None, None, vec![None; functions.len()])
597            };
598
599        #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
600        let mut unwind_info = UnwindInfo::default();
601
602        #[cfg(feature = "unwind")]
603        if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
604            for (func_idx, fde_opt) in fdes.into_iter().enumerate() {
605                if let Some(mut fde) = fde_opt {
606                    let has_lsda = function_lsda_offsets
607                        .get(func_idx)
608                        .and_then(|v| *v)
609                        .is_some();
610                    let lsda_address = if has_lsda {
611                        debug_assert!(
612                            lsda_section_index.is_some(),
613                            "LSDA offsets require an LSDA section"
614                        );
615                        if lsda_section_index.is_some() {
616                            let symbol =
617                                WriterRelocate::lsda_symbol(LocalFunctionIndex::new(func_idx));
618                            Address::Symbol { symbol, addend: 0 }
619                        } else {
620                            Address::Constant(0)
621                        }
622                    } else {
623                        Address::Constant(0)
624                    };
625                    fde.lsda = Some(lsda_address);
626                    dwarf_frametable.add_fde(cie_id, fde);
627                }
628            }
629
630            let mut writer = WriterRelocate::new(target.triple().endianness().ok());
631            if let Some(lsda_section_index) = lsda_section_index {
632                for (func_idx, offset) in function_lsda_offsets.iter().enumerate() {
633                    if let Some(offset) = offset {
634                        writer.register_lsda_symbol(
635                            WriterRelocate::lsda_symbol(LocalFunctionIndex::new(func_idx)),
636                            RelocationTarget::CustomSection(lsda_section_index),
637                            *offset,
638                        );
639                    }
640                }
641            }
642
643            let mut eh_frame = EhFrame(writer);
644            dwarf_frametable.write_eh_frame(&mut eh_frame).unwrap();
645            eh_frame.write(&[0, 0, 0, 0]).unwrap(); // Write a 0 length at the end of the table.
646
647            let eh_frame_section = eh_frame.0.into_section();
648            custom_sections.push(eh_frame_section);
649            unwind_info.eh_frame = Some(SectionIndex::new(custom_sections.len() - 1));
650        };
651
652        #[cfg(feature = "unwind")]
653        if emit_macho_compact_unwind {
654            let entries = compact_unwind_entries
655                .into_iter()
656                .map(|(function, function_length, compact_encoding)| {
657                    let lsda_offset = function_lsda_offsets
658                        .get(function.index())
659                        .and_then(|offset| *offset);
660                    CompactUnwindEntryData {
661                        function,
662                        function_length,
663                        compact_encoding,
664                        lsda_offset,
665                    }
666                })
667                .collect::<Vec<_>>();
668            if let Some(section) = build_compact_unwind_section(entries, lsda_section_index) {
669                custom_sections.push(section);
670                unwind_info.compact_unwind = Some(SectionIndex::new(custom_sections.len() - 1));
671            }
672        }
673
674        let function_call_trampolines = compile_output_in_memory(function_call_trampoline_outputs)
675            .into_iter()
676            .collect();
677        let dynamic_function_trampolines =
678            compile_output_in_memory(dynamic_function_trampoline_outputs)
679                .into_iter()
680                .collect();
681
682        let mut got = wasmer_compiler::types::function::GOT::empty();
683
684        #[cfg(feature = "unwind")]
685        if emit_macho_compact_unwind {
686            let got_idx = SectionIndex::from_u32(custom_sections.len() as u32);
687            custom_sections.push(CustomSection {
688                protection: CustomSectionProtection::Read,
689                alignment: Some(pointer_bytes.into()),
690                bytes: SectionBody::new_with_vec(vec![0; pointer_bytes as usize]),
691                relocations: vec![Relocation {
692                    kind: match pointer_bytes {
693                        4 => RelocationKind::Abs4,
694                        8 => RelocationKind::Abs8,
695                        _ => unreachable!("unsupported pointer size for Mach-O compact unwind GOT"),
696                    },
697                    reloc_target: RelocationTarget::LibCall(LibCall::EHPersonality),
698                    offset: 0,
699                    addend: 0,
700                }],
701            });
702            got.index = Some(got_idx);
703        }
704
705        Ok(Compilation::Rkyv {
706            compilation: RkyvCompilation {
707                functions: functions.into_iter().collect(),
708                custom_sections,
709                function_call_trampolines,
710                dynamic_function_trampolines,
711                unwind_info,
712                got,
713            },
714            function_max_stack_usage,
715        })
716    }
717}
718
719impl Compiler for CraneliftCompiler {
720    fn name(&self) -> &str {
721        "cranelift"
722    }
723
724    fn get_perfmap_enabled(&self) -> bool {
725        self.config.enable_perfmap
726    }
727
728    fn get_debugger(&self) -> Option<wasmer_compiler::Debugger> {
729        self.config.debugger
730    }
731
732    fn deterministic_id(&self) -> String {
733        use wasmer_compiler::DeterministicIdComponent as Component;
734
735        let mut components = vec![Component::Cranelift];
736        components.push(match self.config.opt_level {
737            CraneliftOptLevel::None => Component::OptNone,
738            CraneliftOptLevel::Speed => Component::OptSpeed,
739            CraneliftOptLevel::SpeedAndSize => Component::OptSpeedAndSize,
740        });
741        if self.config.enable_nan_canonicalization {
742            components.push(Component::NanCanonicalization);
743        }
744        if self.config.enable_pic {
745            components.push(Component::Pic);
746        }
747        if self.config.allow_experimental_unaligned_memory_accesses {
748            components.push(Component::ExperimentalUnalignedMemoryAccesses);
749        }
750
751        components
752            .into_iter()
753            .map(|component| component.to_string())
754            .collect_vec()
755            .join("-")
756    }
757
758    fn artifact_format(&self) -> String {
759        if self.config.experimental_artifact {
760            wasmer_compiler::ArtifactFormat::Native
761        } else {
762            wasmer_compiler::ArtifactFormat::Rkyv
763        }
764        .to_string()
765    }
766
767    /// Get the middlewares for this compiler
768    fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
769        &self.config.middlewares
770    }
771
772    /// Compile the module using Cranelift, producing a compilation result with
773    /// associated relocations.
774    fn compile_module(
775        &self,
776        target: &Target,
777        compile_info: &CompileModuleInfo,
778        compile_info_blob: &[u8],
779        module_translation_state: &ModuleTranslationState,
780        function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
781        progress_callback: Option<&CompilationProgressCallback>,
782    ) -> Result<Compilation, CompileError> {
783        self.compile_module_internal(
784            target,
785            compile_info,
786            compile_info_blob,
787            module_translation_state,
788            function_body_inputs,
789            progress_callback,
790        )
791    }
792}
793
794fn mach_reloc_to_reloc(
795    module: &ModuleInfo,
796    func_index_map: &cranelift_entity::PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
797    reloc: &FinalizedMachReloc,
798) -> Relocation {
799    let FinalizedMachReloc {
800        offset,
801        kind,
802        addend,
803        target,
804    } = &reloc;
805    let name = match target {
806        FinalizedRelocTarget::ExternalName(external_name) => external_name,
807        FinalizedRelocTarget::Func(_) => {
808            unimplemented!("relocations to offset in the same function are not yet supported")
809        }
810    };
811    let reloc_target: RelocationTarget = if let ExternalName::User(extname_ref) = name {
812        let func_index = func_index_map[*extname_ref].index;
813        //debug_assert_eq!(namespace, 0);
814        RelocationTarget::LocalFunc(
815            module
816                .local_func_index(FunctionIndex::from_u32(func_index))
817                .expect("The provided function should be local"),
818        )
819    } else if let ExternalName::LibCall(libcall) = name {
820        RelocationTarget::LibCall(irlibcall_to_libcall(*libcall))
821    } else {
822        panic!("unrecognized external target")
823    };
824    Relocation {
825        kind: irreloc_to_relocationkind(*kind),
826        reloc_target,
827        offset: *offset,
828        addend: *addend,
829    }
830}
831
832fn mach_trap_to_trap(trap: &MachTrap) -> TrapInformation {
833    let &MachTrap { offset, code } = trap;
834    TrapInformation {
835        code_offset: offset,
836        trap_code: translate_ir_trapcode(code),
837    }
838}
839
840/// Translates the Cranelift IR TrapCode into generic Trap Code
841fn translate_ir_trapcode(trap: ir::TrapCode) -> TrapCode {
842    if trap == ir::TrapCode::STACK_OVERFLOW {
843        TrapCode::StackOverflow
844    } else if trap == ir::TrapCode::HEAP_OUT_OF_BOUNDS {
845        TrapCode::HeapAccessOutOfBounds
846    } else if trap == crate::TRAP_HEAP_MISALIGNED {
847        TrapCode::UnalignedAtomic
848    } else if trap == crate::TRAP_TABLE_OUT_OF_BOUNDS {
849        TrapCode::TableAccessOutOfBounds
850    } else if trap == crate::TRAP_INDIRECT_CALL_TO_NULL {
851        TrapCode::IndirectCallToNull
852    } else if trap == crate::TRAP_BAD_SIGNATURE {
853        TrapCode::BadSignature
854    } else if trap == ir::TrapCode::INTEGER_OVERFLOW {
855        TrapCode::IntegerOverflow
856    } else if trap == ir::TrapCode::INTEGER_DIVISION_BY_ZERO {
857        TrapCode::IntegerDivisionByZero
858    } else if trap == ir::TrapCode::BAD_CONVERSION_TO_INTEGER {
859        TrapCode::BadConversionToInteger
860    } else if trap == crate::TRAP_UNREACHABLE {
861        TrapCode::UnreachableCodeReached
862    } else if trap == crate::TRAP_INTERRUPT {
863        unimplemented!("Interrupts not supported")
864    } else if trap == crate::TRAP_NULL_REFERENCE || trap == crate::TRAP_NULL_I31_REF {
865        unimplemented!("Null reference not supported")
866    } else {
867        unimplemented!("Trap code {trap:?} not supported")
868    }
869}