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
20pub struct Aarch64SystemV {}
22
23impl Abi for Aarch64SystemV {
24 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.into_pointer_value()
39 }
40
41 fn get_g0_ptr_param<'ctx>(&self, func_value: &FunctionValue<'ctx>) -> IntValue<'ctx> {
44 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 fn get_m0_ptr_param<'ctx>(&self, func_value: &FunctionValue<'ctx>) -> PointerValue<'ctx> {
63 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 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 param_types.push(Ok(intrinsics.i32_ty.as_basic_type_enum()));
94 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, })
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 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 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 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, })
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); 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 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, })
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}