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