1
+ use std:: iter;
2
+
3
+ use rustc_abi:: { BackendRepr , Primitive } ;
4
+
1
5
use crate :: abi:: call:: { ArgAbi , FnAbi , Reg , RegKind , Uniform } ;
2
6
use crate :: abi:: { HasDataLayout , TyAbiInterface } ;
7
+ use crate :: spec:: { HasTargetSpec , Target } ;
3
8
4
9
/// Indicates the variant of the AArch64 ABI we are compiling for.
5
10
/// Used to accommodate Apple and Microsoft's deviations from the usual AAPCS ABI.
@@ -15,7 +20,7 @@ pub(crate) enum AbiKind {
15
20
fn is_homogeneous_aggregate < ' a , Ty , C > ( cx : & C , arg : & mut ArgAbi < ' a , Ty > ) -> Option < Uniform >
16
21
where
17
22
Ty : TyAbiInterface < ' a , C > + Copy ,
18
- C : HasDataLayout ,
23
+ C : HasDataLayout + HasTargetSpec ,
19
24
{
20
25
arg. layout . homogeneous_aggregate ( cx) . ok ( ) . and_then ( |ha| ha. unit ( ) ) . and_then ( |unit| {
21
26
let size = arg. layout . size ;
@@ -27,18 +32,52 @@ where
27
32
28
33
let valid_unit = match unit. kind {
29
34
RegKind :: Integer => false ,
30
- RegKind :: Float => true ,
35
+ // The softfloat ABI treats floats like integers, so they
36
+ // do not get homogeneous aggregate treatment.
37
+ RegKind :: Float => cx. target_spec ( ) . abi != "softfloat" ,
31
38
RegKind :: Vector => size. bits ( ) == 64 || size. bits ( ) == 128 ,
32
39
} ;
33
40
34
41
valid_unit. then_some ( Uniform :: consecutive ( unit, size) )
35
42
} )
36
43
}
37
44
45
+ fn softfloat_float_abi < Ty > ( target : & Target , arg : & mut ArgAbi < ' _ , Ty > ) {
46
+ if target. abi != "softfloat" {
47
+ return ;
48
+ }
49
+ // Do *not* use the float registers for passing arguments, as that would make LLVM pick the ABI
50
+ // and its choice depends on whether `neon` instructions are enabled. Instead, we follow the
51
+ // AAPCS "softfloat" ABI, which specifies that floats should be passed as equivalently-sized
52
+ // integers. Nominally this only exists for "R" profile chips, but sometimes people don't want
53
+ // to use hardfloats even if the hardware supports them, so we do this for all softfloat
54
+ // targets.
55
+ if let BackendRepr :: Scalar ( s) = arg. layout . backend_repr
56
+ && let Primitive :: Float ( f) = s. primitive ( )
57
+ {
58
+ arg. cast_to ( Reg { kind : RegKind :: Integer , size : f. size ( ) } ) ;
59
+ } else if let BackendRepr :: ScalarPair ( s1, s2) = arg. layout . backend_repr
60
+ && ( matches ! ( s1. primitive( ) , Primitive :: Float ( _) )
61
+ || matches ! ( s2. primitive( ) , Primitive :: Float ( _) ) )
62
+ {
63
+ // This case can only be reached for the Rust ABI, so we can do whatever we want here as
64
+ // long as it does not depend on target features (i.e., as long as we do not use float
65
+ // registers). So we pass small things in integer registers and large things via pointer
66
+ // indirection. This means we lose the nice "pass it as two arguments" optimization, but we
67
+ // currently just have to way to combine a `PassMode::Cast` with that optimization (and we
68
+ // need a cast since we want to pass the float as an int).
69
+ if arg. layout . size . bits ( ) <= target. pointer_width . into ( ) {
70
+ arg. cast_to ( Reg { kind : RegKind :: Integer , size : arg. layout . size } ) ;
71
+ } else {
72
+ arg. make_indirect ( ) ;
73
+ }
74
+ }
75
+ }
76
+
38
77
fn classify_ret < ' a , Ty , C > ( cx : & C , ret : & mut ArgAbi < ' a , Ty > , kind : AbiKind )
39
78
where
40
79
Ty : TyAbiInterface < ' a , C > + Copy ,
41
- C : HasDataLayout ,
80
+ C : HasDataLayout + HasTargetSpec ,
42
81
{
43
82
if !ret. layout . is_sized ( ) {
44
83
// Not touching this...
51
90
// See also: <https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-Arguments-to-Functions-Correctly>
52
91
ret. extend_integer_width_to ( 32 )
53
92
}
93
+ softfloat_float_abi ( cx. target_spec ( ) , ret) ;
54
94
return ;
55
95
}
56
96
if let Some ( uniform) = is_homogeneous_aggregate ( cx, ret) {
69
109
fn classify_arg < ' a , Ty , C > ( cx : & C , arg : & mut ArgAbi < ' a , Ty > , kind : AbiKind )
70
110
where
71
111
Ty : TyAbiInterface < ' a , C > + Copy ,
72
- C : HasDataLayout ,
112
+ C : HasDataLayout + HasTargetSpec ,
73
113
{
74
114
if !arg. layout . is_sized ( ) {
75
115
// Not touching this...
82
122
// See also: <https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-Arguments-to-Functions-Correctly>
83
123
arg. extend_integer_width_to ( 32 ) ;
84
124
}
125
+ softfloat_float_abi ( cx. target_spec ( ) , arg) ;
126
+
85
127
return ;
86
128
}
87
129
if let Some ( uniform) = is_homogeneous_aggregate ( cx, arg) {
@@ -112,7 +154,7 @@ where
112
154
pub ( crate ) fn compute_abi_info < ' a , Ty , C > ( cx : & C , fn_abi : & mut FnAbi < ' a , Ty > , kind : AbiKind )
113
155
where
114
156
Ty : TyAbiInterface < ' a , C > + Copy ,
115
- C : HasDataLayout ,
157
+ C : HasDataLayout + HasTargetSpec ,
116
158
{
117
159
if !fn_abi. ret . is_ignore ( ) {
118
160
classify_ret ( cx, & mut fn_abi. ret , kind) ;
@@ -125,3 +167,13 @@ where
125
167
classify_arg ( cx, arg, kind) ;
126
168
}
127
169
}
170
+
171
+ pub ( crate ) fn compute_rust_abi_info < ' a , Ty , C > ( cx : & C , fn_abi : & mut FnAbi < ' a , Ty > )
172
+ where
173
+ Ty : TyAbiInterface < ' a , C > + Copy ,
174
+ C : HasDataLayout + HasTargetSpec ,
175
+ {
176
+ for arg in fn_abi. args . iter_mut ( ) . chain ( iter:: once ( & mut fn_abi. ret ) ) {
177
+ softfloat_float_abi ( cx. target_spec ( ) , arg) ;
178
+ }
179
+ }
0 commit comments