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