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