wasmer_compiler_llvm/translator/
trampoline.rs

1// TODO: Remove
2#![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"; // Needs to be between 1 and 16 chars
48const FUNCTION_SECTION_MACHO: &str = "wasmer_trmpl"; // Needs to be between 1 and 16 chars
49
50fn 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        // The function type, used for the callbacks.
90        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(), // vmctx ptr
112                intrinsics.ptr_ty.into(), // callee function address
113                intrinsics.ptr_ty.into(), // in/out values ptr
114            ],
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        // Use a dummy function index to detect relocations against the trampoline
193        // function's address, which shouldn't exist and are not supported.
194        // Note, we just drop all custom sections, and verify that the function
195        // body itself has no relocations at all. This value should never be
196        // touched at all. However, it is set up so that if we do touch it (maybe
197        // due to someone changing the code later on), it'll explode, which is desirable!
198        let dummy_reloc_target =
199            RelocationTarget::DynamicTrampoline(FunctionIndex::from_u32(u32::MAX - 1));
200
201        // Note: we don't count .gcc_except_table here because native-to-wasm
202        // trampolines are not supposed to generate any LSDA sections. We *want* them
203        // to terminate libunwind's stack searches.
204        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        // Ignore CompiledFunctionFrameInfo. Extra frame info isn't a problem.
247
248        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        // The function type, used for the callbacks
262        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        // Ignore CompiledFunctionFrameInfo. Extra frame info isn't a problem.
381
382        // Also append EH-related sections to the final module, since we expect
383        // dynamic trampolines to participate in unwinding
384        {
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(&section_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                    // Terminate the eh_frame info with a zero-length CIE.
408                    eh_frame_section_bytes.extend_from_slice(&[0, 0, 0, 0]);
409                    eh_frame_section_relocations.extend(custom_section.relocations);
410                    // TODO: we do this to keep the count right, remove it.
411                    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(&section_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                    // TODO: we do this to keep the count right, remove it.
425                    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(&section_index)
432                    || data_dw_ref_personality_section_indices.contains(&section_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            // load mem
503            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        // Allocate stack space for the params and results.
616        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        // Copy params to 'values'.
625        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(), // vmctx ptr
649                intrinsics.ptr_ty.into(), // in/out values ptr
650            ],
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}