1#[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#[derive(Debug)]
87pub struct CraneliftCompiler {
88 config: Cranelift,
89}
90
91impl CraneliftCompiler {
92 pub fn new(config: Cranelift) -> Self {
94 Self { config }
95 }
96
97 pub fn config(&self) -> &Cranelift {
99 &self.config
100 }
101
102 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 #[cfg(feature = "unwind")]
153 let dwarf_frametable = if function_body_inputs.is_empty() {
154 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 None => None,
176 },
177 _ => None,
178 }
179 };
180
181 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 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 symbol: WriterRelocate::FUNCTION_SYMBOL,
315 addend: i.index() as _,
318 });
319 (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 #[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(); 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 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 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 fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
627 &self.config.middlewares
628 }
629
630 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 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
696fn 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}