]>
Commit | Line | Data |
---|---|---|
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 | #![allow(non_upper_case_globals)] | |
15 | use self::RegClass::*; | |
16 | ||
17 | use llvm::{Integer, Pointer, Float, Double}; | |
18 | use llvm::{Struct, Array, Attribute, Vector}; | |
19 | use abi::{self, ArgType, FnType}; | |
20 | use context::CrateContext; | |
21 | use type_::Type; | |
22 | ||
23 | #[derive(Clone, Copy, PartialEq)] | |
24 | enum RegClass { | |
25 | NoClass, | |
26 | Int, | |
27 | SSEFs, | |
28 | SSEFv, | |
29 | SSEDs, | |
30 | SSEDv, | |
31 | SSEInt(/* bitwidth */ u64), | |
32 | /// Data that can appear in the upper half of an SSE register. | |
33 | SSEUp, | |
34 | X87, | |
35 | X87Up, | |
36 | ComplexX87, | |
37 | Memory | |
38 | } | |
39 | ||
40 | trait TypeMethods { | |
41 | fn is_reg_ty(&self) -> bool; | |
42 | } | |
43 | ||
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 | } | |
51 | } | |
52 | ||
53 | impl RegClass { | |
54 | fn is_sse(&self) -> bool { | |
55 | match *self { | |
56 | SSEFs | SSEFv | SSEDs | SSEDv | SSEInt(_) => true, | |
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 | ||
67 | impl ClassList for [RegClass] { | |
68 | fn is_pass_byval(&self) -> bool { | |
69 | if self.is_empty() { return false; } | |
70 | ||
71 | let class = self[0]; | |
72 | class == Memory | |
73 | || class == X87 | |
74 | || class == ComplexX87 | |
75 | } | |
76 | ||
77 | fn is_ret_bysret(&self) -> bool { | |
78 | if self.is_empty() { return false; } | |
79 | ||
80 | self[0] == Memory | |
81 | } | |
82 | } | |
83 | ||
84 | fn classify_ty(ty: Type) -> Vec<RegClass> { | |
85 | fn align(off: usize, ty: Type) -> usize { | |
86 | let a = ty_align(ty); | |
87 | return (off + a - 1) / a * a; | |
88 | } | |
89 | ||
90 | fn ty_align(ty: Type) -> usize { | |
91 | abi::ty_align(ty, 8) | |
92 | } | |
93 | ||
94 | fn ty_size(ty: Type) -> usize { | |
95 | abi::ty_size(ty, 8) | |
96 | } | |
97 | ||
98 | fn all_mem(cls: &mut [RegClass]) { | |
99 | for elt in cls { | |
100 | *elt = Memory; | |
101 | } | |
102 | } | |
103 | ||
104 | fn unify(cls: &mut [RegClass], | |
105 | i: usize, | |
106 | newv: RegClass) { | |
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 | ||
132 | (..) => newv | |
133 | }; | |
134 | cls[i] = to_write; | |
135 | } | |
136 | ||
137 | fn classify_struct(tys: &[Type], | |
138 | cls: &mut [RegClass], | |
139 | i: usize, | |
140 | off: usize, | |
141 | packed: bool) { | |
142 | let mut field_off = off; | |
143 | for ty in tys { | |
144 | if !packed { | |
145 | field_off = align(field_off, *ty); | |
146 | } | |
147 | classify(*ty, cls, i, field_off); | |
148 | field_off += ty_size(*ty); | |
149 | } | |
150 | } | |
151 | ||
152 | fn classify(ty: Type, | |
153 | cls: &mut [RegClass], ix: usize, | |
154 | off: usize) { | |
155 | let t_align = ty_align(ty); | |
156 | let t_size = ty_size(ty); | |
157 | ||
158 | let misalign = off % t_align; | |
159 | if misalign != 0 { | |
160 | let mut i = off / 8; | |
161 | let e = (off + t_size + 7) / 8; | |
162 | while i < e { | |
163 | unify(cls, ix + i, Memory); | |
164 | i += 1; | |
165 | } | |
166 | return; | |
167 | } | |
168 | ||
169 | match ty.kind() { | |
170 | Integer | | |
171 | Pointer => { | |
172 | unify(cls, ix + off / 8, Int); | |
173 | } | |
174 | Float => { | |
175 | if off % 8 == 4 { | |
176 | unify(cls, ix + off / 8, SSEFv); | |
177 | } else { | |
178 | unify(cls, ix + off / 8, SSEFs); | |
179 | } | |
180 | } | |
181 | Double => { | |
182 | unify(cls, ix + off / 8, SSEDs); | |
183 | } | |
184 | Struct => { | |
185 | classify_struct(&ty.field_types(), cls, ix, off, ty.is_packed()); | |
186 | } | |
187 | Array => { | |
188 | let len = ty.array_length(); | |
189 | let elt = ty.element_type(); | |
190 | let eltsz = ty_size(elt); | |
191 | let mut i = 0; | |
192 | while i < len { | |
193 | classify(elt, cls, ix, off + i * eltsz); | |
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, | |
205 | _ => bug!("classify: unhandled vector element type") | |
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; | |
216 | } | |
217 | } | |
218 | _ => bug!("classify: unhandled type") | |
219 | } | |
220 | } | |
221 | ||
222 | fn fixup(ty: Type, cls: &mut [RegClass]) { | |
223 | let mut i = 0; | |
224 | let ty_kind = ty.kind(); | |
225 | let e = cls.len(); | |
226 | if cls.len() > 2 && (ty_kind == Struct || ty_kind == Array || ty_kind == Vector) { | |
227 | if cls[i].is_sse() { | |
228 | i += 1; | |
229 | while i < e { | |
230 | if cls[i] != SSEUp { | |
231 | all_mem(cls); | |
232 | return; | |
233 | } | |
234 | i += 1; | |
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; | |
256 | while i != e && cls[i] == SSEUp { i += 1; } | |
257 | } else if cls[i] == X87 { | |
258 | i += 1; | |
259 | while i != e && cls[i] == X87Up { i += 1; } | |
260 | } else { | |
261 | i += 1; | |
262 | } | |
263 | } | |
264 | } | |
265 | } | |
266 | ||
267 | let words = (ty_size(ty) + 7) / 8; | |
268 | let mut cls = vec![NoClass; words]; | |
269 | if words > 4 { | |
270 | all_mem(&mut cls); | |
271 | return cls; | |
272 | } | |
273 | classify(ty, &mut cls, 0, 0); | |
274 | fixup(ty, &mut cls); | |
275 | return cls; | |
276 | } | |
277 | ||
278 | fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type { | |
279 | fn llvec_len(cls: &[RegClass]) -> usize { | |
280 | let mut len = 1; | |
281 | for c in cls { | |
282 | if *c != SSEUp { | |
283 | break; | |
284 | } | |
285 | len += 1; | |
286 | } | |
287 | return len; | |
288 | } | |
289 | ||
290 | let mut tys = Vec::new(); | |
291 | let mut i = 0; | |
292 | let e = cls.len(); | |
293 | while i < e { | |
294 | match cls[i] { | |
295 | Int => { | |
296 | tys.push(Type::i64(ccx)); | |
297 | } | |
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 | } | |
307 | _ => bug!(), | |
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); | |
311 | tys.push(vec_ty); | |
312 | i += vec_len; | |
313 | continue; | |
314 | } | |
315 | SSEFs => { | |
316 | tys.push(Type::f32(ccx)); | |
317 | } | |
318 | SSEDs => { | |
319 | tys.push(Type::f64(ccx)); | |
320 | } | |
321 | _ => bug!("llregtype: unhandled class") | |
322 | } | |
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) | |
330 | } | |
331 | } | |
332 | ||
333 | pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) { | |
334 | fn x86_64_ty<F>(ccx: &CrateContext, | |
335 | arg: &mut ArgType, | |
336 | is_mem_cls: F, | |
337 | ind_attr: Option<Attribute>) | |
338 | where F: FnOnce(&[RegClass]) -> bool | |
339 | { | |
340 | if !arg.ty.is_reg_ty() { | |
341 | let cls = classify_ty(arg.ty); | |
342 | if is_mem_cls(&cls) { | |
343 | arg.make_indirect(ccx); | |
344 | if let Some(attr) = ind_attr { | |
345 | arg.attrs.set(attr); | |
346 | } | |
347 | } else { | |
348 | arg.cast = Some(llreg_ty(ccx, &cls)); | |
349 | } | |
350 | } else { | |
351 | arg.extend_integer_width_to(32); | |
352 | } | |
353 | } | |
354 | ||
355 | let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9 | |
356 | let mut sse_regs = 8; // XMM0-7 | |
357 | ||
358 | if !fty.ret.is_ignore() { | |
359 | x86_64_ty(ccx, &mut fty.ret, |cls| { | |
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 | } | |
367 | }, None); | |
368 | } | |
369 | ||
370 | for arg in &mut fty.args { | |
371 | if arg.is_ignore() { continue; } | |
372 | x86_64_ty(ccx, arg, |cls| { | |
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; | |
378 | if in_mem { | |
379 | // `byval` parameter thus one less integer register available | |
380 | int_regs -= 1; | |
381 | } else { | |
382 | // split into sized chunks passed individually | |
383 | int_regs -= needed_int; | |
384 | sse_regs -= needed_sse; | |
385 | } | |
386 | in_mem | |
387 | }, Some(Attribute::ByVal)); | |
388 | ||
389 | // An integer, pointer, double or float parameter | |
390 | // thus the above closure passed to `x86_64_ty` won't | |
391 | // get called. | |
392 | match arg.ty.kind() { | |
393 | Integer | Pointer => int_regs -= 1, | |
394 | Double | Float => sse_regs -= 1, | |
395 | _ => {} | |
396 | } | |
397 | } | |
398 | } |