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::{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]
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)
    }
}PartialEq)]
41pub(crate) enum Flavor {
42    General,
43    FastcallOrVectorcall,
44}
45
46pub(crate) struct X86Options {
47    pub flavor: Flavor,
48    pub regparm: Option<u32>,
49    pub reg_struct_return: bool,
50}
51
52pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options)
53where
54    Ty: TyAbiInterface<'a, C> + Copy,
55    C: HasDataLayout + HasTargetSpec,
56{
57    if !fn_abi.ret.is_ignore() {
58        if fn_abi.ret.layout.is_aggregate() && fn_abi.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) = fn_abi.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                fn_abi.ret.cast_to(Reg { kind, size: fn_abi.ret.layout.size });
71            } else if t.abi_return_struct_as_int
72                || opts.reg_struct_return
73                || fn_abi.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(fn_abi.ret.layout, cx) {
78                    match fn_abi.ret.layout.size.bytes() {
79                        2 => fn_abi.ret.cast_to(Reg::f16()),
80                        4 => fn_abi.ret.cast_to(Reg::f32()),
81                        8 => fn_abi.ret.cast_to(Reg::f64()),
82                        _ => fn_abi.ret.make_indirect(),
83                    }
84                } else {
85                    match fn_abi.ret.layout.size.bytes() {
86                        1 => fn_abi.ret.cast_to(Reg::i8()),
87                        2 => fn_abi.ret.cast_to(Reg::i16()),
88                        4 => fn_abi.ret.cast_to(Reg::i32()),
89                        8 => fn_abi.ret.cast_to(Reg::i64()),
90                        _ => fn_abi.ret.make_indirect(),
91                    }
92                }
93            } else {
94                fn_abi.ret.make_indirect();
95            }
96        } else {
97            fn_abi.ret.extend_integer_width_to(32);
98        }
99    }
100
101    for arg in fn_abi.args.iter_mut() {
102        if arg.is_ignore() || !arg.layout.is_sized() {
103            continue;
104        }
105
106        if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
107            arg.make_indirect();
108            continue;
109        }
110
111        let t = cx.target_spec();
112        let align_4 = Align::from_bytes(4).unwrap();
113        let align_16 = Align::from_bytes(16).unwrap();
114
115        if arg.layout.is_aggregate() {
116            // We need to compute the alignment of the `byval` argument. The rules can be found in
117            // `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized
118            // here, they are:
119            //
120            // 1. If the natural alignment of the type is <= 4, the alignment is 4.
121            //
122            // 2. Otherwise, on Linux, the alignment of any vector type is the natural alignment.
123            // This doesn't matter here because we only pass aggregates via `byval`, not vectors.
124            //
125            // 3. Otherwise, on Apple platforms, the alignment of anything that contains a vector
126            // type is 16.
127            //
128            // 4. If none of these conditions are true, the alignment is 4.
129
130            fn contains_vector<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool
131            where
132                Ty: TyAbiInterface<'a, C> + Copy,
133            {
134                match layout.backend_repr {
135                    BackendRepr::Scalar(_) | BackendRepr::ScalarPair { .. } => false,
136                    BackendRepr::SimdVector { .. } => true,
137                    BackendRepr::Memory { .. } => {
138                        for i in 0..layout.fields.count() {
139                            if contains_vector(cx, layout.field(cx, i)) {
140                                return true;
141                            }
142                        }
143                        false
144                    }
145                    BackendRepr::SimdScalableVector { .. } => {
146                        {
    ::core::panicking::panic_fmt(format_args!("scalable vectors are unsupported"));
}panic!("scalable vectors are unsupported")
147                    }
148                }
149            }
150
151            let byval_align = if arg.layout.align.abi < align_4 {
152                // (1.)
153                align_4
154            } else if t.is_like_darwin && contains_vector(cx, arg.layout) {
155                // (3.)
156                align_16
157            } else {
158                // (4.)
159                align_4
160            };
161
162            arg.pass_by_stack_offset(Some(byval_align));
163        } else {
164            arg.extend_integer_width_to(32);
165        }
166    }
167
168    fill_inregs(cx, fn_abi, opts, false);
169}
170
171pub(crate) fn fill_inregs<'a, Ty, C>(
172    cx: &C,
173    fn_abi: &mut FnAbi<'a, Ty>,
174    opts: X86Options,
175    rust_abi: bool,
176) where
177    Ty: TyAbiInterface<'a, C> + Copy,
178{
179    if opts.flavor != Flavor::FastcallOrVectorcall && opts.regparm.is_none_or(|x| x == 0) {
180        return;
181    }
182    // Mark arguments as InReg like clang does it,
183    // so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall.
184
185    // Clang reference: lib/CodeGen/TargetInfo.cpp
186    // See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs()
187
188    // IsSoftFloatABI is only set to true on ARM platforms,
189    // which in turn can't be x86?
190
191    // 2 for fastcall/vectorcall, regparm limited by 3 otherwise
192    let mut free_regs = opts.regparm.unwrap_or(2).into();
193
194    // For types generating PassMode::Cast, InRegs will not be set.
195    // Maybe, this is a FIXME
196    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 { .. }));
197    if has_casts && rust_abi {
198        return;
199    }
200
201    for arg in fn_abi.args.iter_mut() {
202        let attrs = match arg.mode {
203            PassMode::Ignore
204            | PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } => {
205                continue;
206            }
207            PassMode::Direct(ref mut attrs) => attrs,
208            PassMode::Pair(..)
209            | PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ }
210            | PassMode::Cast { .. } => {
211                {
    ::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)
212            }
213        };
214
215        // At this point we know this must be a primitive of sorts.
216        let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap();
217        {
    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);
