Skip to content

Commit d14c5cd

Browse files
committed
[MIR] Handle call return values that need to be casted properly.
1 parent 12d1653 commit d14c5cd

File tree

2 files changed

+65
-3
lines changed

2 files changed

+65
-3
lines changed

src/librustc_trans/mir/block.rs

+41-3
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ use adt;
1616
use base;
1717
use build;
1818
use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual};
19-
use common::{self, type_is_fat_ptr, Block, BlockAndBuilder, C_undef};
19+
use common::{self, type_is_fat_ptr, Block, BlockAndBuilder, C_uint, C_undef};
2020
use debuginfo::DebugLoc;
2121
use Disr;
22-
use machine::{llalign_of_min, llbitsize_of_real};
22+
use machine::{llalign_of_min, llbitsize_of_real, llsize_of_store};
2323
use meth;
2424
use type_of;
2525
use glue;
@@ -32,6 +32,8 @@ use super::lvalue::{LvalueRef, load_fat_ptr};
3232
use super::operand::OperandRef;
3333
use super::operand::OperandValue::{self, FatPtr, Immediate, Ref};
3434

35+
use std::cmp;
36+
3537
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
3638
pub fn trans_block(&mut self, bb: mir::BasicBlock) {
3739
debug!("trans_block({:?})", bb);
@@ -685,7 +687,43 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
685687

686688
match dest {
687689
Nothing => (),
688-
Store(dst) => ret_ty.store(bcx, op.immediate(), dst),
690+
Store(dst) => {
691+
if let Some(llcast_ty) = ret_ty.cast {
692+
let ccx = bcx.ccx();
693+
// The actual return type is a struct, but the ABI
694+
// adaptation code has cast it into some scalar type. The
695+
// code that follows is the only reliable way I have
696+
// found to do a transform like i64 -> {i32,i32}.
697+
// Basically we dump the data onto the stack then memcpy it.
698+
//
699+
// Other approaches I tried:
700+
// - Casting rust ret pointer to the foreign type and using Store
701+
// is (a) unsafe if size of foreign type > size of rust type and
702+
// (b) runs afoul of strict aliasing rules, yielding invalid
703+
// assembly under -O (specifically, the store gets removed).
704+
// - Truncating foreign type to correct integral type and then
705+
// bitcasting to the struct type yields invalid cast errors.
706+
707+
// We instead thus allocate some scratch space...
708+
let llscratch = bcx.alloca(llcast_ty, "fn_ret_cast");
709+
bcx.with_block(|bcx| base::call_lifetime_start(bcx, llscratch));
710+
711+
// ...where we first store the value...
712+
bcx.store(op.immediate(), llscratch);
713+
714+
// ...and then memcpy it to the intended destination.
715+
base::call_memcpy(bcx,
716+
bcx.pointercast(dst, Type::i8p(ccx)),
717+
bcx.pointercast(llscratch, Type::i8p(ccx)),
718+
C_uint(ccx, llsize_of_store(ccx, ret_ty.original_ty)),
719+
cmp::min(llalign_of_min(ccx, ret_ty.original_ty),
720+
llalign_of_min(ccx, llcast_ty)) as u32);
721+
722+
bcx.with_block(|bcx| base::call_lifetime_end(bcx, llscratch));
723+
} else {
724+
ret_ty.store(bcx, op.immediate(), dst);
725+
}
726+
}
689727
IndirectOperand(tmp, idx) => {
690728
let op = self.trans_load(bcx, tmp, op.ty);
691729
self.temps[idx as usize] = TempRef::Operand(Some(op));

src/test/run-pass/mir_cast_fn_ret.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(rustc_attrs)]
12+
13+
pub extern "C" fn foo() -> (u8, u8, u8) {
14+
(1, 2, 3)
15+
}
16+
17+
#[rustc_mir]
18+
pub fn bar() -> u8 {
19+
foo().2
20+
}
21+
22+
fn main() {
23+
assert_eq!(bar(), 3);
24+
}

0 commit comments

Comments
 (0)