about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-11-04 02:13:05 +0000
committerbors <bors@rust-lang.org>2015-11-04 02:13:05 +0000
commita216e847272ddbd3033037b606eaf2d801c250b9 (patch)
treed76ae5cc3c8c4c3f20746ae4d09c1eb67f694a39
parentc340ea1de5dba6d0184103b9d67e290e3ea02981 (diff)
parente78786315b44037258970ee9187f3fd6f89fc61b (diff)
downloadrust-a216e847272ddbd3033037b606eaf2d801c250b9.tar.gz
rust-a216e847272ddbd3033037b606eaf2d801c250b9.zip
Auto merge of #29217 - nikomatsakis:mir-trans, r=dotdash
This branch implements a variant of trans that is based on MIR. It is very incomplete (intentionally), and had only the goal of laying out enough work to enable more incremental follow-on patches. Currently, only fns tagged with `#[rustc_mir]` use the new trans code. I plan to build up a meta-issue as well that tracks the various "not-yet-implemented" points. The only fn that has been tested so far is this amazingly complex "spike" fn:

```rust
#[rustc_mir]
fn sum(x: i32, y: i32) -> i32 {
    x + y
}
```

In general, the most interesting commit is the last one. There are some points on which I would like feedback from @rust-lang/compiler:

- I did not use `Datum`. Originally, I thought that maybe just a `ValueRef` would be enough but I wound up with two very simple structures, `LvalueRef` and `OperandRef`, that just package up a `ValueRef` and a type. Because of MIR's structure, you don't wind up mixing by-ref and by-value so much, and I tend to think that a thinner abstraction layer is better here, but I'm not sure.
- Related to the above, I expect that sooner or later we will analyze temps (and maybe variables too) to find those whose address is never taken and which are word-sized and which perhaps meet a few other criteria. For those, we'll probably want to avoid the alloca, just because it means prettier code.
- I generally tried to re-use data structures from elsewhere in trans, though I'm sure we can trim these down.
- I didn't do any debuginfo primarily because it seems to want node-ids and we have only spans. I haven't really read into that code so I don't know what's going on there.

r? @nrc 
-rw-r--r--mk/crates.mk2
-rw-r--r--src/librustc/middle/ty/context.rs8
-rw-r--r--src/librustc/middle/ty/mod.rs7
-rw-r--r--src/librustc_driver/driver.rs27
-rw-r--r--src/librustc_driver/pretty.rs4
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs11
-rw-r--r--src/librustc_mir/build/expr/into.rs4
-rw-r--r--src/librustc_mir/build/matches/test.rs20
-rw-r--r--src/librustc_mir/build/misc.rs26
-rw-r--r--src/librustc_mir/build/mod.rs20
-rw-r--r--src/librustc_mir/hair/cx/block.rs (renamed from src/librustc_mir/tcx/block.rs)28
-rw-r--r--src/librustc_mir/hair/cx/expr.rs (renamed from src/librustc_mir/tcx/expr.rs)60
-rw-r--r--src/librustc_mir/hair/cx/mod.rs149
-rw-r--r--src/librustc_mir/hair/cx/pattern.rs (renamed from src/librustc_mir/tcx/pattern.rs)37
-rw-r--r--src/librustc_mir/hair/cx/to_ref.rs (renamed from src/librustc_mir/tcx/to_ref.rs)14
-rw-r--r--src/librustc_mir/hair/mod.rs (renamed from src/librustc_mir/hair.rs)8
-rw-r--r--src/librustc_mir/lib.rs6
-rw-r--r--src/librustc_mir/mir_map.rs58
-rw-r--r--src/librustc_mir/repr.rs40
-rw-r--r--src/librustc_mir/tcx/mod.rs227
-rw-r--r--src/librustc_mir/visit.rs239
-rw-r--r--src/librustc_trans/lib.rs3
-rw-r--r--src/librustc_trans/trans/_match.rs2
-rw-r--r--src/librustc_trans/trans/adt.rs10
-rw-r--r--src/librustc_trans/trans/base.rs28
-rw-r--r--src/librustc_trans/trans/common.rs84
-rw-r--r--src/librustc_trans/trans/context.rs8
-rw-r--r--src/librustc_trans/trans/expr.rs61
-rw-r--r--src/librustc_trans/trans/mir/analyze.rs115
-rw-r--r--src/librustc_trans/trans/mir/block.rs106
-rw-r--r--src/librustc_trans/trans/mir/constant.rs63
-rw-r--r--src/librustc_trans/trans/mir/lvalue.rs154
-rw-r--r--src/librustc_trans/trans/mir/mod.rs195
-rw-r--r--src/librustc_trans/trans/mir/operand.rs110
-rw-r--r--src/librustc_trans/trans/mir/rvalue.rs307
-rw-r--r--src/librustc_trans/trans/mir/statement.rs70
-rw-r--r--src/librustc_trans/trans/mod.rs1
-rw-r--r--src/librustc_typeck/check/mod.rs19
-rw-r--r--src/librustc_typeck/check/regionck.rs27
-rw-r--r--src/librustc_typeck/check/writeback.rs23
-rw-r--r--src/librustdoc/core.rs2
-rw-r--r--src/test/run-make/execution-engine/test.rs4
-rw-r--r--src/test/run-pass/mir_trans_spike1.rs24
43 files changed, 2023 insertions, 388 deletions
diff --git a/mk/crates.mk b/mk/crates.mk
index 6673b9b28e7..2a627a6da2b 100644
--- a/mk/crates.mk
+++ b/mk/crates.mk
@@ -103,7 +103,7 @@ DEPS_rustc_mir := rustc rustc_front syntax
 DEPS_rustc_resolve := rustc rustc_front log syntax
 DEPS_rustc_platform_intrinsics := rustc rustc_llvm
 DEPS_rustc_privacy := rustc rustc_front log syntax
-DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back \
+DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back rustc_mir \
                     log syntax serialize rustc_llvm rustc_front rustc_platform_intrinsics
 DEPS_rustc_typeck := rustc syntax rustc_front rustc_platform_intrinsics
 
diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs
index 830232cf373..a8f045074bd 100644
--- a/src/librustc/middle/ty/context.rs
+++ b/src/librustc/middle/ty/context.rs
@@ -121,6 +121,13 @@ pub struct Tables<'tcx> {
     /// Records the type of each closure. The def ID is the ID of the
     /// expression defining the closure.
     pub closure_kinds: DefIdMap<ty::ClosureKind>,
+
+    /// For each fn, records the "liberated" types of its arguments
+    /// and return type. Liberated means that all bound regions
+    /// (including late-bound regions) are replaced with free
+    /// equivalents. This table is not used in trans (since regions
+    /// are erased there) and hence is not serialized to metadata.
+    pub liberated_fn_sigs: NodeMap<ty::FnSig<'tcx>>,
 }
 
 impl<'tcx> Tables<'tcx> {
@@ -133,6 +140,7 @@ impl<'tcx> Tables<'tcx> {
             upvar_capture_map: FnvHashMap(),
             closure_tys: DefIdMap(),
             closure_kinds: DefIdMap(),
+            liberated_fn_sigs: NodeMap(),
         }
     }
 
diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs
index 57363441493..aa189744701 100644
--- a/src/librustc/middle/ty/mod.rs
+++ b/src/librustc/middle/ty/mod.rs
@@ -1732,6 +1732,13 @@ impl<'tcx, 'container> VariantDefData<'tcx, 'container> {
     }
 
     #[inline]
+    pub fn index_of_field_named(&self,
+                                name: ast::Name)
+                                -> Option<usize> {
+        self.fields.iter().position(|f| f.name == name)
+    }
+
+    #[inline]
     pub fn field_named(&self, name: ast::Name) -> &FieldDefData<'tcx, 'container> {
         self.find_field_named(name).unwrap()
     }
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 69c4dcb0a19..06708a5127f 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -11,6 +11,7 @@
 use rustc::front;
 use rustc::front::map as hir_map;
 use rustc_mir as mir;
+use rustc_mir::mir_map::MirMap;
 use rustc::session::Session;
 use rustc::session::config::{self, Input, OutputFilenames, OutputType};
 use rustc::session::search_paths::PathKind;
@@ -22,6 +23,7 @@ use rustc::middle::dependency_format;
 use rustc::middle;
 use rustc::plugin::registry::Registry;
 use rustc::plugin;
+use rustc::util::nodemap::NodeMap;
 use rustc::util::common::time;
 use rustc_borrowck as borrowck;
 use rustc_resolve as resolve;
