@@ -852,63 +852,74 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
852852 op : OperandRef < ' tcx > ) {
853853 use self :: ReturnDest :: * ;
854854
855- match dest {
856- Nothing => ( ) ,
855+ // Handle the simple cases that don't require casts, first.
856+ let llcast_ty = match dest {
857+ Nothing => return ,
857858 Store ( dst) => {
858859 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) ) ;
860+ llcast_ty
890861 } else {
891862 ret_ty. store ( bcx, op. immediate ( ) , dst) ;
863+ return ;
892864 }
893865 }
894866 IndirectOperand ( tmp, idx) => {
895867 let op = self . trans_load ( bcx, tmp, op. ty ) ;
896868 self . temps [ idx as usize ] = TempRef :: Operand ( Some ( op) ) ;
869+ return ;
897870 }
898871 DirectOperand ( idx) => {
899- // If there is a cast, we have to store and reload.
900- let op = if ret_ty. cast . is_some ( ) {
901- let tmp = bcx. with_block ( |bcx| {
902- base:: alloc_ty ( bcx, op. ty , "tmp_ret" )
903- } ) ;
904- ret_ty. store ( bcx, op. immediate ( ) , tmp) ;
905- self . trans_load ( bcx, tmp, op. ty )
872+ if let Some ( llcast_ty) = ret_ty. cast {
873+ llcast_ty
906874 } else {
907- op. unpack_if_pair ( bcx)
908- } ;
875+ let op = op. unpack_if_pair ( bcx) ;
876+ self . temps [ idx as usize ] = TempRef :: Operand ( Some ( op) ) ;
877+ return ;
878+ }
879+ }
880+ } ;
881+
882+ // The actual return type is a struct, but the ABI
883+ // adaptation code has cast it into some scalar type. The
884+ // code that follows is the only reliable way I have
885+ // found to do a transform like i64 -> {i32,i32}.
886+ // Basically we dump the data onto the stack then memcpy it.
887+ //
888+ // Other approaches I tried:
889+ // - Casting rust ret pointer to the foreign type and using Store
890+ // is (a) unsafe if size of foreign type > size of rust type and
891+ // (b) runs afoul of strict aliasing rules, yielding invalid
892+ // assembly under -O (specifically, the store gets removed).
893+ // - Truncating foreign type to correct integral type and then
894+ // bitcasting to the struct type yields invalid cast errors.
895+
896+ // We instead thus allocate some scratch space...
897+ let llscratch = bcx. alloca ( llcast_ty, "fn_ret_cast" ) ;
898+ bcx. with_block ( |bcx| base:: call_lifetime_start ( bcx, llscratch) ) ;
899+
900+ // ...where we first store the value...
901+ bcx. store ( op. immediate ( ) , llscratch) ;
902+
903+ let ccx = bcx. ccx ( ) ;
904+ match dest {
905+ Store ( dst) => {
906+ // ...and then memcpy it to the intended destination.
907+ base:: call_memcpy ( bcx,
908+ bcx. pointercast ( dst, Type :: i8p ( ccx) ) ,
909+ bcx. pointercast ( llscratch, Type :: i8p ( ccx) ) ,
910+ C_uint ( ccx, llsize_of_store ( ccx, ret_ty. original_ty ) ) ,
911+ cmp:: min ( llalign_of_min ( ccx, ret_ty. original_ty ) ,
912+ llalign_of_min ( ccx, llcast_ty) ) as u32 ) ;
913+ }
914+ DirectOperand ( idx) => {
915+ let llptr = bcx. pointercast ( llscratch, ret_ty. original_ty . ptr_to ( ) ) ;
916+ let op = self . trans_load ( bcx, llptr, op. ty ) ;
909917 self . temps [ idx as usize ] = TempRef :: Operand ( Some ( op) ) ;
910918 }
919+ Nothing | IndirectOperand ( _, _) => bug ! ( )
911920 }
921+
922+ bcx. with_block ( |bcx| base:: call_lifetime_end ( bcx, llscratch) ) ;
912923 }
913924}
914925
0 commit comments