]> git.lizzy.rs Git - rust.git/blob - src/librustc_target/abi/call/x86_64.rs
Auto merge of #68414 - michaelwoerister:share-drop-glue, r=alexcrichton
[rust.git] / src / librustc_target / abi / call / 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/master/compiler/src/externals.cpp
3
4 use crate::abi::call::{ArgAbi, CastTarget, FnAbi, Reg, RegKind};
5 use crate::abi::{self, Abi, HasDataLayout, LayoutOf, Size, TyLayout, TyLayoutMethods};
6
7 /// Classification of "eightbyte" components.
8 // N.B., the order of the variants is from general to specific,
9 // such that `unify(a, b)` is the "smaller" of `a` and `b`.
10 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
11 enum Class {
12     Int,
13     Sse,
14     SseUp,
15 }
16
17 #[derive(Clone, Copy, Debug)]
18 struct Memory;
19
20 // Currently supported vector size (AVX-512).
21 const LARGEST_VECTOR_SIZE: usize = 512;
22 const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64;
23
24 fn classify_arg<'a, Ty, C>(
25     cx: &C,
26     arg: &ArgAbi<'a, Ty>,
27 ) -> Result<[Option<Class>; MAX_EIGHTBYTES], Memory>
28 where
29     Ty: TyLayoutMethods<'a, C> + Copy,
30     C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout,
31 {
32     fn classify<'a, Ty, C>(
33         cx: &C,
34         layout: TyLayout<'a, Ty>,
35         cls: &mut [Option<Class>],
36         off: Size,
37     ) -> Result<(), Memory>
38     where
39         Ty: TyLayoutMethods<'a, C> + Copy,
40         C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout,
41     {
42         if !off.is_aligned(layout.align.abi) {
43             if !layout.is_zst() {
44                 return Err(Memory);
45             }
46             return Ok(());
47         }
48
49         let mut c = match layout.abi {
50             Abi::Uninhabited => return Ok(()),
51
52             Abi::Scalar(ref scalar) => match scalar.value {
53                 abi::Int(..) | abi::Pointer => Class::Int,
54                 abi::F32 | abi::F64 => Class::Sse,
55             },
56
57             Abi::Vector { .. } => Class::Sse,
58
59             Abi::ScalarPair(..) | Abi::Aggregate { .. } => match layout.variants {
60                 abi::Variants::Single { .. } => {
61                     for i in 0..layout.fields.count() {
62                         let field_off = off + layout.fields.offset(i);
63                         classify(cx, layout.field(cx, i), cls, field_off)?;
64                     }
65                     return Ok(());
66                 }
67                 abi::Variants::Multiple { .. } => return Err(Memory),
68             },
69         };
70
71         // Fill in `cls` for scalars (Int/Sse) and vectors (Sse).
72         let first = (off.bytes() / 8) as usize;
73         let last = ((off.bytes() + layout.size.bytes() - 1) / 8) as usize;
74         for cls in &mut cls[first..=last] {
75             *cls = Some(cls.map_or(c, |old| old.min(c)));
76
77             // Everything after the first Sse "eightbyte"
78             // component is the upper half of a register.
79             if c == Class::Sse {
80                 c = Class::SseUp;
81             }
82         }
83
84         Ok(())
85     }
86
87     let n = ((arg.layout.size.bytes() + 7) / 8) as usize;
88     if n > MAX_EIGHTBYTES {
89         return Err(Memory);
90     }
91
92     let mut cls = [None; MAX_EIGHTBYTES];
93     classify(cx, arg.layout, &mut cls, Size::ZERO)?;
94     if n > 2 {
95         if cls[0] != Some(Class::Sse) {
96             return Err(Memory);
97         }
98         if cls[1..n].iter().any(|&c| c != Some(Class::SseUp)) {
99             return Err(Memory);
100         }
101     } else {
102         let mut i = 0;
103         while i < n {
104             if cls[i] == Some(Class::SseUp) {
105                 cls[i] = Some(Class::Sse);
106             } else if cls[i] == Some(Class::Sse) {
107                 i += 1;
108                 while i != n && cls[i] == Some(Class::SseUp) {
109                     i += 1;
110                 }
111             } else {
112                 i += 1;
113             }
114         }
115     }
116
117     Ok(cls)
118 }
119
120 fn reg_component(cls: &[Option<Class>], i: &mut usize, size: Size) -> Option<Reg> {
121     if *i >= cls.len() {
122         return None;
123     }
124
125     match cls[*i] {
126         None => None,
127         Some(Class::Int) => {
128             *i += 1;
129             Some(if size.bytes() < 8 { Reg { kind: RegKind::Integer, size } } else { Reg::i64() })
130         }
131         Some(Class::Sse) => {
132             let vec_len =
133                 1 + cls[*i + 1..].iter().take_while(|&&c| c == Some(Class::SseUp)).count();
134             *i += vec_len;
135             Some(if vec_len == 1 {
136                 match size.bytes() {
137                     4 => Reg::f32(),
138                     _ => Reg::f64(),
139                 }
140             } else {
141                 Reg { kind: RegKind::Vector, size: Size::from_bytes(8) * (vec_len as u64) }
142             })
143         }
144         Some(c) => unreachable!("reg_component: unhandled class {:?}", c),
145     }
146 }
147
148 fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
149     let mut i = 0;
150     let lo = reg_component(cls, &mut i, size).unwrap();
151     let offset = Size::from_bytes(8) * (i as u64);
152     let mut target = CastTarget::from(lo);
153     if size > offset {
154         if let Some(hi) = reg_component(cls, &mut i, size - offset) {
155             target = CastTarget::pair(lo, hi);
156         }
157     }
158     assert_eq!(reg_component(cls, &mut i, Size::ZERO), None);
159     target
160 }
161
162 const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9
163 const MAX_SSE_REGS: usize = 8; // XMM0-7
164
165 pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
166 where
167     Ty: TyLayoutMethods<'a, C> + Copy,
168     C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout,
169 {
170     let mut int_regs = MAX_INT_REGS;
171     let mut sse_regs = MAX_SSE_REGS;
172
173     let mut x86_64_arg_or_ret = |arg: &mut ArgAbi<'a, Ty>, is_arg: bool| {
174         let mut cls_or_mem = classify_arg(cx, arg);
175
176         if is_arg {
177             if let Ok(cls) = cls_or_mem {
178                 let mut needed_int = 0;
179                 let mut needed_sse = 0;
180                 for &c in &cls {
181                     match c {
182                         Some(Class::Int) => needed_int += 1,
183                         Some(Class::Sse) => needed_sse += 1,
184                         _ => {}
185                     }
186                 }
187                 match (int_regs.checked_sub(needed_int), sse_regs.checked_sub(needed_sse)) {
188                     (Some(left_int), Some(left_sse)) => {
189                         int_regs = left_int;
190                         sse_regs = left_sse;
191                     }
192                     _ => {
193                         // Not enough registers for this argument, so it will be
194                         // passed on the stack, but we only mark aggregates
195                         // explicitly as indirect `byval` arguments, as LLVM will
196                         // automatically put immediates on the stack itself.
197                         if arg.layout.is_aggregate() {
198                             cls_or_mem = Err(Memory);
199                         }
200                     }
201                 }
202             }
203         }
204
205         match cls_or_mem {
206             Err(Memory) => {
207                 if is_arg {
208                     arg.make_indirect_byval();
209                 } else {
210                     // `sret` parameter thus one less integer register available
211                     arg.make_indirect();
212                     // NOTE(eddyb) return is handled first, so no registers
213                     // should've been used yet.
214                     assert_eq!(int_regs, MAX_INT_REGS);
215                     int_regs -= 1;
216                 }
217             }
218             Ok(ref cls) => {
219                 // split into sized chunks passed individually
220                 if arg.layout.is_aggregate() {
221                     let size = arg.layout.size;
222                     arg.cast_to(cast_target(cls, size))
223                 } else {
224                     arg.extend_integer_width_to(32);
225                 }
226             }
227         }
228     };
229
230     if !fn_abi.ret.is_ignore() {
231         x86_64_arg_or_ret(&mut fn_abi.ret, false);
232     }
233
234     for arg in &mut fn_abi.args {
235         if arg.is_ignore() {
236             continue;
237         }
238         x86_64_arg_or_ret(arg, true);
239     }
240 }