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,
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 gimli::{
33    constants::DW_EH_PE_absptr,
34    write::{Address, EhFrame, FrameDescriptionEntry, FrameTable, Writer},
35};
36
37use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
38#[cfg(feature = "unwind")]
39use std::collections::HashMap;
40use std::sync::Arc;
41use wasmer_compiler::WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE;
42
43use wasmer_compiler::progress::ProgressContext;
44#[cfg(feature = "unwind")]
45use wasmer_compiler::types::{section::SectionIndex, unwind::CompiledFunctionUnwindInfo};
46use wasmer_compiler::{
47    Compiler, FunctionBinaryReader, FunctionBodyData, MiddlewareBinaryReader, ModuleMiddleware,
48    ModuleMiddlewareChain, ModuleTranslationState,
49    types::{
50        function::{
51            Compilation, CompiledFunction, CompiledFunctionFrameInfo, FunctionBody, UnwindInfo,
52        },
53        module::CompileModuleInfo,
54        relocation::{Relocation, RelocationKind, RelocationTarget},
55        section::{CustomSection, CustomSectionProtection, SectionBody},
56    },
57};
58use wasmer_compiler::{build_function_buckets, translate_function_buckets};
59#[cfg(feature = "unwind")]
60use wasmer_types::LibCall;
61#[cfg(feature = "unwind")]
62use wasmer_types::entity::EntityRef;
63use wasmer_types::entity::PrimaryMap;
64#[cfg(feature = "unwind")]
65use wasmer_types::target::CallingConvention;
66use wasmer_types::target::Target;
67use wasmer_types::{
68    CompilationProgressCallback, CompileError, FunctionIndex, LocalFunctionIndex, ModuleInfo,
69    SignatureIndex, TrapCode, TrapInformation,
70};
71
72pub struct CraneliftCompiledFunction {
73    function: CompiledFunction,
74    #[cfg(feature = "unwind")]
75    fde: Option<FrameDescriptionEntry>,
76    #[cfg(feature = "unwind")]
77    function_lsda: Option<FunctionLsdaData>,
78    #[cfg(feature = "unwind")]
79    compact_unwind_encoding: Option<u32>,
80}
81
82impl wasmer_compiler::CompiledFunction for CraneliftCompiledFunction {}
83
84/// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR,
85/// optimizing it and then translating to assembly.
86#[derive(Debug)]
87pub struct CraneliftCompiler {
88    config: Cranelift,
89}
90
91impl CraneliftCompiler {
92    /// Creates a new Cranelift compiler
93    pub fn new(config: Cranelift) -> Self {
94        Self { config }
95    }
96
97    /// Gets the WebAssembly features for this Compiler
98    pub fn config(&self) -> &Cranelift {
99        &self.config
100    }
101
102    // Helper function to create an easy scope boundary for the thread pool used
103    // in [`Self::compile_module`].
104    fn compile_module_internal(
105        &self,
106        target: &Target,
107        compile_info: &CompileModuleInfo,
108        module_translation_state: &ModuleTranslationState,
109        function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
110        progress_callback: Option<&CompilationProgressCallback>,
111    ) -> Result<Compilation, CompileError> {
112        let isa = self
113            .config()
114            .isa(target)
115            .map_err(|error| CompileError::Codegen(error.to_string()))?;
116        let frontend_config = isa.frontend_config();
117        #[cfg(feature = "unwind")]
118        let pointer_bytes = frontend_config.pointer_bytes();
119        #[cfg(feature = "unwind")]
120        let emit_macho_compact_unwind = matches!(
121            target.triple(),
122            target_lexicon::Triple {
123                binary_format: target_lexicon::BinaryFormat::Macho,
124                operating_system: target_lexicon::OperatingSystem::Darwin(_),
125                architecture: target_lexicon::Architecture::Aarch64(_),
126                ..
127            }
128        );
129        let memory_styles = &compile_info.memory_styles;
130        let table_styles = &compile_info.table_styles;
131        let module = &compile_info.module;
132        let signatures = module
133            .signatures
134            .iter()
135            .map(|(_sig_index, func_type)| signature_to_cranelift_ir(func_type, frontend_config))
136            .collect::<PrimaryMap<SignatureIndex, ir::Signature>>();
137        let signature_hashes = &module.signature_hashes;
138
139        let total_function_call_trampolines = module.signatures.len();
140        let total_dynamic_trampolines = module.num_imported_functions;
141        let total_steps = WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE
142            * ((total_dynamic_trampolines + total_function_call_trampolines) as u64)
143            + function_body_inputs
144                .iter()
145                .map(|(_, body)| body.data.len() as u64)
146                .sum::<u64>();
147        let progress = progress_callback
148            .cloned()
149            .map(|cb| ProgressContext::new(cb, total_steps, "cranelift::functions"));
150
151        // Generate the frametable
152        #[cfg(feature = "unwind")]
153        let dwarf_frametable = if function_body_inputs.is_empty() {
154            // If we have no function body inputs, we don't need to
155            // construct the `FrameTable`. Constructing it, with empty
156            // FDEs will cause some issues in Linux.
157            None
158        } else {
159            match target.triple().default_calling_convention() {
160                Ok(CallingConvention::SystemV) => match isa.create_systemv_cie() {
161                    Some(mut cie) => {
162                        cie.personality = Some((
163                            DW_EH_PE_absptr,
164                            Address::Symbol {
165                                symbol: WriterRelocate::PERSONALITY_SYMBOL,
166                                addend: 0,
167                            },
168                        ));
169                        cie.lsda_encoding = Some(DW_EH_PE_absptr);
170                        let mut dwarf_frametable = FrameTable::default();
171                        let cie_id = dwarf_frametable.add_cie(cie);
172                        Some((dwarf_frametable, cie_id))
173                    }
174                    // Even though we are in a SystemV system, Cranelift doesn't support it
175                    None => None,
176                },
177                _ => None,
178            }
179        };
180
181        // The `compile_function` closure is used for both the sequential and
182        // parallel compilation paths to avoid code duplication.
183        let compile_function = |func_translator: &mut FuncTranslator,
184                                i: &LocalFunctionIndex,
185                                input: &FunctionBodyData|
186         -> Result<CraneliftCompiledFunction, CompileError> {
187            let func_index = module.func_index(*i);
188            let mut context = Context::new();
189            let mut func_env = FuncEnvironment::new(
190                isa.frontend_config(),
191                module,
192                &signatures,
193                signature_hashes,
194                memory_styles,
195                table_styles,
196            );
197            context.func.name = match get_function_name(&mut context.func, func_index) {
198                ExternalName::User(nameref) => {
199                    if context.func.params.user_named_funcs().is_valid(nameref) {
200                        let name = &context.func.params.user_named_funcs()[nameref];
201                        UserFuncName::User(name.clone())
202                    } else {
203                        UserFuncName::default()
204                    }
205                }
206                ExternalName::TestCase(testcase) => UserFuncName::Testcase(testcase),
207                _ => UserFuncName::default(),
208            };
209            context.func.signature = signatures[module.functions[func_index]].clone();
210            // if generate_debug_info {
211            //     context.func.collect_debug_info();
212            // }
213
214            let mut reader =
215                MiddlewareBinaryReader::new_with_offset(input.data, input.module_offset);
216            reader.set_middleware_chain(
217                self.config
218                    .middlewares
219                    .generate_function_middleware_chain(*i),
220            );
221
222            func_translator.translate(
223                module_translation_state,
224                &mut reader,
225                &mut context.func,
226                &mut func_env,
227                *i,
228            )?;
229
230            if let Some(callbacks) = self.config.callbacks.as_ref() {
231                use wasmer_compiler::misc::CompiledKind;
232
233                callbacks.preopt_ir(
234                    &CompiledKind::Local(*i, compile_info.module.get_function_name(func_index)),
235                    &compile_info.module.hash_string(),
236                    context.func.display().to_string().as_bytes(),
237                );
238            }
239
240            let mut code_buf: Vec<u8> = Vec::new();
241            let mut ctrl_plane = Default::default();
242            let func_name_map = context.func.params.user_named_funcs().clone();
243            let result = context
244                .compile(&*isa, &mut ctrl_plane)
245                .map_err(|error| CompileError::Codegen(format!("{error:#?}")))?;
246            code_buf.extend_from_slice(result.code_buffer());
247
248            if let Some(callbacks) = self.config.callbacks.as_ref() {
249                use wasmer_compiler::misc::CompiledKind;
250
251                callbacks.obj_memory_buffer(
252                    &CompiledKind::Local(*i, compile_info.module.get_function_name(func_index)),
253                    &compile_info.module.hash_string(),
254                    &code_buf,
255                );
256                callbacks.asm_memory_buffer(
257                    &CompiledKind::Local(*i, compile_info.module.get_function_name(func_index)),
258                    &compile_info.module.hash_string(),
259                    target.triple().architecture,
260                    &code_buf,
261                )?;
262            }
263
264            let func_relocs = result
265                .buffer
266                .relocs()
267                .iter()
268                .map(|r| mach_reloc_to_reloc(module, &func_name_map, r))
269                .collect::<Vec<_>>();
270
271            let traps = result
272                .buffer
273                .traps()
274                .iter()
275                .map(mach_trap_to_trap)
276                .collect::<Vec<_>>();
277
278            #[cfg(feature = "unwind")]
279            let emit_lsda = dwarf_frametable.is_some() || emit_macho_compact_unwind;
280
281            #[cfg(feature = "unwind")]
282            let compact_unwind_encoding = if emit_macho_compact_unwind {
283                Some(
284                    compact_unwind_encoding_aarch64(&result.buffer.unwind_info).map_err(|error| {
285                        CompileError::Codegen(format!(
286                            "failed to encode aarch64 Mach-O compact unwind for function {}: {error}",
287                            i.index()
288                        ))
289                    })?,
290                )
291            } else {
292                None
293            };
294
295            #[cfg(feature = "unwind")]
296            let function_lsda = if emit_lsda {
297                build_function_lsda(
298                    result.buffer.call_sites(),
299                    result.buffer.data().len(),
300                    pointer_bytes,
301                )
302            } else {
303                None
304            };
305
306            #[allow(unused)]
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(CraneliftCompiledFunction {
338                function: CompiledFunction {
339                    body: FunctionBody {
340                        body: code_buf,
341                        unwind_info,
342                    },
343                    relocations: func_relocs,
344                    frame_info: CompiledFunctionFrameInfo { address_map, traps },
345                    maximum_stack_usage: None,
346                },
347                #[cfg(feature = "unwind")]
348                fde,
349                #[cfg(feature = "unwind")]
350                function_lsda,
351                #[cfg(feature = "unwind")]
352                compact_unwind_encoding,
353            })
354        };
355
356        #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
357        let mut custom_sections = PrimaryMap::new();
358
359        let results = {
360            use wasmer_compiler::WASM_LARGE_FUNCTION_THRESHOLD;
361
362            let buckets =
363                build_function_buckets(&function_body_inputs, WASM_LARGE_FUNCTION_THRESHOLD / 3);
364            let largest_bucket = buckets.first().map(|b| b.size).unwrap_or_default();
365            tracing::debug!(buckets = buckets.len(), largest_bucket, "buckets built");
366            let num_threads = self.config.num_threads.get();
367            let pool = rayon::ThreadPoolBuilder::new()
368                .num_threads(num_threads)
369                .build()
370                .unwrap();
371
372            translate_function_buckets(
373                &pool,
374                || FuncTranslator::new(self.config.allow_experimental_unaligned_memory_accesses),
375                |func_translator, i, input| compile_function(func_translator, i, input),
376                progress.clone(),
377                &buckets,
378            )?
379        };
380
381        let mut functions = Vec::with_capacity(function_body_inputs.len());
382        #[cfg(feature = "unwind")]
383        let mut fdes = Vec::with_capacity(function_body_inputs.len());
384        #[cfg(feature = "unwind")]
385        let mut lsda_data = Vec::with_capacity(function_body_inputs.len());
386        #[cfg(feature = "unwind")]
387        let mut compact_unwind_entries = Vec::new();
388
389        for compiled in results {
390            let CraneliftCompiledFunction {
391                function,
392                #[cfg(feature = "unwind")]
393                fde,
394                #[cfg(feature = "unwind")]
395                function_lsda,
396                #[cfg(feature = "unwind")]
397                compact_unwind_encoding,
398            } = compiled;
399            #[cfg(feature = "unwind")]
400            let local_function_index = LocalFunctionIndex::new(functions.len());
401            functions.push(function);
402            #[cfg(feature = "unwind")]
403            {
404                fdes.push(fde);
405                lsda_data.push(function_lsda);
406                if let Some(compact_encoding) = compact_unwind_encoding {
407                    let function_length = functions
408                        .last()
409                        .expect("function was just pushed")
410                        .body
411                        .body
412                        .len()
413                        .try_into()
414                        .map_err(|_| {
415                            CompileError::Codegen(
416                                "function body too large for Mach-O compact unwind".into(),
417                            )
418                        })?;
419                    compact_unwind_entries.push((
420                        local_function_index,
421                        function_length,
422                        compact_encoding,
423                    ));
424                }
425            }
426        }
427
428        #[cfg(feature = "unwind")]
429        let (_tag_section_index, lsda_section_index, function_lsda_offsets) =
430            if dwarf_frametable.is_some() || emit_macho_compact_unwind {
431                let mut tag_section_index = None;
432                let mut tag_offsets = HashMap::new();
433                if let Some((tag_section, offsets)) = build_tag_section(&lsda_data) {
434                    custom_sections.push(tag_section);
435                    tag_section_index = Some(SectionIndex::new(custom_sections.len() - 1));
436                    tag_offsets = offsets;
437                }
438                let lsda_vec = lsda_data;
439                let (lsda_section, offsets_per_function) =
440                    build_lsda_section(lsda_vec, pointer_bytes, &tag_offsets, tag_section_index);
441                let mut lsda_section_index = None;
442                if let Some(section) = lsda_section {
443                    custom_sections.push(section);
444                    lsda_section_index = Some(SectionIndex::new(custom_sections.len() - 1));
445                }
446                (tag_section_index, lsda_section_index, offsets_per_function)
447            } else {
448                (None, None, vec![None; functions.len()])
449            };
450
451        #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
452        let mut unwind_info = UnwindInfo::default();
453
454        #[cfg(feature = "unwind")]
455        if let Some((mut dwarf_frametable, cie_id)) = dwarf_frametable {
456            for (func_idx, fde_opt) in fdes.into_iter().enumerate() {
457                if let Some(mut fde) = fde_opt {
458                    let has_lsda = function_lsda_offsets
459                        .get(func_idx)
460                        .and_then(|v| *v)
461                        .is_some();
462                    let lsda_address = if has_lsda {
463                        debug_assert!(
464                            lsda_section_index.is_some(),
465                            "LSDA offsets require an LSDA section"
466                        );
467                        if lsda_section_index.is_some() {
468                            let symbol =
469                                WriterRelocate::lsda_symbol(LocalFunctionIndex::new(func_idx));
470                            Address::Symbol { symbol, addend: 0 }
471                        } else {
472                            Address::Constant(0)
473                        }
474                    } else {
475                        Address::Constant(0)
476                    };
477                    fde.lsda = Some(lsda_address);
478                    dwarf_frametable.add_fde(cie_id, fde);
479                }
480            }
481
482            let mut writer = WriterRelocate::new(target.triple().endianness().ok());
483            if let Some(lsda_section_index) = lsda_section_index {
484                for (func_idx, offset) in function_lsda_offsets.iter().enumerate() {
485                    if let Some(offset) = offset {
486                        writer.register_lsda_symbol(
487                            WriterRelocate::lsda_symbol(LocalFunctionIndex::new(func_idx)),
488                            RelocationTarget::CustomSection(lsda_section_index),
489                            *offset,
490                        );
491                    }
492                }
493            }
494
495            let mut eh_frame = EhFrame(writer);
496            dwarf_frametable.write_eh_frame(&mut eh_frame).unwrap();
497            eh_frame.write(&[0, 0, 0, 0]).unwrap(); // Write a 0 length at the end of the table.
498
499            let eh_frame_section = eh_frame.0.into_section();
500            custom_sections.push(eh_frame_section);
501            unwind_info.eh_frame = Some(SectionIndex::new(custom_sections.len() - 1));
502        };
503
504        #[cfg(feature = "unwind")]
505        if emit_macho_compact_unwind {
506            let entries = compact_unwind_entries
507                .into_iter()
508                .map(|(function, function_length, compact_encoding)| {
509                    let lsda_offset = function_lsda_offsets
510                        .get(function.index())
511                        .and_then(|offset| *offset);
512                    CompactUnwindEntryData {
513                        function,
514                        function_length,
515                        compact_encoding,
516                        lsda_offset,
517                    }
518                })
519                .collect::<Vec<_>>();
520            if let Some(section) = build_compact_unwind_section(entries, lsda_section_index) {
521                custom_sections.push(section);
522                unwind_info.compact_unwind = Some(SectionIndex::new(custom_sections.len() - 1));
523            }
524        }
525
526        let module_hash = module.hash_string();
527
528        // function call trampolines (only for local functions, by signature)
529        let function_call_trampolines = module
530            .signatures
531            .values()
532            .collect::<Vec<_>>()
533            .par_iter()
534            .map_init(FunctionBuilderContext::new, |cx, sig| {
535                let trampoline = make_trampoline_function_call(
536                    &self.config().callbacks,
537                    &*isa,
538                    target.triple().architecture,
539                    cx,
540                    sig,
541                    &module_hash,
542                )?;
543                if let Some(progress) = progress.as_ref() {
544                    progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
545                }
546                Ok(trampoline)
547            })
548            .collect::<Result<Vec<FunctionBody>, CompileError>>()?
549            .into_iter()
550            .collect();
551
552        use wasmer_types::VMOffsets;
553        let offsets = VMOffsets::new_for_trampolines(frontend_config.pointer_bytes());
554        // dynamic function trampolines (only for imported functions)
555        let dynamic_function_trampolines = module
556            .imported_function_types()
557            .collect::<Vec<_>>()
558            .par_iter()
559            .map_init(FunctionBuilderContext::new, |cx, func_type| {
560                let trampoline = make_trampoline_dynamic_function(
561                    &self.config().callbacks,
562                    &*isa,
563                    target.triple().architecture,
564                    &offsets,
565                    cx,
566                    func_type,
567                    &module_hash,
568                )?;
569                if let Some(progress) = progress.as_ref() {
570                    progress.notify_steps(WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE)?;
571                }
572                Ok(trampoline)
573            })
574            .collect::<Result<Vec<_>, CompileError>>()?
575            .into_iter()
576            .collect();
577
578        let mut got = wasmer_compiler::types::function::GOT::empty();
579
580        #[cfg(feature = "unwind")]
581        if emit_macho_compact_unwind {
582            let got_idx = SectionIndex::from_u32(custom_sections.len() as u32);
583            custom_sections.push(CustomSection {
584                protection: CustomSectionProtection::Read,
585                alignment: Some(pointer_bytes.into()),
586                bytes: SectionBody::new_with_vec(vec![0; pointer_bytes as usize]),
587                relocations: vec![Relocation {
588                    kind: match pointer_bytes {
589                        4 => RelocationKind::Abs4,
590                        8 => RelocationKind::Abs8,
591                        _ => unreachable!("unsupported pointer size for Mach-O compact unwind GOT"),
592                    },
593                    reloc_target: RelocationTarget::LibCall(LibCall::EHPersonality),
594                    offset: 0,
595                    addend: 0,
596                }],
597            });
598            got.index = Some(got_idx);
599        }
600
601        Ok(Compilation {
602            functions: functions.into_iter().collect(),
603            custom_sections,
604            function_call_trampolines,
605            dynamic_function_trampolines,
606            unwind_info,
607            got,
608        })
609    }
610}
611
612impl Compiler for CraneliftCompiler {
613    fn name(&self) -> &str {
614        "cranelift"
615    }
616
617    fn get_perfmap_enabled(&self) -> bool {
618        self.config.enable_perfmap
619    }
620
621    fn deterministic_id(&self) -> String {
622        String::from("cranelift")
623    }
624
625    /// Get the middlewares for this compiler
626    fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
627        &self.config.middlewares
628    }
629
630    /// Compile the module using Cranelift, producing a compilation result with
631    /// associated relocations.
632    fn compile_module(
633        &self,
634        target: &Target,
635        compile_info: &CompileModuleInfo,
636        module_translation_state: &ModuleTranslationState,
637        function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
638        progress_callback: Option<&CompilationProgressCallback>,
639    ) -> Result<Compilation, CompileError> {
640        self.compile_module_internal(
641            target,
642            compile_info,
643            module_translation_state,
644            function_body_inputs,
645            progress_callback,
646        )
647    }
648}
649
650fn mach_reloc_to_reloc(
651    module: &ModuleInfo,
652    func_index_map: &cranelift_entity::PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
653    reloc: &FinalizedMachReloc,
654) -> Relocation {
655    let FinalizedMachReloc {
656        offset,
657        kind,
658        addend,
659        target,
660    } = &reloc;
661    let name = match target {
662        FinalizedRelocTarget::ExternalName(external_name) => external_name,
663        FinalizedRelocTarget::Func(_) => {
664            unimplemented!("relocations to offset in the same function are not yet supported")
665        }
666    };
667    let reloc_target: RelocationTarget = if let ExternalName::User(extname_ref) = name {
668        let func_index = func_index_map[*extname_ref].index;
669        //debug_assert_eq!(namespace, 0);
670        RelocationTarget::LocalFunc(
671            module
672                .local_func_index(FunctionIndex::from_u32(func_index))
673                .expect("The provided function should be local"),
674        )
675    } else if let ExternalName::LibCall(libcall) = name {
676        RelocationTarget::LibCall(irlibcall_to_libcall(*libcall))
677    } else {
678        panic!("unrecognized external target")
679    };
680    Relocation {
681        kind: irreloc_to_relocationkind(*kind),
682        reloc_target,
683        offset: *offset,
684        addend: *addend,
685    }
686}
687
688fn mach_trap_to_trap(trap: &MachTrap) -> TrapInformation {
689    let &MachTrap { offset, code } = trap;
690    TrapInformation {
691        code_offset: offset,
692        trap_code: translate_ir_trapcode(code),
693    }
694}
695
696/// Translates the Cranelift IR TrapCode into generic Trap Code
697fn translate_ir_trapcode(trap: ir::TrapCode) -> TrapCode {
698    if trap == ir::TrapCode::STACK_OVERFLOW {
699        TrapCode::StackOverflow
700    } else if trap == ir::TrapCode::HEAP_OUT_OF_BOUNDS {
701        TrapCode::HeapAccessOutOfBounds
702    } else if trap == crate::TRAP_HEAP_MISALIGNED {
703        TrapCode::UnalignedAtomic
704    } else if trap == crate::TRAP_TABLE_OUT_OF_BOUNDS {
705        TrapCode::TableAccessOutOfBounds
706    } else if trap == crate::TRAP_INDIRECT_CALL_TO_NULL {
707        TrapCode::IndirectCallToNull
708    } else if trap == crate::TRAP_BAD_SIGNATURE {
709        TrapCode::BadSignature
710    } else if trap == ir::TrapCode::INTEGER_OVERFLOW {
711        TrapCode::IntegerOverflow
712    } else if trap == ir::TrapCode::INTEGER_DIVISION_BY_ZERO {
713        TrapCode::IntegerDivisionByZero
714    } else if trap == ir::TrapCode::BAD_CONVERSION_TO_INTEGER {
715        TrapCode::BadConversionToInteger
716    } else if trap == crate::TRAP_UNREACHABLE {
717        TrapCode::UnreachableCodeReached
718    } else if trap == crate::TRAP_INTERRUPT {
719        unimplemented!("Interrupts not supported")
720    } else if trap == crate::TRAP_NULL_REFERENCE || trap == crate::TRAP_NULL_I31_REF {
721        unimplemented!("Null reference not supported")
722    } else {
723        unimplemented!("Trap code {trap:?} not supported")
724    }
725}