Skip to content

Commit

Permalink
trans: update Luqmana's patch for generalized pair handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Jun 5, 2016
1 parent da081e1 commit cee244d
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 46 deletions.
95 changes: 53 additions & 42 deletions src/librustc_trans/mir/block.rs
Expand Up @@ -852,63 +852,74 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
op: OperandRef<'tcx>) {
use self::ReturnDest::*;

match dest {
Nothing => (),
// Handle the simple cases that don't require casts, first.
let llcast_ty = match dest {
Nothing => return,
Store(dst) => {
if let Some(llcast_ty) = ret_ty.cast {
let ccx = bcx.ccx();
// The actual return type is a struct, but the ABI
// adaptation code has cast it into some scalar type. The
// code that follows is the only reliable way I have
// found to do a transform like i64 -> {i32,i32}.
// Basically we dump the data onto the stack then memcpy it.
//
// Other approaches I tried:
// - Casting rust ret pointer to the foreign type and using Store
// is (a) unsafe if size of foreign type > size of rust type and
// (b) runs afoul of strict aliasing rules, yielding invalid
// assembly under -O (specifically, the store gets removed).
// - Truncating foreign type to correct integral type and then
// bitcasting to the struct type yields invalid cast errors.

// We instead thus allocate some scratch space...
let llscratch = bcx.alloca(llcast_ty, "fn_ret_cast");
bcx.with_block(|bcx| base::call_lifetime_start(bcx, llscratch));

// ...where we first store the value...
bcx.store(op.immediate(), llscratch);

// ...and then memcpy it to the intended destination.
base::call_memcpy(bcx,
bcx.pointercast(dst, Type::i8p(ccx)),
bcx.pointercast(llscratch, Type::i8p(ccx)),
C_uint(ccx, llsize_of_store(ccx, ret_ty.original_ty)),
cmp::min(llalign_of_min(ccx, ret_ty.original_ty),
llalign_of_min(ccx, llcast_ty)) as u32);

bcx.with_block(|bcx| base::call_lifetime_end(bcx, llscratch));
llcast_ty
} else {
ret_ty.store(bcx, op.immediate(), dst);
return;
}
}
IndirectOperand(tmp, idx) => {
let op = self.trans_load(bcx, tmp, op.ty);
self.temps[idx as usize] = TempRef::Operand(Some(op));
return;
}
DirectOperand(idx) => {
// If there is a cast, we have to store and reload.
let op = if ret_ty.cast.is_some() {
let tmp = bcx.with_block(|bcx| {
base::alloc_ty(bcx, op.ty, "tmp_ret")
});
ret_ty.store(bcx, op.immediate(), tmp);
self.trans_load(bcx, tmp, op.ty)
if let Some(llcast_ty) = ret_ty.cast {
llcast_ty
} else {
op.unpack_if_pair(bcx)
};
let op = op.unpack_if_pair(bcx);
self.temps[idx as usize] = TempRef::Operand(Some(op));
return;
}
}
};

// The actual return type is a struct, but the ABI
// adaptation code has cast it into some scalar type. The
// code that follows is the only reliable way I have
// found to do a transform like i64 -> {i32,i32}.
// Basically we dump the data onto the stack then memcpy it.
//
// Other approaches I tried:
// - Casting rust ret pointer to the foreign type and using Store
// is (a) unsafe if size of foreign type > size of rust type and
// (b) runs afoul of strict aliasing rules, yielding invalid
// assembly under -O (specifically, the store gets removed).
// - Truncating foreign type to correct integral type and then
// bitcasting to the struct type yields invalid cast errors.

// We instead thus allocate some scratch space...
let llscratch = bcx.alloca(llcast_ty, "fn_ret_cast");
bcx.with_block(|bcx| base::call_lifetime_start(bcx, llscratch));

// ...where we first store the value...
bcx.store(op.immediate(), llscratch);

let ccx = bcx.ccx();
match dest {
Store(dst) => {
// ...and then memcpy it to the intended destination.
base::call_memcpy(bcx,
bcx.pointercast(dst, Type::i8p(ccx)),
bcx.pointercast(llscratch, Type::i8p(ccx)),
C_uint(ccx, llsize_of_store(ccx, ret_ty.original_ty)),
cmp::min(llalign_of_min(ccx, ret_ty.original_ty),
llalign_of_min(ccx, llcast_ty)) as u32);
}
DirectOperand(idx) => {
let llptr = bcx.pointercast(llscratch, ret_ty.original_ty.ptr_to());
let op = self.trans_load(bcx, llptr, op.ty);
self.temps[idx as usize] = TempRef::Operand(Some(op));
}
Nothing | IndirectOperand(_, _) => bug!()
}

bcx.with_block(|bcx| base::call_lifetime_end(bcx, llscratch));
}
}

Expand Down
18 changes: 14 additions & 4 deletions src/test/run-pass/mir_cast_fn_ret.rs
Expand Up @@ -10,15 +10,25 @@

#![feature(rustc_attrs)]

pub extern "C" fn foo() -> (u8, u8, u8) {
pub extern "C" fn tuple2() -> (u16, u8) {
(1, 2)
}

pub extern "C" fn tuple3() -> (u8, u8, u8) {
(1, 2, 3)
}

#[rustc_mir]
pub fn bar() -> u8 {
foo().2
pub fn test2() -> u8 {
tuple2().1
}

#[rustc_mir]
pub fn test3() -> u8 {
tuple3().2
}

fn main() {
assert_eq!(bar(), 3);
assert_eq!(test2(), 2);
assert_eq!(test3(), 3);
}

0 comments on commit cee244d

Please sign in to comment.