]>
Commit | Line | Data |
---|---|---|
b7449926 XL |
1 | // Reference: RISC-V ELF psABI specification |
2 | // https://github.com/riscv/riscv-elf-psabi-doc | |
74b04a01 XL |
3 | // |
4 | // Reference: Clang RISC-V ELF psABI lowering code | |
5 | // https://github.com/llvm/llvm-project/blob/8e780252a7284be45cf1ba224cabd884847e8e92/clang/lib/CodeGen/TargetInfo.cpp#L9311-L9773 | |
b7449926 | 6 | |
fc512014 | 7 | use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}; |
94222f64 | 8 | use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; |
74b04a01 XL |
9 | use crate::spec::HasTargetSpec; |
10 | ||
11 | #[derive(Copy, Clone)] | |
12 | enum RegPassKind { | |
13 | Float(Reg), | |
14 | Integer(Reg), | |
15 | Unknown, | |
16 | } | |
17 | ||
18 | #[derive(Copy, Clone)] | |
19 | enum FloatConv { | |
20 | FloatPair(Reg, Reg), | |
21 | Float(Reg), | |
22 | MixedPair(Reg, Reg), | |
23 | } | |
24 | ||
25 | #[derive(Copy, Clone)] | |
26 | struct CannotUseFpConv; | |
27 | ||
28 | fn is_riscv_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool { | |
29 | match arg.layout.abi { | |
30 | Abi::Vector { .. } => true, | |
31 | _ => arg.layout.is_aggregate(), | |
32 | } | |
33 | } | |
34 | ||
35 | fn should_use_fp_conv_helper<'a, Ty, C>( | |
36 | cx: &C, | |
ba9703b0 | 37 | arg_layout: &TyAndLayout<'a, Ty>, |
74b04a01 XL |
38 | xlen: u64, |
39 | flen: u64, | |
40 | field1_kind: &mut RegPassKind, | |
41 | field2_kind: &mut RegPassKind, | |
42 | ) -> Result<(), CannotUseFpConv> | |
43 | where | |
94222f64 | 44 | Ty: TyAbiInterface<'a, C> + Copy, |
74b04a01 XL |
45 | { |
46 | match arg_layout.abi { | |
04454e1e | 47 | Abi::Scalar(scalar) => match scalar.primitive() { |
74b04a01 XL |
48 | abi::Int(..) | abi::Pointer => { |
49 | if arg_layout.size.bits() > xlen { | |
50 | return Err(CannotUseFpConv); | |
51 | } | |
52 | match (*field1_kind, *field2_kind) { | |
53 | (RegPassKind::Unknown, _) => { | |
54 | *field1_kind = RegPassKind::Integer(Reg { | |
55 | kind: RegKind::Integer, | |
56 | size: arg_layout.size, | |
57 | }); | |
58 | } | |
59 | (RegPassKind::Float(_), RegPassKind::Unknown) => { | |
60 | *field2_kind = RegPassKind::Integer(Reg { | |
61 | kind: RegKind::Integer, | |
62 | size: arg_layout.size, | |
63 | }); | |
64 | } | |
65 | _ => return Err(CannotUseFpConv), | |
66 | } | |
67 | } | |
68 | abi::F32 | abi::F64 => { | |
69 | if arg_layout.size.bits() > flen { | |
70 | return Err(CannotUseFpConv); | |
71 | } | |
72 | match (*field1_kind, *field2_kind) { | |
73 | (RegPassKind::Unknown, _) => { | |
74 | *field1_kind = | |
75 | RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); | |
76 | } | |
77 | (_, RegPassKind::Unknown) => { | |
78 | *field2_kind = | |
79 | RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); | |
80 | } | |
81 | _ => return Err(CannotUseFpConv), | |
82 | } | |
83 | } | |
84 | }, | |
85 | Abi::Vector { .. } | Abi::Uninhabited => return Err(CannotUseFpConv), | |
86 | Abi::ScalarPair(..) | Abi::Aggregate { .. } => match arg_layout.fields { | |
ba9703b0 XL |
87 | FieldsShape::Primitive => { |
88 | unreachable!("aggregates can't have `FieldsShape::Primitive`") | |
89 | } | |
90 | FieldsShape::Union(_) => { | |
74b04a01 XL |
91 | if !arg_layout.is_zst() { |
92 | return Err(CannotUseFpConv); | |
93 | } | |
94 | } | |
ba9703b0 | 95 | FieldsShape::Array { count, .. } => { |
74b04a01 XL |
96 | for _ in 0..count { |
97 | let elem_layout = arg_layout.field(cx, 0); | |
98 | should_use_fp_conv_helper( | |
99 | cx, | |
100 | &elem_layout, | |
101 | xlen, | |
102 | flen, | |
103 | field1_kind, | |
104 | field2_kind, | |
105 | )?; | |
106 | } | |
107 | } | |
ba9703b0 | 108 | FieldsShape::Arbitrary { .. } => { |
74b04a01 XL |
109 | match arg_layout.variants { |
110 | abi::Variants::Multiple { .. } => return Err(CannotUseFpConv), | |
111 | abi::Variants::Single { .. } => (), | |
112 | } | |
113 | for i in arg_layout.fields.index_by_increasing_offset() { | |
114 | let field = arg_layout.field(cx, i); | |
115 | should_use_fp_conv_helper(cx, &field, xlen, flen, field1_kind, field2_kind)?; | |
116 | } | |
117 | } | |
118 | }, | |
119 | } | |
120 | Ok(()) | |
121 | } | |
122 | ||
123 | fn should_use_fp_conv<'a, Ty, C>( | |
124 | cx: &C, | |
ba9703b0 | 125 | arg: &TyAndLayout<'a, Ty>, |
74b04a01 XL |
126 | xlen: u64, |
127 | flen: u64, | |
128 | ) -> Option<FloatConv> | |
129 | where | |
94222f64 | 130 | Ty: TyAbiInterface<'a, C> + Copy, |
74b04a01 XL |
131 | { |
132 | let mut field1_kind = RegPassKind::Unknown; | |
133 | let mut field2_kind = RegPassKind::Unknown; | |
134 | if should_use_fp_conv_helper(cx, arg, xlen, flen, &mut field1_kind, &mut field2_kind).is_err() { | |
135 | return None; | |
136 | } | |
137 | match (field1_kind, field2_kind) { | |
138 | (RegPassKind::Integer(l), RegPassKind::Float(r)) => Some(FloatConv::MixedPair(l, r)), | |
139 | (RegPassKind::Float(l), RegPassKind::Integer(r)) => Some(FloatConv::MixedPair(l, r)), | |
140 | (RegPassKind::Float(l), RegPassKind::Float(r)) => Some(FloatConv::FloatPair(l, r)), | |
141 | (RegPassKind::Float(f), RegPassKind::Unknown) => Some(FloatConv::Float(f)), | |
142 | _ => None, | |
143 | } | |
144 | } | |
145 | ||
146 | fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u64) -> bool | |
147 | where | |
94222f64 | 148 | Ty: TyAbiInterface<'a, C> + Copy, |
74b04a01 XL |
149 | { |
150 | if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) { | |
151 | match conv { | |
152 | FloatConv::Float(f) => { | |
153 | arg.cast_to(f); | |
154 | } | |
155 | FloatConv::FloatPair(l, r) => { | |
156 | arg.cast_to(CastTarget::pair(l, r)); | |
157 | } | |
158 | FloatConv::MixedPair(l, r) => { | |
159 | arg.cast_to(CastTarget::pair(l, r)); | |
160 | } | |
161 | } | |
162 | return false; | |
163 | } | |
164 | ||
165 | let total = arg.layout.size; | |
b7449926 | 166 | |
b7449926 XL |
167 | // "Scalars wider than 2✕XLEN are passed by reference and are replaced in |
168 | // the argument list with the address." | |
169 | // "Aggregates larger than 2✕XLEN bits are passed by reference and are | |
170 | // replaced in the argument list with the address, as are C++ aggregates | |
171 | // with nontrivial copy constructors, destructors, or vtables." | |
74b04a01 XL |
172 | if total.bits() > 2 * xlen { |
173 | // We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN. | |
174 | if is_riscv_aggregate(arg) { | |
175 | arg.make_indirect(); | |
176 | } | |
177 | return true; | |
178 | } | |
179 | ||
180 | let xlen_reg = match xlen { | |
181 | 32 => Reg::i32(), | |
182 | 64 => Reg::i64(), | |
183 | _ => unreachable!("Unsupported XLEN: {}", xlen), | |
184 | }; | |
185 | if is_riscv_aggregate(arg) { | |
186 | if total.bits() <= xlen { | |
187 | arg.cast_to(xlen_reg); | |
188 | } else { | |
189 | arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) }); | |
190 | } | |
191 | return false; | |
b7449926 XL |
192 | } |
193 | ||
194 | // "When passed in registers, scalars narrower than XLEN bits are widened | |
195 | // according to the sign of their type up to 32 bits, then sign-extended to | |
196 | // XLEN bits." | |
74b04a01 XL |
197 | extend_integer_width(arg, xlen); |
198 | false | |
b7449926 XL |
199 | } |
200 | ||
74b04a01 XL |
201 | fn classify_arg<'a, Ty, C>( |
202 | cx: &C, | |
203 | arg: &mut ArgAbi<'a, Ty>, | |
204 | xlen: u64, | |
205 | flen: u64, | |
206 | is_vararg: bool, | |
207 | avail_gprs: &mut u64, | |
208 | avail_fprs: &mut u64, | |
209 | ) where | |
94222f64 | 210 | Ty: TyAbiInterface<'a, C> + Copy, |
74b04a01 XL |
211 | { |
212 | if !is_vararg { | |
213 | match should_use_fp_conv(cx, &arg.layout, xlen, flen) { | |
214 | Some(FloatConv::Float(f)) if *avail_fprs >= 1 => { | |
215 | *avail_fprs -= 1; | |
216 | arg.cast_to(f); | |
217 | return; | |
218 | } | |
219 | Some(FloatConv::FloatPair(l, r)) if *avail_fprs >= 2 => { | |
220 | *avail_fprs -= 2; | |
221 | arg.cast_to(CastTarget::pair(l, r)); | |
222 | return; | |
223 | } | |
224 | Some(FloatConv::MixedPair(l, r)) if *avail_fprs >= 1 && *avail_gprs >= 1 => { | |
225 | *avail_gprs -= 1; | |
226 | *avail_fprs -= 1; | |
227 | arg.cast_to(CastTarget::pair(l, r)); | |
228 | return; | |
229 | } | |
230 | _ => (), | |
231 | } | |
232 | } | |
233 | ||
234 | let total = arg.layout.size; | |
235 | let align = arg.layout.align.abi.bits(); | |
236 | ||
b7449926 XL |
237 | // "Scalars wider than 2✕XLEN are passed by reference and are replaced in |
238 | // the argument list with the address." | |
239 | // "Aggregates larger than 2✕XLEN bits are passed by reference and are | |
240 | // replaced in the argument list with the address, as are C++ aggregates | |
241 | // with nontrivial copy constructors, destructors, or vtables." | |
74b04a01 XL |
242 | if total.bits() > 2 * xlen { |
243 | // We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN. | |
244 | if is_riscv_aggregate(arg) { | |
245 | arg.make_indirect(); | |
246 | } | |
247 | if *avail_gprs >= 1 { | |
248 | *avail_gprs -= 1; | |
249 | } | |
250 | return; | |
251 | } | |
252 | ||
253 | let double_xlen_reg = match xlen { | |
254 | 32 => Reg::i64(), | |
255 | 64 => Reg::i128(), | |
256 | _ => unreachable!("Unsupported XLEN: {}", xlen), | |
257 | }; | |
258 | ||
259 | let xlen_reg = match xlen { | |
260 | 32 => Reg::i32(), | |
261 | 64 => Reg::i64(), | |
262 | _ => unreachable!("Unsupported XLEN: {}", xlen), | |
263 | }; | |
264 | ||
265 | if total.bits() > xlen { | |
266 | let align_regs = align > xlen; | |
267 | if is_riscv_aggregate(arg) { | |
268 | arg.cast_to(Uniform { | |
269 | unit: if align_regs { double_xlen_reg } else { xlen_reg }, | |
270 | total: Size::from_bits(xlen * 2), | |
271 | }); | |
272 | } | |
273 | if align_regs && is_vararg { | |
274 | *avail_gprs -= *avail_gprs % 2; | |
275 | } | |
276 | if *avail_gprs >= 2 { | |
277 | *avail_gprs -= 2; | |
278 | } else { | |
279 | *avail_gprs = 0; | |
280 | } | |
281 | return; | |
282 | } else if is_riscv_aggregate(arg) { | |
283 | arg.cast_to(xlen_reg); | |
284 | if *avail_gprs >= 1 { | |
285 | *avail_gprs -= 1; | |
286 | } | |
287 | return; | |
b7449926 XL |
288 | } |
289 | ||
290 | // "When passed in registers, scalars narrower than XLEN bits are widened | |
291 | // according to the sign of their type up to 32 bits, then sign-extended to | |
292 | // XLEN bits." | |
74b04a01 XL |
293 | if *avail_gprs >= 1 { |
294 | extend_integer_width(arg, xlen); | |
295 | *avail_gprs -= 1; | |
296 | } | |
b7449926 XL |
297 | } |
298 | ||
74b04a01 | 299 | fn extend_integer_width<'a, Ty>(arg: &mut ArgAbi<'a, Ty>, xlen: u64) { |
c295e0f8 | 300 | if let Abi::Scalar(scalar) = arg.layout.abi { |
04454e1e | 301 | if let abi::Int(i, _) = scalar.primitive() { |
ba9703b0 XL |
302 | // 32-bit integers are always sign-extended |
303 | if i.size().bits() == 32 && xlen > 32 { | |
304 | if let PassMode::Direct(ref mut attrs) = arg.mode { | |
fc512014 | 305 | attrs.ext(ArgExtension::Sext); |
ba9703b0 | 306 | return; |
74b04a01 | 307 | } |
74b04a01 XL |
308 | } |
309 | } | |
74b04a01 | 310 | } |
ba9703b0 | 311 | |
74b04a01 XL |
312 | arg.extend_integer_width_to(xlen); |
313 | } | |
314 | ||
315 | pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) | |
316 | where | |
94222f64 XL |
317 | Ty: TyAbiInterface<'a, C> + Copy, |
318 | C: HasDataLayout + HasTargetSpec, | |
74b04a01 | 319 | { |
29967ef6 | 320 | let flen = match &cx.target_spec().llvm_abiname[..] { |
74b04a01 XL |
321 | "ilp32f" | "lp64f" => 32, |
322 | "ilp32d" | "lp64d" => 64, | |
323 | _ => 0, | |
324 | }; | |
325 | let xlen = cx.data_layout().pointer_size.bits(); | |
326 | ||
327 | let mut avail_gprs = 8; | |
328 | let mut avail_fprs = 8; | |
329 | ||
29967ef6 XL |
330 | if !fn_abi.ret.is_ignore() && classify_ret(cx, &mut fn_abi.ret, xlen, flen) { |
331 | avail_gprs -= 1; | |
b7449926 XL |
332 | } |
333 | ||
74b04a01 | 334 | for (i, arg) in fn_abi.args.iter_mut().enumerate() { |
b7449926 XL |
335 | if arg.is_ignore() { |
336 | continue; | |
337 | } | |
74b04a01 XL |
338 | classify_arg( |
339 | cx, | |
340 | arg, | |
341 | xlen, | |
342 | flen, | |
f2b60f7d | 343 | i >= fn_abi.fixed_count as usize, |
74b04a01 XL |
344 | &mut avail_gprs, |
345 | &mut avail_fprs, | |
346 | ); | |
b7449926 XL |
347 | } |
348 | } |