]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/cabi_x86_64.rs
Rollup merge of #41249 - GuillaumeGomez:rustdoc-render, r=steveklabnik,frewsxcv
[rust.git] / src / librustc_trans / cabi_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::{ArgType, ArgAttribute, CastTarget, FnType, LayoutExt, Reg, RegKind};
15 use context::CrateContext;
16
17 use rustc::ty::layout::{self, Layout, TyLayout, Size};
18
19 #[derive(Clone, Copy, PartialEq, Debug)]
20 enum Class {
21     None,
22     Int,
23     Sse,
24     SseUp
25 }
26
27 #[derive(Clone, Copy, Debug)]
28 struct Memory;
29
30 // Currently supported vector size (AVX).
31 const LARGEST_VECTOR_SIZE: usize = 256;
32 const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64;
33
34 fn classify_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &ArgType<'tcx>)
35                           -> Result<[Class; MAX_EIGHTBYTES], Memory> {
36     fn unify(cls: &mut [Class],
37              off: u64,
38              c: Class) {
39         let i = (off / 8) as usize;
40         let to_write = match (cls[i], c) {
41             (Class::None, _) => c,
42             (_, Class::None) => return,
43
44             (Class::Int, _) |
45             (_, Class::Int) => Class::Int,
46
47             (Class::Sse, _) |
48             (_, Class::Sse) => Class::Sse,
49
50             (Class::SseUp, Class::SseUp) => Class::SseUp
51         };
52         cls[i] = to_write;
53     }
54
55     fn classify<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
56                           layout: TyLayout<'tcx>,
57                           cls: &mut [Class],
58                           off: u64)
59                           -> Result<(), Memory> {
60         if off % layout.align(ccx).abi() != 0 {
61             if layout.size(ccx).bytes() > 0 {
62                 return Err(Memory);
63             }
64             return Ok(());
65         }
66
67         match *layout {
68             Layout::Scalar { value, .. } |
69             Layout::RawNullablePointer { value, .. } => {
70                 let reg = match value {
71                     layout::Int(_) |
72                     layout::Pointer => Class::Int,
73                     layout::F32 |
74                     layout::F64 => Class::Sse
75                 };
76                 unify(cls, off, reg);
77             }
78
79             Layout::CEnum { .. } => {
80                 unify(cls, off, Class::Int);
81             }
82
83             Layout::Vector { element, count } => {
84                 unify(cls, off, Class::Sse);
85
86                 // everything after the first one is the upper
87                 // half of a register.
88                 let eltsz = element.size(ccx).bytes();
89                 for i in 1..count {
90                     unify(cls, off + i * eltsz, Class::SseUp);
91                 }
92             }
93
94             Layout::Array { count, .. } => {
95                 if count > 0 {
96                     let elt = layout.field(ccx, 0);
97                     let eltsz = elt.size(ccx).bytes();
98                     for i in 0..count {
99                         classify(ccx, elt, cls, off + i * eltsz)?;
100                     }
101                 }
102             }
103
104             Layout::Univariant { ref variant, .. } => {
105                 for i in 0..layout.field_count() {
106                     let field_off = off + variant.offsets[i].bytes();
107                     classify(ccx, layout.field(ccx, i), cls, field_off)?;
108                 }
109             }
110
111             Layout::UntaggedUnion { .. } => {
112                 for i in 0..layout.field_count() {
113                     classify(ccx, layout.field(ccx, i), cls, off)?;
114                 }
115             }
116
117             Layout::FatPointer { .. } |
118             Layout::General { .. } |
119             Layout::StructWrappedNullablePointer { .. } => return Err(Memory)
120         }
121
122         Ok(())
123     }
124
125     let n = ((arg.layout.size(ccx).bytes() + 7) / 8) as usize;
126     if n > MAX_EIGHTBYTES {
127         return Err(Memory);
128     }
129
130     let mut cls = [Class::None; MAX_EIGHTBYTES];
131     classify(ccx, arg.layout, &mut cls, 0)?;
132     if n > 2 {
133         if cls[0] != Class::Sse {
134             return Err(Memory);
135         }
136         if cls[1..n].iter().any(|&c| c != Class::SseUp) {
137             return Err(Memory);
138         }
139     } else {
140         let mut i = 0;
141         while i < n {
142             if cls[i] == Class::SseUp {
143                 cls[i] = Class::Sse;
144             } else if cls[i] == Class::Sse {
145                 i += 1;
146                 while i != n && cls[i] == Class::SseUp { i += 1; }
147             } else {
148                 i += 1;
149             }
150         }
151     }
152
153     Ok(cls)
154 }
155
156 fn reg_component(cls: &[Class], i: &mut usize, size: u64) -> Option<Reg> {
157     if *i >= cls.len() {
158         return None;
159     }
160
161     match cls[*i] {
162         Class::None => None,
163         Class::Int => {
164             *i += 1;
165             Some(match size {
166                 1 => Reg::i8(),
167                 2 => Reg::i16(),
168                 3 |
169                 4 => Reg::i32(),
170                 _ => Reg::i64()
171             })
172         }
173         Class::Sse => {
174             let vec_len = 1 + cls[*i+1..].iter().take_while(|&&c| c == Class::SseUp).count();
175             *i += vec_len;
176             Some(if vec_len == 1 {
177                 match size {
178                     4 => Reg::f32(),
179                     _ => Reg::f64()
180                 }
181             } else {
182                 Reg {
183                     kind: RegKind::Vector,
184                     size: Size::from_bytes(vec_len as u64 * 8)
185                 }
186             })
187         }
188         c => bug!("reg_component: unhandled class {:?}", c)
189     }
190 }
191
192 fn cast_target(cls: &[Class], size: u64) -> CastTarget {
193     let mut i = 0;
194     let lo = reg_component(cls, &mut i, size).unwrap();
195     let offset = i as u64 * 8;
196     let target = if size <= offset {
197         CastTarget::from(lo)
198     } else {
199         let hi = reg_component(cls, &mut i, size - offset).unwrap();
200         CastTarget::Pair(lo, hi)
201     };
202     assert_eq!(reg_component(cls, &mut i, 0), None);
203     target
204 }
205
206 pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
207     let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9
208     let mut sse_regs = 8; // XMM0-7
209
210     let mut x86_64_ty = |arg: &mut ArgType<'tcx>, is_arg: bool| {
211         let cls = classify_arg(ccx, arg);
212
213         let mut needed_int = 0;
214         let mut needed_sse = 0;
215         let in_mem = match cls {
216             Err(Memory) => true,
217             Ok(ref cls) if is_arg => {
218                 for &c in cls {
219                     match c {
220                         Class::Int => needed_int += 1,
221                         Class::Sse => needed_sse += 1,
222                         _ => {}
223                     }
224                 }
225                 arg.layout.is_aggregate() &&
226                     (int_regs < needed_int || sse_regs < needed_sse)
227             }
228             Ok(_) => false
229         };
230
231         if in_mem {
232             // `sret` / `byval` parameter thus one less integer register available
233             int_regs -= 1;
234
235             arg.make_indirect(ccx);
236             if is_arg {
237                 arg.attrs.set(ArgAttribute::ByVal);
238             }
239         } else {
240             // split into sized chunks passed individually
241             int_regs -= needed_int;
242             sse_regs -= needed_sse;
243
244             if arg.layout.is_aggregate() {
245                 let size = arg.layout.size(ccx).bytes();
246                 arg.cast_to(ccx, cast_target(cls.as_ref().unwrap(), size))
247             } else {
248                 arg.extend_integer_width_to(32);
249             }
250         }
251     };
252
253     if !fty.ret.is_ignore() {
254         x86_64_ty(&mut fty.ret, false);
255     }
256
257     for arg in &mut fty.args {
258         if arg.is_ignore() { continue; }
259         x86_64_ty(arg, true);
260     }
261 }