1- // FIXME: This needs an audit for correctness and completeness.
2-
31use rustc_abi:: {
4- BackendRepr , FieldsShape , Float , HasDataLayout , Primitive , Reg , Scalar , Size , TyAbiInterface ,
5- TyAndLayout ,
2+ Align , BackendRepr , FieldsShape , Float , HasDataLayout , Primitive , Reg , Size , TyAbiInterface ,
3+ TyAndLayout , Variants ,
64} ;
75
86use crate :: callconv:: { ArgAbi , ArgAttribute , CastTarget , FnAbi , Uniform } ;
97use crate :: spec:: HasTargetSpec ;
108
11- #[ derive( Clone , Debug ) ]
12- struct Sdata {
13- pub prefix : [ Option < Reg > ; 8 ] ,
14- pub prefix_index : usize ,
15- pub last_offset : Size ,
16- pub has_float : bool ,
17- pub arg_attribute : ArgAttribute ,
9+ #[ derive( Copy , Clone ) ]
10+ enum DoubleWord {
11+ F64 ,
12+ F128Start ,
13+ F128End ,
14+ Words ( [ Word ; 2 ] ) ,
1815}
1916
20- fn arg_scalar < C > ( cx : & C , scalar : & Scalar , offset : Size , mut data : Sdata ) -> Sdata
21- where
22- C : HasDataLayout ,
23- {
24- let dl = cx. data_layout ( ) ;
25-
26- if !matches ! ( scalar. primitive( ) , Primitive :: Float ( Float :: F32 | Float :: F64 ) ) {
27- return data;
28- }
29-
30- data. has_float = true ;
31-
32- if !data. last_offset . is_aligned ( dl. f64_align . abi ) && data. last_offset < offset {
33- if data. prefix_index == data. prefix . len ( ) {
34- return data;
35- }
36- data. prefix [ data. prefix_index ] = Some ( Reg :: i32 ( ) ) ;
37- data. prefix_index += 1 ;
38- data. last_offset = data. last_offset + Reg :: i32 ( ) . size ;
39- }
40-
41- for _ in 0 ..( ( offset - data. last_offset ) . bits ( ) / 64 )
42- . min ( ( data. prefix . len ( ) - data. prefix_index ) as u64 )
43- {
44- data. prefix [ data. prefix_index ] = Some ( Reg :: i64 ( ) ) ;
45- data. prefix_index += 1 ;
46- data. last_offset = data. last_offset + Reg :: i64 ( ) . size ;
47- }
48-
49- if data. last_offset < offset {
50- if data. prefix_index == data. prefix . len ( ) {
51- return data;
52- }
53- data. prefix [ data. prefix_index ] = Some ( Reg :: i32 ( ) ) ;
54- data. prefix_index += 1 ;
55- data. last_offset = data. last_offset + Reg :: i32 ( ) . size ;
56- }
57-
58- if data. prefix_index == data. prefix . len ( ) {
59- return data;
60- }
61-
62- if scalar. primitive ( ) == Primitive :: Float ( Float :: F32 ) {
63- data. arg_attribute = ArgAttribute :: InReg ;
64- data. prefix [ data. prefix_index ] = Some ( Reg :: f32 ( ) ) ;
65- data. last_offset = offset + Reg :: f32 ( ) . size ;
66- } else {
67- data. prefix [ data. prefix_index ] = Some ( Reg :: f64 ( ) ) ;
68- data. last_offset = offset + Reg :: f64 ( ) . size ;
69- }
70- data. prefix_index += 1 ;
71- data
17+ #[ derive( Copy , Clone ) ]
18+ enum Word {
19+ F32 ,
20+ Integer ,
7221}
7322
74- fn arg_scalar_pair < C > (
23+ fn classify < ' a , Ty , C > (
7524 cx : & C ,
76- scalar1 : & Scalar ,
77- scalar2 : & Scalar ,
78- mut offset : Size ,
79- mut data : Sdata ,
80- ) -> Sdata
81- where
82- C : HasDataLayout ,
83- {
84- data = arg_scalar ( cx, scalar1, offset, data) ;
85- match ( scalar1. primitive ( ) , scalar2. primitive ( ) ) {
86- ( Primitive :: Float ( Float :: F32 ) , _) => offset += Reg :: f32 ( ) . size ,
87- ( _, Primitive :: Float ( Float :: F64 ) ) => offset += Reg :: f64 ( ) . size ,
88- ( Primitive :: Int ( i, _signed) , _) => offset += i. size ( ) ,
89- ( Primitive :: Pointer ( _) , _) => offset += Reg :: i64 ( ) . size ,
90- _ => { }
91- }
92-
93- if ( offset. bytes ( ) % 4 ) != 0
94- && matches ! ( scalar2. primitive( ) , Primitive :: Float ( Float :: F32 | Float :: F64 ) )
95- {
96- offset += Size :: from_bytes ( 4 - ( offset. bytes ( ) % 4 ) ) ;
97- }
98- data = arg_scalar ( cx, scalar2, offset, data) ;
99- data
100- }
101-
102- fn parse_structure < ' a , Ty , C > (
103- cx : & C ,
104- layout : TyAndLayout < ' a , Ty > ,
105- mut data : Sdata ,
106- mut offset : Size ,
107- ) -> Sdata
108- where
25+ arg_layout : & TyAndLayout < ' a , Ty > ,
26+ offset : Size ,
27+ double_words : & mut [ DoubleWord ; 4 ] ,
28+ ) where
10929 Ty : TyAbiInterface < ' a , C > + Copy ,
11030 C : HasDataLayout ,
11131{
112- if let FieldsShape :: Union ( _) = layout. fields {
113- return data;
114- }
115-
116- match layout. backend_repr {
117- BackendRepr :: Scalar ( scalar) => {
118- data = arg_scalar ( cx, & scalar, offset, data) ;
119- }
120- BackendRepr :: Memory { .. } => {
121- for i in 0 ..layout. fields . count ( ) {
122- if offset < layout. fields . offset ( i) {
123- offset = layout. fields . offset ( i) ;
32+ match arg_layout. backend_repr {
33+ BackendRepr :: Scalar ( scalar) => match scalar. primitive ( ) {
34+ Primitive :: Float ( float)
35+ if offset. is_aligned ( float. align ( cx) . abi . min ( Align :: from_bytes ( 8 ) . unwrap ( ) ) ) =>
36+ {
37+ let index = offset. bytes_usize ( ) / 8 ;
38+ match float {
39+ Float :: F128 => {
40+ double_words[ index] = DoubleWord :: F128Start ;
41+ double_words[ index + 1 ] = DoubleWord :: F128End ;
42+ }
43+ Float :: F64 => {
44+ double_words[ index] = DoubleWord :: F64 ;
45+ }
46+ Float :: F32 => {
47+ if let DoubleWord :: Words ( words) = & mut double_words[ index] {
48+ words[ ( offset. bytes_usize ( ) % 8 ) / 4 ] = Word :: F32 ;
49+ } else {
50+ unreachable ! ( ) ;
51+ }
52+ }
53+ Float :: F16 => {
54+ // FIXME(llvm/llvm-project#97981): f16 doesn't have a proper ABI in LLVM on
55+ // sparc64 yet. Once it does, double-check if it needs to be passed in a
56+ // floating-point register here.
57+ }
12458 }
125- data = parse_structure ( cx, layout. field ( cx, i) , data. clone ( ) , offset) ;
12659 }
127- }
128- _ => {
129- if let BackendRepr :: ScalarPair ( scalar1, scalar2) = & layout. backend_repr {
130- data = arg_scalar_pair ( cx, scalar1, scalar2, offset, data) ;
60+ _ => { }
61+ } ,
62+ BackendRepr :: SimdVector { .. } => { }
63+ BackendRepr :: ScalarPair ( ..) | BackendRepr :: Memory { .. } => match arg_layout. fields {
64+ FieldsShape :: Primitive => {
65+ unreachable ! ( "aggregates can't have `FieldsShape::Primitive`" )
13166 }
132- }
67+ FieldsShape :: Union ( _) => {
68+ if !arg_layout. is_zst ( ) {
69+ if arg_layout. is_transparent ( ) {
70+ let non_1zst_elem = arg_layout. non_1zst_field ( cx) . expect ( "not exactly one non-1-ZST field in non-ZST repr(transparent) union" ) . 1 ;
71+ classify ( cx, & non_1zst_elem, offset, double_words) ;
72+ }
73+ }
74+ }
75+ FieldsShape :: Array { .. } => { }
76+ FieldsShape :: Arbitrary { .. } => match arg_layout. variants {
77+ Variants :: Multiple { .. } => { }
78+ Variants :: Single { .. } | Variants :: Empty => {
79+ for i in arg_layout. fields . index_by_increasing_offset ( ) {
80+ classify (
81+ cx,
82+ & arg_layout. field ( cx, i) ,
83+ offset + arg_layout. fields . offset ( i) ,
84+ double_words,
85+ ) ;
86+ }
87+ }
88+ } ,
89+ } ,
13390 }
134-
135- data
13691}
13792
138- fn classify_arg < ' a , Ty , C > ( cx : & C , arg : & mut ArgAbi < ' a , Ty > , in_registers_max : Size )
139- where
93+ fn classify_arg < ' a , Ty , C > (
94+ cx : & C ,
95+ arg : & mut ArgAbi < ' a , Ty > ,
96+ in_registers_max : Size ,
97+ total_double_word_count : & mut usize ,
98+ ) where
14099 Ty : TyAbiInterface < ' a , C > + Copy ,
141100 C : HasDataLayout ,
142101{
102+ let pad = !total_double_word_count. is_multiple_of ( 2 ) && arg. layout . align . abi . bytes ( ) == 16 ;
103+ let double_word_count = arg. layout . size . bytes_usize ( ) . div_ceil ( 8 ) ;
104+ let start_double_word_count = * total_double_word_count + usize:: from ( pad) ;
143105 if !arg. layout . is_aggregate ( ) {
144106 arg. extend_integer_width_to ( 64 ) ;
107+ * total_double_word_count = start_double_word_count + double_word_count;
145108 return ;
146109 }
147110
148111 let total = arg. layout . size ;
149112 if total > in_registers_max {
150113 arg. make_indirect ( ) ;
114+ * total_double_word_count += 1 ;
151115 return ;
152116 }
153117
154- match arg. layout . fields {
155- FieldsShape :: Primitive => unreachable ! ( ) ,
156- FieldsShape :: Array { .. } => {
157- // Arrays are passed indirectly
158- arg. make_indirect ( ) ;
159- return ;
160- }
161- FieldsShape :: Union ( _) => {
162- // Unions and are always treated as a series of 64-bit integer chunks
163- }
164- FieldsShape :: Arbitrary { .. } => {
165- // Structures with floating point numbers need special care.
118+ * total_double_word_count = start_double_word_count + double_word_count;
166119
167- let mut data = parse_structure (
168- cx,
169- arg. layout ,
170- Sdata {
171- prefix : [ None ; 8 ] ,
172- prefix_index : 0 ,
173- last_offset : Size :: ZERO ,
174- has_float : false ,
175- arg_attribute : ArgAttribute :: default ( ) ,
176- } ,
177- Size :: ZERO ,
178- ) ;
120+ let mut double_words = [ DoubleWord :: Words ( [ Word :: Integer ; 2 ] ) ; 4 ] ;
121+ classify ( cx, & arg. layout , Size :: ZERO , & mut double_words) ;
179122
180- if data. has_float {
181- // Structure { float, int, int } doesn't like to be handled like
182- // { float, long int }. Other way around it doesn't mind.
183- if data. last_offset < arg. layout . size
184- && ( data. last_offset . bytes ( ) % 8 ) != 0
185- && data. prefix_index < data. prefix . len ( )
186- {
187- data. prefix [ data. prefix_index ] = Some ( Reg :: i32 ( ) ) ;
188- data. prefix_index += 1 ;
189- data. last_offset += Reg :: i32 ( ) . size ;
190- }
123+ let mut regs = [ None ; 8 ] ;
124+ let mut i = 0 ;
125+ let mut push = |reg| {
126+ regs[ i] = Some ( reg) ;
127+ i += 1 ;
128+ } ;
129+ let mut attrs = ArgAttribute :: empty ( ) ;
191130
192- let mut rest_size = arg. layout . size - data. last_offset ;
193- if ( rest_size. bytes ( ) % 8 ) != 0 && data. prefix_index < data. prefix . len ( ) {
194- data. prefix [ data. prefix_index ] = Some ( Reg :: i32 ( ) ) ;
195- rest_size = rest_size - Reg :: i32 ( ) . size ;
131+ for ( index, double_word) in double_words. into_iter ( ) . enumerate ( ) {
132+ if arg. layout . size . bytes_usize ( ) <= index * 8 {
133+ break ;
134+ }
135+ match double_word {
136+ // `f128` must be aligned to be assigned a float register.
137+ DoubleWord :: F128Start if ( start_double_word_count + index) . is_multiple_of ( 2 ) => {
138+ push ( Reg :: f128 ( ) ) ;
139+ }
140+ DoubleWord :: F128Start => {
141+ push ( Reg :: i64 ( ) ) ;
142+ push ( Reg :: i64 ( ) ) ;
143+ }
144+ DoubleWord :: F128End => { } // Already handled by `F128Start`
145+ DoubleWord :: F64 => push ( Reg :: f64 ( ) ) ,
146+ DoubleWord :: Words ( [ Word :: Integer , Word :: Integer ] ) => push ( Reg :: i64 ( ) ) ,
147+ DoubleWord :: Words ( words) => {
148+ attrs |= ArgAttribute :: InReg ;
149+ for word in words {
150+ match word {
151+ Word :: F32 => push ( Reg :: f32 ( ) ) ,
152+ Word :: Integer => push ( Reg :: i32 ( ) ) ,
153+ }
196154 }
197-
198- arg. cast_to (
199- CastTarget :: prefixed ( data. prefix , Uniform :: new ( Reg :: i64 ( ) , rest_size) )
200- . with_attrs ( data. arg_attribute . into ( ) ) ,
201- ) ;
202- return ;
203155 }
204156 }
205157 }
206158
207- arg. cast_to ( Uniform :: new ( Reg :: i64 ( ) , total) ) ;
159+ if let [ Some ( reg) , None , ..] = regs {
160+ arg. cast_to_and_pad_i32 ( CastTarget :: from ( reg) . with_attrs ( attrs. into ( ) ) , pad) ;
161+ } else {
162+ arg. cast_to_and_pad_i32 (
163+ CastTarget :: prefixed ( regs, Uniform :: new ( Reg :: i8 ( ) , Size :: ZERO ) )
164+ . with_attrs ( attrs. into ( ) ) ,
165+ pad,
166+ ) ;
167+ }
208168}
209169
210170pub ( crate ) fn compute_abi_info < ' a , Ty , C > ( cx : & C , fn_abi : & mut FnAbi < ' a , Ty > )
@@ -213,9 +173,10 @@ where
213173 C : HasDataLayout + HasTargetSpec ,
214174{
215175 if !fn_abi. ret . is_ignore ( ) {
216- classify_arg ( cx, & mut fn_abi. ret , Size :: from_bytes ( 32 ) ) ;
176+ classify_arg ( cx, & mut fn_abi. ret , Size :: from_bytes ( 32 ) , & mut 0 ) ;
217177 }
218178
179+ let mut double_word_count = 0 ;
219180 for arg in fn_abi. args . iter_mut ( ) {
220181 if arg. is_ignore ( ) {
221182 // sparc64-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs.
@@ -224,9 +185,10 @@ where
224185 && arg. layout . is_zst ( )
225186 {
226187 arg. make_indirect_from_ignore ( ) ;
188+ double_word_count += 1 ;
227189 }
228- return ;
190+ continue ;
229191 }
230- classify_arg ( cx, arg, Size :: from_bytes ( 16 ) ) ;
192+ classify_arg ( cx, arg, Size :: from_bytes ( 16 ) , & mut double_word_count ) ;
231193 }
232194}
0 commit comments