wasmer_compiler_llvm/abi/
aarch64_systemv.rs

1use crate::abi::Abi;
2use crate::error::{err, err_nt};
3use crate::translator::intrinsics::{Intrinsics, type_to_llvm};
4use inkwell::{
5    AddressSpace,
6    attributes::{Attribute, AttributeLoc},
7    builder::Builder,
8    context::Context,
9    types::{AnyType, BasicMetadataTypeEnum, BasicType, FunctionType, StructType},
10    values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue},
11};
12use wasmer_types::CompileError;
13use wasmer_types::{FunctionType as FuncSig, Type};
14use wasmer_vm::VMOffsets;
15
16use std::convert::TryInto;
17
18use super::{G0M0FunctionKind, LocalFunctionG0M0params};
19
20/// Implementation of the [`Abi`] trait for the Aarch64 ABI on Linux.
21pub struct Aarch64SystemV {}
22
23impl Abi for Aarch64SystemV {
24    // Given a function definition, retrieve the parameter that is the vmctx pointer.
25    fn get_vmctx_ptr_param<'ctx>(&self, func_value: &FunctionValue<'ctx>) -> PointerValue<'ctx> {
26        let param = func_value
27            .get_nth_param(u32::from(
28                func_value
29                    .get_enum_attribute(
30                        AttributeLoc::Param(0),
31                        Attribute::get_named_enum_kind_id("sret"),
32                    )
33                    .is_some(),
34            ))
35            .unwrap();
36        //param.set_name("vmctx");
37
38        param.into_pointer_value()
39    }
40
41    /// Given a function definition, retrieve the parameter that is the pointer to the first --
42    /// number 0 -- local global.
43    fn get_g0_ptr_param<'ctx>(&self, func_value: &FunctionValue<'ctx>) -> IntValue<'ctx> {
44        // g0 is always after the vmctx.
45        let vmctx_idx = u32::from(
46            func_value
47                .get_enum_attribute(
48                    AttributeLoc::Param(0),
49                    Attribute::get_named_enum_kind_id("sret"),
50                )
51                .is_some(),
52        );
53
54        let param = func_value.get_nth_param(vmctx_idx + 1).unwrap();
55        param.set_name("g0");
56
57        param.into_int_value()
58    }
59
60    /// Given a function definition, retrieve the parameter that is the pointer to the first --
61    /// number 0 -- local memory.
62    fn get_m0_ptr_param<'ctx>(&self, func_value: &FunctionValue<'ctx>) -> PointerValue<'ctx> {
63        // m0 is always after g0.
64        let vmctx_idx = u32::from(
65            func_value
66                .get_enum_attribute(
67                    AttributeLoc::Param(0),
68                    Attribute::get_named_enum_kind_id("sret"),
69                )
70                .is_some(),
71        );
72
73        let param = func_value.get_nth_param(vmctx_idx + 2).unwrap();
74        param.set_name("m0_base_ptr");
75
76        param.into_pointer_value()
77    }
78
79    // Given a wasm function type, produce an llvm function declaration.
80    fn func_type_to_llvm<'ctx>(
81        &self,
82        context: &'ctx Context,
83        intrinsics: &Intrinsics<'ctx>,
84        offsets: Option<&VMOffsets>,
85        sig: &FuncSig,
86        function_kind: Option<G0M0FunctionKind>,
87    ) -> Result<(FunctionType<'ctx>, Vec<(Attribute, AttributeLoc)>), CompileError> {
88        let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty));
89
90        let mut param_types = vec![Ok(intrinsics.ptr_ty.as_basic_type_enum())];
91        if function_kind.is_some_and(|v| v.is_local()) {
92            // The value of g0
93            param_types.push(Ok(intrinsics.i32_ty.as_basic_type_enum()));
94            // The base pointer to m0
95            param_types.push(Ok(intrinsics.ptr_ty.as_basic_type_enum()));
96        }
97
98        let param_types = param_types.into_iter().chain(user_param_types);
99
100        let vmctx_attributes = |i: u32| {
101            vec![
102                (
103                    context.create_enum_attribute(Attribute::get_named_enum_kind_id("nofree"), 0),
104                    AttributeLoc::Param(i),
105                ),
106                (
107                    if let Some(offsets) = offsets {
108                        context.create_enum_attribute(
109                            Attribute::get_named_enum_kind_id("dereferenceable"),
110                            offsets.size_of_vmctx().into(),
111                        )
112                    } else {
113                        context
114                            .create_enum_attribute(Attribute::get_named_enum_kind_id("nonnull"), 0)
115                    },
116                    AttributeLoc::Param(i),
117                ),
118                (
119                    context.create_enum_attribute(
120                        Attribute::get_named_enum_kind_id("align"),
121                        std::mem::align_of::<wasmer_vm::VMContext>()
122                            .try_into()
123                            .unwrap(),
124                    ),
125                    AttributeLoc::Param(i),
126                ),
127            ]
128        };
129
130        Ok(match sig.results() {
131            [] => (
132                intrinsics.void_ty.fn_type(
133                    param_types
134                        .map(|v| v.map(Into::into))
135                        .collect::<Result<Vec<BasicMetadataTypeEnum>, _>>()?
136                        .as_slice(),
137                    false,
138                ),
139                vmctx_attributes(0),
140            ),
141            [_] => {
142                let single_value = sig.results()[0];
143                (
144                    type_to_llvm(intrinsics, single_value)?.fn_type(
145                        param_types
146                            .map(|v| v.map(Into::into))
147                            .collect::<Result<Vec<BasicMetadataTypeEnum>, _>>()?
148                            .as_slice(),
149                        false,
150                    ),
151                    vmctx_attributes(0),
152                )
153            }
154            [Type::F32, Type::F32] => {
155                let f32_ty = intrinsics.f32_ty.as_basic_type_enum();
156                (
157                    context.struct_type(&[f32_ty, f32_ty], false).fn_type(
158                        param_types
159                            .map(|v| v.map(Into::into))
160                            .collect::<Result<Vec<BasicMetadataTypeEnum>, _>>()?
161                            .as_slice(),
162                        false,
163                    ),
164                    vmctx_attributes(0),
165                )
166            }
167            [Type::F64, Type::F64] => {
168                let f64_ty = intrinsics.f64_ty.as_basic_type_enum();
169                (
170                    context.struct_type(&[f64_ty, f64_ty], false).fn_type(
171                        param_types
172                            .map(|v| v.map(Into::into))
173                            .collect::<Result<Vec<BasicMetadataTypeEnum>, _>>()?
174                            .as_slice(),
175                        false,
176                    ),
177                    vmctx_attributes(0),
178                )
179            }
180            [Type::F32, Type::F32, Type::F32] => {
181                let f32_ty = intrinsics.f32_ty.as_basic_type_enum();
182                (
183                    context
184                        .struct_type(&[f32_ty, f32_ty, f32_ty], false)
185                        .fn_type(
186                            param_types
187                                .map(|v| v.map(Into::into))
188                                .collect::<Result<Vec<BasicMetadataTypeEnum>, _>>()?
189                                .as_slice(),
190                            false,
191                        ),
192                    vmctx_attributes(0),
193                )
194            }
195            [Type::F32, Type::F32, Type::F32, Type::F32] => {
196                let f32_ty = intrinsics.f32_ty.as_basic_type_enum();
197                (
198                    context
199                        .struct_type(&[f32_ty, f32_ty, f32_ty, f32_ty], false)
200                        .fn_type(
201                            param_types
202                                .map(|v| v.map(Into::into))
203                                .collect::<Result<Vec<BasicMetadataTypeEnum>, _>>()?
204                                .as_slice(),
205                            false,
206                        ),
207                    vmctx_attributes(0),
208                )
209            }
210            [t1, t2]
211                if matches!(t1, Type::I32 | Type::I64 | Type::F32 | Type::F64)
212                    && matches!(t2, Type::FuncRef | Type::ExternRef | Type::ExceptionRef) =>
213            {
214                let t1 = type_to_llvm(intrinsics, *t1).unwrap();
215                let t2 = type_to_llvm(intrinsics, *t2).unwrap();
216                (
217                    context
218                        .struct_type(&[t1.as_basic_type_enum(), t2.as_basic_type_enum()], false)
219                        .fn_type(
220                            param_types
221                                .map(|v| v.map(Into::into))
222                                .collect::<Result<Vec<BasicMetadataTypeEnum>, _>>()?
223                                .as_slice(),
224                            false,
225                        ),
226                    vmctx_attributes(0),
227                )
228            }
229            _ => {
230                let sig_returns_bitwidths = sig
231                    .results()
232                    .iter()
233                    .map(|ty| match ty {
234                        Type::I32 | Type::F32 => 32,
235                        Type::I64 | Type::F64 => 64,
236                        Type::V128 => 128,
237                        Type::ExternRef | Type::FuncRef | Type::ExceptionRef => 64, /* pointer */
238                    })
239                    .collect::<Vec<i32>>();
240                match sig_returns_bitwidths.as_slice() {
241                    [32, 32] => (
242                        intrinsics.i64_ty.fn_type(
243                            param_types
244                                .map(|v| v.map(Into::into))
245                                .collect::<Result<Vec<BasicMetadataTypeEnum>, _>>()?
246                                .as_slice(),
247                            false,
248                        ),
249                        vmctx_attributes(0),
250                    ),
251                    [32, 64]
252                    | [64, 32]
253                    | [64, 64]
254                    | [32, 32, 32]
255                    | [64, 32, 32]
256                    | [32, 32, 64]
257                    | [32, 32, 32, 32] => (
258                        intrinsics.i64_ty.array_type(2).fn_type(
259                            param_types
260                                .map(|v| v.map(Into::into))
261                                .collect::<Result<Vec<BasicMetadataTypeEnum>, _>>()?
262                                .as_slice(),
263                            false,
264                        ),
265                        vmctx_attributes(0),
266                    ),
267                    _ => {
268                        let basic_types: Vec<_> = sig
269                            .results()
270                            .iter()
271                            .map(|&ty| type_to_llvm(intrinsics, ty))
272                            .collect::<Result<_, _>>()?;
273
274                        let sret = context.struct_type(&basic_types, false);
275                        let sret_ptr = context.ptr_type(AddressSpace::default());
276
277                        let param_types =
278                            std::iter::once(Ok(sret_ptr.as_basic_type_enum())).chain(param_types);
279
280                        let mut attributes = vec![(
281                            context.create_type_attribute(
282                                Attribute::get_named_enum_kind_id("sret"),
283                                sret.as_any_type_enum(),
284                            ),
285                            AttributeLoc::Param(0),
286                        )];
287                        attributes.append(&mut vmctx_attributes(1));
288
289                        (
290                            intrinsics.void_ty.fn_type(
291                                param_types
292                                    .map(|v| v.map(Into::into))
293                                    .collect::<Result<Vec<BasicMetadataTypeEnum>, _>>()?
294                                    .as_slice(),
295                                false,
296                            ),
297                            attributes,
298                        )
299                    }
300                }
301            }
302        })
303    }
304
305    // Marshall wasm stack values into function parameters.
306    fn args_to_call<'ctx>(
307        &self,
308        alloca_builder: &Builder<'ctx>,
309        func_sig: &FuncSig,
310        llvm_fn_ty: &FunctionType<'ctx>,
311        ctx_ptr: PointerValue<'ctx>,
312        values: &[BasicValueEnum<'ctx>],
313        intrinsics: &Intrinsics<'ctx>,
314        g0m0: LocalFunctionG0M0params<'ctx>,
315    ) -> Result<Vec<BasicValueEnum<'ctx>>, CompileError> {
316        // If it's an sret, allocate the return space.
317        let sret = if llvm_fn_ty.get_return_type().is_none() && func_sig.results().len() > 1 {
318            let llvm_params: Vec<_> = func_sig
319                .results()
320                .iter()
321                .map(|x| type_to_llvm(intrinsics, *x).unwrap())
322                .collect();
323            let llvm_params = llvm_fn_ty
324                .get_context()
325                .struct_type(llvm_params.as_slice(), false);
326            Some(err!(alloca_builder.build_alloca(llvm_params, "sret")))
327        } else {
328            None
329        };
330
331        let mut args = vec![ctx_ptr.as_basic_value_enum()];
332
333        if let Some((g0, m0)) = g0m0 {
334            args.push(g0.into());
335            args.push(m0.into());
336        }
337
338        let args = args.into_iter().chain(values.iter().copied());
339
340        let ret = if let Some(sret) = sret {
341            std::iter::once(sret.as_basic_value_enum())
342                .chain(args)
343                .collect()
344        } else {
345            args.collect()
346        };
347
348        Ok(ret)
349    }
350
351    // Given a CallSite, extract the returned values and return them in a Vec.
352    fn rets_from_call<'ctx>(
353        &self,
354        builder: &Builder<'ctx>,
355        intrinsics: &Intrinsics<'ctx>,
356        call_site: CallSiteValue<'ctx>,
357        func_sig: &FuncSig,
358    ) -> Result<Vec<BasicValueEnum<'ctx>>, CompileError> {
359        let split_i64 =
360            |value: IntValue<'ctx>| -> Result<(IntValue<'ctx>, IntValue<'ctx>), CompileError> {
361                assert!(value.get_type() == intrinsics.i64_ty);
362                let low = err!(builder.build_int_truncate(value, intrinsics.i32_ty, ""));
363                let lshr = err!(builder.build_right_shift(
364                    value,
365                    intrinsics.i64_ty.const_int(32, false),
366                    false,
367                    "",
368                ));
369                let high = err!(builder.build_int_truncate(lshr, intrinsics.i32_ty, ""));
370                Ok((low, high))
371            };
372
373        let casted =
374            |value: BasicValueEnum<'ctx>, ty: Type| -> Result<BasicValueEnum<'ctx>, CompileError> {
375                match ty {
376                    Type::I32 => {
377                        assert!(
378                            value.get_type() == intrinsics.i32_ty.as_basic_type_enum()
379                                || value.get_type() == intrinsics.f32_ty.as_basic_type_enum()
380                        );
381                        err_nt!(builder.build_bit_cast(value, intrinsics.i32_ty, ""))
382                    }
383                    Type::F32 => {
384                        assert!(
385                            value.get_type() == intrinsics.i32_ty.as_basic_type_enum()
386                                || value.get_type() == intrinsics.f32_ty.as_basic_type_enum()
387                        );
388                        err_nt!(builder.build_bit_cast(value, intrinsics.f32_ty, ""))
389                    }
390                    Type::I64 => {
391                        assert!(
392                            value.get_type() == intrinsics.i64_ty.as_basic_type_enum()
393                                || value.get_type() == intrinsics.f64_ty.as_basic_type_enum()
394                        );
395                        err_nt!(builder.build_bit_cast(value, intrinsics.i64_ty, ""))
396                    }
397                    Type::F64 => {
398                        assert!(
399                            value.get_type() == intrinsics.i64_ty.as_basic_type_enum()
400                                || value.get_type() == intrinsics.f64_ty.as_basic_type_enum()
401                        );
402                        err_nt!(builder.build_bit_cast(value, intrinsics.f64_ty, ""))
403                    }
404                    Type::V128 => {
405                        assert!(value.get_type() == intrinsics.i128_ty.as_basic_type_enum());
406                        Ok(value)
407                    }
408                    Type::ExternRef | Type::FuncRef | Type::ExceptionRef => {
409                        assert!(value.get_type() == intrinsics.ptr_ty.as_basic_type_enum());
410                        Ok(value)
411                    }
412                }
413            };
414
415        if let Some(basic_value) = call_site.try_as_basic_value().left() {
416            if func_sig.results().len() > 1 {
417                if basic_value.get_type() == intrinsics.i64_ty.as_basic_type_enum() {
418                    assert!(func_sig.results().len() == 2);
419                    let value = basic_value.into_int_value();
420                    let (low, high) = split_i64(value)?;
421                    let low = casted(low.into(), func_sig.results()[0])?;
422                    let high = casted(high.into(), func_sig.results()[1])?;
423                    return Ok(vec![low, high]);
424                }
425                if basic_value.is_struct_value() {
426                    let struct_value = basic_value.into_struct_value();
427                    return Ok((0..struct_value.get_type().count_fields())
428                        .map(|i| builder.build_extract_value(struct_value, i, "").unwrap())
429                        .collect::<Vec<_>>());
430                }
431                let array_value = basic_value.into_array_value();
432                let low = builder
433                    .build_extract_value(array_value, 0, "")
434                    .unwrap()
435                    .into_int_value();
436                let high = builder
437                    .build_extract_value(array_value, 1, "")
438                    .unwrap()
439                    .into_int_value();
440                let func_sig_returns_bitwidths = func_sig
441                    .results()
442                    .iter()
443                    .map(|ty| match ty {
444                        Type::I32 | Type::F32 => 32,
445                        Type::I64 | Type::F64 => 64,
446                        Type::V128 => 128,
447                        Type::ExternRef | Type::FuncRef | Type::ExceptionRef => 64, /* pointer */
448                    })
449                    .collect::<Vec<i32>>();
450
451                match func_sig_returns_bitwidths.as_slice() {
452                    [32, 64] => {
453                        let (low, _) = split_i64(low)?;
454                        let low = casted(low.into(), func_sig.results()[0])?;
455                        let high = casted(high.into(), func_sig.results()[1])?;
456                        Ok(vec![low, high])
457                    }
458                    [64, 32] => {
459                        let (high, _) = split_i64(high)?;
460                        let low = casted(low.into(), func_sig.results()[0])?;
461                        let high = casted(high.into(), func_sig.results()[1])?;
462                        Ok(vec![low, high])
463                    }
464                    [64, 64] => {
465                        let low = casted(low.into(), func_sig.results()[0])?;
466                        let high = casted(high.into(), func_sig.results()[1])?;
467                        Ok(vec![low, high])
468                    }
469                    [32, 32, 32] => {
470                        let (v1, v2) = split_i64(low)?;
471                        let (v3, _) = split_i64(high)?;
472                        let v1 = casted(v1.into(), func_sig.results()[0])?;
473                        let v2 = casted(v2.into(), func_sig.results()[1])?;
474                        let v3 = casted(v3.into(), func_sig.results()[2])?;
475                        Ok(vec![v1, v2, v3])
476                    }
477                    [32, 32, 64] => {
478                        let (v1, v2) = split_i64(low)?;
479                        let v1 = casted(v1.into(), func_sig.results()[0])?;
480                        let v2 = casted(v2.into(), func_sig.results()[1])?;
481                        let v3 = casted(high.into(), func_sig.results()[2])?;
482                        Ok(vec![v1, v2, v3])
483                    }
484                    [64, 32, 32] => {
485                        let v1 = casted(low.into(), func_sig.results()[0])?;
486                        let (v2, v3) = split_i64(high)?;
487                        let v2 = casted(v2.into(), func_sig.results()[1])?;
488                        let v3 = casted(v3.into(), func_sig.results()[2])?;
489                        Ok(vec![v1, v2, v3])
490                    }
491                    [32, 32, 32, 32] => {
492                        let (v1, v2) = split_i64(low)?;
493                        let (v3, v4) = split_i64(high)?;
494                        let v1 = casted(v1.into(), func_sig.results()[0])?;
495                        let v2 = casted(v2.into(), func_sig.results()[1])?;
496                        let v3 = casted(v3.into(), func_sig.results()[2])?;
497                        let v4 = casted(v4.into(), func_sig.results()[3])?;
498                        Ok(vec![v1, v2, v3, v4])
499                    }
500                    _ => unreachable!("expected an sret for this type"),
501                }
502            } else {
503                assert!(func_sig.results().len() == 1);
504                Ok(vec![basic_value])
505            }
506        } else {
507            assert!(call_site.count_arguments() > 0); // Either sret or vmctx.
508            if call_site
509                .get_enum_attribute(
510                    AttributeLoc::Param(0),
511                    Attribute::get_named_enum_kind_id("sret"),
512                )
513                .is_some()
514            {
515                let sret_ty = call_site
516                    .try_as_basic_value()
517                    .right()
518                    .unwrap()
519                    .get_operand(0)
520                    .unwrap()
521                    .left()
522                    .unwrap();
523                let sret = sret_ty.into_pointer_value();
524                // re-build the llvm-type struct holding the return values
525                let llvm_results: Vec<_> = func_sig
526                    .results()
527                    .iter()
528                    .map(|x| type_to_llvm(intrinsics, *x).unwrap())
529                    .collect();
530                let struct_type = intrinsics
531                    .i32_ty
532                    .get_context()
533                    .struct_type(llvm_results.as_slice(), false);
534
535                let struct_value =
536                    err!(builder.build_load(struct_type, sret, "")).into_struct_value();
537                let mut rets: Vec<_> = Vec::new();
538                for i in 0..struct_value.get_type().count_fields() {
539                    let value = err!(builder.build_extract_value(struct_value, i, ""));
540                    rets.push(value);
541                }
542                assert!(func_sig.results().len() == rets.len());
543                Ok(rets)
544            } else {
545                assert!(func_sig.results().is_empty());
546                Ok(vec![])
547            }
548        }
549    }
550
551    fn is_sret(&self, func_sig: &FuncSig) -> Result<bool, CompileError> {
552        let func_sig_returns_bitwidths = func_sig
553            .results()
554            .iter()
555            .map(|ty| match ty {
556                Type::I32 | Type::F32 => 32,
557                Type::I64 | Type::F64 => 64,
558                Type::V128 => 128,
559                Type::ExternRef | Type::FuncRef | Type::ExceptionRef => 64, /* pointer */
560            })
561            .collect::<Vec<i32>>();
562
563        Ok(!matches!(
564            func_sig_returns_bitwidths.as_slice(),
565            [] | [_]
566                | [32, 32]
567                | [32, 64]
568                | [64, 32]
569                | [64, 64]
570                | [32, 32, 32]
571                | [32, 32, 64]
572                | [64, 32, 32]
573                | [32, 32, 32, 32]
574        ))
575    }
576
577    fn pack_values_for_register_return<'ctx>(
578        &self,
579        intrinsics: &Intrinsics<'ctx>,
580        builder: &Builder<'ctx>,
581        values: &[BasicValueEnum<'ctx>],
582        func_type: &FunctionType<'ctx>,
583    ) -> Result<BasicValueEnum<'ctx>, CompileError> {
584        let is_32 = |value: BasicValueEnum| {
585            (value.is_int_value() && value.into_int_value().get_type() == intrinsics.i32_ty)
586                || (value.is_float_value()
587                    && value.into_float_value().get_type() == intrinsics.f32_ty)
588        };
589        let is_64 = |value: BasicValueEnum| {
590            (value.is_int_value() && value.into_int_value().get_type() == intrinsics.i64_ty)
591                || (value.is_float_value()
592                    && value.into_float_value().get_type() == intrinsics.f64_ty)
593        };
594
595        let pack_i32s = |low: BasicValueEnum<'ctx>, high: BasicValueEnum<'ctx>| {
596            assert!(low.get_type() == intrinsics.i32_ty.as_basic_type_enum());
597            assert!(high.get_type() == intrinsics.i32_ty.as_basic_type_enum());
598            let (low, high) = (low.into_int_value(), high.into_int_value());
599            let low = err!(builder.build_int_z_extend(low, intrinsics.i64_ty, ""));
600            let high = err!(builder.build_int_z_extend(high, intrinsics.i64_ty, ""));
601            let high =
602                err!(builder.build_left_shift(high, intrinsics.i64_ty.const_int(32, false), ""));
603            err_nt!(
604                builder
605                    .build_or(low, high, "")
606                    .map(|v| v.as_basic_value_enum())
607            )
608        };
609
610        let to_i64 = |v: BasicValueEnum<'ctx>| -> Result<BasicValueEnum<'_>, CompileError> {
611            if v.is_float_value() {
612                let v = v.into_float_value();
613                if v.get_type() == intrinsics.f32_ty {
614                    let v = err!(builder.build_bit_cast(v, intrinsics.i32_ty, "")).into_int_value();
615                    let v = err!(builder.build_int_z_extend(v, intrinsics.i64_ty, ""));
616                    Ok(v.as_basic_value_enum())
617                } else {
618                    debug_assert!(v.get_type() == intrinsics.f64_ty);
619                    let v = err!(builder.build_bit_cast(v, intrinsics.i64_ty, ""));
620                    Ok(v.as_basic_value_enum())
621                }
622            } else {
623                let v = v.into_int_value();
624                if v.get_type() == intrinsics.i32_ty {
625                    let v = err!(builder.build_int_z_extend(v, intrinsics.i64_ty, ""));
626                    Ok(v.as_basic_value_enum())
627                } else {
628                    debug_assert!(v.get_type() == intrinsics.i64_ty);
629                    Ok(v.as_basic_value_enum())
630                }
631            }
632        };
633
634        let build_struct = |ty: StructType<'ctx>,
635                            values: &[BasicValueEnum<'ctx>]|
636         -> Result<BasicValueEnum<'_>, CompileError> {
637            let mut struct_value = ty.get_undef();
638            for (i, v) in values.iter().enumerate() {
639                struct_value = err!(builder.build_insert_value(struct_value, *v, i as u32, ""))
640                    .into_struct_value();
641            }
642            Ok(struct_value.as_basic_value_enum())
643        };
644
645        let build_2xi64 = |low: BasicValueEnum<'ctx>,
646                           high: BasicValueEnum<'ctx>|
647         -> Result<BasicValueEnum<'_>, CompileError> {
648            let low = to_i64(low)?;
649            let high = to_i64(high)?;
650            let value = intrinsics.i64_ty.array_type(2).get_undef();
651            let value = err!(builder.build_insert_value(value, low, 0, ""));
652            let value = err!(builder.build_insert_value(value, high, 1, ""));
653            Ok(value.as_basic_value_enum())
654        };
655
656        Ok(match *values {
657            [one_value] => one_value,
658            [v1, v2]
659                if v1.is_float_value()
660                    && v2.is_float_value()
661                    && v1.into_float_value().get_type() == v2.into_float_value().get_type() =>
662            {
663                build_struct(
664                    func_type.get_return_type().unwrap().into_struct_type(),
665                    &[v1, v2],
666                )?
667            }
668            [v1, v2] if is_32(v1) && is_32(v2) => {
669                let v1 = err!(builder.build_bit_cast(v1, intrinsics.i32_ty, ""));
670                let v2 = err!(builder.build_bit_cast(v2, intrinsics.i32_ty, ""));
671                pack_i32s(v1, v2)?
672            }
673            [v1, v2] => build_2xi64(v1, v2)?,
674            [v1, v2, v3]
675                if is_32(v1)
676                    && is_32(v2)
677                    && is_32(v3)
678                    && v1.is_float_value()
679                    && v2.is_float_value()
680                    && v3.is_float_value() =>
681            {
682                build_struct(
683                    func_type.get_return_type().unwrap().into_struct_type(),
684                    &[v1, v2, v3],
685                )?
686            }
687            [v1, v2, v3] if is_32(v1) && is_32(v2) => {
688                let v1 = err!(builder.build_bit_cast(v1, intrinsics.i32_ty, ""));
689                let v2 = err!(builder.build_bit_cast(v2, intrinsics.i32_ty, ""));
690                let v1v2_pack = pack_i32s(v1, v2)?;
691                build_2xi64(v1v2_pack, v3)?
692            }
693            [v1, v2, v3] if is_64(v1) && is_32(v2) && is_32(v3) => {
694                let v2 = err!(builder.build_bit_cast(v2, intrinsics.i32_ty, ""));
695                let v3 = err!(builder.build_bit_cast(v3, intrinsics.i32_ty, ""));
696                let v2v3_pack = pack_i32s(v2, v3)?;
697                build_2xi64(v1, v2v3_pack)?
698            }
699            [v1, v2, v3, v4]
700                if is_32(v1)
701                    && is_32(v2)
702                    && is_32(v3)
703                    && is_32(v4)
704                    && v1.is_float_value()
705                    && v2.is_float_value()
706                    && v3.is_float_value()
707                    && v4.is_float_value() =>
708            {
709                build_struct(
710                    func_type.get_return_type().unwrap().into_struct_type(),
711                    &[v1, v2, v3, v4],
712                )?
713            }
714            [v1, v2, v3, v4] if is_32(v1) && is_32(v2) && is_32(v3) && is_32(v4) => {
715                let v1 = err!(builder.build_bit_cast(v1, intrinsics.i32_ty, ""));
716                let v2 = err!(builder.build_bit_cast(v2, intrinsics.i32_ty, ""));
717                let v1v2_pack = pack_i32s(v1, v2)?;
718                let v3 = err!(builder.build_bit_cast(v3, intrinsics.i32_ty, ""));
719                let v4 = err!(builder.build_bit_cast(v4, intrinsics.i32_ty, ""));
720                let v3v4_pack = pack_i32s(v3, v4)?;
721                build_2xi64(v1v2_pack, v3v4_pack)?
722            }
723            _ => {
724                unreachable!("called to perform register return on struct return or void function")
725            }
726        })
727    }
728}