Skip to main content

rustc_target/callconv/
x86.rs

1use rustc_abi::{
2    AddressSpace, Align, BackendRepr, Float, HasDataLayout, Primitive, Reg, RegKind, TyAndLayout,
3};
4
5use crate::callconv::{ArgAbi, ArgAttribute, FnAbi, PassMode, TyAbiInterface};
6use crate::spec::{HasTargetSpec, RustcAbi};
7
8/// Is this a struct with a single float field?
9fn is_single_fp_element<'a, Ty, C>(mut layout: TyAndLayout<'a, Ty>, cx: &C) -> bool
10where
11    Ty: TyAbiInterface<'a, C> + Copy,
12    C: HasDataLayout,
13{
14    // On X86 over-aligned structs are disqualified.
15    let outer_size = layout.layout.size();
16
17    loop {
18        layout = layout.peel_transparent_wrappers(cx);
19
20        return match layout.backend_repr {
21            BackendRepr::Scalar(scalar) => match scalar.primitive() {
22                Primitive::Float(float) => float.size() == outer_size,
23                Primitive::Int(_, _) | Primitive::Pointer(_) => false,
24            },
25            BackendRepr::Memory { .. } => {
26                // Structs, unions and arrays all qualify.
27                if let Some((_idx, field)) = layout.non_zst_field_ignore_alignment(cx) {
28                    // NOTE: alignment is not relevant here, checking for 1-ZST is incorrect.
29                    layout = field;
30                    continue;
31                } else {
32                    false
33                }
34            }
35            _ => false,
36        };
37    }
38}
39
40#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Flavor { }
#[automatically_derived]
impl ::core::clone::Clone for Flavor {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<Option<u32>>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Flavor { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for Flavor { }
#[automatically_derived]
impl ::core::cmp::PartialEq for Flavor {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::General { regparam: __self_0 }, Self::General {
                    regparam: __arg1_0 }) => __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq)]
41pub(crate) enum Flavor {
42    General { regparam: Option<u32> },
43    FastcallOrVectorcall,
44}
45
46#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for X86Options { }
#[automatically_derived]
impl ::core::clone::Clone for X86Options {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<Flavor>;
        let _: ::core::clone::AssertParamIsClone<bool>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for X86Options { }Copy)]
