@@ -16,10 +16,10 @@ use adt;
16
16
use base;
17
17
use build;
18
18
use callee:: { Callee , CalleeData , Fn , Intrinsic , NamedTupleConstructor , Virtual } ;
19
- use common:: { self , type_is_fat_ptr, Block , BlockAndBuilder , C_undef } ;
19
+ use common:: { self , type_is_fat_ptr, Block , BlockAndBuilder , C_uint , C_undef } ;
20
20
use debuginfo:: DebugLoc ;
21
21
use Disr ;
22
- use machine:: { llalign_of_min, llbitsize_of_real} ;
22
+ use machine:: { llalign_of_min, llbitsize_of_real, llsize_of_store } ;
23
23
use meth;
24
24
use type_of;
25
25
use glue;
@@ -32,6 +32,8 @@ use super::lvalue::{LvalueRef, load_fat_ptr};
32
32
use super :: operand:: OperandRef ;
33
33
use super :: operand:: OperandValue :: { self , FatPtr , Immediate , Ref } ;
34
34
35
+ use std:: cmp;
36
+
35
37
impl < ' bcx , ' tcx > MirContext < ' bcx , ' tcx > {
36
38
pub fn trans_block ( & mut self , bb : mir:: BasicBlock ) {
37
39
debug ! ( "trans_block({:?})" , bb) ;
@@ -685,7 +687,43 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
685
687
686
688
match dest {
687
689
Nothing => ( ) ,
688
- Store ( dst) => ret_ty. store ( bcx, op. immediate ( ) , dst) ,
690
+ Store ( dst) => {
691
+ if let Some ( llcast_ty) = ret_ty. cast {
692
+ let ccx = bcx. ccx ( ) ;
693
+ // The actual return type is a struct, but the ABI
694
+ // adaptation code has cast it into some scalar type. The
695
+ // code that follows is the only reliable way I have
696
+ // found to do a transform like i64 -> {i32,i32}.
697
+ // Basically we dump the data onto the stack then memcpy it.
698
+ //
699
+ // Other approaches I tried:
700
+ // - Casting rust ret pointer to the foreign type and using Store
701
+ // is (a) unsafe if size of foreign type > size of rust type and
702
+ // (b) runs afoul of strict aliasing rules, yielding invalid
703
+ // assembly under -O (specifically, the store gets removed).
704
+ // - Truncating foreign type to correct integral type and then
705
+ // bitcasting to the struct type yields invalid cast errors.
706
+
707
+ // We instead thus allocate some scratch space...
708
+ let llscratch = bcx. alloca ( llcast_ty, "fn_ret_cast" ) ;
709
+ bcx. with_block ( |bcx| base:: call_lifetime_start ( bcx, llscratch) ) ;
710
+
711
+ // ...where we first store the value...
712
+ bcx. store ( op. immediate ( ) , llscratch) ;
713
+
714
+ // ...and then memcpy it to the intended destination.
715
+ base:: call_memcpy ( bcx,
716
+ bcx. pointercast ( dst, Type :: i8p ( ccx) ) ,
717
+ bcx. pointercast ( llscratch, Type :: i8p ( ccx) ) ,
718
+ C_uint ( ccx, llsize_of_store ( ccx, ret_ty. original_ty ) ) ,
719
+ cmp:: min ( llalign_of_min ( ccx, ret_ty. original_ty ) ,
720
+ llalign_of_min ( ccx, llcast_ty) ) as u32 ) ;
721
+
722
+ bcx. with_block ( |bcx| base:: call_lifetime_end ( bcx, llscratch) ) ;
723
+ } else {
724
+ ret_ty. store ( bcx, op. immediate ( ) , dst) ;
725
+ }
726
+ }
689
727
IndirectOperand ( tmp, idx) => {
690
728
let op = self . trans_load ( bcx, tmp, op. ty ) ;
691
729
self . temps [ idx as usize ] = TempRef :: Operand ( Some ( op) ) ;
0 commit comments