]>
Commit | Line | Data |
---|---|---|
970d7e83 | 1 | // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT |
223e47cc LB |
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 | ||
ff7c6d11 | 14 | use abi::{ArgType, CastTarget, FnType, LayoutExt, Reg, RegKind}; |
2c00a5a8 | 15 | use context::CodegenCx; |
223e47cc | 16 | |
ff7c6d11 | 17 | use rustc::ty::layout::{self, TyLayout, Size}; |
970d7e83 | 18 | |
2c00a5a8 XL |
19 | /// Classification of "eightbyte" components. |
20 | // NB: the order of the variants is from general to specific, | |
21 | // such that `unify(a, b)` is the "smaller" of `a` and `b`. | |
22 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] | |
cc61c64b | 23 | enum Class { |
cc61c64b XL |
24 | Int, |
25 | Sse, | |
26 | SseUp | |
970d7e83 LB |
27 | } |
28 | ||
cc61c64b XL |
29 | #[derive(Clone, Copy, Debug)] |
30 | struct Memory; | |
970d7e83 | 31 | |
abe05a73 XL |
32 | // Currently supported vector size (AVX-512). |
33 | const LARGEST_VECTOR_SIZE: usize = 512; | |
cc61c64b | 34 | const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64; |
223e47cc | 35 | |
2c00a5a8 XL |
36 | fn classify_arg<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, arg: &ArgType<'tcx>) |
37 | -> Result<[Option<Class>; MAX_EIGHTBYTES], Memory> { | |
38 | fn classify<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, | |
cc61c64b | 39 | layout: TyLayout<'tcx>, |
2c00a5a8 | 40 | cls: &mut [Option<Class>], |
ff7c6d11 | 41 | off: Size) |
cc61c64b | 42 | -> Result<(), Memory> { |
ff7c6d11 XL |
43 | if !off.is_abi_aligned(layout.align) { |
44 | if !layout.is_zst() { | |
cc61c64b | 45 | return Err(Memory); |
1a4d82fc | 46 | } |
cc61c64b | 47 | return Ok(()); |
223e47cc | 48 | } |
223e47cc | 49 | |
2c00a5a8 XL |
50 | let mut c = match layout.abi { |
51 | layout::Abi::Uninhabited => return Ok(()), | |
ff7c6d11 XL |
52 | |
53 | layout::Abi::Scalar(ref scalar) => { | |
2c00a5a8 | 54 | match scalar.value { |
ff7c6d11 | 55 | layout::Int(..) | |
cc61c64b XL |
56 | layout::Pointer => Class::Int, |
57 | layout::F32 | | |
58 | layout::F64 => Class::Sse | |
223e47cc | 59 | } |
970d7e83 | 60 | } |
cc61c64b | 61 | |
2c00a5a8 XL |
62 | layout::Abi::Vector { .. } => Class::Sse, |
63 | ||
ff7c6d11 XL |
64 | layout::Abi::ScalarPair(..) | |
65 | layout::Abi::Aggregate { .. } => { | |
66 | match layout.variants { | |
67 | layout::Variants::Single { .. } => { | |
68 | for i in 0..layout.fields.count() { | |
69 | let field_off = off + layout.fields.offset(i); | |
2c00a5a8 | 70 | classify(cx, layout.field(cx, i), cls, field_off)?; |
ff7c6d11 | 71 | } |
2c00a5a8 | 72 | return Ok(()); |
cc61c64b | 73 | } |
ff7c6d11 XL |
74 | layout::Variants::Tagged { .. } | |
75 | layout::Variants::NicheFilling { .. } => return Err(Memory), | |
85aaf69f SL |
76 | } |
77 | } | |
85aaf69f | 78 | |
2c00a5a8 XL |
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 | } | |
223e47cc | 92 | } |
cc61c64b XL |
93 | |
94 | Ok(()) | |
223e47cc LB |
95 | } |
96 | ||
ff7c6d11 | 97 | let n = ((arg.layout.size.bytes() + 7) / 8) as usize; |
cc61c64b XL |
98 | if n > MAX_EIGHTBYTES { |
99 | return Err(Memory); | |
100 | } | |
101 | ||
2c00a5a8 XL |
102 | let mut cls = [None; MAX_EIGHTBYTES]; |
103 | classify(cx, arg.layout, &mut cls, Size::from_bytes(0))?; | |
cc61c64b | 104 | if n > 2 { |
2c00a5a8 | 105 | if cls[0] != Some(Class::Sse) { |
cc61c64b XL |
106 | return Err(Memory); |
107 | } | |
2c00a5a8 | 108 | if cls[1..n].iter().any(|&c| c != Some(Class::SseUp)) { |
cc61c64b XL |
109 | return Err(Memory); |
110 | } | |
111 | } else { | |
85aaf69f | 112 | let mut i = 0; |
cc61c64b | 113 | while i < n { |
2c00a5a8 XL |
114 | if cls[i] == Some(Class::SseUp) { |
115 | cls[i] = Some(Class::Sse); | |
116 | } else if cls[i] == Some(Class::Sse) { | |
85aaf69f | 117 | i += 1; |
2c00a5a8 | 118 | while i != n && cls[i] == Some(Class::SseUp) { i += 1; } |
970d7e83 | 119 | } else { |
cc61c64b | 120 | i += 1; |
223e47cc LB |
121 | } |
122 | } | |
123 | } | |
124 | ||
cc61c64b | 125 | Ok(cls) |
223e47cc LB |
126 | } |
127 | ||
2c00a5a8 | 128 | fn reg_component(cls: &[Option<Class>], i: &mut usize, size: Size) -> Option<Reg> { |
cc61c64b XL |
129 | if *i >= cls.len() { |
130 | return None; | |
223e47cc LB |
131 | } |
132 | ||
cc61c64b | 133 | match cls[*i] { |
2c00a5a8 XL |
134 | None => None, |
135 | Some(Class::Int) => { | |
cc61c64b | 136 | *i += 1; |
2c00a5a8 XL |
137 | Some(if size.bytes() < 8 { |
138 | Reg { | |
139 | kind: RegKind::Integer, | |
140 | size | |
141 | } | |
142 | } else { | |
143 | Reg::i64() | |
cc61c64b | 144 | }) |
223e47cc | 145 | } |
2c00a5a8 XL |
146 | Some(Class::Sse) => { |
147 | let vec_len = 1 + cls[*i+1..].iter() | |
148 | .take_while(|&&c| c == Some(Class::SseUp)) | |
149 | .count(); | |
cc61c64b XL |
150 | *i += vec_len; |
151 | Some(if vec_len == 1 { | |
ff7c6d11 | 152 | match size.bytes() { |
cc61c64b XL |
153 | 4 => Reg::f32(), |
154 | _ => Reg::f64() | |
54a0048b | 155 | } |
223e47cc | 156 | } else { |
cc61c64b XL |
157 | Reg { |
158 | kind: RegKind::Vector, | |
ff7c6d11 | 159 | size: Size::from_bytes(8) * (vec_len as u64) |
cc61c64b XL |
160 | } |
161 | }) | |
1a4d82fc | 162 | } |
2c00a5a8 | 163 | Some(c) => bug!("reg_component: unhandled class {:?}", c) |
223e47cc | 164 | } |
cc61c64b XL |
165 | } |
166 | ||
2c00a5a8 | 167 | fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget { |
cc61c64b XL |
168 | let mut i = 0; |
169 | let lo = reg_component(cls, &mut i, size).unwrap(); | |
ff7c6d11 | 170 | let offset = Size::from_bytes(8) * (i as u64); |
2c00a5a8 XL |
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 | } | |
ff7c6d11 | 177 | assert_eq!(reg_component(cls, &mut i, Size::from_bytes(0)), None); |
cc61c64b XL |
178 | target |
179 | } | |
223e47cc | 180 | |
2c00a5a8 | 181 | pub fn compute_abi_info<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, fty: &mut FnType<'tcx>) { |
b039eaaf SL |
182 | let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9 |
183 | let mut sse_regs = 8; // XMM0-7 | |
1a4d82fc | 184 | |
cc61c64b | 185 | let mut x86_64_ty = |arg: &mut ArgType<'tcx>, is_arg: bool| { |
2c00a5a8 | 186 | let mut cls_or_mem = classify_arg(cx, arg); |
cc61c64b XL |
187 | |
188 | let mut needed_int = 0; | |
189 | let mut needed_sse = 0; | |
2c00a5a8 XL |
190 | if is_arg { |
191 | if let Ok(cls) = cls_or_mem { | |
192 | for &c in &cls { | |
cc61c64b | 193 | match c { |
2c00a5a8 XL |
194 | Some(Class::Int) => needed_int += 1, |
195 | Some(Class::Sse) => needed_sse += 1, | |
cc61c64b XL |
196 | _ => {} |
197 | } | |
198 | } | |
2c00a5a8 XL |
199 | if arg.layout.is_aggregate() { |
200 | if int_regs < needed_int || sse_regs < needed_sse { | |
201 | cls_or_mem = Err(Memory); | |
202 | } | |
203 | } | |
cc61c64b | 204 | } |
2c00a5a8 | 205 | } |
cc61c64b | 206 | |
2c00a5a8 XL |
207 | match cls_or_mem { |
208 | Err(Memory) => { | |
209 | if is_arg { | |
210 | arg.make_indirect_byval(); | |
211 | } else { | |
212 | // `sret` parameter thus one less integer register available | |
213 | arg.make_indirect(); | |
214 | int_regs -= 1; | |
215 | } | |
cc61c64b | 216 | } |
2c00a5a8 XL |
217 | Ok(ref cls) => { |
218 | // split into sized chunks passed individually | |
219 | int_regs -= needed_int; | |
220 | sse_regs -= needed_sse; | |
221 | ||
222 | if arg.layout.is_aggregate() { | |
223 | let size = arg.layout.size; | |
224 | arg.cast_to(cast_target(cls, size)) | |
225 | } else { | |
226 | arg.extend_integer_width_to(32); | |
227 | } | |
b039eaaf | 228 | } |
cc61c64b XL |
229 | } |
230 | }; | |
231 | ||
232 | if !fty.ret.is_ignore() { | |
233 | x86_64_ty(&mut fty.ret, false); | |
54a0048b | 234 | } |
1a4d82fc | 235 | |
54a0048b SL |
236 | for arg in &mut fty.args { |
237 | if arg.is_ignore() { continue; } | |
cc61c64b | 238 | x86_64_ty(arg, true); |
b039eaaf | 239 | } |
223e47cc | 240 | } |