]>
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 | ||
1a4d82fc JJ |
14 | #![allow(non_upper_case_globals)] |
15 | use self::RegClass::*; | |
223e47cc | 16 | |
1a4d82fc | 17 | use llvm::{Integer, Pointer, Float, Double}; |
476ff2be SL |
18 | use llvm::{Struct, Array, Vector}; |
19 | use abi::{self, ArgType, ArgAttribute, FnType}; | |
54a0048b SL |
20 | use context::CrateContext; |
21 | use type_::Type; | |
970d7e83 | 22 | |
1a4d82fc | 23 | #[derive(Clone, Copy, PartialEq)] |
970d7e83 LB |
24 | enum RegClass { |
25 | NoClass, | |
26 | Int, | |
27 | SSEFs, | |
28 | SSEFv, | |
29 | SSEDs, | |
30 | SSEDv, | |
85aaf69f SL |
31 | SSEInt(/* bitwidth */ u64), |
32 | /// Data that can appear in the upper half of an SSE register. | |
970d7e83 LB |
33 | SSEUp, |
34 | X87, | |
35 | X87Up, | |
36 | ComplexX87, | |
37 | Memory | |
223e47cc LB |
38 | } |
39 | ||
970d7e83 LB |
40 | trait TypeMethods { |
41 | fn is_reg_ty(&self) -> bool; | |
223e47cc LB |
42 | } |
43 | ||
970d7e83 LB |
44 | impl TypeMethods for Type { |
45 | fn is_reg_ty(&self) -> bool { | |
46 | match self.kind() { | |
47 | Integer | Pointer | Float | Double => true, | |
48 | _ => false | |
49 | } | |
50 | } | |
223e47cc LB |
51 | } |
52 | ||
970d7e83 LB |
53 | impl RegClass { |
54 | fn is_sse(&self) -> bool { | |
55 | match *self { | |
85aaf69f | 56 | SSEFs | SSEFv | SSEDs | SSEDv | SSEInt(_) => true, |
970d7e83 LB |
57 | _ => false |
58 | } | |
59 | } | |
60 | } | |
61 | ||
62 | trait ClassList { | |
63 | fn is_pass_byval(&self) -> bool; | |
64 | fn is_ret_bysret(&self) -> bool; | |
65 | } | |
66 | ||
1a4d82fc | 67 | impl ClassList for [RegClass] { |
970d7e83 | 68 | fn is_pass_byval(&self) -> bool { |
9346a6ac | 69 | if self.is_empty() { return false; } |
970d7e83 LB |
70 | |
71 | let class = self[0]; | |
72 | class == Memory | |
73 | || class == X87 | |
74 | || class == ComplexX87 | |
75 | } | |
76 | ||
77 | fn is_ret_bysret(&self) -> bool { | |
9346a6ac | 78 | if self.is_empty() { return false; } |
970d7e83 LB |
79 | |
80 | self[0] == Memory | |
81 | } | |
82 | } | |
83 | ||
1a4d82fc | 84 | fn classify_ty(ty: Type) -> Vec<RegClass> { |
c34b1796 | 85 | fn align(off: usize, ty: Type) -> usize { |
223e47cc | 86 | let a = ty_align(ty); |
85aaf69f | 87 | return (off + a - 1) / a * a; |
223e47cc LB |
88 | } |
89 | ||
c34b1796 | 90 | fn ty_align(ty: Type) -> usize { |
c30ab7b3 | 91 | abi::ty_align(ty, 8) |
223e47cc LB |
92 | } |
93 | ||
c34b1796 | 94 | fn ty_size(ty: Type) -> usize { |
c30ab7b3 | 95 | abi::ty_size(ty, 8) |
223e47cc LB |
96 | } |
97 | ||
970d7e83 | 98 | fn all_mem(cls: &mut [RegClass]) { |
85aaf69f | 99 | for elt in cls { |
1a4d82fc | 100 | *elt = Memory; |
223e47cc LB |
101 | } |
102 | } | |
103 | ||
970d7e83 | 104 | fn unify(cls: &mut [RegClass], |
c34b1796 | 105 | i: usize, |
970d7e83 | 106 | newv: RegClass) { |
85aaf69f SL |
107 | if cls[i] == newv { return } |
108 | ||
109 | let to_write = match (cls[i], newv) { | |
110 | (NoClass, _) => newv, | |
111 | (_, NoClass) => return, | |
112 | ||
113 | (Memory, _) | | |
114 | (_, Memory) => Memory, | |
115 | ||
116 | (Int, _) | | |
117 | (_, Int) => Int, | |
118 | ||
119 | (X87, _) | | |
120 | (X87Up, _) | | |
121 | (ComplexX87, _) | | |
122 | (_, X87) | | |
123 | (_, X87Up) | | |
124 | (_, ComplexX87) => Memory, | |
125 | ||
126 | (SSEFv, SSEUp) | | |
127 | (SSEFs, SSEUp) | | |
128 | (SSEDv, SSEUp) | | |
129 | (SSEDs, SSEUp) | | |
130 | (SSEInt(_), SSEUp) => return, | |
131 | ||
9e0c209e | 132 | (..) => newv |
85aaf69f SL |
133 | }; |
134 | cls[i] = to_write; | |
223e47cc LB |
135 | } |
136 | ||
970d7e83 | 137 | fn classify_struct(tys: &[Type], |
1a4d82fc | 138 | cls: &mut [RegClass], |
c34b1796 AL |
139 | i: usize, |
140 | off: usize, | |
1a4d82fc | 141 | packed: bool) { |
223e47cc | 142 | let mut field_off = off; |
85aaf69f | 143 | for ty in tys { |
1a4d82fc JJ |
144 | if !packed { |
145 | field_off = align(field_off, *ty); | |
146 | } | |
223e47cc LB |
147 | classify(*ty, cls, i, field_off); |
148 | field_off += ty_size(*ty); | |
149 | } | |
150 | } | |
151 | ||
970d7e83 | 152 | fn classify(ty: Type, |
c34b1796 AL |
153 | cls: &mut [RegClass], ix: usize, |
154 | off: usize) { | |
970d7e83 LB |
155 | let t_align = ty_align(ty); |
156 | let t_size = ty_size(ty); | |
157 | ||
158 | let misalign = off % t_align; | |
85aaf69f SL |
159 | if misalign != 0 { |
160 | let mut i = off / 8; | |
161 | let e = (off + t_size + 7) / 8; | |
970d7e83 LB |
162 | while i < e { |
163 | unify(cls, ix + i, Memory); | |
85aaf69f | 164 | i += 1; |
223e47cc | 165 | } |
970d7e83 LB |
166 | return; |
167 | } | |
223e47cc | 168 | |
970d7e83 LB |
169 | match ty.kind() { |
170 | Integer | | |
171 | Pointer => { | |
85aaf69f | 172 | unify(cls, ix + off / 8, Int); |
970d7e83 LB |
173 | } |
174 | Float => { | |
85aaf69f SL |
175 | if off % 8 == 4 { |
176 | unify(cls, ix + off / 8, SSEFv); | |
970d7e83 | 177 | } else { |
85aaf69f | 178 | unify(cls, ix + off / 8, SSEFs); |
223e47cc | 179 | } |
970d7e83 LB |
180 | } |
181 | Double => { | |
85aaf69f | 182 | unify(cls, ix + off / 8, SSEDs); |
970d7e83 LB |
183 | } |
184 | Struct => { | |
85aaf69f | 185 | classify_struct(&ty.field_types(), cls, ix, off, ty.is_packed()); |
970d7e83 LB |
186 | } |
187 | Array => { | |
188 | let len = ty.array_length(); | |
189 | let elt = ty.element_type(); | |
190 | let eltsz = ty_size(elt); | |
85aaf69f | 191 | let mut i = 0; |
970d7e83 LB |
192 | while i < len { |
193 | classify(elt, cls, ix, off + i * eltsz); | |
85aaf69f SL |
194 | i += 1; |
195 | } | |
196 | } | |
197 | Vector => { | |
198 | let len = ty.vector_length(); | |
199 | let elt = ty.element_type(); | |
200 | let eltsz = ty_size(elt); | |
201 | let mut reg = match elt.kind() { | |
202 | Integer => SSEInt(elt.int_width()), | |
203 | Float => SSEFv, | |
204 | Double => SSEDv, | |
54a0048b | 205 | _ => bug!("classify: unhandled vector element type") |
85aaf69f SL |
206 | }; |
207 | ||
208 | let mut i = 0; | |
209 | while i < len { | |
210 | unify(cls, ix + (off + i * eltsz) / 8, reg); | |
211 | ||
212 | // everything after the first one is the upper | |
213 | // half of a register. | |
214 | reg = SSEUp; | |
215 | i += 1; | |
223e47cc | 216 | } |
223e47cc | 217 | } |
54a0048b | 218 | _ => bug!("classify: unhandled type") |
223e47cc LB |
219 | } |
220 | } | |
221 | ||
970d7e83 | 222 | fn fixup(ty: Type, cls: &mut [RegClass]) { |
85aaf69f | 223 | let mut i = 0; |
970d7e83 LB |
224 | let ty_kind = ty.kind(); |
225 | let e = cls.len(); | |
85aaf69f | 226 | if cls.len() > 2 && (ty_kind == Struct || ty_kind == Array || ty_kind == Vector) { |
970d7e83 | 227 | if cls[i].is_sse() { |
85aaf69f | 228 | i += 1; |
223e47cc | 229 | while i < e { |
970d7e83 | 230 | if cls[i] != SSEUp { |
223e47cc LB |
231 | all_mem(cls); |
232 | return; | |
233 | } | |
85aaf69f | 234 | i += 1; |
970d7e83 LB |
235 | } |
236 | } else { | |
237 | all_mem(cls); | |
238 | return | |
239 | } | |
240 | } else { | |
241 | while i < e { | |
242 | if cls[i] == Memory { | |
243 | all_mem(cls); | |
244 | return; | |
245 | } | |
246 | if cls[i] == X87Up { | |
247 | // for darwin | |
248 | // cls[i] = SSEDs; | |
249 | all_mem(cls); | |
250 | return; | |
251 | } | |
252 | if cls[i] == SSEUp { | |
253 | cls[i] = SSEDv; | |
254 | } else if cls[i].is_sse() { | |
255 | i += 1; | |
85aaf69f | 256 | while i != e && cls[i] == SSEUp { i += 1; } |
970d7e83 LB |
257 | } else if cls[i] == X87 { |
258 | i += 1; | |
85aaf69f | 259 | while i != e && cls[i] == X87Up { i += 1; } |
970d7e83 LB |
260 | } else { |
261 | i += 1; | |
223e47cc LB |
262 | } |
263 | } | |
264 | } | |
265 | } | |
266 | ||
267 | let words = (ty_size(ty) + 7) / 8; | |
c1a9b12d | 268 | let mut cls = vec![NoClass; words]; |
223e47cc | 269 | if words > 4 { |
85aaf69f | 270 | all_mem(&mut cls); |
223e47cc LB |
271 | return cls; |
272 | } | |
85aaf69f SL |
273 | classify(ty, &mut cls, 0, 0); |
274 | fixup(ty, &mut cls); | |
223e47cc LB |
275 | return cls; |
276 | } | |
277 | ||
1a4d82fc | 278 | fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type { |
c34b1796 | 279 | fn llvec_len(cls: &[RegClass]) -> usize { |
85aaf69f SL |
280 | let mut len = 1; |
281 | for c in cls { | |
970d7e83 | 282 | if *c != SSEUp { |
223e47cc LB |
283 | break; |
284 | } | |
85aaf69f | 285 | len += 1; |
223e47cc LB |
286 | } |
287 | return len; | |
288 | } | |
289 | ||
1a4d82fc | 290 | let mut tys = Vec::new(); |
85aaf69f | 291 | let mut i = 0; |
970d7e83 LB |
292 | let e = cls.len(); |
293 | while i < e { | |
294 | match cls[i] { | |
295 | Int => { | |
1a4d82fc | 296 | tys.push(Type::i64(ccx)); |
970d7e83 | 297 | } |
85aaf69f SL |
298 | SSEFv | SSEDv | SSEInt(_) => { |
299 | let (elts_per_word, elt_ty) = match cls[i] { | |
300 | SSEFv => (2, Type::f32(ccx)), | |
301 | SSEDv => (1, Type::f64(ccx)), | |
302 | SSEInt(bits) => { | |
303 | assert!(bits == 8 || bits == 16 || bits == 32 || bits == 64, | |
304 | "llreg_ty: unsupported SSEInt width {}", bits); | |
305 | (64 / bits, Type::ix(ccx, bits)) | |
306 | } | |
54a0048b | 307 | _ => bug!(), |
85aaf69f SL |
308 | }; |
309 | let vec_len = llvec_len(&cls[i + 1..]); | |
310 | let vec_ty = Type::vector(&elt_ty, vec_len as u64 * elts_per_word); | |
970d7e83 LB |
311 | tys.push(vec_ty); |
312 | i += vec_len; | |
1a4d82fc | 313 | continue; |
970d7e83 LB |
314 | } |
315 | SSEFs => { | |
1a4d82fc | 316 | tys.push(Type::f32(ccx)); |
970d7e83 LB |
317 | } |
318 | SSEDs => { | |
1a4d82fc | 319 | tys.push(Type::f64(ccx)); |
223e47cc | 320 | } |
54a0048b | 321 | _ => bug!("llregtype: unhandled class") |
223e47cc | 322 | } |
85aaf69f SL |
323 | i += 1; |
324 | } | |
325 | if tys.len() == 1 && tys[0].kind() == Vector { | |
326 | // if the type contains only a vector, pass it as that vector. | |
327 | tys[0] | |
328 | } else { | |
329 | Type::struct_(ccx, &tys, false) | |
223e47cc LB |
330 | } |
331 | } | |
332 | ||
54a0048b | 333 | pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) { |
1a4d82fc | 334 | fn x86_64_ty<F>(ccx: &CrateContext, |
54a0048b | 335 | arg: &mut ArgType, |
1a4d82fc | 336 | is_mem_cls: F, |
476ff2be | 337 | ind_attr: Option<ArgAttribute>) |
54a0048b | 338 | where F: FnOnce(&[RegClass]) -> bool |
1a4d82fc | 339 | { |
54a0048b SL |
340 | if !arg.ty.is_reg_ty() { |
341 | let cls = classify_ty(arg.ty); | |
85aaf69f | 342 | if is_mem_cls(&cls) { |
54a0048b SL |
343 | arg.make_indirect(ccx); |
344 | if let Some(attr) = ind_attr { | |
345 | arg.attrs.set(attr); | |
346 | } | |
223e47cc | 347 | } else { |
54a0048b | 348 | arg.cast = Some(llreg_ty(ccx, &cls)); |
223e47cc | 349 | } |
970d7e83 | 350 | } else { |
54a0048b | 351 | arg.extend_integer_width_to(32); |
1a4d82fc | 352 | } |
223e47cc LB |
353 | } |
354 | ||
b039eaaf SL |
355 | let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9 |
356 | let mut sse_regs = 8; // XMM0-7 | |
1a4d82fc | 357 | |
54a0048b SL |
358 | if !fty.ret.is_ignore() { |
359 | x86_64_ty(ccx, &mut fty.ret, |cls| { | |
b039eaaf SL |
360 | if cls.is_ret_bysret() { |
361 | // `sret` parameter thus one less register available | |
362 | int_regs -= 1; | |
363 | true | |
364 | } else { | |
365 | false | |
366 | } | |
54a0048b SL |
367 | }, None); |
368 | } | |
1a4d82fc | 369 | |
54a0048b SL |
370 | for arg in &mut fty.args { |
371 | if arg.is_ignore() { continue; } | |
372 | x86_64_ty(ccx, arg, |cls| { | |
b039eaaf SL |
373 | let needed_int = cls.iter().filter(|&&c| c == Int).count() as isize; |
374 | let needed_sse = cls.iter().filter(|c| c.is_sse()).count() as isize; | |
375 | let in_mem = cls.is_pass_byval() || | |
376 | int_regs < needed_int || | |
377 | sse_regs < needed_sse; | |
8bb4bdeb | 378 | if !in_mem { |
b039eaaf SL |
379 | // split into sized chunks passed individually |
380 | int_regs -= needed_int; | |
381 | sse_regs -= needed_sse; | |
382 | } | |
383 | in_mem | |
476ff2be | 384 | }, Some(ArgAttribute::ByVal)); |
b039eaaf SL |
385 | |
386 | // An integer, pointer, double or float parameter | |
387 | // thus the above closure passed to `x86_64_ty` won't | |
388 | // get called. | |
54a0048b SL |
389 | match arg.ty.kind() { |
390 | Integer | Pointer => int_regs -= 1, | |
391 | Double | Float => sse_regs -= 1, | |
392 | _ => {} | |
b039eaaf SL |
393 | } |
394 | } | |
223e47cc | 395 | } |