@@ -211,47 +211,33 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
211211 bug ! ( "unsized `ArgAbi` must be handled through `store_fn_arg`" ) ;
212212 }
213213 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) ;
255241 }
256242 _ => {
257243 OperandRef :: from_immediate_or_packed_pair ( bx, val, self . layout ) . val . store ( bx, dst) ;
0 commit comments