@@ -146,7 +148,7 @@ pub fn compile_input(sess: Session,
                                     &arenas,
                                     &id,
                                     control.make_glob_map,
-                                    |tcx, analysis| {
+                                    |tcx, mir_map, analysis| {
 
             {
                 let state = CompileState::state_after_analysis(input,
@@ -170,7 +172,7 @@ pub fn compile_input(sess: Session,
                 println!("Pre-trans");
                 tcx.print_debug_stats();
             }
-            let trans = phase_4_translate_to_llvm(tcx, analysis);
+            let trans = phase_4_translate_to_llvm(tcx, &mir_map, analysis);
 
             if log_enabled!(::log::INFO) {
                 println!("Post-trans");
@@ -670,6 +672,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
                                                f: F)
                                                -> R
                                                where F: for<'a> FnOnce(&'a ty::ctxt<'tcx>,
+                                                                       MirMap<'tcx>,
                                                                        ty::CrateAnalysis) -> R
 {
     let time_passes = sess.time_passes();
@@ -751,18 +754,18 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
         time(time_passes, "match checking", ||
             middle::check_match::check_crate(tcx));
 
-        match tcx.sess.opts.unstable_features {
+        let mir_map = match tcx.sess.opts.unstable_features {
             UnstableFeatures::Disallow => {
                 // use this as a shorthand for beta/stable, and skip
                 // MIR construction there until known regressions are
                 // addressed
+                NodeMap()
             }
             UnstableFeatures::Allow | UnstableFeatures::Cheat => {
-                let _mir_map =
-                    time(time_passes, "MIR dump", ||
-                            mir::mir_map::build_mir_for_crate(tcx));
+                time(time_passes, "MIR dump", ||
+                     mir::mir_map::build_mir_for_crate(tcx))
             }
-        }
+        };
 
         time(time_passes, "liveness checking", ||
             middle::liveness::check_crate(tcx));
@@ -804,7 +807,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
         // The above three passes generate errors w/o aborting
         tcx.sess.abort_if_errors();
 
-        f(tcx, ty::CrateAnalysis {
+        f(tcx, mir_map, ty::CrateAnalysis {
             export_map: export_map,
             exported_items: exported_items,
             public_items: public_items,
@@ -817,8 +820,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
 
 /// Run the translation phase to LLVM, after which the AST and analysis can
 /// be discarded.
-pub fn phase_4_translate_to_llvm(tcx: &ty::ctxt, analysis: ty::CrateAnalysis)
-                                 -> trans::CrateTranslation {
+pub fn phase_4_translate_to_llvm<'tcx>(tcx: &ty::ctxt<'tcx>,
+                                       mir_map: &MirMap<'tcx>,
+                                       analysis: ty::CrateAnalysis)
+                                       -> trans::CrateTranslation {
     let time_passes = tcx.sess.time_passes();
 
     time(time_passes, "resolving dependency formats", ||
@@ -826,7 +831,7 @@ pub fn phase_4_translate_to_llvm(tcx: &ty::ctxt, analysis: ty::CrateAnalysis)
 
     // Option dance to work around the lack of stack once closures.
     time(time_passes, "translation", move ||
-         trans::trans_crate(tcx, analysis))
+         trans::trans_crate(tcx, mir_map, analysis))
 }
 
 /// Run LLVM itself, producing a bitcode file, assembly file or object file
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs
index a30c437197c..f53822d2400 100644
--- a/src/librustc_driver/pretty.rs
+++ b/src/librustc_driver/pretty.rs
@@ -182,7 +182,7 @@ impl PpSourceMode {
                                                     arenas,
                                                     id,
                                                     resolve::MakeGlobMap::No,
-                                                    |tcx, _| {
+                                                    |tcx, _, _| {
                     let annotation = TypedAnnotation { tcx: tcx };
                     f(&annotation, payload, &ast_map.forest.krate)
                 })
@@ -782,7 +782,7 @@ pub fn pretty_print_input(sess: Session,
                                                         &arenas,
                                                         &id,
                                                         resolve::MakeGlobMap::No,
-                                                        |tcx, _| {
+                                                        |tcx, _, _| {
                         print_flowgraph(variants, tcx, code, mode, out)
                     })
                 }
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index 3cfc51ec3c9..23ca22129fd 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -71,7 +71,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
 
                 // schedule a shallow free of that memory, lest we unwind:
                 let extent = this.extent_of_innermost_scope().unwrap();
-                this.schedule_drop(expr_span, extent, DropKind::Shallow, &result, value_ty);
+                this.schedule_drop(expr_span, extent, DropKind::Free, &result, value_ty);
 
                 // initialize the box contents:
                 let contents = result.clone().deref();
@@ -149,16 +149,19 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 block.and(Rvalue::Aggregate(AggregateKind::Closure(closure_id, substs), upvars))
             }
             ExprKind::Adt { adt_def, variant_index, substs, fields, base } => { // see (*) above
-                // first process the set of fields
+                // first process the set of fields that were provided
+                // (evaluating them in order given by user)
                 let fields_map: FnvHashMap<_, _> =
                     fields.into_iter()
                           .map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr))))
                           .collect();
 
-                let field_names = this.hir.fields(adt_def, variant_index);
-
+                // if base expression is given, evaluate it now
                 let base = base.map(|base| unpack!(block = this.as_lvalue(block, base)));
 
+                // get list of all fields that we will need
+                let field_names = this.hir.all_fields(adt_def, variant_index);
+
                 // for the actual values we use, take either the
                 // expr the user specified or, if they didn't
                 // specify something for this field name, create a
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index a7d68b09b54..57c6db79c52 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -211,10 +211,10 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 this.cfg.start_new_block().unit()
             }
             ExprKind::Call { fun, args } => {
-                let fun = unpack!(block = this.as_lvalue(block, fun));
+                let fun = unpack!(block = this.as_operand(block, fun));
                 let args: Vec<_> =
                     args.into_iter()
-                        .map(|arg| unpack!(block = this.as_lvalue(block, arg)))
+                        .map(|arg| unpack!(block = this.as_operand(block, arg)))
                         .collect();
                 let success = this.cfg.start_new_block();
                 let panic = this.diverge_cleanup();
diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs
index d5745eb28c7..e035f53dacf 100644
--- a/src/librustc_mir/build/matches/test.rs
+++ b/src/librustc_mir/build/matches/test.rs
@@ -92,6 +92,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                                           .collect();
                 self.cfg.terminate(block, Terminator::Switch {
                     discr: lvalue.clone(),
+                    adt_def: adt_def,
                     targets: target_blocks.clone()
                 });
                 target_blocks
@@ -99,27 +100,28 @@ impl<'a,'tcx> Builder<'a,'tcx> {
 
             TestKind::Eq { value, ty } => {
                 // call PartialEq::eq(discrim, constant)
-                let constant = self.push_literal(block, test.span, ty.clone(), value);
+                let constant = self.literal_operand(test.span, ty.clone(), value);
                 let item_ref = self.hir.partial_eq(ty);
-                self.call_comparison_fn(block, test.span, item_ref, lvalue.clone(), constant)
+                self.call_comparison_fn(block, test.span, item_ref,
+                                        Operand::Consume(lvalue.clone()), constant)
             }
 
             TestKind::Range { lo, hi, ty } => {
                 // Test `v` by computing `PartialOrd::le(lo, v) && PartialOrd::le(v, hi)`.
-                let lo = self.push_literal(block, test.span, ty.clone(), lo);
-                let hi = self.push_literal(block, test.span, ty.clone(), hi);
+                let lo = self.literal_operand(test.span, ty.clone(), lo);
+                let hi = self.literal_operand(test.span, ty.clone(), hi);
                 let item_ref = self.hir.partial_le(ty);
 
                 let lo_blocks = self.call_comparison_fn(block,
                                                         test.span,
                                                         item_ref.clone(),
                                                         lo,
-                                                        lvalue.clone());
+                                                        Operand::Consume(lvalue.clone()));
 
                 let hi_blocks = self.call_comparison_fn(lo_blocks[0],
                                                         test.span,
                                                         item_ref,
-                                                        lvalue.clone(),
+                                                        Operand::Consume(lvalue.clone()),
                                                         hi);
 
                 let failure = self.cfg.start_new_block();
@@ -164,14 +166,14 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                           block: BasicBlock,
                           span: Span,
                           item_ref: ItemRef<'tcx>,
-                          lvalue1: Lvalue<'tcx>,
-                          lvalue2: Lvalue<'tcx>)
+                          lvalue1: Operand<'tcx>,
+                          lvalue2: Operand<'tcx>)
                           -> Vec<BasicBlock> {
         let target_blocks = vec![self.cfg.start_new_block(), self.cfg.start_new_block()];
 
         let bool_ty = self.hir.bool_ty();
         let eq_result = self.temp(bool_ty);
-        let func = self.push_item_ref(block, span, item_ref);
+        let func = self.item_ref_operand(span, item_ref);
         let call_blocks = [self.cfg.start_new_block(), self.diverge_cleanup()];
         self.cfg.terminate(block,
                            Terminator::Call {
diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs
index 86b6df19b77..41274f3f373 100644
--- a/src/librustc_mir/build/misc.rs
+++ b/src/librustc_mir/build/misc.rs
@@ -34,20 +34,17 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         lvalue
     }
 
-    pub fn push_literal(&mut self,
-                        block: BasicBlock,
-                        span: Span,
-                        ty: Ty<'tcx>,
-                        literal: Literal<'tcx>)
-                        -> Lvalue<'tcx> {
-        let temp = self.temp(ty.clone());
+    pub fn literal_operand(&mut self,
+                           span: Span,
+                           ty: Ty<'tcx>,
+                           literal: Literal<'tcx>)
+                           -> Operand<'tcx> {
         let constant = Constant {
             span: span,
             ty: ty,
             literal: literal,
         };
-        self.cfg.push_assign_constant(block, span, &temp, constant);
-        temp
+        Operand::Constant(constant)
     }
 
     pub fn push_usize(&mut self, block: BasicBlock, span: Span, value: usize) -> Lvalue<'tcx> {
@@ -63,15 +60,14 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         temp
     }
 
-    pub fn push_item_ref(&mut self,
-                         block: BasicBlock,
-                         span: Span,
-                         item_ref: ItemRef<'tcx>)
-                         -> Lvalue<'tcx> {
+    pub fn item_ref_operand(&mut self,
+                            span: Span,
+                            item_ref: ItemRef<'tcx>)
+                            -> Operand<'tcx> {
         let literal = Literal::Item {
             def_id: item_ref.def_id,
             substs: item_ref.substs,
         };
-        self.push_literal(block, span, item_ref.ty, literal)
+        self.literal_operand(span, item_ref.ty, literal)
     }
 }
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 857540e2109..56a20167b79 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -9,14 +9,14 @@
 // except according to those terms.
 
 use hair;
+use hair::cx::{Cx, PatNode};
 use rustc::middle::region::CodeExtent;
-use rustc::middle::ty::Ty;
+use rustc::middle::ty::{FnOutput, Ty};
 use rustc_data_structures::fnv::FnvHashMap;
 use rustc_front::hir;
 use repr::*;
 use syntax::ast;
 use syntax::codemap::Span;
-use tcx::{Cx, PatNode};
 
 struct Builder<'a, 'tcx: 'a> {
     hir: Cx<'a, 'tcx>,
@@ -75,13 +75,14 @@ macro_rules! unpack {
 ///////////////////////////////////////////////////////////////////////////
 // construct() -- the main entry point for building MIR for a function
 
-pub fn construct<'a, 'tcx>(mut hir: Cx<'a, 'tcx>,
-                           _span: Span,
-                           implicit_arguments: Vec<Ty<'tcx>>,
-                           explicit_arguments: Vec<(Ty<'tcx>, PatNode<'tcx>)>,
-                           argument_extent: CodeExtent,
-                           ast_block: &'tcx hir::Block)
-                           -> Mir<'tcx> {
+pub fn construct<'a,'tcx>(mut hir: Cx<'a,'tcx>,
+                          _span: Span,
+                          implicit_arguments: Vec<Ty<'tcx>>,
+                          explicit_arguments: Vec<(Ty<'tcx>, PatNode<'tcx>)>,
+                          argument_extent: CodeExtent,
+                          return_ty: FnOutput<'tcx>,
+                          ast_block: &'tcx hir::Block)
+                          -> Mir<'tcx> {
     let cfg = CFG { basic_blocks: vec![] };
 
     // it's handy to have a temporary of type `()` sometimes, so make
@@ -121,6 +122,7 @@ pub fn construct<'a, 'tcx>(mut hir: Cx<'a, 'tcx>,
         var_decls: builder.var_decls,
         arg_decls: arg_decls,
         temp_decls: builder.temp_decls,
+        return_ty: return_ty,
     }
 }
 
diff --git a/src/librustc_mir/tcx/block.rs b/src/librustc_mir/hair/cx/block.rs
index dc168bc7c2b..a407c42372a 100644
--- a/src/librustc_mir/tcx/block.rs
+++ b/src/librustc_mir/hair/cx/block.rs
@@ -9,10 +9,9 @@
 // except according to those terms.
 
 use hair::*;
-
-use tcx::Cx;
-use tcx::pattern::PatNode;
-use tcx::to_ref::ToRef;
+use hair::cx::Cx;
+use hair::cx::pattern::PatNode;
+use hair::cx::to_ref::ToRef;
 use rustc::middle::region::{BlockRemainder, CodeExtentData};
 use rustc_front::hir;
 use syntax::ast;
@@ -34,22 +33,11 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block {
     }
 }
 
-impl<'tcx> Mirror<'tcx> for &'tcx hir::Stmt {
-    type Output = Stmt<'tcx>;
-
-    fn make_mirror<'a>(self, _cx: &mut Cx<'a, 'tcx>) -> Stmt<'tcx> {
-        // In order to get the scoping correct, we eagerly mirror
-        // statements when we translate the enclosing block, so we
-        // should in fact never get to this point.
-        panic!("statements are eagerly mirrored");
-    }
-}
-
-fn mirror_stmts<'a, 'tcx: 'a, STMTS>(cx: &mut Cx<'a, 'tcx>,
-                                     block_id: ast::NodeId,
-                                     mut stmts: STMTS)
-                                     -> Vec<StmtRef<'tcx>>
-    where STMTS: Iterator<Item = (usize, &'tcx P<hir::Stmt>)>
+fn mirror_stmts<'a,'tcx:'a,STMTS>(cx: &mut Cx<'a,'tcx>,
+                                  block_id: ast::NodeId,
+                                  mut stmts: STMTS)
+                                  -> Vec<StmtRef<'tcx>>
+    where STMTS: Iterator<Item=(usize, &'tcx P<hir::Stmt>)>
 {
     let mut result = vec![];
     while let Some((index, stmt)) = stmts.next() {
diff --git a/src/librustc_mir/tcx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 9f7ecf52287..847d76f7d17 100644
--- a/src/librustc_mir/tcx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -12,16 +12,16 @@ use hair::*;
 use repr::*;
 use rustc_data_structures::fnv::FnvHashMap;
 use std::rc::Rc;
-use tcx::Cx;
-use tcx::block;
-use tcx::pattern::PatNode;
-use tcx::to_ref::ToRef;
+use hair::cx::Cx;
+use hair::cx::block;
+use hair::cx::pattern::PatNode;
+use hair::cx::to_ref::ToRef;
 use rustc::front::map;
 use rustc::middle::const_eval;
 use rustc::middle::def;
 use rustc::middle::region::CodeExtent;
 use rustc::middle::pat_util;
-use rustc::middle::ty::{self, Ty};
+use rustc::middle::ty::{self, VariantDef, Ty};
 use rustc_front::hir;
 use rustc_front::util as hir_util;
 use syntax::parse::token;
@@ -170,11 +170,12 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
             hir::ExprStruct(_, ref fields, ref base) => {
                 match expr_ty.sty {
                     ty::TyStruct(adt, substs) => {
+                        let field_refs = field_refs(&adt.variants[0], fields);
                         ExprKind::Adt {
                             adt_def: adt,
                             variant_index: 0,
                             substs: substs,
-                            fields: fields.to_ref(),
+                            fields: field_refs,
                             base: base.to_ref(),
                         }
                     }
@@ -183,11 +184,12 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                             def::DefVariant(enum_id, variant_id, true) => {
                                 debug_assert!(adt.did == enum_id);
                                 let index = adt.variant_index_with_id(variant_id);
+                                let field_refs = field_refs(&adt.variants[index], fields);
                                 ExprKind::Adt {
                                     adt_def: adt,
                                     variant_index: index,
                                     substs: substs,
-                                    fields: fields.to_ref(),
+                                    fields: field_refs,
                                     base: base.to_ref(),
                                 }
                             }
@@ -238,11 +240,10 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                     }
                 };
 
-                let field_expr_ref = |s: &'tcx P<hir::Expr>, nm: &str| {
-                    FieldExprRef {
-                        name: Field::Named(token::intern(nm)),
-                        expr: s.to_ref(),
-                    }
+                let field_expr_ref = |s: &'tcx P<hir::Expr>, name: &str| {
+                    let name = token::intern(name);
+                    let index = adt_def.variants[0].index_of_field_named(name).unwrap();
+                    FieldExprRef { name: Field::new(index), expr: s.to_ref() }
                 };
 
                 let start_field = start.as_ref()
@@ -293,12 +294,25 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
             hir::ExprLoop(ref body, _) =>
                 ExprKind::Loop { condition: None,
                                  body: block::to_expr_ref(cx, body) },
-            hir::ExprField(ref source, name) =>
-                ExprKind::Field { lhs: source.to_ref(),
-                                  name: Field::Named(name.node) },
+            hir::ExprField(ref source, name) => {
+                let index = match cx.tcx.expr_ty_adjusted(source).sty {
+                    ty::TyStruct(adt_def, _) =>
+                        adt_def.variants[0].index_of_field_named(name.node),
+                    ref ty =>
+                        cx.tcx.sess.span_bug(
+                            self.span,
+                            &format!("field of non-struct: {:?}", ty)),
+                };
+                let index = index.unwrap_or_else(|| {
+                    cx.tcx.sess.span_bug(
+                        self.span,
+                        &format!("no index found for field `{}`", name.node));
+                });
+                ExprKind::Field { lhs: source.to_ref(), name: Field::new(index) }
+            }
             hir::ExprTupField(ref source, index) =>
                 ExprKind::Field { lhs: source.to_ref(),
-                                  name: Field::Indexed(index.node) },
+                                  name: Field::new(index.node as usize) },
             hir::ExprCast(ref source, _) =>
                 ExprKind::Cast { source: source.to_ref() },
             hir::ExprBox(ref value) =>
@@ -616,7 +630,7 @@ fn convert_var<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>,
             // at this point we have `self.n`, which loads up the upvar
             let field_kind = ExprKind::Field {
                 lhs: self_expr.to_ref(),
-                name: Field::Indexed(index),
+                name: Field::new(index),
             };
 
             // ...but the upvar might be an `&T` or `&mut T` capture, at which
@@ -814,3 +828,15 @@ fn loop_label<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr) -> Cod
         }
     }
 }
+
+fn field_refs<'tcx>(variant: VariantDef<'tcx>,
+                    fields: &'tcx [hir::Field])
+                    -> Vec<FieldExprRef<'tcx>>
+{
+    fields.iter()
+          .map(|field| FieldExprRef {
+              name: Field::new(variant.index_of_field_named(field.name.node).unwrap()),
+              expr: field.expr.to_ref(),
+          })
+          .collect()
+}
diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs
new file mode 100644
index 00000000000..8d4b05afcb6
--- /dev/null
+++ b/src/librustc_mir/hair/cx/mod.rs
@@ -0,0 +1,149 @@
+// Copyright 2015 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.
+
+/*!
+ * This module contains the code to convert from the wacky tcx data
+ * structures into the hair. The `builder` is generally ignorant of
+ * the tcx etc, and instead goes through the `Cx` for most of its
+ * work.
+ */
+
+use hair::*;
+use repr::*;
+
+use rustc::middle::const_eval::ConstVal;
+use rustc::middle::def_id::DefId;
+use rustc::middle::infer::InferCtxt;
+use rustc::middle::subst::{Subst, Substs};
+use rustc::middle::ty::{self, Ty};
+use syntax::codemap::Span;
+use syntax::parse::token;
+
+#[derive(Copy, Clone)]
+pub struct Cx<'a, 'tcx: 'a> {
+    tcx: &'a ty::ctxt<'tcx>,
+    infcx: &'a InferCtxt<'a, 'tcx>,
+}
+
+impl<'a,'tcx> Cx<'a,'tcx> {
+    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Cx<'a, 'tcx> {
+        Cx {
+            tcx: infcx.tcx,
+            infcx: infcx,
+        }
+    }
+}
+
+pub use self::pattern::PatNode;
+
+impl<'a,'tcx:'a> Cx<'a, 'tcx> {
+    /// Normalizes `ast` into the appropriate `mirror` type.
+    pub fn mirror<M: Mirror<'tcx>>(&mut self, ast: M) -> M::Output {
+        ast.make_mirror(self)
+    }
+
+    pub fn unit_ty(&mut self) -> Ty<'tcx> {
+        self.tcx.mk_nil()
+    }
+
+    pub fn usize_ty(&mut self) -> Ty<'tcx> {
+        self.tcx.types.usize
+    }
+
+    pub fn usize_literal(&mut self, value: usize) -> Literal<'tcx> {
+        Literal::Value { value: ConstVal::Uint(value as u64) }
+    }
+
+    pub fn bool_ty(&mut self) -> Ty<'tcx> {
+        self.tcx.types.bool
+    }
+
+    pub fn true_literal(&mut self) -> Literal<'tcx> {
+        Literal::Value { value: ConstVal::Bool(true) }
+    }
+
+    pub fn false_literal(&mut self) -> Literal<'tcx> {
+        Literal::Value { value: ConstVal::Bool(false) }
+    }
+
+    pub fn partial_eq(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> {
+        let eq_def_id = self.tcx.lang_items.eq_trait().unwrap();
+        self.cmp_method_ref(eq_def_id, "eq", ty)
+    }
+
+    pub fn partial_le(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> {
+        let ord_def_id = self.tcx.lang_items.ord_trait().unwrap();
+        self.cmp_method_ref(ord_def_id, "le", ty)
+    }
+
+    pub fn num_variants(&mut self, adt_def: ty::AdtDef<'tcx>) -> usize {
+        adt_def.variants.len()
+    }
+
+    pub fn all_fields(&mut self, adt_def: ty::AdtDef<'tcx>, variant_index: usize) -> Vec<Field> {
+        (0..adt_def.variants[variant_index].fields.len())
+            .map(Field::new)
+            .collect()
+    }
+
+    pub fn needs_drop(&mut self, ty: Ty<'tcx>, span: Span) -> bool {
+        if self.infcx.type_moves_by_default(ty, span) {
+            // FIXME(#21859) we should do an add'l check here to determine if
+            // any dtor will execute, but the relevant fn
+            // (`type_needs_drop`) is currently factored into
+            // `librustc_trans`, so we can't easily do so.
+            true
+        } else {
+            // if type implements Copy, cannot require drop
+            false
+        }
+    }
+
+    pub fn span_bug(&mut self, span: Span, message: &str) -> ! {
+        self.tcx.sess.span_bug(span, message)
+    }
+
+    pub fn tcx(&self) -> &'a ty::ctxt<'tcx> {
+        self.tcx
+    }
+
+    fn cmp_method_ref(&mut self,
+                      trait_def_id: DefId,
+                      method_name: &str,
+                      arg_ty: Ty<'tcx>)
+                      -> ItemRef<'tcx> {
+        let method_name = token::intern(method_name);
+        let substs = Substs::new_trait(vec![arg_ty], vec![], arg_ty);
+        for trait_item in self.tcx.trait_items(trait_def_id).iter() {
+            match *trait_item {
+                ty::ImplOrTraitItem::MethodTraitItem(ref method) => {
+                    if method.name == method_name {
+                        let method_ty = self.tcx.lookup_item_type(method.def_id);
+                        let method_ty = method_ty.ty.subst(self.tcx, &substs);
+                        return ItemRef {
+                            ty: method_ty,
+                            def_id: method.def_id,
+                            substs: self.tcx.mk_substs(substs),
+                        };
+                    }
+                }
+                ty::ImplOrTraitItem::ConstTraitItem(..) |
+                ty::ImplOrTraitItem::TypeTraitItem(..) => {}
+            }
+        }
+
+        self.tcx.sess.bug(&format!("found no method `{}` in `{:?}`", method_name, trait_def_id));
+    }
+}
+
+mod block;
+mod expr;
+mod pattern;
+mod to_ref;
diff --git a/src/librustc_mir/tcx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs
index db434636496..31dbffa0ae3 100644
--- a/src/librustc_mir/tcx/pattern.rs
+++ b/src/librustc_mir/hair/cx/pattern.rs
@@ -9,12 +9,11 @@
 // except according to those terms.
 
 use hair::*;
+use hair::cx::Cx;
+use hair::cx::to_ref::ToRef;
 use repr::*;
-
 use rustc_data_structures::fnv::FnvHashMap;
 use std::rc::Rc;
-use tcx::Cx;
-use tcx::to_ref::ToRef;
 use rustc::middle::const_eval;
 use rustc::middle::def;
 use rustc::middle::pat_util::{pat_is_resolved_const, pat_is_binding};
@@ -223,7 +222,7 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> {
                     subpatterns.iter()
                                .enumerate()
                                .map(|(i, subpattern)| FieldPatternRef {
-                                   field: Field::Indexed(i),
+                                   field: Field::new(i),
                                    pattern: self.pat_ref(subpattern),
                                })
                                .collect();
@@ -273,7 +272,7 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> {
                                    .flat_map(|v| v.iter())
                                    .enumerate()
                                    .map(|(i, field)| FieldPatternRef {
-                                       field: Field::Indexed(i),
+                                       field: Field::new(i),
                                        pattern: self.pat_ref(field),
                                    })
                                    .collect();
@@ -281,13 +280,35 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> {
             }
 
             hir::PatStruct(_, ref fields, _) => {
+                let pat_ty = cx.tcx.node_id_to_type(self.pat.id);
+                let adt_def = match pat_ty.sty {
+                    ty::TyStruct(adt_def, _) | ty::TyEnum(adt_def, _) => adt_def,
+                    _ => {
+                        cx.tcx.sess.span_bug(
+                            self.pat.span,
+                            "struct pattern not applied to struct or enum");
+                    }
+                };
+
+                let def = cx.tcx.def_map.borrow().get(&self.pat.id).unwrap().full_def();
+                let variant_def = adt_def.variant_of_def(def);
+
                 let subpatterns =
                     fields.iter()
-                          .map(|field| FieldPatternRef {
-                              field: Field::Named(field.node.name),
-                              pattern: self.pat_ref(&field.node.pat),
+                          .map(|field| {
+                              let index = variant_def.index_of_field_named(field.node.name);
+                              let index = index.unwrap_or_else(|| {
+                                  cx.tcx.sess.span_bug(
+                                      self.pat.span,
+                                      &format!("no field with name {:?}", field.node.name));
+                              });
+                              FieldPatternRef {
+                                  field: Field::new(index),
+                                  pattern: self.pat_ref(&field.node.pat),
+                              }
                           })
                           .collect();
+
                 self.variant_or_leaf(cx, subpatterns)
             }
 
diff --git a/src/librustc_mir/tcx/to_ref.rs b/src/librustc_mir/hair/cx/to_ref.rs
index 13ca82e3e4c..e0b8abfbd9c 100644
--- a/src/librustc_mir/tcx/to_ref.rs
+++ b/src/librustc_mir/hair/cx/to_ref.rs
@@ -9,9 +9,8 @@
 // except according to those terms.
 
 use hair::*;
-use repr::*;
 
-use tcx::pattern::PatNode;
+use hair::cx::pattern::PatNode;
 use rustc_front::hir;
 use syntax::ptr::P;
 
@@ -79,14 +78,3 @@ impl<'a,'tcx:'a,T,U> ToRef for &'tcx Vec<T>
         self.iter().map(|expr| expr.to_ref()).collect()
     }
 }
-
-impl<'a,'tcx:'a> ToRef for &'tcx hir::Field {
-    type Output = FieldExprRef<'tcx>;
-
-    fn to_ref(self) -> FieldExprRef<'tcx> {
-        FieldExprRef {
-            name: Field::Named(self.name.node),
-            expr: self.expr.to_ref(),
-        }
-    }
-}
diff --git a/src/librustc_mir/hair.rs b/src/librustc_mir/hair/mod.rs
index 641cbae4be2..becaa19974d 100644
--- a/src/librustc_mir/hair.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -22,7 +22,9 @@ use rustc::middle::ty::{AdtDef, ClosureSubsts, Region, Ty};
 use rustc_front::hir;
 use syntax::ast;
 use syntax::codemap::Span;
-use tcx::{Cx, PatNode};
+use self::cx::{Cx, PatNode};
+
+pub mod cx;
 
 #[derive(Clone, Debug)]
 pub struct ItemRef<'tcx> {
@@ -41,7 +43,6 @@ pub struct Block<'tcx> {
 
 #[derive(Clone, Debug)]
 pub enum StmtRef<'tcx> {
-    Hair(&'tcx hir::Stmt),
     Mirror(Box<Stmt<'tcx>>),
 }
 
@@ -392,9 +393,8 @@ impl<'tcx> Mirror<'tcx> for Stmt<'tcx> {
 impl<'tcx> Mirror<'tcx> for StmtRef<'tcx> {
     type Output = Stmt<'tcx>;
 
-    fn make_mirror<'a>(self, hir: &mut Cx<'a, 'tcx>) -> Stmt<'tcx> {
+    fn make_mirror<'a>(self, _: &mut Cx<'a,'tcx>) -> Stmt<'tcx> {
         match self {
-            StmtRef::Hair(h) => h.make_mirror(hir),
             StmtRef::Mirror(m) => *m,
         }
     }
diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs
index 934dd660177..ed5df21d911 100644
--- a/src/librustc_mir/lib.rs
+++ b/src/librustc_mir/lib.rs
@@ -31,7 +31,9 @@ extern crate syntax;
 
 pub mod build;
 pub mod mir_map;
-pub mod hair;
+mod hair;
 pub mod repr;
 mod graphviz;
-mod tcx;
+pub mod tcx;
+pub mod visit;
+
diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs
index 1acbc8d733d..ebcb1db1151 100644
--- a/src/librustc_mir/mir_map.rs
+++ b/src/librustc_mir/mir_map.rs
@@ -23,8 +23,8 @@ extern crate rustc_front;
 use build;
 use dot;
 use repr::Mir;
+use hair::cx::{PatNode, Cx};
 use std::fs::File;
-use tcx::{PatNode, Cx};
 
 use self::rustc::middle::infer;
 use self::rustc::middle::region::CodeExtentData;
@@ -189,26 +189,42 @@ impl<'a, 'm, 'tcx> visit::Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
     }
 }
 
-fn build_mir<'a, 'tcx: 'a>(cx: Cx<'a, 'tcx>,
-                           implicit_arg_tys: Vec<Ty<'tcx>>,
-                           fn_id: ast::NodeId,
-                           span: Span,
-                           decl: &'tcx hir::FnDecl,
-                           body: &'tcx hir::Block)
-                           -> Result<Mir<'tcx>, ErrorReported> {
-    let arguments = decl.inputs
-                        .iter()
-                        .map(|arg| {
-                            let ty = cx.tcx().node_id_to_type(arg.id);
-                            (ty, PatNode::irrefutable(&arg.pat))
-                        })
-                        .collect();
-
-    let parameter_scope = cx.tcx().region_maps.lookup_code_extent(CodeExtentData::ParameterScope {
-        fn_id: fn_id,
-        body_id: body.id,
-    });
-    Ok(build::construct(cx, span, implicit_arg_tys, arguments, parameter_scope, body))
+fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>,
+                         implicit_arg_tys: Vec<Ty<'tcx>>,
+                         fn_id: ast::NodeId,
+                         span: Span,
+                         decl: &'tcx hir::FnDecl,
+                         body: &'tcx hir::Block)
+                         -> Result<Mir<'tcx>, ErrorReported> {
+    // fetch the fully liberated fn signature (that is, all bound
+    // types/lifetimes replaced)
+    let fn_sig = match cx.tcx().tables.borrow().liberated_fn_sigs.get(&fn_id) {
+        Some(f) => f.clone(),
+        None => {
+            cx.tcx().sess.span_bug(span,
+                                   &format!("no liberated fn sig for {:?}", fn_id));
+        }
+    };
+
+    let arguments =
+        decl.inputs
+            .iter()
+            .enumerate()
+            .map(|(index, arg)| {
+                (fn_sig.inputs[index], PatNode::irrefutable(&arg.pat))
+            })
+            .collect();
+
+    let parameter_scope =
+        cx.tcx().region_maps.lookup_code_extent(
+            CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id });
+    Ok(build::construct(cx,
+                        span,
+                        implicit_arg_tys,
+                        arguments,
+                        parameter_scope,
+                        fn_sig.output,
+                        body))
 }
 
 fn closure_self_ty<'a, 'tcx>(tcx: &ty::ctxt<'tcx>,
diff --git a/src/librustc_mir/repr.rs b/src/librustc_mir/repr.rs
index 5059955c5dc..89b1afa8723 100644
--- a/src/librustc_mir/repr.rs
+++ b/src/librustc_mir/repr.rs
@@ -12,7 +12,7 @@ use rustc::middle::const_eval::ConstVal;
 use rustc::middle::def_id::DefId;
 use rustc::middle::region::CodeExtent;
 use rustc::middle::subst::Substs;
-use rustc::middle::ty::{AdtDef, ClosureSubsts, Region, Ty};
+use rustc::middle::ty::{AdtDef, ClosureSubsts, FnOutput, Region, Ty};
 use rustc_back::slice;
 use rustc_data_structures::fnv::FnvHashMap;
 use rustc_front::hir::InlineAsm;
@@ -25,6 +25,8 @@ use std::u32;
 pub struct Mir<'tcx> {
     pub basic_blocks: Vec<BasicBlockData<'tcx>>,
 
+    pub return_ty: FnOutput<'tcx>,
+
     // for every node id
     pub extents: FnvHashMap<CodeExtent, Vec<GraphExtent>>,
 
@@ -245,6 +247,7 @@ pub enum Terminator<'tcx> {
     /// lvalue evaluates to some enum; jump depending on the branch
     Switch {
         discr: Lvalue<'tcx>,
+        adt_def: AdtDef<'tcx>,
         targets: Vec<BasicBlock>,
     },
 
@@ -277,7 +280,7 @@ impl<'tcx> Terminator<'tcx> {
             Goto { target: ref b } => slice::ref_slice(b),
             Panic { target: ref b } => slice::ref_slice(b),
             If { cond: _, targets: ref b } => b,
-            Switch { discr: _, targets: ref b } => b,
+            Switch { discr: _, adt_def: _, targets: ref b } => b,
             Diverge => &[],
             Return => &[],
             Call { data: _, targets: ref b } => b,
@@ -291,10 +294,10 @@ pub struct CallData<'tcx> {
     pub destination: Lvalue<'tcx>,
 
     /// the fn being called
-    pub func: Lvalue<'tcx>,
+    pub func: Operand<'tcx>,
 
     /// the arguments
-    pub args: Vec<Lvalue<'tcx>>,
+    pub args: Vec<Operand<'tcx>>,
 }
 
 impl<'tcx> BasicBlockData<'tcx> {
@@ -316,7 +319,7 @@ impl<'tcx> Debug for Terminator<'tcx> {
                 write!(fmt, "panic -> {:?}", target),
             If { cond: ref lv, ref targets } =>
                 write!(fmt, "if({:?}) -> {:?}", lv, targets),
-            Switch { discr: ref lv, ref targets } =>
+            Switch { discr: ref lv, adt_def: _, ref targets } =>
                 write!(fmt, "switch({:?}) -> {:?}", lv, targets),
             Diverge =>
                 write!(fmt, "diverge"),
@@ -353,8 +356,8 @@ pub enum StatementKind<'tcx> {
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum DropKind {
-    Shallow,
-    Deep,
+    Free, // free a partially constructed box, should go away eventually
+    Deep
 }
 
 impl<'tcx> Debug for Statement<'tcx> {
@@ -362,7 +365,7 @@ impl<'tcx> Debug for Statement<'tcx> {
         use self::StatementKind::*;
         match self.kind {
             Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
-            Drop(DropKind::Shallow, ref lv) => write!(fmt, "shallow_drop {:?}", lv),
+            Drop(DropKind::Free, ref lv) => write!(fmt, "free {:?}", lv),
             Drop(DropKind::Deep, ref lv) => write!(fmt, "drop {:?}", lv),
         }
     }
@@ -441,10 +444,19 @@ pub type LvalueProjection<'tcx> =
 pub type LvalueElem<'tcx> =
     ProjectionElem<'tcx,Operand<'tcx>>;
 
+/// Index into the list of fields found in a `VariantDef`
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-pub enum Field {
-    Named(Name),
-    Indexed(usize),
+pub struct Field(u32);
+
+impl Field {
+    pub fn new(value: usize) -> Field {
+        assert!(value < (u32::MAX) as usize);
+        Field(value as u32)
+    }
+
+    pub fn index(self) -> usize {
+        self.0 as usize
+    }
 }
 
 impl<'tcx> Lvalue<'tcx> {
@@ -489,10 +501,8 @@ impl<'tcx> Debug for Lvalue<'tcx> {
                         write!(fmt,"({:?} as {:?})", data.base, variant_index),
                     ProjectionElem::Deref =>
                         write!(fmt,"(*{:?})", data.base),
-                    ProjectionElem::Field(Field::Named(name)) =>
-                        write!(fmt,"{:?}.{:?}", data.base, name),
-                    ProjectionElem::Field(Field::Indexed(index)) =>
-                        write!(fmt,"{:?}.{:?}", data.base, index),
+                    ProjectionElem::Field(field) =>
+                        write!(fmt,"{:?}.{:?}", data.base, field.index()),
                     ProjectionElem::Index(ref index) =>
                         write!(fmt,"{:?}[{:?}]", data.base, index),
                     ProjectionElem::ConstantIndex { offset, min_length, from_end: false } =>
diff --git a/src/librustc_mir/tcx/mod.rs b/src/librustc_mir/tcx/mod.rs
index 92b026e5035..3b9d9228a16 100644
--- a/src/librustc_mir/tcx/mod.rs
+++ b/src/librustc_mir/tcx/mod.rs
@@ -9,150 +9,117 @@
 // except according to those terms.
 
 /*!
- * This module contains the code to convert from the wacky tcx data
- * structures into the hair. The `builder` is generally ignorant of
- * the tcx etc, and instead goes through the `Cx` for most of its
- * work.
+ * Methods for the various MIR types. These are intended for use after
+ * building is complete.
  */
 
-use hair::*;
 use repr::*;
-
-use rustc::middle::const_eval::ConstVal;
-use rustc::middle::def_id::DefId;
-use rustc::middle::infer::InferCtxt;
-use rustc::middle::subst::{Subst, Substs};
-use rustc::middle::ty::{self, Ty};
-use syntax::codemap::Span;
-use syntax::parse::token::{self, special_idents};
-
-#[derive(Copy, Clone)]
-pub struct Cx<'a, 'tcx: 'a> {
-    tcx: &'a ty::ctxt<'tcx>,
-    infcx: &'a InferCtxt<'a, 'tcx>,
-}
-
-impl<'a,'tcx> Cx<'a,'tcx> {
-    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Cx<'a, 'tcx> {
-        Cx {
-            tcx: infcx.tcx,
-            infcx: infcx,
-        }
-    }
+use rustc::middle::subst::Substs;
+use rustc::middle::ty::{self, AdtDef, Ty};
+
+#[derive(Copy, Clone, Debug)]
+pub enum LvalueTy<'tcx> {
+    /// Normal type.
+    Ty { ty: Ty<'tcx> },
+
+    /// Downcast to a particular variant of an enum.
+    Downcast { adt_def: AdtDef<'tcx>,
+               substs: &'tcx Substs<'tcx>,
+               variant_index: usize },
 }
 
-pub use self::pattern::PatNode;
-
-impl<'a,'tcx:'a> Cx<'a, 'tcx> {
-    /// Normalizes `ast` into the appropriate `mirror` type.
-    pub fn mirror<M: Mirror<'tcx>>(&mut self, ast: M) -> M::Output {
-        ast.make_mirror(self)
-    }
-
-    pub fn unit_ty(&mut self) -> Ty<'tcx> {
-        self.tcx.mk_nil()
-    }
-
-    pub fn usize_ty(&mut self) -> Ty<'tcx> {
-        self.tcx.types.usize
-    }
-
-    pub fn usize_literal(&mut self, value: usize) -> Literal<'tcx> {
-        Literal::Value { value: ConstVal::Uint(value as u64) }
-    }
-
-    pub fn bool_ty(&mut self) -> Ty<'tcx> {
-        self.tcx.types.bool
-    }
-
-    pub fn true_literal(&mut self) -> Literal<'tcx> {
-        Literal::Value { value: ConstVal::Bool(true) }
-    }
-
-    pub fn false_literal(&mut self) -> Literal<'tcx> {
-        Literal::Value { value: ConstVal::Bool(false) }
+impl<'tcx> LvalueTy<'tcx> {
+    pub fn from_ty(ty: Ty<'tcx>) -> LvalueTy<'tcx> {
+        LvalueTy::Ty { ty: ty }
     }
 
-    pub fn partial_eq(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> {
-        let eq_def_id = self.tcx.lang_items.eq_trait().unwrap();
-        self.cmp_method_ref(eq_def_id, "eq", ty)
-    }
-
-    pub fn partial_le(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> {
-        let ord_def_id = self.tcx.lang_items.ord_trait().unwrap();
-        self.cmp_method_ref(ord_def_id, "le", ty)
-    }
-
-    pub fn num_variants(&mut self, adt_def: ty::AdtDef<'tcx>) -> usize {
-        adt_def.variants.len()
-    }
-
-    pub fn fields(&mut self, adt_def: ty::AdtDef<'tcx>, variant_index: usize) -> Vec<Field> {
-        adt_def.variants[variant_index]
-            .fields
-            .iter()
-            .enumerate()
-            .map(|(index, field)| {
-                if field.name == special_idents::unnamed_field.name {
-                    Field::Indexed(index)
-                } else {
-                    Field::Named(field.name)
-                }
-            })
-            .collect()
-    }
-
-    pub fn needs_drop(&mut self, ty: Ty<'tcx>, span: Span) -> bool {
-        if self.infcx.type_moves_by_default(ty, span) {
-            // FIXME(#21859) we should do an add'l check here to determine if
-            // any dtor will execute, but the relevant fn
-            // (`type_needs_drop`) is currently factored into
-            // `librustc_trans`, so we can't easily do so.
-            true
-        } else {
-            // if type implements Copy, cannot require drop
-            false
+    pub fn to_ty(&self, tcx: &ty::ctxt<'tcx>) -> Ty<'tcx> {
+        match *self {
+            LvalueTy::Ty { ty } =>
+                ty,
+            LvalueTy::Downcast { adt_def, substs, variant_index: _ } =>
+                tcx.mk_enum(adt_def, substs),
         }
     }
 
-    pub fn span_bug(&mut self, span: Span, message: &str) -> ! {
-        self.tcx.sess.span_bug(span, message)
+    pub fn projection_ty(self,
+                         tcx: &ty::ctxt<'tcx>,
+                         elem: &LvalueElem<'tcx>)
+                         -> LvalueTy<'tcx>
+    {
+        match *elem {
+            ProjectionElem::Deref =>
+                LvalueTy::Ty {
+                    ty: self.to_ty(tcx).builtin_deref(true, ty::LvaluePreference::NoPreference)
+                                          .unwrap()
+                                          .ty
+                },
+            ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } =>
+                LvalueTy::Ty {
+                    ty: self.to_ty(tcx).builtin_index().unwrap()
+                },
+            ProjectionElem::Downcast(adt_def1, index) =>
+                match self.to_ty(tcx).sty {
+                    ty::TyEnum(adt_def, substs) => {
+                        assert!(index < adt_def.variants.len());
+                        assert_eq!(adt_def, adt_def1);
+                        LvalueTy::Downcast { adt_def: adt_def,
+                                             substs: substs,
+                                             variant_index: index }
+                    }
+                    _ => {
+                        tcx.sess.bug(&format!("cannot downcast non-enum type: `{:?}`", self))
+                    }
+                },
+            ProjectionElem::Field(field) => {
+                let field_ty = match self {
+                    LvalueTy::Ty { ty } => match ty.sty {
+                        ty::TyStruct(adt_def, substs) =>
+                            adt_def.struct_variant().fields[field.index()].ty(tcx, substs),
+                        ty::TyTuple(ref tys) =>
+                            tys[field.index()],
+                        _ =>
+                            tcx.sess.bug(&format!("cannot get field of type: `{:?}`", ty)),
+                    },
+                    LvalueTy::Downcast { adt_def, substs, variant_index } =>
+                        adt_def.variants[variant_index].fields[field.index()].ty(tcx, substs),
+                };
+                LvalueTy::Ty { ty: field_ty }
+            }
+        }
     }
+}
 
-    pub fn tcx(&self) -> &'a ty::ctxt<'tcx> {
-        self.tcx
+impl<'tcx> Mir<'tcx> {
+    pub fn operand_ty(&self,
+                      tcx: &ty::ctxt<'tcx>,
+                      operand: &Operand<'tcx>)
+                      -> Ty<'tcx>
+    {
+        match *operand {
+            Operand::Consume(ref l) => self.lvalue_ty(tcx, l).to_ty(tcx),
+            Operand::Constant(ref c) => c.ty,
+        }
     }
 
-    fn cmp_method_ref(&mut self,
-                      trait_def_id: DefId,
-                      method_name: &str,
-                      arg_ty: Ty<'tcx>)
-                      -> ItemRef<'tcx> {
-        let method_name = token::intern(method_name);
-        let substs = Substs::new_trait(vec![arg_ty], vec![], arg_ty);
-        for trait_item in self.tcx.trait_items(trait_def_id).iter() {
-            match *trait_item {
-                ty::ImplOrTraitItem::MethodTraitItem(ref method) => {
-                    if method.name == method_name {
-                        let method_ty = self.tcx.lookup_item_type(method.def_id);
-                        let method_ty = method_ty.ty.subst(self.tcx, &substs);
-                        return ItemRef {
-                            ty: method_ty,
-                            def_id: method.def_id,
-                            substs: self.tcx.mk_substs(substs),
-                        };
-                    }
-                }
-                ty::ImplOrTraitItem::ConstTraitItem(..) |
-                ty::ImplOrTraitItem::TypeTraitItem(..) => {}
-            }
+    pub fn lvalue_ty(&self,
+                     tcx: &ty::ctxt<'tcx>,
+                     lvalue: &Lvalue<'tcx>)
+                     -> LvalueTy<'tcx>
+    {
+        match *lvalue {
+            Lvalue::Var(index) =>
+                LvalueTy::Ty { ty: self.var_decls[index as usize].ty },
+            Lvalue::Temp(index) =>
+                LvalueTy::Ty { ty: self.temp_decls[index as usize].ty },
+            Lvalue::Arg(index) =>
+                LvalueTy::Ty { ty: self.arg_decls[index as usize].ty },
+            Lvalue::Static(def_id) =>
+                LvalueTy::Ty { ty: tcx.lookup_item_type(def_id).ty },
+            Lvalue::ReturnPointer =>
+                LvalueTy::Ty { ty: self.return_ty.unwrap() },
+            Lvalue::Projection(ref proj) =>
+                self.lvalue_ty(tcx, &proj.base).projection_ty(tcx, &proj.elem)
         }
-
-        self.tcx.sess.bug(&format!("found no method `{}` in `{:?}`", method_name, trait_def_id));
     }
 }
-
-mod block;
-mod expr;
-mod pattern;
-mod to_ref;
diff --git a/src/librustc_mir/visit.rs b/src/librustc_mir/visit.rs
new file mode 100644
index 00000000000..b4d6075d0ad
--- /dev/null
+++ b/src/librustc_mir/visit.rs
@@ -0,0 +1,239 @@
+// 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.
+
+use rustc::middle::ty::Region;
+use repr::*;
+
+pub trait Visitor<'tcx> {
+    // Override these, and call `self.super_xxx` to revert back to the
+    // default behavior.
+
+    fn visit_mir(&mut self, mir: &Mir<'tcx>) {
+        self.super_mir(mir);
+    }
+
+    fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) {
+        self.super_basic_block_data(block, data);
+    }
+
+    fn visit_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) {
+        self.super_statement(block, statement);
+    }
+
+    fn visit_assign(&mut self, block: BasicBlock, lvalue: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) {
+        self.super_assign(block, lvalue, rvalue);
+    }
+
+    fn visit_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) {
+        self.super_terminator(block, terminator);
+    }
+
+    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
+        self.super_rvalue(rvalue);
+    }
+
+    fn visit_operand(&mut self, operand: &Operand<'tcx>) {
+        self.super_operand(operand);
+    }
+
+    fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) {
+        self.super_lvalue(lvalue, context);
+    }
+
+    fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) {
+        self.super_branch(source, target);
+    }
+
+    fn visit_constant(&mut self, constant: &Constant<'tcx>) {
+        self.super_constant(constant);
+    }
+
+    // The `super_xxx` methods comprise the default behavior and are
+    // not meant to be overidden.
+
+    fn super_mir(&mut self, mir: &Mir<'tcx>) {
+        for block in mir.all_basic_blocks() {
+            let data = mir.basic_block_data(block);
+            self.visit_basic_block_data(block, data);
+        }
+    }
+
+    fn super_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) {
+        for statement in &data.statements {
+            self.visit_statement(block, statement);
+        }
+        self.visit_terminator(block, &data.terminator);
+    }
+
+    fn super_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) {
+        match statement.kind {
+            StatementKind::Assign(ref lvalue, ref rvalue) => {
+                self.visit_assign(block, lvalue, rvalue);
+            }
+            StatementKind::Drop(_, ref lvalue) => {
+                self.visit_lvalue(lvalue, LvalueContext::Drop);
+            }
+        }
+    }
+
+    fn super_assign(&mut self, _block: BasicBlock, lvalue: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) {
+        self.visit_lvalue(lvalue, LvalueContext::Store);
+        self.visit_rvalue(rvalue);
+    }
+
+    fn super_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) {
+        match *terminator {
+            Terminator::Goto { target } |
+            Terminator::Panic { target } => {
+                self.visit_branch(block, target);
+            }
+
+            Terminator::If { ref cond, ref targets } => {
+                self.visit_operand(cond);
+                for &target in &targets[..] {
+                    self.visit_branch(block, target);
+                }
+            }
+
+            Terminator::Switch { ref discr, adt_def: _, ref targets } => {
+                self.visit_lvalue(discr, LvalueContext::Inspect);
+                for &target in targets {
+                    self.visit_branch(block, target);
+                }
+            }
+
+            Terminator::Diverge |
+            Terminator::Return => {
+            }
+
+            Terminator::Call { ref data, ref targets } => {
+                self.visit_lvalue(&data.destination, LvalueContext::Store);
+                self.visit_operand(&data.func);
+                for arg in &data.args {
+                    self.visit_operand(arg);
+                }
+                for &target in &targets[..] {
+                    self.visit_branch(block, target);
+                }
+            }
+        }
+    }
+
+    fn super_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
+        match *rvalue {
+            Rvalue::Use(ref operand) => {
+                self.visit_operand(operand);
+            }
+
+            Rvalue::Repeat(ref value, ref len) => {
+                self.visit_operand(value);
+                self.visit_operand(len);
+            }
+
+            Rvalue::Ref(r, bk, ref path) => {
+                self.visit_lvalue(path, LvalueContext::Borrow {
+                    region: r,
+                    kind: bk
+                });
+            }
+
+            Rvalue::Len(ref path) => {
+                self.visit_lvalue(path, LvalueContext::Inspect);
+            }
+
+            Rvalue::Cast(_, ref operand, _) => {
+                self.visit_operand(operand);
+            }
+
+            Rvalue::BinaryOp(_, ref lhs, ref rhs) => {
+                self.visit_operand(lhs);
+                self.visit_operand(rhs);
+            }
+
+            Rvalue::UnaryOp(_, ref op) => {
+                self.visit_operand(op);
+            }
+
+            Rvalue::Box(_) => {
+            }
+
+            Rvalue::Aggregate(_, ref operands) => {
+                for operand in operands {
+                    self.visit_operand(operand);
+                }
+            }
+
+            Rvalue::Slice { ref input, from_start, from_end } => {
+                self.visit_lvalue(input, LvalueContext::Slice {
+                    from_start: from_start,
+                    from_end: from_end,
+                });
+            }
+
+            Rvalue::InlineAsm(_) => {
+            }
+        }
+    }
+
+    fn super_operand(&mut self, operand: &Operand<'tcx>) {
+        match *operand {
+            Operand::Consume(ref lvalue) => {
+                self.visit_lvalue(lvalue, LvalueContext::Consume);
+            }
+            Operand::Constant(ref constant) => {
+                self.visit_constant(constant);
+            }
+        }
+    }
+
+    fn super_lvalue(&mut self, lvalue: &Lvalue<'tcx>, _context: LvalueContext) {
+        match *lvalue {
+            Lvalue::Var(_) |
+            Lvalue::Temp(_) |
+            Lvalue::Arg(_) |
+            Lvalue::Static(_) |
+            Lvalue::ReturnPointer => {
+            }
+            Lvalue::Projection(ref proj) => {
+                self.visit_lvalue(&proj.base, LvalueContext::Projection);
+            }
+        }
+    }
+
+    fn super_branch(&mut self, _source: BasicBlock, _target: BasicBlock) {
+    }
+
+    fn super_constant(&mut self, _constant: &Constant<'tcx>) {
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum LvalueContext {
+    // Appears as LHS of an assignment or as dest of a call
+    Store,
+
+    // Being dropped
+    Drop,
+
+    // Being inspected in some way, like loading a len
+    Inspect,
+
+    // Being borrowed
+    Borrow { region: Region, kind: BorrowKind },
+
+    // Being sliced -- this should be same as being borrowed, probably
+    Slice { from_start: usize, from_end: usize },
+
+    // Used as base for another lvalue, e.g. `x` in `x.y`
+    Projection,
+
+    // Consumed as part of an operand
+    Consume,
+}
diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs
index e02ce49132a..84ce458ed14 100644
--- a/src/librustc_trans/lib.rs
+++ b/src/librustc_trans/lib.rs
@@ -37,6 +37,7 @@
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(rustc_private)]
+#![feature(slice_patterns)]
 #![feature(staged_api)]
 #![feature(unicode)]
 #![feature(vec_push_all)]
@@ -50,8 +51,10 @@ extern crate graphviz;
 extern crate libc;
 extern crate rustc;
 extern crate rustc_back;
+extern crate rustc_data_structures;
 extern crate rustc_front;
 extern crate rustc_llvm as llvm;
+extern crate rustc_mir;
 extern crate rustc_platform_intrinsics as intrinsics;
 extern crate serialize;
 
diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs
index 2654ab3339a..3c53d558865 100644
--- a/src/librustc_trans/trans/_match.rs
+++ b/src/librustc_trans/trans/_match.rs
@@ -303,7 +303,7 @@ impl<'a, 'tcx> Opt<'a, 'tcx> {
                 RangeResult(Result::new(bcx, l1), Result::new(bcx, l2))
             }
             Variant(disr_val, ref repr, _, _) => {
-                adt::trans_case(bcx, &**repr, disr_val)
+                SingleResult(Result::new(bcx, adt::trans_case(bcx, &**repr, disr_val)))
             }
             SliceLengthEqual(length, _) => {
                 SingleResult(Result::new(bcx, C_uint(ccx, length)))
diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs
index de09e33ec14..a4f66110450 100644
--- a/src/librustc_trans/trans/adt.rs
+++ b/src/librustc_trans/trans/adt.rs
@@ -945,15 +945,13 @@ fn load_discr(bcx: Block, ity: IntType, ptr: ValueRef, min: Disr, max: Disr)
 ///
 /// This should ideally be less tightly tied to `_match`.
 pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr)
-                              -> _match::OptResult<'blk, 'tcx> {
+                              -> ValueRef {
     match *r {
         CEnum(ity, _, _) => {
-            _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity),
-                                                              discr as u64, true)))
+            C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true)
         }
         General(ity, _, _) => {
-            _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity),
-                                                              discr as u64, true)))
+            C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true)
         }
         Univariant(..) => {
             bcx.ccx().sess().bug("no cases for univariants or structs")
@@ -961,7 +959,7 @@ pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr)
         RawNullablePointer { .. } |
         StructWrappedNullablePointer { .. } => {
             assert!(discr == 0 || discr == 1);
-            _match::SingleResult(Result::new(bcx, C_bool(bcx.ccx(), discr != 0)))
+            C_bool(bcx.ccx(), discr != 0)
         }
     }
 }
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 46a4cb0c92e..a536060efbd 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -44,6 +44,7 @@ use middle::pat_util::simple_name;
 use middle::subst::Substs;
 use middle::ty::{self, Ty, HasTypeFlags};
 use rustc::front::map as hir_map;
