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 ( ) . is_multiple_of ( 4 )
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+ // Match LLVM by passing `f16` in integer registers.
55+ }
12456 }
125- data = parse_structure ( cx, layout. field ( cx, i) , data. clone ( ) , offset) ;
12657 }
127- }
128- _ => {
129- if let BackendRepr :: ScalarPair ( scalar1, scalar2) = & layout. backend_repr {
130- data = arg_scalar_pair ( cx, scalar1, scalar2, offset, data) ;
58+ _ => { }
59+ } ,
60+ BackendRepr :: SimdVector { .. } => { }
61+ BackendRepr :: ScalarPair ( ..) | BackendRepr :: Memory { .. } => match arg_layout. fields {
62+ FieldsShape :: Primitive => {
63+ unreachable ! ( "aggregates can't have `FieldsShape::Primitive`" )
13164 }
132- }
65+ FieldsShape :: Union ( _) => {
66+ if !arg_layout. is_zst ( ) {
67+ if arg_layout. is_transparent ( ) {
68+ 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 ;
69+ classify ( cx, & non_1zst_elem, offset, double_words) ;
70+ }
71+ }
72+ }
73+ FieldsShape :: Array { .. } => { }
74+ FieldsShape :: Arbitrary { .. } => match arg_layout. variants {
75+ Variants :: Multiple { .. } => { }
76+ Variants :: Single { .. } | Variants :: Empty => {
77+ // Match Clang by ignoring whether a struct is packed and just considering
78+ // whether individual fields are aligned. GCC currently uses only integer
79+ // registers when passing packed structs.
80+ for i in arg_layout. fields . index_by_increasing_offset ( ) {
81+ classify (
82+ cx,
83+ & arg_layout. field ( cx, i) ,
84+ offset + arg_layout. fields . offset ( i) ,
85+ double_words,
86+ ) ;
87+ }
88+ }
89+ } ,
90+ } ,
13391 }
134-
135- data
13692}
13793
138- fn classify_arg < ' a , Ty , C > ( cx : & C , arg : & mut ArgAbi < ' a , Ty > , in_registers_max : Size )
139- where
94+ fn classify_arg < ' a , Ty , C > (
95+ cx : & C ,
96+ arg : & mut ArgAbi < ' a , Ty > ,
97+ in_registers_max : Size ,
98+ total_double_word_count : & mut usize ,
99+ ) where
140100 Ty : TyAbiInterface < ' a , C > + Copy ,
141101 C : HasDataLayout ,
142102{
103+ // 64-bit SPARC allocates argument stack space in 64-bit chunks (double words), some of which
104+ // are promoted to registers based on their position on the stack.
105+
106+ // Keep track of the total number of double words used by arguments so far. This allows padding
107+ // arguments to be inserted where necessary to ensure that 16-aligned arguments are passed in an
108+ // aligned set of registers.
109+
110+ let pad = !total_double_word_count. is_multiple_of ( 2 ) && arg. layout . align . abi . bytes ( ) == 16 ;
111+ // The number of double words used by this argument.
112+ let double_word_count = arg. layout . size . bytes_usize ( ) . div_ceil ( 8 ) ;
113+ // The number of double words before this argument, including any padding.
114+ let start_double_word_count = * total_double_word_count + usize:: from ( pad) ;
143115 if !arg. layout . is_aggregate ( ) {
144116 arg. extend_integer_width_to ( 64 ) ;
117+ * total_double_word_count = start_double_word_count + double_word_count;
145118 return ;
146119 }
147120
148121 let total = arg. layout . size ;
149122 if total > in_registers_max {
150123 arg. make_indirect ( ) ;
124+ * total_double_word_count += 1 ;
151125 return ;
152126 }
153127
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.
128+ * total_double_word_count = start_double_word_count + double_word_count;
166129
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- ) ;
130+ let mut double_words = [ DoubleWord :: Words ( [ Word :: Integer ; 2 ] ) ; 4 ] ;
131+ classify ( cx, & arg. layout , Size :: ZERO , & mut double_words) ;
179132
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 ( ) . is_multiple_of ( 8 )
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- }
133+ let mut regs = [ None ; 8 ] ;
134+ let mut i = 0 ;
135+ let mut push = |reg| {
136+ regs[ i] = Some ( reg) ;
137+ i += 1 ;
138+ } ;
139+ let mut attrs = ArgAttribute :: empty ( ) ;
191140
192- let mut rest_size = arg. layout . size - data. last_offset ;
193- if !rest_size. bytes ( ) . is_multiple_of ( 8 ) && data. prefix_index < data. prefix . len ( ) {
194- data. prefix [ data. prefix_index ] = Some ( Reg :: i32 ( ) ) ;
195- rest_size = rest_size - Reg :: i32 ( ) . size ;
141+ for ( index, double_word) in double_words. into_iter ( ) . enumerate ( ) {
142+ if arg. layout . size . bytes_usize ( ) <= index * 8 {
143+ break ;
144+ }
145+ match double_word {
146+ // `f128` must be aligned to be assigned a float register.
147+ DoubleWord :: F128Start if ( start_double_word_count + index) . is_multiple_of ( 2 ) => {
148+ push ( Reg :: f128 ( ) ) ;
149+ }
150+ DoubleWord :: F128Start => {
151+ // Clang currently handles this case nonsensically, always returning a packed
152+ // `struct { long double x; }` in an aligned quad floating-point register even when
153+ // the `long double` isn't aligned on the stack, which also makes all future
154+ // arguments get passed in the wrong registers. This passes the `f128` in integer
155+ // registers when it is unaligned, same as with `f32` and `f64`.
156+ push ( Reg :: i64 ( ) ) ;
157+ push ( Reg :: i64 ( ) ) ;
158+ }
159+ DoubleWord :: F128End => { } // Already handled by `F128Start`
160+ DoubleWord :: F64 => push ( Reg :: f64 ( ) ) ,
161+ DoubleWord :: Words ( [ Word :: Integer , Word :: Integer ] ) => push ( Reg :: i64 ( ) ) ,
162+ DoubleWord :: Words ( words) => {
163+ attrs |= ArgAttribute :: InReg ;
164+ for word in words {
165+ match word {
166+ Word :: F32 => push ( Reg :: f32 ( ) ) ,
167+ Word :: Integer => push ( Reg :: i32 ( ) ) ,
168+ }
196169 }
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 ;
203170 }
204171 }
205172 }
206173
207- arg. cast_to ( Uniform :: new ( Reg :: i64 ( ) , total) ) ;
174+ if let [ Some ( reg) , None , ..] = regs {
175+ arg. cast_to_and_pad_i32 ( CastTarget :: from ( reg) . with_attrs ( attrs. into ( ) ) , pad) ;
176+ } else {
177+ arg. cast_to_and_pad_i32 (
178+ CastTarget :: prefixed ( regs, Uniform :: new ( Reg :: i8 ( ) , Size :: ZERO ) )
179+ . with_attrs ( attrs. into ( ) ) ,
180+ pad,
181+ ) ;
182+ }
208183}
209184
210185pub ( crate ) fn compute_abi_info < ' a , Ty , C > ( cx : & C , fn_abi : & mut FnAbi < ' a , Ty > )
@@ -213,9 +188,10 @@ where
213188 C : HasDataLayout + HasTargetSpec ,
214189{
215190 if !fn_abi. ret . is_ignore ( ) {
216- classify_arg ( cx, & mut fn_abi. ret , Size :: from_bytes ( 32 ) ) ;
191+ classify_arg ( cx, & mut fn_abi. ret , Size :: from_bytes ( 32 ) , & mut 0 ) ;
217192 }
218193
194+ let mut double_word_count = 0 ;
219195 for arg in fn_abi. args . iter_mut ( ) {
220196 if arg. is_ignore ( ) {
221197 // sparc64-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs.
@@ -224,9 +200,10 @@ where
224200 && arg. layout . is_zst ( )
225201 {
226202 arg. make_indirect_from_ignore ( ) ;
203+ double_word_count += 1 ;
227204 }
228- return ;
205+ continue ;
229206 }
230- classify_arg ( cx, arg, Size :: from_bytes ( 16 ) ) ;
207+ classify_arg ( cx, arg, Size :: from_bytes ( 16 ) , & mut double_word_count ) ;
231208 }
232209}
0 commit comments