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