diff --git a/src/librustc_trans/trans/mir/analyze.rs b/src/librustc_trans/trans/mir/analyze.rs new file mode 100644 index 00000000000..acf6e53468e --- /dev/null +++ b/src/librustc_trans/trans/mir/analyze.rs @@ -0,0 +1,115 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! An analysis to determine which temporaries require allocas and +//! which do not. + +use rustc_data_structures::fnv::FnvHashSet; +use rustc_mir::repr as mir; +use rustc_mir::visit::{Visitor, LvalueContext}; +use trans::common::{self, Block}; +use super::rvalue; + +pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>, + mir: &mir::Mir<'tcx>) + -> FnvHashSet { + let mut analyzer = TempAnalyzer::new(); + + analyzer.visit_mir(mir); + + for (index, temp_decl) in mir.temp_decls.iter().enumerate() { + let ty = bcx.monomorphize(&temp_decl.ty); + debug!("temp {:?} has type {:?}", index, ty); + if + ty.is_scalar() || + ty.is_unique() || + ty.is_region_ptr() || + ty.is_simd() + { + // These sorts of types are immediates that we can store + // in an ValueRef without an alloca. + assert!(common::type_is_immediate(bcx.ccx(), ty)); + } else { + // These sorts of types require an alloca. Note that + // type_is_immediate() may *still* be true, particularly + // for newtypes, but we currently force some types + // (e.g. structs) into an alloca unconditionally, just so + // that we don't have to deal with having two pathways + // (gep vs getvalue etc). + analyzer.mark_as_lvalue(index); + } + } + + analyzer.lvalue_temps +} + +struct TempAnalyzer { + lvalue_temps: FnvHashSet, +} + +impl TempAnalyzer { + fn new() -> TempAnalyzer { + TempAnalyzer { lvalue_temps: FnvHashSet() } + } + + fn mark_as_lvalue(&mut self, temp: usize) { + debug!("marking temp {} as lvalue", temp); + self.lvalue_temps.insert(temp); + } +} + +impl<'tcx> Visitor<'tcx> for TempAnalyzer { + fn visit_assign(&mut self, + block: mir::BasicBlock, + lvalue: &mir::Lvalue<'tcx>, + rvalue: &mir::Rvalue<'tcx>) { + debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue); + + match *lvalue { + mir::Lvalue::Temp(index) => { + if !rvalue::rvalue_creates_operand(rvalue) { + self.mark_as_lvalue(index as usize); + } + } + _ => { + self.visit_lvalue(lvalue, LvalueContext::Store); + } + } + + self.visit_rvalue(rvalue); + } + + fn visit_lvalue(&mut self, + lvalue: &mir::Lvalue<'tcx>, + context: LvalueContext) { + debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context); + + match *lvalue { + mir::Lvalue::Temp(index) => { + match context { + LvalueContext::Consume => { + } + LvalueContext::Store | + LvalueContext::Drop | + LvalueContext::Inspect | + LvalueContext::Borrow { .. } | + LvalueContext::Slice { .. } | + LvalueContext::Projection => { + self.mark_as_lvalue(index as usize); + } + } + } + _ => { + } + } + + self.super_lvalue(lvalue, context); + } +} diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 760018b4313..353ac2e5f79 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -10,7 +10,6 @@ use libc::c_uint; use llvm::{self, ValueRef}; -use rustc_data_structures::fnv::FnvHashSet; use rustc_mir::repr as mir; use rustc_mir::tcx::LvalueTy; use trans::base; @@ -79,7 +78,7 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { // Analyze the temps to determine which must be lvalues // FIXME - let lvalue_temps: FnvHashSet = (0..mir.temp_decls.len()).collect(); + let lvalue_temps = analyze::lvalue_temps(bcx, mir); // Allocate variable and temp allocas let vars = mir.var_decls.iter() @@ -183,6 +182,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>, .collect() } +mod analyze; mod block; mod constant; mod lvalue; diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs index 786b84ae807..a0308032ac0 100644 --- a/src/librustc_trans/trans/mir/operand.rs +++ b/src/librustc_trans/trans/mir/operand.rs @@ -16,8 +16,9 @@ use trans::build; use trans::common::Block; use trans::datum; -use super::MirContext; +use super::{MirContext, TempRef}; +#[derive(Copy, Clone)] pub struct OperandRef<'tcx> { // This will be "indirect" if `appropriate_rvalue_mode` returns // ByRef, and otherwise ByValue. @@ -37,6 +38,25 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { match *operand { mir::Operand::Consume(ref lvalue) => { + // watch out for temporaries that do not have an + // alloca; they are handled somewhat differently + if let &mir::Lvalue::Temp(index) = lvalue { + match self.temps[index as usize] { + TempRef::Operand(Some(o)) => { + return o; + } + TempRef::Operand(None) => { + bcx.tcx().sess.bug( + &format!("use of {:?} before def", lvalue)); + } + TempRef::Lvalue(..) => { + // use path below + } + } + } + + // for most lvalues, to consume them we just load them + // out from their home let tr_lvalue = self.trans_lvalue(bcx, lvalue); let ty = tr_lvalue.ty.to_ty(bcx.tcx()); debug!("trans_operand: tr_lvalue={} @ {:?}", diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index c82726fd0e5..7c0912592b6 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -80,7 +80,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } _ => { - assert!(self.rvalue_creates_operand(rvalue)); + assert!(rvalue_creates_operand(rvalue)); let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); build::Store(bcx, temp.llval, lldest); bcx @@ -88,32 +88,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool { - match *rvalue { - mir::Rvalue::Use(..) | // (*) - mir::Rvalue::Ref(..) | - mir::Rvalue::Len(..) | - mir::Rvalue::Cast(..) | // (*) - mir::Rvalue::BinaryOp(..) | - mir::Rvalue::UnaryOp(..) | - mir::Rvalue::Box(..) => - true, - mir::Rvalue::Repeat(..) | - mir::Rvalue::Aggregate(..) | - mir::Rvalue::Slice { .. } | - mir::Rvalue::InlineAsm(..) => - false, - } - - // (*) this is only true if the type is suitable - } - pub fn trans_rvalue_operand(&mut self, bcx: Block<'bcx, 'tcx>, rvalue: &mir::Rvalue<'tcx>) -> (Block<'bcx, 'tcx>, OperandRef<'tcx>) { - assert!(self.rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); + assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); match *rvalue { mir::Rvalue::Use(ref operand) => { @@ -300,3 +280,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } } + +pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool { + match *rvalue { + mir::Rvalue::Use(..) | // (*) + mir::Rvalue::Ref(..) | + mir::Rvalue::Len(..) | + mir::Rvalue::Cast(..) | // (*) + mir::Rvalue::BinaryOp(..) | + mir::Rvalue::UnaryOp(..) | + mir::Rvalue::Box(..) => + true, + mir::Rvalue::Repeat(..) | + mir::Rvalue::Aggregate(..) | + mir::Rvalue::Slice { .. } | + mir::Rvalue::InlineAsm(..) => + false, + } + + // (*) this is only true if the type is suitable +}