]>
Commit | Line | Data |
---|---|---|
9cc50fc6 SL |
1 | // Copyright 2014-2016 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 | // FIXME: The PowerPC64 ABI needs to zero or sign extend function | |
12 | // call parameters, but compute_abi_info() is passed LLVM types | |
13 | // which have no sign information. | |
14 | // | |
15 | // Alignment of 128 bit types is not currently handled, this will | |
16 | // need to be fixed when PowerPC vector support is added. | |
17 | ||
18 | use llvm::{Integer, Pointer, Float, Double, Struct, Array, Attribute}; | |
19 | use trans::cabi::{FnType, ArgType}; | |
20 | use trans::context::CrateContext; | |
21 | use trans::type_::Type; | |
22 | ||
23 | use std::cmp; | |
24 | ||
25 | fn align_up_to(off: usize, a: usize) -> usize { | |
26 | return (off + a - 1) / a * a; | |
27 | } | |
28 | ||
29 | fn align(off: usize, ty: Type) -> usize { | |
30 | let a = ty_align(ty); | |
31 | return align_up_to(off, a); | |
32 | } | |
33 | ||
34 | fn ty_align(ty: Type) -> usize { | |
35 | match ty.kind() { | |
36 | Integer => ((ty.int_width() as usize) + 7) / 8, | |
37 | Pointer => 8, | |
38 | Float => 4, | |
39 | Double => 8, | |
40 | Struct => { | |
41 | if ty.is_packed() { | |
42 | 1 | |
43 | } else { | |
44 | let str_tys = ty.field_types(); | |
45 | str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t))) | |
46 | } | |
47 | } | |
48 | Array => { | |
49 | let elt = ty.element_type(); | |
50 | ty_align(elt) | |
51 | } | |
52 | _ => panic!("ty_align: unhandled type") | |
53 | } | |
54 | } | |
55 | ||
56 | fn ty_size(ty: Type) -> usize { | |
57 | match ty.kind() { | |
58 | Integer => ((ty.int_width() as usize) + 7) / 8, | |
59 | Pointer => 8, | |
60 | Float => 4, | |
61 | Double => 8, | |
62 | Struct => { | |
63 | if ty.is_packed() { | |
64 | let str_tys = ty.field_types(); | |
65 | str_tys.iter().fold(0, |s, t| s + ty_size(*t)) | |
66 | } else { | |
67 | let str_tys = ty.field_types(); | |
68 | let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t)); | |
69 | align(size, ty) | |
70 | } | |
71 | } | |
72 | Array => { | |
73 | let len = ty.array_length(); | |
74 | let elt = ty.element_type(); | |
75 | let eltsz = ty_size(elt); | |
76 | len * eltsz | |
77 | } | |
78 | _ => panic!("ty_size: unhandled type") | |
79 | } | |
80 | } | |
81 | ||
82 | fn is_homogenous_aggregate_ty(ty: Type) -> Option<(Type, u64)> { | |
83 | fn check_array(ty: Type) -> Option<(Type, u64)> { | |
84 | let len = ty.array_length() as u64; | |
85 | if len == 0 { | |
86 | return None | |
87 | } | |
88 | let elt = ty.element_type(); | |
89 | ||
90 | // if our element is an HFA/HVA, so are we; multiply members by our len | |
91 | is_homogenous_aggregate_ty(elt).map(|(base_ty, members)| (base_ty, len * members)) | |
92 | } | |
93 | ||
94 | fn check_struct(ty: Type) -> Option<(Type, u64)> { | |
95 | let str_tys = ty.field_types(); | |
96 | if str_tys.len() == 0 { | |
97 | return None | |
98 | } | |
99 | ||
100 | let mut prev_base_ty = None; | |
101 | let mut members = 0; | |
102 | for opt_homog_agg in str_tys.iter().map(|t| is_homogenous_aggregate_ty(*t)) { | |
103 | match (prev_base_ty, opt_homog_agg) { | |
104 | // field isn't itself an HFA, so we aren't either | |
105 | (_, None) => return None, | |
106 | ||
107 | // first field - store its type and number of members | |
108 | (None, Some((field_ty, field_members))) => { | |
109 | prev_base_ty = Some(field_ty); | |
110 | members = field_members; | |
111 | }, | |
112 | ||
113 | // 2nd or later field - give up if it's a different type; otherwise incr. members | |
114 | (Some(prev_ty), Some((field_ty, field_members))) => { | |
115 | if prev_ty != field_ty { | |
116 | return None; | |
117 | } | |
118 | members += field_members; | |
119 | } | |
120 | } | |
121 | } | |
122 | ||
123 | // Because of previous checks, we know prev_base_ty is Some(...) because | |
124 | // 1. str_tys has at least one element; and | |
125 | // 2. prev_base_ty was filled in (or we would've returned early) | |
126 | let (base_ty, members) = (prev_base_ty.unwrap(), members); | |
127 | ||
128 | // Ensure there is no padding. | |
129 | if ty_size(ty) == ty_size(base_ty) * (members as usize) { | |
130 | Some((base_ty, members)) | |
131 | } else { | |
132 | None | |
133 | } | |
134 | } | |
135 | ||
136 | let homog_agg = match ty.kind() { | |
137 | Float => Some((ty, 1)), | |
138 | Double => Some((ty, 1)), | |
139 | Array => check_array(ty), | |
140 | Struct => check_struct(ty), | |
141 | _ => None | |
142 | }; | |
143 | ||
144 | // Ensure we have at most eight uniquely addressable members | |
145 | homog_agg.and_then(|(base_ty, members)| { | |
146 | if members > 0 && members <= 8 { | |
147 | Some((base_ty, members)) | |
148 | } else { | |
149 | None | |
150 | } | |
151 | }) | |
152 | } | |
153 | ||
154 | fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType { | |
155 | if is_reg_ty(ty) { | |
156 | let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None }; | |
157 | return ArgType::direct(ty, None, None, attr); | |
158 | } | |
159 | ||
160 | // The PowerPC64 big endian ABI doesn't return aggregates in registers | |
7453a54e | 161 | if ccx.sess().target.target.target_endian == "big" { |
9cc50fc6 SL |
162 | return ArgType::indirect(ty, Some(Attribute::StructRet)) |
163 | } | |
164 | ||
165 | if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ty) { | |
166 | let llty = Type::array(&base_ty, members); | |
167 | return ArgType::direct(ty, Some(llty), None, None); | |
168 | } | |
169 | let size = ty_size(ty); | |
170 | if size <= 16 { | |
171 | let llty = if size <= 1 { | |
172 | Type::i8(ccx) | |
173 | } else if size <= 2 { | |
174 | Type::i16(ccx) | |
175 | } else if size <= 4 { | |
176 | Type::i32(ccx) | |
177 | } else if size <= 8 { | |
178 | Type::i64(ccx) | |
179 | } else { | |
180 | Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64) | |
181 | }; | |
182 | return ArgType::direct(ty, Some(llty), None, None); | |
183 | } | |
184 | ||
185 | ArgType::indirect(ty, Some(Attribute::StructRet)) | |
186 | } | |
187 | ||
188 | fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType { | |
189 | if is_reg_ty(ty) { | |
190 | let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None }; | |
191 | return ArgType::direct(ty, None, None, attr); | |
192 | } | |
193 | if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ty) { | |
194 | let llty = Type::array(&base_ty, members); | |
195 | return ArgType::direct(ty, Some(llty), None, None); | |
196 | } | |
197 | ||
198 | ArgType::direct( | |
199 | ty, | |
200 | Some(struct_ty(ccx, ty)), | |
201 | None, | |
202 | None | |
203 | ) | |
204 | } | |
205 | ||
206 | fn is_reg_ty(ty: Type) -> bool { | |
207 | match ty.kind() { | |
208 | Integer | |
209 | | Pointer | |
210 | | Float | |
211 | | Double => true, | |
212 | _ => false | |
213 | } | |
214 | } | |
215 | ||
216 | fn coerce_to_long(ccx: &CrateContext, size: usize) -> Vec<Type> { | |
217 | let long_ty = Type::i64(ccx); | |
218 | let mut args = Vec::new(); | |
219 | ||
220 | let mut n = size / 64; | |
221 | while n > 0 { | |
222 | args.push(long_ty); | |
223 | n -= 1; | |
224 | } | |
225 | ||
226 | let r = size % 64; | |
227 | if r > 0 { | |
228 | args.push(Type::ix(ccx, r as u64)); | |
229 | } | |
230 | ||
231 | args | |
232 | } | |
233 | ||
234 | fn struct_ty(ccx: &CrateContext, ty: Type) -> Type { | |
235 | let size = ty_size(ty) * 8; | |
236 | Type::struct_(ccx, &coerce_to_long(ccx, size), false) | |
237 | } | |
238 | ||
239 | pub fn compute_abi_info(ccx: &CrateContext, | |
240 | atys: &[Type], | |
241 | rty: Type, | |
242 | ret_def: bool) -> FnType { | |
243 | let ret_ty = if ret_def { | |
244 | classify_ret_ty(ccx, rty) | |
245 | } else { | |
246 | ArgType::direct(Type::void(ccx), None, None, None) | |
247 | }; | |
248 | ||
249 | let mut arg_tys = Vec::new(); | |
250 | for &aty in atys { | |
251 | let ty = classify_arg_ty(ccx, aty); | |
252 | arg_tys.push(ty); | |
253 | }; | |
254 | ||
255 | return FnType { | |
256 | arg_tys: arg_tys, | |
257 | ret_ty: ret_ty, | |
258 | }; | |
259 | } |