From cee244d4f02df90732c9e182f3567036ac695928 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Sun, 5 Jun 2016 15:34:13 +0300 Subject: [PATCH] trans: update Luqmana's patch for generalized pair handling. --- src/librustc_trans/mir/block.rs | 95 ++++++++++++++++------------ src/test/run-pass/mir_cast_fn_ret.rs | 18 ++++-- 2 files changed, 67 insertions(+), 46 deletions(-) diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index c8f9d46f7493f..b7aca4c8d7fed 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -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)); } } diff --git a/src/test/run-pass/mir_cast_fn_ret.rs b/src/test/run-pass/mir_cast_fn_ret.rs index 5bdc14f659cd5..8a723967aff5f 100644 --- a/src/test/run-pass/mir_cast_fn_ret.rs +++ b/src/test/run-pass/mir_cast_fn_ret.rs @@ -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); }