]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_target/src/abi/call/riscv.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / compiler / rustc_target / src / abi / call / riscv.rs
CommitLineData
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 7use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform};
94222f64 8use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
74b04a01
XL
9use crate::spec::HasTargetSpec;
10
11#[derive(Copy, Clone)]
12enum RegPassKind {
13 Float(Reg),
14 Integer(Reg),
15 Unknown,
16}
17
18#[derive(Copy, Clone)]
19enum FloatConv {
20 FloatPair(Reg, Reg),
21 Float(Reg),
22 MixedPair(Reg, Reg),
23}
24
25#[derive(Copy, Clone)]
26struct CannotUseFpConv;
27
28fn 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
35fn 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>
43where
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
123fn 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>
129where
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
146fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u64) -> bool
147where
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
201fn 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 299fn 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
315pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
316where
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}