diff options
| author | Luqman Aden <me@luqman.ca> | 2016-06-02 21:32:07 -0400 |
|---|---|---|
| committer | Eduard Burtescu <edy.burt@gmail.com> | 2016-06-05 14:46:33 +0300 |
| commit | da081e1eac64d981209a742f8edea1a55127ce42 (patch) | |
| tree | 06960cbf49f2d22372235bdf9504272146d4ad15 | |
| parent | e71f6d8ac98a9db4fe58448fc70582339bc97ccd (diff) | |
| download | rust-da081e1eac64d981209a742f8edea1a55127ce42.tar.gz rust-da081e1eac64d981209a742f8edea1a55127ce42.zip | |
[MIR] Handle call return values that need to be casted properly.
| -rw-r--r-- | src/librustc_trans/mir/block.rs | 44 | ||||
| -rw-r--r-- | src/test/run-pass/mir_cast_fn_ret.rs | 24 |
2 files changed, 65 insertions, 3 deletions
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index b404475b584..c8f9d46f749 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -19,11 +19,11 @@ use base; use build; use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual}; use common::{self, Block, BlockAndBuilder, LandingPad}; -use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef}; +use common::{C_bool, C_str_slice, C_struct, C_u32, C_uint, C_undef}; use consts; use debuginfo::DebugLoc; use Disr; -use machine::{llalign_of_min, llbitsize_of_real}; +use machine::{llalign_of_min, llbitsize_of_real, llsize_of_store}; use meth; use type_of; use glue; @@ -39,6 +39,8 @@ use super::lvalue::{LvalueRef, load_fat_ptr}; use super::operand::OperandRef; use super::operand::OperandValue::*; +use std::cmp; + impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_block(&mut self, bb: mir::BasicBlock) { let mut bcx = self.bcx(bb); @@ -852,7 +854,43 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { match dest { Nothing => (), - Store(dst) => ret_ty.store(bcx, op.immediate(), dst), + 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)); + } else { + ret_ty.store(bcx, op.immediate(), dst); + } + } IndirectOperand(tmp, idx) => { let op = self.trans_load(bcx, tmp, op.ty); self.temps[idx as usize] = TempRef::Operand(Some(op)); diff --git a/src/test/run-pass/mir_cast_fn_ret.rs b/src/test/run-pass/mir_cast_fn_ret.rs new file mode 100644 index 00000000000..5bdc14f659c --- /dev/null +++ b/src/test/run-pass/mir_cast_fn_ret.rs @@ -0,0 +1,24 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +pub extern "C" fn foo() -> (u8, u8, u8) { + (1, 2, 3) +} + +#[rustc_mir] +pub fn bar() -> u8 { + foo().2 +} + +fn main() { + assert_eq!(bar(), 3); +} |
