trans: update Luqmana's patch for generalized pair handling.
This commit is contained in:
parent
da081e1eac
commit
cee244d4f0
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user