+use rustc_mir::mir_map::MirMap;
 use session::config::{self, NoDebugInfo, FullDebugInfo};
 use session::Session;
 use trans::_match;
@@ -74,6 +75,7 @@ use trans::intrinsic;
 use trans::machine;
 use trans::machine::{llsize_of, llsize_of_real};
 use trans::meth;
+use trans::mir;
 use trans::monomorphize;
 use trans::tvec;
 use trans::type_::Type;
@@ -497,13 +499,8 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
                               &format!("enum-iter-variant-{}",
                                       &variant.disr_val.to_string())
                               );
-                      match adt::trans_case(cx, &*repr, variant.disr_val) {
-                          _match::SingleResult(r) => {
-                              AddCase(llswitch, r.val, variant_cx.llbb)
-                          }
-                          _ => ccx.sess().unimpl("value from adt::trans_case \
-                                                  in iter_structural_ty")
-                      }
+                      let case_val = adt::trans_case(cx, &*repr, variant.disr_val);
+                      AddCase(llswitch, case_val, variant_cx.llbb);
                       let variant_cx =
                           iter_variant(variant_cx,
                                        &*repr,
@@ -1235,7 +1232,10 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
         false
     };
 
+    let mir = ccx.mir_map().get(&id);
+
     let mut fcx = FunctionContext {
+          mir: mir,
           llfn: llfndecl,
           llenv: None,
           llretslotptr: Cell::new(None),
@@ -1575,7 +1575,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                    llfndecl: ValueRef,
                                    param_substs: &'tcx Substs<'tcx>,
                                    fn_ast_id: ast::NodeId,
-                                   _attributes: &[ast::Attribute],
+                                   attributes: &[ast::Attribute],
                                    output_type: ty::FnOutput<'tcx>,
                                    abi: Abi,
                                    closure_env: closure::ClosureEnv<'b>) {
@@ -1604,6 +1604,12 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                       &arena);
     let mut bcx = init_function(&fcx, false, output_type);
 
