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