Skip to main content

rustc_target/callconv/
wasm.rs

1use rustc_abi::{
2    BackendRepr, Float, HasDataLayout, Integer, Primitive, Reg, RegKind, TagEncoding,
3    TyAbiInterface, TyAndLayout, Variants,
4};
5
6use crate::callconv::{ArgAbi, FnAbi};
7
8fn singleton_scalar<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> Option<Reg>
9where
10    Ty: TyAbiInterface<'a, C> + Copy,
11    C: HasDataLayout,
12{
13    // The base case: a single scalar is a singleton scalar.
14    if !(layout.is_aggregate() || layout.peel_transparent_wrappers(cx).is_enum()) {
15        let BackendRepr::Scalar(scalar) = layout.backend_repr else {
16            return None;
17        };
18        return Some(Reg { kind: RegKind::from_primitive(scalar.primitive()), size: layout.size });
19    }
20
21    // Enums that are represented as scalars need special care:
22    //
23    // - `#[repr(u8)] enum { A, B }` is a singleton scalar
24    // - `#[repr(u8)] enum { A(()), B }` is not
25    //
26    // To rust their representation is the same, but clang looks at the syntax.
27    // Niches have custom behavior too, so `Option<&i32>` is a singleton scalar.
28    if let Variants::Multiple { tag, tag_encoding: TagEncoding::Direct, variants, .. } =
29        &layout.variants
30    {
31        if variants.iter().all(|x| x.field_offsets.is_empty()) {
32            return Some(Reg { kind: RegKind::from_primitive(tag.primitive()), size: layout.size });
33        }
34        return None;
35    }
36
37    let mut found = None;
38    for i in 0..layout.fields.count() {
39        let field = layout.field(cx, i);
40        if field.is_zst() {
41            continue;
42        }
43        if found.is_some() {
44            // A second member, so not a singleton.
45            return None;
46        }
47        found = Some(singleton_scalar(cx, field)?);
48    }
49
50    // Reject over-aligned types.
51    found.filter(|scalar| scalar.size == layout.size)
52}
53
54/// Return whether the value should be passed as an aggregate (i.e. indirectly).
55///
56/// - Enums with integer layout and variants with only zst members are passed as aggregates
57/// - Aggregate wrappers around a single scalar are passed as scalars
58fn is_aggregate_for_abi<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool
59where
60    Ty: TyAbiInterface<'a, C> + Copy,
61    C: HasDataLayout,
62{
63    // An enum that is represented as an integer is not an aggregate to rust, but may still
64    // need to be passed as one if its variants have any (even ZST) fields.
65    if !(val.layout.is_aggregate() || val.layout.peel_transparent_wrappers(cx).is_enum()) {
66        return false;
67    }
68
69    let Some(scalar) = singleton_scalar(cx, val.layout) else {
70        return true;
71    };
72
73    // This is an enum with integer layout, no need to cast.
74    if !val.layout.is_aggregate() {
75        return false;
76    }
77
78    val.cast_to(scalar);
79    false
80}
81
82fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>)
83where
84    Ty: TyAbiInterface<'a, C> + Copy,
85    C: HasDataLayout,
86{
87    // `long double`, `__int128_t` and `__uint128_t` use an indirect return
88    if let BackendRepr::Scalar(scalar) = ret.layout.backend_repr
89        && #[allow(non_exhaustive_omitted_patterns)] match scalar.primitive() {
    Primitive::Int(Integer::I128, _) | Primitive::Float(Float::F128) => true,
    _ => false,
}matches!(
90            scalar.primitive(),
91            Primitive::Int(Integer::I128, _) | Primitive::Float(Float::F128)
92        )
93    {
94        ret.make_indirect();
95        return;
96    }
97
98    ret.extend_integer_width_to(32);
99    if is_aggregate_for_abi(cx, ret) {
100        ret.make_indirect();
101    }
102}
103
104fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
105where
106    Ty: TyAbiInterface<'a, C> + Copy,
107    C: HasDataLayout,
108{
109    if !arg.layout.is_sized() {
110        // Not touching this...
111        return;
112    }
113    if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
114        arg.make_indirect();
115        return;
116    }
117    arg.extend_integer_width_to(32);
118    if is_aggregate_for_abi(cx, arg) {
119        arg.make_indirect();
120    }
121}
122
123/// The purpose of this ABI is to match the C ABI (aka clang) exactly.
124pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
125where
126    Ty: TyAbiInterface<'a, C> + Copy,
127    C: HasDataLayout,
128{
129    if !fn_abi.ret.is_ignore() {
130        classify_ret(cx, &mut fn_abi.ret);
131    }
132
133    for arg in fn_abi.args.iter_mut() {
134        if arg.is_ignore() {
135            continue;
136        }
137        classify_arg(cx, arg);
138    }
139}