+    if attributes.iter().any(|item| item.check_name("rustc_mir")) {
+        mir::trans_mir(bcx);
+        fcx.cleanup();
+        return;
+    }
+
     // cleanup scope for the incoming arguments
     let fn_cleanup_debug_loc =
         debuginfo::get_cleanup_debug_loc_for_ast_node(ccx, fn_ast_id, body.span, true);
@@ -2737,7 +2743,10 @@ pub fn filter_reachable_ids(ccx: &SharedCrateContext) -> NodeSet {
     }).collect()
 }
 
-pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslation {
+pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>,
+                         mir_map: &MirMap<'tcx>,
+                         analysis: ty::CrateAnalysis)
+                         -> CrateTranslation {
     let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis;
     let krate = tcx.map.krate();
 
@@ -2779,6 +2788,7 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat
     let shared_ccx = SharedCrateContext::new(&link_meta.crate_name,
                                              codegen_units,
                                              tcx,
+                                             &mir_map,
                                              export_map,
                                              Sha256::new(),
                                              link_meta.clone(),
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index b39b7818a63..8d6ba53dd22 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -16,7 +16,7 @@ pub use self::ExprOrMethodCall::*;
 
 use session::Session;
 use llvm;
-use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef};
+use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef, TypeKind};
 use llvm::{True, False, Bool};
 use middle::cfg;
 use middle::def;
