about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2015-11-03 06:35:09 -0500
committerNiko Matsakis <niko@alum.mit.edu>2015-11-03 18:00:30 -0500
commit6a5b263503dce838c2a09619171b409c9f11bb9f (patch)
treec398937eaa8ffe93bc7c3e95b308626cfcdfd0f7
parent544b06d455a067ab54a850e34bdc7794f23c6734 (diff)
downloadrust-6a5b263503dce838c2a09619171b409c9f11bb9f.tar.gz
rust-6a5b263503dce838c2a09619171b409c9f11bb9f.zip
Add (and use) an analysis to determine which temps can forgo an alloca.
-rw-r--r--src/librustc_trans/trans/mir/analyze.rs115
-rw-r--r--src/librustc_trans/trans/mir/mod.rs4
-rw-r--r--src/librustc_trans/trans/mir/operand.rs22
-rw-r--r--src/librustc_trans/trans/mir/rvalue.rs44
4 files changed, 160 insertions, 25 deletions
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 <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.
+
+//! 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<usize> {
+    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<usize>,
+}
+
+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<usize> = (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
+}