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