@@ -19,11 +19,11 @@ use base;
1919use build;
2020use callee:: { Callee , CalleeData , Fn , Intrinsic , NamedTupleConstructor , Virtual } ;
2121use common:: { self , Block , BlockAndBuilder , LandingPad } ;
22- use common:: { C_bool , C_str_slice , C_struct , C_u32 , C_undef } ;
22+ use common:: { C_bool , C_str_slice , C_struct , C_u32 , C_uint , C_undef } ;
2323use consts;
2424use debuginfo:: DebugLoc ;
2525use Disr ;
26- use machine:: { llalign_of_min, llbitsize_of_real} ;
26+ use machine:: { llalign_of_min, llbitsize_of_real, llsize_of_store } ;
2727use meth;
2828use type_of;
2929use glue;
@@ -39,6 +39,8 @@ use super::lvalue::{LvalueRef, load_fat_ptr};
3939use super :: operand:: OperandRef ;
4040use super :: operand:: OperandValue :: * ;
4141
42+ use std:: cmp;
43+
4244impl < ' bcx , ' tcx > MirContext < ' bcx , ' tcx > {
4345 pub fn trans_block ( & mut self , bb : mir:: BasicBlock ) {
4446 let mut bcx = self . bcx ( bb) ;
@@ -852,7 +854,43 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
852854
853855 match dest {
854856 Nothing => ( ) ,
855- Store ( dst) => ret_ty. store ( bcx, op. immediate ( ) , dst) ,
857+ Store ( dst) => {
858+ if let Some ( llcast_ty) = ret_ty. cast {
859+ let ccx = bcx. ccx ( ) ;
860+ // The actual return type is a struct, but the ABI
861+ // adaptation code has cast it into some scalar type. The
862+ // code that follows is the only reliable way I have
863+ // found to do a transform like i64 -> {i32,i32}.
864+ // Basically we dump the data onto the stack then memcpy it.
865+ //
866+ // Other approaches I tried:
867+ // - Casting rust ret pointer to the foreign type and using Store
868+ // is (a) unsafe if size of foreign type > size of rust type and
869+ // (b) runs afoul of strict aliasing rules, yielding invalid
870+ // assembly under -O (specifically, the store gets removed).
871+ // - Truncating foreign type to correct integral type and then
872+ // bitcasting to the struct type yields invalid cast errors.
873+
874+ // We instead thus allocate some scratch space...
875+ let llscratch = bcx. alloca ( llcast_ty, "fn_ret_cast" ) ;
876+ bcx. with_block ( |bcx| base:: call_lifetime_start ( bcx, llscratch) ) ;
877+
878+ // ...where we first store the value...
879+ bcx. store ( op. immediate ( ) , llscratch) ;
880+
881+ // ...and then memcpy it to the intended destination.
882+ base:: call_memcpy ( bcx,
883+ bcx. pointercast ( dst, Type :: i8p ( ccx) ) ,
884+ bcx. pointercast ( llscratch, Type :: i8p ( ccx) ) ,
885+ C_uint ( ccx, llsize_of_store ( ccx, ret_ty. original_ty ) ) ,
886+ cmp:: min ( llalign_of_min ( ccx, ret_ty. original_ty ) ,
887+ llalign_of_min ( ccx, llcast_ty) ) as u32 ) ;
888+
889+ bcx. with_block ( |bcx| base:: call_lifetime_end ( bcx, llscratch) ) ;
890+ } else {
891+ ret_ty. store ( bcx, op. immediate ( ) , dst) ;
892+ }
893+ }
856894 IndirectOperand ( tmp, idx) => {
857895 let op = self . trans_load ( bcx, tmp, op. ty ) ;
858896 self . temps [ idx as usize ] = TempRef :: Operand ( Some ( op) ) ;
0 commit comments