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