@@ -40,6 +40,7 @@ use middle::traits;
 use middle::ty::{self, HasTypeFlags, Ty};
 use middle::ty::fold::{TypeFolder, TypeFoldable};
 use rustc_front::hir;
+use rustc_mir::repr::Mir;
 use util::nodemap::{FnvHashMap, NodeMap};
 
 use arena::TypedArena;
@@ -328,6 +329,11 @@ impl<'tcx> DropFlagHintsMap<'tcx> {
 // Function context.  Every LLVM function we create will have one of
 // these.
 pub struct FunctionContext<'a, 'tcx: 'a> {
+    // The MIR for this function. At present, this is optional because
+    // we only have MIR available for things that are local to the
+    // crate.
+    pub mir: Option<&'a Mir<'tcx>>,
+
     // The ValueRef returned from a call to llvm::LLVMAddFunction; the
     // address of the first instruction in the sequence of
     // instructions for this function that will go in the .text
@@ -407,6 +413,10 @@ pub struct FunctionContext<'a, 'tcx: 'a> {
 }
 
 impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
+    pub fn mir(&self) -> &'a Mir<'tcx> {
+        self.mir.unwrap()
+    }
+
     pub fn arg_offset(&self) -> usize {
         self.env_arg_pos() + if self.llenv.is_some() { 1 } else { 0 }
     }
@@ -644,6 +654,10 @@ impl<'blk, 'tcx> BlockS<'blk, 'tcx> {
     }
     pub fn sess(&self) -> &'blk Session { self.fcx.ccx.sess() }
 
+    pub fn mir(&self) -> &'blk Mir<'tcx> {
+        self.fcx.mir()
+    }
+
     pub fn name(&self, name: ast::Name) -> String {
         name.to_string()
     }
@@ -729,6 +743,12 @@ pub fn C_floating(s: &str, t: Type) -> ValueRef {
     }
 }
 
+pub fn C_floating_f64(f: f64, t: Type) -> ValueRef {
+    unsafe {
+        llvm::LLVMConstReal(t.to_ref(), f)
+    }
+}
+
 pub fn C_nil(ccx: &CrateContext) -> ValueRef {
     C_struct(ccx, &[], false)
 }
@@ -1132,3 +1152,65 @@ pub fn inlined_variant_def<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         ccx.sess().bug(&format!("no variant for {:?}::{}", adt_def, inlined_vid))
     })
 }
+
+// To avoid UB from LLVM, these two functions mask RHS with an
+// appropriate mask unconditionally (i.e. the fallback behavior for
+// all shifts). For 32- and 64-bit types, this matches the semantics
+// of Java. (See related discussion on #1877 and #10183.)
+
+pub fn build_unchecked_lshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                                          lhs: ValueRef,
+                                          rhs: ValueRef,
+                                          binop_debug_loc: DebugLoc) -> ValueRef {
+    let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShl, lhs, rhs);
+    // #1877, #10183: Ensure that input is always valid
+    let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc);
+    build::Shl(bcx, lhs, rhs, binop_debug_loc)
+}
+
+pub fn build_unchecked_rshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                                          lhs_t: Ty<'tcx>,
+                                          lhs: ValueRef,
+                                          rhs: ValueRef,
+                                          binop_debug_loc: DebugLoc) -> ValueRef {
+    let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShr, lhs, rhs);
+    // #1877, #10183: Ensure that input is always valid
+    let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc);
+    let is_signed = lhs_t.is_signed();
+    if is_signed {
+        build::AShr(bcx, lhs, rhs, binop_debug_loc)
+    } else {
+        build::LShr(bcx, lhs, rhs, binop_debug_loc)
+    }
+}
+
+fn shift_mask_rhs<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                              rhs: ValueRef,
+                              debug_loc: DebugLoc) -> ValueRef {
+    let rhs_llty = val_ty(rhs);
+    build::And(bcx, rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false), debug_loc)
+}
+
+pub fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                              llty: Type,
+                              mask_llty: Type,
+                              invert: bool) -> ValueRef {
+    let kind = llty.kind();
+    match kind {
+        TypeKind::Integer => {
+            // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc.
+            let val = llty.int_width() - 1;
+            if invert {
+                C_integral(mask_llty, !val, true)
+            } else {
+                C_integral(mask_llty, val, false)
+            }
+        },
+        TypeKind::Vector => {
+            let mask = shift_mask_val(bcx, llty.element_type(), mask_llty.element_type(), invert);
+            build::VectorSplat(bcx, mask_llty.vector_length(), mask)
+        },
+        _ => panic!("shift_mask_val: expected Integer or Vector, found {:?}", kind),
+    }
+}
+
diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs
index 01d0c51a08d..1f1d43feeb3 100644
--- a/src/librustc_trans/trans/context.rs
+++ b/src/librustc_trans/trans/context.rs
@@ -14,6 +14,7 @@ use metadata::common::LinkMeta;
 use middle::def::ExportMap;
 use middle::def_id::DefId;
 use middle::traits;
+use rustc_mir::mir_map::MirMap;
 use trans::adt;
 use trans::base;
 use trans::builder::Builder;
@@ -70,6 +71,7 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> {
     stats: Stats,
     check_overflow: bool,
     check_drop_flag_for_sanity: bool,
+    mir_map: &'a MirMap<'tcx>,
 
     available_drop_glues: RefCell<FnvHashMap<DropGlueKind<'tcx>, String>>,
     use_dll_storage_attrs: bool,
@@ -251,6 +253,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
     pub fn new(crate_name: &str,
                local_count: usize,
                tcx: &'b ty::ctxt<'tcx>,
+               mir_map: &'b MirMap<'tcx>,
                export_map: ExportMap,
                symbol_hasher: Sha256,
                link_meta: LinkMeta,
@@ -317,6 +320,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
             link_meta: link_meta,
             symbol_hasher: RefCell::new(symbol_hasher),
             tcx: tcx,
+            mir_map: mir_map,
             stats: Stats {
                 n_glues_created: Cell::new(0),
                 n_null_glues: Cell::new(0),
@@ -803,6 +807,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
     pub fn use_dll_storage_attrs(&self) -> bool {
         self.shared.use_dll_storage_attrs()
     }
+
+    pub fn mir_map(&self) -> &'b MirMap<'tcx> {
+        self.shared.mir_map
+    }
 }
 
 pub struct TypeOfDepthLock<'a, 'tcx: 'a>(&'a LocalCrateContext<'tcx>);
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 6144de7109f..7648587e352 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -2574,29 +2574,6 @@ impl OverflowOpViaInputCheck {
     }
 }
 
-fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                              llty: Type,
-                              mask_llty: Type,
-                              invert: bool) -> ValueRef {
-    let kind = llty.kind();
-    match kind {
-        TypeKind::Integer => {
-            // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc.
-            let val = llty.int_width() - 1;
-            if invert {
-                C_integral(mask_llty, !val, true)
-            } else {
-                C_integral(mask_llty, val, false)
-            }
-        },
-        TypeKind::Vector => {
-            let mask = shift_mask_val(bcx, llty.element_type(), mask_llty.element_type(), invert);
-            VectorSplat(bcx, mask_llty.vector_length(), mask)
-        },
-        _ => panic!("shift_mask_val: expected Integer or Vector, found {:?}", kind),
-    }
-}
-
 // Check if an integer or vector contains a nonzero element.
 fn build_nonzero_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                    value: ValueRef,
@@ -2616,44 +2593,6 @@ fn build_nonzero_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     }
 }
 
