Skip to content

Commit cee244d

Browse files
committed
trans: update Luqmana's patch for generalized pair handling.
1 parent da081e1 commit cee244d

File tree

2 files changed

+67
-46
lines changed

2 files changed

+67
-46
lines changed

Diff for: src/librustc_trans/mir/block.rs

+53-42
Original file line numberDiff line numberDiff line change
@@ -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

Diff for: src/test/run-pass/mir_cast_fn_ret.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,25 @@
1010

1111
#![feature(rustc_attrs)]
1212

13-
pub extern "C" fn foo() -> (u8, u8, u8) {
13+
pub extern "C" fn tuple2() -> (u16, u8) {
14+
(1, 2)
15+
}
16+
17+
pub extern "C" fn tuple3() -> (u8, u8, u8) {
1418
(1, 2, 3)
1519
}
1620

1721
#[rustc_mir]
18-
pub fn bar() -> u8 {
19-
foo().2
22+
pub fn test2() -> u8 {
23+
tuple2().1
24+
}
25+
26+
#[rustc_mir]
27+
pub fn test3() -> u8 {
28+
tuple3().2
2029
}
2130

2231
fn main() {
23-
assert_eq!(bar(), 3);
32+
assert_eq!(test2(), 2);
33+
assert_eq!(test3(), 3);
2434
}

0 commit comments

Comments
 (0)