]>
Commit | Line | Data |
---|---|---|
32a655c1 SL |
1 | // FIXME: This needs an audit for correctness and completeness. |
2 | ||
a2a8927a | 3 | use crate::abi::call::{ |
5e7ed085 | 4 | ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Reg, Uniform, |
a2a8927a | 5 | }; |
5e7ed085 FG |
6 | use crate::abi::{self, HasDataLayout, Scalar, Size, TyAbiInterface, TyAndLayout}; |
7 | ||
8 | #[derive(Clone, Debug)] | |
9 | pub struct Sdata { | |
10 | pub prefix: [Option<Reg>; 8], | |
11 | pub prefix_index: usize, | |
12 | pub last_offset: Size, | |
13 | pub has_float: bool, | |
14 | pub arg_attribute: ArgAttribute, | |
15 | } | |
32a655c1 | 16 | |
5e7ed085 | 17 | fn arg_scalar<C>(cx: &C, scalar: &Scalar, offset: Size, mut data: Sdata) -> Sdata |
dfeec247 | 18 | where |
94222f64 | 19 | C: HasDataLayout, |
83c7162d | 20 | { |
5e7ed085 FG |
21 | let dl = cx.data_layout(); |
22 | ||
04454e1e | 23 | if !scalar.primitive().is_float() { |
5e7ed085 FG |
24 | return data; |
25 | } | |
26 | ||
27 | data.has_float = true; | |
28 | ||
29 | if !data.last_offset.is_aligned(dl.f64_align.abi) && data.last_offset < offset { | |
30 | if data.prefix_index == data.prefix.len() { | |
31 | return data; | |
32a655c1 | 32 | } |
5e7ed085 FG |
33 | data.prefix[data.prefix_index] = Some(Reg::i32()); |
34 | data.prefix_index += 1; | |
35 | data.last_offset = data.last_offset + Reg::i32().size; | |
36 | } | |
32a655c1 | 37 | |
5e7ed085 FG |
38 | for _ in 0..((offset - data.last_offset).bits() / 64) |
39 | .min((data.prefix.len() - data.prefix_index) as u64) | |
40 | { | |
41 | data.prefix[data.prefix_index] = Some(Reg::i64()); | |
42 | data.prefix_index += 1; | |
43 | data.last_offset = data.last_offset + Reg::i64().size; | |
44 | } | |
32a655c1 | 45 | |
5e7ed085 FG |
46 | if data.last_offset < offset { |
47 | if data.prefix_index == data.prefix.len() { | |
48 | return data; | |
49 | } | |
50 | data.prefix[data.prefix_index] = Some(Reg::i32()); | |
51 | data.prefix_index += 1; | |
52 | data.last_offset = data.last_offset + Reg::i32().size; | |
53 | } | |
54 | ||
55 | if data.prefix_index == data.prefix.len() { | |
56 | return data; | |
57 | } | |
58 | ||
04454e1e | 59 | if scalar.primitive() == abi::F32 { |
5e7ed085 FG |
60 | data.arg_attribute = ArgAttribute::InReg; |
61 | data.prefix[data.prefix_index] = Some(Reg::f32()); | |
62 | data.last_offset = offset + Reg::f32().size; | |
63 | } else { | |
64 | data.prefix[data.prefix_index] = Some(Reg::f64()); | |
65 | data.last_offset = offset + Reg::f64().size; | |
66 | } | |
67 | data.prefix_index += 1; | |
68 | return data; | |
32a655c1 SL |
69 | } |
70 | ||
5e7ed085 FG |
71 | fn arg_scalar_pair<C>( |
72 | cx: &C, | |
73 | scalar1: &Scalar, | |
74 | scalar2: &Scalar, | |
75 | mut offset: Size, | |
76 | mut data: Sdata, | |
77 | ) -> Sdata | |
dfeec247 | 78 | where |
94222f64 | 79 | C: HasDataLayout, |
83c7162d | 80 | { |
6522a427 | 81 | data = arg_scalar(cx, scalar1, offset, data); |
04454e1e FG |
82 | match (scalar1.primitive(), scalar2.primitive()) { |
83 | (abi::F32, _) => offset += Reg::f32().size, | |
84 | (_, abi::F64) => offset += Reg::f64().size, | |
85 | (abi::Int(i, _signed), _) => offset += i.size(), | |
86 | (abi::Pointer, _) => offset += Reg::i64().size, | |
87 | _ => {} | |
32a655c1 SL |
88 | } |
89 | ||
6522a427 EL |
90 | if (offset.bytes() % 4) != 0 && scalar2.primitive().is_float() { |
91 | offset += Size::from_bytes(4 - (offset.bytes() % 4)); | |
5e7ed085 | 92 | } |
6522a427 | 93 | data = arg_scalar(cx, scalar2, offset, data); |
5e7ed085 FG |
94 | return data; |
95 | } | |
96 | ||
97 | fn parse_structure<'a, Ty, C>( | |
98 | cx: &C, | |
99 | layout: TyAndLayout<'a, Ty>, | |
100 | mut data: Sdata, | |
101 | mut offset: Size, | |
102 | ) -> Sdata | |
103 | where | |
104 | Ty: TyAbiInterface<'a, C> + Copy, | |
105 | C: HasDataLayout, | |
106 | { | |
107 | if let abi::FieldsShape::Union(_) = layout.fields { | |
108 | return data; | |
32a655c1 SL |
109 | } |
110 | ||
5e7ed085 FG |
111 | match layout.abi { |
112 | abi::Abi::Scalar(scalar) => { | |
113 | data = arg_scalar(cx, &scalar, offset, data); | |
114 | } | |
115 | abi::Abi::Aggregate { .. } => { | |
04454e1e | 116 | for i in 0..layout.fields.count() { |
5e7ed085 FG |
117 | if offset < layout.fields.offset(i) { |
118 | offset = layout.fields.offset(i); | |
a2a8927a | 119 | } |
04454e1e | 120 | data = parse_structure(cx, layout.field(cx, i), data.clone(), offset); |
a2a8927a XL |
121 | } |
122 | } | |
5e7ed085 FG |
123 | _ => { |
124 | if let abi::Abi::ScalarPair(scalar1, scalar2) = &layout.abi { | |
125 | data = arg_scalar_pair(cx, scalar1, scalar2, offset, data); | |
a2a8927a | 126 | } |
a2a8927a XL |
127 | } |
128 | } | |
129 | ||
5e7ed085 FG |
130 | return data; |
131 | } | |
132 | ||
133 | fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, in_registers_max: Size) | |
134 | where | |
135 | Ty: TyAbiInterface<'a, C> + Copy, | |
136 | C: HasDataLayout, | |
137 | { | |
138 | if !arg.layout.is_aggregate() { | |
139 | arg.extend_integer_width_to(64); | |
140 | return; | |
141 | } | |
142 | ||
ff7c6d11 | 143 | let total = arg.layout.size; |
a2a8927a | 144 | if total > in_registers_max { |
2c00a5a8 XL |
145 | arg.make_indirect(); |
146 | return; | |
147 | } | |
148 | ||
5e7ed085 FG |
149 | match arg.layout.fields { |
150 | abi::FieldsShape::Primitive => unreachable!(), | |
151 | abi::FieldsShape::Array { .. } => { | |
152 | // Arrays are passed indirectly | |
153 | arg.make_indirect(); | |
154 | return; | |
155 | } | |
156 | abi::FieldsShape::Union(_) => { | |
157 | // Unions and are always treated as a series of 64-bit integer chunks | |
158 | } | |
159 | abi::FieldsShape::Arbitrary { .. } => { | |
160 | // Structures with floating point numbers need special care. | |
161 | ||
162 | let mut data = parse_structure( | |
163 | cx, | |
04454e1e | 164 | arg.layout, |
5e7ed085 FG |
165 | Sdata { |
166 | prefix: [None; 8], | |
167 | prefix_index: 0, | |
168 | last_offset: Size::ZERO, | |
169 | has_float: false, | |
170 | arg_attribute: ArgAttribute::default(), | |
171 | }, | |
6522a427 | 172 | Size::ZERO, |
5e7ed085 FG |
173 | ); |
174 | ||
175 | if data.has_float { | |
176 | // Structure { float, int, int } doesn't like to be handled like | |
177 | // { float, long int }. Other way around it doesn't mind. | |
178 | if data.last_offset < arg.layout.size | |
6522a427 | 179 | && (data.last_offset.bytes() % 8) != 0 |
5e7ed085 FG |
180 | && data.prefix_index < data.prefix.len() |
181 | { | |
182 | data.prefix[data.prefix_index] = Some(Reg::i32()); | |
183 | data.prefix_index += 1; | |
184 | data.last_offset += Reg::i32().size; | |
185 | } | |
186 | ||
187 | let mut rest_size = arg.layout.size - data.last_offset; | |
6522a427 | 188 | if (rest_size.bytes() % 8) != 0 && data.prefix_index < data.prefix.len() { |
5e7ed085 FG |
189 | data.prefix[data.prefix_index] = Some(Reg::i32()); |
190 | rest_size = rest_size - Reg::i32().size; | |
191 | } | |
192 | ||
193 | arg.cast_to(CastTarget { | |
194 | prefix: data.prefix, | |
195 | rest: Uniform { unit: Reg::i64(), total: rest_size }, | |
196 | attrs: ArgAttributes { | |
197 | regular: data.arg_attribute, | |
198 | arg_ext: ArgExtension::None, | |
199 | pointee_size: Size::ZERO, | |
200 | pointee_align: None, | |
201 | }, | |
202 | }); | |
203 | return; | |
204 | } | |
205 | } | |
206 | } | |
207 | ||
dfeec247 | 208 | arg.cast_to(Uniform { unit: Reg::i64(), total }); |
32a655c1 SL |
209 | } |
210 | ||
60c5eb7d | 211 | pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) |
dfeec247 | 212 | where |
94222f64 XL |
213 | Ty: TyAbiInterface<'a, C> + Copy, |
214 | C: HasDataLayout, | |
83c7162d | 215 | { |
60c5eb7d | 216 | if !fn_abi.ret.is_ignore() { |
6522a427 | 217 | classify_arg(cx, &mut fn_abi.ret, Size::from_bytes(32)); |
32a655c1 SL |
218 | } |
219 | ||
f2b60f7d | 220 | for arg in fn_abi.args.iter_mut() { |
dfeec247 XL |
221 | if arg.is_ignore() { |
222 | continue; | |
223 | } | |
6522a427 | 224 | classify_arg(cx, arg, Size::from_bytes(16)); |
32a655c1 SL |
225 | } |
226 | } |