-// To avoid UB from LLVM, these two functions mask RHS with an
-// appropriate mask unconditionally (i.e. the fallback behavior for
-// all shifts). For 32- and 64-bit types, this matches the semantics
-// of Java. (See related discussion on #1877 and #10183.)
-
-fn build_unchecked_lshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                      lhs: ValueRef,
-                                      rhs: ValueRef,
-                                      binop_debug_loc: DebugLoc) -> ValueRef {
-    let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShl, lhs, rhs);
-    // #1877, #10183: Ensure that input is always valid
-    let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc);
-    Shl(bcx, lhs, rhs, binop_debug_loc)
-}
-
-fn build_unchecked_rshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                      lhs_t: Ty<'tcx>,
-                                      lhs: ValueRef,
-                                      rhs: ValueRef,
-                                      binop_debug_loc: DebugLoc) -> ValueRef {
-    let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShr, lhs, rhs);
-    // #1877, #10183: Ensure that input is always valid
-    let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc);
-    let is_signed = lhs_t.is_signed();
-    if is_signed {
-        AShr(bcx, lhs, rhs, binop_debug_loc)
-    } else {
-        LShr(bcx, lhs, rhs, binop_debug_loc)
-    }
-}
-
-fn shift_mask_rhs<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                              rhs: ValueRef,
-                              debug_loc: DebugLoc) -> ValueRef {
-    let rhs_llty = val_ty(rhs);
-    And(bcx, rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false), debug_loc)
-}
-
 fn with_overflow_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, oop: OverflowOp, info: NodeIdAndSpan,
                                    lhs_t: Ty<'tcx>, lhs: ValueRef,
                                    rhs: ValueRef,
