]> git.lizzy.rs Git - rust.git/blob - src/librustc_target/abi/call/x86_64.rs
f091f80924d5937d3e32e10fd82f13ddc2b66da3
[rust.git] / src / librustc_target / abi / call / x86_64.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // The classification code for the x86_64 ABI is taken from the clay language
12 // https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp
13
14 use abi::call::{ArgType, CastTarget, FnType, Reg, RegKind};
15 use abi::{self, Abi, HasDataLayout, LayoutOf, Size, TyLayout, TyLayoutMethods};
16
17 /// Classification of "eightbyte" components.
18 // NB: the order of the variants is from general to specific,
19 // such that `unify(a, b)` is the "smaller" of `a` and `b`.
20 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
21 enum Class {
22     Int,
23     Sse,
24     SseUp
25 }
26
27 #[derive(Clone, Copy, Debug)]
28 struct Memory;
29
30 // Currently supported vector size (AVX-512).
31 const LARGEST_VECTOR_SIZE: usize = 512;
32 const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64;
33
34 fn classify_arg<'a, Ty, C>(cx: &C, arg: &ArgType<'a, Ty>)
35                           -> Result<[Option<Class>; MAX_EIGHTBYTES], Memory>
36     where Ty: TyLayoutMethods<'a, C> + Copy,
37           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
38 {
39     fn classify<'a, Ty, C>(cx: &C, layout: TyLayout<'a, Ty>,
40                           cls: &mut [Option<Class>], off: Size) -> Result<(), Memory>
41         where Ty: TyLayoutMethods<'a, C> + Copy,
42             C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
43     {
44         if !off.is_aligned(layout.align.abi) {
45             if !layout.is_zst() {
46                 return Err(Memory);
47             }
48             return Ok(());
49         }
50
51         let mut c = match layout.abi {
52             Abi::Uninhabited => return Ok(()),
53
54             Abi::Scalar(ref scalar) => {
55                 match scalar.value {
56                     abi::Int(..) |
57                     abi::Pointer => Class::Int,
58                     abi::Float(_) => Class::Sse
59                 }
60             }
61
62             Abi::Vector { .. } => Class::Sse,
63
64             Abi::ScalarPair(..) |
65             Abi::Aggregate { .. } => {
66                 match layout.variants {
67                     abi::Variants::Single { .. } => {
68                         for i in 0..layout.fields.count() {
69                             let field_off = off + layout.fields.offset(i);
70                             classify(cx, layout.field(cx, i), cls, field_off)?;
71                         }
72                         return Ok(());
73                     }
74                     abi::Variants::Tagged { .. } |
75                     abi::Variants::NicheFilling { .. } => return Err(Memory),
76                 }
77             }
78
79         };
80
81         // Fill in `cls` for scalars (Int/Sse) and vectors (Sse).
82         let first = (off.bytes() / 8) as usize;
83         let last = ((off.bytes() + layout.size.bytes() - 1) / 8) as usize;
84         for cls in &mut cls[first..=last] {
85             *cls = Some(cls.map_or(c, |old| old.min(c)));
86
87             // Everything after the first Sse "eightbyte"
88             // component is the upper half of a register.
89             if c == Class::Sse {
90                 c = Class::SseUp;
91             }
92         }
93
94         Ok(())
95     }
96
97     let n = ((arg.layout.size.bytes() + 7) / 8) as usize;
98     if n > MAX_EIGHTBYTES {
99         return Err(Memory);
100     }
101
102     let mut cls = [None; MAX_EIGHTBYTES];
103     classify(cx, arg.layout, &mut cls, Size::ZERO)?;
104     if n > 2 {
105         if cls[0] != Some(Class::Sse) {
106             return Err(Memory);
107         }
108         if cls[1..n].iter().any(|&c| c != Some(Class::SseUp)) {
109             return Err(Memory);
110         }
111     } else {
112         let mut i = 0;
113         while i < n {
114             if cls[i] == Some(Class::SseUp) {
115                 cls[i] = Some(Class::Sse);
116             } else if cls[i] == Some(Class::Sse) {
117                 i += 1;
118                 while i != n && cls[i] == Some(Class::SseUp) { i += 1; }
119             } else {
120                 i += 1;
121             }
122         }
123     }
124
125     Ok(cls)
126 }
127
128 fn reg_component(cls: &[Option<Class>], i: &mut usize, size: Size) -> Option<Reg> {
129     if *i >= cls.len() {
130         return None;
131     }
132
133     match cls[*i] {
134         None => None,
135         Some(Class::Int) => {
136             *i += 1;
137             Some(if size.bytes() < 8 {
138                 Reg {
139                     kind: RegKind::Integer,
140                     size
141                 }
142             } else {
143                 Reg::i64()
144             })
145         }
146         Some(Class::Sse) => {
147             let vec_len = 1 + cls[*i+1..].iter()
148                 .take_while(|&&c| c == Some(Class::SseUp))
149                 .count();
150             *i += vec_len;
151             Some(if vec_len == 1 {
152                 match size.bytes() {
153                     4 => Reg::f32(),
154                     _ => Reg::f64()
155                 }
156             } else {
157                 Reg {
158                     kind: RegKind::Vector,
159                     size: Size::from_bytes(8) * (vec_len as u64)
160                 }
161             })
162         }
163         Some(c) => unreachable!("reg_component: unhandled class {:?}", c)
164     }
165 }
166
167 fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
168     let mut i = 0;
169     let lo = reg_component(cls, &mut i, size).unwrap();
170     let offset = Size::from_bytes(8) * (i as u64);
171     let mut target = CastTarget::from(lo);
172     if size > offset {
173         if let Some(hi) = reg_component(cls, &mut i, size - offset) {
174             target = CastTarget::pair(lo, hi);
175         }
176     }
177     assert_eq!(reg_component(cls, &mut i, Size::ZERO), None);
178     target
179 }
180
181 pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
182     where Ty: TyLayoutMethods<'a, C> + Copy,
183           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
184 {
185     let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9
186     let mut sse_regs = 8; // XMM0-7
187
188     let mut x86_64_ty = |arg: &mut ArgType<'a, Ty>, is_arg: bool| {
189         let mut cls_or_mem = classify_arg(cx, arg);
190
191         let mut needed_int = 0;
192         let mut needed_sse = 0;
193         if is_arg {
194             if let Ok(cls) = cls_or_mem {
195                 for &c in &cls {
196                     match c {
197                         Some(Class::Int) => needed_int += 1,
198                         Some(Class::Sse) => needed_sse += 1,
199                         _ => {}
200                     }
201                 }
202                 if arg.layout.is_aggregate() && (int_regs < needed_int || sse_regs < needed_sse) {
203                     cls_or_mem = Err(Memory);
204                 }
205             }
206         }
207
208         match cls_or_mem {
209             Err(Memory) => {
210                 if is_arg {
211                     arg.make_indirect_byval();
212                 } else {
213                     // `sret` parameter thus one less integer register available
214                     arg.make_indirect();
215                     int_regs -= 1;
216                 }
217             }
218             Ok(ref cls) => {
219                 // split into sized chunks passed individually
220                 int_regs -= needed_int;
221                 sse_regs -= needed_sse;
222
223                 if arg.layout.is_aggregate() {
224                     let size = arg.layout.size;
225                     arg.cast_to(cast_target(cls, size))
226                 } else {
227                     arg.extend_integer_width_to(32);
228                 }
229             }
230         }
231     };
232
233     if !fty.ret.is_ignore() {
234         x86_64_ty(&mut fty.ret, false);
235     }
236
237     for arg in &mut fty.args {
238         if arg.is_ignore() { continue; }
239         x86_64_ty(arg, true);
240     }
241 }