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