]> git.proxmox.com Git - rustc.git/blob - src/librustc_target/abi/call/x86_64.rs
New upstream version 1.28.0~beta.14+dfsg1
[rustc.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_abi_aligned(layout.align) {
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() {
203 if int_regs < needed_int || sse_regs < needed_sse {
204 cls_or_mem = Err(Memory);
205 }
206 }
207 }
208 }
209
210 match cls_or_mem {
211 Err(Memory) => {
212 if is_arg {
213 arg.make_indirect_byval();
214 } else {
215 // `sret` parameter thus one less integer register available
216 arg.make_indirect();
217 int_regs -= 1;
218 }
219 }
220 Ok(ref cls) => {
221 // split into sized chunks passed individually
222 int_regs -= needed_int;
223 sse_regs -= needed_sse;
224
225 if arg.layout.is_aggregate() {
226 let size = arg.layout.size;
227 arg.cast_to(cast_target(cls, size))
228 } else {
229 arg.extend_integer_width_to(32);
230 }
231 }
232 }
233 };
234
235 if !fty.ret.is_ignore() {
236 x86_64_ty(&mut fty.ret, false);
237 }
238
239 for arg in &mut fty.args {
240 if arg.is_ignore() { continue; }
241 x86_64_ty(arg, true);
242 }
243 }