@@ -19,11 +19,11 @@ use base;
19
19
use build;
20
20
use callee:: { Callee , CalleeData , Fn , Intrinsic , NamedTupleConstructor , Virtual } ;
21
21
use 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 } ;
23
23
use consts;
24
24
use debuginfo:: DebugLoc ;
25
25
use Disr ;
26
- use machine:: { llalign_of_min, llbitsize_of_real} ;
26
+ use machine:: { llalign_of_min, llbitsize_of_real, llsize_of_store } ;
27
27
use meth;
28
28
use type_of;
29
29
use glue;
@@ -39,6 +39,8 @@ use super::lvalue::{LvalueRef, load_fat_ptr};
39
39
use super :: operand:: OperandRef ;
40
40
use super :: operand:: OperandValue :: * ;
41
41
42
+ use std:: cmp;
43
+
42
44
impl < ' bcx , ' tcx > MirContext < ' bcx , ' tcx > {
43
45
pub fn trans_block ( & mut self , bb : mir:: BasicBlock ) {
44
46
let mut bcx = self . bcx ( bb) ;
@@ -852,7 +854,43 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
852
854
853
855
match dest {
854
856
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
+ }
856
894
IndirectOperand ( tmp, idx) => {
857
895
let op = self . trans_load ( bcx, tmp, op. ty ) ;
858
896
self . temps [ idx as usize ] = TempRef :: Operand ( Some ( op) ) ;
0 commit comments