Handle operand temps for function calls

This allows temporary destinations for function calls to have their
allocas omitted.
This commit is contained in:
James Miller 2016-04-04 19:21:27 +12:00
parent 9c2186d4d7
commit e4d4fa3295
5 changed files with 191 additions and 45 deletions

View File

@ -45,8 +45,9 @@ impl fmt::Debug for CodeExtent {
ty::tls::with_opt(|opt_tcx| {
if let Some(tcx) = opt_tcx {
let data = tcx.region_maps.code_extents.borrow()[self.0 as usize];
write!(f, "/{:?}", data)?;
if let Some(data) = tcx.region_maps.code_extents.borrow().get(self.0 as usize) {
write!(f, "/{:?}", data)?;
}
}
Ok(())
})?;

View File

@ -407,7 +407,7 @@ macro_rules! make_mir_visitor {
self.visit_operand(arg);
}
if let Some((ref $($mutability)* destination, target)) = *destination {
self.visit_lvalue(destination, LvalueContext::Store);
self.visit_lvalue(destination, LvalueContext::Call);
self.visit_branch(block, target);
}
cleanup.map(|t| self.visit_branch(block, t));
@ -692,9 +692,12 @@ make_mir_visitor!(MutVisitor,mut);
#[derive(Copy, Clone, Debug)]
pub enum LvalueContext {
// Appears as LHS of an assignment or as dest of a call
// Appears as LHS of an assignment
Store,
// Dest of a call
Call,
// Being dropped
Drop,

View File

@ -105,6 +105,7 @@ impl<'tcx> Visitor<'tcx> for TempAnalyzer {
match *lvalue {
mir::Lvalue::Temp(index) => {
match context {
LvalueContext::Call |
LvalueContext::Consume => {
}
LvalueContext::Store |

View File

@ -11,7 +11,7 @@
use llvm::{self, BasicBlockRef, ValueRef, OperandBundleDef};
use rustc::ty;
use rustc::mir::repr as mir;
use abi::{Abi, FnType};
use abi::{Abi, FnType, ArgType};
use adt;
use base;
use build;
@ -25,7 +25,7 @@ use type_of;
use glue;
use type_::Type;
use super::{MirContext, drop};
use super::{MirContext, TempRef, drop};
use super::lvalue::{LvalueRef, load_fat_ptr};
use super::operand::OperandRef;
use super::operand::OperandValue::{self, FatPtr, Immediate, Ref};
@ -191,25 +191,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
if intrinsic == Some("transmute") {
let &(ref dest, target) = destination.as_ref().unwrap();
let dst = self.trans_lvalue(&bcx, dest);
let mut val = self.trans_operand(&bcx, &args[0]);
if let ty::TyFnDef(def_id, substs, _) = val.ty.sty {
let llouttype = type_of::type_of(bcx.ccx(), dst.ty.to_ty(bcx.tcx()));
let out_type_size = llbitsize_of_real(bcx.ccx(), llouttype);
if out_type_size != 0 {
// FIXME #19925 Remove this hack after a release cycle.
let f = Callee::def(bcx.ccx(), def_id, substs);
let datum = f.reify(bcx.ccx());
val = OperandRef {
val: OperandValue::Immediate(datum.val),
ty: datum.ty
};
}
}
self.with_lvalue_ref(&bcx, dest, |this, dest| {
this.trans_transmute(&bcx, &args[0], dest);
});
let llty = type_of::type_of(bcx.ccx(), val.ty);
let cast_ptr = bcx.pointercast(dst.llval, llty.ptr_to());
self.store_operand(&bcx, cast_ptr, val);
self.set_operand_dropped(&bcx, &args[0]);
funclet_br(bcx, self.llblock(target));
return;
@ -227,17 +212,71 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// Prepare the return value destination
let ret_dest = if let Some((ref d, _)) = *destination {
let dest = self.trans_lvalue(&bcx, d);
if fn_ty.ret.is_indirect() {
llargs.push(dest.llval);
None
} else if fn_ty.ret.is_ignore() {
None
} else {
Some(dest)
match *d {
// Handle temporary lvalues, specifically Operand ones, as
// they don't have allocas
mir::Lvalue::Temp(idx) => {
let lvalue_ty = self.mir.lvalue_ty(bcx.tcx(), d);
let ret_ty = lvalue_ty.to_ty(bcx.tcx());
match self.temps[idx as usize] {
TempRef::Lvalue(dest) => {
if fn_ty.ret.is_indirect() {
llargs.push(dest.llval);
ReturnDest::Nothing
} else if fn_ty.ret.is_ignore() {
ReturnDest::Nothing
} else {
ReturnDest::Store(dest.llval)
}
}
TempRef::Operand(None) => {
let is_intrinsic = if let Intrinsic = callee.data {
true
} else {
false
};
if fn_ty.ret.is_indirect() {
// Odd, but possible, case, we have an operand temporary,
// but the calling convention has an indirect return.
let tmp = bcx.with_block(|bcx| {
base::alloc_ty(bcx, ret_ty, "tmp_ret")
});
llargs.push(tmp);
ReturnDest::IndirectOperand(tmp, idx)
} else if is_intrinsic {
// Currently, intrinsics always need a location to store
// the result. so we create a temporary alloca for the
// result
let tmp = bcx.with_block(|bcx| {
base::alloc_ty(bcx, ret_ty, "tmp_ret")
});
ReturnDest::IndirectOperand(tmp, idx)
} else if fn_ty.ret.is_ignore() {
ReturnDest::Nothing
} else {
ReturnDest::DirectOperand(idx)
}
}
TempRef::Operand(Some(_)) => {
bug!("lvalue temp already assigned to");
}
}
}
_ => {
let dest = self.trans_lvalue(&bcx, d);
if fn_ty.ret.is_indirect() {
llargs.push(dest.llval);
ReturnDest::Nothing
} else if fn_ty.ret.is_ignore() {
ReturnDest::Nothing
} else {
ReturnDest::Store(dest.llval)
}
}
}
} else {
None
ReturnDest::Nothing
};
// Split the rust-call tupled arguments off.
@ -269,12 +308,15 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
use expr::{Ignore, SaveIn};
use intrinsic::trans_intrinsic_call;
let (dest, llargs) = if fn_ty.ret.is_indirect() {
(SaveIn(llargs[0]), &llargs[1..])
} else if let Some(dest) = ret_dest {
(SaveIn(dest.llval), &llargs[..])
} else {
(Ignore, &llargs[..])
let (dest, llargs) = match ret_dest {
_ if fn_ty.ret.is_indirect() => {
(SaveIn(llargs[0]), &llargs[1..])
}
ReturnDest::Nothing => (Ignore, &llargs[..]),
ReturnDest::IndirectOperand(dst, _) |
ReturnDest::Store(dst) => (SaveIn(dst), &llargs[..]),
ReturnDest::DirectOperand(_) =>
bug!("Cannot use direct operand with an intrinsic call")
};
bcx.with_block(|bcx| {
@ -292,6 +334,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// bcx.unreachable();
}
});
if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
// Make a fake operand for store_return
let op = OperandRef {
val: OperandValue::Ref(dst),
ty: sig.0.output.unwrap()
};
self.store_return(&bcx, ret_dest, fn_ty.ret, op);
}
return;
}
Fn(f) => f,
@ -321,9 +373,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
if destination.is_some() {
let ret_bcx = ret_bcx.build();
ret_bcx.at_start(|ret_bcx| {
if let Some(ret_dest) = ret_dest {
fn_ty.ret.store(&ret_bcx, invokeret, ret_dest.llval);
}
let op = OperandRef {
val: OperandValue::Immediate(invokeret),
ty: sig.0.output.unwrap()
};
self.store_return(&ret_bcx, ret_dest, fn_ty.ret, op);
for op in args {
self.set_operand_dropped(&ret_bcx, op);
}
@ -333,9 +387,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle.as_ref());
fn_ty.apply_attrs_callsite(llret);
if let Some((_, target)) = *destination {
if let Some(ret_dest) = ret_dest {
fn_ty.ret.store(&bcx, llret, ret_dest.llval);
}
let op = OperandRef {
val: OperandValue::Immediate(llret),
ty: sig.0.output.unwrap()
};
self.store_return(&bcx, ret_dest, fn_ty.ret, op);
for op in args {
self.set_operand_dropped(&bcx, op);
}
@ -544,4 +600,58 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
pub fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef {
self.blocks[bb.index()].llbb
}
fn trans_transmute(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>,
src: &mir::Operand<'tcx>, dst: LvalueRef<'tcx>) {
let mut val = self.trans_operand(bcx, src);
if let ty::TyFnDef(def_id, substs, _) = val.ty.sty {
let llouttype = type_of::type_of(bcx.ccx(), dst.ty.to_ty(bcx.tcx()));
let out_type_size = llbitsize_of_real(bcx.ccx(), llouttype);
if out_type_size != 0 {
// FIXME #19925 Remove this hack after a release cycle.
let f = Callee::def(bcx.ccx(), def_id, substs);
let datum = f.reify(bcx.ccx());
val = OperandRef {
val: OperandValue::Immediate(datum.val),
ty: datum.ty
};
}
}
let llty = type_of::type_of(bcx.ccx(), val.ty);
let cast_ptr = bcx.pointercast(dst.llval, llty.ptr_to());
self.store_operand(bcx, cast_ptr, val);
}
// Stores the return value of a function call into it's final location.
fn store_return(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>,
dest: ReturnDest,
ret_ty: ArgType,
op: OperandRef<'tcx>) {
use self::ReturnDest::*;
match dest {
Nothing => (),
Store(dst) => 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));
}
DirectOperand(idx) => {
self.temps[idx as usize] = TempRef::Operand(Some(op));
}
}
}
}
enum ReturnDest {
// Do nothing, the return value is indirect or ignored
Nothing,
// Store the return value to the pointer
Store(ValueRef),
// Stores an indirect return value to an operand temporary lvalue
IndirectOperand(ValueRef, u32),
// Stores a direct return value to an operand temporary lvalue
DirectOperand(u32)
}

View File

@ -207,6 +207,37 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}
}
// Perform an action using the given Lvalue.
// If the Lvalue is an empty TempRef::Operand, then a temporary stack slot
// is created first, then used as an operand to update the Lvalue.
pub fn with_lvalue_ref<F, U>(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>,
lvalue: &mir::Lvalue<'tcx>, f: F) -> U
where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U
{
match *lvalue {
mir::Lvalue::Temp(idx) => {
match self.temps[idx as usize] {
TempRef::Lvalue(lvalue) => f(self, lvalue),
TempRef::Operand(None) => {
let lvalue_ty = self.mir.lvalue_ty(bcx.tcx(), lvalue);
let lvalue = LvalueRef::alloca(bcx, lvalue_ty.to_ty(bcx.tcx()), "lvalue_temp");
let ret = f(self, lvalue);
let op = self.trans_load(bcx, lvalue.llval, lvalue_ty.to_ty(bcx.tcx()));
self.temps[idx as usize] = TempRef::Operand(Some(op));
ret
}
TempRef::Operand(Some(_)) => {
bug!("Lvalue temp already set");
}
}
}
_ => {
let lvalue = self.trans_lvalue(bcx, lvalue);
f(self, lvalue)
}
}
}
/// Adjust the bitwidth of an index since LLVM is less forgiving
/// than we are.
///