1#![allow(unused)]
3
4use crate::{
5 abi::{Abi, get_abi},
6 config::LLVM,
7 error::{err, err_nt},
8 object_file::{CompiledFunction, load_object_file},
9 translator::intrinsics::{Intrinsics, type_to_llvm},
10};
11use inkwell::{
12 AddressSpace, DLLStorageClass,
13 attributes::{Attribute, AttributeLoc},
14 context::Context,
15 module::{Linkage, Module},
16 passes::PassBuilderOptions,
17 targets::{FileType, TargetMachine},
18 types::FunctionType,
19 values::{BasicMetadataValueEnum, FunctionValue},
20};
21use std::{cmp, convert::TryInto};
22use target_lexicon::{BinaryFormat, Triple};
23use wasmer_compiler::{
24 misc::CompiledKind,
25 types::{
26 function::FunctionBody,
27 module::CompileModuleInfo,
28 relocation::{Relocation, RelocationTarget},
29 section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex},
30 },
31};
32use wasmer_types::{
33 CompileError, FunctionIndex, FunctionType as FuncType, LocalFunctionIndex, MemoryIndex,
34 entity::PrimaryMap,
35};
36use wasmer_vm::MemoryStyle;
37
38pub struct FuncTrampoline {
39 ctx: Context,
40 target_machine: TargetMachine,
41 target_triple: Triple,
42 abi: Box<dyn Abi>,
43 binary_fmt: BinaryFormat,
44 func_section: String,
45}
46
47const FUNCTION_SECTION_ELF: &str = "__TEXT,wasmer_trmpl"; const FUNCTION_SECTION_MACHO: &str = "wasmer_trmpl"; fn enable_m0_optimization(compile_info: &CompileModuleInfo) -> bool {
51 compile_info
52 .memory_styles
53 .get(MemoryIndex::from_u32(0))
54 .is_some_and(|memory| matches!(memory, MemoryStyle::Static { .. }))
55}
56
57impl FuncTrampoline {
58 pub fn new(
59 target_machine: TargetMachine,
60 target_triple: Triple,
61 binary_fmt: BinaryFormat,
62 ) -> Result<Self, CompileError> {
63 let abi = get_abi(&target_machine);
64 Ok(Self {
65 ctx: Context::create(),
66 target_machine,
67 target_triple,
68 abi,
69 func_section: match binary_fmt {
70 BinaryFormat::Elf => FUNCTION_SECTION_ELF.to_string(),
71 BinaryFormat::Macho => FUNCTION_SECTION_MACHO.to_string(),
72 _ => {
73 return Err(CompileError::UnsupportedTarget(format!(
74 "Unsupported binary format: {binary_fmt:?}",
75 )));
76 }
77 },
78 binary_fmt,
79 })
80 }
81
82 pub fn trampoline_to_module(
83 &self,
84 ty: &FuncType,
85 config: &LLVM,
86 name: &str,
87 compile_info: &CompileModuleInfo,
88 ) -> Result<Module<'_>, CompileError> {
89 let function = CompiledKind::FunctionCallTrampoline(ty.clone());
91 let module = self.ctx.create_module("");
92 let target_machine = &self.target_machine;
93 let target_triple = target_machine.get_triple();
94 let target_data: inkwell::targets::TargetData = target_machine.get_target_data();
95 module.set_triple(&target_triple);
96 module.set_data_layout(&target_data.get_data_layout());
97 let intrinsics = Intrinsics::declare(
98 &module,
99 &self.ctx,
100 &target_data,
101 &self.target_triple,
102 &self.binary_fmt,
103 );
104
105 let m0_is_enabled = enable_m0_optimization(compile_info);
106 let (callee_ty, callee_attrs) =
107 self.abi
108 .func_type_to_llvm(&self.ctx, &intrinsics, None, ty, m0_is_enabled)?;
109 let trampoline_ty = intrinsics.void_ty.fn_type(
110 &[
111 intrinsics.ptr_ty.into(), intrinsics.ptr_ty.into(), intrinsics.ptr_ty.into(), ],
115 false,
116 );
117
118 let trampoline_func = module.add_function(name, trampoline_ty, Some(Linkage::External));
119 trampoline_func
120 .as_global_value()
121 .set_section(Some(&self.func_section));
122 trampoline_func
123 .as_global_value()
124 .set_linkage(Linkage::DLLExport);
125 trampoline_func
126 .as_global_value()
127 .set_dll_storage_class(DLLStorageClass::Export);
128 trampoline_func.add_attribute(AttributeLoc::Function, intrinsics.uwtable);
129 trampoline_func.add_attribute(AttributeLoc::Function, intrinsics.frame_pointer);
130 self.generate_trampoline(
131 config,
132 compile_info,
133 trampoline_func,
134 ty,
135 callee_ty,
136 &callee_attrs,
137 &self.ctx,
138 &intrinsics,
139 )?;
140
141 if let Some(ref callbacks) = config.callbacks {
142 callbacks.preopt_ir(&function, &compile_info.module.hash_string(), &module);
143 }
144
145 let mut passes = vec![];
146
147 if config.enable_verifier {
148 passes.push("verify");
149 }
150
151 passes.push("instcombine");
152 module
153 .run_passes(
154 passes.join(",").as_str(),
155 target_machine,
156 PassBuilderOptions::create(),
157 )
158 .unwrap();
159
160 if let Some(ref callbacks) = config.callbacks {
161 callbacks.postopt_ir(&function, &compile_info.module.hash_string(), &module);
162 }
163 Ok(module)
164 }
165
166 pub fn trampoline(
167 &self,
168 ty: &FuncType,
169 config: &LLVM,
170 name: &str,
171 compile_info: &CompileModuleInfo,
172 ) -> Result<FunctionBody, CompileError> {
173 let module = self.trampoline_to_module(ty, config, name, compile_info)?;
174 let function = CompiledKind::FunctionCallTrampoline(ty.clone());
175 let target_machine = &self.target_machine;
176
177 let memory_buffer = target_machine
178 .write_to_memory_buffer(&module, FileType::Object)
179 .unwrap();
180
181 if let Some(ref callbacks) = config.callbacks {
182 let module_hash = compile_info.module.hash_string();
183 callbacks.obj_memory_buffer(&function, &module_hash, &memory_buffer);
184 let asm_buffer = target_machine
185 .write_to_memory_buffer(&module, FileType::Assembly)
186 .unwrap();
187 callbacks.asm_memory_buffer(&function, &module_hash, &asm_buffer);
188 }
189
190 let mem_buf_slice = memory_buffer.as_slice();
191
192 let dummy_reloc_target =
199 RelocationTarget::DynamicTrampoline(FunctionIndex::from_u32(u32::MAX - 1));
200
201 let CompiledFunction {
205 compiled_function,
206 custom_sections,
207 eh_frame_section_indices,
208 mut compact_unwind_section_indices,
209 ..
210 } = load_object_file(
211 mem_buf_slice,
212 &self.func_section,
213 dummy_reloc_target,
214 |name: &str| {
215 Err(CompileError::Codegen(format!(
216 "trampoline generation produced reference to unknown function {name}",
217 )))
218 },
219 self.binary_fmt,
220 &self.target_triple,
221 )?;
222 let mut all_sections_are_eh_sections = true;
223 let mut unwind_section_indices = eh_frame_section_indices;
224 unwind_section_indices.append(&mut compact_unwind_section_indices);
225 if unwind_section_indices.len() != custom_sections.len() {
226 all_sections_are_eh_sections = false;
227 } else {
228 unwind_section_indices.sort_unstable();
229 for (idx, section_idx) in unwind_section_indices.iter().enumerate() {
230 if idx as u32 != section_idx.as_u32() {
231 all_sections_are_eh_sections = false;
232 break;
233 }
234 }
235 }
236 if !all_sections_are_eh_sections {
237 return Err(CompileError::Codegen(
238 "trampoline generation produced non-eh custom sections".into(),
239 ));
240 }
241 if !compiled_function.relocations.is_empty() {
242 return Err(CompileError::Codegen(
243 "trampoline generation produced relocations".into(),
244 ));
245 }
246 Ok(FunctionBody {
249 body: compiled_function.body.body,
250 unwind_info: compiled_function.body.unwind_info,
251 })
252 }
253
254 pub fn dynamic_trampoline_to_module(
255 &self,
256 ty: &FuncType,
257 config: &LLVM,
258 name: &str,
259 module_hash: &Option<String>,
260 ) -> Result<Module<'_>, CompileError> {
261 let function = CompiledKind::DynamicFunctionTrampoline(ty.clone());
263 let module = self.ctx.create_module("");
264 let target_machine = &self.target_machine;
265 let target_data = target_machine.get_target_data();
266 let target_triple = target_machine.get_triple();
267 module.set_triple(&target_triple);
268 module.set_data_layout(&target_data.get_data_layout());
269 let intrinsics = Intrinsics::declare(
270 &module,
271 &self.ctx,
272 &target_data,
273 &self.target_triple,
274 &self.binary_fmt,
275 );
276
277 let (trampoline_ty, trampoline_attrs) =
278 self.abi
279 .func_type_to_llvm(&self.ctx, &intrinsics, None, ty, false)?;
280 let trampoline_func = module.add_function(name, trampoline_ty, Some(Linkage::External));
281 trampoline_func.set_personality_function(intrinsics.personality);
282 trampoline_func.add_attribute(AttributeLoc::Function, intrinsics.frame_pointer);
283 for (attr, attr_loc) in trampoline_attrs {
284 trampoline_func.add_attribute(attr_loc, attr);
285 }
286 trampoline_func
287 .as_global_value()
288 .set_section(Some(&self.func_section));
289 trampoline_func
290 .as_global_value()
291 .set_linkage(Linkage::DLLExport);
292 trampoline_func
293 .as_global_value()
294 .set_dll_storage_class(DLLStorageClass::Export);
295 self.generate_dynamic_trampoline(trampoline_func, ty, &self.ctx, &intrinsics)?;
296
297 if let Some(ref callbacks) = config.callbacks {
298 callbacks.preopt_ir(&function, module_hash, &module);
299 }
300
301 let mut passes = vec![];
302
303 if config.enable_verifier {
304 passes.push("verify");
305 }
306
307 passes.push("early-cse");
308 module
309 .run_passes(
310 passes.join(",").as_str(),
311 target_machine,
312 PassBuilderOptions::create(),
313 )
314 .unwrap();
315
316 if let Some(ref callbacks) = config.callbacks {
317 callbacks.postopt_ir(&function, module_hash, &module);
318 }
319
320 Ok(module)
321 }
322
323 #[allow(clippy::too_many_arguments)]
324 pub fn dynamic_trampoline(
325 &self,
326 ty: &FuncType,
327 config: &LLVM,
328 name: &str,
329 dynamic_trampoline_index: u32,
330 final_module_custom_sections: &mut PrimaryMap<SectionIndex, CustomSection>,
331 eh_frame_section_bytes: &mut Vec<u8>,
332 eh_frame_section_relocations: &mut Vec<Relocation>,
333 compact_unwind_section_bytes: &mut Vec<u8>,
334 compact_unwind_section_relocations: &mut Vec<Relocation>,
335 module_hash: &Option<String>,
336 ) -> Result<FunctionBody, CompileError> {
337 let function = CompiledKind::DynamicFunctionTrampoline(ty.clone());
338 let target_machine = &self.target_machine;
339
340 let module = self.dynamic_trampoline_to_module(ty, config, name, module_hash)?;
341
342 let memory_buffer = target_machine
343 .write_to_memory_buffer(&module, FileType::Object)
344 .unwrap();
345
346 if let Some(ref callbacks) = config.callbacks {
347 callbacks.obj_memory_buffer(&function, module_hash, &memory_buffer);
348 let asm_buffer = target_machine
349 .write_to_memory_buffer(&module, FileType::Assembly)
350 .unwrap();
351 callbacks.asm_memory_buffer(&function, module_hash, &asm_buffer)
352 }
353
354 let mem_buf_slice = memory_buffer.as_slice();
355 let CompiledFunction {
356 compiled_function,
357 custom_sections,
358 eh_frame_section_indices,
359 compact_unwind_section_indices,
360 gcc_except_table_section_indices,
361 data_dw_ref_personality_section_indices,
362 } = load_object_file(
363 mem_buf_slice,
364 &self.func_section,
365 RelocationTarget::DynamicTrampoline(FunctionIndex::from_u32(dynamic_trampoline_index)),
366 |name: &str| {
367 Err(CompileError::Codegen(format!(
368 "trampoline generation produced reference to unknown function {name}",
369 )))
370 },
371 self.binary_fmt,
372 &self.target_triple,
373 )?;
374
375 if !compiled_function.relocations.is_empty() {
376 return Err(CompileError::Codegen(
377 "trampoline generation produced relocations".into(),
378 ));
379 }
380 {
385 let first_section = final_module_custom_sections.len() as u32;
386 for (section_index, mut custom_section) in custom_sections.into_iter() {
387 for reloc in &mut custom_section.relocations {
388 if let RelocationTarget::CustomSection(index) = reloc.reloc_target {
389 reloc.reloc_target = RelocationTarget::CustomSection(
390 SectionIndex::from_u32(first_section + index.as_u32()),
391 )
392 }
393
394 if reloc.kind.needs_got() {
395 return Err(CompileError::Codegen(
396 "trampoline generation produced GOT relocation".into(),
397 ));
398 }
399 }
400
401 if eh_frame_section_indices.contains(§ion_index) {
402 let offset = eh_frame_section_bytes.len() as u32;
403 for reloc in &mut custom_section.relocations {
404 reloc.offset += offset;
405 }
406 eh_frame_section_bytes.extend_from_slice(custom_section.bytes.as_slice());
407 eh_frame_section_bytes.extend_from_slice(&[0, 0, 0, 0]);
409 eh_frame_section_relocations.extend(custom_section.relocations);
410 final_module_custom_sections.push(CustomSection {
412 protection: CustomSectionProtection::Read,
413 alignment: None,
414 bytes: SectionBody::new_with_vec(vec![]),
415 relocations: vec![],
416 });
417 } else if compact_unwind_section_indices.contains(§ion_index) {
418 let offset = compact_unwind_section_bytes.len() as u32;
419 for reloc in &mut custom_section.relocations {
420 reloc.offset += offset;
421 }
422 compact_unwind_section_bytes.extend_from_slice(custom_section.bytes.as_slice());
423 compact_unwind_section_relocations.extend(custom_section.relocations);
424 final_module_custom_sections.push(CustomSection {
426 protection: CustomSectionProtection::Read,
427 alignment: None,
428 bytes: SectionBody::new_with_vec(vec![]),
429 relocations: vec![],
430 });
431 } else if gcc_except_table_section_indices.contains(§ion_index)
432 || data_dw_ref_personality_section_indices.contains(§ion_index)
433 {
434 final_module_custom_sections.push(custom_section);
435 } else {
436 return Err(CompileError::Codegen(
437 "trampoline generation produced non-eh custom sections".into(),
438 ));
439 }
440 }
441 }
442
443 Ok(FunctionBody {
444 body: compiled_function.body.body,
445 unwind_info: compiled_function.body.unwind_info,
446 })
447 }
448
449 #[allow(clippy::too_many_arguments)]
450 fn generate_trampoline<'ctx>(
451 &self,
452 config: &LLVM,
453 compile_info: &CompileModuleInfo,
454 trampoline_func: FunctionValue,
455 func_sig: &FuncType,
456 llvm_func_type: FunctionType,
457 func_attrs: &[(Attribute, AttributeLoc)],
458 context: &'ctx Context,
459 intrinsics: &Intrinsics<'ctx>,
460 ) -> Result<(), CompileError> {
461 let entry_block = context.append_basic_block(trampoline_func, "entry");
462 let builder = context.create_builder();
463 builder.position_at_end(entry_block);
464
465 let (callee_vmctx_ptr, func_ptr, args_rets_ptr) =
466 match *trampoline_func.get_params().as_slice() {
467 [callee_vmctx_ptr, func_ptr, args_rets_ptr] => (
468 callee_vmctx_ptr,
469 func_ptr.into_pointer_value(),
470 args_rets_ptr.into_pointer_value(),
471 ),
472 _ => {
473 return Err(CompileError::Codegen(
474 "trampoline function unimplemented".to_string(),
475 ));
476 }
477 };
478 func_ptr.set_name("func_ptr");
479
480 let mut args_vec = Vec::with_capacity(func_sig.params().len() + 3);
481
482 if self.abi.is_sret(func_sig)? {
483 let basic_types: Vec<_> = func_sig
484 .results()
485 .iter()
486 .map(|&ty| type_to_llvm(intrinsics, ty))
487 .collect::<Result<_, _>>()?;
488
489 let sret_ty = context.struct_type(&basic_types, false);
490 args_vec.push(err!(builder.build_alloca(sret_ty, "sret")).into());
491 }
492
493 callee_vmctx_ptr.set_name("vmctx");
494 args_vec.push(callee_vmctx_ptr.into());
495
496 if enable_m0_optimization(compile_info) {
497 let wasm_module = &compile_info.module;
498 let memory_styles = &compile_info.memory_styles;
499 let callee_vmctx_ptr_value = callee_vmctx_ptr.into_pointer_value();
500 let offsets = wasmer_vm::VMOffsets::new(8, wasm_module);
501
502 let memory_index = wasmer_types::MemoryIndex::from_u32(0);
504 let memory_definition_ptr = if let Some(local_memory_index) =
505 wasm_module.local_memory_index(memory_index)
506 {
507 let offset = offsets.vmctx_vmmemory_definition(local_memory_index);
508 let offset = intrinsics.i32_ty.const_int(offset.into(), false);
509 unsafe {
510 err!(builder.build_gep(intrinsics.i8_ty, callee_vmctx_ptr_value, &[offset], ""))
511 }
512 } else {
513 let offset = offsets.vmctx_vmmemory_import(memory_index);
514 let offset = intrinsics.i32_ty.const_int(offset.into(), false);
515 let memory_definition_ptr_ptr = unsafe {
516 err!(builder.build_gep(intrinsics.i8_ty, callee_vmctx_ptr_value, &[offset], ""))
517 };
518 let memory_definition_ptr_ptr =
519 err!(builder.build_bit_cast(memory_definition_ptr_ptr, intrinsics.ptr_ty, "",))
520 .into_pointer_value();
521
522 err!(builder.build_load(intrinsics.ptr_ty, memory_definition_ptr_ptr, ""))
523 .into_pointer_value()
524 };
525 let memory_definition_ptr =
526 err!(builder.build_bit_cast(memory_definition_ptr, intrinsics.ptr_ty, "",))
527 .into_pointer_value();
528 let base_ptr = err!(builder.build_struct_gep(
529 intrinsics.vmmemory_definition_ty,
530 memory_definition_ptr,
531 intrinsics.vmmemory_definition_base_element,
532 "",
533 ));
534
535 let memory_style = &memory_styles[memory_index];
536 let base_ptr = if let MemoryStyle::Dynamic { .. } = memory_style {
537 base_ptr
538 } else {
539 err!(builder.build_load(intrinsics.ptr_ty, base_ptr, "")).into_pointer_value()
540 };
541
542 base_ptr.set_name("trmpl_m0_base_ptr");
543
544 args_vec.push(base_ptr.into());
545 }
546
547 for (i, param_ty) in func_sig.params().iter().enumerate() {
548 let index = intrinsics.i32_ty.const_int(i as _, false);
549 let item_pointer = unsafe {
550 err!(builder.build_in_bounds_gep(
551 intrinsics.i128_ty,
552 args_rets_ptr,
553 &[index],
554 "arg_ptr"
555 ))
556 };
557
558 let casted_type = type_to_llvm(intrinsics, *param_ty)?;
559
560 let typed_item_pointer = err!(builder.build_pointer_cast(
561 item_pointer,
562 intrinsics.ptr_ty,
563 "typed_arg_pointer"
564 ));
565
566 let arg = err!(builder.build_load(casted_type, typed_item_pointer, "arg"));
567 args_vec.push(arg.into());
568 }
569
570 let call_site = err!(builder.build_indirect_call(
571 llvm_func_type,
572 func_ptr,
573 args_vec.as_slice(),
574 "call"
575 ));
576 for (attr, attr_loc) in func_attrs {
577 call_site.add_attribute(*attr_loc, *attr);
578 }
579
580 let rets = self
581 .abi
582 .rets_from_call(&builder, intrinsics, call_site, func_sig)?;
583 for (idx, v) in rets.into_iter().enumerate() {
584 let ptr = unsafe {
585 err!(builder.build_gep(
586 intrinsics.i128_ty,
587 args_rets_ptr,
588 &[intrinsics.i32_ty.const_int(idx as u64, false)],
589 "",
590 ))
591 };
592 let ptr = err!(builder.build_pointer_cast(
593 ptr,
594 self.ctx.ptr_type(AddressSpace::default()),
595 ""
596 ));
597 err!(builder.build_store(ptr, v));
598 }
599
600 err!(builder.build_return(None));
601 Ok(())
602 }
603
604 fn generate_dynamic_trampoline<'ctx>(
605 &self,
606 trampoline_func: FunctionValue,
607 func_sig: &FuncType,
608 context: &'ctx Context,
609 intrinsics: &Intrinsics<'ctx>,
610 ) -> Result<(), CompileError> {
611 let entry_block = context.append_basic_block(trampoline_func, "entry");
612 let builder = context.create_builder();
613 builder.position_at_end(entry_block);
614
615 let values = err!(builder.build_alloca(
617 intrinsics.i128_ty.array_type(cmp::max(
618 func_sig.params().len().try_into().unwrap(),
619 func_sig.results().len().try_into().unwrap(),
620 )),
621 "",
622 ));
623
624 let first_user_param = if self.abi.is_sret(func_sig)? { 2 } else { 1 };
626 for i in 0..func_sig.params().len() {
627 let ptr = unsafe {
628 err!(builder.build_in_bounds_gep(
629 intrinsics.i128_ty,
630 values,
631 &[intrinsics.i32_ty.const_int(i.try_into().unwrap(), false)],
632 "args",
633 ))
634 };
635 let ptr = err!(builder.build_bit_cast(ptr, intrinsics.ptr_ty, "")).into_pointer_value();
636 err!(
637 builder.build_store(
638 ptr,
639 trampoline_func
640 .get_nth_param(i as u32 + first_user_param)
641 .unwrap(),
642 )
643 );
644 }
645
646 let callee_ptr_ty = intrinsics.void_ty.fn_type(
647 &[
648 intrinsics.ptr_ty.into(), intrinsics.ptr_ty.into(), ],
651 false,
652 );
653 let vmctx = self.abi.get_vmctx_ptr_param(&trampoline_func);
654 let callee_ty =
655 err!(builder.build_bit_cast(vmctx, self.ctx.ptr_type(AddressSpace::default()), ""));
656 let callee =
657 err!(builder.build_load(intrinsics.ptr_ty, callee_ty.into_pointer_value(), ""))
658 .into_pointer_value();
659 callee.set_name("func_ptr");
660
661 let values_ptr = err!(builder.build_pointer_cast(values, intrinsics.ptr_ty, ""));
662 values_ptr.set_name("value_ptr");
663 err!(builder.build_indirect_call(
664 callee_ptr_ty,
665 callee,
666 &[vmctx.into(), values_ptr.into()],
667 "",
668 ));
669
670 if func_sig.results().is_empty() {
671 err!(builder.build_return(None));
672 } else {
673 let results = func_sig
674 .results()
675 .iter()
676 .enumerate()
677 .map(|(idx, ty)| {
678 let ptr = unsafe {
679 err!(builder.build_gep(
680 intrinsics.i128_ty,
681 values,
682 &[intrinsics.i32_ty.const_int(idx.try_into().unwrap(), false)],
683 "",
684 ))
685 };
686 let ptr = err!(builder.build_pointer_cast(ptr, intrinsics.ptr_ty, ""));
687 err_nt!(builder.build_load(type_to_llvm(intrinsics, *ty)?, ptr, ""))
688 })
689 .collect::<Result<Vec<_>, CompileError>>()?;
690
691 if self.abi.is_sret(func_sig)? {
692 let sret = trampoline_func
693 .get_first_param()
694 .unwrap()
695 .into_pointer_value();
696
697 let basic_types: Vec<_> = func_sig
698 .results()
699 .iter()
700 .map(|&ty| type_to_llvm(intrinsics, ty))
701 .collect::<Result<_, _>>()?;
702 let mut struct_value = context.struct_type(&basic_types, false).get_undef();
703
704 for (idx, value) in results.iter().enumerate() {
705 let value = err!(builder.build_bit_cast(
706 *value,
707 type_to_llvm(intrinsics, func_sig.results()[idx])?,
708 "",
709 ));
710 struct_value =
711 err!(builder.build_insert_value(struct_value, value, idx as u32, ""))
712 .into_struct_value();
713 }
714 err!(builder.build_store(sret, struct_value));
715 err!(builder.build_return(None));
716 } else {
717 err!(
718 builder.build_return(Some(&self.abi.pack_values_for_register_return(
719 intrinsics,
720 &builder,
721 results.as_slice(),
722 &trampoline_func.get_type(),
723 )?))
724 );
725 }
726 }
727
728 Ok(())
729 }
730}