diff --git a/src/librustc_trans/trans/mir/analyze.rs b/src/librustc_trans/trans/mir/analyze.rs
new file mode 100644
index 00000000000..f5fa897bca6
--- /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() && !common::type_is_fat_ptr(bcx.tcx(), ty)) ||
+            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 extractvalue 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/block.rs b/src/librustc_trans/trans/mir/block.rs
new file mode 100644
index 00000000000..c9f4123c171
--- /dev/null
+++ b/src/librustc_trans/trans/mir/block.rs
@@ -0,0 +1,106 @@
+// 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.
+
+use llvm::BasicBlockRef;
+use rustc_mir::repr as mir;
+use trans::base;
+use trans::build;
+use trans::common::Block;
+use trans::debuginfo::DebugLoc;
+
+use super::MirContext;
+
+impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
+    pub fn trans_block(&mut self, bb: mir::BasicBlock) {
+        debug!("trans_block({:?})", bb);
+
+        let mut bcx = self.bcx(bb);
+        let data = self.mir.basic_block_data(bb);
+
+        for statement in &data.statements {
+            bcx = self.trans_statement(bcx, statement);
+        }
+
+        debug!("trans_block: terminator: {:?}", data.terminator);
+
+        match data.terminator {
+            mir::Terminator::Goto { target } => {
+                build::Br(bcx, self.llblock(target), DebugLoc::None)
+            }
+
+            mir::Terminator::Panic { .. } => {
+                unimplemented!()
+            }
+
+            mir::Terminator::If { ref cond, targets: [true_bb, false_bb] } => {
+                let cond = self.trans_operand(bcx, cond);
+                let lltrue = self.llblock(true_bb);
+                let llfalse = self.llblock(false_bb);
+                build::CondBr(bcx, cond.llval, lltrue, llfalse, DebugLoc::None);
+            }
+
+            mir::Terminator::Switch { .. } => {
+                unimplemented!()
+            }
+
+            mir::Terminator::Diverge => {
+                if let Some(llpersonalityslot) = self.llpersonalityslot {
+                    let lp = build::Load(bcx, llpersonalityslot);
+                    // FIXME(lifetime) base::call_lifetime_end(bcx, self.personality);
+                    build::Resume(bcx, lp);
+                } else {
+                    // This fn never encountered anything fallible, so
+                    // a Diverge cannot actually happen. Note that we
+                    // do a total hack to ensure that we visit the
+                    // DIVERGE block last.
+                    build::Unreachable(bcx);
+                }
+            }
+
+            mir::Terminator::Return => {
+                let return_ty = bcx.monomorphize(&self.mir.return_ty);
+                base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None);
+            }
+
+            mir::Terminator::Call { .. } => {
+                unimplemented!()
+                //let llbb = unimplemented!(); // self.make_landing_pad(panic_bb);
+                //
+                //let tr_dest = self.trans_lvalue(bcx, &data.destination);
+                //
+                //// Create the callee. This will always be a fn
+                //// ptr and hence a kind of scalar.
+                //let callee = self.trans_operand(bcx, &data.func);
+                //
+                //// Process the arguments.
+                //
+                //let args = unimplemented!();
+                //
+                //callee::trans_call_inner(bcx,
+                //                         DebugLoc::None,
+                //                         |bcx, _| Callee {
+                //                             bcx: bcx,
+                //                             data: CalleeData::Fn(callee.llval),
+                //                             ty: callee.ty,
+                //                         },
+                //                         args,
+                //                         Some(Dest::SaveIn(tr_dest.llval)));
+            }
+        }
+    }
+
+    fn bcx(&self, bb: mir::BasicBlock) -> Block<'bcx, 'tcx> {
+        self.blocks[bb.index()]
+    }
+
+    fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef {
+        self.blocks[bb.index()].llbb
+    }
+}
diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs
new file mode 100644
index 00000000000..1b61001834a
--- /dev/null
+++ b/src/librustc_trans/trans/mir/constant.rs
@@ -0,0 +1,63 @@
+// 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.
+
+use llvm::ValueRef;
+use rustc::middle::const_eval::ConstVal;
+use rustc_mir::repr as mir;
+use trans::consts::{self, TrueConst};
+use trans::common::{self, Block};
+use trans::type_of;
+
+use super::MirContext;
+
+impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
+    pub fn trans_constant(&mut self,
+                          bcx: Block<'bcx, 'tcx>,
+                          constant: &mir::Constant<'tcx>)
+                          -> ValueRef
+    {
+        let ccx = bcx.ccx();
+        let constant_ty = bcx.monomorphize(&constant.ty);
+        let llty = type_of::type_of(ccx, constant_ty);
+        match constant.literal {
+            mir::Literal::Item { .. } => {
+                unimplemented!()
+            }
+            mir::Literal::Value { ref value } => {
+                match *value {
+                    ConstVal::Float(v) => common::C_floating_f64(v, llty),
+                    ConstVal::Bool(v) => common::C_bool(ccx, v),
+                    ConstVal::Int(v) => common::C_integral(llty, v as u64, true),
+                    ConstVal::Uint(v) => common::C_integral(llty, v, false),
+                    ConstVal::Str(ref v) => common::C_str_slice(ccx, v.clone()),
+                    ConstVal::ByteStr(ref v) => consts::addr_of(ccx,
+                                                                common::C_bytes(ccx, v),
+                                                                1,
+                                                                "byte_str"),
+                    ConstVal::Struct(id) | ConstVal::Tuple(id) => {
+                        let expr = bcx.tcx().map.expect_expr(id);
+                        let (llval, _) = match consts::const_expr(ccx,
+                                                                  expr,
+                                                                  bcx.fcx.param_substs,
+                                                                  None,
+                                                                  TrueConst::Yes) {
+                            Ok(v) => v,
+                            Err(_) => panic!("constant eval failure"),
+                        };
+                        llval
+                    }
+                    ConstVal::Function(_) => {
+                        unimplemented!()
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs
new file mode 100644
index 00000000000..1ce7b55a9c6
--- /dev/null
+++ b/src/librustc_trans/trans/mir/lvalue.rs
@@ -0,0 +1,154 @@
+// 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.
+
+use llvm::ValueRef;
+use rustc::middle::ty::Ty;
+use rustc_mir::repr as mir;
+use rustc_mir::tcx::LvalueTy;
+use trans::adt;
+use trans::base;
+use trans::build;
+use trans::common::{self, Block};
+use trans::debuginfo::DebugLoc;
+use trans::machine;
+use trans::tvec;
+
+use super::{MirContext, TempRef};
+
+#[derive(Copy, Clone)]
+pub struct LvalueRef<'tcx> {
+    /// Pointer to the contents of the lvalue
+    pub llval: ValueRef,
+
+    /// Monomorphized type of this lvalue, including variant information
+    pub ty: LvalueTy<'tcx>,
+}
+
+impl<'tcx> LvalueRef<'tcx> {
+    pub fn new(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> {
+        LvalueRef { llval: llval, ty: lvalue_ty }
+    }
+
+    pub fn alloca<'bcx>(bcx: Block<'bcx, 'tcx>,
+                        ty: Ty<'tcx>,
+                        name: &str)
+                        -> LvalueRef<'tcx>
+    {
+        let lltemp = base::alloc_ty(bcx, ty, name);
+        LvalueRef::new(lltemp, LvalueTy::from_ty(ty))
+    }
+}
+
+impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
+    pub fn trans_lvalue(&mut self,
+                        bcx: Block<'bcx, 'tcx>,
+                        lvalue: &mir::Lvalue<'tcx>)
+                        -> LvalueRef<'tcx> {
+        debug!("trans_lvalue(lvalue={:?})", lvalue);
+
+        let fcx = bcx.fcx;
+        let ccx = fcx.ccx;
+        let tcx = bcx.tcx();
+        match *lvalue {
+            mir::Lvalue::Var(index) => self.vars[index as usize],
+            mir::Lvalue::Temp(index) => match self.temps[index as usize] {
+                TempRef::Lvalue(lvalue) =>
+                    lvalue,
+                TempRef::Operand(..) =>
+                    tcx.sess.bug(&format!("using operand temp {:?} as lvalue", lvalue)),
+            },
+            mir::Lvalue::Arg(index) => self.args[index as usize],
+            mir::Lvalue::Static(_def_id) => unimplemented!(),
+            mir::Lvalue::ReturnPointer => {
+                let return_ty = bcx.monomorphize(&self.mir.return_ty);
+                let llval = fcx.get_ret_slot(bcx, return_ty, "return");
+                LvalueRef::new(llval, LvalueTy::from_ty(return_ty.unwrap()))
+            }
+            mir::Lvalue::Projection(ref projection) => {
+                let tr_base = self.trans_lvalue(bcx, &projection.base);
+                let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem);
+                let llprojected = match projection.elem {
+                    mir::ProjectionElem::Deref => {
+                        let base_ty = tr_base.ty.to_ty(tcx);
+                        base::load_ty(bcx, tr_base.llval, base_ty)
+                    }
+                    mir::ProjectionElem::Field(ref field) => {
+                        let base_ty = tr_base.ty.to_ty(tcx);
+                        let base_repr = adt::represent_type(ccx, base_ty);
+                        let discr = match tr_base.ty {
+                            LvalueTy::Ty { .. } => 0,
+                            LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v,
+                        };
+                        let discr = discr as u64;
+                        adt::trans_field_ptr(bcx, &base_repr, tr_base.llval, discr, field.index())
+                    }
+                    mir::ProjectionElem::Index(ref index) => {
+                        let base_ty = tr_base.ty.to_ty(tcx);
+                        let index = self.trans_operand(bcx, index);
+                        let llindex = self.prepare_index(bcx, index.llval);
+                        let (llbase, _) = tvec::get_base_and_len(bcx, tr_base.llval, base_ty);
+                        build::InBoundsGEP(bcx, llbase, &[llindex])
+                    }
+                    mir::ProjectionElem::ConstantIndex { offset,
+                                                         from_end: false,
+                                                         min_length: _ } => {
+                        let base_ty = tr_base.ty.to_ty(tcx);
+                        let lloffset = common::C_u32(bcx.ccx(), offset);
+                        let llindex = self.prepare_index(bcx, lloffset);
+                        let (llbase, _) = tvec::get_base_and_len(bcx,
+                                                                 tr_base.llval,
+                                                                 base_ty);
+                        build::InBoundsGEP(bcx, llbase, &[llindex])
+                    }
+                    mir::ProjectionElem::ConstantIndex { offset,
+                                                         from_end: true,
+                                                         min_length: _ } => {
+                        let lloffset = common::C_u32(bcx.ccx(), offset);
+                        let base_ty = tr_base.ty.to_ty(tcx);
+                        let (llbase, lllen) = tvec::get_base_and_len(bcx,
+                                                                     tr_base.llval,
+                                                                     base_ty);
+                        let llindex = build::Sub(bcx, lllen, lloffset, DebugLoc::None);
+                        let llindex = self.prepare_index(bcx, llindex);
+                        build::InBoundsGEP(bcx, llbase, &[llindex])
+                    }
+                    mir::ProjectionElem::Downcast(..) => {
+                        tr_base.llval
+                    }
+                };
+                LvalueRef {
+                    llval: llprojected,
+                    ty: projected_ty,
+                }
+            }
+        }
+    }
+
+    /// Adjust the bitwidth of an index since LLVM is less forgiving
+    /// than we are.
+    ///
+    /// nmatsakis: is this still necessary? Not sure.
+    fn prepare_index(&mut self,
+                     bcx: Block<'bcx, 'tcx>,
+                     llindex: ValueRef)
+                     -> ValueRef
+    {
+        let ccx = bcx.ccx();
+        let index_size = machine::llbitsize_of_real(bcx.ccx(), common::val_ty(llindex));
+        let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type());
+        if index_size < int_size {
+            build::ZExt(bcx, llindex, ccx.int_type())
+        } else if index_size > int_size {
+            build::Trunc(bcx, llindex, ccx.int_type())
+        } else {
+            llindex
+        }
+    }
+}
diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs
new file mode 100644
index 00000000000..3b018cc1321
--- /dev/null
+++ b/src/librustc_trans/trans/mir/mod.rs
@@ -0,0 +1,195 @@
+// 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.
+
+use libc::c_uint;
+use llvm::{self, ValueRef};
+use rustc_mir::repr as mir;
+use rustc_mir::tcx::LvalueTy;
+use trans::base;
+use trans::build;
+use trans::common::{self, Block};
+use trans::debuginfo::DebugLoc;
+use trans::expr;
+use trans::type_of;
+
+use self::lvalue::LvalueRef;
+use self::operand::OperandRef;
+
+// FIXME DebugLoc is always None right now
+
+/// Master context for translating MIR.
+pub struct MirContext<'bcx, 'tcx:'bcx> {
+    mir: &'bcx mir::Mir<'tcx>,
+
+    /// When unwinding is initiated, we have to store this personality
+    /// value somewhere so that we can load it and re-use it in the
+    /// resume instruction. The personality is (afaik) some kind of
+    /// value used for C++ unwinding, which must filter by type: we
+    /// don't really care about it very much. Anyway, this value
+    /// contains an alloca into which the personality is stored and
+    /// then later loaded when generating the DIVERGE_BLOCK.
+    llpersonalityslot: Option<ValueRef>,
+
+    /// A `Block` for each MIR `BasicBlock`
+    blocks: Vec<Block<'bcx, 'tcx>>,
+
+    /// An LLVM alloca for each MIR `VarDecl`
+    vars: Vec<LvalueRef<'tcx>>,
+
+    /// The location where each MIR `TempDecl` is stored. This is
+    /// usually an `LvalueRef` representing an alloca, but not always:
+    /// sometimes we can skip the alloca and just store the value
+    /// directly using an `OperandRef`, which makes for tighter LLVM
+    /// IR. The conditions for using an `OperandRef` are as follows:
+    ///
+    /// - the type of the temporary must be judged "immediate" by `type_is_immediate`
+    /// - the operand must never be referenced indirectly
+    ///     - we should not take its address using the `&` operator
+    ///     - nor should it appear in an lvalue path like `tmp.a`
+    /// - the operand must be defined by an rvalue that can generate immediate
+    ///   values
+    ///
+    /// Avoiding allocs can also be important for certain intrinsics,
+    /// notably `expect`.
+    temps: Vec<TempRef<'tcx>>,
+
+    /// The arguments to the function; as args are lvalues, these are
+    /// always indirect, though we try to avoid creating an alloca
+    /// when we can (and just reuse the pointer the caller provided).
+    args: Vec<LvalueRef<'tcx>>,
+}
+
+enum TempRef<'tcx> {
+    Lvalue(LvalueRef<'tcx>),
+    Operand(Option<OperandRef<'tcx>>),
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) {
+    let fcx = bcx.fcx;
+    let mir = bcx.mir();
+
+    let mir_blocks = bcx.mir().all_basic_blocks();
+
+    // Analyze the temps to determine which must be lvalues
+    // FIXME
+    let lvalue_temps = analyze::lvalue_temps(bcx, mir);
+
+    // Allocate variable and temp allocas
+    let vars = mir.var_decls.iter()
+                            .map(|decl| (bcx.monomorphize(&decl.ty), decl.name))
+                            .map(|(mty, name)| LvalueRef::alloca(bcx, mty, &name.as_str()))
+                            .collect();
+    let temps = mir.temp_decls.iter()
+                              .map(|decl| bcx.monomorphize(&decl.ty))
+                              .enumerate()
+                              .map(|(i, mty)| if lvalue_temps.contains(&i) {
+                                  TempRef::Lvalue(LvalueRef::alloca(bcx,
+                                                                    mty,
+                                                                    &format!("temp{:?}", i)))
+                              } else {
+                                  // If this is an immediate temp, we do not create an
+                                  // alloca in advance. Instead we wait until we see the
+                                  // definition and update the operand there.
+                                  TempRef::Operand(None)
+                              })
+                              .collect();
+    let args = arg_value_refs(bcx, mir);
+
+    // Allocate a `Block` for every basic block
+    let block_bcxs: Vec<Block<'bcx,'tcx>> =
+        mir_blocks.iter()
+                  .map(|&bb| fcx.new_block(false, &format!("{:?}", bb), None))
+                  .collect();
+
+    // Branch to the START block
+    let start_bcx = block_bcxs[mir::START_BLOCK.index()];
+    build::Br(bcx, start_bcx.llbb, DebugLoc::None);
+
+    let mut mircx = MirContext {
+        mir: mir,
+        llpersonalityslot: None,
+        blocks: block_bcxs,
+        vars: vars,
+        temps: temps,
+        args: args,
+    };
+
+    // Translate the body of each block
+    for &bb in &mir_blocks {
+        if bb != mir::DIVERGE_BLOCK {
+            mircx.trans_block(bb);
+        }
+    }
+
+    // Total hack: translate DIVERGE_BLOCK last. This is so that any
+    // panics which the fn may do can initialize the
+    // `llpersonalityslot` cell. We don't do this up front because the
+    // LLVM type of it is (frankly) annoying to compute.
+    mircx.trans_block(mir::DIVERGE_BLOCK);
+}
+
+/// Produce, for each argument, a `ValueRef` pointing at the
+/// argument's value. As arguments are lvalues, these are always
+/// indirect.
+fn arg_value_refs<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>,
+                              mir: &mir::Mir<'tcx>)
+                              -> Vec<LvalueRef<'tcx>> {
+    // FIXME tupled_args? I think I'd rather that mapping is done in MIR land though
+    let fcx = bcx.fcx;
+    let tcx = bcx.tcx();
+    let mut idx = fcx.arg_offset() as c_uint;
+    mir.arg_decls
+       .iter()
+       .enumerate()
+       .map(|(arg_index, arg_decl)| {
+           let arg_ty = bcx.monomorphize(&arg_decl.ty);
+           let llval = if type_of::arg_is_indirect(bcx.ccx(), arg_ty) {
+               // Don't copy an indirect argument to an alloca, the caller
+               // already put it in a temporary alloca and gave it up, unless
+               // we emit extra-debug-info, which requires local allocas :(.
+               // FIXME: lifetimes, debug info
+               let llarg = llvm::get_param(fcx.llfn, idx);
+               idx += 1;
+               llarg
+           } else if common::type_is_fat_ptr(tcx, arg_ty) {
+               // we pass fat pointers as two words, but we want to
+               // represent them internally as a pointer to two words,
+               // so make an alloca to store them in.
+               let lldata = llvm::get_param(fcx.llfn, idx);
+               let llextra = llvm::get_param(fcx.llfn, idx + 1);
+               idx += 2;
+               let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index));
+               build::Store(bcx, lldata, expr::get_dataptr(bcx, lltemp));
+               build::Store(bcx, llextra, expr::get_dataptr(bcx, lltemp));
+               lltemp
+           } else {
+               // otherwise, arg is passed by value, so make a
+               // temporary and store it there
+               let llarg = llvm::get_param(fcx.llfn, idx);
+               idx += 1;
+               let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index));
+               build::Store(bcx, llarg, lltemp);
+               lltemp
+           };
+           LvalueRef::new(llval, LvalueTy::from_ty(arg_ty))
+       })
+       .collect()
+}
+
+mod analyze;
+mod block;
+mod constant;
+mod lvalue;
+mod rvalue;
+mod operand;
+mod statement;
+
diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs
new file mode 100644
index 00000000000..a0308032ac0
--- /dev/null
+++ b/src/librustc_trans/trans/mir/operand.rs
@@ -0,0 +1,110 @@
+// 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.
+
+use llvm::ValueRef;
+use rustc::middle::ty::Ty;
+use rustc_mir::repr as mir;
+use trans::base;
+use trans::build;
+use trans::common::Block;
+use trans::datum;
+
+use super::{MirContext, TempRef};
+
+#[derive(Copy, Clone)]
+pub struct OperandRef<'tcx> {
+    // This will be "indirect" if `appropriate_rvalue_mode` returns
+    // ByRef, and otherwise ByValue.
+    pub llval: ValueRef,
+
+    // The type of value being returned.
+    pub ty: Ty<'tcx>
+}
+
+impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
+    pub fn trans_operand(&mut self,
+                         bcx: Block<'bcx, 'tcx>,
+                         operand: &mir::Operand<'tcx>)
+                         -> OperandRef<'tcx>
+    {
+        debug!("trans_operand(operand={:?})", operand);
+
+        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={} @ {:?}",
+                       bcx.val_to_string(tr_lvalue.llval),
+                       ty);
+                let llval = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) {
+                    datum::ByValue => build::Load(bcx, tr_lvalue.llval),
+                    datum::ByRef => tr_lvalue.llval,
+                };
+                OperandRef {
+                    llval: llval,
+                    ty: ty
+                }
+            }
+
+            mir::Operand::Constant(ref constant) => {
+                let llval = self.trans_constant(bcx, constant);
+                let ty = bcx.monomorphize(&constant.ty);
+                OperandRef {
+                    llval: llval,
+                    ty: ty,
+                }
+            }
+        }
+    }
+
+    pub fn trans_operand_into(&mut self,
+                              bcx: Block<'bcx, 'tcx>,
+                              lldest: ValueRef,
+                              operand: &mir::Operand<'tcx>)
+    {
+        debug!("trans_operand_into(lldest={}, operand={:?})",
+               bcx.val_to_string(lldest),
+               operand);
+
+        match *operand {
+            mir::Operand::Consume(ref lvalue) => {
+                let tr_lvalue = self.trans_lvalue(bcx, lvalue);
+                let lvalue_ty = tr_lvalue.ty.to_ty(bcx.tcx());
+                debug!("trans_operand_into: tr_lvalue={} @ {:?}",
+                       bcx.val_to_string(tr_lvalue.llval),
+                       lvalue_ty);
+                base::memcpy_ty(bcx, lldest, tr_lvalue.llval, lvalue_ty);
+            }
+
+            mir::Operand::Constant(..) => {
+                unimplemented!()
+            }
+        }
+    }
+}
diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs
new file mode 100644
index 00000000000..ad89ee79de4
--- /dev/null
+++ b/src/librustc_trans/trans/mir/rvalue.rs
@@ -0,0 +1,307 @@
+// 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.
+
+use llvm::ValueRef;
+use rustc::middle::ty::Ty;
+use rustc_front::hir;
+use rustc_mir::repr as mir;
+
+use trans::asm;
+use trans::base;
+use trans::build;
+use trans::common::{self, Block, Result};
+use trans::debuginfo::DebugLoc;
+use trans::declare;
+use trans::expr;
+use trans::machine;
+use trans::type_::Type;
+use trans::type_of;
+use trans::tvec;
+
+use super::MirContext;
+use super::operand::OperandRef;
+
+impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
+    pub fn trans_rvalue(&mut self,
+                        bcx: Block<'bcx, 'tcx>,
+                        lldest: ValueRef,
+                        rvalue: &mir::Rvalue<'tcx>)
+                        -> Block<'bcx, 'tcx>
+    {
+        debug!("trans_rvalue(lldest={}, rvalue={:?})",
+               bcx.val_to_string(lldest),
+               rvalue);
+
+        match *rvalue {
+            mir::Rvalue::Use(ref operand) => {
+                self.trans_operand_into(bcx, lldest, operand);
+                bcx
+            }
+
+            mir::Rvalue::Cast(..) => {
+                unimplemented!()
+            }
+
+            mir::Rvalue::Repeat(..) => {
+                unimplemented!()
+            }
+
+            mir::Rvalue::Aggregate(_, ref operands) => {
+                for (i, operand) in operands.iter().enumerate() {
+                    // Note: perhaps this should be StructGep, but
+                    // note that in some cases the values here will
+                    // not be structs but arrays.
+                    let lldest_i = build::GEPi(bcx, lldest, &[0, i]);
+                    self.trans_operand_into(bcx, lldest_i, operand);
+                }
+                bcx
+            }
+
+            mir::Rvalue::Slice { ref input, from_start, from_end } => {
+                let ccx = bcx.ccx();
+                let input = self.trans_lvalue(bcx, input);
+                let (llbase, lllen) = tvec::get_base_and_len(bcx,
+                                                             input.llval,
+                                                             input.ty.to_ty(bcx.tcx()));
+                let llbase1 = build::GEPi(bcx, llbase, &[from_start]);
+                let adj = common::C_uint(ccx, from_start + from_end);
+                let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None);
+                let lladdrdest = expr::get_dataptr(bcx, lldest);
+                build::Store(bcx, llbase1, lladdrdest);
+                let llmetadest = expr::get_meta(bcx, lldest);
+                build::Store(bcx, lllen1, llmetadest);
+                bcx
+            }
+
+            mir::Rvalue::InlineAsm(inline_asm) => {
+                asm::trans_inline_asm(bcx, inline_asm)
+            }
+
+            _ => {
+                assert!(rvalue_creates_operand(rvalue));
+                let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
+                build::Store(bcx, temp.llval, lldest);
+                bcx
+            }
+        }
+    }
+
+    pub fn trans_rvalue_operand(&mut self,
+                                bcx: Block<'bcx, 'tcx>,
+                                rvalue: &mir::Rvalue<'tcx>)
+                                -> (Block<'bcx, 'tcx>, OperandRef<'tcx>)
+    {
+        assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);
+
+        match *rvalue {
+            mir::Rvalue::Use(ref operand) => {
+                let operand = self.trans_operand(bcx, operand);
+                (bcx, operand)
+            }
+
+            mir::Rvalue::Cast(..) => {
+                unimplemented!()
+            }
+
+            mir::Rvalue::Ref(_, _, ref lvalue) => {
+                let tr_lvalue = self.trans_lvalue(bcx, lvalue);
+
+                // Note: lvalues are indirect, so storing the `llval` into the
+                // destination effectively creates a reference.
+                (bcx, OperandRef {
+                    llval: tr_lvalue.llval,
+                    ty: tr_lvalue.ty.to_ty(bcx.tcx()),
+                })
+            }
+
+            mir::Rvalue::Len(ref lvalue) => {
+                let tr_lvalue = self.trans_lvalue(bcx, lvalue);
+                let (_, lllen) = tvec::get_base_and_len(bcx,
+                                                        tr_lvalue.llval,
+                                                        tr_lvalue.ty.to_ty(bcx.tcx()));
+                (bcx, OperandRef {
+                    llval: lllen,
+                    ty: bcx.tcx().types.usize,
+                })
+            }
+
+            mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
+                let lhs = self.trans_operand(bcx, lhs);
+                let rhs = self.trans_operand(bcx, rhs);
+                let is_float = lhs.ty.is_fp();
+                let is_signed = lhs.ty.is_signed();
+                let binop_debug_loc = DebugLoc::None;
+                let llval = match op {
+                    mir::BinOp::Add => if is_float {
+                        build::FAdd(bcx, lhs.llval, rhs.llval, binop_debug_loc)
+                    } else {
+                        build::Add(bcx, lhs.llval, rhs.llval, binop_debug_loc)
+                    },
+                    mir::BinOp::Sub => if is_float {
+                        build::FSub(bcx, lhs.llval, rhs.llval, binop_debug_loc)
+                    } else {
+                        build::Sub(bcx, lhs.llval, rhs.llval, binop_debug_loc)
+                    },
+                    mir::BinOp::Mul => if is_float {
+                        build::FMul(bcx, lhs.llval, rhs.llval, binop_debug_loc)
+                    } else {
+                        build::Mul(bcx, lhs.llval, rhs.llval, binop_debug_loc)
+                    },
+                    mir::BinOp::Div => if is_float {
+                        build::FDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc)
+                    } else if is_signed {
+                        build::SDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc)
+                    } else {
+                        build::UDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc)
+                    },
+                    mir::BinOp::Rem => if is_float {
+                        // LLVM currently always lowers the `frem` instructions appropriate
+                        // library calls typically found in libm. Notably f64 gets wired up
+                        // to `fmod` and f32 gets wired up to `fmodf`. Inconveniently for
+                        // us, 32-bit MSVC does not actually have a `fmodf` symbol, it's
+                        // instead just an inline function in a header that goes up to a
+                        // f64, uses `fmod`, and then comes back down to a f32.
+                        //
+                        // Although LLVM knows that `fmodf` doesn't exist on MSVC, it will
+                        // still unconditionally lower frem instructions over 32-bit floats
+                        // to a call to `fmodf`. To work around this we special case MSVC
+                        // 32-bit float rem instructions and instead do the call out to
+                        // `fmod` ourselves.
+                        //
+                        // Note that this is currently duplicated with src/libcore/ops.rs
+                        // which does the same thing, and it would be nice to perhaps unify
+                        // these two implementations one day! Also note that we call `fmod`
+                        // for both 32 and 64-bit floats because if we emit any FRem
+                        // instruction at all then LLVM is capable of optimizing it into a
+                        // 32-bit FRem (which we're trying to avoid).
+                        let tcx = bcx.tcx();
+                        let use_fmod = tcx.sess.target.target.options.is_like_msvc &&
+                            tcx.sess.target.target.arch == "x86";
+                        if use_fmod {
+                            let f64t = Type::f64(bcx.ccx());
+                            let fty = Type::func(&[f64t, f64t], &f64t);
+                            let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty,
+                                                            tcx.types.f64);
+                            if lhs.ty == tcx.types.f32 {
+                                let lllhs = build::FPExt(bcx, lhs.llval, f64t);
+                                let llrhs = build::FPExt(bcx, rhs.llval, f64t);
+                                let llres = build::Call(bcx, llfn, &[lllhs, llrhs],
+                                                        None, binop_debug_loc);
+                                build::FPTrunc(bcx, llres, Type::f32(bcx.ccx()))
+                            } else {
+                                build::Call(bcx, llfn, &[lhs.llval, rhs.llval],
+                                            None, binop_debug_loc)
+                            }
+                        } else {
+                            build::FRem(bcx, lhs.llval, rhs.llval, binop_debug_loc)
+                        }
+                    } else if is_signed {
+                        build::SRem(bcx, lhs.llval, rhs.llval, binop_debug_loc)
+                    } else {
+                        build::URem(bcx, lhs.llval, rhs.llval, binop_debug_loc)
+                    },
+                    mir::BinOp::BitOr => build::Or(bcx, lhs.llval, rhs.llval, binop_debug_loc),
+                    mir::BinOp::BitAnd => build::And(bcx, lhs.llval, rhs.llval, binop_debug_loc),
+                    mir::BinOp::BitXor => build::Xor(bcx, lhs.llval, rhs.llval, binop_debug_loc),
+                    mir::BinOp::Shl => common::build_unchecked_lshift(bcx,
+                                                                      lhs.llval,
+                                                                      rhs.llval,
+                                                                      binop_debug_loc),
+                    mir::BinOp::Shr => common::build_unchecked_rshift(bcx,
+                                                                      lhs.ty,
+                                                                      lhs.llval,
+                                                                      rhs.llval,
+                                                                      binop_debug_loc),
+                    mir::BinOp::Eq => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
+                                                                 hir::BiEq, binop_debug_loc),
+                    mir::BinOp::Lt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
+                                                                 hir::BiLt, binop_debug_loc),
+                    mir::BinOp::Le => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
+                                                                 hir::BiLe, binop_debug_loc),
+                    mir::BinOp::Ne => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
+                                                                 hir::BiNe, binop_debug_loc),
+                    mir::BinOp::Ge => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
+                                                                 hir::BiGe, binop_debug_loc),
+                    mir::BinOp::Gt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
+                                                                 hir::BiGt, binop_debug_loc),
+                };
+                (bcx, OperandRef {
+                    llval: llval,
+                    ty: lhs.ty,
+                })
+            }
+
+            mir::Rvalue::UnaryOp(op, ref operand) => {
+                let operand = self.trans_operand(bcx, operand);
+                let is_float = operand.ty.is_fp();
+                let debug_loc = DebugLoc::None;
+                let llval = match op {
+                    mir::UnOp::Not => build::Not(bcx, operand.llval, debug_loc),
+                    mir::UnOp::Neg => if is_float {
+                        build::FNeg(bcx, operand.llval, debug_loc)
+                    } else {
+                        build::Neg(bcx, operand.llval, debug_loc)
+                    }
+                };
+                (bcx, OperandRef {
+                    llval: llval,
+                    ty: operand.ty,
+                })
+            }
+
+            mir::Rvalue::Box(content_ty) => {
+                let content_ty: Ty<'tcx> = bcx.monomorphize(&content_ty);
+                let llty = type_of::type_of(bcx.ccx(), content_ty);
+                let llsize = machine::llsize_of(bcx.ccx(), llty);
+                let align = type_of::align_of(bcx.ccx(), content_ty);
+                let llalign = common::C_uint(bcx.ccx(), align);
+                let llty_ptr = llty.ptr_to();
+                let box_ty = bcx.tcx().mk_box(content_ty);
+                let Result { bcx, val: llval } = base::malloc_raw_dyn(bcx,
+                                                                      llty_ptr,
+                                                                      box_ty,
+                                                                      llsize,
+                                                                      llalign,
+                                                                      DebugLoc::None);
+                (bcx, OperandRef {
+                    llval: llval,
+                    ty: box_ty,
+                })
+            }
+
+            mir::Rvalue::Repeat(..) |
+            mir::Rvalue::Aggregate(..) |
+            mir::Rvalue::Slice { .. } |
+            mir::Rvalue::InlineAsm(..) => {
+                bcx.tcx().sess.bug(&format!("cannot generate operand from rvalue {:?}", rvalue));
+            }
+        }
+    }
+}
+
+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
+}
diff --git a/src/librustc_trans/trans/mir/statement.rs b/src/librustc_trans/trans/mir/statement.rs
new file mode 100644
index 00000000000..95ff049836e
--- /dev/null
+++ b/src/librustc_trans/trans/mir/statement.rs
@@ -0,0 +1,70 @@
+// 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.
+
+use rustc::middle::ty::LvaluePreference;
+use rustc_mir::repr as mir;
+use trans::common::Block;
+use trans::debuginfo::DebugLoc;
+use trans::glue;
+
+use super::MirContext;
+use super::TempRef;
+
+impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
+    pub fn trans_statement(&mut self,
+                           bcx: Block<'bcx, 'tcx>,
+                           statement: &mir::Statement<'tcx>)
+                           -> Block<'bcx, 'tcx> {
+        debug!("trans_statement(statement={:?})", statement);
+
+        match statement.kind {
+            mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
+                match *lvalue {
+                    mir::Lvalue::Temp(index) => {
+                        let index = index as usize;
+                        match self.temps[index as usize] {
+                            TempRef::Lvalue(tr_dest) => {
+                                self.trans_rvalue(bcx, tr_dest.llval, rvalue)
+                            }
+                            TempRef::Operand(None) => {
+                                let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue);
+                                self.temps[index] = TempRef::Operand(Some(operand));
+                                bcx
+                            }
+                            TempRef::Operand(Some(_)) => {
+                                bcx.tcx().sess.span_bug(
+                                    statement.span,
+                                    &format!("operand {:?} already assigned", rvalue));
+                            }
+                        }
+                    }
+                    _ => {
+                        let tr_dest = self.trans_lvalue(bcx, lvalue);
+                        self.trans_rvalue(bcx, tr_dest.llval, rvalue)
+                    }
+                }
+            }
+
+            mir::StatementKind::Drop(mir::DropKind::Deep, ref lvalue) => {
+                let tr_lvalue = self.trans_lvalue(bcx, lvalue);
+                let ty = tr_lvalue.ty.to_ty(bcx.tcx());
+                glue::drop_ty(bcx, tr_lvalue.llval, ty, DebugLoc::None)
+            }
+
+            mir::StatementKind::Drop(mir::DropKind::Free, ref lvalue) => {
+                let tr_lvalue = self.trans_lvalue(bcx, lvalue);
+                let ty = tr_lvalue.ty.to_ty(bcx.tcx());
+                let content_ty = ty.builtin_deref(true, LvaluePreference::NoPreference);
+                let content_ty = content_ty.unwrap().ty;
+                glue::trans_exchange_free_ty(bcx, tr_lvalue.llval, content_ty, DebugLoc::None)
+            }
+        }
+    }
+}
diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs
index 04854501312..fa37b005539 100644
--- a/src/librustc_trans/trans/mod.rs
+++ b/src/librustc_trans/trans/mod.rs
@@ -52,6 +52,7 @@ mod llrepr;
 mod machine;
 mod _match;
 mod meth;
