@@ -211,47 +211,33 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
211
211
bug ! ( "unsized `ArgAbi` must be handled through `store_fn_arg`" ) ;
212
212
}
213
213
PassMode :: Cast { cast, pad_i32 : _ } => {
214
- // FIXME(eddyb): Figure out when the simpler Store is safe, clang
215
- // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
216
- let can_store_through_cast_ptr = false ;
217
- if can_store_through_cast_ptr {
218
- bx. store ( val, dst. llval , self . layout . align . abi ) ;
219
- } else {
220
- // The actual return type is a struct, but the ABI
221
- // adaptation code has cast it into some scalar type. The
222
- // code that follows is the only reliable way I have
223
- // found to do a transform like i64 -> {i32,i32}.
224
- // Basically we dump the data onto the stack then memcpy it.
225
- //
226
- // Other approaches I tried:
227
- // - Casting rust ret pointer to the foreign type and using Store
228
- // is (a) unsafe if size of foreign type > size of rust type and
229
- // (b) runs afoul of strict aliasing rules, yielding invalid
230
- // assembly under -O (specifically, the store gets removed).
231
- // - Truncating foreign type to correct integral type and then
232
- // bitcasting to the struct type yields invalid cast errors.
233
-
234
- // We instead thus allocate some scratch space...
235
- let scratch_size = cast. size ( bx) ;
236
- let scratch_align = cast. align ( bx) ;
237
- let llscratch = bx. alloca ( cast. llvm_type ( bx) , scratch_align) ;
238
- bx. lifetime_start ( llscratch, scratch_size) ;
239
-
240
- // ... where we first store the value...
241
- bx. store ( val, llscratch, scratch_align) ;
242
-
243
- // ... and then memcpy it to the intended destination.
244
- bx. memcpy (
245
- dst. llval ,
246
- self . layout . align . abi ,
247
- llscratch,
248
- scratch_align,
249
- bx. const_usize ( self . layout . size . bytes ( ) ) ,
250
- MemFlags :: empty ( ) ,
251
- ) ;
252
-
253
- bx. lifetime_end ( llscratch, scratch_size) ;
254
- }
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) ;
255
241
}
256
242
_ => {
257
243
OperandRef :: from_immediate_or_packed_pair ( bx, val, self . layout ) . val . store ( bx, dst) ;
0 commit comments