1 // Reference: RISC-V ELF psABI specification
2 // https://github.com/riscv/riscv-elf-psabi-doc
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
7 use crate::abi
::call
::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}
;
8 use crate::abi
::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}
;
9 use crate::spec
::HasTargetSpec
;
11 #[derive(Copy, Clone)]
18 #[derive(Copy, Clone)]
25 #[derive(Copy, Clone)]
26 struct CannotUseFpConv
;
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(),
35 fn should_use_fp_conv_helper
<'a
, Ty
, C
>(
37 arg_layout
: &TyAndLayout
<'a
, Ty
>,
40 field1_kind
: &mut RegPassKind
,
41 field2_kind
: &mut RegPassKind
,
42 ) -> Result
<(), CannotUseFpConv
>
44 Ty
: TyAbiInterface
<'a
, C
> + Copy
,
46 match arg_layout
.abi
{
47 Abi
::Scalar(ref scalar
) => match scalar
.value
{
48 abi
::Int(..) | abi
::Pointer
=> {
49 if arg_layout
.size
.bits() > xlen
{
50 return Err(CannotUseFpConv
);
52 match (*field1_kind
, *field2_kind
) {
53 (RegPassKind
::Unknown
, _
) => {
54 *field1_kind
= RegPassKind
::Integer(Reg
{
55 kind
: RegKind
::Integer
,
56 size
: arg_layout
.size
,
59 (RegPassKind
::Float(_
), RegPassKind
::Unknown
) => {
60 *field2_kind
= RegPassKind
::Integer(Reg
{
61 kind
: RegKind
::Integer
,
62 size
: arg_layout
.size
,
65 _
=> return Err(CannotUseFpConv
),
68 abi
::F32
| abi
::F64
=> {
69 if arg_layout
.size
.bits() > flen
{
70 return Err(CannotUseFpConv
);
72 match (*field1_kind
, *field2_kind
) {
73 (RegPassKind
::Unknown
, _
) => {
75 RegPassKind
::Float(Reg { kind: RegKind::Float, size: arg_layout.size }
);
77 (_
, RegPassKind
::Unknown
) => {
79 RegPassKind
::Float(Reg { kind: RegKind::Float, size: arg_layout.size }
);
81 _
=> return Err(CannotUseFpConv
),
85 Abi
::Vector { .. }
| Abi
::Uninhabited
=> return Err(CannotUseFpConv
),
86 Abi
::ScalarPair(..) | Abi
::Aggregate { .. }
=> match arg_layout
.fields
{
87 FieldsShape
::Primitive
=> {
88 unreachable
!("aggregates can't have `FieldsShape::Primitive`")
90 FieldsShape
::Union(_
) => {
91 if !arg_layout
.is_zst() {
92 return Err(CannotUseFpConv
);
95 FieldsShape
::Array { count, .. }
=> {
97 let elem_layout
= arg_layout
.field(cx
, 0);
98 should_use_fp_conv_helper(
108 FieldsShape
::Arbitrary { .. }
=> {
109 match arg_layout
.variants
{
110 abi
::Variants
::Multiple { .. }
=> return Err(CannotUseFpConv
),
111 abi
::Variants
::Single { .. }
=> (),
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
)?
;
123 fn should_use_fp_conv
<'a
, Ty
, C
>(
125 arg
: &TyAndLayout
<'a
, Ty
>,
128 ) -> Option
<FloatConv
>
130 Ty
: TyAbiInterface
<'a
, C
> + Copy
,
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() {
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
)),
146 fn classify_ret
<'a
, Ty
, C
>(cx
: &C
, arg
: &mut ArgAbi
<'a
, Ty
>, xlen
: u64, flen
: u64) -> bool
148 Ty
: TyAbiInterface
<'a
, C
> + Copy
,
150 if let Some(conv
) = should_use_fp_conv(cx
, &arg
.layout
, xlen
, flen
) {
152 FloatConv
::Float(f
) => {
155 FloatConv
::FloatPair(l
, r
) => {
156 arg
.cast_to(CastTarget
::pair(l
, r
));
158 FloatConv
::MixedPair(l
, r
) => {
159 arg
.cast_to(CastTarget
::pair(l
, r
));
165 let total
= arg
.layout
.size
;
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."
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
) {
180 let xlen_reg
= match xlen
{
183 _
=> unreachable
!("Unsupported XLEN: {}", xlen
),
185 if is_riscv_aggregate(arg
) {
186 if total
.bits() <= xlen
{
187 arg
.cast_to(xlen_reg
);
189 arg
.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) }
);
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
197 extend_integer_width(arg
, xlen
);
201 fn classify_arg
<'a
, Ty
, C
>(
203 arg
: &mut ArgAbi
<'a
, Ty
>,
207 avail_gprs
: &mut u64,
208 avail_fprs
: &mut u64,
210 Ty
: TyAbiInterface
<'a
, C
> + Copy
,
213 match should_use_fp_conv(cx
, &arg
.layout
, xlen
, flen
) {
214 Some(FloatConv
::Float(f
)) if *avail_fprs
>= 1 => {
219 Some(FloatConv
::FloatPair(l
, r
)) if *avail_fprs
>= 2 => {
221 arg
.cast_to(CastTarget
::pair(l
, r
));
224 Some(FloatConv
::MixedPair(l
, r
)) if *avail_fprs
>= 1 && *avail_gprs
>= 1 => {
227 arg
.cast_to(CastTarget
::pair(l
, r
));
234 let total
= arg
.layout
.size
;
235 let align
= arg
.layout
.align
.abi
.bits();
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."
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
) {
247 if *avail_gprs
>= 1 {
253 let double_xlen_reg
= match xlen
{
256 _
=> unreachable
!("Unsupported XLEN: {}", xlen
),
259 let xlen_reg
= match xlen
{
262 _
=> unreachable
!("Unsupported XLEN: {}", xlen
),
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),
273 if align_regs
&& is_vararg
{
274 *avail_gprs
-= *avail_gprs
% 2;
276 if *avail_gprs
>= 2 {
282 } else if is_riscv_aggregate(arg
) {
283 arg
.cast_to(xlen_reg
);
284 if *avail_gprs
>= 1 {
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
293 if *avail_gprs
>= 1 {
294 extend_integer_width(arg
, xlen
);
299 fn extend_integer_width
<'a
, Ty
>(arg
: &mut ArgAbi
<'a
, Ty
>, xlen
: u64) {
300 if let Abi
::Scalar(ref scalar
) = arg
.layout
.abi
{
301 if let abi
::Int(i
, _
) = scalar
.value
{
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
{
305 attrs
.ext(ArgExtension
::Sext
);
312 arg
.extend_integer_width_to(xlen
);
315 pub fn compute_abi_info
<'a
, Ty
, C
>(cx
: &C
, fn_abi
: &mut FnAbi
<'a
, Ty
>)
317 Ty
: TyAbiInterface
<'a
, C
> + Copy
,
318 C
: HasDataLayout
+ HasTargetSpec
,
320 let flen
= match &cx
.target_spec().llvm_abiname
[..] {
321 "ilp32f" | "lp64f" => 32,
322 "ilp32d" | "lp64d" => 64,
325 let xlen
= cx
.data_layout().pointer_size
.bits();
327 let mut avail_gprs
= 8;
328 let mut avail_fprs
= 8;
330 if !fn_abi
.ret
.is_ignore() && classify_ret(cx
, &mut fn_abi
.ret
, xlen
, flen
) {
334 for (i
, arg
) in fn_abi
.args
.iter_mut().enumerate() {
343 i
>= fn_abi
.fixed_count
,