+mod mir;
 mod monomorphize;
 mod tvec;
 mod type_;
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index f085ce23e3f..dc2b2b75ab6 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -164,11 +164,6 @@ pub struct Inherited<'a, 'tcx: 'a> {
 
     tables: &'a RefCell<ty::Tables<'tcx>>,
 
-    // A mapping from each fn's id to its signature, with all bound
-    // regions replaced with free ones. Unlike the other tables, this
-    // one is never copied into the tcx: it is only used by regionck.
-    fn_sig_map: RefCell<NodeMap<Vec<Ty<'tcx>>>>,
-
     // When we process a call like `c()` where `c` is a closure type,
     // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or
     // `FnOnce` closure. In that case, we defer full resolution of the
@@ -314,7 +309,6 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> {
             infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env), true),
             locals: RefCell::new(NodeMap()),
             tables: tables,
-            fn_sig_map: RefCell::new(NodeMap()),
             deferred_call_resolutions: RefCell::new(DefIdMap()),
             deferred_cast_checks: RefCell::new(Vec::new()),
         }
@@ -620,22 +614,13 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
         ccx: ccx
     };
 
-    // Remember return type so that regionck can access it later.
-    let mut fn_sig_tys: Vec<Ty> =
-        arg_tys.iter()
-        .cloned()
-        .collect();
-
     if let ty::FnConverging(ret_ty) = ret_ty {
         fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType);
-        fn_sig_tys.push(ret_ty); // FIXME(#25759) just take implied bounds from the arguments
     }
 
-    debug!("fn-sig-map: fn_id={} fn_sig_tys={:?}",
-           fn_id,
-           fn_sig_tys);
+    debug!("fn-sig-map: fn_id={} fn_sig={:?}", fn_id, fn_sig);
 
-    inherited.fn_sig_map.borrow_mut().insert(fn_id, fn_sig_tys);
+    inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig.clone());
 
     {
         let mut visit = GatherLocalsVisitor { fcx: &fcx, };
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 6e60f926b7c..3cdc9b55944 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -284,19 +284,32 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
         // When we enter a function, we can derive
         debug!("visit_fn_body(id={})", id);
 
-        let fn_sig_map = self.fcx.inh.fn_sig_map.borrow();
-        let fn_sig = match fn_sig_map.get(&id) {
-            Some(f) => f,
-            None => {
-                self.tcx().sess.bug(
-                    &format!("No fn-sig entry for id={}", id));
+        let fn_sig = {
+            let fn_sig_map = &self.infcx().tables.borrow().liberated_fn_sigs;
+            match fn_sig_map.get(&id) {
+                Some(f) => f.clone(),
+                None => {
+                    self.tcx().sess.bug(
+                        &format!("No fn-sig entry for id={}", id));
+                }
             }
         };
 
         let old_region_bounds_pairs_len = self.region_bound_pairs.len();
 
+        // Collect the types from which we create inferred bounds.
+        // For the return type, if diverging, substitute `bool` just
+        // because it will have no effect.
+        //
+        // FIXME(#25759) return types should not be implied bounds
+        let fn_sig_tys: Vec<_> =
+            fn_sig.inputs.iter()
+                         .cloned()
+                         .chain(Some(fn_sig.output.unwrap_or(self.tcx().types.bool)))
+                         .collect();
+
         let old_body_id = self.set_body_id(body.id);
-        self.relate_free_regions(&fn_sig[..], body.id, span);
+        self.relate_free_regions(&fn_sig_tys[..], body.id, span);
         link_fn_args(self,
                      self.tcx().region_maps.node_extent(body.id),
                      &fn_decl.inputs[..]);
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 2c18a245159..cfab28f923e 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -43,6 +43,7 @@ pub fn resolve_type_vars_in_expr(fcx: &FnCtxt, e: &hir::Expr) {
     wbcx.visit_expr(e);
     wbcx.visit_upvar_borrow_map();
     wbcx.visit_closures();
+    wbcx.visit_liberated_fn_sigs();
 }
 
 pub fn resolve_type_vars_in_fn(fcx: &FnCtxt,
@@ -63,6 +64,7 @@ pub fn resolve_type_vars_in_fn(fcx: &FnCtxt,
     }
     wbcx.visit_upvar_borrow_map();
     wbcx.visit_closures();
+    wbcx.visit_liberated_fn_sigs();
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -361,6 +363,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
         }
     }
 
+    fn visit_liberated_fn_sigs(&self) {
+        for (&node_id, fn_sig) in self.fcx.inh.tables.borrow().liberated_fn_sigs.iter() {
+            let fn_sig = self.resolve(fn_sig, ResolvingFnSig(node_id));
+            self.tcx().tables.borrow_mut().liberated_fn_sigs.insert(node_id, fn_sig.clone());
+        }
+    }
+
     fn resolve<T:TypeFoldable<'tcx>>(&self, t: &T, reason: ResolveReason) -> T {
         t.fold_with(&mut Resolver::new(self.fcx, reason))
     }
@@ -376,6 +385,7 @@ enum ResolveReason {
     ResolvingPattern(Span),
     ResolvingUpvar(ty::UpvarId),
     ResolvingClosure(DefId),
+    ResolvingFnSig(ast::NodeId),
 }
 
 impl ResolveReason {
@@ -387,6 +397,9 @@ impl ResolveReason {
             ResolvingUpvar(upvar_id) => {
                 tcx.expr_span(upvar_id.closure_expr_id)
             }
+            ResolvingFnSig(id) => {
+                tcx.map.span(id)
+            }
             ResolvingClosure(did) => {
                 if let Some(node_id) = tcx.map.as_local_node_id(did) {
                     tcx.expr_span(node_id)
@@ -463,6 +476,16 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
                     span_err!(self.tcx.sess, span, E0196,
                               "cannot determine a type for this closure")
                 }
+
+                ResolvingFnSig(id) => {
+                    // any failures here should also fail when
+                    // resolving the patterns, closure types, or
+                    // something else.
+                    let span = self.reason.span(self.tcx);
+                    self.tcx.sess.delay_span_bug(
+                        span,
+                        &format!("cannot resolve some aspect of fn sig for {:?}", id));
+                }
             }
         }
     }
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index ae01d6e5426..f9a138f8fd7 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -146,7 +146,7 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
                                         &arenas,
                                         &name,
                                         resolve::MakeGlobMap::No,
-                                        |tcx, analysis| {
+                                        |tcx, _, analysis| {
         let ty::CrateAnalysis { exported_items, public_items, .. } = analysis;
 
         // Convert from a NodeId set to a DefId set since we don't always have easy access
diff --git a/src/test/run-make/execution-engine/test.rs b/src/test/run-make/execution-engine/test.rs
index f4031a3aaae..f2dd155595a 100644
--- a/src/test/run-make/execution-engine/test.rs
+++ b/src/test/run-make/execution-engine/test.rs
@@ -229,9 +229,9 @@ fn compile_program(input: &str, sysroot: PathBuf)
         let ast_map = driver::make_map(&sess, &mut hir_forest);
 
         driver::phase_3_run_analysis_passes(
-            &sess, ast_map, &arenas, &id, MakeGlobMap::No, |tcx, analysis| {
+            &sess, ast_map, &arenas, &id, MakeGlobMap::No, |tcx, mir_map, analysis| {
 
-            let trans = driver::phase_4_translate_to_llvm(tcx, analysis);
+            let trans = driver::phase_4_translate_to_llvm(tcx, &mir_map, analysis);
 
             let crates = tcx.sess.cstore.get_used_crates(RequireDynamic);
 
diff --git a/src/test/run-pass/mir_trans_spike1.rs b/src/test/run-pass/mir_trans_spike1.rs
new file mode 100644
index 00000000000..9a06ab78e73
--- /dev/null
+++ b/src/test/run-pass/mir_trans_spike1.rs
@@ -0,0 +1,24 @@
+// Copyright 2012 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.
+
+// A simple spike test for MIR version of trans.
+
+#![feature(rustc_attrs)]
+
+#[rustc_mir]
+fn sum(x: i32, y: i32) -> i32 {
+    x + y
+}
+
+fn main() {
+    let x = sum(22, 44);
+    assert_eq!(x, 66);
+    println!("sum()={:?}", x);
+}