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