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