218        if #[allow(non_exhaustive_omitted_patterns)] match unit.kind {
    RegKind::Float | RegKind::Vector { .. } => true,
    _ => false,
}matches!(unit.kind, RegKind::Float | RegKind::Vector { .. }) {
219            continue;
220        }
221
222        let size_in_regs = arg.layout.size.bits().div_ceil(32);
223
224        if size_in_regs == 0 {
225            continue;
226        }
227
228        if size_in_regs > free_regs {
229            break;
230        }
231
232        free_regs -= size_in_regs;
233
234        if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer {
235            attrs.set(ArgAttribute::InReg);
236        }
237
238        if free_regs == 0 {
239            break;
240        }
241    }
242}
243
244pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
245where
246    Ty: TyAbiInterface<'a, C> + Copy,
247    C: HasDataLayout + HasTargetSpec,
248{
249    // Avoid returning floats in x87 registers on x86 as loading and storing from x87
250    // registers will quiet signalling NaNs. Also avoid using SSE registers since they
251    // are not always available (depending on target features).
252    if !fn_abi.ret.is_ignore() {
253        let has_float = match fn_abi.ret.layout.backend_repr {
254            BackendRepr::Scalar(s) => #[allow(non_exhaustive_omitted_patterns)] match s.primitive() {
    Primitive::Float(_) => true,
    _ => false,
}matches!(s.primitive(), Primitive::Float(_)),
255            BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } => {
256                #[allow(non_exhaustive_omitted_patterns)] match s1.primitive() {
    Primitive::Float(_) => true,
    _ => false,
}matches!(s1.primitive(), Primitive::Float(_))
257                    || #[allow(non_exhaustive_omitted_patterns)] match s2.primitive() {
    Primitive::Float(_) => true,
    _ => false,
}matches!(s2.primitive(), Primitive::Float(_))
258            }
259            _ => false, // anyway not passed via registers on x86
260        };
261        if has_float {
262            if cx.target_spec().rustc_abi == Some(RustcAbi::X86Sse2)
263                && fn_abi.ret.layout.backend_repr.is_scalar()
264                && fn_abi.ret.layout.size.bits() <= 128
265            {
266                // This is a single scalar that fits into an SSE register, and the target uses the
267                // SSE ABI. We prefer this over integer registers as float scalars need to be in SSE
268                // registers for float operations, so that's the best place to pass them around.
269                fn_abi.ret.cast_to(Reg::opaque_vector(fn_abi.ret.layout.size));
270            } else if fn_abi.ret.layout.size <= Primitive::Pointer(AddressSpace::ZERO).size(cx) {
271                // Same size or smaller than pointer, return in an integer register.
272                fn_abi.ret.cast_to(Reg { kind: RegKind::Integer, size: fn_abi.ret.layout.size });
273            } else {
274                // Larger than a pointer, return indirectly.
275                fn_abi.ret.make_indirect();
276            }
277            return;
278        }
279    }
280}