@@ -16,13 +16,15 @@ pub use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
16
16
use rustc_middle:: ty:: Ty ;
17
17
use rustc_session:: config;
18
18
pub use rustc_target:: abi:: call:: * ;
19
- use rustc_target:: abi:: { self , HasDataLayout , Int } ;
19
+ use rustc_target:: abi:: { self , HasDataLayout , Int , Size } ;
20
20
pub use rustc_target:: spec:: abi:: Abi ;
21
21
use rustc_target:: spec:: SanitizerSet ;
22
22
23
23
use libc:: c_uint;
24
24
use smallvec:: SmallVec ;
25
25
26
+ use std:: cmp;
27
+
26
28
pub trait ArgAttributesExt {
27
29
fn apply_attrs_to_llfn ( & self , idx : AttributePlace , cx : & CodegenCx < ' _ , ' _ > , llfn : & Value ) ;
28
30
fn apply_attrs_to_callsite (
@@ -130,42 +132,36 @@ impl LlvmType for Reg {
130
132
impl LlvmType for CastTarget {
131
133
fn llvm_type < ' ll > ( & self , cx : & CodegenCx < ' ll , ' _ > ) -> & ' ll Type {
132
134
let rest_ll_unit = self . rest . unit . llvm_type ( cx) ;
133
- let ( rest_count, rem_bytes ) = if self . rest . unit . size . bytes ( ) == 0 {
134
- ( 0 , 0 )
135
+ let rest_count = if self . rest . total == Size :: ZERO {
136
+ 0
135
137
} else {
136
- (
137
- self . rest . total . bytes ( ) / self . rest . unit . size . bytes ( ) ,
138
- self . rest . total . bytes ( ) % self . rest . unit . size . bytes ( ) ,
139
- )
138
+ assert_ne ! (
139
+ self . rest. unit. size,
140
+ Size :: ZERO ,
141
+ "total size {:?} cannot be divided into units of zero size" ,
142
+ self . rest. total
143
+ ) ;
144
+ if self . rest . total . bytes ( ) % self . rest . unit . size . bytes ( ) != 0 {
145
+ assert_eq ! ( self . rest. unit. kind, RegKind :: Integer , "only int regs can be split" ) ;
146
+ }
147
+ self . rest . total . bytes ( ) . div_ceil ( self . rest . unit . size . bytes ( ) )
140
148
} ;
141
149
150
+ // Simplify to a single unit or an array if there's no prefix.
151
+ // This produces the same layout, but using a simpler type.
142
152
if self . prefix . iter ( ) . all ( |x| x. is_none ( ) ) {
143
- // Simplify to a single unit when there is no prefix and size <= unit size
144
- if self . rest . total <= self . rest . unit . size {
153
+ if rest_count == 1 {
145
154
return rest_ll_unit;
146
155
}
147
156
148
- // Simplify to array when all chunks are the same size and type
149
- if rem_bytes == 0 {
150
- return cx. type_array ( rest_ll_unit, rest_count) ;
151
- }
152
- }
153
-
154
- // Create list of fields in the main structure
155
- let mut args: Vec < _ > = self
156
- . prefix
157
- . iter ( )
158
- . flat_map ( |option_reg| option_reg. map ( |reg| reg. llvm_type ( cx) ) )
159
- . chain ( ( 0 ..rest_count) . map ( |_| rest_ll_unit) )
160
- . collect ( ) ;
161
-
162
- // Append final integer
163
- if rem_bytes != 0 {
164
- // Only integers can be really split further.
165
- assert_eq ! ( self . rest. unit. kind, RegKind :: Integer ) ;
166
- args. push ( cx. type_ix ( rem_bytes * 8 ) ) ;
157
+ return cx. type_array ( rest_ll_unit, rest_count) ;
167
158
}
168
159
160
+ // Generate a struct type with the prefix and the "rest" arguments.
161
+ let prefix_args =
162
+ self . prefix . iter ( ) . flat_map ( |option_reg| option_reg. map ( |reg| reg. llvm_type ( cx) ) ) ;
163
+ let rest_args = ( 0 ..rest_count) . map ( |_| rest_ll_unit) ;
164
+ let args: Vec < _ > = prefix_args. chain ( rest_args) . collect ( ) ;
169
165
cx. type_struct ( & args, false )
170
166
}
171
167
}
@@ -215,47 +211,33 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
215
211
bug ! ( "unsized `ArgAbi` must be handled through `store_fn_arg`" ) ;
216
212
}
217
213
PassMode :: Cast { cast, pad_i32 : _ } => {
218
- // FIXME(eddyb): Figure out when the simpler Store is safe, clang
219
- // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
220
- let can_store_through_cast_ptr = false ;
221
- if can_store_through_cast_ptr {
222
- bx. store ( val, dst. llval , self . layout . align . abi ) ;
223
- } else {
224
- // The actual return type is a struct, but the ABI
225
- // adaptation code has cast it into some scalar type. The
226
- // code that follows is the only reliable way I have
227
- // found to do a transform like i64 -> {i32,i32}.
228
- // Basically we dump the data onto the stack then memcpy it.
229
- //
230
- // Other approaches I tried:
231
- // - Casting rust ret pointer to the foreign type and using Store
232
- // is (a) unsafe if size of foreign type > size of rust type and
233
- // (b) runs afoul of strict aliasing rules, yielding invalid
234
- // assembly under -O (specifically, the store gets removed).
235
- // - Truncating foreign type to correct integral type and then
236
- // bitcasting to the struct type yields invalid cast errors.
237
-
238
- // We instead thus allocate some scratch space...
239
- let scratch_size = cast. size ( bx) ;
240
- let scratch_align = cast. align ( bx) ;
241
- let llscratch = bx. alloca ( cast. llvm_type ( bx) , scratch_align) ;
242
- bx. lifetime_start ( llscratch, scratch_size) ;
243
-
244
- // ... where we first store the value...
245
- bx. store ( val, llscratch, scratch_align) ;
246
-
247
- // ... and then memcpy it to the intended destination.
248
- bx. memcpy (
249
- dst. llval ,
250
- self . layout . align . abi ,
251
- llscratch,
252
- scratch_align,
253
- bx. const_usize ( self . layout . size . bytes ( ) ) ,
254
- MemFlags :: empty ( ) ,
255
- ) ;
256
-
257
- bx. lifetime_end ( llscratch, scratch_size) ;
258
- }
214
+ // The ABI mandates that the value is passed as a different struct representation.
215
+ // Spill and reload it from the stack to convert from the ABI representation to
216
+ // the Rust representation.
217
+ let scratch_size = cast. size ( bx) ;
218
+ let scratch_align = cast. align ( bx) ;
219
+ // Note that the ABI type may be either larger or smaller than the Rust type,
220
+ // due to the presence or absence of trailing padding. For example:
221
+ // - On some ABIs, the Rust layout { f64, f32, <f32 padding> } may omit padding
222
+ // when passed by value, making it smaller.
223
+ // - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes
224
+ // when passed by value, making it larger.
225
+ let copy_bytes = cmp:: min ( scratch_size. bytes ( ) , self . layout . size . bytes ( ) ) ;
226
+ // Allocate some scratch space...
227
+ let llscratch = bx. alloca ( cast. llvm_type ( bx) , scratch_align) ;
228
+ bx. lifetime_start ( llscratch, scratch_size) ;
229
+ // ...store the value...
230
+ bx. store ( val, llscratch, scratch_align) ;
231
+ // ... and then memcpy it to the intended destination.
232
+ bx. memcpy (
233
+ dst. llval ,
234
+ self . layout . align . abi ,
235
+ llscratch,
236
+ scratch_align,
237
+ bx. const_usize ( copy_bytes) ,
238
+ MemFlags :: empty ( ) ,
239
+ ) ;
240
+ bx. lifetime_end ( llscratch, scratch_size) ;
259
241
}
260
242
_ => {
261
243
OperandRef :: from_immediate_or_packed_pair ( bx, val, self . layout ) . val . store ( bx, dst) ;
0 commit comments