47pub(crate) struct X86Options {
48    pub flavor: Flavor,
49    pub reg_struct_return: bool,
50}
51
52fn classify_ret<'a, Ty, C>(cx: &C, opts: X86Options, ret: &mut ArgAbi<'a, Ty>)
53where
54    Ty: TyAbiInterface<'a, C> + Copy,
55    C: HasDataLayout + HasTargetSpec,
56{
57    if ret.layout.is_aggregate() && ret.layout.is_sized() {
58        // Returning a structure. Most often, this will use
59        // a hidden first argument. On some platforms, though,
60        // small structs are returned as integers.
61        //
62        // Some links:
63        // https://www.angelcode.com/dev/callconv/callconv.html
64        // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp
65        let t = cx.target_spec();
66        if let Some(Float::F16) = ret.layout.complex_float(cx) {
67            // `_Complex _Float16` is returned as `<2 x half>`.
68            let kind = RegKind::Vector { hint_vector_elem: Primitive::Float(Float::F16) };
69            ret.cast_to(Reg { kind, size: ret.layout.size });
70        } else if t.abi_return_struct_as_int
71            || opts.reg_struct_return
72            || ret.layout.is_complex_number(cx)
73        {
74            // According to Clang, everyone but MSVC returns single-element
75            // float aggregates directly in a floating-point register.
76            if is_single_fp_element(ret.layout, cx) {
77                match ret.layout.size.bytes() {
78                    2 => ret.cast_to(Reg::f16()),
79                    4 => ret.cast_to(Reg::f32()),
80                    8 => ret.cast_to(Reg::f64()),
81                    _ => ret.make_indirect(),
82                }
83            } else {
84                match ret.layout.size.bytes() {
85                    1 => ret.cast_to(Reg::i8()),
86                    2 => ret.cast_to(Reg::i16()),
87                    4 => ret.cast_to(Reg::i32()),
88                    8 => ret.cast_to(Reg::i64()),
89                    _ => ret.make_indirect(),
90                }
91            }
92        } else {
93            ret.make_indirect();
94        }
95    } else {
96        ret.extend_integer_width_to(32);
97    }
98}
99
100fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
101where
102    Ty: TyAbiInterface<'a, C> + Copy,
103    C: HasDataLayout + HasTargetSpec,
104{
105    let t = cx.target_spec();
106    let align_4 = Align::from_bytes(4).unwrap();
107    let align_16 = Align::from_bytes(16).unwrap();
108
109    if arg.layout.is_aggregate() {
110        // We need to compute the alignment of the `byval` argument. The rules can be found in
111        // `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized
112        // here, they are:
113        //
114        // 1. If the natural alignment of the type is <= 4, the alignment is 4.
115        //
116        // 2. Otherwise, on Linux, the alignment of any vector type is the natural alignment.
117        // This doesn't matter here because we only pass aggregates via `byval`, not vectors.
118        //
119        // 3. Otherwise, on Apple platforms, the alignment of anything that contains a vector
120        // type is 16.
121        //
122        // 4. If none of these conditions are true, the alignment is 4.
123
124        fn contains_vector<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool
125        where
126            Ty: TyAbiInterface<'a, C> + Copy,
127        {
128            match layout.backend_repr {
129                BackendRepr::Scalar(_) | BackendRepr::ScalarPair { .. } => false,
130                BackendRepr::SimdVector { .. } => true,
131                BackendRepr::Memory { .. } => {
132                    for i in 0..layout.fields.count() {
133                        if contains_vector(cx, layout.field(cx, i)) {
134                            return true;
135                        }
136                    }
137                    false
138                }
139                BackendRepr::SimdScalableVector { .. } => {
140                    {
    ::core::panicking::panic_fmt(format_args!("scalable vectors are unsupported"));
}panic!("scalable vectors are unsupported")
141                }
142            }
143        }
144
145        let byval_align = if arg.layout.align.abi < align_4 {
146            // (1.)
147            align_4
148        } else if t.is_like_darwin && contains_vector(cx, arg.layout) {
149            // (3.)
150            align_16
151        } else {
152            // (4.)
153            align_4
154        };
155
156        arg.pass_by_stack_offset(Some(byval_align));
157    } else {
158        arg.extend_integer_width_to(32);
159    }
160}
161
162pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options)
163where
164    Ty: TyAbiInterface<'a, C> + Copy,
165    C: HasDataLayout + HasTargetSpec,
166{
167    if !fn_abi.ret.is_ignore() {
168        classify_ret(cx, opts, &mut fn_abi.ret);
169    }
170
171    for arg in fn_abi.args.iter_mut() {
172        if arg.is_ignore() || !arg.layout.is_sized() {
173            continue;
174        }
175
176        if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
177            arg.make_indirect();
178            continue;
179        }
180
181        classify_arg(cx, arg);
182    }
183
184    fill_inregs(cx, fn_abi, opts, false);
185}
186
187pub(crate) fn fill_inregs<'a, Ty, C>(
188    cx: &C,
189    fn_abi: &mut FnAbi<'a, Ty>,
190    opts: X86Options,
191    rust_abi: bool,
192) where
193    Ty: TyAbiInterface<'a, C> + Copy,
194{
195    // Mark arguments as InReg like clang does it,
196    // so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall.
197
198    // Clang reference: lib/CodeGen/TargetInfo.cpp
199    // See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs()
200
201    // IsSoftFloatABI is only set to true on ARM platforms,
202    // which in turn can't be x86?
203
204    // The number of registers available for argument passing.
205    //
206    // An `extern "fastcall"` and `extern "vectorcall"` function always have 2 registers available.
207    // Otherwise the `regparam` count (in the range 0..=3) determines the number of available
208    // registers. If unspecified, no registers are used for argument passing.
209    let mut free_regs = match opts.flavor {
210        Flavor::FastcallOrVectorcall => 2,
211        Flavor::General { regparam } => u64::from(regparam.unwrap_or(0)),
212    };
213
214    if free_regs == 0 {
215        return;
216    }
217
218    // For types generating PassMode::Cast, InRegs will not be set.
219    // Maybe, this is a FIXME
220    let has_casts = fn_abi.args.iter().any(|arg| #[allow(non_exhaustive_omitted_patterns)] match arg.mode {
    PassMode::Cast { .. } => true,
    _ => false,
}matches!(arg.mode, PassMode::Cast { .. }));
221    if has_casts && rust_abi {
222        return;
223    }
224
225    for arg in fn_abi.args.iter_mut() {
226        let attrs = match arg.mode {
227            PassMode::Ignore
228            | PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } => {
229                continue;
230            }
231            PassMode::Direct(ref mut attrs) => attrs,
232            PassMode::Pair(..)
233            | PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ }
234            | PassMode::Cast { .. } => {
235                {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("x86 shouldn\'t be passing arguments by {0:?}",
                arg.mode)));
}unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
236            }
237        };
238
239        // At this point we know this must be a primitive of sorts.
240        let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap();
241        {
    match (&unit.size, &arg.layout.size) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(unit.size, arg.layout.size);
242        if #[allow(non_exhaustive_omitted_patterns)] match unit.kind {
    RegKind::Float | RegKind::Vector { .. } => true,
    _ => false,
}matches!(unit.kind, RegKind::Float | RegKind::Vector { .. }) {
243            continue;
244        }
245
246        let size_in_regs = arg.layout.size.bits().div_ceil(32);
247
248        if size_in_regs == 0 {
249            continue;
250        }
251
252        if size_in_regs > free_regs {
253            break;
254        }
255
256        free_regs -= size_in_regs;
257
258        if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer {
259            attrs.set(ArgAttribute::InReg);
260        }
261
262        if free_regs == 0 {
263            break;
264        }
265    }
266}
267
268pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
269where
270    Ty: TyAbiInterface<'a, C> + Copy,
271    C: HasDataLayout + HasTargetSpec,
272{
273    // Avoid returning floats in x87 registers on x86 as loading and storing from x87
274    // registers will quiet signalling NaNs. Also avoid using SSE registers since they
275    // are not always available (depending on target features).
276    if !fn_abi.ret.is_ignore() {
277        let has_float = match fn_abi.ret.layout.backend_repr {
278            BackendRepr::Scalar(s) => #[allow(non_exhaustive_omitted_patterns)] match s.primitive() {
    Primitive::Float(_) => true,
    _ => false,
}matches!(s.primitive(), Primitive::Float(_)),
279            BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } => {
280                #[allow(non_exhaustive_omitted_patterns)] match s1.primitive() {
    Primitive::Float(_) => true,
    _ => false,
}matches!(s1.primitive(), Primitive::Float(_))
281                    || #[allow(non_exhaustive_omitted_patterns)] match s2.primitive() {
    Primitive::Float(_) => true,
    _ => false,
}matches!(s2.primitive(), Primitive::Float(_))
282            }
283            _ => false, // anyway not passed via registers on x86
284        };
285        if has_float {
286            if cx.target_spec().rustc_abi == Some(RustcAbi::X86Sse2)
287                && fn_abi.ret.layout.backend_repr.is_scalar()
288                && fn_abi.ret.layout.size.bits() <= 128
289            {
290                // This is a single scalar that fits into an SSE register, and the target uses the
291                // SSE ABI. We prefer this over integer registers as float scalars need to be in SSE
292                // registers for float operations, so that's the best place to pass them around.
293                fn_abi.ret.cast_to(Reg::opaque_vector(fn_abi.ret.layout.size));
294            } else if fn_abi.ret.layout.size <= Primitive::Pointer(AddressSpace::ZERO).size(cx) {
295                // Same size or smaller than pointer, return in an integer register.
296                fn_abi.ret.cast_to(Reg { kind: RegKind::Integer, size: fn_abi.ret.layout.size });
297            } else {
298                // Larger than a pointer, return indirectly.
299                fn_abi.ret.make_indirect();
300            }
301            return;
302        }
303    }
304}