Skip to main content

rustc_target/callconv/
x86_64.rs

1// The classification code for the x86_64 ABI is taken from the clay language
2// https://github.com/jckarter/clay/blob/db0bd2702ab0b6e48965cd85f8859bbd5f60e48e/compiler/externals.cpp
3
4use rustc_abi::{
5    BackendRepr, HasDataLayout, Primitive, Reg, RegKind, Size, TyAbiInterface, TyAndLayout,
6    Variants,
7};
8
9use crate::callconv::{ArgAbi, CastTarget, FnAbi};
10use crate::spec::HasTargetSpec;
11
12/// Classification of "eightbyte" components.
13// N.B., the order of the variants is from general to specific,
14// such that `unify(a, b)` is the "smaller" of `a` and `b`.
15#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Class { }
#[automatically_derived]
impl ::core::clone::Clone for Class {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Class { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for Class { }
#[automatically_derived]
impl ::core::cmp::PartialEq for Class {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for Class { }Eq, #[automatically_derived]
impl ::core::cmp::PartialOrd for Class {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
    }
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for Class {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        ::core::cmp::Ord::cmp(&::core::intrinsics::discriminant_value(self),
            &::core::intrinsics::discriminant_value(other))
    }
}Ord, #[automatically_derived]
impl ::core::fmt::Debug for Class {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                Class::Int => "Int",
                Class::Sse => "Sse",
                Class::SseUp => "SseUp",
            })
    }
}Debug)]
16enum Class {
17    Int,
18    Sse,
19    SseUp,
20}
21
22#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Memory { }
#[automatically_derived]
impl ::core::clone::Clone for Memory {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Memory { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for Memory {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "Memory")
    }
}Debug)]
23struct Memory;
24
25// Currently supported vector size (AVX-512).
26const LARGEST_VECTOR_SIZE: usize = 512;
27const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64;
28
29fn classify_arg<'a, Ty, C>(
30    cx: &C,
31    arg: &ArgAbi<'a, Ty>,
32) -> Result<[Option<Class>; MAX_EIGHTBYTES], Memory>
33where
34    Ty: TyAbiInterface<'a, C> + Copy,
35    C: HasDataLayout,
36{
37    fn classify<'a, Ty, C>(
38        cx: &C,
39        layout: TyAndLayout<'a, Ty>,
40        cls: &mut [Option<Class>],
41        off: Size,
42    ) -> Result<(), Memory>
43    where
44        Ty: TyAbiInterface<'a, C> + Copy,
45        C: HasDataLayout,
46    {
47        if !off.is_aligned(layout.align.abi) {
48            if !layout.is_zst() {
49                return Err(Memory);
50            }
51            return Ok(());
52        }
53
54        let mut c = match layout.backend_repr {
55            BackendRepr::Scalar(scalar) => match scalar.primitive() {
56                Primitive::Int(..) | Primitive::Pointer(_) => Class::Int,
57                Primitive::Float(_) => Class::Sse,
58            },
59
60            BackendRepr::SimdVector { .. } => Class::Sse,
61
62            BackendRepr::SimdScalableVector { .. } => {
    ::core::panicking::panic_fmt(format_args!("scalable vectors are unsupported"));
}panic!("scalable vectors are unsupported"),
63
64            BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => {
65                for i in 0..layout.fields.count() {
66                    let field_off = off + layout.fields.offset(i);
67                    classify(cx, layout.field(cx, i), cls, field_off)?;
68                }
69
70                match &layout.variants {
71                    Variants::Single { .. } | Variants::Empty => {}
72                    Variants::Multiple { variants, .. } => {
73                        // Treat enum variants like union members.
74                        for variant_idx in variants.indices() {
75                            classify(cx, layout.for_variant(cx, variant_idx), cls, off)?;
76                        }
77                    }
78                }
79
80                return Ok(());
81            }
82        };
83
84        // Fill in `cls` for scalars (Int/Sse) and vectors (Sse).
85        let first = (off.bytes() / 8) as usize;
86        let last = ((off.bytes() + layout.size.bytes() - 1) / 8) as usize;
87        for cls in &mut cls[first..=last] {
88            *cls = Some(cls.map_or(c, |old| old.min(c)));
89
90            // Everything after the first Sse "eightbyte"
91            // component is the upper half of a register.
92            if c == Class::Sse {
93                c = Class::SseUp;
94            }
95        }
96
97        Ok(())
98    }
99
100    let n = arg.layout.size.bytes().div_ceil(8) as usize;
101    if n > MAX_EIGHTBYTES {
102        return Err(Memory);
103    }
104
105    let mut cls = [None; MAX_EIGHTBYTES];
106    classify(cx, arg.layout, &mut cls, Size::ZERO)?;
107    if n > 2 {
108        if cls[0] != Some(Class::Sse) {
109            return Err(Memory);
110        }
111        if cls[1..n].iter().any(|&c| c != Some(Class::SseUp)) {
112            return Err(Memory);
113        }
114    } else {
115        let mut i = 0;
116        while i < n {
117            if cls[i] == Some(Class::SseUp) {
118                cls[i] = Some(Class::Sse);
119            } else if cls[i] == Some(Class::Sse) {
120                i += 1;
121                while i != n && cls[i] == Some(Class::SseUp) {
122                    i += 1;
123                }
124            } else {
125                i += 1;
126            }
127        }
128    }
129
130    Ok(cls)
131}
132
133fn reg_component(cls: &[Option<Class>], i: &mut usize, size: Size) -> Option<Reg> {
134    let Some(Some(class)) = cls.get(*i) else {
135        return None;
136    };
137
138    match class {
139        Class::Int => {
140            *i += 1;
141            Some(if size.bytes() < 8 { Reg { kind: RegKind::Integer, size } } else { Reg::i64() })
142        }
143        Class::Sse => {
144            let vec_len =
145                1 + cls[*i + 1..].iter().take_while(|&&c| c == Some(Class::SseUp)).count();
146            *i += vec_len;
147            Some(if vec_len == 1 {
148                match size.bytes() {
149                    4 => Reg::f32(),
150                    _ => Reg::f64(),
151                }
152            } else {
153                Reg::opaque_vector(Size::from_bytes(8) * (vec_len as u64))
154            })
155        }
156        c => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("reg_component: unhandled class {0:?}", c)));
}unreachable!("reg_component: unhandled class {:?}", c),
157    }
158}
159
160fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
161    let mut i = 0;
162    let lo = reg_component(cls, &mut i, size).unwrap();
163    let offset = Size::from_bytes(8) * (i as u64);
164    let mut target = CastTarget::from(lo);
165    if size > offset {
166        if let Some(hi) = reg_component(cls, &mut i, size - offset) {
167            target = CastTarget::pair(lo, hi);
168        }
169    }
170    {
    match (&reg_component(cls, &mut i, Size::ZERO), &None) {
        (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!(reg_component(cls, &mut i, Size::ZERO), None);
171    target
172}
173
174const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9
175const MAX_SSE_REGS: usize = 8; // XMM0-7
176
177pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
178where
179    Ty: TyAbiInterface<'a, C> + Copy,
180    C: HasDataLayout + HasTargetSpec,
181{
182    let mut int_regs = MAX_INT_REGS;
183    let mut sse_regs = MAX_SSE_REGS;
184
185    let mut x86_64_arg_or_ret = |arg: &mut ArgAbi<'a, Ty>, is_arg: bool| {
186        if !arg.layout.is_sized() {
187            // FIXME: Update int_regs?
188            // Not touching this...
189            return;
190        }
191        if is_arg && arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
192            int_regs = int_regs.saturating_sub(1);
193            arg.make_indirect();
194            return;
195        }
196        let mut cls_or_mem = classify_arg(cx, arg);
197
198        if is_arg {
199            if let Ok(cls) = cls_or_mem {
200                let mut needed_int = 0;
201                let mut needed_sse = 0;
202                for c in cls {
203                    match c {
204                        Some(Class::Int) => needed_int += 1,
205                        Some(Class::Sse) => needed_sse += 1,
206                        _ => {}
207                    }
208                }
209                match (int_regs.checked_sub(needed_int), sse_regs.checked_sub(needed_sse)) {
210                    (Some(left_int), Some(left_sse)) => {
211                        int_regs = left_int;
212                        sse_regs = left_sse;
213                    }
214                    _ => {
215                        // Not enough registers for this argument, so it will be
216                        // passed on the stack, but we only mark aggregates
217                        // explicitly as indirect `byval` arguments, as LLVM will
218                        // automatically put immediates on the stack itself.
219                        if arg.layout.is_aggregate() {
220                            cls_or_mem = Err(Memory);
221                        }
222                    }
223                }
224            }
225        }
226
227        match cls_or_mem {
228            Err(Memory) => {
229                if is_arg {
230                    // The x86_64 ABI doesn't have any special requirements for `byval` alignment,
231                    // the type's alignment is always used.
232                    arg.pass_by_stack_offset(None);
233                } else {
234                    // `sret` parameter thus one less integer register available
235                    arg.make_indirect();
236                    // NOTE(eddyb) return is handled first, so no registers
237                    // should've been used yet.
238                    {
    match (&int_regs, &MAX_INT_REGS) {
        (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!(int_regs, MAX_INT_REGS);
239                    int_regs -= 1;
240                }
241            }
242            Ok(ref cls) => {
243                // split into sized chunks passed individually
244                if arg.layout.is_aggregate() {
245                    let size = arg.layout.size;
246                    arg.cast_to(cast_target(cls, size));
247                } else if is_arg || cx.target_spec().is_like_darwin {
248                    arg.extend_integer_width_to(32);
249                }
250            }
251        }
252    };
253
254    if !fn_abi.ret.is_ignore() {
255        x86_64_arg_or_ret(&mut fn_abi.ret, false);
256    }
257
258    for arg in fn_abi.args.iter_mut() {
259        if arg.is_ignore() {
260            continue;
261        }
262        x86_64_arg_or_ret(arg, true);
263    }
264}