about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/build/channel.rs4
-rw-r--r--src/bootstrap/build/dist.rs290
-rw-r--r--src/bootstrap/build/mod.rs9
-rw-r--r--src/bootstrap/build/native.rs3
-rw-r--r--src/bootstrap/build/step.rs38
-rw-r--r--src/bootstrap/mk/Makefile.in4
-rw-r--r--src/compiletest/header.rs13
-rw-r--r--src/compiletest/runtest.rs34
-rw-r--r--src/doc/book/getting-started.md4
-rw-r--r--src/doc/book/guessing-game.md2
-rw-r--r--src/doc/book/references-and-borrowing.md4
-rw-r--r--src/doc/book/variable-bindings.md4
-rw-r--r--src/libcollections/btree/map.rs13
-rw-r--r--src/libcollectionstest/btree/map.rs36
-rw-r--r--src/libcollectionstest/lib.rs1
-rw-r--r--src/libcore/intrinsics.rs26
-rw-r--r--src/libcore/lib.rs1
-rw-r--r--src/libcore/num/mod.rs3
-rw-r--r--src/librustc/front/map/mod.rs3
-rw-r--r--src/librustc/lib.rs1
-rw-r--r--src/librustc/lint/builtin.rs9
-rw-r--r--src/librustc/middle/cfg/construct.rs17
-rw-r--r--src/librustc/middle/check_match.rs11
-rw-r--r--src/librustc/middle/const_eval.rs69
-rw-r--r--src/librustc/middle/cstore.rs6
-rw-r--r--src/librustc/middle/expr_use_visitor.rs17
-rw-r--r--src/librustc/middle/liveness.rs66
-rw-r--r--src/librustc/middle/ty/mod.rs13
-rw-r--r--src/librustc/middle/ty/sty.rs2
-rw-r--r--src/librustc/mir/repr.rs42
-rw-r--r--src/librustc/mir/tcx.rs26
-rw-r--r--src/librustc/mir/visit.rs9
-rw-r--r--src/librustc/session/config.rs2
-rw-r--r--src/librustc/util/ppaux.rs80
-rw-r--r--src/librustc_back/abi.rs24
-rw-r--r--src/librustc_back/lib.rs1
-rw-r--r--src/librustc_back/svh.rs2
-rw-r--r--src/librustc_front/fold.rs33
-rw-r--r--src/librustc_front/hir.rs28
-rw-r--r--src/librustc_front/intravisit.rs10
-rw-r--r--src/librustc_front/lowering.rs8
-rw-r--r--src/librustc_front/print/pprust.rs12
-rw-r--r--src/librustc_lint/lib.rs4
-rw-r--r--src/librustc_llvm/lib.rs163
-rw-r--r--src/librustc_metadata/astencode.rs98
-rw-r--r--src/librustc_metadata/csearch.rs15
-rw-r--r--src/librustc_metadata/decoder.rs93
-rw-r--r--src/librustc_metadata/encoder.rs73
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs25
-rw-r--r--src/librustc_mir/build/matches/mod.rs7
-rw-r--r--src/librustc_mir/build/matches/simplify.rs20
-rw-r--r--src/librustc_mir/build/matches/test.rs105
-rw-r--r--src/librustc_mir/build/matches/util.rs3
-rw-r--r--src/librustc_mir/build/mod.rs7
-rw-r--r--src/librustc_mir/hair/cx/expr.rs21
-rw-r--r--src/librustc_mir/hair/cx/mod.rs43
-rw-r--r--src/librustc_mir/hair/cx/pattern.rs41
-rw-r--r--src/librustc_mir/hair/mod.rs2
-rw-r--r--src/librustc_mir/mir_map.rs22
-rw-r--r--src/librustc_mir/transform/erase_regions.rs2
-rw-r--r--src/librustc_passes/consts.rs7
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs32
-rw-r--r--src/librustc_resolve/lib.rs66
-rw-r--r--src/librustc_resolve/resolve_imports.rs69
-rw-r--r--src/librustc_trans/lib.rs1
-rw-r--r--src/librustc_trans/trans/_match.rs76
-rw-r--r--src/librustc_trans/trans/abi.rs538
-rw-r--r--src/librustc_trans/trans/adt.rs91
-rw-r--r--src/librustc_trans/trans/asm.rs110
-rw-r--r--src/librustc_trans/trans/attributes.rs205
-rw-r--r--src/librustc_trans/trans/base.rs1566
-rw-r--r--src/librustc_trans/trans/build.rs92
-rw-r--r--src/librustc_trans/trans/builder.rs135
-rw-r--r--src/librustc_trans/trans/cabi.rs137
-rw-r--r--src/librustc_trans/trans/cabi_aarch64.rs69
-rw-r--r--src/librustc_trans/trans/cabi_arm.rs57
-rw-r--r--src/librustc_trans/trans/cabi_asmjs.rs57
-rw-r--r--src/librustc_trans/trans/cabi_mips.rs63
-rw-r--r--src/librustc_trans/trans/cabi_powerpc.rs63
-rw-r--r--src/librustc_trans/trans/cabi_powerpc64.rs74
-rw-r--r--src/librustc_trans/trans/cabi_x86.rs72
-rw-r--r--src/librustc_trans/trans/cabi_x86_64.rs63
-rw-r--r--src/librustc_trans/trans/cabi_x86_win64.rs61
-rw-r--r--src/librustc_trans/trans/callee.rs852
-rw-r--r--src/librustc_trans/trans/cleanup.rs29
-rw-r--r--src/librustc_trans/trans/closure.rs323
-rw-r--r--src/librustc_trans/trans/collector.rs227
-rw-r--r--src/librustc_trans/trans/common.rs242
-rw-r--r--src/librustc_trans/trans/consts.rs242
-rw-r--r--src/librustc_trans/trans/context.rs86
-rw-r--r--src/librustc_trans/trans/controlflow.rs39
-rw-r--r--src/librustc_trans/trans/datum.rs55
-rw-r--r--src/librustc_trans/trans/debuginfo/create_scope_map.rs13
-rw-r--r--src/librustc_trans/trans/debuginfo/mod.rs18
-rw-r--r--src/librustc_trans/trans/debuginfo/type_names.rs2
-rw-r--r--src/librustc_trans/trans/declare.rs146
-rw-r--r--src/librustc_trans/trans/expr.rs176
-rw-r--r--src/librustc_trans/trans/foreign.rs1072
-rw-r--r--src/librustc_trans/trans/glue.rs124
-rw-r--r--src/librustc_trans/trans/inline.rs90
-rw-r--r--src/librustc_trans/trans/intrinsic.rs377
-rw-r--r--src/librustc_trans/trans/llrepr.rs36
-rw-r--r--src/librustc_trans/trans/meth.rs235
-rw-r--r--src/librustc_trans/trans/mir/block.rs454
-rw-r--r--src/librustc_trans/trans/mir/constant.rs41
-rw-r--r--src/librustc_trans/trans/mir/lvalue.rs87
-rw-r--r--src/librustc_trans/trans/mir/mod.rs152
-rw-r--r--src/librustc_trans/trans/mir/operand.rs105
-rw-r--r--src/librustc_trans/trans/mir/rvalue.rs147
-rw-r--r--src/librustc_trans/trans/mod.rs4
-rw-r--r--src/librustc_trans/trans/monomorphize.rs275
-rw-r--r--src/librustc_trans/trans/tvec.rs39
-rw-r--r--src/librustc_trans/trans/type_.rs41
-rw-r--r--src/librustc_trans/trans/type_of.rs180
-rw-r--r--src/librustc_trans/trans/value.rs13
-rw-r--r--src/librustc_typeck/check/intrinsic.rs2
-rw-r--r--src/librustc_typeck/check/mod.rs10
-rw-r--r--src/librustc_typeck/coherence/overlap.rs18
-rw-r--r--src/librustc_typeck/collect.rs25
-rw-r--r--src/librustdoc/clean/inline.rs4
-rw-r--r--src/librustdoc/html/static/main.js8
-rw-r--r--src/librustdoc/test.rs8
-rw-r--r--src/libstd/collections/hash/map.rs49
-rw-r--r--src/libstd/lib.rs1
-rw-r--r--src/libstd/net/ip.rs60
-rw-r--r--src/libstd/num/f32.rs1
-rw-r--r--src/libstd/num/f64.rs1
-rw-r--r--src/libstd/panic.rs26
-rw-r--r--src/libstd/panicking.rs62
-rw-r--r--src/libsyntax/ext/quote.rs6
-rw-r--r--src/libsyntax/feature_gate.rs12
-rw-r--r--src/libsyntax/print/pprust.rs3
-rw-r--r--src/libsyntax_ext/deriving/cmp/ord.rs8
-rw-r--r--src/libsyntax_ext/deriving/cmp/partial_eq.rs13
-rw-r--r--src/libsyntax_ext/deriving/cmp/partial_ord.rs31
-rw-r--r--src/libsyntax_ext/deriving/generic/mod.rs88
m---------src/llvm0
-rw-r--r--src/rustc/Cargo.lock15
-rw-r--r--src/rustllvm/RustWrapper.cpp5
-rw-r--r--src/rustllvm/llvm-auto-clean-trigger2
-rw-r--r--src/test/codegen/adjustments.rs2
-rw-r--r--src/test/codegen/coercions.rs3
-rw-r--r--src/test/codegen/consts.rs5
-rw-r--r--src/test/codegen/drop.rs2
-rw-r--r--src/test/codegen/extern-functions.rs5
-rw-r--r--src/test/codegen/float_math.rs60
-rw-r--r--src/test/codegen/refs.rs2
-rw-r--r--src/test/codegen/stores.rs26
-rw-r--r--src/test/compile-fail/asm-out-assign-imm.rs1
-rw-r--r--src/test/compile-fail/blind-item-block-item-shadow.rs4
-rw-r--r--src/test/compile-fail/blind-item-item-shadow.rs4
-rw-r--r--src/test/compile-fail/borrowck/borrowck-box-insensitivity.rs17
-rw-r--r--src/test/compile-fail/borrowck/borrowck-let-suggestion.rs1
-rw-r--r--src/test/compile-fail/borrowck/borrowck-report-with-custom-diagnostic.rs3
-rw-r--r--src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs4
-rw-r--r--src/test/compile-fail/cast-as-bool.rs1
-rw-r--r--src/test/compile-fail/cast-rfc0401.rs8
-rw-r--r--src/test/compile-fail/const-err.rs6
-rw-r--r--src/test/compile-fail/const-eval-overflow.rs2
-rw-r--r--src/test/compile-fail/enum-and-module-in-same-scope.rs4
-rw-r--r--src/test/compile-fail/fat-ptr-cast.rs1
-rw-r--r--src/test/compile-fail/feature-gate-negate-unsigned.rs1
-rw-r--r--src/test/compile-fail/fn-item-type.rs4
-rw-r--r--src/test/compile-fail/infinite-instantiation.rs2
-rw-r--r--src/test/compile-fail/inherent-overlap.rs15
-rw-r--r--src/test/compile-fail/intrinsic-return-address.rs13
-rw-r--r--src/test/compile-fail/issue-11714.rs1
-rw-r--r--src/test/compile-fail/issue-13058.rs1
-rw-r--r--src/test/compile-fail/issue-13428.rs2
-rw-r--r--src/test/compile-fail/issue-15260.rs1
-rw-r--r--src/test/compile-fail/issue-16747.rs1
-rw-r--r--src/test/compile-fail/issue-17263.rs2
-rw-r--r--src/test/compile-fail/issue-19498.rs12
-rw-r--r--src/test/compile-fail/issue-19707.rs2
-rw-r--r--src/test/compile-fail/issue-21221-1.rs7
-rw-r--r--src/test/compile-fail/issue-21221-2.rs1
-rw-r--r--src/test/compile-fail/issue-21221-3.rs1
-rw-r--r--src/test/compile-fail/issue-21221-4.rs1
-rw-r--r--src/test/compile-fail/issue-21546.rs24
-rw-r--r--src/test/compile-fail/issue-21600.rs3
-rw-r--r--src/test/compile-fail/issue-22638.rs2
-rw-r--r--src/test/compile-fail/issue-24036.rs5
-rw-r--r--src/test/compile-fail/issue-24081.rs20
-rw-r--r--src/test/compile-fail/issue-25385.rs1
-rw-r--r--src/test/compile-fail/issue-25386.rs2
-rw-r--r--src/test/compile-fail/issue-25793.rs1
-rw-r--r--src/test/compile-fail/issue-26638.rs5
-rw-r--r--src/test/compile-fail/issue-28472.rs4
-rw-r--r--src/test/compile-fail/issue-30302.rs2
-rw-r--r--src/test/compile-fail/issue-3099-a.rs2
-rw-r--r--src/test/compile-fail/issue-3099-b.rs2
-rw-r--r--src/test/compile-fail/issue-3099.rs2
-rw-r--r--src/test/compile-fail/issue-32323.rs18
-rw-r--r--src/test/compile-fail/issue-6702.rs1
-rw-r--r--src/test/compile-fail/issue-6936.rs8
-rw-r--r--src/test/compile-fail/issue-7044.rs2
-rw-r--r--src/test/compile-fail/issue-8640.rs2
-rw-r--r--src/test/compile-fail/issue-8727.rs2
-rw-r--r--src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs6
-rw-r--r--src/test/compile-fail/linkage2.rs2
-rw-r--r--src/test/compile-fail/lint-group-style.rs1
-rw-r--r--src/test/compile-fail/lint-no-drop-on-repr-extern.rs2
-rw-r--r--src/test/compile-fail/lint-unconditional-recursion.rs14
-rw-r--r--src/test/compile-fail/liveness-return-last-stmt-semi.rs4
-rw-r--r--src/test/compile-fail/macro-backtrace-nested.rs2
-rw-r--r--src/test/compile-fail/macro-backtrace-println.rs3
-rw-r--r--src/test/compile-fail/method-suggestion-no-duplication.rs2
-rw-r--r--src/test/compile-fail/no-std-inject.rs2
-rw-r--r--src/test/compile-fail/object-safety-generics.rs1
-rw-r--r--src/test/compile-fail/on-unimplemented.rs2
-rw-r--r--src/test/compile-fail/recursion_limit.rs11
-rw-r--r--src/test/compile-fail/ref-suggestion.rs3
-rw-r--r--src/test/compile-fail/resolve-conflict-extern-crate-vs-extern-crate.rs2
-rw-r--r--src/test/compile-fail/resolve-conflict-import-vs-extern-crate.rs2
-rw-r--r--src/test/compile-fail/resolve-conflict-item-vs-extern-crate.rs2
-rw-r--r--src/test/compile-fail/resolve-conflict-item-vs-import.rs3
-rw-r--r--src/test/compile-fail/resolve-conflict-type-vs-import.rs2
-rw-r--r--src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs3
-rw-r--r--src/test/compile-fail/simd-intrinsic-generic-cast.rs3
-rw-r--r--src/test/compile-fail/simd-intrinsic-generic-comparison.rs3
-rw-r--r--src/test/compile-fail/simd-intrinsic-generic-elements.rs3
-rw-r--r--src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs8
-rw-r--r--src/test/compile-fail/suggest-private-fields.rs1
-rw-r--r--src/test/compile-fail/trait-duplicate-methods.rs4
-rw-r--r--src/test/compile-fail/trait-object-reference-without-parens-suggestion.rs2
-rw-r--r--src/test/compile-fail/unresolved-extern-mod-suggestion.rs2
-rw-r--r--src/test/compile-fail/use-mod.rs1
-rw-r--r--src/test/compile-fail/variance-unused-type-param.rs3
-rw-r--r--src/test/compile-fail/variant-namespacing.rs32
-rw-r--r--src/test/pretty/issue-4264.pp58
-rw-r--r--src/test/pretty/top-level-doc-comments.rs20
-rw-r--r--src/test/run-fail/divide-by-zero.rs5
-rw-r--r--src/test/run-fail/mod-zero.rs5
-rw-r--r--src/test/run-fail/overflowing-add.rs4
-rw-r--r--src/test/run-fail/overflowing-lsh-1.rs4
-rw-r--r--src/test/run-fail/overflowing-lsh-2.rs4
-rw-r--r--src/test/run-fail/overflowing-lsh-3.rs4
-rw-r--r--src/test/run-fail/overflowing-lsh-4.rs4
-rw-r--r--src/test/run-fail/overflowing-mul.rs4
-rw-r--r--src/test/run-fail/overflowing-neg.rs4
-rw-r--r--src/test/run-fail/overflowing-rsh-1.rs4
-rw-r--r--src/test/run-fail/overflowing-rsh-2.rs4
-rw-r--r--src/test/run-fail/overflowing-rsh-3.rs4
-rw-r--r--src/test/run-fail/overflowing-rsh-4.rs4
-rw-r--r--src/test/run-fail/overflowing-rsh-5.rs4
-rw-r--r--src/test/run-fail/overflowing-rsh-6.rs4
-rw-r--r--src/test/run-fail/overflowing-sub.rs4
-rw-r--r--src/test/run-make/debug-assertions/debug.rs2
-rw-r--r--src/test/run-make/issue-25581/test.c13
-rw-r--r--src/test/run-pass-valgrind/cast-enum-with-dtor.rs5
-rw-r--r--src/test/run-pass/backtrace-debuginfo-aux.rs2
-rw-r--r--src/test/run-pass/backtrace-debuginfo.rs6
-rw-r--r--src/test/run-pass/binary-heap-panic-safe.rs2
-rw-r--r--src/test/run-pass/const-bitshift-rhs-inference.rs33
-rw-r--r--src/test/run-pass/const-str-ptr.rs4
-rw-r--r--src/test/run-pass/float_math.rs24
-rw-r--r--src/test/run-pass/issue-23338-ensure-param-drop-order.rs3
-rw-r--r--src/test/run-pass/issue-31597.rs37
-rw-r--r--src/test/run-pass/issue-32292.rs (renamed from src/test/compile-fail/dupe-symbols-8.rs)15
-rw-r--r--src/test/run-pass/issue-8460.rs55
-rw-r--r--src/test/run-pass/mir_raw_fat_ptr.rs3
-rw-r--r--src/test/run-pass/panic-handler-chain.rs10
-rw-r--r--src/test/run-pass/panic-handler-flail-wildly.rs30
-rw-r--r--src/test/run-pass/panic-handler-set-twice.rs4
-rw-r--r--src/test/run-pass/simd-intrinsic-generic-elements.rs5
-rw-r--r--src/test/run-pass/super-fast-paren-parsing.rs1
-rw-r--r--src/test/run-pass/unique-ffi-symbols.rs25
-rw-r--r--src/test/run-pass/variadic-ffi.rs6
-rw-r--r--src/test/run-pass/volatile-fat-ptr.rs22
-rw-r--r--src/test/run-pass/zero-size-type-destructors.rs4
270 files changed, 6017 insertions, 6713 deletions
diff --git a/src/bootstrap/build/channel.rs b/src/bootstrap/build/channel.rs
index 628b1d76432..5c39356d214 100644
--- a/src/bootstrap/build/channel.rs
+++ b/src/bootstrap/build/channel.rs
@@ -36,19 +36,23 @@ pub fn collect(build: &mut Build) {
     match &build.config.channel[..] {
         "stable" => {
             build.release = release_num.to_string();
+            build.package_vers = build.release.clone();
             build.unstable_features = false;
         }
         "beta" => {
             build.release = format!("{}-beta{}", release_num,
                                    prerelease_version);
+            build.package_vers = "beta".to_string();
             build.unstable_features = false;
         }
         "nightly" => {
             build.release = format!("{}-nightly", release_num);
+            build.package_vers = "nightly".to_string();
             build.unstable_features = true;
         }
         _ => {
             build.release = format!("{}-dev", release_num);
+            build.package_vers = build.release.clone();
             build.unstable_features = true;
         }
     }
diff --git a/src/bootstrap/build/dist.rs b/src/bootstrap/build/dist.rs
new file mode 100644
index 00000000000..855528ea440
--- /dev/null
+++ b/src/bootstrap/build/dist.rs
@@ -0,0 +1,290 @@
+// Copyright 2016 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 std::fs::{self, File};
+use std::io::Write;
+use std::path::{PathBuf, Path};
+use std::process::Command;
+
+use build::{Build, Compiler};
+use build::util::{cp_r, libdir, is_dylib};
+
+fn package_vers(build: &Build) -> &str {
+    match &build.config.channel[..] {
+        "stable" => &build.release,
+        "beta" => "beta",
+        "nightly" => "nightly",
+        _ => &build.release,
+    }
+}
+
+fn distdir(build: &Build) -> PathBuf {
+    build.out.join("dist")
+}
+
+fn tmpdir(build: &Build) -> PathBuf {
+    build.out.join("tmp/dist")
+}
+
+pub fn docs(build: &Build, stage: u32, host: &str) {
+    println!("Dist docs stage{} ({})", stage, host);
+    let name = format!("rust-docs-{}", package_vers(build));
+    let image = tmpdir(build).join(format!("{}-{}-image", name, name));
+    let _ = fs::remove_dir_all(&image);
+
+    let dst = image.join("share/doc/rust/html");
+    t!(fs::create_dir_all(&dst));
+    let src = build.out.join(host).join("doc");
+    cp_r(&src, &dst);
+
+    let mut cmd = Command::new("sh");
+    cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
+       .arg("--product-name=Rust-Documentation")
+       .arg("--rel-manifest-dir=rustlib")
+       .arg("--success-message=Rust-documentation-is-installed.")
+       .arg(format!("--image-dir={}", sanitize_sh(&image)))
+       .arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
+       .arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
+       .arg(format!("--package-name={}", name))
+       .arg("--component-name=rust-docs")
+       .arg("--legacy-manifest-dirs=rustlib,cargo")
+       .arg("--bulk-dirs=share/doc/rust/html");
+    build.run(&mut cmd);
+    t!(fs::remove_dir_all(&image));
+
+    // As part of this step, *also* copy the docs directory to a directory which
+    // buildbot typically uploads.
+    let dst = distdir(build).join("doc").join(&build.package_vers);
+    t!(fs::create_dir_all(&dst));
+    cp_r(&src, &dst);
+}
+
+pub fn mingw(build: &Build, host: &str) {
+    println!("Dist mingw ({})", host);
+    let name = format!("rust-mingw-{}", package_vers(build));
+    let image = tmpdir(build).join(format!("{}-{}-image", name, host));
+    let _ = fs::remove_dir_all(&image);
+
+    // The first argument to the script is a "temporary directory" which is just
+    // thrown away (this contains the runtime DLLs included in the rustc package
+    // above) and the second argument is where to place all the MinGW components
+    // (which is what we want).
+    //
+    // FIXME: this script should be rewritten into Rust
+    let mut cmd = Command::new("python");
+    cmd.arg(build.src.join("src/etc/make-win-dist.py"))
+       .arg(tmpdir(build))
+       .arg(&image)
+       .arg(host);
+    build.run(&mut cmd);
+
+    let mut cmd = Command::new("sh");
+    cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
+       .arg("--product-name=Rust-MinGW")
+       .arg("--rel-manifest-dir=rustlib")
+       .arg("--success-message=Rust-MinGW-is-installed.")
+       .arg(format!("--image-dir={}", sanitize_sh(&image)))
+       .arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
+       .arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
+       .arg(format!("--package-name={}-{}", name, host))
+       .arg("--component-name=rust-mingw")
+       .arg("--legacy-manifest-dirs=rustlib,cargo");
+    build.run(&mut cmd);
+    t!(fs::remove_dir_all(&image));
+}
+
+pub fn rustc(build: &Build, stage: u32, host: &str) {
+    println!("Dist rustc stage{} ({})", stage, host);
+    let name = format!("rustc-{}", package_vers(build));
+    let image = tmpdir(build).join(format!("{}-{}-image", name, host));
+    let _ = fs::remove_dir_all(&image);
+    let overlay = tmpdir(build).join(format!("{}-{}-overlay", name, host));
+    let _ = fs::remove_dir_all(&overlay);
+
+    // Prepare the rustc "image", what will actually end up getting installed
+    prepare_image(build, stage, host, &image);
+
+    // Prepare the overlay which is part of the tarball but won't actually be
+    // installed
+    t!(fs::create_dir_all(&overlay));
+    let cp = |file: &str| {
+        install(&build.src.join(file), &overlay, 0o644);
+    };
+    cp("COPYRIGHT");
+    cp("LICENSE-APACHE");
+    cp("LICENSE-MIT");
+    cp("README.md");
+    // tiny morsel of metadata is used by rust-packaging
+    let version = &build.version;
+    t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
+
+    // On MinGW we've got a few runtime DLL dependencies that we need to
+    // include. The first argument to this script is where to put these DLLs
+    // (the image we're creating), and the second argument is a junk directory
+    // to ignore all other MinGW stuff the script creates.
+    //
+    // On 32-bit MinGW we're always including a DLL which needs some extra
+    // licenses to distribute. On 64-bit MinGW we don't actually distribute
+    // anything requiring us to distribute a license, but it's likely the
+    // install will *also* include the rust-mingw package, which also needs
+    // licenses, so to be safe we just include it here in all MinGW packages.
+    //
+    // FIXME: this script should be rewritten into Rust
+    if host.contains("pc-windows-gnu") {
+        let mut cmd = Command::new("python");
+        cmd.arg(build.src.join("src/etc/make-win-dist.py"))
+           .arg(&image)
+           .arg(tmpdir(build))
+           .arg(host);
+        build.run(&mut cmd);
+
+        let dst = image.join("share/doc");
+        t!(fs::create_dir_all(&dst));
+        cp_r(&build.src.join("src/etc/third-party"), &dst);
+    }
+
+    // Finally, wrap everything up in a nice tarball!
+    let mut cmd = Command::new("sh");
+    cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
+       .arg("--product-name=Rust")
+       .arg("--rel-manifest-dir=rustlib")
+       .arg("--success-message=Rust-is-ready-to-roll.")
+       .arg(format!("--image-dir={}", sanitize_sh(&image)))
+       .arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
+       .arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
+       .arg(format!("--non-installed-overlay={}", sanitize_sh(&overlay)))
+       .arg(format!("--package-name={}-{}", name, host))
+       .arg("--component-name=rustc")
+       .arg("--legacy-manifest-dirs=rustlib,cargo");
+    build.run(&mut cmd);
+    t!(fs::remove_dir_all(&image));
+    t!(fs::remove_dir_all(&overlay));
+
+    fn prepare_image(build: &Build, stage: u32, host: &str, image: &Path) {
+        let src = build.sysroot(&Compiler::new(stage, host));
+        let libdir = libdir(host);
+
+        // Copy rustc/rustdoc binaries
+        t!(fs::create_dir_all(image.join("bin")));
+        cp_r(&src.join("bin"), &image.join("bin"));
+
+        // Copy runtime DLLs needed by the compiler
+        if libdir != "bin" {
+            t!(fs::create_dir_all(image.join(libdir)));
+            for entry in t!(src.join(libdir).read_dir()).map(|e| t!(e)) {
+                let name = entry.file_name();
+                if let Some(s) = name.to_str() {
+                    if is_dylib(s) {
+                        install(&entry.path(), &image.join(libdir), 0o644);
+                    }
+                }
+            }
+        }
+
+        // Man pages
+        t!(fs::create_dir_all(image.join("share/man/man1")));
+        cp_r(&build.src.join("man"), &image.join("share/man/man1"));
+
+        // Debugger scripts
+        let cp_debugger_script = |file: &str| {
+            let dst = image.join("lib/rustlib/etc");
+            t!(fs::create_dir_all(&dst));
+            install(&build.src.join("src/etc/").join(file), &dst, 0o644);
+        };
+        if host.contains("windows") {
+            // no debugger scripts
+        } else if host.contains("darwin") {
+            // lldb debugger scripts
+            install(&build.src.join("src/etc/rust-lldb"), &image.join("bin"),
+                    0o755);
+
+            cp_debugger_script("lldb_rust_formatters.py");
+            cp_debugger_script("debugger_pretty_printers_common.py");
+        } else {
+            // gdb debugger scripts
+            install(&build.src.join("src/etc/rust-gdb"), &image.join("bin"),
+                    0o755);
+
+            cp_debugger_script("gdb_load_rust_pretty_printers.py");
+            cp_debugger_script("gdb_rust_pretty_printing.py");
+            cp_debugger_script("debugger_pretty_printers_common.py");
+        }
+
+        // Misc license info
+        let cp = |file: &str| {
+            install(&build.src.join(file), &image.join("share/doc/rust"), 0o644);
+        };
+        t!(fs::create_dir_all(&image.join("share/doc/rust")));
+        cp("COPYRIGHT");
+        cp("LICENSE-APACHE");
+        cp("LICENSE-MIT");
+        cp("README.md");
+    }
+}
+
+pub fn std(build: &Build, compiler: &Compiler, target: &str) {
+    println!("Dist std stage{} ({} -> {})", compiler.stage, compiler.host,
+             target);
+    let name = format!("rust-std-{}", package_vers(build));
+    let image = tmpdir(build).join(format!("{}-{}-image", name, target));
+    let _ = fs::remove_dir_all(&image);
+
+    let dst = image.join("lib/rustlib").join(target);
+    t!(fs::create_dir_all(&dst));
+    let src = build.sysroot(compiler).join("lib/rustlib");
+    cp_r(&src.join(target), &dst);
+
+    let mut cmd = Command::new("sh");
+    cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
+       .arg("--product-name=Rust")
+       .arg("--rel-manifest-dir=rustlib")
+       .arg("--success-message=std-is-standing-at-the-ready.")
+       .arg(format!("--image-dir={}", sanitize_sh(&image)))
+       .arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
+       .arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
+       .arg(format!("--package-name={}-{}", name, target))
+       .arg(format!("--component-name=rust-std-{}", target))
+       .arg("--legacy-manifest-dirs=rustlib,cargo");
+    build.run(&mut cmd);
+    t!(fs::remove_dir_all(&image));
+}
+
+fn install(src: &Path, dstdir: &Path, perms: u32) {
+    let dst = dstdir.join(src.file_name().unwrap());
+    t!(fs::copy(src, &dst));
+    chmod(&dst, perms);
+}
+
+#[cfg(unix)]
+fn chmod(path: &Path, perms: u32) {
+    use std::os::unix::fs::*;
+    t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
+}
+#[cfg(windows)]
+fn chmod(_path: &Path, _perms: u32) {}
+
+// We have to run a few shell scripts, which choke quite a bit on both `\`
+// characters and on `C:\` paths, so normalize both of them away.
+fn sanitize_sh(path: &Path) -> String {
+    let path = path.to_str().unwrap().replace("\\", "/");
+    return change_drive(&path).unwrap_or(path);
+
+    fn change_drive(s: &str) -> Option<String> {
+        let mut ch = s.chars();
+        let drive = ch.next().unwrap_or('C');
+        if ch.next() != Some(':') {
+            return None
+        }
+        if ch.next() != Some('/') {
+            return None
+        }
+        Some(format!("/{}/{}", drive, &s[drive.len_utf8() + 2..]))
+    }
+}
diff --git a/src/bootstrap/build/mod.rs b/src/bootstrap/build/mod.rs
index 39bd74c78ff..d5b7f0e96aa 100644
--- a/src/bootstrap/build/mod.rs
+++ b/src/bootstrap/build/mod.rs
@@ -34,6 +34,7 @@ mod check;
 mod clean;
 mod compile;
 mod config;
+mod dist;
 mod doc;
 mod flags;
 mod native;
@@ -76,6 +77,7 @@ pub struct Build {
     short_ver_hash: Option<String>,
     ver_date: Option<String>,
     version: String,
+    package_vers: String,
     bootstrap_key: String,
 
     // Runtime state filled in later on
@@ -121,6 +123,7 @@ impl Build {
             ver_date: None,
             version: String::new(),
             bootstrap_key: String::new(),
+            package_vers: String::new(),
             cc: HashMap::new(),
             cxx: HashMap::new(),
             compiler_rt_built: RefCell::new(HashMap::new()),
@@ -208,6 +211,12 @@ impl Build {
                     check::linkcheck(self, stage, target.target);
                 }
 
+                DistDocs { stage } => dist::docs(self, stage, target.target),
+                DistMingw { _dummy } => dist::mingw(self, target.target),
+                DistRustc { stage } => dist::rustc(self, stage, target.target),
+                DistStd { compiler } => dist::std(self, &compiler, target.target),
+
+                Dist { .. } |
                 Doc { .. } | // pseudo-steps
                 Check { .. } => {}
             }
diff --git a/src/bootstrap/build/native.rs b/src/bootstrap/build/native.rs
index 52595d3638c..b3bd6b92299 100644
--- a/src/bootstrap/build/native.rs
+++ b/src/bootstrap/build/native.rs
@@ -114,7 +114,8 @@ pub fn compiler_rt(build: &Build, target: &str) {
     let arch = target.split('-').next().unwrap();
     let mode = if build.config.rust_optimize {"Release"} else {"Debug"};
     let (dir, build_target, libname) = if target.contains("linux") ||
-                                          target.contains("freebsd") {
+                                          target.contains("freebsd") ||
+                                          target.contains("netbsd") {
         let os = if target.contains("android") {"-android"} else {""};
         let arch = if arch.starts_with("arm") && target.contains("eabihf") {
             "armhf"
diff --git a/src/bootstrap/build/step.rs b/src/bootstrap/build/step.rs
index dfac074e3cd..2fd961003a4 100644
--- a/src/bootstrap/build/step.rs
+++ b/src/bootstrap/build/step.rs
@@ -73,6 +73,13 @@ macro_rules! targets {
             // target to depend on a bunch of others.
             (check, Check { stage: u32, compiler: Compiler<'a> }),
             (check_linkcheck, CheckLinkcheck { stage: u32 }),
+
+            // Distribution targets, creating tarballs
+            (dist, Dist { stage: u32 }),
+            (dist_docs, DistDocs { stage: u32 }),
+            (dist_mingw, DistMingw { _dummy: () }),
+            (dist_rustc, DistRustc { stage: u32 }),
+            (dist_std, DistStd { compiler: Compiler<'a> }),
         }
     }
 }
@@ -279,7 +286,8 @@ impl<'a> Step<'a> {
                      self.doc_error_index(stage)]
             }
             Source::Check { stage, compiler: _ } => {
-                vec![self.check_linkcheck(stage)]
+                vec![self.check_linkcheck(stage),
+                     self.dist(stage)]
             }
             Source::CheckLinkcheck { stage } => {
                 vec![self.tool_linkchecker(stage), self.doc(stage)]
@@ -292,6 +300,34 @@ impl<'a> Step<'a> {
             Source::ToolRustbook { stage } => {
                 vec![self.librustc(self.compiler(stage))]
             }
+
+            Source::DistDocs { stage } => vec![self.doc(stage)],
+            Source::DistMingw { _dummy: _ } => Vec::new(),
+            Source::DistRustc { stage } => {
+                vec![self.rustc(stage)]
+            }
+            Source::DistStd { compiler } => {
+                vec![self.libstd(compiler)]
+            }
+
+            Source::Dist { stage } => {
+                let mut base = Vec::new();
+                base.push(self.dist_docs(stage));
+
+                for host in build.config.host.iter() {
+                    let host = self.target(host);
+                    base.push(host.dist_rustc(stage));
+                    if host.target.contains("windows-gnu") {
+                        base.push(host.dist_mingw(()));
+                    }
+
+                    let compiler = self.compiler(stage);
+                    for target in build.config.target.iter() {
+                        base.push(self.target(target).dist_std(compiler));
+                    }
+                }
+                return base
+            }
         }
     }
 }
diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in
index 7d793002149..36f4269a260 100644
--- a/src/bootstrap/mk/Makefile.in
+++ b/src/bootstrap/mk/Makefile.in
@@ -38,3 +38,7 @@ standalone-docs:
 	$(Q)$(BOOTSTRAP) --step doc-standalone
 check:
 	$(Q)$(BOOTSTRAP) --step check
+dist:
+	$(Q)$(BOOTSTRAP) --step dist
+
+.PHONY: dist
diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs
index cf4d545a827..ef93fcfa013 100644
--- a/src/compiletest/header.rs
+++ b/src/compiletest/header.rs
@@ -31,6 +31,8 @@ pub struct TestProps {
     pub pp_exact: Option<PathBuf>,
     // Modules from aux directory that should be compiled
     pub aux_builds: Vec<String> ,
+    // Environment settings to use for compiling
+    pub rustc_env: Vec<(String,String)> ,
     // Environment settings to use during execution
     pub exec_env: Vec<(String,String)> ,
     // Lines to check if they appear in the expected debugger output
@@ -77,6 +79,7 @@ pub fn load_props(testfile: &Path) -> TestProps {
         pp_exact: pp_exact,
         aux_builds: aux_builds,
         revisions: vec![],
+        rustc_env: vec![],
         exec_env: exec_env,
         check_lines: check_lines,
         build_aux_docs: build_aux_docs,
@@ -153,10 +156,14 @@ pub fn load_props_into(props: &mut TestProps, testfile: &Path, cfg: Option<&str>
             props.aux_builds.push(ab);
         }
 
-        if let Some(ee) = parse_exec_env(ln) {
+        if let Some(ee) = parse_env(ln, "exec-env") {
             props.exec_env.push(ee);
         }
 
+        if let Some(ee) = parse_env(ln, "rustc-env") {
+            props.rustc_env.push(ee);
+        }
+
         if let Some(cl) =  parse_check_line(ln) {
             props.check_lines.push(cl);
         }
@@ -372,8 +379,8 @@ fn parse_pretty_compare_only(line: &str) -> bool {
     parse_name_directive(line, "pretty-compare-only")
 }
 
-fn parse_exec_env(line: &str) -> Option<(String, String)> {
-    parse_name_value_directive(line, "exec-env").map(|nv| {
+fn parse_env(line: &str, name: &str) -> Option<(String, String)> {
+    parse_name_value_directive(line, name).map(|nv| {
         // nv is either FOO or FOO=BAR
         let mut strs: Vec<String> = nv
                                       .splitn(2, '=')
diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs
index 8c3ee3fb5f4..5af70e53125 100644
--- a/src/compiletest/runtest.rs
+++ b/src/compiletest/runtest.rs
@@ -863,12 +863,28 @@ fn cleanup_debug_info_options(options: &Option<String>) -> Option<String> {
         "-g".to_owned(),
         "--debuginfo".to_owned()
     ];
-    let new_options =
+    let mut new_options =
         split_maybe_args(options).into_iter()
                                  .filter(|x| !options_to_remove.contains(x))
-                                 .collect::<Vec<String>>()
-                                 .join(" ");
-    Some(new_options)
+                                 .collect::<Vec<String>>();
+
+    let mut i = 0;
+    while i + 1 < new_options.len() {
+        if new_options[i] == "-Z" {
+            // FIXME #31005 MIR missing debuginfo currently.
+            if new_options[i + 1] == "orbit" {
+                // Remove "-Z" and "orbit".
+                new_options.remove(i);
+                new_options.remove(i);
+                continue;
+            }
+            // Always skip over -Z's argument.
+            i += 1;
+        }
+        i += 1;
+    }
+
+    Some(new_options.join(" "))
 }
 
 fn check_debugger_output(debugger_run_result: &ProcRes, check_lines: &[String]) {
@@ -1009,12 +1025,16 @@ fn check_expected_errors(revision: Option<&str>,
         expected.replace(r"\", "/")
     }).collect::<Vec<String>>();
 
+    // If the testcase being checked contains at least one expected "help"
+    // message, then we'll ensure that all "help" messages are expected.
+    // Otherwise, all "help" messages reported by the compiler will be ignored.
+    // This logic also applies to "note" messages.
     let (expect_help, expect_note) =
         expected_errors.iter()
                         .fold((false, false),
                               |(acc_help, acc_note), ee|
-                                  (acc_help || ee.kind == "help:", acc_note ||
-                                   ee.kind == "note:"));
+                                  (acc_help || ee.kind == "help:" || ee.kind == "help",
+                                   acc_note || ee.kind == "note:" || ee.kind == "note"));
 
     // Scan and extract our error/warning messages,
     // which look like:
@@ -1386,7 +1406,7 @@ fn compose_and_run_compiler(config: &Config, props: &TestProps,
     compose_and_run(config,
                     testpaths,
                     args,
-                    Vec::new(),
+                    props.rustc_env.clone(),
                     &config.compile_lib_path,
                     Some(aux_dir.to_str().unwrap()),
                     input)
diff --git a/src/doc/book/getting-started.md b/src/doc/book/getting-started.md
index ca83f2226c0..539fdd32c6b 100644
--- a/src/doc/book/getting-started.md
+++ b/src/doc/book/getting-started.md
@@ -93,8 +93,8 @@ unofficial locations.
 | `armv7-apple-ios`             |  ✓  |     |     | ARM iOS                    |
 | `armv7s-apple-ios`            |  ✓  |     |     | ARM iOS                    |
 | `aarch64-apple-ios`           |  ✓  |     |     | ARM64 iOS                  |
-| `i686-unknown-freebsd`        |  ✓  |  ✓  |     | 32-bit FreeBSD             |
-| `x86_64-unknown-freebsd`      |  ✓  |  ✓  |     | 64-bit FreeBSD             |
+| `i686-unknown-freebsd`        |  ✓  |  ✓  |  ✓  | 32-bit FreeBSD             |
+| `x86_64-unknown-freebsd`      |  ✓  |  ✓  |  ✓  | 64-bit FreeBSD             |
 | `x86_64-unknown-openbsd`      |  ✓  |  ✓  |     | 64-bit OpenBSD             |
 | `x86_64-unknown-netbsd`       |  ✓  |  ✓  |     | 64-bit NetBSD              |
 | `x86_64-unknown-bitrig`       |  ✓  |  ✓  |     | 64-bit Bitrig              |
diff --git a/src/doc/book/guessing-game.md b/src/doc/book/guessing-game.md
index e071bfdf8bc..590c7e84819 100644
--- a/src/doc/book/guessing-game.md
+++ b/src/doc/book/guessing-game.md
@@ -912,7 +912,7 @@ returned by `parse()`, this is an `enum`  like `Ordering`, but in this case,
 each variant has some data associated with it: `Ok` is a success, and `Err` is a
 failure. Each contains more information: the successfully parsed integer, or an
 error type. In this case, we `match` on `Ok(num)`, which sets the name `num` to
-the unwrapped `Ok` value (ythe integer), and then we  return it on the
+the unwrapped `Ok` value (the integer), and then we  return it on the
 right-hand side. In the `Err` case, we don’t care what kind of error it is, so
 we just use the catch all `_` instead of a name. This catches everything that
 isn't `Ok`, and `continue` lets us move to the next iteration of the loop; in
diff --git a/src/doc/book/references-and-borrowing.md b/src/doc/book/references-and-borrowing.md
index 7be5cc442dd..74983c12553 100644
--- a/src/doc/book/references-and-borrowing.md
+++ b/src/doc/book/references-and-borrowing.md
@@ -163,8 +163,8 @@ both at the same time:
 * exactly one mutable reference (`&mut T`).
 
 
-You may notice that this is very similar, though not exactly the same as,
-to the definition of a data race:
+You may notice that this is very similar to, though not exactly the same as,
+the definition of a data race:
 
 > There is a ‘data race’ when two or more pointers access the same memory
 > location at the same time, where at least one of them is writing, and the
diff --git a/src/doc/book/variable-bindings.md b/src/doc/book/variable-bindings.md
index 29b59937a63..1c8c03cf679 100644
--- a/src/doc/book/variable-bindings.md
+++ b/src/doc/book/variable-bindings.md
@@ -18,14 +18,14 @@ function, rather than leaving it off. Otherwise, you’ll get an error.
 
 In many languages, a variable binding would be called a *variable*, but Rust’s
 variable bindings have a few tricks up their sleeves. For example the
-left-hand side of a `let` expression is a ‘[pattern][pattern]’, not a
+left-hand side of a `let` statement is a ‘[pattern][pattern]’, not a
 variable name. This means we can do things like:
 
 ```rust
 let (x, y) = (1, 2);
 ```
 
-After this expression is evaluated, `x` will be one, and `y` will be two.
+After this statement is evaluated, `x` will be one, and `y` will be two.
 Patterns are really powerful, and have [their own section][pattern] in the
 book. We don’t need those features for now, so we’ll keep this in the back
 of our minds as we go forward.
diff --git a/src/libcollections/btree/map.rs b/src/libcollections/btree/map.rs
index 1967f83ab27..7819c7ef796 100644
--- a/src/libcollections/btree/map.rs
+++ b/src/libcollections/btree/map.rs
@@ -1465,6 +1465,13 @@ impl<'a, K: Ord, V> Entry<'a, K, V> {
 }
 
 impl<'a, K: Ord, V> VacantEntry<'a, K, V> {
+    /// Gets a reference to the key that would be used when inserting a value
+    /// through the VacantEntry.
+    #[unstable(feature = "map_entry_keys", issue = "32281")]
+    pub fn key(&self) -> &K {
+        &self.key
+    }
+
     /// Sets the value of the entry with the VacantEntry's key,
     /// and returns a mutable reference to it.
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -1509,6 +1516,12 @@ impl<'a, K: Ord, V> VacantEntry<'a, K, V> {
 }
 
 impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> {
+    /// Gets a reference to the key in the entry.
+    #[unstable(feature = "map_entry_keys", issue = "32281")]
+    pub fn key(&self) -> &K {
+        self.handle.reborrow().into_kv().0
+    }
+
     /// Gets a reference to the value in the entry.
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn get(&self) -> &V {
diff --git a/src/libcollectionstest/btree/map.rs b/src/libcollectionstest/btree/map.rs
index 7f368f0205b..a27c2847a98 100644
--- a/src/libcollectionstest/btree/map.rs
+++ b/src/libcollectionstest/btree/map.rs
@@ -395,6 +395,42 @@ fn test_variance() {
     fn vals<'a, 'new>(v: Values<'a, (), &'static str>) -> Values<'a, (), &'new str> { v }
 }
 
+#[test]
+fn test_occupied_entry_key() {
+    let mut a = BTreeMap::new();
+    let key = "hello there";
+    let value = "value goes here";
+    assert!(a.is_empty());
+    a.insert(key.clone(), value.clone());
+    assert_eq!(a.len(), 1);
+    assert_eq!(a[key], value);
+
+    match a.entry(key.clone()) {
+        Vacant(_) => panic!(),
+        Occupied(e) => assert_eq!(key, *e.key()),
+    }
+    assert_eq!(a.len(), 1);
+    assert_eq!(a[key], value);
+}
+
+#[test]
+fn test_vacant_entry_key() {
+    let mut a = BTreeMap::new();
+    let key = "hello there";
+    let value = "value goes here";
+
+    assert!(a.is_empty());
+    match a.entry(key.clone()) {
+        Occupied(_) => panic!(),
+        Vacant(e) => {
+            assert_eq!(key, *e.key());
+            e.insert(value.clone());
+        },
+    }
+    assert_eq!(a.len(), 1);
+    assert_eq!(a[key], value);
+}
+
 mod bench {
     use std::collections::BTreeMap;
     use std::__rand::{Rng, thread_rng};
diff --git a/src/libcollectionstest/lib.rs b/src/libcollectionstest/lib.rs
index 3596b8306a8..62fefaa10f6 100644
--- a/src/libcollectionstest/lib.rs
+++ b/src/libcollectionstest/lib.rs
@@ -21,6 +21,7 @@
 #![feature(fn_traits)]
 #![feature(enumset)]
 #![feature(iter_arith)]
+#![feature(map_entry_keys)]
 #![feature(pattern)]
 #![feature(rand)]
 #![feature(set_recovery)]
diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs
index 0417ef84163..69cfd0368d6 100644
--- a/src/libcore/intrinsics.rs
+++ b/src/libcore/intrinsics.rs
@@ -539,6 +539,32 @@ extern "rust-intrinsic" {
     /// Returns the nearest integer to an `f64`. Rounds half-way cases away from zero.
     pub fn roundf64(x: f64) -> f64;
 
+    /// Float addition that allows optimizations based on algebraic rules.
+    /// May assume inputs are finite.
+    #[cfg(not(stage0))]
+    pub fn fadd_fast<T>(a: T, b: T) -> T;
+
+    /// Float subtraction that allows optimizations based on algebraic rules.
+    /// May assume inputs are finite.
+    #[cfg(not(stage0))]
+    pub fn fsub_fast<T>(a: T, b: T) -> T;
+
+    /// Float multiplication that allows optimizations based on algebraic rules.
+    /// May assume inputs are finite.
+    #[cfg(not(stage0))]
+    pub fn fmul_fast<T>(a: T, b: T) -> T;
+
+    /// Float division that allows optimizations based on algebraic rules.
+    /// May assume inputs are finite.
+    #[cfg(not(stage0))]
+    pub fn fdiv_fast<T>(a: T, b: T) -> T;
+
+    /// Float remainder that allows optimizations based on algebraic rules.
+    /// May assume inputs are finite.
+    #[cfg(not(stage0))]
+    pub fn frem_fast<T>(a: T, b: T) -> T;
+
+
     /// Returns the number of bits set in an integer type `T`
     pub fn ctpop<T>(x: T) -> T;
 
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index f76b8655ad1..13d3f42ba18 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -72,6 +72,7 @@
 #![feature(reflect)]
 #![feature(unwind_attributes)]
 #![feature(repr_simd, platform_intrinsics)]
+#![feature(rustc_attrs)]
 #![feature(staged_api)]
 #![feature(unboxed_closures)]
 
diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs
index 5a2a043d6f1..e6f83498ab1 100644
--- a/src/libcore/num/mod.rs
+++ b/src/libcore/num/mod.rs
@@ -1008,6 +1008,7 @@ macro_rules! int_impl {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
+        #[cfg_attr(not(stage0), rustc_no_mir)] // FIXME #29769 MIR overflow checking is TBD.
         pub fn pow(self, mut exp: u32) -> Self {
             let mut base = self;
             let mut acc = Self::one();
@@ -1049,6 +1050,7 @@ macro_rules! int_impl {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
+        #[cfg_attr(not(stage0), rustc_no_mir)] // FIXME #29769 MIR overflow checking is TBD.
         pub fn abs(self) -> Self {
             if self.is_negative() {
                 // Note that the #[inline] above means that the overflow
@@ -2013,6 +2015,7 @@ macro_rules! uint_impl {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
+        #[cfg_attr(not(stage0), rustc_no_mir)] // FIXME #29769 MIR overflow checking is TBD.
         pub fn pow(self, mut exp: u32) -> Self {
             let mut base = self;
             let mut acc = Self::one();
diff --git a/src/librustc/front/map/mod.rs b/src/librustc/front/map/mod.rs
index 44f588c2e9c..dfc8560b58d 100644
--- a/src/librustc/front/map/mod.rs
+++ b/src/librustc/front/map/mod.rs
@@ -22,6 +22,7 @@ use middle::def_id::DefId;
 
 use syntax::abi::Abi;
 use syntax::ast::{self, Name, NodeId, DUMMY_NODE_ID};
+use syntax::attr::ThinAttributesExt;
 use syntax::codemap::{Span, Spanned};
 use syntax::parse::token;
 
@@ -718,6 +719,8 @@ impl<'ast> Map<'ast> {
             Some(NodeTraitItem(ref ti)) => Some(&ti.attrs[..]),
             Some(NodeImplItem(ref ii)) => Some(&ii.attrs[..]),
             Some(NodeVariant(ref v)) => Some(&v.node.attrs[..]),
+            Some(NodeExpr(ref e)) => Some(e.attrs.as_attr_slice()),
+            Some(NodeStmt(ref s)) => Some(s.node.attrs()),
             // unit/tuple structs take the attributes straight from
             // the struct definition.
             Some(NodeStructCtor(_)) => {
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 3988545c201..d2ca1cd3f93 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -73,7 +73,6 @@ mod macros;
 pub mod diagnostics;
 
 pub mod back {
-    pub use rustc_back::abi;
     pub use rustc_back::rpath;
     pub use rustc_back::svh;
 }
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 97a550a4076..d99d6afd812 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -154,6 +154,12 @@ declare_lint! {
     "transmute from function item type to pointer-sized type erroneously allowed"
 }
 
+declare_lint! {
+    pub OVERLAPPING_INHERENT_IMPLS,
+    Warn,
+    "two overlapping inherent impls define an item with the same name were erroneously allowed"
+}
+
 /// Does nothing as a lint pass, but registers some `Lint`s
 /// which are used by other parts of the compiler.
 #[derive(Copy, Clone)]
@@ -184,7 +190,8 @@ impl LintPass for HardwiredLints {
             MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
             CONST_ERR,
             RAW_POINTER_DERIVE,
-            TRANSMUTE_FROM_FN_ITEM_TYPES
+            TRANSMUTE_FROM_FN_ITEM_TYPES,
+            OVERLAPPING_INHERENT_IMPLS
         )
     }
 }
diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs
index 1fb27261c4d..dcfa8d1e36a 100644
--- a/src/librustc/middle/cfg/construct.rs
+++ b/src/librustc/middle/cfg/construct.rs
@@ -354,19 +354,10 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
                 self.straightline(expr, pred, Some(&**e).into_iter())
             }
 
-            hir::ExprInlineAsm(ref inline_asm) => {
-                let inputs = inline_asm.inputs.iter();
-                let outputs = inline_asm.outputs.iter();
-                let post_inputs = self.exprs(inputs.map(|a| {
-                    debug!("cfg::construct InlineAsm id:{} input:{:?}", expr.id, a);
-                    let &(_, ref expr) = a;
-                    &**expr
-                }), pred);
-                let post_outputs = self.exprs(outputs.map(|a| {
-                    debug!("cfg::construct InlineAsm id:{} output:{:?}", expr.id, a);
-                    &*a.expr
-                }), post_inputs);
-                self.add_ast_node(expr.id, &[post_outputs])
+            hir::ExprInlineAsm(_, ref outputs, ref inputs) => {
+                let post_outputs = self.exprs(outputs.iter().map(|e| &**e), pred);
+                let post_inputs = self.exprs(inputs.iter().map(|e| &**e), post_outputs);
+                self.add_ast_node(expr.id, &[post_inputs])
             }
 
             hir::ExprClosure(..) |
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index 89b57e0d90a..16e0a334440 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -475,9 +475,9 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
                 let def = self.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def());
                 match def {
                     Some(Def::AssociatedConst(did)) |
-                    Some(Def::Const(did)) => match lookup_const_by_id(self.tcx, did,
-                                                                    Some(pat.id), None) {
-                        Some((const_expr, _const_ty)) => {
+                    Some(Def::Const(did)) => {
+                        let substs = Some(self.tcx.node_id_item_substs(pat.id).substs);
+                        if let Some((const_expr, _)) = lookup_const_by_id(self.tcx, did, substs) {
                             const_expr_to_pat(self.tcx, const_expr, pat.span).map(|new_pat| {
 
                                 if let Some(ref mut renaming_map) = self.renaming_map {
@@ -487,14 +487,13 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
 
                                 new_pat
                             })
-                        }
-                        None => {
+                        } else {
                             self.failed = true;
                             span_err!(self.tcx.sess, pat.span, E0158,
                                 "statics cannot be referenced in patterns");
                             pat
                         }
-                    },
+                    }
                     _ => noop_fold_pat(pat, self)
                 }
             }
diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs
index 8a1a0080eb0..47b6c49fddb 100644
--- a/src/librustc/middle/const_eval.rs
+++ b/src/librustc/middle/const_eval.rs
@@ -19,7 +19,6 @@ use front::map::blocks::FnLikeNode;
 use middle::cstore::{self, CrateStore, InlinedItem};
 use middle::{infer, subst, traits};
 use middle::def::Def;
-use middle::subst::Subst;
 use middle::def_id::DefId;
 use middle::pat_util::def_to_path;
 use middle::ty::{self, Ty, TyCtxt};
@@ -89,16 +88,13 @@ fn lookup_variant_by_id<'a>(tcx: &'a ty::TyCtxt,
 }
 
 /// * `def_id` is the id of the constant.
-/// * `maybe_ref_id` is the id of the expr referencing the constant.
-/// * `param_substs` is the monomorphization substitution for the expression.
+/// * `substs` is the monomorphized substitutions for the expression.
 ///
-/// `maybe_ref_id` and `param_substs` are optional and are used for
-/// finding substitutions in associated constants. This generally
-/// happens in late/trans const evaluation.
+/// `substs` is optional and is used for associated constants.
+/// This generally happens in late/trans const evaluation.
 pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>,
                                         def_id: DefId,
-                                        maybe_ref_id: Option<ast::NodeId>,
-                                        param_substs: Option<&'tcx subst::Substs<'tcx>>)
+                                        substs: Option<subst::Substs<'tcx>>)
                                         -> Option<(&'tcx Expr, Option<ty::Ty<'tcx>>)> {
     if let Some(node_id) = tcx.map.as_local_node_id(def_id) {
         match tcx.map.find(node_id) {
@@ -111,28 +107,20 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>,
             },
             Some(ast_map::NodeTraitItem(ti)) => match ti.node {
                 hir::ConstTraitItem(_, _) => {
-                    match maybe_ref_id {
-                        // If we have a trait item, and we know the expression
-                        // that's the source of the obligation to resolve it,
+                    if let Some(substs) = substs {
+                        // If we have a trait item and the substitutions for it,
                         // `resolve_trait_associated_const` will select an impl
                         // or the default.
-                        Some(ref_id) => {
-                            let trait_id = tcx.trait_of_item(def_id)
-                                              .unwrap();
-                            let mut substs = tcx.node_id_item_substs(ref_id)
-                                                .substs;
-                            if let Some(param_substs) = param_substs {
-                                substs = substs.subst(tcx, param_substs);
-                            }
-                            resolve_trait_associated_const(tcx, ti, trait_id, substs)
-                        }
+                        let trait_id = tcx.trait_of_item(def_id).unwrap();
+                        resolve_trait_associated_const(tcx, ti, trait_id, substs)
+                    } else {
                         // Technically, without knowing anything about the
                         // expression that generates the obligation, we could
                         // still return the default if there is one. However,
                         // it's safer to return `None` than to return some value
                         // that may differ from what you would get from
                         // correctly selecting an impl.
-                        None => None
+                        None
                     }
                 }
                 _ => None
@@ -153,7 +141,7 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>,
             }
             None => {}
         }
-        let mut used_ref_id = false;
+        let mut used_substs = false;
         let expr_ty = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) {
             cstore::FoundAst::Found(&InlinedItem::Item(ref item)) => match item.node {
                 hir::ItemConst(ref ty, ref const_expr) => {
@@ -163,21 +151,15 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>,
             },
             cstore::FoundAst::Found(&InlinedItem::TraitItem(trait_id, ref ti)) => match ti.node {
                 hir::ConstTraitItem(_, _) => {
-                    used_ref_id = true;
-                    match maybe_ref_id {
+                    used_substs = true;
+                    if let Some(substs) = substs {
                         // As mentioned in the comments above for in-crate
                         // constants, we only try to find the expression for
                         // a trait-associated const if the caller gives us
-                        // the expression that refers to it.
-                        Some(ref_id) => {
-                            let mut substs = tcx.node_id_item_substs(ref_id)
-                                                .substs;
-                            if let Some(param_substs) = param_substs {
-                                substs = substs.subst(tcx, param_substs);
-                            }
-                            resolve_trait_associated_const(tcx, ti, trait_id, substs)
-                        }
-                        None => None
+                        // the substitutions for the reference to it.
+                        resolve_trait_associated_const(tcx, ti, trait_id, substs)
+                    } else {
+                        None
                     }
                 }
                 _ => None
@@ -190,10 +172,10 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>,
             },
             _ => None
         };
-        // If we used the reference expression, particularly to choose an impl
+        // If we used the substitutions, particularly to choose an impl
         // of a trait-associated const, don't cache that, because the next
         // lookup with the same def_id may yield a different result.
-        if !used_ref_id {
+        if !used_substs {
             tcx.extern_const_statics
                .borrow_mut()
                .insert(def_id, expr_ty.map(|(e, t)| (e.id, t)));
@@ -389,7 +371,8 @@ pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P<hir::Pat> {
                     PatKind::Path(path.clone()),
                 Some(Def::Const(def_id)) |
                 Some(Def::AssociatedConst(def_id)) => {
-                    let (expr, _ty) = lookup_const_by_id(tcx, def_id, Some(expr.id), None).unwrap();
+                    let substs = Some(tcx.node_id_item_substs(expr.id).substs);
+                    let (expr, _ty) = lookup_const_by_id(tcx, def_id, substs).unwrap();
                     return const_expr_to_pat(tcx, expr, span);
                 },
                 _ => unreachable!(),
@@ -671,7 +654,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>,
       }
       hir::ExprBinary(op, ref a, ref b) => {
         let b_ty = match op.node {
-            hir::BiShl | hir::BiShr => ty_hint.checked_or(tcx.types.usize),
+            hir::BiShl | hir::BiShr => ty_hint.erase_hint(),
             _ => ty_hint
         };
         // technically, if we don't have type hints, but integral eval
@@ -788,12 +771,12 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>,
           match opt_def {
               Def::Const(def_id) |
               Def::AssociatedConst(def_id) => {
-                  let maybe_ref_id = if let ExprTypeChecked = ty_hint {
-                      Some(e.id)
+                  let substs = if let ExprTypeChecked = ty_hint {
+                      Some(tcx.node_id_item_substs(e.id).substs)
                   } else {
                       None
                   };
-                  if let Some((e, ty)) = lookup_const_by_id(tcx, def_id, maybe_ref_id, None) {
+                  if let Some((e, ty)) = lookup_const_by_id(tcx, def_id, substs) {
                       let item_hint = match ty {
                           Some(ty) => ty_hint.checked_or(ty),
                           None => ty_hint,
@@ -1077,7 +1060,7 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>,
         traits::VtableImpl(ref impl_data) => {
             match tcx.associated_consts(impl_data.impl_def_id)
                      .iter().find(|ic| ic.name == ti.name) {
-                Some(ic) => lookup_const_by_id(tcx, ic.def_id, None, None),
+                Some(ic) => lookup_const_by_id(tcx, ic.def_id, None),
                 None => match ti.node {
                     hir::ConstTraitItem(ref ty, Some(ref expr)) => {
                         Some((&*expr, ast_ty_to_prim_ty(tcx, ty)))
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index 6a4680ecbaf..f85d8741384 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -122,7 +122,7 @@ pub struct ChildItem {
 
 pub enum FoundAst<'ast> {
     Found(&'ast InlinedItem),
-    FoundParent(DefId, &'ast InlinedItem),
+    FoundParent(DefId, &'ast hir::Item),
     NotFound,
 }
 
@@ -182,7 +182,7 @@ pub trait CrateStore<'tcx> : Any {
     fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId)
                      -> Option<DefId>;
     fn impl_or_trait_item(&self, tcx: &TyCtxt<'tcx>, def: DefId)
-                          -> ty::ImplOrTraitItem<'tcx>;
+                          -> Option<ty::ImplOrTraitItem<'tcx>>;
 
     // flags
     fn is_const_fn(&self, did: DefId) -> bool;
@@ -353,7 +353,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId)
                      -> Option<DefId> { unimplemented!() }
     fn impl_or_trait_item(&self, tcx: &TyCtxt<'tcx>, def: DefId)
-                          -> ty::ImplOrTraitItem<'tcx> { unimplemented!() }
+                          -> Option<ty::ImplOrTraitItem<'tcx>> { unimplemented!() }
 
     // flags
     fn is_const_fn(&self, did: DefId) -> bool { unimplemented!() }
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index a87ce1206b4..91ffef2aa59 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -449,23 +449,20 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
                 }
             }
 
-            hir::ExprInlineAsm(ref ia) => {
-                for &(_, ref input) in &ia.inputs {
-                    self.consume_expr(&input);
-                }
-
-                for output in &ia.outputs {
-                    if output.is_indirect {
-                        self.consume_expr(&output.expr);
+            hir::ExprInlineAsm(ref ia, ref outputs, ref inputs) => {
+                for (o, output) in ia.outputs.iter().zip(outputs) {
+                    if o.is_indirect {
+                        self.consume_expr(output);
                     } else {
-                        self.mutate_expr(expr, &output.expr,
-                                         if output.is_rw {
+                        self.mutate_expr(expr, output,
+                                         if o.is_rw {
                                              MutateMode::WriteAndRead
                                          } else {
                                              MutateMode::JustWrite
                                          });
                     }
                 }
+                self.consume_exprs(inputs);
             }
 
             hir::ExprBreak(..) |
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 04240bf2875..d5c0980b280 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -112,7 +112,10 @@ use self::VarKind::*;
 use dep_graph::DepNode;
 use middle::def::*;
 use middle::pat_util;
-use middle::ty::{self, TyCtxt};
+use middle::ty::{self, TyCtxt, ParameterEnvironment};
+use middle::traits::{self, ProjectionMode};
+use middle::infer;
+use middle::subst::Subst;
 use lint;
 use util::nodemap::NodeMap;
 
@@ -1168,25 +1171,21 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
             self.propagate_through_expr(&e, succ)
           }
 
-          hir::ExprInlineAsm(ref ia) => {
-
-            let succ = ia.outputs.iter().rev().fold(succ,
-                |succ, out| {
-                    // see comment on lvalues
-                    // in propagate_through_lvalue_components()
-                    if out.is_indirect {
-                        self.propagate_through_expr(&out.expr, succ)
-                    } else {
-                        let acc = if out.is_rw { ACC_WRITE|ACC_READ } else { ACC_WRITE };
-                        let succ = self.write_lvalue(&out.expr, succ, acc);
-                        self.propagate_through_lvalue_components(&out.expr, succ)
-                    }
+          hir::ExprInlineAsm(ref ia, ref outputs, ref inputs) => {
+            let succ = ia.outputs.iter().zip(outputs).rev().fold(succ, |succ, (o, output)| {
+                // see comment on lvalues
+                // in propagate_through_lvalue_components()
+                if o.is_indirect {
+                    self.propagate_through_expr(output, succ)
+                } else {
+                    let acc = if o.is_rw { ACC_WRITE|ACC_READ } else { ACC_WRITE };
+                    let succ = self.write_lvalue(output, succ, acc);
+                    self.propagate_through_lvalue_components(output, succ)
                 }
-            );
+            });
+
             // Inputs are executed first. Propagate last because of rev order
-            ia.inputs.iter().rev().fold(succ, |succ, &(_, ref expr)| {
-                self.propagate_through_expr(&expr, succ)
-            })
+            self.propagate_through_exprs(inputs, succ)
           }
 
           hir::ExprLit(..) => {
@@ -1423,17 +1422,17 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
         intravisit::walk_expr(this, expr);
       }
 
-      hir::ExprInlineAsm(ref ia) => {
-        for &(_, ref input) in &ia.inputs {
-          this.visit_expr(&input);
+      hir::ExprInlineAsm(ref ia, ref outputs, ref inputs) => {
+        for input in inputs {
+          this.visit_expr(input);
         }
 
         // Output operands must be lvalues
-        for out in &ia.outputs {
-          if !out.is_indirect {
-            this.check_lvalue(&out.expr);
+        for (o, output) in ia.outputs.iter().zip(outputs) {
+          if !o.is_indirect {
+            this.check_lvalue(output);
           }
-          this.visit_expr(&out.expr);
+          this.visit_expr(output);
         }
 
         intravisit::walk_expr(this, expr);
@@ -1490,9 +1489,20 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
 
         match fn_ret {
             ty::FnConverging(t_ret)
-                if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() => {
-
-                if t_ret.is_nil() {
+                    if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() => {
+
+                let param_env = ParameterEnvironment::for_item(&self.ir.tcx, id);
+                let t_ret_subst = t_ret.subst(&self.ir.tcx, &param_env.free_substs);
+                let infcx = infer::new_infer_ctxt(&self.ir.tcx,
+                                                  &self.ir.tcx.tables,
+                                                  Some(param_env),
+                                                  ProjectionMode::Any);
+                let cause = traits::ObligationCause::dummy();
+                let norm = traits::fully_normalize(&infcx,
+                                                   cause,
+                                                   &t_ret_subst);
+
+                if norm.unwrap().is_nil() {
                     // for nil return types, it is ok to not return a value expl.
                 } else {
                     let ends_with_stmt = match body.expr {
diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs
index 08119683593..6344553e88b 100644
--- a/src/librustc/middle/ty/mod.rs
+++ b/src/librustc/middle/ty/mod.rs
@@ -2182,7 +2182,8 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn impl_or_trait_item(&self, id: DefId) -> ImplOrTraitItem<'tcx> {
         lookup_locally_or_in_crate_store(
             "impl_or_trait_items", id, &self.impl_or_trait_items,
-            || self.sess.cstore.impl_or_trait_item(self, id))
+            || self.sess.cstore.impl_or_trait_item(self, id)
+                   .expect("missing ImplOrTraitItem in metadata"))
     }
 
     pub fn trait_item_def_ids(&self, id: DefId) -> Rc<Vec<ImplOrTraitItemId>> {
@@ -2502,10 +2503,12 @@ impl<'tcx> TyCtxt<'tcx> {
     /// ID of the impl that the method belongs to. Otherwise, return `None`.
     pub fn impl_of_method(&self, def_id: DefId) -> Option<DefId> {
         if def_id.krate != LOCAL_CRATE {
-            return match self.sess.cstore.impl_or_trait_item(self, def_id).container() {
-                TraitContainer(_) => None,
-                ImplContainer(def_id) => Some(def_id),
-            };
+            return self.sess.cstore.impl_or_trait_item(self, def_id).and_then(|item| {
+                match item.container() {
+                    TraitContainer(_) => None,
+                    ImplContainer(def_id) => Some(def_id),
+                }
+            });
         }
         match self.impl_or_trait_items.borrow().get(&def_id).cloned() {
             Some(trait_item) => {
diff --git a/src/librustc/middle/ty/sty.rs b/src/librustc/middle/ty/sty.rs
index bbc5948f2ca..2d4d4e51ba7 100644
--- a/src/librustc/middle/ty/sty.rs
+++ b/src/librustc/middle/ty/sty.rs
@@ -948,7 +948,7 @@ impl<'tcx> TyS<'tcx> {
         }
     }
 
-    fn is_slice(&self) -> bool {
+    pub fn is_slice(&self) -> bool {
         match self.sty {
             TyRawPtr(mt) | TyRef(_, mt) => match mt.ty.sty {
                 TySlice(_) | TyStr => true,
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs
index 11bb381ec33..06d68af8838 100644
--- a/src/librustc/mir/repr.rs
+++ b/src/librustc/mir/repr.rs
@@ -14,6 +14,7 @@ use rustc_const_eval::{ConstUsize, ConstInt};
 use middle::def_id::DefId;
 use middle::subst::Substs;
 use middle::ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
+use util::ppaux;
 use rustc_back::slice;
 use rustc_front::hir::InlineAsm;
 use std::ascii;
@@ -177,6 +178,10 @@ pub struct TempDecl<'tcx> {
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct ArgDecl<'tcx> {
     pub ty: Ty<'tcx>,
+
+    /// If true, this argument is a tuple after monomorphization,
+    /// and has to be collected from multiple actual arguments.
+    pub spread: bool
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -675,7 +680,11 @@ pub enum Rvalue<'tcx> {
         from_end: usize,
     },
 
-    InlineAsm(InlineAsm),
+    InlineAsm {
+        asm: InlineAsm,
+        outputs: Vec<Lvalue<'tcx>>,
+        inputs: Vec<Operand<'tcx>>
+    }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
@@ -760,7 +769,9 @@ impl<'tcx> Debug for Rvalue<'tcx> {
             BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b),
             UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
             Box(ref t) => write!(fmt, "Box({:?})", t),
-            InlineAsm(ref asm) => write!(fmt, "InlineAsm({:?})", asm),
+            InlineAsm { ref asm, ref outputs, ref inputs } => {
+                write!(fmt, "asm!({:?} : {:?} : {:?})", asm, outputs, inputs)
+            }
             Slice { ref input, from_start, from_end } =>
                 write!(fmt, "{:?}[{:?}..-{:?}]", input, from_start, from_end),
 
@@ -775,8 +786,8 @@ impl<'tcx> Debug for Rvalue<'tcx> {
             Aggregate(ref kind, ref lvs) => {
                 use self::AggregateKind::*;
 
-                fn fmt_tuple(fmt: &mut Formatter, name: &str, lvs: &[Operand]) -> fmt::Result {
-                    let mut tuple_fmt = fmt.debug_tuple(name);
+                fn fmt_tuple(fmt: &mut Formatter, lvs: &[Operand]) -> fmt::Result {
+                    let mut tuple_fmt = fmt.debug_tuple("");
                     for lv in lvs {
                         tuple_fmt.field(lv);
                     }
@@ -790,19 +801,24 @@ impl<'tcx> Debug for Rvalue<'tcx> {
                         match lvs.len() {
                             0 => write!(fmt, "()"),
                             1 => write!(fmt, "({:?},)", lvs[0]),
-                            _ => fmt_tuple(fmt, "", lvs),
+                            _ => fmt_tuple(fmt, lvs),
                         }
                     }
 
-                    Adt(adt_def, variant, _) => {
+                    Adt(adt_def, variant, substs) => {
                         let variant_def = &adt_def.variants[variant];
-                        let name = ty::tls::with(|tcx| tcx.item_path_str(variant_def.did));
+
+                        try!(ppaux::parameterized(fmt, substs, variant_def.did,
+                                                  ppaux::Ns::Value, &[],
+                                                  |tcx| {
+                            tcx.lookup_item_type(variant_def.did).generics
+                        }));
 
                         match variant_def.kind() {
-                            ty::VariantKind::Unit => write!(fmt, "{}", name),
-                            ty::VariantKind::Tuple => fmt_tuple(fmt, &name, lvs),
+                            ty::VariantKind::Unit => Ok(()),
+                            ty::VariantKind::Tuple => fmt_tuple(fmt, lvs),
                             ty::VariantKind::Struct => {
-                                let mut struct_fmt = fmt.debug_struct(&name);
+                                let mut struct_fmt = fmt.debug_struct("");
                                 for (field, lv) in variant_def.fields.iter().zip(lvs) {
                                     struct_fmt.field(&field.name.as_str(), lv);
                                 }
@@ -882,8 +898,10 @@ impl<'tcx> Debug for Literal<'tcx> {
     fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
         use self::Literal::*;
         match *self {
-            Item { def_id, .. } =>
-                write!(fmt, "{}", item_path_str(def_id)),
+            Item { def_id, substs } => {
+                ppaux::parameterized(fmt, substs, def_id, ppaux::Ns::Value, &[],
+                                     |tcx| tcx.lookup_item_type(def_id).generics)
+            }
             Value { ref value } => {
                 try!(write!(fmt, "const "));
                 fmt_const_val(fmt, value)
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index b6b2694a7cb..c8a2bc440ce 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -16,6 +16,7 @@
 use mir::repr::*;
 use middle::subst::{Subst, Substs};
 use middle::ty::{self, AdtDef, Ty, TyCtxt};
+use middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
 use rustc_front::hir;
 
 #[derive(Copy, Clone, Debug)]
@@ -77,6 +78,29 @@ impl<'tcx> LvalueTy<'tcx> {
     }
 }
 
+impl<'tcx> TypeFoldable<'tcx> for LvalueTy<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        match *self {
+            LvalueTy::Ty { ty } => LvalueTy::Ty { ty: ty.fold_with(folder) },
+            LvalueTy::Downcast { adt_def, substs, variant_index } => {
+                let substs = substs.fold_with(folder);
+                LvalueTy::Downcast {
+                    adt_def: adt_def,
+                    substs: folder.tcx().mk_substs(substs),
+                    variant_index: variant_index
+                }
+            }
+        }
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        match *self {
+            LvalueTy::Ty { ty } => ty.visit_with(visitor),
+            LvalueTy::Downcast { substs, .. } => substs.visit_with(visitor)
+        }
+    }
+}
+
 impl<'tcx> Mir<'tcx> {
     pub fn operand_ty(&self,
                       tcx: &TyCtxt<'tcx>,
@@ -196,7 +220,7 @@ impl<'tcx> Mir<'tcx> {
                 }
             }
             Rvalue::Slice { .. } => None,
-            Rvalue::InlineAsm(..) => None
+            Rvalue::InlineAsm { .. } => None
         }
     }
 }
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index 5e3c6e028a3..36d45f0a51e 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -261,7 +261,14 @@ macro_rules! make_mir_visitor {
                         });
                     }
 
-                    Rvalue::InlineAsm(_) => {
+                    Rvalue::InlineAsm { ref $($mutability)* outputs,
+                                        ref $($mutability)* inputs, .. } => {
+                        for output in & $($mutability)* outputs[..] {
+                            self.visit_lvalue(output, LvalueContext::Store);
+                        }
+                        for input in & $($mutability)* inputs[..] {
+                            self.visit_operand(input);
+                        }
                     }
                 }
             }
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 9f097215a8a..17f70b2d8dc 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -663,6 +663,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
           "print the result of the translation item collection pass"),
     mir_opt_level: Option<usize> = (None, parse_opt_uint,
           "set the MIR optimization level (0-3)"),
+    orbit: bool = (false, parse_bool,
+          "get MIR where it belongs - everywhere; most importantly, in orbit"),
 }
 
 pub fn default_lib_output() -> CrateType {
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 8fd784cbde7..e1690f1a859 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -60,17 +60,37 @@ fn fn_sig(f: &mut fmt::Formatter,
     }
 }
 
-fn parameterized<GG>(f: &mut fmt::Formatter,
-                     substs: &subst::Substs,
-                     did: DefId,
-                     projections: &[ty::ProjectionPredicate],
-                     get_generics: GG)
-                     -> fmt::Result
+/// Namespace of the path given to parameterized to print.
+#[derive(Copy, Clone, PartialEq)]
+pub enum Ns {
+    Type,
+    Value
+}
+
+pub fn parameterized<GG>(f: &mut fmt::Formatter,
+                         substs: &subst::Substs,
+                         did: DefId,
+                         ns: Ns,
+                         projections: &[ty::ProjectionPredicate],
+                         get_generics: GG)
+                         -> fmt::Result
     where GG: for<'tcx> FnOnce(&TyCtxt<'tcx>) -> ty::Generics<'tcx>
 {
-    let (fn_trait_kind, verbose) = try!(ty::tls::with(|tcx| {
+    if let (Ns::Value, Some(self_ty)) = (ns, substs.self_ty()) {
+        try!(write!(f, "<{} as ", self_ty));
+    }
+
+    let (fn_trait_kind, verbose, last_name) = try!(ty::tls::with(|tcx| {
+        let (did, last_name) = if ns == Ns::Value {
+            // Try to get the impl/trait parent, if this is an
+            // associated value item (method or constant).
+            tcx.trait_of_item(did).or_else(|| tcx.impl_of_method(did))
+               .map_or((did, None), |parent| (parent, Some(tcx.item_name(did))))
+        } else {
+            (did, None)
+        };
         try!(write!(f, "{}", tcx.item_path_str(did)));
-        Ok((tcx.lang_items.fn_trait_kind(did), tcx.sess.verbose()))
+        Ok((tcx.lang_items.fn_trait_kind(did), tcx.sess.verbose(), last_name))
     }));
 
     let mut empty = true;
@@ -185,7 +205,28 @@ fn parameterized<GG>(f: &mut fmt::Formatter,
                     projection.ty));
     }
 
-    start_or_continue(f, "", ">")
+    try!(start_or_continue(f, "", ">"));
+
+    // For values, also print their name and type parameters.
+    if ns == Ns::Value {
+        if substs.self_ty().is_some() {
+            try!(write!(f, ">"));
+        }
+
+        if let Some(name) = last_name {
+            try!(write!(f, "::{}", name));
+        }
+        let tps = substs.types.get_slice(subst::FnSpace);
+        if !tps.is_empty() {
+            try!(write!(f, "::<{}", tps[0]));
+            for ty in &tps[1..] {
+                try!(write!(f, ", {}", ty));
+            }
+            try!(write!(f, ">"));
+        }
+    }
+
+    Ok(())
 }
 
 fn in_binder<'tcx, T, U>(f: &mut fmt::Formatter,
@@ -265,6 +306,7 @@ impl<'tcx> fmt::Display for TraitAndProjections<'tcx> {
         let TraitAndProjections(ref trait_ref, ref projection_bounds) = *self;
         parameterized(f, trait_ref.substs,
                       trait_ref.def_id,
+                      Ns::Type,
                       projection_bounds,
                       |tcx| tcx.lookup_trait_def(trait_ref.def_id).generics.clone())
     }
@@ -769,7 +811,7 @@ impl fmt::Display for ty::Binder<ty::OutlivesPredicate<ty::Region, ty::Region>>
 
 impl<'tcx> fmt::Display for ty::TraitRef<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        parameterized(f, self.substs, self.def_id, &[],
+        parameterized(f, self.substs, self.def_id, Ns::Type, &[],
                       |tcx| tcx.lookup_trait_def(self.def_id).generics.clone())
     }
 }
@@ -821,19 +863,9 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
                     try!(write!(f, "extern {} ", bare_fn.abi));
                 }
 
-                try!(write!(f, "{}", bare_fn.sig.0));
-                try!(ty::tls::with(|tcx| {
-                    write!(f, " {{{}", tcx.item_path_str(def_id))
-                }));
-
-                let tps = substs.types.get_slice(subst::FnSpace);
-                if tps.len() >= 1 {
-                    try!(write!(f, "::<{}", tps[0]));
-                    for &ty in &tps[1..] {
-                        try!(write!(f, ", {}", ty));
-                    }
-                    try!(write!(f, ">"));
-                }
+                try!(write!(f, "{} {{", bare_fn.sig.0));
+                try!(parameterized(f, substs, def_id, Ns::Value, &[],
+                                   |tcx| tcx.lookup_item_type(def_id).generics));
                 write!(f, "}}")
             }
             TyFnPtr(ref bare_fn) => {
@@ -856,7 +888,7 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
                           !tcx.tcache.borrow().contains_key(&def.did) {
                         write!(f, "{}<..>", tcx.item_path_str(def.did))
                     } else {
-                        parameterized(f, substs, def.did, &[],
+                        parameterized(f, substs, def.did, Ns::Type, &[],
                                       |tcx| tcx.lookup_item_type(def.did).generics)
                     }
                 })
diff --git a/src/librustc_back/abi.rs b/src/librustc_back/abi.rs
deleted file mode 100644
index c3a3a8d582a..00000000000
--- a/src/librustc_back/abi.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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.
-
-pub const BOX_FIELD_DROP_GLUE: usize = 1;
-pub const BOX_FIELD_BODY: usize = 4;
-
-/// The first half of a fat pointer.
-/// - For a closure, this is the code address.
-/// - For an object or trait instance, this is the address of the box.
-/// - For a slice, this is the base address.
-pub const FAT_PTR_ADDR: usize = 0;
-
-/// The second half of a fat pointer.
-/// - For a closure, this is the address of the environment.
-/// - For an object or trait instance, this is the address of the vtable.
-/// - For a slice, this is the length.
-pub const FAT_PTR_EXTRA: usize = 1;
diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs
index 2b677d665d4..3ffc031d621 100644
--- a/src/librustc_back/lib.rs
+++ b/src/librustc_back/lib.rs
@@ -48,7 +48,6 @@ extern crate rustc_llvm;
 extern crate rustc_front;
 #[macro_use] extern crate log;
 
-pub mod abi;
 pub mod tempdir;
 pub mod rpath;
 pub mod sha2;
diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs
index b01b80b8133..3507a119e54 100644
--- a/src/librustc_back/svh.rs
+++ b/src/librustc_back/svh.rs
@@ -279,7 +279,7 @@ mod svh_visitor {
             ExprBreak(id)            => SawExprBreak(id.map(|id| id.node.name.as_str())),
             ExprAgain(id)            => SawExprAgain(id.map(|id| id.node.name.as_str())),
             ExprRet(..)              => SawExprRet,
-            ExprInlineAsm(ref asm)   => SawExprInlineAsm(asm),
+            ExprInlineAsm(ref a,_,_) => SawExprInlineAsm(a),
             ExprStruct(..)           => SawExprStruct,
             ExprRepeat(..)           => SawExprRepeat,
         }
diff --git a/src/librustc_front/fold.rs b/src/librustc_front/fold.rs
index 6ae59122f71..e65f2fc37de 100644
--- a/src/librustc_front/fold.rs
+++ b/src/librustc_front/fold.rs
@@ -1107,34 +1107,11 @@ pub fn noop_fold_expr<T: Folder>(Expr { id, node, span, attrs }: Expr, folder: &
                 respan(folder.new_span(label.span), folder.fold_ident(label.node))
             })),
             ExprRet(e) => ExprRet(e.map(|x| folder.fold_expr(x))),
-            ExprInlineAsm(InlineAsm {
-                inputs,
-                outputs,
-                asm,
-                asm_str_style,
-                clobbers,
-                volatile,
-                alignstack,
-                dialect,
-                expn_id,
-            }) => ExprInlineAsm(InlineAsm {
-                inputs: inputs.move_map(|(c, input)| (c, folder.fold_expr(input))),
-                outputs: outputs.move_map(|out| {
-                    InlineAsmOutput {
-                        constraint: out.constraint,
-                        expr: folder.fold_expr(out.expr),
-                        is_rw: out.is_rw,
-                        is_indirect: out.is_indirect,
-                    }
-                }),
-                asm: asm,
-                asm_str_style: asm_str_style,
-                clobbers: clobbers,
-                volatile: volatile,
-                alignstack: alignstack,
-                dialect: dialect,
-                expn_id: expn_id,
-            }),
+            ExprInlineAsm(asm, outputs, inputs) => {
+                ExprInlineAsm(asm,
+                              outputs.move_map(|x| folder.fold_expr(x)),
+                              inputs.move_map(|x| folder.fold_expr(x)))
+            }
             ExprStruct(path, fields, maybe_expr) => {
                 ExprStruct(folder.fold_path(path),
                            fields.move_map(|x| folder.fold_field(x)),
diff --git a/src/librustc_front/hir.rs b/src/librustc_front/hir.rs
index 0b1418fc878..ee530677b60 100644
--- a/src/librustc_front/hir.rs
+++ b/src/librustc_front/hir.rs
@@ -39,7 +39,7 @@ use syntax::codemap::{self, Span, Spanned, DUMMY_SP, ExpnId};
 use syntax::abi::Abi;
 use syntax::ast::{Name, NodeId, DUMMY_NODE_ID, TokenTree, AsmDialect};
 use syntax::ast::{Attribute, Lit, StrStyle, FloatTy, IntTy, UintTy, MetaItem};
-use syntax::attr::ThinAttributes;
+use syntax::attr::{ThinAttributes, ThinAttributesExt};
 use syntax::parse::token::InternedString;
 use syntax::ptr::P;
 
@@ -635,6 +635,16 @@ pub enum Stmt_ {
     StmtSemi(P<Expr>, NodeId),
 }
 
+impl Stmt_ {
+    pub fn attrs(&self) -> &[Attribute] {
+        match *self {
+            StmtDecl(ref d, _) => d.node.attrs(),
+            StmtExpr(ref e, _) |
+            StmtSemi(ref e, _) => e.attrs.as_attr_slice(),
+        }
+    }
+}
+
 // FIXME (pending discussion of #1697, #2178...): local should really be
 // a refinement on pat.
 /// Local represents a `let` statement, e.g., `let <pat>:<ty> = <expr>;`
@@ -659,6 +669,15 @@ pub enum Decl_ {
     DeclItem(ItemId),
 }
 
+impl Decl_ {
+    pub fn attrs(&self) -> &[Attribute] {
+        match *self {
+            DeclLocal(ref l) => l.attrs.as_attr_slice(),
+            DeclItem(_) => &[]
+        }
+    }
+}
+
 /// represents one arm of a 'match'
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Arm {
@@ -793,8 +812,8 @@ pub enum Expr_ {
     /// A `return`, with an optional value to be returned
     ExprRet(Option<P<Expr>>),
 
-    /// Output of the `asm!()` macro
-    ExprInlineAsm(InlineAsm),
+    /// Inline assembly (from `asm!`), with its outputs and inputs.
+    ExprInlineAsm(InlineAsm, Vec<P<Expr>>, Vec<P<Expr>>),
 
     /// A struct literal expression.
     ///
@@ -978,7 +997,6 @@ pub enum Ty_ {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct InlineAsmOutput {
     pub constraint: InternedString,
-    pub expr: P<Expr>,
     pub is_rw: bool,
     pub is_indirect: bool,
 }
@@ -988,7 +1006,7 @@ pub struct InlineAsm {
     pub asm: InternedString,
     pub asm_str_style: StrStyle,
     pub outputs: HirVec<InlineAsmOutput>,
-    pub inputs: HirVec<(InternedString, P<Expr>)>,
+    pub inputs: HirVec<InternedString>,
     pub clobbers: HirVec<InternedString>,
     pub volatile: bool,
     pub alignstack: bool,
diff --git a/src/librustc_front/intravisit.rs b/src/librustc_front/intravisit.rs
index d71e392f521..e031dfc5b16 100644
--- a/src/librustc_front/intravisit.rs
+++ b/src/librustc_front/intravisit.rs
@@ -798,12 +798,12 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
         ExprRet(ref optional_expression) => {
             walk_list!(visitor, visit_expr, optional_expression);
         }
-        ExprInlineAsm(ref ia) => {
-            for &(_, ref input) in &ia.inputs {
-                visitor.visit_expr(&input)
+        ExprInlineAsm(_, ref outputs, ref inputs) => {
+            for output in outputs {
+                visitor.visit_expr(output)
             }
-            for output in &ia.outputs {
-                visitor.visit_expr(&output.expr)
+            for input in inputs {
+                visitor.visit_expr(input)
             }
         }
     }
diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs
index 825ab3fbd4c..8aac6356f9d 100644
--- a/src/librustc_front/lowering.rs
+++ b/src/librustc_front/lowering.rs
@@ -1320,14 +1320,11 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                     dialect,
                     expn_id,
                 }) => hir::ExprInlineAsm(hir::InlineAsm {
-                inputs: inputs.iter()
-                              .map(|&(ref c, ref input)| (c.clone(), lower_expr(lctx, input)))
-                              .collect(),
+                inputs: inputs.iter().map(|&(ref c, _)| c.clone()).collect(),
                 outputs: outputs.iter()
                                 .map(|out| {
                                     hir::InlineAsmOutput {
                                         constraint: out.constraint.clone(),
-                                        expr: lower_expr(lctx, &out.expr),
                                         is_rw: out.is_rw,
                                         is_indirect: out.is_indirect,
                                     }
@@ -1340,7 +1337,8 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                 alignstack: alignstack,
                 dialect: dialect,
                 expn_id: expn_id,
-            }),
+            }, outputs.iter().map(|out| lower_expr(lctx, &out.expr)).collect(),
+               inputs.iter().map(|&(_, ref input)| lower_expr(lctx, input)).collect()),
             ExprKind::Struct(ref path, ref fields, ref maybe_expr) => {
                 hir::ExprStruct(lower_path(lctx, path),
                                 fields.iter().map(|x| lower_field(lctx, x)).collect(),
diff --git a/src/librustc_front/print/pprust.rs b/src/librustc_front/print/pprust.rs
index 1100f084454..1ebbbb73e8c 100644
--- a/src/librustc_front/print/pprust.rs
+++ b/src/librustc_front/print/pprust.rs
@@ -1486,12 +1486,13 @@ impl<'a> State<'a> {
                     _ => (),
                 }
             }
-            hir::ExprInlineAsm(ref a) => {
+            hir::ExprInlineAsm(ref a, ref outputs, ref inputs) => {
                 try!(word(&mut self.s, "asm!"));
                 try!(self.popen());
                 try!(self.print_string(&a.asm, a.asm_str_style));
                 try!(self.word_space(":"));
 
+                let mut out_idx = 0;
                 try!(self.commasep(Inconsistent, &a.outputs, |s, out| {
                     match out.constraint.slice_shift_char() {
                         Some(('=', operand)) if out.is_rw => {
@@ -1500,18 +1501,21 @@ impl<'a> State<'a> {
                         _ => try!(s.print_string(&out.constraint, ast::StrStyle::Cooked)),
                     }
                     try!(s.popen());
-                    try!(s.print_expr(&out.expr));
+                    try!(s.print_expr(&outputs[out_idx]));
                     try!(s.pclose());
+                    out_idx += 1;
                     Ok(())
                 }));
                 try!(space(&mut self.s));
                 try!(self.word_space(":"));
 
-                try!(self.commasep(Inconsistent, &a.inputs, |s, &(ref co, ref o)| {
+                let mut in_idx = 0;
+                try!(self.commasep(Inconsistent, &a.inputs, |s, co| {
                     try!(s.print_string(&co, ast::StrStyle::Cooked));
                     try!(s.popen());
-                    try!(s.print_expr(&o));
+                    try!(s.print_expr(&inputs[in_idx]));
                     try!(s.pclose());
+                    in_idx += 1;
                     Ok(())
                 }));
                 try!(space(&mut self.s));
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index e47f67dad8f..4288f6258ae 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -175,6 +175,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
             id: LintId::of(TRANSMUTE_FROM_FN_ITEM_TYPES),
             reference: "issue #19925 <https://github.com/rust-lang/rust/issues/19925>",
         },
+        FutureIncompatibleInfo {
+            id: LintId::of(OVERLAPPING_INHERENT_IMPLS),
+            reference: "issue #22889 <https://github.com/rust-lang/rust/issues/22889>",
+        },
         ]);
 
     // We have one lint pass defined specially
diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs
index bc54b1ebab7..c1b909bd877 100644
--- a/src/librustc_llvm/lib.rs
+++ b/src/librustc_llvm/lib.rs
@@ -33,8 +33,6 @@
 extern crate libc;
 #[macro_use] #[no_link] extern crate rustc_bitflags;
 
-pub use self::OtherAttribute::*;
-pub use self::SpecialAttribute::*;
 pub use self::AttributeSet::*;
 pub use self::IntPredicate::*;
 pub use self::RealPredicate::*;
@@ -133,6 +131,7 @@ pub enum DLLStorageClassTypes {
 }
 
 bitflags! {
+    #[derive(Default, Debug)]
     flags Attribute : u64 {
         const ZExt            = 1 << 0,
         const SExt            = 1 << 1,
@@ -150,138 +149,95 @@ bitflags! {
         const OptimizeForSize = 1 << 13,
         const StackProtect    = 1 << 14,
         const StackProtectReq = 1 << 15,
-        const Alignment       = 1 << 16,
         const NoCapture       = 1 << 21,
         const NoRedZone       = 1 << 22,
         const NoImplicitFloat = 1 << 23,
         const Naked           = 1 << 24,
         const InlineHint      = 1 << 25,
-        const Stack           = 7 << 26,
         const ReturnsTwice    = 1 << 29,
         const UWTable         = 1 << 30,
         const NonLazyBind     = 1 << 31,
+
+        // Some of these are missing from the LLVM C API, the rest are
+        // present, but commented out, and preceded by the following warning:
+        // FIXME: These attributes are currently not included in the C API as
+        // a temporary measure until the API/ABI impact to the C API is understood
+        // and the path forward agreed upon.
+        const SanitizeAddress = 1 << 32,
+        const MinSize         = 1 << 33,
+        const NoDuplicate     = 1 << 34,
+        const StackProtectStrong = 1 << 35,
+        const SanitizeThread  = 1 << 36,
+        const SanitizeMemory  = 1 << 37,
+        const NoBuiltin       = 1 << 38,
+        const Returned        = 1 << 39,
+        const Cold            = 1 << 40,
+        const Builtin         = 1 << 41,
         const OptimizeNone    = 1 << 42,
+        const InAlloca        = 1 << 43,
+        const NonNull         = 1 << 44,
+        const JumpTable       = 1 << 45,
+        const Convergent      = 1 << 46,
+        const SafeStack       = 1 << 47,
+        const NoRecurse       = 1 << 48,
+        const InaccessibleMemOnly         = 1 << 49,
+        const InaccessibleMemOrArgMemOnly = 1 << 50,
     }
 }
 
-
-#[repr(u64)]
-#[derive(Copy, Clone)]
-pub enum OtherAttribute {
-    // The following are not really exposed in
-    // the LLVM C api so instead to add these
-    // we call a wrapper function in RustWrapper
-    // that uses the C++ api.
-    SanitizeAddressAttribute = 1 << 32,
-    MinSizeAttribute = 1 << 33,
-    NoDuplicateAttribute = 1 << 34,
-    StackProtectStrongAttribute = 1 << 35,
-    SanitizeThreadAttribute = 1 << 36,
-    SanitizeMemoryAttribute = 1 << 37,
-    NoBuiltinAttribute = 1 << 38,
-    ReturnedAttribute = 1 << 39,
-    ColdAttribute = 1 << 40,
-    BuiltinAttribute = 1 << 41,
-    OptimizeNoneAttribute = 1 << 42,
-    InAllocaAttribute = 1 << 43,
-    NonNullAttribute = 1 << 44,
-}
-
-#[derive(Copy, Clone)]
-pub enum SpecialAttribute {
-    DereferenceableAttribute(u64)
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub enum AttributeSet {
-    ReturnIndex = 0,
-    FunctionIndex = !0
-}
-
-pub trait AttrHelper {
-    fn apply_llfn(&self, idx: c_uint, llfn: ValueRef);
-    fn apply_callsite(&self, idx: c_uint, callsite: ValueRef);
+#[derive(Copy, Clone, Default, Debug)]
+pub struct Attributes {
+    regular: Attribute,
+    dereferenceable_bytes: u64
 }
 
-impl AttrHelper for Attribute {
-    fn apply_llfn(&self, idx: c_uint, llfn: ValueRef) {
-        unsafe {
-            LLVMAddFunctionAttribute(llfn, idx, self.bits() as uint64_t);
-        }
+impl Attributes {
+    pub fn set(&mut self, attr: Attribute) -> &mut Self {
+        self.regular = self.regular | attr;
+        self
     }
 
-    fn apply_callsite(&self, idx: c_uint, callsite: ValueRef) {
-        unsafe {
-            LLVMAddCallSiteAttribute(callsite, idx, self.bits() as uint64_t);
-        }
+    pub fn unset(&mut self, attr: Attribute) -> &mut Self {
+        self.regular = self.regular - attr;
+        self
     }
-}
 
-impl AttrHelper for OtherAttribute {
-    fn apply_llfn(&self, idx: c_uint, llfn: ValueRef) {
-        unsafe {
-            LLVMAddFunctionAttribute(llfn, idx, *self as uint64_t);
-        }
+    pub fn set_dereferenceable(&mut self, bytes: u64) -> &mut Self {
+        self.dereferenceable_bytes = bytes;
+        self
     }
 
-    fn apply_callsite(&self, idx: c_uint, callsite: ValueRef) {
-        unsafe {
-            LLVMAddCallSiteAttribute(callsite, idx, *self as uint64_t);
-        }
+    pub fn unset_dereferenceable(&mut self) -> &mut Self {
+        self.dereferenceable_bytes = 0;
+        self
     }
-}
 
-impl AttrHelper for SpecialAttribute {
-    fn apply_llfn(&self, idx: c_uint, llfn: ValueRef) {
-        match *self {
-            DereferenceableAttribute(bytes) => unsafe {
-                LLVMAddDereferenceableAttr(llfn, idx, bytes as uint64_t);
+    pub fn apply_llfn(&self, idx: usize, llfn: ValueRef) {
+        unsafe {
+            LLVMAddFunctionAttribute(llfn, idx as c_uint, self.regular.bits());
+            if self.dereferenceable_bytes != 0 {
+                LLVMAddDereferenceableAttr(llfn, idx as c_uint,
+                                           self.dereferenceable_bytes);
             }
         }
     }
 
-    fn apply_callsite(&self, idx: c_uint, callsite: ValueRef) {
-        match *self {
-            DereferenceableAttribute(bytes) => unsafe {
-                LLVMAddDereferenceableCallSiteAttr(callsite, idx, bytes as uint64_t);
+    pub fn apply_callsite(&self, idx: usize, callsite: ValueRef) {
+        unsafe {
+            LLVMAddCallSiteAttribute(callsite, idx as c_uint, self.regular.bits());
+            if self.dereferenceable_bytes != 0 {
+                LLVMAddDereferenceableCallSiteAttr(callsite, idx as c_uint,
+                                                   self.dereferenceable_bytes);
             }
         }
     }
 }
 
-pub struct AttrBuilder {
-    attrs: Vec<(usize, Box<AttrHelper+'static>)>
-}
-
-impl AttrBuilder {
-    pub fn new() -> AttrBuilder {
-        AttrBuilder {
-            attrs: Vec::new()
-        }
-    }
-
-    pub fn arg<T: AttrHelper + 'static>(&mut self, idx: usize, a: T) -> &mut AttrBuilder {
-        self.attrs.push((idx, box a as Box<AttrHelper+'static>));
-        self
-    }
-
-    pub fn ret<T: AttrHelper + 'static>(&mut self, a: T) -> &mut AttrBuilder {
-        self.attrs.push((ReturnIndex as usize, box a as Box<AttrHelper+'static>));
-        self
-    }
-
-    pub fn apply_llfn(&self, llfn: ValueRef) {
-        for &(idx, ref attr) in &self.attrs {
-            attr.apply_llfn(idx as c_uint, llfn);
-        }
-    }
-
-    pub fn apply_callsite(&self, callsite: ValueRef) {
-        for &(idx, ref attr) in &self.attrs {
-            attr.apply_callsite(idx as c_uint, callsite);
-        }
-    }
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub enum AttributeSet {
+    ReturnIndex = 0,
+    FunctionIndex = !0
 }
 
 // enum for the LLVM IntPredicate type
@@ -1354,6 +1310,7 @@ extern {
                          -> ValueRef;
     pub fn LLVMBuildNot(B: BuilderRef, V: ValueRef, Name: *const c_char)
                         -> ValueRef;
+    pub fn LLVMRustSetHasUnsafeAlgebra(Instr: ValueRef);
 
     /* Memory */
     pub fn LLVMBuildAlloca(B: BuilderRef, Ty: TypeRef, Name: *const c_char)
diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs
index d43a9f4dcda..5c5574c3a83 100644
--- a/src/librustc_metadata/astencode.rs
+++ b/src/librustc_metadata/astencode.rs
@@ -125,61 +125,51 @@ pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata,
                                  tcx: &TyCtxt<'tcx>,
                                  parent_path: Vec<ast_map::PathElem>,
                                  parent_def_path: ast_map::DefPath,
-                                 par_doc: rbml::Doc,
+                                 ast_doc: rbml::Doc,
                                  orig_did: DefId)
-                                 -> Result<&'tcx InlinedItem, (Vec<ast_map::PathElem>,
-                                                               ast_map::DefPath)> {
-    match par_doc.opt_child(c::tag_ast) {
-      None => Err((parent_path, parent_def_path)),
-      Some(ast_doc) => {
-        let mut path_as_str = None;
-        debug!("> Decoding inlined fn: {:?}::?",
-        {
-            // Do an Option dance to use the path after it is moved below.
-            let s = ast_map::path_to_string(parent_path.iter().cloned());
-            path_as_str = Some(s);
-            path_as_str.as_ref().map(|x| &x[..])
-        });
-        let mut ast_dsr = reader::Decoder::new(ast_doc);
-        let from_id_range = Decodable::decode(&mut ast_dsr).unwrap();
-        let to_id_range = reserve_id_range(&tcx.sess, from_id_range);
-        let dcx = &DecodeContext {
-            cdata: cdata,
-            tcx: tcx,
-            from_id_range: from_id_range,
-            to_id_range: to_id_range,
-            last_filemap_index: Cell::new(0)
-        };
-        let raw_ii = decode_ast(ast_doc);
-        let ii = ast_map::map_decoded_item(&dcx.tcx.map,
-                                           parent_path,
-                                           parent_def_path,
-                                           raw_ii,
-                                           dcx);
-        let name = match *ii {
-            InlinedItem::Item(ref i) => i.name,
-            InlinedItem::Foreign(ref i) => i.name,
-            InlinedItem::TraitItem(_, ref ti) => ti.name,
-            InlinedItem::ImplItem(_, ref ii) => ii.name
-        };
-        debug!("Fn named: {}", name);
-        debug!("< Decoded inlined fn: {}::{}",
-               path_as_str.unwrap(),
-               name);
-        region::resolve_inlined_item(&tcx.sess, &tcx.region_maps, ii);
-        decode_side_tables(dcx, ast_doc);
-        copy_item_types(dcx, ii, orig_did);
-        match *ii {
-          InlinedItem::Item(ref i) => {
-            debug!(">>> DECODED ITEM >>>\n{}\n<<< DECODED ITEM <<<",
-                   ::rustc_front::print::pprust::item_to_string(&i));
-          }
-          _ => { }
-        }
-
-        Ok(ii)
-      }
-    }
+                                 -> &'tcx InlinedItem {
+    let mut path_as_str = None;
+    debug!("> Decoding inlined fn: {:?}::?",
+    {
+        // Do an Option dance to use the path after it is moved below.
+        let s = ast_map::path_to_string(parent_path.iter().cloned());
+        path_as_str = Some(s);
+        path_as_str.as_ref().map(|x| &x[..])
+    });
+    let mut ast_dsr = reader::Decoder::new(ast_doc);
+    let from_id_range = Decodable::decode(&mut ast_dsr).unwrap();
+    let to_id_range = reserve_id_range(&tcx.sess, from_id_range);
+    let dcx = &DecodeContext {
+        cdata: cdata,
+        tcx: tcx,
+        from_id_range: from_id_range,
+        to_id_range: to_id_range,
+        last_filemap_index: Cell::new(0)
+    };
+    let ii = ast_map::map_decoded_item(&dcx.tcx.map,
+                                       parent_path,
+                                       parent_def_path,
+                                       decode_ast(ast_doc),
+                                       dcx);
+    let name = match *ii {
+        InlinedItem::Item(ref i) => i.name,
+        InlinedItem::Foreign(ref i) => i.name,
+        InlinedItem::TraitItem(_, ref ti) => ti.name,
+        InlinedItem::ImplItem(_, ref ii) => ii.name
+    };
+    debug!("Fn named: {}", name);
+    debug!("< Decoded inlined fn: {}::{}",
+            path_as_str.unwrap(),
+            name);
+    region::resolve_inlined_item(&tcx.sess, &tcx.region_maps, ii);
+    decode_side_tables(dcx, ast_doc);
+    copy_item_types(dcx, ii, orig_did);
+    if let InlinedItem::Item(ref i) = *ii {
+        debug!(">>> DECODED ITEM >>>\n{}\n<<< DECODED ITEM <<<",
+               ::rustc_front::print::pprust::item_to_string(&i));
+    }
+
+    ii
 }
 
 // ______________________________________________________________________
diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs
index 2cd119cfc48..9ac7216165c 100644
--- a/src/librustc_metadata/csearch.rs
+++ b/src/librustc_metadata/csearch.rs
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use astencode;
 use cstore;
 use decoder;
 use encoder;
@@ -237,7 +236,7 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
     }
 
     fn impl_or_trait_item(&self, tcx: &TyCtxt<'tcx>, def: DefId)
-                          -> ty::ImplOrTraitItem<'tcx>
+                          -> Option<ty::ImplOrTraitItem<'tcx>>
     {
         let cdata = self.get_crate_data(def.krate);
         decoder::get_impl_or_trait_item(
@@ -439,8 +438,7 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
                           -> FoundAst<'tcx>
     {
         let cdata = self.get_crate_data(def.krate);
-        let decode_inlined_item = Box::new(astencode::decode_inlined_item);
-        decoder::maybe_get_item_ast(&cdata, tcx, def.index, decode_inlined_item)
+        decoder::maybe_get_item_ast(&cdata, tcx, def.index)
     }
 
     fn maybe_get_item_mir(&self, tcx: &TyCtxt<'tcx>, def: DefId)
@@ -509,21 +507,18 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
                        mir_map: &MirMap<'tcx>,
                        krate: &hir::Crate) -> Vec<u8>
     {
-        let encode_inlined_item: encoder::EncodeInlinedItem =
-            Box::new(|ecx, rbml_w, ii| astencode::encode_inlined_item(ecx, rbml_w, ii));
-
-        let encode_params = encoder::EncodeParams {
+        let ecx = encoder::EncodeContext {
             diag: tcx.sess.diagnostic(),
             tcx: tcx,
             reexports: reexports,
             item_symbols: item_symbols,
             link_meta: link_meta,
             cstore: self,
-            encode_inlined_item: encode_inlined_item,
             reachable: reachable,
             mir_map: mir_map,
+            type_abbrevs: RefCell::new(FnvHashMap()),
         };
-        encoder::encode_metadata(encode_params, krate)
+        encoder::encode_metadata(ecx, krate)
 
     }
 
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 38a2a7794bc..1cb5f2b1c3f 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -14,6 +14,7 @@
 
 use self::Family::*;
 
+use astencode::decode_inlined_item;
 use cstore::{self, crate_metadata};
 use common::*;
 use encoder::def_to_u64;
@@ -797,64 +798,36 @@ pub fn get_item_name(intr: &IdentInterner, cdata: Cmd, id: DefIndex) -> ast::Nam
     item_name(intr, cdata.lookup_item(id))
 }
 
-pub type DecodeInlinedItem<'a> =
-    Box<for<'tcx> FnMut(Cmd,
-                        &TyCtxt<'tcx>,
-                        Vec<hir_map::PathElem>, // parent_path
-                        hir_map::DefPath,       // parent_def_path
-                        rbml::Doc,
-                        DefId)
-                        -> Result<&'tcx InlinedItem, (Vec<hir_map::PathElem>,
-                                                      hir_map::DefPath)> + 'a>;
-
-pub fn maybe_get_item_ast<'tcx>(cdata: Cmd,
-                                tcx: &TyCtxt<'tcx>,
-                                id: DefIndex,
-                                mut decode_inlined_item: DecodeInlinedItem)
+pub fn maybe_get_item_ast<'tcx>(cdata: Cmd, tcx: &TyCtxt<'tcx>, id: DefIndex)
                                 -> FoundAst<'tcx> {
     debug!("Looking up item: {:?}", id);
     let item_doc = cdata.lookup_item(id);
     let item_did = item_def_id(item_doc, cdata);
-    let parent_path = {
-        let mut path = item_path(item_doc);
-        path.pop();
-        path
-    };
-    let parent_def_path = {
-        let mut def_path = def_path(cdata, id);
-        def_path.pop();
-        def_path
-    };
-    match decode_inlined_item(cdata,
-                              tcx,
-                              parent_path,
-                              parent_def_path,
-                              item_doc,
-                              item_did) {
-        Ok(ii) => FoundAst::Found(ii),
-        Err((mut parent_path, mut parent_def_path)) => {
-            match item_parent_item(cdata, item_doc) {
-                Some(parent_did) => {
-                    // Remove the last element from the paths, since we are now
-                    // trying to inline the parent.
-                    parent_path.pop();
-                    parent_def_path.pop();
-
-                    let parent_item = cdata.lookup_item(parent_did.index);
-                    match decode_inlined_item(cdata,
-                                              tcx,
-                                              parent_path,
-                                              parent_def_path,
-                                              parent_item,
-                                              parent_did) {
-                        Ok(ii) => FoundAst::FoundParent(parent_did, ii),
-                        Err(_) => FoundAst::NotFound
-                    }
-                }
-                None => FoundAst::NotFound
+    let mut parent_path = item_path(item_doc);
+    parent_path.pop();
+    let mut parent_def_path = def_path(cdata, id);
+    parent_def_path.pop();
+    if let Some(ast_doc) = reader::maybe_get_doc(item_doc, tag_ast as usize) {
+        let ii = decode_inlined_item(cdata, tcx, parent_path,
+                                     parent_def_path,
+                                     ast_doc, item_did);
+        return FoundAst::Found(ii);
+    } else if let Some(parent_did) = item_parent_item(cdata, item_doc) {
+        // Remove the last element from the paths, since we are now
+        // trying to inline the parent.
+        parent_path.pop();
+        parent_def_path.pop();
+        let parent_doc = cdata.lookup_item(parent_did.index);
+        if let Some(ast_doc) = reader::maybe_get_doc(parent_doc, tag_ast as usize) {
+            let ii = decode_inlined_item(cdata, tcx, parent_path,
+                                         parent_def_path,
+                                         ast_doc, parent_did);
+            if let &InlinedItem::Item(ref i) = ii {
+                return FoundAst::FoundParent(parent_did, i);
             }
         }
     }
+    FoundAst::NotFound
 }
 
 pub fn is_item_mir_available<'tcx>(cdata: Cmd, id: DefIndex) -> bool {
@@ -982,12 +955,16 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc<IdentInterner>,
                                     cdata: Cmd,
                                     id: DefIndex,
                                     tcx: &TyCtxt<'tcx>)
-                                    -> ty::ImplOrTraitItem<'tcx> {
+                                    -> Option<ty::ImplOrTraitItem<'tcx>> {
     let item_doc = cdata.lookup_item(id);
 
     let def_id = item_def_id(item_doc, cdata);
 
-    let container_id = item_require_parent_item(cdata, item_doc);
+    let container_id = if let Some(id) = item_parent_item(cdata, item_doc) {
+        id
+    } else {
+        return None;
+    };
     let container_doc = cdata.lookup_item(container_id.index);
     let container = match item_family(container_doc) {
         Trait => TraitContainer(container_id),
@@ -998,7 +975,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc<IdentInterner>,
     let vis = item_visibility(item_doc);
     let defaultness = item_defaultness(item_doc);
 
-    match item_sort(item_doc) {
+    Some(match item_sort(item_doc) {
         sort @ Some('C') | sort @ Some('c') => {
             let ty = doc_type(item_doc, tcx, cdata);
             ty::ConstTraitItem(Rc::new(ty::AssociatedConst {
@@ -1044,8 +1021,8 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc<IdentInterner>,
                 container: container,
             }))
         }
-        _ => panic!("unknown impl/trait item sort"),
-    }
+        _ => return None
+    })
 }
 
 pub fn get_trait_item_def_ids(cdata: Cmd, id: DefIndex)
@@ -1085,7 +1062,7 @@ pub fn get_provided_trait_methods<'tcx>(intr: Rc<IdentInterner>,
                                                     cdata,
                                                     did.index,
                                                     tcx);
-            if let ty::MethodTraitItem(ref method) = trait_item {
+            if let Some(ty::MethodTraitItem(ref method)) = trait_item {
                 Some((*method).clone())
             } else {
                 None
@@ -1114,7 +1091,7 @@ pub fn get_associated_consts<'tcx>(intr: Rc<IdentInterner>,
                                                             cdata,
                                                             did.index,
                                                             tcx);
-                    if let ty::ConstTraitItem(ref ac) = trait_item {
+                    if let Some(ty::ConstTraitItem(ref ac)) = trait_item {
                         Some((*ac).clone())
                     } else {
                         None
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index 41baa0b1591..e677ea962f9 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -13,6 +13,7 @@
 #![allow(unused_must_use)] // everything is just a MemWriter, can't fail
 #![allow(non_camel_case_types)]
 
+use astencode::encode_inlined_item;
 use common::*;
 use cstore;
 use decoder;
@@ -55,21 +56,6 @@ use rustc_front::hir::{self, PatKind};
 use rustc_front::intravisit::Visitor;
 use rustc_front::intravisit;
 
-pub type EncodeInlinedItem<'a> =
-    Box<FnMut(&EncodeContext, &mut Encoder, InlinedItemRef) + 'a>;
-
-pub struct EncodeParams<'a, 'tcx: 'a> {
-    pub diag: &'a Handler,
-    pub tcx: &'a TyCtxt<'tcx>,
-    pub reexports: &'a def::ExportMap,
-    pub item_symbols: &'a RefCell<NodeMap<String>>,
-    pub link_meta: &'a LinkMeta,
-    pub cstore: &'a cstore::CStore,
-    pub encode_inlined_item: EncodeInlinedItem<'a>,
-    pub reachable: &'a NodeSet,
-    pub mir_map: &'a MirMap<'tcx>,
-}
-
 pub struct EncodeContext<'a, 'tcx: 'a> {
     pub diag: &'a Handler,
     pub tcx: &'a TyCtxt<'tcx>,
@@ -77,7 +63,6 @@ pub struct EncodeContext<'a, 'tcx: 'a> {
     pub item_symbols: &'a RefCell<NodeMap<String>>,
     pub link_meta: &'a LinkMeta,
     pub cstore: &'a cstore::CStore,
-    pub encode_inlined_item: RefCell<EncodeInlinedItem<'a>>,
     pub type_abbrevs: tyencode::abbrev_map<'tcx>,
     pub reachable: &'a NodeSet,
     pub mir_map: &'a MirMap<'tcx>,
@@ -688,6 +673,7 @@ fn encode_info_for_associated_const<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
                             rbml_w,
                             InlinedItemRef::ImplItem(ecx.tcx.map.local_def_id(parent_id),
                                                      ii));
+        encode_mir(ecx, rbml_w, ii.id);
     }
 
     rbml_w.end_tag();
@@ -733,6 +719,7 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
                                     rbml_w,
                                     InlinedItemRef::ImplItem(ecx.tcx.map.local_def_id(parent_id),
                                                              impl_item));
+                encode_mir(ecx, rbml_w, impl_item.id);
             }
             encode_constness(rbml_w, sig.constness);
             encode_defaultness(rbml_w, impl_item.defaultness);
@@ -820,23 +807,6 @@ fn encode_repr_attrs(rbml_w: &mut Encoder,
     rbml_w.end_tag();
 }
 
-fn encode_inlined_item(ecx: &EncodeContext,
-                       rbml_w: &mut Encoder,
-                       ii: InlinedItemRef) {
-    let mut eii = ecx.encode_inlined_item.borrow_mut();
-    let eii: &mut EncodeInlinedItem = &mut *eii;
-    eii(ecx, rbml_w, ii);
-
-    let node_id = match ii {
-        InlinedItemRef::Item(item) => item.id,
-        InlinedItemRef::TraitItem(_, trait_item) => trait_item.id,
-        InlinedItemRef::ImplItem(_, impl_item) => impl_item.id,
-        InlinedItemRef::Foreign(foreign_item) => foreign_item.id
-    };
-
-    encode_mir(ecx, rbml_w, node_id);
-}
-
 fn encode_mir(ecx: &EncodeContext, rbml_w: &mut Encoder, node_id: NodeId) {
     if let Some(mir) = ecx.mir_map.map.get(&node_id) {
         rbml_w.start_tag(tag_mir as usize);
@@ -958,6 +928,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_path(rbml_w, path);
         encode_attributes(rbml_w, &item.attrs);
         encode_inlined_item(ecx, rbml_w, InlinedItemRef::Item(item));
+        encode_mir(ecx, rbml_w, item.id);
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
         encode_deprecation(rbml_w, depr);
@@ -976,6 +947,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         let needs_inline = tps_len > 0 || attr::requests_inline(&item.attrs);
         if needs_inline || constness == hir::Constness::Const {
             encode_inlined_item(ecx, rbml_w, InlinedItemRef::Item(item));
+            encode_mir(ecx, rbml_w, item.id);
         }
         if tps_len == 0 {
             encode_symbol(ecx, rbml_w, item.id);
@@ -1044,6 +1016,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
             encode_variant_id(rbml_w, ecx.tcx.map.local_def_id(v.node.data.id()));
         }
         encode_inlined_item(ecx, rbml_w, InlinedItemRef::Item(item));
+        encode_mir(ecx, rbml_w, item.id);
         encode_path(rbml_w, path);
 
         // Encode inherent implementations for this enumeration.
@@ -1092,6 +1065,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_struct_fields(rbml_w, variant);
 
         encode_inlined_item(ecx, rbml_w, InlinedItemRef::Item(item));
+        encode_mir(ecx, rbml_w, item.id);
 
         // Encode inherent implementations for this structure.
         encode_inherent_implementations(ecx, rbml_w, def_id);
@@ -1374,6 +1348,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
 
                     encode_inlined_item(ecx, rbml_w,
                                         InlinedItemRef::TraitItem(def_id, trait_item));
+                    encode_mir(ecx, rbml_w, trait_item.id);
                 }
                 hir::MethodTraitItem(ref sig, ref body) => {
                     // If this is a static method, we've already
@@ -1389,6 +1364,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
                         encode_item_sort(rbml_w, 'p');
                         encode_inlined_item(ecx, rbml_w,
                                             InlinedItemRef::TraitItem(def_id, trait_item));
+                        encode_mir(ecx, rbml_w, trait_item.id);
                     } else {
                         encode_item_sort(rbml_w, 'r');
                     }
@@ -1426,13 +1402,15 @@ fn encode_info_for_foreign_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_name(rbml_w, nitem.name);
         if abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
             encode_inlined_item(ecx, rbml_w, InlinedItemRef::Foreign(nitem));
+            encode_mir(ecx, rbml_w, nitem.id);
+        } else {
+            encode_symbol(ecx, rbml_w, nitem.id);
         }
         encode_attributes(rbml_w, &nitem.attrs);
         let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id));
         let depr = stability::lookup_deprecation(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id));
         encode_stability(rbml_w, stab);
         encode_deprecation(rbml_w, depr);
-        encode_symbol(ecx, rbml_w, nitem.id);
         encode_method_argument_names(rbml_w, &fndecl);
       }
       hir::ForeignItemStatic(_, mutbl) => {
@@ -1928,32 +1906,7 @@ fn encode_dylib_dependency_formats(rbml_w: &mut Encoder, ecx: &EncodeContext) {
 #[allow(non_upper_case_globals)]
 pub const metadata_encoding_version : &'static [u8] = &[b'r', b'u', b's', b't', 0, 0, 0, 2 ];
 
-pub fn encode_metadata(parms: EncodeParams, krate: &hir::Crate) -> Vec<u8> {
-    let EncodeParams {
-        item_symbols,
-        diag,
-        tcx,
-        reexports,
-        cstore,
-        encode_inlined_item,
-        link_meta,
-        reachable,
-        mir_map,
-        ..
-    } = parms;
-    let ecx = EncodeContext {
-        diag: diag,
-        tcx: tcx,
-        reexports: reexports,
-        item_symbols: item_symbols,
-        link_meta: link_meta,
-        cstore: cstore,
-        encode_inlined_item: RefCell::new(encode_inlined_item),
-        type_abbrevs: RefCell::new(FnvHashMap()),
-        reachable: reachable,
-        mir_map: mir_map,
-    };
-
+pub fn encode_metadata(ecx: EncodeContext, krate: &hir::Crate) -> Vec<u8> {
     let mut wr = Cursor::new(Vec::new());
 
     {
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index 2338d7df01a..4c0e9b98d9a 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -39,8 +39,20 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             ExprKind::Scope { extent, value } => {
                 this.in_scope(extent, block, |this| this.as_rvalue(block, value))
             }
-            ExprKind::InlineAsm { asm } => {
-                block.and(Rvalue::InlineAsm(asm.clone()))
+            ExprKind::InlineAsm { asm, outputs, inputs } => {
+                let outputs = outputs.into_iter().map(|output| {
+                    unpack!(block = this.as_lvalue(block, output))
+                }).collect();
+
+                let inputs = inputs.into_iter().map(|input| {
+                    unpack!(block = this.as_operand(block, input))
+                }).collect();
+
+                block.and(Rvalue::InlineAsm {
+                    asm: asm.clone(),
+                    outputs: outputs,
+                    inputs: inputs
+                })
             }
             ExprKind::Repeat { value, count } => {
                 let value_operand = unpack!(block = this.as_operand(block, value));
@@ -73,8 +85,13 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 })
             }
             ExprKind::Cast { source } => {
-                let source = unpack!(block = this.as_operand(block, source));
-                block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty))
+                let source = this.hir.mirror(source);
+                if source.ty == expr.ty {
+                    this.expr_as_rvalue(block, source)
+                } else {
+                    let source = unpack!(block = this.as_operand(block, source));
+                    block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty))
+                }
             }
             ExprKind::ReifyFnPointer { source } => {
                 let source = unpack!(block = this.as_operand(block, source));
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 9ecbf748d7c..673ff9e86c4 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -238,6 +238,13 @@ pub struct MatchPair<'pat, 'tcx:'pat> {
 
     // ... must match this pattern.
     pattern: &'pat Pattern<'tcx>,
+
+    // HACK(eddyb) This is used to toggle whether a Slice pattern
+    // has had its length checked. This is only necessary because
+    // the "rest" part of the pattern right now has type &[T] and
+    // as such, it requires an Rvalue::Slice to be generated.
+    // See RFC 495 / issue #23121 for the eventual (proper) solution.
+    slice_len_checked: bool
 }
 
 #[derive(Clone, Debug, PartialEq)]
diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs
index 2c8e1c1ccf6..a3337badf88 100644
--- a/src/librustc_mir/build/matches/simplify.rs
+++ b/src/librustc_mir/build/matches/simplify.rs
@@ -95,7 +95,18 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 Err(match_pair)
             }
 
-            PatternKind::Array { ref prefix, ref slice, ref suffix } => {
+            PatternKind::Range { .. } |
+            PatternKind::Variant { .. } => {
+                // cannot simplify, test is required
+                Err(match_pair)
+            }
+
+            PatternKind::Slice { .. } if !match_pair.slice_len_checked => {
+                Err(match_pair)
+            }
+
+            PatternKind::Array { ref prefix, ref slice, ref suffix } |
+            PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
                 unpack!(block = self.prefix_suffix_slice(&mut candidate.match_pairs,
                                                          block,
                                                          match_pair.lvalue.clone(),
@@ -105,13 +116,6 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 Ok(block)
             }
 
-            PatternKind::Slice { .. } |
-            PatternKind::Range { .. } |
-            PatternKind::Variant { .. } => {
-                // cannot simplify, test is required
-                Err(match_pair)
-            }
-
             PatternKind::Leaf { ref subpatterns } => {
                 // tuple struct, match subpats (if any)
                 candidate.match_pairs
diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs
index d42c8ff7bd7..0efa24f3119 100644
--- a/src/librustc_mir/build/matches/test.rs
+++ b/src/librustc_mir/build/matches/test.rs
@@ -75,7 +75,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 }
             }
 
-            PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
+            PatternKind::Slice { ref prefix, ref slice, ref suffix }
+                    if !match_pair.slice_len_checked => {
                 let len = prefix.len() + suffix.len();
                 let op = if slice.is_some() {
                     BinOp::Ge
@@ -89,6 +90,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             }
 
             PatternKind::Array { .. } |
+            PatternKind::Slice { .. } |
             PatternKind::Wild |
             PatternKind::Binding { .. } |
             PatternKind::Leaf { .. } |
@@ -174,14 +176,78 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 targets
             }
 
-            TestKind::Eq { ref value, ty } => {
-                let expect = self.literal_operand(test.span, ty.clone(), Literal::Value {
-                    value: value.clone()
-                });
-                let val = Operand::Consume(lvalue.clone());
+            TestKind::Eq { ref value, mut ty } => {
+                let mut val = Operand::Consume(lvalue.clone());
+
+                // If we're using b"..." as a pattern, we need to insert an
+                // unsizing coercion, as the byte string has the type &[u8; N].
+                let expect = if let ConstVal::ByteStr(ref bytes) = *value {
+                    let tcx = self.hir.tcx();
+
+                    // Unsize the lvalue to &[u8], too, if necessary.
+                    if let ty::TyRef(region, mt) = ty.sty {
+                        if let ty::TyArray(_, _) = mt.ty.sty {
+                            ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8));
+                            let val_slice = self.temp(ty);
+                            self.cfg.push_assign(block, test.span, &val_slice,
+                                                 Rvalue::Cast(CastKind::Unsize, val, ty));
+                            val = Operand::Consume(val_slice);
+                        }
+                    }
+
+                    assert!(ty.is_slice());
+
+                    let array_ty = tcx.mk_array(tcx.types.u8, bytes.len());
+                    let array_ref = tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), array_ty);
+                    let array = self.literal_operand(test.span, array_ref, Literal::Value {
+                        value: value.clone()
+                    });
+
+                    let slice = self.temp(ty);
+                    self.cfg.push_assign(block, test.span, &slice,
+                                         Rvalue::Cast(CastKind::Unsize, array, ty));
+                    Operand::Consume(slice)
+                } else {
+                    self.literal_operand(test.span, ty, Literal::Value {
+                        value: value.clone()
+                    })
+                };
+
+                // Use PartialEq::eq for &str and &[u8] slices, instead of BinOp::Eq.
                 let fail = self.cfg.start_new_block();
-                let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val.clone());
-                vec![block, fail]
+                if let ty::TyRef(_, mt) = ty.sty {
+                    assert!(ty.is_slice());
+                    let eq_def_id = self.hir.tcx().lang_items.eq_trait().unwrap();
+                    let ty = mt.ty;
+                    let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, vec![ty]);
+
+                    let bool_ty = self.hir.bool_ty();
+                    let eq_result = self.temp(bool_ty);
+                    let eq_block = self.cfg.start_new_block();
+                    let cleanup = self.diverge_cleanup();
+                    self.cfg.terminate(block, Terminator::Call {
+                        func: Operand::Constant(Constant {
+                            span: test.span,
+                            ty: mty,
+                            literal: method
+                        }),
+                        args: vec![val, expect],
+                        destination: Some((eq_result.clone(), eq_block)),
+                        cleanup: cleanup,
+                    });
+
+                    // check the result
+                    let block = self.cfg.start_new_block();
+                    self.cfg.terminate(eq_block, Terminator::If {
+                        cond: Operand::Consume(eq_result),
+                        targets: (block, fail),
+                    });
+
+                    vec![block, fail]
+                } else {
+                    let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val);
+                    vec![block, fail]
+                }
             }
 
             TestKind::Range { ref lo, ref hi, ty } => {
@@ -349,9 +415,26 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 }
             }
 
-            TestKind::Eq { .. } |
-            TestKind::Range { .. } |
+            // If we are performing a length check, then this
+            // informs slice patterns, but nothing else.
             TestKind::Len { .. } => {
+                let pattern_test = self.test(&match_pair);
+                match *match_pair.pattern.kind {
+                    PatternKind::Slice { .. } if pattern_test.kind == test.kind => {
+                        let mut new_candidate = candidate.clone();
+
+                        // Set up the MatchKind to simplify this like an array.
+                        new_candidate.match_pairs[match_pair_index]
+                                     .slice_len_checked = true;
+                        resulting_candidates[0].push(new_candidate);
+                        true
+                    }
+                    _ => false
+                }
+            }
+
+            TestKind::Eq { .. } |
+            TestKind::Range { .. } => {
                 // These are all binary tests.
                 //
                 // FIXME(#29623) we can be more clever here
@@ -405,7 +488,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                        .map(|subpattern| {
                            // e.g., `(x as Variant).0`
                            let lvalue = downcast_lvalue.clone().field(subpattern.field,
-                                                                      subpattern.field_ty());
+                                                                      subpattern.pattern.ty);
                            // e.g., `(x as Variant).0 @ P1`
                            MatchPair::new(lvalue, &subpattern.pattern)
                        });
diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs
index c295ed168ba..b46c3ffb76a 100644
--- a/src/librustc_mir/build/matches/util.rs
+++ b/src/librustc_mir/build/matches/util.rs
@@ -22,7 +22,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         subpatterns.iter()
                    .map(|fieldpat| {
                        let lvalue = lvalue.clone().field(fieldpat.field,
-                                                         fieldpat.field_ty());
+                                                         fieldpat.pattern.ty);
                        MatchPair::new(lvalue, &fieldpat.pattern)
                    })
                    .collect()
@@ -118,6 +118,7 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
         MatchPair {
             lvalue: lvalue,
             pattern: pattern,
+            slice_len_checked: false,
         }
     }
 }
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 5d9f827984e..b40775f939f 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -141,15 +141,18 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 .chain(explicits)
                 .enumerate()
                 .map(|(index, (ty, pattern))| {
+                    let lvalue = Lvalue::Arg(index as u32);
                     if let Some(pattern) = pattern {
-                        let lvalue = Lvalue::Arg(index as u32);
                         let pattern = this.hir.irrefutable_pat(pattern);
                         unpack!(block = this.lvalue_into_pattern(block,
                                                                  argument_extent,
                                                                  pattern,
                                                                  &lvalue));
                     }
-                    ArgDecl { ty: ty }
+                    // Make sure we drop (parts of) the argument even when not matched on.
+                    this.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
+                                       argument_extent, &lvalue, ty);
+                    ArgDecl { ty: ty, spread: false }
                 })
                 .collect();
 
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 1f8a5da9c1b..4d87d926e40 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -337,8 +337,12 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                 convert_path_expr(cx, self)
             }
 
-            hir::ExprInlineAsm(ref asm) => {
-                ExprKind::InlineAsm { asm: asm }
+            hir::ExprInlineAsm(ref asm, ref outputs, ref inputs) => {
+                ExprKind::InlineAsm {
+                    asm: asm,
+                    outputs: outputs.to_ref(),
+                    inputs: inputs.to_ref()
+                }
             }
 
             // Now comes the rote stuff:
@@ -668,11 +672,16 @@ fn convert_path_expr<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr)
         },
         Def::Const(def_id) |
         Def::AssociatedConst(def_id) => {
-            if let Some(v) = cx.try_const_eval_literal(expr) {
-                return ExprKind::Literal { literal: v };
-            } else {
-                def_id
+            let substs = Some(cx.tcx.node_id_item_substs(expr.id).substs);
+            if let Some((e, _)) = const_eval::lookup_const_by_id(cx.tcx, def_id, substs) {
+                // FIXME ConstVal can't be yet used with adjustments, as they would be lost.
+                if !cx.tcx.tables.borrow().adjustments.contains_key(&e.id) {
+                    if let Some(v) = cx.try_const_eval_literal(e) {
+                        return ExprKind::Literal { literal: v };
+                    }
+                }
             }
+            def_id
         }
 
         Def::Static(node_id, _) => return ExprKind::StaticRef {
diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs
index fd4cf7c0473..b97bfaf5aef 100644
--- a/src/librustc_mir/hair/cx/mod.rs
+++ b/src/librustc_mir/hair/cx/mod.rs
@@ -19,7 +19,9 @@ use hair::*;
 use rustc::mir::repr::*;
 
 use rustc::middle::const_eval::{self, ConstVal};
+use rustc::middle::def_id::DefId;
 use rustc::middle::infer::InferCtxt;
+use rustc::middle::subst::{Subst, Substs};
 use rustc::middle::ty::{self, Ty, TyCtxt};
 use syntax::codemap::Span;
 use syntax::parse::token;
@@ -84,9 +86,44 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> {
 
     pub fn try_const_eval_literal(&mut self, e: &hir::Expr) -> Option<Literal<'tcx>> {
         let hint = const_eval::EvalHint::ExprTypeChecked;
-        const_eval::eval_const_expr_partial(self.tcx, e, hint, None)
-            .ok()
-            .map(|v| Literal::Value { value: v })
+        const_eval::eval_const_expr_partial(self.tcx, e, hint, None).ok().and_then(|v| {
+            match v {
+                // All of these contain local IDs, unsuitable for storing in MIR.
+                ConstVal::Struct(_) | ConstVal::Tuple(_) |
+                ConstVal::Array(..) | ConstVal::Repeat(..) |
+                ConstVal::Function(_) => None,
+
+                _ => Some(Literal::Value { value: v })
+            }
+        })
+    }
+
+    pub fn trait_method(&mut self,
+                        trait_def_id: DefId,
+                        method_name: &str,
+                        self_ty: Ty<'tcx>,
+                        params: Vec<Ty<'tcx>>)
+                        -> (Ty<'tcx>, Literal<'tcx>) {
+        let method_name = token::intern(method_name);
+        let substs = Substs::new_trait(params, vec![], self_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 (method_ty, Literal::Item {
+                            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));
     }
 
     pub fn num_variants(&mut self, adt_def: ty::AdtDef<'tcx>) -> usize {
diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs
index d1e3f08aff8..a9873778374 100644
--- a/src/librustc_mir/hair/cx/pattern.rs
+++ b/src/librustc_mir/hair/cx/pattern.rs
@@ -63,6 +63,8 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
     }
 
     fn to_pattern(&mut self, pat: &hir::Pat) -> Pattern<'tcx> {
+        let mut ty = self.cx.tcx.node_id_to_type(pat.id);
+
         let kind = match pat.node {
             PatKind::Wild => PatternKind::Wild,
 
@@ -84,9 +86,9 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
             {
                 let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
                 match def {
-                    Def::Const(def_id) | Def::AssociatedConst(def_id) =>
-                        match const_eval::lookup_const_by_id(self.cx.tcx, def_id,
-                                                             Some(pat.id), None) {
+                    Def::Const(def_id) | Def::AssociatedConst(def_id) => {
+                        let substs = Some(self.cx.tcx.node_id_item_substs(pat.id).substs);
+                        match const_eval::lookup_const_by_id(self.cx.tcx, def_id, substs) {
                             Some((const_expr, _const_ty)) => {
                                 let pat = const_eval::const_expr_to_pat(self.cx.tcx, const_expr,
                                                                         pat.span);
@@ -97,7 +99,8 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
                                     pat.span,
                                     &format!("cannot eval constant: {:?}", def_id))
                             }
-                        },
+                        }
+                    }
                     _ =>
                         self.cx.tcx.sess.span_bug(
                             pat.span,
@@ -169,6 +172,17 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
                     hir::BindByRef(hir::MutImmutable) =>
                         (Mutability::Not, BindingMode::ByRef(region.unwrap(), BorrowKind::Shared)),
                 };
+
+                // A ref x pattern is the same node used for x, and as such it has
+                // x's type, which is &T, where we want T (the type being matched).
+                if let hir::BindByRef(_) = bm {
+                    if let ty::TyRef(_, mt) = ty.sty {
+                        ty = mt.ty;
+                    } else {
+                        unreachable!("`ref {}` has wrong type {}", ident.node, ty);
+                    }
+                }
+
                 PatternKind::Binding {
                     mutability: mutability,
                     mode: mode,
@@ -234,8 +248,6 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
             }
         };
 
-        let ty = self.cx.tcx.node_id_to_type(pat.id);
-
         Pattern {
             span: pat.span,
             ty: ty,
@@ -314,20 +326,3 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
         }
     }
 }
-
-impl<'tcx> FieldPattern<'tcx> {
-    pub fn field_ty(&self) -> Ty<'tcx> {
-        debug!("field_ty({:?},ty={:?})", self, self.pattern.ty);
-        let r = match *self.pattern.kind {
-            PatternKind::Binding { mode: BindingMode::ByRef(..), ..} => {
-                match self.pattern.ty.sty {
-                    ty::TyRef(_, mt) => mt.ty,
-                    _ => unreachable!()
-                }
-            }
-            _ => self.pattern.ty
-        };
-        debug!("field_ty -> {:?}", r);
-        r
-    }
-}
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index 6a22dce7af9..78c438df6f8 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -230,6 +230,8 @@ pub enum ExprKind<'tcx> {
     },
     InlineAsm {
         asm: &'tcx hir::InlineAsm,
+        outputs: Vec<ExprRef<'tcx>>,
+        inputs: Vec<ExprRef<'tcx>>
     },
 }
 
diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs
index 13521de78af..4717c54ca64 100644
--- a/src/librustc_mir/mir_map.rs
+++ b/src/librustc_mir/mir_map.rs
@@ -33,6 +33,7 @@ use rustc::util::common::ErrorReported;
 use rustc::util::nodemap::NodeMap;
 use rustc_front::hir;
 use rustc_front::intravisit::{self, Visitor};
+use syntax::abi::Abi;
 use syntax::ast;
 use syntax::attr::AttrMetaMethods;
 use syntax::codemap::Span;
@@ -181,13 +182,20 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>,
     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))
+    let mut mir = build::construct(cx, span, implicit_arg_tys, arguments,
+                                  parameter_scope, fn_sig.output, body);
+
+    match cx.tcx().node_id_to_type(fn_id).sty {
+        ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => {
+            // RustCall pseudo-ABI untuples the last argument.
+            if let Some(arg_decl) = mir.arg_decls.last_mut() {
+                arg_decl.spread = true;
+            }
+        }
+        _ => {}
+    }
+
+    Ok(mir)
 }
 
 fn closure_self_ty<'a, 'tcx>(tcx: &TyCtxt<'tcx>,
diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs
index 1920bd552ec..9ac3749589e 100644
--- a/src/librustc_mir/transform/erase_regions.rs
+++ b/src/librustc_mir/transform/erase_regions.rs
@@ -82,7 +82,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
             Rvalue::BinaryOp(_, _, _) |
             Rvalue::UnaryOp(_, _) |
             Rvalue::Slice { input: _, from_start: _, from_end: _ } |
-            Rvalue::InlineAsm(_) => {},
+            Rvalue::InlineAsm {..} => {},
 
             Rvalue::Repeat(_, ref mut value) => value.ty = self.tcx.erase_regions(&value.ty),
             Rvalue::Ref(ref mut region, _, _) => *region = ty::ReStatic,
diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs
index 6be7f6c2002..c964179d407 100644
--- a/src/librustc_passes/consts.rs
+++ b/src/librustc_passes/consts.rs
@@ -610,9 +610,8 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                 }
                 Some(Def::Const(did)) |
                 Some(Def::AssociatedConst(did)) => {
-                    if let Some((expr, _ty)) = const_eval::lookup_const_by_id(v.tcx, did,
-                                                                       Some(e.id),
-                                                                       None) {
+                    let substs = Some(v.tcx.node_id_item_substs(e.id).substs);
+                    if let Some((expr, _)) = const_eval::lookup_const_by_id(v.tcx, did, substs) {
                         let inner = v.global_expr(Mode::Const, expr);
                         v.add_qualif(inner);
                     }
@@ -756,7 +755,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
         // Expressions with side-effects.
         hir::ExprAssign(..) |
         hir::ExprAssignOp(..) |
-        hir::ExprInlineAsm(_) => {
+        hir::ExprInlineAsm(..) => {
             v.add_qualif(ConstQualif::NOT_CONST);
             if v.mode != Mode::Var {
                 span_err!(v.tcx.sess, e.span, E0019,
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index dba7d178b4c..08b5e517290 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -105,36 +105,8 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
     /// otherwise, reports an error.
     fn define<T: ToNameBinding<'b>>(&self, parent: Module<'b>, name: Name, ns: Namespace, def: T) {
         let binding = def.to_name_binding();
-        let old_binding = match parent.try_define_child(name, ns, binding.clone()) {
-            Ok(()) => return,
-            Err(old_binding) => old_binding,
-        };
-
-        let span = binding.span.unwrap_or(DUMMY_SP);
-        if !old_binding.is_extern_crate() && !binding.is_extern_crate() {
-            // Record an error here by looking up the namespace that had the duplicate
-            let ns_str = match ns { TypeNS => "type or module", ValueNS => "value" };
-            let resolution_error = ResolutionError::DuplicateDefinition(ns_str, name);
-            let mut err = resolve_struct_error(self, span, resolution_error);
-
-            if let Some(sp) = old_binding.span {
-                let note = format!("first definition of {} `{}` here", ns_str, name);
-                err.span_note(sp, &note);
-            }
-            err.emit();
-        } else if old_binding.is_extern_crate() && binding.is_extern_crate() {
-            span_err!(self.session,
-                      span,
-                      E0259,
-                      "an external crate named `{}` has already been imported into this module",
-                      name);
-        } else {
-            span_err!(self.session,
-                      span,
-                      E0260,
-                      "the name `{}` conflicts with an external crate \
-                      that has been imported into this module",
-                      name);
+        if let Err(old_binding) = parent.try_define_child(name, ns, binding.clone()) {
+            self.report_conflict(parent, name, ns, old_binding, &binding);
         }
     }
 
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 9a4173bad6e..6313d7b7036 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -183,8 +183,6 @@ pub enum ResolutionError<'a> {
     UndeclaredLabel(&'a str),
     /// error E0427: cannot use `ref` binding mode with ...
     CannotUseRefBindingModeWith(&'a str),
-    /// error E0428: duplicate definition
-    DuplicateDefinition(&'a str, Name),
     /// error E0429: `self` imports are only allowed within a { } list
     SelfImportsOnlyAllowedWithin,
     /// error E0430: `self` import can only appear once in the list
@@ -490,14 +488,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
                              "cannot use `ref` binding mode with {}",
                              descr)
         }
-        ResolutionError::DuplicateDefinition(namespace, name) => {
-            struct_span_err!(resolver.session,
-                             span,
-                             E0428,
-                             "duplicate definition of {} `{}`",
-                             namespace,
-                             name)
-        }
         ResolutionError::SelfImportsOnlyAllowedWithin => {
             struct_span_err!(resolver.session,
                              span,
@@ -3530,8 +3520,62 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
         }
     }
-}
 
+    fn report_conflict(&self,
+                       parent: Module,
+                       name: Name,
+                       ns: Namespace,
+                       binding: &NameBinding,
+                       old_binding: &NameBinding) {
+        // Error on the second of two conflicting names
+        if old_binding.span.unwrap().lo > binding.span.unwrap().lo {
+            return self.report_conflict(parent, name, ns, old_binding, binding);
+        }
+
+        let container = match parent.def {
+            Some(Def::Mod(_)) => "module",
+            Some(Def::Trait(_)) => "trait",
+            None => "block",
+            _ => "enum",
+        };
+
+        let (participle, noun) = match old_binding.is_import() || old_binding.is_extern_crate() {
+            true => ("imported", "import"),
+            false => ("defined", "definition"),
+        };
+
+        let span = binding.span.unwrap();
+        let msg = {
+            let kind = match (ns, old_binding.module()) {
+                (ValueNS, _) => "a value",
+                (TypeNS, Some(module)) if module.extern_crate_id.is_some() => "an extern crate",
+                (TypeNS, Some(module)) if module.is_normal() => "a module",
+                (TypeNS, Some(module)) if module.is_trait() => "a trait",
+                (TypeNS, _) => "a type",
+            };
+            format!("{} named `{}` has already been {} in this {}",
+                    kind, name, participle, container)
+        };
+
+        let mut err = match (old_binding.is_extern_crate(), binding.is_extern_crate()) {
+            (true, true) => struct_span_err!(self.session, span, E0259, "{}", msg),
+            (true, _) | (_, true) if binding.is_import() || old_binding.is_import() =>
+                struct_span_err!(self.session, span, E0254, "{}", msg),
+            (true, _) | (_, true) => struct_span_err!(self.session, span, E0260, "{}", msg),
+            _ => match (old_binding.is_import(), binding.is_import()) {
+                (false, false) => struct_span_err!(self.session, span, E0428, "{}", msg),
+                (true, true) => struct_span_err!(self.session, span, E0252, "{}", msg),
+                _ => struct_span_err!(self.session, span, E0255, "{}", msg),
+            },
+        };
+
+        let span = old_binding.span.unwrap();
+        if span != codemap::DUMMY_SP {
+            err.span_note(span, &format!("previous {} of `{}` here", noun, name));
+        }
+        err.emit();
+    }
+}
 
 fn names_to_string(names: &[Name]) -> String {
     let mut first = true;
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index c2b665b3b4c..bca79df7a91 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -513,7 +513,8 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                 let imported_binding = directive.import(binding, privacy_error);
                 let conflict = module_.try_define_child(target, ns, imported_binding);
                 if let Err(old_binding) = conflict {
-                    self.report_conflict(target, ns, &directive.import(binding, None), old_binding);
+                    let binding = &directive.import(binding, None);
+                    self.resolver.report_conflict(module_, target, ns, binding, old_binding);
                 }
             }
 
@@ -650,67 +651,6 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
         return Success(());
     }
 
-    fn report_conflict(&mut self,
-                       name: Name,
-                       ns: Namespace,
-                       binding: &NameBinding,
-                       old_binding: &NameBinding) {
-        // Error on the second of two conflicting imports
-        if old_binding.is_import() && binding.is_import() &&
-           old_binding.span.unwrap().lo > binding.span.unwrap().lo {
-            self.report_conflict(name, ns, old_binding, binding);
-            return;
-        }
-
-        if old_binding.is_extern_crate() {
-            let msg = format!("import `{0}` conflicts with imported crate \
-                               in this module (maybe you meant `use {0}::*`?)",
-                              name);
-            span_err!(self.resolver.session, binding.span.unwrap(), E0254, "{}", &msg);
-        } else if old_binding.is_import() {
-            let ns_word = match (ns, old_binding.module()) {
-                (ValueNS, _) => "value",
-                (TypeNS, Some(module)) if module.is_normal() => "module",
-                (TypeNS, Some(module)) if module.is_trait() => "trait",
-                (TypeNS, _) => "type",
-            };
-            let mut err = struct_span_err!(self.resolver.session,
-                                           binding.span.unwrap(),
-                                           E0252,
-                                           "a {} named `{}` has already been imported \
-                                            in this module",
-                                           ns_word,
-                                           name);
-            err.span_note(old_binding.span.unwrap(),
-                          &format!("previous import of `{}` here", name));
-            err.emit();
-        } else if ns == ValueNS { // Check for item conflicts in the value namespace
-            let mut err = struct_span_err!(self.resolver.session,
-                                           binding.span.unwrap(),
-                                           E0255,
-                                           "import `{}` conflicts with value in this module",
-                                           name);
-            err.span_note(old_binding.span.unwrap(), "conflicting value here");
-            err.emit();
-        } else { // Check for item conflicts in the type namespace
-            let (what, note) = match old_binding.module() {
-                Some(ref module) if module.is_normal() =>
-                    ("existing submodule", "note conflicting module here"),
-                Some(ref module) if module.is_trait() =>
-                    ("trait in this module", "note conflicting trait here"),
-                _ => ("type in this module", "note conflicting type here"),
-            };
-            let mut err = struct_span_err!(self.resolver.session,
-                                           binding.span.unwrap(),
-                                           E0256,
-                                           "import `{}` conflicts with {}",
-                                           name,
-                                           what);
-            err.span_note(old_binding.span.unwrap(), note);
-            err.emit();
-        }
-    }
-
     // Miscellaneous post-processing, including recording reexports, recording shadowed traits,
     // reporting conflicts, reporting the PRIVATE_IN_PUBLIC lint, and reporting unresolved imports.
     fn finalize_resolutions(&mut self, module: Module<'b>, report_unresolved_imports: bool) {
@@ -720,7 +660,10 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
 
         let mut reexports = Vec::new();
         for (&(name, ns), resolution) in module.resolutions.borrow().iter() {
-            resolution.report_conflicts(|b1, b2| self.report_conflict(name, ns, b1, b2));
+            resolution.report_conflicts(|b1, b2| {
+                self.resolver.report_conflict(module, name, ns, b1, b2)
+            });
+
             let binding = match resolution.binding {
                 Some(binding) => binding,
                 None => continue,
diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs
index d7e79e46720..c8ce09b4d79 100644
--- a/src/librustc_trans/lib.rs
+++ b/src/librustc_trans/lib.rs
@@ -61,7 +61,6 @@ pub use rustc::lint;
 pub use rustc::util;
 
 pub mod back {
-    pub use rustc_back::abi;
     pub use rustc_back::rpath;
     pub use rustc_back::svh;
 
diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs
index f5fbec0b187..8aea2f1ec4f 100644
--- a/src/librustc_trans/trans/_match.rs
+++ b/src/librustc_trans/trans/_match.rs
@@ -200,12 +200,13 @@ use middle::lang_items::StrEqFnLangItem;
 use middle::mem_categorization as mc;
 use middle::mem_categorization::Categorization;
 use middle::pat_util::*;
+use middle::subst::Substs;
 use trans::adt;
 use trans::base::*;
 use trans::build::{AddCase, And, Br, CondBr, GEPi, InBoundsGEP, Load, PointerCast};
 use trans::build::{Not, Store, Sub, add_comment};
 use trans::build;
-use trans::callee;
+use trans::callee::{Callee, ArgVals};
 use trans::cleanup::{self, CleanupMethods, DropHintMethods};
 use trans::common::*;
 use trans::consts;
@@ -216,6 +217,7 @@ use trans::monomorphize;
 use trans::tvec;
 use trans::type_of;
 use trans::Disr;
+use trans::value::Value;
 use middle::ty::{self, Ty, TyCtxt};
 use middle::traits::ProjectionMode;
 use session::config::NoDebugInfo;
@@ -448,6 +450,12 @@ impl<'tcx> Datum<'tcx, Lvalue> {
     }
 }
 
+impl fmt::Debug for MatchInput {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(&Value(self.val), f)
+    }
+}
+
 impl MatchInput {
     fn from_val(val: ValueRef) -> MatchInput {
         MatchInput {
@@ -466,11 +474,8 @@ fn expand_nested_bindings<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                               col: usize,
                                               val: MatchInput)
                                               -> Vec<Match<'a, 'p, 'blk, 'tcx>> {
-    debug!("expand_nested_bindings(bcx={}, m={:?}, col={}, val={})",
-           bcx.to_str(),
-           m,
-           col,
-           bcx.val_to_string(val.val));
+    debug!("expand_nested_bindings(bcx={}, m={:?}, col={}, val={:?})",
+           bcx.to_str(), m, col, val);
     let _indenter = indenter();
 
     m.iter().map(|br| {
@@ -506,11 +511,8 @@ fn enter_match<'a, 'b, 'p, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
                                           -> Vec<Match<'a, 'p, 'blk, 'tcx>> where
     F: FnMut(&[&'p hir::Pat]) -> Option<Vec<&'p hir::Pat>>,
 {
-    debug!("enter_match(bcx={}, m={:?}, col={}, val={})",
-           bcx.to_str(),
-           m,
-           col,
-           bcx.val_to_string(val.val));
+    debug!("enter_match(bcx={}, m={:?}, col={}, val={:?})",
+           bcx.to_str(), m, col, val);
     let _indenter = indenter();
 
     m.iter().filter_map(|br| {
@@ -549,11 +551,8 @@ fn enter_default<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                      col: usize,
                                      val: MatchInput)
                                      -> Vec<Match<'a, 'p, 'blk, 'tcx>> {
-    debug!("enter_default(bcx={}, m={:?}, col={}, val={})",
-           bcx.to_str(),
-           m,
-           col,
-           bcx.val_to_string(val.val));
+    debug!("enter_default(bcx={}, m={:?}, col={}, val={:?})",
+           bcx.to_str(), m, col, val);
     let _indenter = indenter();
 
     // Collect all of the matches that can match against anything.
@@ -606,12 +605,8 @@ fn enter_opt<'a, 'p, 'blk, 'tcx>(
              variant_size: usize,
              val: MatchInput)
              -> Vec<Match<'a, 'p, 'blk, 'tcx>> {
-    debug!("enter_opt(bcx={}, m={:?}, opt={:?}, col={}, val={})",
-           bcx.to_str(),
-           m,
-           *opt,
-           col,
-           bcx.val_to_string(val.val));
+    debug!("enter_opt(bcx={}, m={:?}, opt={:?}, col={}, val={:?})",
+           bcx.to_str(), m, *opt, col, val);
     let _indenter = indenter();
 
     let ctor = match opt {
@@ -887,7 +882,7 @@ fn compare_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                               rhs_t: Ty<'tcx>,
                               debug_loc: DebugLoc)
                               -> Result<'blk, 'tcx> {
-    fn compare_str<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
+    fn compare_str<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                lhs_data: ValueRef,
                                lhs_len: ValueRef,
                                rhs_data: ValueRef,
@@ -895,11 +890,13 @@ fn compare_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                                rhs_t: Ty<'tcx>,
                                debug_loc: DebugLoc)
                                -> Result<'blk, 'tcx> {
-        let did = langcall(cx,
+        let did = langcall(bcx,
                            None,
                            &format!("comparison of `{}`", rhs_t),
                            StrEqFnLangItem);
-        callee::trans_lang_call(cx, did, &[lhs_data, lhs_len, rhs_data, rhs_len], None, debug_loc)
+        let args = [lhs_data, lhs_len, rhs_data, rhs_len];
+        Callee::def(bcx.ccx(), did, bcx.tcx().mk_substs(Substs::empty()))
+            .call(bcx, debug_loc, ArgVals(&args), None)
     }
 
     let _icx = push_ctxt("compare_values");
@@ -1032,7 +1029,7 @@ fn insert_lllocals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             bcx.fcx.schedule_drop_and_fill_mem(cs, llval, binding_info.ty, opt_datum);
         }
 
-        debug!("binding {} to {}", binding_info.id, bcx.val_to_string(llval));
+        debug!("binding {} to {:?}", binding_info.id, Value(llval));
         bcx.fcx.lllocals.borrow_mut().insert(binding_info.id, datum);
         debuginfo::create_match_binding_metadata(bcx, name, binding_info);
     }
@@ -1047,11 +1044,8 @@ fn compile_guard<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                      chk: &FailureHandler,
                                      has_genuine_default: bool)
                                      -> Block<'blk, 'tcx> {
-    debug!("compile_guard(bcx={}, guard_expr={:?}, m={:?}, vals=[{}])",
-           bcx.to_str(),
-           guard_expr,
-           m,
-           vals.iter().map(|v| bcx.val_to_string(v.val)).collect::<Vec<_>>().join(", "));
+    debug!("compile_guard(bcx={}, guard_expr={:?}, m={:?}, vals={:?})",
+           bcx.to_str(), guard_expr, m, vals);
     let _indenter = indenter();
 
     let mut bcx = insert_lllocals(bcx, &data.bindings_map, None);
@@ -1093,10 +1087,8 @@ fn compile_submatch<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                         vals: &[MatchInput],
                                         chk: &FailureHandler,
                                         has_genuine_default: bool) {
-    debug!("compile_submatch(bcx={}, m={:?}, vals=[{}])",
-           bcx.to_str(),
-           m,
-           vals.iter().map(|v| bcx.val_to_string(v.val)).collect::<Vec<_>>().join(", "));
+    debug!("compile_submatch(bcx={}, m={:?}, vals=[{:?}])",
+           bcx.to_str(), m, vals);
     let _indenter = indenter();
     let _icx = push_ctxt("match::compile_submatch");
     let mut bcx = bcx;
@@ -1256,7 +1248,7 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     debug!("options={:?}", opts);
     let mut kind = NoBranch;
     let mut test_val = val.val;
-    debug!("test_val={}", bcx.val_to_string(test_val));
+    debug!("test_val={:?}", Value(test_val));
     if !opts.is_empty() {
         match opts[0] {
             ConstantValue(..) | ConstantRange(..) => {
@@ -1761,8 +1753,8 @@ fn mk_binding_alloca<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>,
     let lvalue = Lvalue::new_with_hint(caller_name, bcx, p_id, HintKind::DontZeroJustUse);
     let datum = Datum::new(llval, var_ty, lvalue);
 
-    debug!("mk_binding_alloca cleanup_scope={:?} llval={} var_ty={:?}",
-           cleanup_scope, bcx.ccx().tn().val_to_string(llval), var_ty);
+    debug!("mk_binding_alloca cleanup_scope={:?} llval={:?} var_ty={:?}",
+           cleanup_scope, Value(llval), var_ty);
 
     // Subtle: be sure that we *populate* the memory *before*
     // we schedule the cleanup.
@@ -1794,10 +1786,8 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                     val: MatchInput,
                                     cleanup_scope: cleanup::ScopeId)
                                     -> Block<'blk, 'tcx> {
-    debug!("bind_irrefutable_pat(bcx={}, pat={:?}, val={})",
-           bcx.to_str(),
-           pat,
-           bcx.val_to_string(val.val));
+    debug!("bind_irrefutable_pat(bcx={}, pat={:?}, val={:?})",
+           bcx.to_str(), pat, val);
 
     if bcx.sess().asm_comments() {
         add_comment(bcx, &format!("bind_irrefutable_pat(pat={:?})",
@@ -1923,7 +1913,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 // a regular one
                 if !type_is_sized(tcx, fty) {
                     let scratch = alloc_ty(bcx, fty, "__struct_field_fat_ptr");
-                    debug!("Creating fat pointer {}", bcx.val_to_string(scratch));
+                    debug!("Creating fat pointer {:?}", Value(scratch));
                     Store(bcx, fldptr, expr::get_dataptr(bcx, scratch));
                     Store(bcx, val.meta, expr::get_meta(bcx, scratch));
                     fldptr = scratch;
diff --git a/src/librustc_trans/trans/abi.rs b/src/librustc_trans/trans/abi.rs
new file mode 100644
index 00000000000..fd086522b2e
--- /dev/null
+++ b/src/librustc_trans/trans/abi.rs
@@ -0,0 +1,538 @@
+// Copyright 2012-2016 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::{self, ValueRef};
+use trans::base;
+use trans::builder::Builder;
+use trans::common::{type_is_fat_ptr, BlockAndBuilder};
+use trans::context::CrateContext;
+use trans::cabi_x86;
+use trans::cabi_x86_64;
+use trans::cabi_x86_win64;
+use trans::cabi_arm;
+use trans::cabi_aarch64;
+use trans::cabi_powerpc;
+use trans::cabi_powerpc64;
+use trans::cabi_mips;
+use trans::cabi_asmjs;
+use trans::machine::{llalign_of_min, llsize_of, llsize_of_real};
+use trans::type_::Type;
+use trans::type_of;
+
+use rustc_front::hir;
+use middle::ty::{self, Ty};
+
+use libc::c_uint;
+
+pub use syntax::abi::Abi;
+
+/// The first half of a fat pointer.
+/// - For a closure, this is the code address.
+/// - For an object or trait instance, this is the address of the box.
+/// - For a slice, this is the base address.
+pub const FAT_PTR_ADDR: usize = 0;
+
+/// The second half of a fat pointer.
+/// - For a closure, this is the address of the environment.
+/// - For an object or trait instance, this is the address of the vtable.
+/// - For a slice, this is the length.
+pub const FAT_PTR_EXTRA: usize = 1;
+
+#[derive(Clone, Copy, PartialEq, Debug)]
+enum ArgKind {
+    /// Pass the argument directly using the normal converted
+    /// LLVM type or by coercing to another specified type
+    Direct,
+    /// Pass the argument indirectly via a hidden pointer
+    Indirect,
+    /// Ignore the argument (useful for empty struct)
+    Ignore,
+}
+
+/// Information about how a specific C type
+/// should be passed to or returned from a function
+///
+/// This is borrowed from clang's ABIInfo.h
+#[derive(Clone, Copy, Debug)]
+pub struct ArgType {
+    kind: ArgKind,
+    /// Original LLVM type
+    pub original_ty: Type,
+    /// Sizing LLVM type (pointers are opaque).
+    /// Unlike original_ty, this is guaranteed to be complete.
+    ///
+    /// For example, while we're computing the function pointer type in
+    /// `struct Foo(fn(Foo));`, `original_ty` is still LLVM's `%Foo = {}`.
+    /// The field type will likely end up being `void(%Foo)*`, but we cannot
+    /// use `%Foo` to compute properties (e.g. size and alignment) of `Foo`,
+    /// until `%Foo` is completed by having all of its field types inserted,
+    /// so `ty` holds the "sizing type" of `Foo`, which replaces all pointers
+    /// with opaque ones, resulting in `{i8*}` for `Foo`.
+    /// ABI-specific logic can then look at the size, alignment and fields of
+    /// `{i8*}` in order to determine how the argument will be passed.
+    /// Only later will `original_ty` aka `%Foo` be used in the LLVM function
+    /// pointer type, without ever having introspected it.
+    pub ty: Type,
+    /// Coerced LLVM Type
+    pub cast: Option<Type>,
+    /// Dummy argument, which is emitted before the real argument
+    pub pad: Option<Type>,
+    /// LLVM attributes of argument
+    pub attrs: llvm::Attributes
+}
+
+impl ArgType {
+    fn new(original_ty: Type, ty: Type) -> ArgType {
+        ArgType {
+            kind: ArgKind::Direct,
+            original_ty: original_ty,
+            ty: ty,
+            cast: None,
+            pad: None,
+            attrs: llvm::Attributes::default()
+        }
+    }
+
+    pub fn make_indirect(&mut self, ccx: &CrateContext) {
+        assert_eq!(self.kind, ArgKind::Direct);
+
+        // Wipe old attributes, likely not valid through indirection.
+        self.attrs = llvm::Attributes::default();
+
+        let llarg_sz = llsize_of_real(ccx, self.ty);
+
+        // For non-immediate arguments the callee gets its own copy of
+        // the value on the stack, so there are no aliases. It's also
+        // program-invisible so can't possibly capture
+        self.attrs.set(llvm::Attribute::NoAlias)
+                  .set(llvm::Attribute::NoCapture)
+                  .set_dereferenceable(llarg_sz);
+
+        self.kind = ArgKind::Indirect;
+    }
+
+    pub fn ignore(&mut self) {
+        assert_eq!(self.kind, ArgKind::Direct);
+        self.kind = ArgKind::Ignore;
+    }
+
+    pub fn is_indirect(&self) -> bool {
+        self.kind == ArgKind::Indirect
+    }
+
+    pub fn is_ignore(&self) -> bool {
+        self.kind == ArgKind::Ignore
+    }
+
+    /// Get the LLVM type for an lvalue of the original Rust type of
+    /// this argument/return, i.e. the result of `type_of::type_of`.
+    pub fn memory_ty(&self, ccx: &CrateContext) -> Type {
+        if self.original_ty == Type::i1(ccx) {
+            Type::i8(ccx)
+        } else {
+            self.original_ty
+        }
+    }
+
+    /// Store a direct/indirect value described by this ArgType into a
+    /// lvalue for the original Rust type of this argument/return.
+    /// Can be used for both storing formal arguments into Rust variables
+    /// or results of call/invoke instructions into their destinations.
+    pub fn store(&self, b: &Builder, mut val: ValueRef, dst: ValueRef) {
+        if self.is_ignore() {
+            return;
+        }
+        if self.is_indirect() {
+            let llsz = llsize_of(b.ccx, self.ty);
+            let llalign = llalign_of_min(b.ccx, self.ty);
+            base::call_memcpy(b, dst, val, llsz, llalign as u32);
+        } else if let Some(ty) = self.cast {
+            let cast_dst = b.pointercast(dst, ty.ptr_to());
+            let store = b.store(val, cast_dst);
+            let llalign = llalign_of_min(b.ccx, self.ty);
+            unsafe {
+                llvm::LLVMSetAlignment(store, llalign);
+            }
+        } else {
+            if self.original_ty == Type::i1(b.ccx) {
+                val = b.zext(val, Type::i8(b.ccx));
+            }
+            b.store(val, dst);
+        }
+    }
+
+    pub fn store_fn_arg(&self, bcx: &BlockAndBuilder, idx: &mut usize, dst: ValueRef) {
+        if self.pad.is_some() {
+            *idx += 1;
+        }
+        if self.is_ignore() {
+            return;
+        }
+        let val = llvm::get_param(bcx.fcx().llfn, *idx as c_uint);
+        *idx += 1;
+        self.store(bcx, val, dst);
+    }
+}
+
+/// Metadata describing how the arguments to a native function
+/// should be passed in order to respect the native ABI.
+///
+/// I will do my best to describe this structure, but these
+/// comments are reverse-engineered and may be inaccurate. -NDM
+pub struct FnType {
+    /// The LLVM types of each argument.
+    pub args: Vec<ArgType>,
+
+    /// LLVM return type.
+    pub ret: ArgType,
+
+    pub variadic: bool,
+
+    pub cconv: llvm::CallConv
+}
+
+impl FnType {
+    pub fn new<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                         abi: Abi,
+                         sig: &ty::FnSig<'tcx>,
+                         extra_args: &[Ty<'tcx>]) -> FnType {
+        let mut fn_ty = FnType::unadjusted(ccx, abi, sig, extra_args);
+        fn_ty.adjust_for_abi(ccx, abi, sig);
+        fn_ty
+    }
+
+    pub fn unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                                abi: Abi,
+                                sig: &ty::FnSig<'tcx>,
+                                extra_args: &[Ty<'tcx>]) -> FnType {
+        use self::Abi::*;
+        let cconv = match ccx.sess().target.target.adjust_abi(abi) {
+            RustIntrinsic | PlatformIntrinsic |
+            Rust | RustCall => llvm::CCallConv,
+
+            // It's the ABI's job to select this, not us.
+            System => ccx.sess().bug("system abi should be selected elsewhere"),
+
+            Stdcall => llvm::X86StdcallCallConv,
+            Fastcall => llvm::X86FastcallCallConv,
+            Vectorcall => llvm::X86_VectorCall,
+            C => llvm::CCallConv,
+            Win64 => llvm::X86_64_Win64,
+
+            // These API constants ought to be more specific...
+            Cdecl => llvm::CCallConv,
+            Aapcs => llvm::CCallConv,
+        };
+
+        let mut inputs = &sig.inputs[..];
+        let extra_args = if abi == RustCall {
+            assert!(!sig.variadic && extra_args.is_empty());
+
+            match inputs[inputs.len() - 1].sty {
+                ty::TyTuple(ref tupled_arguments) => {
+                    inputs = &inputs[..inputs.len() - 1];
+                    &tupled_arguments[..]
+                }
+                _ => {
+                    unreachable!("argument to function with \"rust-call\" ABI \
+                                  is not a tuple");
+                }
+            }
+        } else {
+            assert!(sig.variadic || extra_args.is_empty());
+            extra_args
+        };
+
+        let target = &ccx.sess().target.target;
+        let win_x64_gnu = target.target_os == "windows"
+                       && target.arch == "x86_64"
+                       && target.target_env == "gnu";
+        let rust_abi = match abi {
+            RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true,
+            _ => false
+        };
+
+        let arg_of = |ty: Ty<'tcx>, is_return: bool| {
+            if ty.is_bool() {
+                let llty = Type::i1(ccx);
+                let mut arg = ArgType::new(llty, llty);
+                arg.attrs.set(llvm::Attribute::ZExt);
+                arg
+            } else {
+                let mut arg = ArgType::new(type_of::type_of(ccx, ty),
+                                           type_of::sizing_type_of(ccx, ty));
+                if llsize_of_real(ccx, arg.ty) == 0 {
+                    // For some forsaken reason, x86_64-pc-windows-gnu
+                    // doesn't ignore zero-sized struct arguments.
+                    if is_return || rust_abi || !win_x64_gnu {
+                        arg.ignore();
+                    }
+                }
+                arg
+            }
+        };
+
+        let ret_ty = match sig.output {
+            ty::FnConverging(ret_ty) => ret_ty,
+            ty::FnDiverging => ccx.tcx().mk_nil()
+        };
+        let mut ret = arg_of(ret_ty, true);
+
+        if !type_is_fat_ptr(ccx.tcx(), ret_ty) {
+            // The `noalias` attribute on the return value is useful to a
+            // function ptr caller.
+            if let ty::TyBox(_) = ret_ty.sty {
+                // `Box` pointer return values never alias because ownership
+                // is transferred
+                ret.attrs.set(llvm::Attribute::NoAlias);
+            }
+
+            // We can also mark the return value as `dereferenceable` in certain cases
+            match ret_ty.sty {
+                // These are not really pointers but pairs, (pointer, len)
+                ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
+                ty::TyBox(ty) => {
+                    let llty = type_of::sizing_type_of(ccx, ty);
+                    let llsz = llsize_of_real(ccx, llty);
+                    ret.attrs.set_dereferenceable(llsz);
+                }
+                _ => {}
+            }
+        }
+
+        let mut args = Vec::with_capacity(inputs.len() + extra_args.len());
+
+        // Handle safe Rust thin and fat pointers.
+        let rust_ptr_attrs = |ty: Ty<'tcx>, arg: &mut ArgType| match ty.sty {
+            // `Box` pointer parameters never alias because ownership is transferred
+            ty::TyBox(inner) => {
+                arg.attrs.set(llvm::Attribute::NoAlias);
+                Some(inner)
+            }
+
+            ty::TyRef(b, mt) => {
+                use middle::ty::{BrAnon, ReLateBound};
+
+                // `&mut` pointer parameters never alias other parameters, or mutable global data
+                //
+                // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as
+                // both `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely
+                // on memory dependencies rather than pointer equality
+                let interior_unsafe = mt.ty.type_contents(ccx.tcx()).interior_unsafe();
+
+                if mt.mutbl != hir::MutMutable && !interior_unsafe {
+                    arg.attrs.set(llvm::Attribute::NoAlias);
+                }
+
+                if mt.mutbl == hir::MutImmutable && !interior_unsafe {
+                    arg.attrs.set(llvm::Attribute::ReadOnly);
+                }
+
+                // When a reference in an argument has no named lifetime, it's
+                // impossible for that reference to escape this function
+                // (returned or stored beyond the call by a closure).
+                if let ReLateBound(_, BrAnon(_)) = *b {
+                    arg.attrs.set(llvm::Attribute::NoCapture);
+                }
+
+                Some(mt.ty)
+            }
+            _ => None
+        };
+
+        for ty in inputs.iter().chain(extra_args.iter()) {
+            let mut arg = arg_of(ty, false);
+
+            if type_is_fat_ptr(ccx.tcx(), ty) {
+                let original_tys = arg.original_ty.field_types();
+                let sizing_tys = arg.ty.field_types();
+                assert_eq!((original_tys.len(), sizing_tys.len()), (2, 2));
+
+                let mut data = ArgType::new(original_tys[0], sizing_tys[0]);
+                let mut info = ArgType::new(original_tys[1], sizing_tys[1]);
+
+                if let Some(inner) = rust_ptr_attrs(ty, &mut data) {
+                    data.attrs.set(llvm::Attribute::NonNull);
+                    if ccx.tcx().struct_tail(inner).is_trait() {
+                        info.attrs.set(llvm::Attribute::NonNull);
+                    }
+                }
+                args.push(data);
+                args.push(info);
+            } else {
+                if let Some(inner) = rust_ptr_attrs(ty, &mut arg) {
+                    let llty = type_of::sizing_type_of(ccx, inner);
+                    let llsz = llsize_of_real(ccx, llty);
+                    arg.attrs.set_dereferenceable(llsz);
+                }
+                args.push(arg);
+            }
+        }
+
+        FnType {
+            args: args,
+            ret: ret,
+            variadic: sig.variadic,
+            cconv: cconv
+        }
+    }
+
+    pub fn adjust_for_abi<'a, 'tcx>(&mut self,
+                                    ccx: &CrateContext<'a, 'tcx>,
+                                    abi: Abi,
+                                    sig: &ty::FnSig<'tcx>) {
+        if abi == Abi::Rust || abi == Abi::RustCall ||
+           abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
+            let fixup = |arg: &mut ArgType| {
+                let mut llty = arg.ty;
+
+                // Replace newtypes with their inner-most type.
+                while llty.kind() == llvm::TypeKind::Struct {
+                    let inner = llty.field_types();
+                    if inner.len() != 1 {
+                        break;
+                    }
+                    llty = inner[0];
+                }
+
+                if !llty.is_aggregate() {
+                    // Scalars and vectors, always immediate.
+                    if llty != arg.ty {
+                        // Needs a cast as we've unpacked a newtype.
+                        arg.cast = Some(llty);
+                    }
+                    return;
+                }
+
+                let size = llsize_of_real(ccx, llty);
+                if size > llsize_of_real(ccx, ccx.int_type()) {
+                    arg.make_indirect(ccx);
+                } else if size > 0 {
+                    // We want to pass small aggregates as immediates, but using
+                    // a LLVM aggregate type for this leads to bad optimizations,
+                    // so we pick an appropriately sized integer type instead.
+                    arg.cast = Some(Type::ix(ccx, size * 8));
+                }
+            };
+            // Fat pointers are returned by-value.
+            if !self.ret.is_ignore() {
+                if !type_is_fat_ptr(ccx.tcx(), sig.output.unwrap()) {
+                    fixup(&mut self.ret);
+                }
+            }
+            for arg in &mut self.args {
+                if arg.is_ignore() { continue; }
+                fixup(arg);
+            }
+            if self.ret.is_indirect() {
+                self.ret.attrs.set(llvm::Attribute::StructRet);
+            }
+            return;
+        }
+
+        match &ccx.sess().target.target.arch[..] {
+            "x86" => cabi_x86::compute_abi_info(ccx, self),
+            "x86_64" => if ccx.sess().target.target.options.is_like_windows {
+                cabi_x86_win64::compute_abi_info(ccx, self);
+            } else {
+                cabi_x86_64::compute_abi_info(ccx, self);
+            },
+            "aarch64" => cabi_aarch64::compute_abi_info(ccx, self),
+            "arm" => {
+                let flavor = if ccx.sess().target.target.target_os == "ios" {
+                    cabi_arm::Flavor::Ios
+                } else {
+                    cabi_arm::Flavor::General
+                };
+                cabi_arm::compute_abi_info(ccx, self, flavor);
+            },
+            "mips" => cabi_mips::compute_abi_info(ccx, self),
+            "powerpc" => cabi_powerpc::compute_abi_info(ccx, self),
+            "powerpc64" => cabi_powerpc64::compute_abi_info(ccx, self),
+            "asmjs" => cabi_asmjs::compute_abi_info(ccx, self),
+            a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a))
+        }
+
+        if self.ret.is_indirect() {
+            self.ret.attrs.set(llvm::Attribute::StructRet);
+        }
+    }
+
+    pub fn llvm_type(&self, ccx: &CrateContext) -> Type {
+        let mut llargument_tys = Vec::new();
+
+        let llreturn_ty = if self.ret.is_ignore() {
+            Type::void(ccx)
+        } else if self.ret.is_indirect() {
+            llargument_tys.push(self.ret.original_ty.ptr_to());
+            Type::void(ccx)
+        } else {
+            self.ret.cast.unwrap_or(self.ret.original_ty)
+        };
+
+        for arg in &self.args {
+            if arg.is_ignore() {
+                continue;
+            }
+            // add padding
+            if let Some(ty) = arg.pad {
+                llargument_tys.push(ty);
+            }
+
+            let llarg_ty = if arg.is_indirect() {
+                arg.original_ty.ptr_to()
+            } else {
+                arg.cast.unwrap_or(arg.original_ty)
+            };
+
+            llargument_tys.push(llarg_ty);
+        }
+
+        if self.variadic {
+            Type::variadic_func(&llargument_tys, &llreturn_ty)
+        } else {
+            Type::func(&llargument_tys, &llreturn_ty)
+        }
+    }
+
+    pub fn apply_attrs_llfn(&self, llfn: ValueRef) {
+        let mut i = if self.ret.is_indirect() { 1 } else { 0 };
+        if !self.ret.is_ignore() {
+            self.ret.attrs.apply_llfn(i, llfn);
+        }
+        i += 1;
+        for arg in &self.args {
+            if !arg.is_ignore() {
+                if arg.pad.is_some() { i += 1; }
+                arg.attrs.apply_llfn(i, llfn);
+                i += 1;
+            }
+        }
+    }
+
+    pub fn apply_attrs_callsite(&self, callsite: ValueRef) {
+        let mut i = if self.ret.is_indirect() { 1 } else { 0 };
+        if !self.ret.is_ignore() {
+            self.ret.attrs.apply_callsite(i, callsite);
+        }
+        i += 1;
+        for arg in &self.args {
+            if !arg.is_ignore() {
+                if arg.pad.is_some() { i += 1; }
+                arg.attrs.apply_callsite(i, callsite);
+                i += 1;
+            }
+        }
+
+        if self.cconv != llvm::CCallConv {
+            llvm::SetInstructionCallConv(callsite, self.cconv);
+        }
+    }
+}
diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs
index 320bb1eab3c..45a1236647e 100644
--- a/src/librustc_trans/trans/adt.rs
+++ b/src/librustc_trans/trans/adt.rs
@@ -48,13 +48,13 @@ use std;
 use std::rc::Rc;
 
 use llvm::{ValueRef, True, IntEQ, IntNE};
-use back::abi::FAT_PTR_ADDR;
 use middle::subst;
 use middle::ty::{self, Ty, TyCtxt};
 use syntax::ast;
 use syntax::attr;
 use syntax::attr::IntType;
 use trans::_match;
+use trans::abi::FAT_PTR_ADDR;
 use trans::base::InitAlloca;
 use trans::build::*;
 use trans::cleanup;
@@ -67,6 +67,7 @@ use trans::machine;
 use trans::monomorphize;
 use trans::type_::Type;
 use trans::type_of;
+use trans::value::Value;
 
 type Hint = attr::ReprAttr;
 
@@ -88,11 +89,6 @@ impl TypeContext {
     fn may_need_drop_flag(t: Type, needs_drop_flag: bool) -> TypeContext {
         TypeContext { prefix: t, needs_drop_flag: needs_drop_flag }
     }
-    pub fn to_string(self) -> String {
-        let TypeContext { prefix, needs_drop_flag } = self;
-        format!("TypeContext {{ prefix: {}, needs_drop_flag: {} }}",
-                prefix.to_string(), needs_drop_flag)
-    }
 }
 
 /// Representations.
@@ -1069,6 +1065,15 @@ pub fn num_args(r: &Repr, discr: Disr) -> usize {
 /// Access a field, at a point when the value's case is known.
 pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
                                    val: MaybeSizedValue, discr: Disr, ix: usize) -> ValueRef {
+    trans_field_ptr_builder(&bcx.build(), r, val, discr, ix)
+}
+
+/// Access a field, at a point when the value's case is known.
+pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
+                                           r: &Repr<'tcx>,
+                                           val: MaybeSizedValue,
+                                           discr: Disr, ix: usize)
+                                           -> ValueRef {
     // Note: if this ever needs to generate conditionals (e.g., if we
     // decide to do some kind of cdr-coding-like non-unique repr
     // someday), it will need to return a possibly-new bcx as well.
@@ -1091,13 +1096,15 @@ pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
             assert_eq!(machine::llsize_of_alloc(bcx.ccx(), ty), 0);
             // The contents of memory at this pointer can't matter, but use
             // the value that's "reasonable" in case of pointer comparison.
-            PointerCast(bcx, val.value, ty.ptr_to())
+            if bcx.is_unreachable() { return C_undef(ty.ptr_to()); }
+            bcx.pointercast(val.value, ty.ptr_to())
         }
         RawNullablePointer { nndiscr, nnty, .. } => {
             assert_eq!(ix, 0);
             assert_eq!(discr, nndiscr);
             let ty = type_of::type_of(bcx.ccx(), nnty);
-            PointerCast(bcx, val.value, ty.ptr_to())
+            if bcx.is_unreachable() { return C_undef(ty.ptr_to()); }
+            bcx.pointercast(val.value, ty.ptr_to())
         }
         StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
             assert_eq!(discr, nndiscr);
@@ -1106,43 +1113,48 @@ pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
     }
 }
 
-pub fn struct_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, st: &Struct<'tcx>, val: MaybeSizedValue,
-                                    ix: usize, needs_cast: bool) -> ValueRef {
+fn struct_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
+                                st: &Struct<'tcx>, val: MaybeSizedValue,
+                                ix: usize, needs_cast: bool) -> ValueRef {
     let ccx = bcx.ccx();
+    let fty = st.fields[ix];
+    let ll_fty = type_of::in_memory_type_of(bcx.ccx(), fty);
+    if bcx.is_unreachable() {
+        return C_undef(ll_fty.ptr_to());
+    }
+
     let ptr_val = if needs_cast {
         let fields = st.fields.iter().map(|&ty| {
             type_of::in_memory_type_of(ccx, ty)
         }).collect::<Vec<_>>();
         let real_ty = Type::struct_(ccx, &fields[..], st.packed);
-        PointerCast(bcx, val.value, real_ty.ptr_to())
+        bcx.pointercast(val.value, real_ty.ptr_to())
     } else {
         val.value
     };
 
-    let fty = st.fields[ix];
     // Simple case - we can just GEP the field
     //   * First field - Always aligned properly
     //   * Packed struct - There is no alignment padding
     //   * Field is sized - pointer is properly aligned already
     if ix == 0 || st.packed || type_is_sized(bcx.tcx(), fty) {
-        return StructGEP(bcx, ptr_val, ix);
+        return bcx.struct_gep(ptr_val, ix);
     }
 
     // If the type of the last field is [T] or str, then we don't need to do
     // any adjusments
     match fty.sty {
         ty::TySlice(..) | ty::TyStr => {
-            return StructGEP(bcx, ptr_val, ix);
+            return bcx.struct_gep(ptr_val, ix);
         }
         _ => ()
     }
 
     // There's no metadata available, log the case and just do the GEP.
     if !val.has_meta() {
-        debug!("Unsized field `{}`, of `{}` has no metadata for adjustment",
-               ix,
-               bcx.val_to_string(ptr_val));
-        return StructGEP(bcx, ptr_val, ix);
+        debug!("Unsized field `{}`, of `{:?}` has no metadata for adjustment",
+               ix, Value(ptr_val));
+        return bcx.struct_gep(ptr_val, ix);
     }
 
     let dbloc = DebugLoc::None;
@@ -1183,23 +1195,21 @@ pub fn struct_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, st: &Struct<'tcx>, v
     //   (unaligned offset + (align - 1)) & -align
 
     // Calculate offset
-    let align_sub_1 = Sub(bcx, align, C_uint(bcx.ccx(), 1u64), dbloc);
-    let offset = And(bcx,
-                     Add(bcx, unaligned_offset, align_sub_1, dbloc),
-                     Neg(bcx, align, dbloc),
-                     dbloc);
+    dbloc.apply(bcx.fcx());
+    let align_sub_1 = bcx.sub(align, C_uint(bcx.ccx(), 1u64));
+    let offset = bcx.and(bcx.add(unaligned_offset, align_sub_1),
+                         bcx.neg(align));
 
-    debug!("struct_field_ptr: DST field offset: {}",
-           bcx.val_to_string(offset));
+    debug!("struct_field_ptr: DST field offset: {:?}", Value(offset));
 
     // Cast and adjust pointer
-    let byte_ptr = PointerCast(bcx, ptr_val, Type::i8p(bcx.ccx()));
-    let byte_ptr = GEP(bcx, byte_ptr, &[offset]);
+    let byte_ptr = bcx.pointercast(ptr_val, Type::i8p(bcx.ccx()));
+    let byte_ptr = bcx.gep(byte_ptr, &[offset]);
 
     // Finally, cast back to the type expected
     let ll_fty = type_of::in_memory_type_of(bcx.ccx(), fty);
-    debug!("struct_field_ptr: Field type is {}", ll_fty.to_string());
-    PointerCast(bcx, byte_ptr, ll_fty.ptr_to())
+    debug!("struct_field_ptr: Field type is {:?}", ll_fty);
+    bcx.pointercast(byte_ptr, ll_fty.ptr_to())
 }
 
 pub fn fold_variants<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
@@ -1283,14 +1293,15 @@ pub fn trans_drop_flag_ptr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             let scratch = unpack_datum!(bcx, datum::lvalue_scratch_datum(
                 bcx, tcx.dtor_type(), "drop_flag",
                 InitAlloca::Uninit("drop flag itself has no dtor"),
-                cleanup::CustomScope(custom_cleanup_scope), (), |_, bcx, _| {
+                cleanup::CustomScope(custom_cleanup_scope), |bcx, _| {
                     debug!("no-op populate call for trans_drop_flag_ptr on dtor_type={:?}",
                            tcx.dtor_type());
                     bcx
                 }
             ));
             bcx = fold_variants(bcx, r, val, |variant_cx, st, value| {
-                let ptr = struct_field_ptr(variant_cx, st, MaybeSizedValue::sized(value),
+                let ptr = struct_field_ptr(&variant_cx.build(), st,
+                                           MaybeSizedValue::sized(value),
                                            (st.fields.len() - 1), false);
                 datum::Datum::new(ptr, ptr_ty, datum::Lvalue::new("adt::trans_drop_flag_ptr"))
                     .store_to(variant_cx, scratch.val)
@@ -1442,7 +1453,7 @@ fn padding(ccx: &CrateContext, size: u64) -> ValueRef {
 fn roundup(x: u64, a: u32) -> u64 { let a = a as u64; ((x + (a - 1)) / a) * a }
 
 /// Get the discriminant of a constant value.
-pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef) -> Disr {
+pub fn const_get_discrim(r: &Repr, val: ValueRef) -> Disr {
     match *r {
         CEnum(ity, _, _) => {
             match ity {
@@ -1452,13 +1463,13 @@ pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef) -> Disr {
         }
         General(ity, _, _) => {
             match ity {
-                attr::SignedInt(..) => Disr(const_to_int(const_get_elt(ccx, val, &[0])) as u64),
-                attr::UnsignedInt(..) => Disr(const_to_uint(const_get_elt(ccx, val, &[0])))
+                attr::SignedInt(..) => Disr(const_to_int(const_get_elt(val, &[0])) as u64),
+                attr::UnsignedInt(..) => Disr(const_to_uint(const_get_elt(val, &[0])))
             }
         }
         Univariant(..) => Disr(0),
         RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => {
-            ccx.sess().bug("const discrim access of non c-like enum")
+            unreachable!("const discrim access of non c-like enum")
         }
     }
 }
@@ -1472,25 +1483,25 @@ pub fn const_get_field(ccx: &CrateContext, r: &Repr, val: ValueRef,
                        _discr: Disr, ix: usize) -> ValueRef {
     match *r {
         CEnum(..) => ccx.sess().bug("element access in C-like enum const"),
-        Univariant(..) => const_struct_field(ccx, val, ix),
-        General(..) => const_struct_field(ccx, val, ix + 1),
+        Univariant(..) => const_struct_field(val, ix),
+        General(..) => const_struct_field(val, ix + 1),
         RawNullablePointer { .. } => {
             assert_eq!(ix, 0);
             val
         },
-        StructWrappedNullablePointer{ .. } => const_struct_field(ccx, val, ix)
+        StructWrappedNullablePointer{ .. } => const_struct_field(val, ix)
     }
 }
 
 /// Extract field of struct-like const, skipping our alignment padding.
-fn const_struct_field(ccx: &CrateContext, val: ValueRef, ix: usize) -> ValueRef {
+fn const_struct_field(val: ValueRef, ix: usize) -> ValueRef {
     // Get the ix-th non-undef element of the struct.
     let mut real_ix = 0; // actual position in the struct
     let mut ix = ix; // logical index relative to real_ix
     let mut field;
     loop {
         loop {
-            field = const_get_elt(ccx, val, &[real_ix]);
+            field = const_get_elt(val, &[real_ix]);
             if !is_undef(field) {
                 break;
             }
diff --git a/src/librustc_trans/trans/asm.rs b/src/librustc_trans/trans/asm.rs
index 98e9a1c98ad..4d0c55131f7 100644
--- a/src/librustc_trans/trans/asm.rs
+++ b/src/librustc_trans/trans/asm.rs
@@ -10,13 +10,11 @@
 
 //! # Translation of inline assembly.
 
-use llvm;
+use llvm::{self, ValueRef};
+use trans::base;
 use trans::build::*;
-use trans::callee;
 use trans::common::*;
-use trans::cleanup;
-use trans::cleanup::CleanupMethods;
-use trans::expr;
+use trans::datum::{Datum, Lvalue};
 use trans::type_of;
 use trans::type_::Type;
 
@@ -26,64 +24,35 @@ use syntax::ast::AsmDialect;
 use libc::{c_uint, c_char};
 
 // Take an inline assembly expression and splat it out via LLVM
-pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm)
-                                    -> Block<'blk, 'tcx> {
-    let fcx = bcx.fcx;
-    let mut bcx = bcx;
-    let mut constraints = Vec::new();
-    let mut output_types = Vec::new();
-
-    let temp_scope = fcx.push_custom_cleanup_scope();
-
-    let mut ext_inputs = Vec::new();
-    let mut ext_constraints = Vec::new();
+pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                                    ia: &ast::InlineAsm,
+                                    outputs: Vec<Datum<'tcx, Lvalue>>,
+                                    mut inputs: Vec<ValueRef>) {
+    let mut ext_constraints = vec![];
+    let mut output_types = vec![];
 
     // Prepare the output operands
-    let mut outputs = Vec::new();
-    let mut inputs = Vec::new();
-    for (i, out) in ia.outputs.iter().enumerate() {
-        constraints.push(out.constraint.clone());
-
-        let out_datum = unpack_datum!(bcx, expr::trans(bcx, &out.expr));
+    let mut indirect_outputs = vec![];
+    for (i, (out, out_datum)) in ia.outputs.iter().zip(&outputs).enumerate() {
+        let val = if out.is_rw || out.is_indirect {
+            Some(base::load_ty(bcx, out_datum.val, out_datum.ty))
+        } else {
+            None
+        };
+        if out.is_rw {
+            inputs.push(val.unwrap());
+            ext_constraints.push(i.to_string());
+        }
         if out.is_indirect {
-            bcx = callee::trans_arg_datum(bcx,
-                                          expr_ty(bcx, &out.expr),
-                                          out_datum,
-                                          cleanup::CustomScope(temp_scope),
-                                          &mut inputs);
-            if out.is_rw {
-                ext_inputs.push(*inputs.last().unwrap());
-                ext_constraints.push(i.to_string());
-            }
+            indirect_outputs.push(val.unwrap());
         } else {
             output_types.push(type_of::type_of(bcx.ccx(), out_datum.ty));
-            outputs.push(out_datum.val);
-            if out.is_rw {
-                bcx = callee::trans_arg_datum(bcx,
-                                              expr_ty(bcx, &out.expr),
-                                              out_datum,
-                                              cleanup::CustomScope(temp_scope),
-                                              &mut ext_inputs);
-                ext_constraints.push(i.to_string());
-            }
         }
     }
-
-    // Now the input operands
-    for &(ref c, ref input) in &ia.inputs {
-        constraints.push((*c).clone());
-
-        let in_datum = unpack_datum!(bcx, expr::trans(bcx, &input));
-        bcx = callee::trans_arg_datum(bcx,
-                                    expr_ty(bcx, &input),
-                                    in_datum,
-                                    cleanup::CustomScope(temp_scope),
-                                    &mut inputs);
+    if !indirect_outputs.is_empty() {
+        indirect_outputs.extend_from_slice(&inputs);
+        inputs = indirect_outputs;
     }
-    inputs.extend_from_slice(&ext_inputs[..]);
-
-    // no failure occurred preparing operands, no need to cleanup
-    fcx.pop_custom_cleanup_scope(temp_scope);
 
     let clobbers = ia.clobbers.iter()
                               .map(|s| format!("~{{{}}}", &s));
@@ -95,19 +64,18 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm)
         _                => Vec::new()
     };
 
-    let all_constraints= constraints.iter()
-                                    .map(|s| s.to_string())
-                                    .chain(ext_constraints)
-                                    .chain(clobbers)
-                                    .chain(arch_clobbers.iter()
-                                               .map(|s| s.to_string()))
-                                    .collect::<Vec<String>>()
-                                    .join(",");
+    let all_constraints =
+        ia.outputs.iter().map(|out| out.constraint.to_string())
+          .chain(ia.inputs.iter().map(|s| s.to_string()))
+          .chain(ext_constraints)
+          .chain(clobbers)
+          .chain(arch_clobbers.iter().map(|s| s.to_string()))
+          .collect::<Vec<String>>().join(",");
 
     debug!("Asm Constraints: {}", &all_constraints[..]);
 
     // Depending on how many outputs we have, the return type is different
-    let num_outputs = outputs.len();
+    let num_outputs = output_types.len();
     let output_type = match num_outputs {
         0 => Type::void(bcx.ccx()),
         1 => output_types[0],
@@ -131,13 +99,10 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm)
                           dialect);
 
     // Again, based on how many outputs we have
-    if num_outputs == 1 {
-        Store(bcx, r, outputs[0]);
-    } else {
-        for (i, o) in outputs.iter().enumerate() {
-            let v = ExtractValue(bcx, r, i);
-            Store(bcx, v, *o);
-        }
+    let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect);
+    for (i, (_, datum)) in outputs.enumerate() {
+        let v = if num_outputs == 1 { r } else { ExtractValue(bcx, r, i) };
+        Store(bcx, v, datum.val);
     }
 
     // Store expn_id in a metadata node so we can map LLVM errors
@@ -152,7 +117,4 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm)
         llvm::LLVMSetMetadata(r, kind,
             llvm::LLVMMDNodeInContext(bcx.ccx().llcx(), &val, 1));
     }
-
-    return bcx;
-
 }
diff --git a/src/librustc_trans/trans/attributes.rs b/src/librustc_trans/trans/attributes.rs
index d93d32f8e0d..99dc3ade823 100644
--- a/src/librustc_trans/trans/attributes.rs
+++ b/src/librustc_trans/trans/attributes.rs
@@ -10,20 +10,11 @@
 //! Set and unset common attributes on LLVM values.
 
 use libc::{c_uint, c_ulonglong};
-use llvm::{self, ValueRef, AttrHelper};
-use middle::ty;
-use middle::infer;
-use middle::traits::ProjectionMode;
+use llvm::{self, ValueRef};
 use session::config::NoDebugInfo;
-use syntax::abi::Abi;
 pub use syntax::attr::InlineAttr;
 use syntax::ast;
-use rustc_front::hir;
-use trans::base;
-use trans::common;
 use trans::context::CrateContext;
-use trans::machine;
-use trans::type_of;
 
 /// Mark LLVM function to use provided inline heuristic.
 #[inline]
@@ -112,199 +103,13 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe
 
     for attr in attrs {
         if attr.check_name("cold") {
-            unsafe {
-                llvm::LLVMAddFunctionAttribute(llfn,
-                                               llvm::FunctionIndex as c_uint,
-                                               llvm::ColdAttribute as u64)
-            }
+            llvm::Attributes::default().set(llvm::Attribute::Cold)
+                .apply_llfn(llvm::FunctionIndex as usize, llfn)
         } else if attr.check_name("allocator") {
-            llvm::Attribute::NoAlias.apply_llfn(llvm::ReturnIndex as c_uint, llfn);
+            llvm::Attributes::default().set(llvm::Attribute::NoAlias)
+                .apply_llfn(llvm::ReturnIndex as usize, llfn)
         } else if attr.check_name("unwind") {
             unwind(llfn, true);
         }
     }
 }
-
-/// Composite function which converts function type into LLVM attributes for the function.
-pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx>)
-                              -> llvm::AttrBuilder {
-    use middle::ty::{BrAnon, ReLateBound};
-
-    let function_type;
-    let (fn_sig, abi, env_ty) = match fn_type.sty {
-        ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => (&f.sig, f.abi, None),
-        ty::TyClosure(closure_did, ref substs) => {
-            let infcx = infer::normalizing_infer_ctxt(ccx.tcx(),
-                                                      &ccx.tcx().tables,
-                                                      ProjectionMode::Any);
-            function_type = infcx.closure_type(closure_did, substs);
-            let self_type = base::self_type_for_closure(ccx, closure_did, fn_type);
-            (&function_type.sig, Abi::RustCall, Some(self_type))
-        }
-        _ => ccx.sess().bug("expected closure or function.")
-    };
-
-    let fn_sig = ccx.tcx().erase_late_bound_regions(fn_sig);
-    let fn_sig = infer::normalize_associated_type(ccx.tcx(), &fn_sig);
-
-    let mut attrs = llvm::AttrBuilder::new();
-    let ret_ty = fn_sig.output;
-
-    // These have an odd calling convention, so we need to manually
-    // unpack the input ty's
-    let input_tys = match fn_type.sty {
-        ty::TyClosure(..) => {
-            assert!(abi == Abi::RustCall);
-
-            match fn_sig.inputs[0].sty {
-                ty::TyTuple(ref inputs) => {
-                    let mut full_inputs = vec![env_ty.expect("Missing closure environment")];
-                    full_inputs.extend_from_slice(inputs);
-                    full_inputs
-                }
-                _ => ccx.sess().bug("expected tuple'd inputs")
-            }
-        },
-        ty::TyFnDef(..) | ty::TyFnPtr(_) if abi == Abi::RustCall => {
-            let mut inputs = vec![fn_sig.inputs[0]];
-
-            match fn_sig.inputs[1].sty {
-                ty::TyTuple(ref t_in) => {
-                    inputs.extend_from_slice(&t_in[..]);
-                    inputs
-                }
-                _ => ccx.sess().bug("expected tuple'd inputs")
-            }
-        }
-        _ => fn_sig.inputs.clone()
-    };
-
-    // Index 0 is the return value of the llvm func, so we start at 1
-    let mut idx = 1;
-    if let ty::FnConverging(ret_ty) = ret_ty {
-        // A function pointer is called without the declaration
-        // available, so we have to apply any attributes with ABI
-        // implications directly to the call instruction. Right now,
-        // the only attribute we need to worry about is `sret`.
-        if type_of::return_uses_outptr(ccx, ret_ty) {
-            let llret_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, ret_ty));
-
-            // The outptr can be noalias and nocapture because it's entirely
-            // invisible to the program. We also know it's nonnull as well
-            // as how many bytes we can dereference
-            attrs.arg(1, llvm::Attribute::StructRet)
-                 .arg(1, llvm::Attribute::NoAlias)
-                 .arg(1, llvm::Attribute::NoCapture)
-                 .arg(1, llvm::DereferenceableAttribute(llret_sz));
-
-            // Add one more since there's an outptr
-            idx += 1;
-        } else {
-            // The `noalias` attribute on the return value is useful to a
-            // function ptr caller.
-            match ret_ty.sty {
-                // `Box` pointer return values never alias because ownership
-                // is transferred
-                ty::TyBox(it) if common::type_is_sized(ccx.tcx(), it) => {
-                    attrs.ret(llvm::Attribute::NoAlias);
-                }
-                _ => {}
-            }
-
-            // We can also mark the return value as `dereferenceable` in certain cases
-            match ret_ty.sty {
-                // These are not really pointers but pairs, (pointer, len)
-                ty::TyRef(_, ty::TypeAndMut { ty: inner, .. })
-                | ty::TyBox(inner) if common::type_is_sized(ccx.tcx(), inner) => {
-                    let llret_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, inner));
-                    attrs.ret(llvm::DereferenceableAttribute(llret_sz));
-                }
-                _ => {}
-            }
-
-            if let ty::TyBool = ret_ty.sty {
-                attrs.ret(llvm::Attribute::ZExt);
-            }
-        }
-    }
-
-    for &t in input_tys.iter() {
-        match t.sty {
-            _ if type_of::arg_is_indirect(ccx, t) => {
-                let llarg_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, t));
-
-                // For non-immediate arguments the callee gets its own copy of
-                // the value on the stack, so there are no aliases. It's also
-                // program-invisible so can't possibly capture
-                attrs.arg(idx, llvm::Attribute::NoAlias)
-                     .arg(idx, llvm::Attribute::NoCapture)
-                     .arg(idx, llvm::DereferenceableAttribute(llarg_sz));
-            }
-
-            ty::TyBool => {
-                attrs.arg(idx, llvm::Attribute::ZExt);
-            }
-
-            // `Box` pointer parameters never alias because ownership is transferred
-            ty::TyBox(inner) => {
-                attrs.arg(idx, llvm::Attribute::NoAlias);
-
-                if common::type_is_sized(ccx.tcx(), inner) {
-                    let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, inner));
-                    attrs.arg(idx, llvm::DereferenceableAttribute(llsz));
-                } else {
-                    attrs.arg(idx, llvm::NonNullAttribute);
-                    if inner.is_trait() {
-                        attrs.arg(idx + 1, llvm::NonNullAttribute);
-                    }
-                }
-            }
-
-            ty::TyRef(b, mt) => {
-                // `&mut` pointer parameters never alias other parameters, or mutable global data
-                //
-                // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as
-                // both `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely
-                // on memory dependencies rather than pointer equality
-                let interior_unsafe = mt.ty.type_contents(ccx.tcx()).interior_unsafe();
-
-                if mt.mutbl != hir::MutMutable && !interior_unsafe {
-                    attrs.arg(idx, llvm::Attribute::NoAlias);
-                }
-
-                if mt.mutbl == hir::MutImmutable && !interior_unsafe {
-                    attrs.arg(idx, llvm::Attribute::ReadOnly);
-                }
-
-                // & pointer parameters are also never null and for sized types we also know
-                // exactly how many bytes we can dereference
-                if common::type_is_sized(ccx.tcx(), mt.ty) {
-                    let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, mt.ty));
-                    attrs.arg(idx, llvm::DereferenceableAttribute(llsz));
-                } else {
-                    attrs.arg(idx, llvm::NonNullAttribute);
-                    if mt.ty.is_trait() {
-                        attrs.arg(idx + 1, llvm::NonNullAttribute);
-                    }
-                }
-
-                // When a reference in an argument has no named lifetime, it's
-                // impossible for that reference to escape this function
-                // (returned or stored beyond the call by a closure).
-                if let ReLateBound(_, BrAnon(_)) = *b {
-                    attrs.arg(idx, llvm::Attribute::NoCapture);
-                }
-            }
-
-            _ => ()
-        }
-
-        if common::type_is_fat_ptr(ccx.tcx(), t) {
-            idx += 2;
-        } else {
-            idx += 1;
-        }
-    }
-
-    attrs
-}
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 5088dabfbe7..93ca306f0b0 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -10,8 +10,8 @@
 //! Translate the completed AST to the LLVM IR.
 //!
 //! Some functions here, such as trans_block and trans_expr, return a value --
-//! the result of the translation to LLVM -- while others, such as trans_fn,
-//! trans_impl, and trans_item, are called only for the side effect of adding a
+//! the result of the translation to LLVM -- while others, such as trans_fn
+//! and trans_item, are called only for the side effect of adding a
 //! particular definition to the LLVM IR output we're producing.
 //!
 //! Hopefully useful general knowledge about trans:
@@ -30,7 +30,7 @@ use super::CrateTranslation;
 use super::ModuleTranslation;
 
 use back::link::mangle_exported_name;
-use back::{link, abi};
+use back::link;
 use lint;
 use llvm::{BasicBlockRef, Linkage, ValueRef, Vector, get_param};
 use llvm;
@@ -52,12 +52,13 @@ use rustc::mir::mir_map::MirMap;
 use session::config::{self, NoDebugInfo, FullDebugInfo};
 use session::Session;
 use trans::_match;
+use trans::abi::{self, Abi, FnType};
 use trans::adt;
 use trans::assert_dep_graph;
 use trans::attributes;
 use trans::build::*;
 use trans::builder::{Builder, noname};
-use trans::callee;
+use trans::callee::{Callee, CallArgs, ArgExprs, ArgVals};
 use trans::cleanup::{self, CleanupMethods, DropHint};
 use trans::closure;
 use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_uint, C_integral};
@@ -65,7 +66,7 @@ use trans::collector::{self, TransItem, TransItemState, TransItemCollectionMode}
 use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef};
 use trans::common::{CrateContext, DropFlagHintsMap, Field, FunctionContext};
 use trans::common::{Result, NodeIdAndSpan, VariantInfo};
-use trans::common::{node_id_type, return_type_is_void, fulfill_obligation};
+use trans::common::{node_id_type, fulfill_obligation};
 use trans::common::{type_is_immediate, type_is_zero_size, val_ty};
 use trans::common;
 use trans::consts;
@@ -75,14 +76,14 @@ use trans::datum;
 use trans::debuginfo::{self, DebugLoc, ToDebugLoc};
 use trans::declare;
 use trans::expr;
-use trans::foreign;
 use trans::glue;
+use trans::inline;
 use trans::intrinsic;
 use trans::machine;
-use trans::machine::{llsize_of, llsize_of_real};
+use trans::machine::{llalign_of_min, llsize_of, llsize_of_real};
 use trans::meth;
 use trans::mir;
-use trans::monomorphize;
+use trans::monomorphize::{self, Instance};
 use trans::tvec;
 use trans::type_::Type;
 use trans::type_of;
@@ -100,7 +101,6 @@ use std::cell::{Cell, RefCell};
 use std::collections::{HashMap, HashSet};
 use std::str;
 use std::{i8, i16, i32, i64};
-use syntax::abi::Abi;
 use syntax::codemap::{Span, DUMMY_SP};
 use syntax::parse::token::InternedString;
 use syntax::attr::AttrMetaMethods;
@@ -192,72 +192,10 @@ impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> {
     }
 }
 
-fn get_extern_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                fn_ty: Ty<'tcx>,
-                                name: &str,
-                                attrs: &[ast::Attribute])
-                                -> ValueRef {
-    if let Some(n) = ccx.externs().borrow().get(name) {
-        return *n;
-    }
-
-    let f = declare::declare_rust_fn(ccx, name, fn_ty);
-    attributes::from_fn_attrs(ccx, &attrs, f);
-
-    ccx.externs().borrow_mut().insert(name.to_string(), f);
-    f
-}
-
-pub fn self_type_for_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                       closure_id: DefId,
-                                       fn_ty: Ty<'tcx>)
-                                       -> Ty<'tcx> {
-    let closure_kind = ccx.tcx().closure_kind(closure_id);
-    match closure_kind {
-        ty::ClosureKind::Fn => {
-            ccx.tcx().mk_imm_ref(ccx.tcx().mk_region(ty::ReStatic), fn_ty)
-        }
-        ty::ClosureKind::FnMut => {
-            ccx.tcx().mk_mut_ref(ccx.tcx().mk_region(ty::ReStatic), fn_ty)
-        }
-        ty::ClosureKind::FnOnce => fn_ty,
-    }
-}
-
 pub fn kind_for_closure(ccx: &CrateContext, closure_id: DefId) -> ty::ClosureKind {
     *ccx.tcx().tables.borrow().closure_kinds.get(&closure_id).unwrap()
 }
 
-pub fn get_extern_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                  did: DefId,
-                                  t: Ty<'tcx>)
-                                  -> ValueRef {
-    let name = ccx.sess().cstore.item_symbol(did);
-    let ty = type_of(ccx, t);
-    if let Some(n) = ccx.externs().borrow_mut().get(&name) {
-        return *n;
-    }
-    // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow?
-    // FIXME(nagisa): investigate whether it can be changed into define_global
-    let c = declare::declare_global(ccx, &name[..], ty);
-    // Thread-local statics in some other crate need to *always* be linked
-    // against in a thread-local fashion, so we need to be sure to apply the
-    // thread-local attribute locally if it was present remotely. If we
-    // don't do this then linker errors can be generated where the linker
-    // complains that one object files has a thread local version of the
-    // symbol and another one doesn't.
-    for attr in ccx.tcx().get_attrs(did).iter() {
-        if attr.check_name("thread_local") {
-            llvm::set_thread_local(c, true);
-        }
-    }
-    if ccx.use_dll_storage_attrs() {
-        llvm::SetDLLStorageClass(c, llvm::DLLImportStorageClass);
-    }
-    ccx.externs().borrow_mut().insert(name.to_string(), c);
-    return c;
-}
-
 fn require_alloc_fn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, info_ty: Ty<'tcx>, it: LangItem) -> DefId {
     match bcx.tcx().lang_items.require(it) {
         Ok(id) => id,
@@ -280,11 +218,9 @@ pub fn malloc_raw_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let _icx = push_ctxt("malloc_raw_exchange");
 
     // Allocate space:
-    let r = callee::trans_lang_call(bcx,
-                                    require_alloc_fn(bcx, info_ty, ExchangeMallocFnLangItem),
-                                    &[size, align],
-                                    None,
-                                    debug_loc);
+    let def_id = require_alloc_fn(bcx, info_ty, ExchangeMallocFnLangItem);
+    let r = Callee::def(bcx.ccx(), def_id, bcx.tcx().mk_substs(Substs::empty()))
+        .call(bcx, debug_loc, ArgVals(&[size, align]), None);
 
     Result::new(r.bcx, PointerCast(r.bcx, r.val, llty_ptr))
 }
@@ -897,38 +833,9 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
     }
 }
 
-pub fn get_extern_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                               def_id: DefId)
-                               -> datum::Datum<'tcx, datum::Rvalue> {
-    let name = ccx.sess().cstore.item_symbol(def_id);
-    let attrs = ccx.sess().cstore.item_attrs(def_id);
-    let ty = ccx.tcx().lookup_item_type(def_id).ty;
-    match ty.sty {
-        ty::TyFnDef(_, _, fty) => {
-            let abi = fty.abi;
-            let fty = infer::normalize_associated_type(ccx.tcx(), fty);
-            let ty = ccx.tcx().mk_fn_ptr(fty);
-            let llfn = match ccx.sess().target.target.adjust_abi(abi) {
-                Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
-                    ccx.sess().bug("unexpected intrinsic in get_extern_fn")
-                }
-                Abi::Rust | Abi::RustCall => {
-                    get_extern_rust_fn(ccx, ty, &name, &attrs)
-                }
-                _ => {
-                    foreign::register_foreign_item_fn(ccx, abi, ty, &name, &attrs)
-                }
-            };
-            datum::immediate_rvalue(llfn, ty)
-        }
-        _ => unreachable!("get_extern_fn: expected fn item type, found {}", ty)
-    }
-}
-
 pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                           llfn: ValueRef,
                           llargs: &[ValueRef],
-                          fn_ty: Ty<'tcx>,
                           debug_loc: DebugLoc)
                           -> (ValueRef, Block<'blk, 'tcx>) {
     let _icx = push_ctxt("invoke_");
@@ -936,8 +843,6 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         return (C_null(Type::i8(bcx.ccx())), bcx);
     }
 
-    let attributes = attributes::from_fn_type(bcx.ccx(), fn_ty);
-
     match bcx.opt_node_id {
         None => {
             debug!("invoke at ???");
@@ -948,9 +853,9 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     }
 
     if need_invoke(bcx) {
-        debug!("invoking {} at {:?}", bcx.val_to_string(llfn), bcx.llbb);
+        debug!("invoking {:?} at {:?}", Value(llfn), bcx.llbb);
         for &llarg in llargs {
-            debug!("arg: {}", bcx.val_to_string(llarg));
+            debug!("arg: {:?}", Value(llarg));
         }
         let normal_bcx = bcx.fcx.new_temp_block("normal-return");
         let landing_pad = bcx.fcx.get_landing_pad();
@@ -960,16 +865,15 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                               &llargs[..],
                               normal_bcx.llbb,
                               landing_pad,
-                              Some(attributes),
                               debug_loc);
         return (llresult, normal_bcx);
     } else {
-        debug!("calling {} at {:?}", bcx.val_to_string(llfn), bcx.llbb);
+        debug!("calling {:?} at {:?}", Value(llfn), bcx.llbb);
         for &llarg in llargs {
-            debug!("arg: {}", bcx.val_to_string(llarg));
+            debug!("arg: {:?}", Value(llarg));
         }
 
-        let llresult = Call(bcx, llfn, &llargs[..], Some(attributes), debug_loc);
+        let llresult = Call(bcx, llfn, &llargs[..], debug_loc);
         return (llresult, bcx);
     }
 }
@@ -1007,19 +911,16 @@ pub fn load_if_immediate<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, t: Ty<'
 /// differs from the type used for SSA values. Also handles various special cases where the type
 /// gives us better information about what we are loading.
 pub fn load_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, ptr: ValueRef, t: Ty<'tcx>) -> ValueRef {
-    if cx.unreachable.get() || type_is_zero_size(cx.ccx(), t) {
+    if cx.unreachable.get() {
         return C_undef(type_of::type_of(cx.ccx(), t));
     }
+    load_ty_builder(&B(cx), ptr, t)
+}
 
-    let ptr = to_arg_ty_ptr(cx, ptr, t);
-    let align = type_of::align_of(cx.ccx(), t);
-
-    if type_is_immediate(cx.ccx(), t) && type_of::type_of(cx.ccx(), t).is_aggregate() {
-        let load = Load(cx, ptr);
-        unsafe {
-            llvm::LLVMSetAlignment(load, align);
-        }
-        return load;
+pub fn load_ty_builder<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, t: Ty<'tcx>) -> ValueRef {
+    let ccx = b.ccx;
+    if type_is_zero_size(ccx, t) {
+        return C_undef(type_of::type_of(ccx, t));
     }
 
     unsafe {
@@ -1027,28 +928,26 @@ pub fn load_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, ptr: ValueRef, t: Ty<'tcx>) ->
         if !global.is_null() && llvm::LLVMIsGlobalConstant(global) == llvm::True {
             let val = llvm::LLVMGetInitializer(global);
             if !val.is_null() {
-                return to_arg_ty(cx, val, t);
+                if t.is_bool() {
+                    return llvm::LLVMConstTrunc(val, Type::i1(ccx).to_ref());
+                }
+                return val;
             }
         }
     }
 
-    let val = if t.is_bool() {
-        LoadRangeAssert(cx, ptr, 0, 2, llvm::False)
+    if t.is_bool() {
+        b.trunc(b.load_range_assert(ptr, 0, 2, llvm::False), Type::i1(ccx))
     } else if t.is_char() {
         // a char is a Unicode codepoint, and so takes values from 0
         // to 0x10FFFF inclusive only.
-        LoadRangeAssert(cx, ptr, 0, 0x10FFFF + 1, llvm::False)
-    } else if (t.is_region_ptr() || t.is_unique()) && !common::type_is_fat_ptr(cx.tcx(), t) {
-        LoadNonNull(cx, ptr)
+        b.load_range_assert(ptr, 0, 0x10FFFF + 1, llvm::False)
+    } else if (t.is_region_ptr() || t.is_unique()) &&
+              !common::type_is_fat_ptr(ccx.tcx(), t) {
+        b.load_nonnull(ptr)
     } else {
-        Load(cx, ptr)
-    };
-
-    unsafe {
-        llvm::LLVMSetAlignment(val, align);
+        b.load(ptr)
     }
-
-    to_arg_ty(cx, val, t)
 }
 
 /// Helper for storing values in memory. Does the necessary conversion if the in-memory type
@@ -1058,10 +957,7 @@ pub fn store_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, dst: ValueRef, t
         return;
     }
 
-    debug!("store_ty: {} : {:?} <- {}",
-           cx.val_to_string(dst),
-           t,
-           cx.val_to_string(v));
+    debug!("store_ty: {:?} : {:?} <- {:?}", Value(dst), t, Value(v));
 
     if common::type_is_fat_ptr(cx.tcx(), t) {
         Store(cx,
@@ -1071,10 +967,7 @@ pub fn store_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, dst: ValueRef, t
               ExtractValue(cx, v, abi::FAT_PTR_EXTRA),
               expr::get_meta(cx, dst));
     } else {
-        let store = Store(cx, from_arg_ty(cx, v, t), to_arg_ty_ptr(cx, dst, t));
-        unsafe {
-            llvm::LLVMSetAlignment(store, type_of::align_of(cx.ccx(), t));
-        }
+        Store(cx, from_immediate(cx, v), dst);
     }
 }
 
@@ -1097,15 +990,15 @@ pub fn load_fat_ptr<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
      Load(cx, expr::get_meta(cx, src)))
 }
 
-pub fn from_arg_ty(bcx: Block, val: ValueRef, ty: Ty) -> ValueRef {
-    if ty.is_bool() {
+pub fn from_immediate(bcx: Block, val: ValueRef) -> ValueRef {
+    if val_ty(val) == Type::i1(bcx.ccx()) {
         ZExt(bcx, val, Type::i8(bcx.ccx()))
     } else {
         val
     }
 }
 
-pub fn to_arg_ty(bcx: Block, val: ValueRef, ty: Ty) -> ValueRef {
+pub fn to_immediate(bcx: Block, val: ValueRef, ty: Ty) -> ValueRef {
     if ty.is_bool() {
         Trunc(bcx, val, Type::i1(bcx.ccx()))
     } else {
@@ -1113,17 +1006,6 @@ pub fn to_arg_ty(bcx: Block, val: ValueRef, ty: Ty) -> ValueRef {
     }
 }
 
-pub fn to_arg_ty_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ptr: ValueRef, ty: Ty<'tcx>) -> ValueRef {
-    if type_is_immediate(bcx.ccx(), ty) && type_of::type_of(bcx.ccx(), ty).is_aggregate() {
-        // We want to pass small aggregates as immediate values, but using an aggregate LLVM type
-        // for this leads to bad optimizations, so its arg type is an appropriately sized integer
-        // and we have to convert it
-        BitCast(bcx, ptr, type_of::arg_type_of(bcx.ccx(), ty).ptr_to())
-    } else {
-        ptr
-    }
-}
-
 pub fn init_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, local: &hir::Local) -> Block<'blk, 'tcx> {
     debug!("init_local(bcx={}, local.id={})", bcx.to_str(), local.id);
     let _indenter = indenter();
@@ -1200,7 +1082,6 @@ pub fn call_lifetime_start(cx: Block, ptr: ValueRef) {
         Call(cx,
              lifetime_start,
              &[C_u64(ccx, size), ptr],
-             None,
              DebugLoc::None);
     })
 }
@@ -1211,7 +1092,6 @@ pub fn call_lifetime_end(cx: Block, ptr: ValueRef) {
         Call(cx,
              lifetime_end,
              &[C_u64(ccx, size), ptr],
-             None,
              DebugLoc::None);
     })
 }
@@ -1222,36 +1102,34 @@ pub fn trans_unwind_resume(bcx: Block, lpval: ValueRef) {
         Resume(bcx, lpval);
     } else {
         let exc_ptr = ExtractValue(bcx, lpval, 0);
-        let llunwresume = bcx.fcx.eh_unwind_resume();
-        Call(bcx, llunwresume, &[exc_ptr], None, DebugLoc::None);
-        Unreachable(bcx);
+        bcx.fcx.eh_unwind_resume()
+            .call(bcx, DebugLoc::None, ArgVals(&[exc_ptr]), None);
     }
 }
 
-
-pub fn call_memcpy(cx: Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef, align: u32) {
+pub fn call_memcpy<'bcx, 'tcx>(b: &Builder<'bcx, 'tcx>,
+                               dst: ValueRef,
+                               src: ValueRef,
+                               n_bytes: ValueRef,
+                               align: u32) {
     let _icx = push_ctxt("call_memcpy");
-    let ccx = cx.ccx();
+    let ccx = b.ccx;
     let ptr_width = &ccx.sess().target.target.target_pointer_width[..];
     let key = format!("llvm.memcpy.p0i8.p0i8.i{}", ptr_width);
     let memcpy = ccx.get_intrinsic(&key);
-    let src_ptr = PointerCast(cx, src, Type::i8p(ccx));
-    let dst_ptr = PointerCast(cx, dst, Type::i8p(ccx));
-    let size = IntCast(cx, n_bytes, ccx.int_type());
+    let src_ptr = b.pointercast(src, Type::i8p(ccx));
+    let dst_ptr = b.pointercast(dst, Type::i8p(ccx));
+    let size = b.intcast(n_bytes, ccx.int_type());
     let align = C_i32(ccx, align as i32);
     let volatile = C_bool(ccx, false);
-    Call(cx,
-         memcpy,
-         &[dst_ptr, src_ptr, size, align, volatile],
-         None,
-         DebugLoc::None);
+    b.call(memcpy, &[dst_ptr, src_ptr, size, align, volatile], None);
 }
 
 pub fn memcpy_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, dst: ValueRef, src: ValueRef, t: Ty<'tcx>) {
     let _icx = push_ctxt("memcpy_ty");
     let ccx = bcx.ccx();
 
-    if type_is_zero_size(ccx, t) {
+    if type_is_zero_size(ccx, t) || bcx.unreachable.get() {
         return;
     }
 
@@ -1259,7 +1137,7 @@ pub fn memcpy_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, dst: ValueRef, src: ValueRe
         let llty = type_of::type_of(ccx, t);
         let llsz = llsize_of(ccx, llty);
         let llalign = type_of::align_of(ccx, t);
-        call_memcpy(bcx, dst, src, llsz, llalign as u32);
+        call_memcpy(&B(bcx), dst, src, llsz, llalign as u32);
     } else if common::type_is_fat_ptr(bcx.tcx(), t) {
         let (data, extra) = load_fat_ptr(bcx, src, t);
         store_fat_ptr(bcx, data, extra, dst, t);
@@ -1313,7 +1191,7 @@ pub fn call_memset<'bcx, 'tcx>(b: &Builder<'bcx, 'tcx>,
     let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width);
     let llintrinsicfn = ccx.get_intrinsic(&intrinsic_key);
     let volatile = C_bool(ccx, volatile);
-    b.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None, None);
+    b.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None);
 }
 
 
@@ -1388,7 +1266,7 @@ pub fn alloca_dropped<'blk, 'tcx>(cx: Block<'blk, 'tcx>, ty: Ty<'tcx>, name: &st
     // Block, which we do not have for `alloca_insert_pt`).
     core_lifetime_emit(cx.ccx(), p, Lifetime::Start, |ccx, size, lifetime_start| {
         let ptr = b.pointercast(p, Type::i8p(ccx));
-        b.call(lifetime_start, &[C_u64(ccx, size), ptr], None, None);
+        b.call(lifetime_start, &[C_u64(ccx, size), ptr], None);
     });
     memfill(&b, p, ty, adt::DTOR_DONE);
     p
@@ -1412,41 +1290,6 @@ pub fn set_value_name(val: ValueRef, name: &str) {
     }
 }
 
-// Creates the alloca slot which holds the pointer to the slot for the final return value
-pub fn make_return_slot_pointer<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
-                                          output_type: Ty<'tcx>)
-                                          -> ValueRef {
-    let lloutputtype = type_of::type_of(fcx.ccx, output_type);
-
-    // We create an alloca to hold a pointer of type `output_type`
-    // which will hold the pointer to the right alloca which has the
-    // final ret value
-    if fcx.needs_ret_allocas {
-        // Let's create the stack slot
-        let slot = AllocaFcx(fcx, lloutputtype.ptr_to(), "llretslotptr");
-
-        // and if we're using an out pointer, then store that in our newly made slot
-        if type_of::return_uses_outptr(fcx.ccx, output_type) {
-            let outptr = get_param(fcx.llfn, 0);
-
-            let b = fcx.ccx.builder();
-            b.position_before(fcx.alloca_insert_pt.get().unwrap());
-            b.store(outptr, slot);
-        }
-
-        slot
-
-    // But if there are no nested returns, we skip the indirection and have a single
-    // retslot
-    } else {
-        if type_of::return_uses_outptr(fcx.ccx, output_type) {
-            get_param(fcx.llfn, 0)
-        } else {
-            AllocaFcx(fcx, lloutputtype, "sret_slot")
-        }
-    }
-}
-
 struct FindNestedReturn {
     found: bool,
 }
@@ -1553,517 +1396,477 @@ fn has_nested_returns(tcx: &TyCtxt, cfg: &cfg::CFG, blk_id: ast::NodeId) -> bool
     return false;
 }
 
-// NB: must keep 4 fns in sync:
-//
-//  - type_of_fn
-//  - create_datums_for_fn_args.
-//  - new_fn_ctxt
-//  - trans_args
-//
-// Be warned! You must call `init_function` before doing anything with the
-// returned function context.
-pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
-                             llfndecl: ValueRef,
-                             id: ast::NodeId,
-                             has_env: bool,
-                             output_type: ty::FnOutput<'tcx>,
-                             param_substs: &'tcx Substs<'tcx>,
-                             sp: Option<Span>,
-                             block_arena: &'a TypedArena<common::BlockS<'a, 'tcx>>)
-                             -> FunctionContext<'a, 'tcx> {
-    common::validate_substs(param_substs);
-
-    debug!("new_fn_ctxt(path={}, id={}, param_substs={:?})",
-           if id == !0 {
-               "".to_string()
-           } else {
-               ccx.tcx().map.path_to_string(id).to_string()
-           },
-           id,
-           param_substs);
-
-    let uses_outptr = match output_type {
-        ty::FnConverging(output_type) => {
-            let substd_output_type = monomorphize::apply_param_substs(ccx.tcx(),
-                                                                      param_substs,
-                                                                      &output_type);
-            type_of::return_uses_outptr(ccx, substd_output_type)
-        }
-        ty::FnDiverging => false,
-    };
-    let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl);
-    let (blk_id, cfg) = build_cfg(ccx.tcx(), id);
-    let nested_returns = if let Some(ref cfg) = cfg {
-        has_nested_returns(ccx.tcx(), cfg, blk_id)
-    } else {
-        false
-    };
+impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> {
+    /// Create a function context for the given function.
+    /// Beware that you must call `fcx.init` or `fcx.bind_args`
+    /// before doing anything with the returned function context.
+    pub fn new(ccx: &'blk CrateContext<'blk, 'tcx>,
+               llfndecl: ValueRef,
+               fn_ty: FnType,
+               def_id: Option<DefId>,
+               param_substs: &'tcx Substs<'tcx>,
+               block_arena: &'blk TypedArena<common::BlockS<'blk, 'tcx>>)
+               -> FunctionContext<'blk, 'tcx> {
+        common::validate_substs(param_substs);
+
+        let inlined_did = def_id.and_then(|def_id| inline::get_local_instance(ccx, def_id));
+        let inlined_id = inlined_did.and_then(|id| ccx.tcx().map.as_local_node_id(id));
+        let local_id = def_id.and_then(|id| ccx.tcx().map.as_local_node_id(id));
+
+        debug!("FunctionContext::new(path={}, def_id={:?}, param_substs={:?})",
+            inlined_id.map_or(String::new(), |id| {
+                ccx.tcx().map.path_to_string(id).to_string()
+            }),
+            def_id,
+            param_substs);
+
+        let debug_context = debuginfo::create_function_debug_context(ccx,
+            inlined_id.unwrap_or(ast::DUMMY_NODE_ID), param_substs, llfndecl);
+
+        let cfg = inlined_id.map(|id| build_cfg(ccx.tcx(), id));
+        let nested_returns = if let Some((blk_id, Some(ref cfg))) = cfg {
+            has_nested_returns(ccx.tcx(), cfg, blk_id)
+        } else {
+            false
+        };
 
-    let mir = ccx.mir_map().map.get(&id);
-
-    let mut fcx = FunctionContext {
-        mir: mir,
-        llfn: llfndecl,
-        llenv: None,
-        llretslotptr: Cell::new(None),
-        param_env: ccx.tcx().empty_parameter_environment(),
-        alloca_insert_pt: Cell::new(None),
-        llreturn: Cell::new(None),
-        needs_ret_allocas: nested_returns,
-        landingpad_alloca: Cell::new(None),
-        caller_expects_out_pointer: uses_outptr,
-        lllocals: RefCell::new(NodeMap()),
-        llupvars: RefCell::new(NodeMap()),
-        lldropflag_hints: RefCell::new(DropFlagHintsMap::new()),
-        id: id,
-        param_substs: param_substs,
-        span: sp,
-        block_arena: block_arena,
-        lpad_arena: TypedArena::new(),
-        ccx: ccx,
-        debug_context: debug_context,
-        scopes: RefCell::new(Vec::new()),
-        cfg: cfg,
-    };
+        let check_attrs = |attrs: &[ast::Attribute]| {
+            let default_to_mir = ccx.sess().opts.debugging_opts.orbit;
+            let invert = if default_to_mir { "rustc_no_mir" } else { "rustc_mir" };
+            default_to_mir ^ attrs.iter().any(|item| item.check_name(invert))
+        };
 
-    if has_env {
-        fcx.llenv = Some(get_param(fcx.llfn, fcx.env_arg_pos() as c_uint))
-    }
-
-    fcx
-}
-
-/// Performs setup on a newly created function, creating the entry scope block
-/// and allocating space for the return pointer.
-pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>,
-                               skip_retptr: bool,
-                               output: ty::FnOutput<'tcx>)
-                               -> Block<'a, 'tcx> {
-    let entry_bcx = fcx.new_temp_block("entry-block");
-
-    // Use a dummy instruction as the insertion point for all allocas.
-    // This is later removed in FunctionContext::cleanup.
-    fcx.alloca_insert_pt.set(Some(unsafe {
-        Load(entry_bcx, C_null(Type::i8p(fcx.ccx)));
-        llvm::LLVMGetFirstInstruction(entry_bcx.llbb)
-    }));
-
-    if let ty::FnConverging(output_type) = output {
-        // This shouldn't need to recompute the return type,
-        // as new_fn_ctxt did it already.
-        let substd_output_type = fcx.monomorphize(&output_type);
-        if !return_type_is_void(fcx.ccx, substd_output_type) {
-            // If the function returns nil/bot, there is no real return
-            // value, so do not set `llretslotptr`.
-            if !skip_retptr || fcx.caller_expects_out_pointer {
-                // Otherwise, we normally allocate the llretslotptr, unless we
-                // have been instructed to skip it for immediate return
-                // values.
-                fcx.llretslotptr.set(Some(make_return_slot_pointer(fcx, substd_output_type)));
-            }
+        let use_mir = if let Some(id) = local_id {
+            check_attrs(ccx.tcx().map.attrs(id))
+        } else if let Some(def_id) = def_id {
+            check_attrs(&ccx.sess().cstore.item_attrs(def_id))
+        } else {
+            check_attrs(&[])
+        };
+
+        let mir = if use_mir {
+            def_id.and_then(|id| ccx.get_mir(id))
+        } else {
+            None
+        };
+
+        FunctionContext {
+            needs_ret_allocas: nested_returns && mir.is_none(),
+            mir: mir,
+            llfn: llfndecl,
+            llretslotptr: Cell::new(None),
+            param_env: ccx.tcx().empty_parameter_environment(),
+            alloca_insert_pt: Cell::new(None),
+            llreturn: Cell::new(None),
+            landingpad_alloca: Cell::new(None),
+            lllocals: RefCell::new(NodeMap()),
+            llupvars: RefCell::new(NodeMap()),
+            lldropflag_hints: RefCell::new(DropFlagHintsMap::new()),
+            fn_ty: fn_ty,
+            param_substs: param_substs,
+            span: inlined_id.and_then(|id| ccx.tcx().map.opt_span(id)),
+            block_arena: block_arena,
+            lpad_arena: TypedArena::new(),
+            ccx: ccx,
+            debug_context: debug_context,
+            scopes: RefCell::new(Vec::new()),
+            cfg: cfg.and_then(|(_, cfg)| cfg)
+        }
+    }
+
+    /// Performs setup on a newly created function, creating the entry
+    /// scope block and allocating space for the return pointer.
+    pub fn init(&'blk self, skip_retptr: bool, fn_did: Option<DefId>)
+                -> Block<'blk, 'tcx> {
+        let entry_bcx = self.new_temp_block("entry-block");
+
+        // Use a dummy instruction as the insertion point for all allocas.
+        // This is later removed in FunctionContext::cleanup.
+        self.alloca_insert_pt.set(Some(unsafe {
+            Load(entry_bcx, C_null(Type::i8p(self.ccx)));
+            llvm::LLVMGetFirstInstruction(entry_bcx.llbb)
+        }));
+
+        if !self.fn_ty.ret.is_ignore() && !skip_retptr {
+            // We normally allocate the llretslotptr, unless we
+            // have been instructed to skip it for immediate return
+            // values, or there is nothing to return at all.
+
+            // We create an alloca to hold a pointer of type `ret.original_ty`
+            // which will hold the pointer to the right alloca which has the
+            // final ret value
+            let llty = self.fn_ty.ret.memory_ty(self.ccx);
+            let slot = if self.needs_ret_allocas {
+                // Let's create the stack slot
+                let slot = AllocaFcx(self, llty.ptr_to(), "llretslotptr");
+
+                // and if we're using an out pointer, then store that in our newly made slot
+                if self.fn_ty.ret.is_indirect() {
+                    let outptr = get_param(self.llfn, 0);
+
+                    let b = self.ccx.builder();
+                    b.position_before(self.alloca_insert_pt.get().unwrap());
+                    b.store(outptr, slot);
+                }
+
+                slot
+            } else {
+                // But if there are no nested returns, we skip the indirection
+                // and have a single retslot
+                if self.fn_ty.ret.is_indirect() {
+                    get_param(self.llfn, 0)
+                } else {
+                    AllocaFcx(self, llty, "sret_slot")
+                }
+            };
+
+            self.llretslotptr.set(Some(slot));
         }
-    }
 
-    // Create the drop-flag hints for every unfragmented path in the function.
-    let tcx = fcx.ccx.tcx();
-    let fn_did = tcx.map.local_def_id(fcx.id);
-    let tables = tcx.tables.borrow();
-    let mut hints = fcx.lldropflag_hints.borrow_mut();
-    let fragment_infos = tcx.fragment_infos.borrow();
+        // Create the drop-flag hints for every unfragmented path in the function.
+        let tcx = self.ccx.tcx();
+        let tables = tcx.tables.borrow();
+        let mut hints = self.lldropflag_hints.borrow_mut();
+        let fragment_infos = tcx.fragment_infos.borrow();
 
-    // Intern table for drop-flag hint datums.
-    let mut seen = HashMap::new();
+        // Intern table for drop-flag hint datums.
+        let mut seen = HashMap::new();
 
-    if let Some(fragment_infos) = fragment_infos.get(&fn_did) {
-        for &info in fragment_infos {
+        let fragment_infos = fn_did.and_then(|did| fragment_infos.get(&did));
+        if let Some(fragment_infos) = fragment_infos {
+            for &info in fragment_infos {
 
-            let make_datum = |id| {
-                let init_val = C_u8(fcx.ccx, adt::DTOR_NEEDED_HINT);
-                let llname = &format!("dropflag_hint_{}", id);
-                debug!("adding hint {}", llname);
-                let ty = tcx.types.u8;
-                let ptr = alloc_ty(entry_bcx, ty, llname);
-                Store(entry_bcx, init_val, ptr);
-                let flag = datum::Lvalue::new_dropflag_hint("base::init_function");
-                datum::Datum::new(ptr, ty, flag)
-            };
+                let make_datum = |id| {
+                    let init_val = C_u8(self.ccx, adt::DTOR_NEEDED_HINT);
+                    let llname = &format!("dropflag_hint_{}", id);
+                    debug!("adding hint {}", llname);
+                    let ty = tcx.types.u8;
+                    let ptr = alloc_ty(entry_bcx, ty, llname);
+                    Store(entry_bcx, init_val, ptr);
+                    let flag = datum::Lvalue::new_dropflag_hint("FunctionContext::init");
+                    datum::Datum::new(ptr, ty, flag)
+                };
 
-            let (var, datum) = match info {
-                ty::FragmentInfo::Moved { var, .. } |
-                ty::FragmentInfo::Assigned { var, .. } => {
-                    let opt_datum = seen.get(&var).cloned().unwrap_or_else(|| {
-                        let ty = tables.node_types[&var];
-                        if fcx.type_needs_drop(ty) {
-                            let datum = make_datum(var);
-                            seen.insert(var, Some(datum.clone()));
-                            Some(datum)
+                let (var, datum) = match info {
+                    ty::FragmentInfo::Moved { var, .. } |
+                    ty::FragmentInfo::Assigned { var, .. } => {
+                        let opt_datum = seen.get(&var).cloned().unwrap_or_else(|| {
+                            let ty = tables.node_types[&var];
+                            if self.type_needs_drop(ty) {
+                                let datum = make_datum(var);
+                                seen.insert(var, Some(datum.clone()));
+                                Some(datum)
+                            } else {
+                                // No drop call needed, so we don't need a dropflag hint
+                                None
+                            }
+                        });
+                        if let Some(datum) = opt_datum {
+                            (var, datum)
                         } else {
-                            // No drop call needed, so we don't need a dropflag hint
-                            None
+                            continue
                         }
-                    });
-                    if let Some(datum) = opt_datum {
-                        (var, datum)
-                    } else {
-                        continue
                     }
-                }
-            };
-            match info {
-                ty::FragmentInfo::Moved { move_expr: expr_id, .. } => {
-                    debug!("FragmentInfo::Moved insert drop hint for {}", expr_id);
-                    hints.insert(expr_id, DropHint::new(var, datum));
-                }
-                ty::FragmentInfo::Assigned { assignee_id: expr_id, .. } => {
-                    debug!("FragmentInfo::Assigned insert drop hint for {}", expr_id);
-                    hints.insert(expr_id, DropHint::new(var, datum));
+                };
+                match info {
+                    ty::FragmentInfo::Moved { move_expr: expr_id, .. } => {
+                        debug!("FragmentInfo::Moved insert drop hint for {}", expr_id);
+                        hints.insert(expr_id, DropHint::new(var, datum));
+                    }
+                    ty::FragmentInfo::Assigned { assignee_id: expr_id, .. } => {
+                        debug!("FragmentInfo::Assigned insert drop hint for {}", expr_id);
+                        hints.insert(expr_id, DropHint::new(var, datum));
+                    }
                 }
             }
         }
-    }
-
-    entry_bcx
-}
 
-// NB: must keep 4 fns in sync:
-//
-//  - type_of_fn
-//  - create_datums_for_fn_args.
-//  - new_fn_ctxt
-//  - trans_args
-
-pub fn arg_kind<'a, 'tcx>(cx: &FunctionContext<'a, 'tcx>, t: Ty<'tcx>) -> datum::Rvalue {
-    use trans::datum::{ByRef, ByValue};
-
-    datum::Rvalue {
-        mode: if arg_is_indirect(cx.ccx, t) { ByRef } else { ByValue }
-    }
-}
-
-// create_datums_for_fn_args: creates lvalue datums for each of the
-// incoming function arguments.
-pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>,
-                                           args: &[hir::Arg],
-                                           arg_tys: &[Ty<'tcx>],
-                                           has_tupled_arg: bool,
-                                           arg_scope: cleanup::CustomScopeIndex)
-                                           -> Block<'a, 'tcx> {
-    let _icx = push_ctxt("create_datums_for_fn_args");
-    let fcx = bcx.fcx;
-    let arg_scope_id = cleanup::CustomScope(arg_scope);
-
-    debug!("create_datums_for_fn_args");
-
-    // Return an array wrapping the ValueRefs that we get from `get_param` for
-    // each argument into datums.
-    //
-    // For certain mode/type combinations, the raw llarg values are passed
-    // by value.  However, within the fn body itself, we want to always
-    // have all locals and arguments be by-ref so that we can cancel the
-    // cleanup and for better interaction with LLVM's debug info.  So, if
-    // the argument would be passed by value, we store it into an alloca.
-    // This alloca should be optimized away by LLVM's mem-to-reg pass in
-    // the event it's not truly needed.
-    let mut idx = fcx.arg_offset() as c_uint;
-    let uninit_reason = InitAlloca::Uninit("fn_arg populate dominates dtor");
-    for (i, &arg_ty) in arg_tys.iter().enumerate() {
-        let arg_datum = if !has_tupled_arg || i < arg_tys.len() - 1 {
-            if type_of::arg_is_indirect(bcx.ccx(), arg_ty) &&
-               bcx.sess().opts.debuginfo != FullDebugInfo {
-                // 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 :(.
-                let llarg = get_param(fcx.llfn, idx);
-                idx += 1;
-                bcx.fcx.schedule_lifetime_end(arg_scope_id, llarg);
-                bcx.fcx.schedule_drop_mem(arg_scope_id, llarg, arg_ty, None);
-
-                datum::Datum::new(llarg,
-                                  arg_ty,
-                                  datum::Lvalue::new("create_datum_for_fn_args"))
-            } else if common::type_is_fat_ptr(bcx.tcx(), arg_ty) {
-                let data = get_param(fcx.llfn, idx);
-                let extra = get_param(fcx.llfn, idx + 1);
-                idx += 2;
-                unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx, arg_ty, "", uninit_reason,
-                                                        arg_scope_id, (data, extra),
-                                                        |(data, extra), bcx, dst| {
-                    debug!("populate call for create_datum_for_fn_args \
-                            early fat arg, on arg[{}] ty={:?}", i, arg_ty);
-
-                    Store(bcx, data, expr::get_dataptr(bcx, dst));
-                    Store(bcx, extra, expr::get_meta(bcx, dst));
-                    bcx
-                }))
-            } else {
-                let llarg = get_param(fcx.llfn, idx);
+        entry_bcx
+    }
+
+    /// Creates lvalue datums for each of the incoming function arguments,
+    /// matches all argument patterns against them to produce bindings,
+    /// and returns the entry block (see FunctionContext::init).
+    fn bind_args(&'blk self,
+                 args: &[hir::Arg],
+                 abi: Abi,
+                 id: ast::NodeId,
+                 closure_env: closure::ClosureEnv,
+                 arg_scope: cleanup::CustomScopeIndex)
+                 -> Block<'blk, 'tcx> {
+        let _icx = push_ctxt("FunctionContext::bind_args");
+        let fn_did = self.ccx.tcx().map.local_def_id(id);
+        let mut bcx = self.init(false, Some(fn_did));
+        let arg_scope_id = cleanup::CustomScope(arg_scope);
+
+        let mut idx = 0;
+        let mut llarg_idx = self.fn_ty.ret.is_indirect() as usize;
+
+        let has_tupled_arg = match closure_env {
+            closure::ClosureEnv::NotClosure => abi == Abi::RustCall,
+            closure::ClosureEnv::Closure(..) => {
+                closure_env.load(bcx, arg_scope_id);
+                let env_arg = &self.fn_ty.args[idx];
                 idx += 1;
-                let tmp = datum::Datum::new(llarg, arg_ty, arg_kind(fcx, arg_ty));
-                unpack_datum!(bcx,
-                              datum::lvalue_scratch_datum(bcx,
-                                                          arg_ty,
-                                                          "",
-                                                          uninit_reason,
-                                                          arg_scope_id,
-                                                          tmp,
-                                                          |tmp, bcx, dst| {
-
-                        debug!("populate call for create_datum_for_fn_args \
-                                early thin arg, on arg[{}] ty={:?}", i, arg_ty);
-
-                                                              tmp.store_to(bcx, dst)
-                                                          }))
-            }
-        } else {
-            // FIXME(pcwalton): Reduce the amount of code bloat this is responsible for.
-            match arg_ty.sty {
-                ty::TyTuple(ref tupled_arg_tys) => {
-                    unpack_datum!(bcx,
-                                  datum::lvalue_scratch_datum(bcx,
-                                                              arg_ty,
-                                                              "tupled_args",
-                                                              uninit_reason,
-                                                              arg_scope_id,
-                                                              (),
-                                                              |(),
-                                                               mut bcx,
-                                                              llval| {
-                        debug!("populate call for create_datum_for_fn_args \
-                                tupled_args, on arg[{}] ty={:?}", i, arg_ty);
-                        for (j, &tupled_arg_ty) in
-                                    tupled_arg_tys.iter().enumerate() {
-                            let lldest = StructGEP(bcx, llval, j);
-                            if common::type_is_fat_ptr(bcx.tcx(), tupled_arg_ty) {
-                                let data = get_param(bcx.fcx.llfn, idx);
-                                let extra = get_param(bcx.fcx.llfn, idx + 1);
-                                Store(bcx, data, expr::get_dataptr(bcx, lldest));
-                                Store(bcx, extra, expr::get_meta(bcx, lldest));
-                                idx += 2;
-                            } else {
-                                let datum = datum::Datum::new(
-                                    get_param(bcx.fcx.llfn, idx),
-                                    tupled_arg_ty,
-                                    arg_kind(bcx.fcx, tupled_arg_ty));
-                                idx += 1;
-                                bcx = datum.store_to(bcx, lldest);
-                            };
-                        }
-                        bcx
-                    }))
+                if env_arg.pad.is_some() {
+                    llarg_idx += 1;
                 }
-                _ => {
-                    bcx.tcx()
-                       .sess
-                       .bug("last argument of a function with `rust-call` ABI isn't a tuple?!")
+                if !env_arg.is_ignore() {
+                    llarg_idx += 1;
                 }
+                false
             }
         };
-
-        let pat = &*args[i].pat;
-        bcx = if let Some(name) = simple_name(pat) {
-            // Generate nicer LLVM for the common case of fn a pattern
-            // like `x: T`
-            set_value_name(arg_datum.val, &bcx.name(name));
-            bcx.fcx.lllocals.borrow_mut().insert(pat.id, arg_datum);
-            bcx
+        let tupled_arg_id = if has_tupled_arg {
+            args[args.len() - 1].id
         } else {
-            // General path. Copy out the values that are used in the
-            // pattern.
-            _match::bind_irrefutable_pat(bcx, pat, arg_datum.match_input(), arg_scope_id)
+            ast::DUMMY_NODE_ID
         };
-        debuginfo::create_argument_metadata(bcx, &args[i]);
-    }
 
-    bcx
-}
+        // Return an array wrapping the ValueRefs that we get from `get_param` for
+        // each argument into datums.
+        //
+        // For certain mode/type combinations, the raw llarg values are passed
+        // by value.  However, within the fn body itself, we want to always
+        // have all locals and arguments be by-ref so that we can cancel the
+        // cleanup and for better interaction with LLVM's debug info.  So, if
+        // the argument would be passed by value, we store it into an alloca.
+        // This alloca should be optimized away by LLVM's mem-to-reg pass in
+        // the event it's not truly needed.
+        let uninit_reason = InitAlloca::Uninit("fn_arg populate dominates dtor");
+        for hir_arg in args {
+            let arg_ty = node_id_type(bcx, hir_arg.id);
+            let arg_datum = if hir_arg.id != tupled_arg_id {
+                let arg = &self.fn_ty.args[idx];
+                idx += 1;
+                if arg.is_indirect() && bcx.sess().opts.debuginfo != FullDebugInfo {
+                    // 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 :(.
+                    let llarg = get_param(self.llfn, llarg_idx as c_uint);
+                    llarg_idx += 1;
+                    self.schedule_lifetime_end(arg_scope_id, llarg);
+                    self.schedule_drop_mem(arg_scope_id, llarg, arg_ty, None);
+
+                    datum::Datum::new(llarg,
+                                    arg_ty,
+                                    datum::Lvalue::new("FunctionContext::bind_args"))
+                } else {
+                    unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx, arg_ty, "",
+                                                                   uninit_reason,
+                                                                   arg_scope_id, |bcx, dst| {
+                        debug!("FunctionContext::bind_args: {:?}: {:?}", hir_arg, arg_ty);
+                        let b = &bcx.build();
+                        if common::type_is_fat_ptr(bcx.tcx(), arg_ty) {
+                            let meta = &self.fn_ty.args[idx];
+                            idx += 1;
+                            arg.store_fn_arg(b, &mut llarg_idx, expr::get_dataptr(bcx, dst));
+                            meta.store_fn_arg(b, &mut llarg_idx, expr::get_meta(bcx, dst));
+                        } else {
+                            arg.store_fn_arg(b, &mut llarg_idx, dst);
+                        }
+                        bcx
+                    }))
+                }
+            } else {
+                // FIXME(pcwalton): Reduce the amount of code bloat this is responsible for.
+                let tupled_arg_tys = match arg_ty.sty {
+                    ty::TyTuple(ref tys) => tys,
+                    _ => unreachable!("last argument of `rust-call` fn isn't a tuple?!")
+                };
 
-// Ties up the llstaticallocas -> llloadenv -> lltop edges,
-// and builds the return block.
-pub fn finish_fn<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>,
-                             last_bcx: Block<'blk, 'tcx>,
-                             retty: ty::FnOutput<'tcx>,
-                             ret_debug_loc: DebugLoc) {
-    let _icx = push_ctxt("finish_fn");
+                unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx,
+                                                            arg_ty,
+                                                            "tupled_args",
+                                                            uninit_reason,
+                                                            arg_scope_id,
+                                                            |bcx, llval| {
+                    debug!("FunctionContext::bind_args: tupled {:?}: {:?}", hir_arg, arg_ty);
+                    for (j, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() {
+                        let dst = StructGEP(bcx, llval, j);
+                        let arg = &self.fn_ty.args[idx];
+                        let b = &bcx.build();
+                        if common::type_is_fat_ptr(bcx.tcx(), tupled_arg_ty) {
+                            let meta = &self.fn_ty.args[idx];
+                            idx += 1;
+                            arg.store_fn_arg(b, &mut llarg_idx, expr::get_dataptr(bcx, dst));
+                            meta.store_fn_arg(b, &mut llarg_idx, expr::get_meta(bcx, dst));
+                        } else {
+                            arg.store_fn_arg(b, &mut llarg_idx, dst);
+                        }
+                    }
+                    bcx
+                }))
+            };
 
-    let ret_cx = match fcx.llreturn.get() {
-        Some(llreturn) => {
-            if !last_bcx.terminated.get() {
-                Br(last_bcx, llreturn, DebugLoc::None);
-            }
-            raw_block(fcx, llreturn)
+            let pat = &hir_arg.pat;
+            bcx = if let Some(name) = simple_name(pat) {
+                // Generate nicer LLVM for the common case of fn a pattern
+                // like `x: T`
+                set_value_name(arg_datum.val, &bcx.name(name));
+                self.lllocals.borrow_mut().insert(pat.id, arg_datum);
+                bcx
+            } else {
+                // General path. Copy out the values that are used in the
+                // pattern.
+                _match::bind_irrefutable_pat(bcx, pat, arg_datum.match_input(), arg_scope_id)
+            };
+            debuginfo::create_argument_metadata(bcx, hir_arg);
         }
-        None => last_bcx,
-    };
 
-    // This shouldn't need to recompute the return type,
-    // as new_fn_ctxt did it already.
-    let substd_retty = fcx.monomorphize(&retty);
-    build_return_block(fcx, ret_cx, substd_retty, ret_debug_loc);
+        bcx
+    }
 
-    debuginfo::clear_source_location(fcx);
-    fcx.cleanup();
-}
+    /// Ties up the llstaticallocas -> llloadenv -> lltop edges,
+    /// and builds the return block.
+    pub fn finish(&'blk self, last_bcx: Block<'blk, 'tcx>,
+                  ret_debug_loc: DebugLoc) {
+        let _icx = push_ctxt("FunctionContext::finish");
 
-// Builds the return block for a function.
-pub fn build_return_block<'blk, 'tcx>(fcx: &FunctionContext<'blk, 'tcx>,
-                                      ret_cx: Block<'blk, 'tcx>,
-                                      retty: ty::FnOutput<'tcx>,
-                                      ret_debug_location: DebugLoc) {
-    if fcx.llretslotptr.get().is_none() ||
-       (!fcx.needs_ret_allocas && fcx.caller_expects_out_pointer) {
-        return RetVoid(ret_cx, ret_debug_location);
+        let ret_cx = match self.llreturn.get() {
+            Some(llreturn) => {
+                if !last_bcx.terminated.get() {
+                    Br(last_bcx, llreturn, DebugLoc::None);
+                }
+                raw_block(self, llreturn)
+            }
+            None => last_bcx,
+        };
+
+        self.build_return_block(ret_cx, ret_debug_loc);
+
+        debuginfo::clear_source_location(self);
+        self.cleanup();
     }
 
-    let retslot = if fcx.needs_ret_allocas {
-        Load(ret_cx, fcx.llretslotptr.get().unwrap())
-    } else {
-        fcx.llretslotptr.get().unwrap()
-    };
-    let retptr = Value(retslot);
-    match retptr.get_dominating_store(ret_cx) {
-        // If there's only a single store to the ret slot, we can directly return
-        // the value that was stored and omit the store and the alloca
-        Some(s) => {
-            let retval = s.get_operand(0).unwrap().get();
-            s.erase_from_parent();
-
-            if retptr.has_no_uses() {
-                retptr.erase_from_parent();
-            }
+    // Builds the return block for a function.
+    pub fn build_return_block(&self, ret_cx: Block<'blk, 'tcx>,
+                              ret_debug_location: DebugLoc) {
+        if self.llretslotptr.get().is_none() ||
+           ret_cx.unreachable.get() ||
+           (!self.needs_ret_allocas && self.fn_ty.ret.is_indirect()) {
+            return RetVoid(ret_cx, ret_debug_location);
+        }
 
-            let retval = if retty == ty::FnConverging(fcx.ccx.tcx().types.bool) {
-                Trunc(ret_cx, retval, Type::i1(fcx.ccx))
-            } else {
-                retval
-            };
+        let retslot = if self.needs_ret_allocas {
+            Load(ret_cx, self.llretslotptr.get().unwrap())
+        } else {
+            self.llretslotptr.get().unwrap()
+        };
+        let retptr = Value(retslot);
+        let llty = self.fn_ty.ret.original_ty;
+        match (retptr.get_dominating_store(ret_cx), self.fn_ty.ret.cast) {
+            // If there's only a single store to the ret slot, we can directly return
+            // the value that was stored and omit the store and the alloca.
+            // However, we only want to do this when there is no cast needed.
+            (Some(s), None) => {
+                let mut retval = s.get_operand(0).unwrap().get();
+                s.erase_from_parent();
+
+                if retptr.has_no_uses() {
+                    retptr.erase_from_parent();
+                }
 
-            if fcx.caller_expects_out_pointer {
-                if let ty::FnConverging(retty) = retty {
-                    store_ty(ret_cx, retval, get_param(fcx.llfn, 0), retty);
+                if self.fn_ty.ret.is_indirect() {
+                    Store(ret_cx, retval, get_param(self.llfn, 0));
+                    RetVoid(ret_cx, ret_debug_location)
+                } else {
+                    if llty == Type::i1(self.ccx) {
+                        retval = Trunc(ret_cx, retval, llty);
+                    }
+                    Ret(ret_cx, retval, ret_debug_location)
                 }
+            }
+            (_, cast_ty) if self.fn_ty.ret.is_indirect() => {
+                // Otherwise, copy the return value to the ret slot.
+                assert_eq!(cast_ty, None);
+                let llsz = llsize_of(self.ccx, self.fn_ty.ret.ty);
+                let llalign = llalign_of_min(self.ccx, self.fn_ty.ret.ty);
+                call_memcpy(&B(ret_cx), get_param(self.llfn, 0),
+                            retslot, llsz, llalign as u32);
                 RetVoid(ret_cx, ret_debug_location)
-            } else {
-                Ret(ret_cx, retval, ret_debug_location)
             }
-        }
-        // Otherwise, copy the return value to the ret slot
-        None => match retty {
-            ty::FnConverging(retty) => {
-                if fcx.caller_expects_out_pointer {
-                    memcpy_ty(ret_cx, get_param(fcx.llfn, 0), retslot, retty);
-                    RetVoid(ret_cx, ret_debug_location)
-                } else {
-                    Ret(ret_cx, load_ty(ret_cx, retslot, retty), ret_debug_location)
+            (_, Some(cast_ty)) => {
+                let load = Load(ret_cx, PointerCast(ret_cx, retslot, cast_ty.ptr_to()));
+                let llalign = llalign_of_min(self.ccx, self.fn_ty.ret.ty);
+                unsafe {
+                    llvm::LLVMSetAlignment(load, llalign);
                 }
+                Ret(ret_cx, load, ret_debug_location)
             }
-            ty::FnDiverging => {
-                if fcx.caller_expects_out_pointer {
-                    RetVoid(ret_cx, ret_debug_location)
+            (_, None) => {
+                let retval = if llty == Type::i1(self.ccx) {
+                    let val = LoadRangeAssert(ret_cx, retslot, 0, 2, llvm::False);
+                    Trunc(ret_cx, val, llty)
                 } else {
-                    Ret(ret_cx, C_undef(Type::nil(fcx.ccx)), ret_debug_location)
-                }
+                    Load(ret_cx, retslot)
+                };
+                Ret(ret_cx, retval, ret_debug_location)
             }
-        },
+        }
     }
 }
 
 /// Builds an LLVM function out of a source function.
 ///
 /// If the function closes over its environment a closure will be returned.
-pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                   decl: &hir::FnDecl,
-                                   body: &hir::Block,
-                                   llfndecl: ValueRef,
-                                   param_substs: &'tcx Substs<'tcx>,
-                                   fn_ast_id: ast::NodeId,
-                                   attributes: &[ast::Attribute],
-                                   output_type: ty::FnOutput<'tcx>,
-                                   abi: Abi,
-                                   closure_env: closure::ClosureEnv<'b>) {
+pub fn trans_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                               decl: &hir::FnDecl,
+                               body: &hir::Block,
+                               llfndecl: ValueRef,
+                               param_substs: &'tcx Substs<'tcx>,
+                               def_id: DefId,
+                               inlined_id: ast::NodeId,
+                               fn_ty: FnType,
+                               abi: Abi,
+                               closure_env: closure::ClosureEnv) {
     ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1);
 
-    record_translation_item_as_generated(ccx, fn_ast_id, param_substs);
+    if collector::collecting_debug_information(ccx) {
+        ccx.record_translation_item_as_generated(TransItem::Fn(Instance {
+            def: def_id,
+            params: &param_substs.types
+        }))
+    }
 
     let _icx = push_ctxt("trans_closure");
     attributes::emit_uwtable(llfndecl, true);
 
     debug!("trans_closure(..., param_substs={:?})", param_substs);
 
-    let has_env = match closure_env {
-        closure::ClosureEnv::Closure(..) => true,
-        closure::ClosureEnv::NotClosure => false,
-    };
-
     let (arena, fcx): (TypedArena<_>, FunctionContext);
     arena = TypedArena::new();
-    fcx = new_fn_ctxt(ccx,
-                      llfndecl,
-                      fn_ast_id,
-                      has_env,
-                      output_type,
-                      param_substs,
-                      Some(body.span),
-                      &arena);
-    let mut bcx = init_function(&fcx, false, output_type);
-
-    if attributes.iter().any(|item| item.check_name("rustc_mir")) {
-        mir::trans_mir(bcx.build());
-        fcx.cleanup();
-        return;
+    fcx = FunctionContext::new(ccx, llfndecl, fn_ty, Some(def_id), param_substs, &arena);
+
+    if fcx.mir.is_some() {
+        return mir::trans_mir(&fcx);
     }
 
     // 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);
+    let fn_cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(
+        ccx, inlined_id, body.span, true);
     let arg_scope = fcx.push_custom_cleanup_scope_with_debug_loc(fn_cleanup_debug_loc);
 
-    let block_ty = node_id_type(bcx, body.id);
-
     // Set up arguments to the function.
-    let monomorphized_arg_types = decl.inputs
-                                      .iter()
-                                      .map(|arg| node_id_type(bcx, arg.id))
-                                      .collect::<Vec<_>>();
-    for monomorphized_arg_type in &monomorphized_arg_types {
-        debug!("trans_closure: monomorphized_arg_type: {:?}",
-               monomorphized_arg_type);
-    }
-    debug!("trans_closure: function lltype: {}",
-           bcx.fcx.ccx.tn().val_to_string(bcx.fcx.llfn));
-
-    let has_tupled_arg = match closure_env {
-        closure::ClosureEnv::NotClosure => abi == Abi::RustCall,
-        _ => false,
-    };
-
-    bcx = create_datums_for_fn_args(bcx,
-                                    &decl.inputs,
-                                    &monomorphized_arg_types,
-                                    has_tupled_arg,
-                                    arg_scope);
-
-    bcx = closure_env.load(bcx, cleanup::CustomScope(arg_scope));
+    debug!("trans_closure: function: {:?}", Value(fcx.llfn));
+    let bcx = fcx.bind_args(&decl.inputs, abi, inlined_id, closure_env, arg_scope);
 
     // Up until here, IR instructions for this function have explicitly not been annotated with
     // source code location, so we don't step into call setup code. From here on, source location
     // emitting should be enabled.
     debuginfo::start_emitting_source_locations(&fcx);
 
-    let dest = match fcx.llretslotptr.get() {
-        Some(_) => expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(block_ty), "iret_slot")),
-        None => {
-            assert!(type_is_zero_size(bcx.ccx(), block_ty));
-            expr::Ignore
-        }
+    let dest = if fcx.fn_ty.ret.is_ignore() {
+        expr::Ignore
+    } else {
+        expr::SaveIn(fcx.get_ret_slot(bcx, "iret_slot"))
     };
 
     // This call to trans_block is the place where we bridge between
     // translation calls that don't have a return value (trans_crate,
     // trans_mod, trans_item, et cetera) and those that do
     // (trans_block, trans_expr, et cetera).
-    bcx = controlflow::trans_block(bcx, body, dest);
+    let mut bcx = controlflow::trans_block(bcx, body, dest);
 
     match dest {
         expr::SaveIn(slot) if fcx.needs_ret_allocas => {
@@ -2096,29 +1899,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     let ret_debug_loc = DebugLoc::At(fn_cleanup_debug_loc.id, fn_cleanup_debug_loc.span);
 
     // Insert the mandatory first few basic blocks before lltop.
-    finish_fn(&fcx, bcx, output_type, ret_debug_loc);
-
-    fn record_translation_item_as_generated<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                                      node_id: ast::NodeId,
-                                                      param_substs: &'tcx Substs<'tcx>) {
-        if !collector::collecting_debug_information(ccx) {
-            return;
-        }
-
-        let def_id = match ccx.tcx().node_id_to_type(node_id).sty {
-            ty::TyClosure(def_id, _) => def_id,
-            _ => ccx.external_srcs()
-                    .borrow()
-                    .get(&node_id)
-                    .map(|did| *did)
-                    .unwrap_or_else(|| ccx.tcx().map.local_def_id(node_id)),
-        };
-
-        ccx.record_translation_item_as_generated(TransItem::Fn{
-            def_id: def_id,
-            substs: ccx.tcx().mk_substs(ccx.tcx().erase_regions(param_substs)),
-        });
-    }
+    fcx.finish(bcx, ret_debug_loc);
 }
 
 /// Creates an LLVM function corresponding to a source language function.
@@ -2127,44 +1908,37 @@ pub fn trans_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                           body: &hir::Block,
                           llfndecl: ValueRef,
                           param_substs: &'tcx Substs<'tcx>,
-                          id: ast::NodeId,
-                          attrs: &[ast::Attribute]) {
+                          id: ast::NodeId) {
     let _s = StatRecorder::new(ccx, ccx.tcx().map.path_to_string(id).to_string());
     debug!("trans_fn(param_substs={:?})", param_substs);
     let _icx = push_ctxt("trans_fn");
     let fn_ty = ccx.tcx().node_id_to_type(id);
     let fn_ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, &fn_ty);
-    let sig = fn_ty.fn_sig();
-    let sig = ccx.tcx().erase_late_bound_regions(&sig);
+    let sig = ccx.tcx().erase_late_bound_regions(fn_ty.fn_sig());
     let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
-    let output_type = sig.output;
     let abi = fn_ty.fn_abi();
+    let fn_ty = FnType::new(ccx, abi, &sig, &[]);
+    let def_id = if let Some(&def_id) = ccx.external_srcs().borrow().get(&id) {
+        def_id
+    } else {
+        ccx.tcx().map.local_def_id(id)
+    };
     trans_closure(ccx,
                   decl,
                   body,
                   llfndecl,
                   param_substs,
+                  def_id,
                   id,
-                  attrs,
-                  output_type,
+                  fn_ty,
                   abi,
                   closure::ClosureEnv::NotClosure);
 }
 
-pub fn trans_enum_variant<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                    ctor_id: ast::NodeId,
-                                    disr: Disr,
-                                    param_substs: &'tcx Substs<'tcx>,
-                                    llfndecl: ValueRef) {
-    let _icx = push_ctxt("trans_enum_variant");
-
-    trans_enum_variant_or_tuple_like_struct(ccx, ctor_id, disr, param_substs, llfndecl);
-}
-
 pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                                                  ctor_ty: Ty<'tcx>,
                                                  disr: Disr,
-                                                 args: callee::CallArgs,
+                                                 args: CallArgs,
                                                  dest: expr::Dest,
                                                  debug_loc: DebugLoc)
                                                  -> Result<'blk, 'tcx> {
@@ -2192,7 +1966,7 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
 
     if !type_is_zero_size(ccx, result_ty) {
         match args {
-            callee::ArgExprs(exprs) => {
+            ArgExprs(exprs) => {
                 let fields = exprs.iter().map(|x| &**x).enumerate().collect::<Vec<_>>();
                 bcx = expr::trans_adt(bcx,
                                       result_ty,
@@ -2208,7 +1982,7 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         // Just eval all the expressions (if any). Since expressions in Rust can have arbitrary
         // contents, there could be side-effects we need from them.
         match args {
-            callee::ArgExprs(exprs) => {
+            ArgExprs(exprs) => {
                 for expr in exprs {
                     bcx = expr::trans_into(bcx, expr, expr::Ignore);
                 }
@@ -2233,72 +2007,51 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     Result::new(bcx, llresult)
 }
 
-pub fn trans_tuple_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                    ctor_id: ast::NodeId,
-                                    param_substs: &'tcx Substs<'tcx>,
-                                    llfndecl: ValueRef) {
-    let _icx = push_ctxt("trans_tuple_struct");
-
-    trans_enum_variant_or_tuple_like_struct(ccx, ctor_id, Disr(0), param_substs, llfndecl);
-}
-
-fn trans_enum_variant_or_tuple_like_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                                     ctor_id: ast::NodeId,
-                                                     disr: Disr,
-                                                     param_substs: &'tcx Substs<'tcx>,
-                                                     llfndecl: ValueRef) {
+pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                                 ctor_id: ast::NodeId,
+                                 disr: Disr,
+                                 param_substs: &'tcx Substs<'tcx>,
+                                 llfndecl: ValueRef) {
     let ctor_ty = ccx.tcx().node_id_to_type(ctor_id);
     let ctor_ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, &ctor_ty);
 
     let sig = ccx.tcx().erase_late_bound_regions(&ctor_ty.fn_sig());
     let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
-    let arg_tys = sig.inputs;
-    let result_ty = sig.output;
+    let fn_ty = FnType::new(ccx, Abi::Rust, &sig, &[]);
 
     let (arena, fcx): (TypedArena<_>, FunctionContext);
     arena = TypedArena::new();
-    fcx = new_fn_ctxt(ccx,
-                      llfndecl,
-                      ctor_id,
-                      false,
-                      result_ty,
-                      param_substs,
-                      None,
-                      &arena);
-    let bcx = init_function(&fcx, false, result_ty);
+    fcx = FunctionContext::new(ccx, llfndecl, fn_ty,
+                               Some(ccx.tcx().map.local_def_id(ctor_id)),
+                               param_substs, &arena);
+    let bcx = fcx.init(false, None);
 
     assert!(!fcx.needs_ret_allocas);
 
-    if !type_is_zero_size(fcx.ccx, result_ty.unwrap()) {
-        let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot");
+    if !fcx.fn_ty.ret.is_ignore() {
+        let dest = fcx.get_ret_slot(bcx, "eret_slot");
         let dest_val = adt::MaybeSizedValue::sized(dest); // Can return unsized value
-        let repr = adt::represent_type(ccx, result_ty.unwrap());
-        let mut llarg_idx = fcx.arg_offset() as c_uint;
-        for (i, arg_ty) in arg_tys.into_iter().enumerate() {
+        let repr = adt::represent_type(ccx, sig.output.unwrap());
+        let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize;
+        let mut arg_idx = 0;
+        for (i, arg_ty) in sig.inputs.into_iter().enumerate() {
             let lldestptr = adt::trans_field_ptr(bcx, &repr, dest_val, Disr::from(disr), i);
+            let arg = &fcx.fn_ty.args[arg_idx];
+            arg_idx += 1;
+            let b = &bcx.build();
             if common::type_is_fat_ptr(bcx.tcx(), arg_ty) {
-                Store(bcx,
-                      get_param(fcx.llfn, llarg_idx),
-                      expr::get_dataptr(bcx, lldestptr));
-                Store(bcx,
-                      get_param(fcx.llfn, llarg_idx + 1),
-                      expr::get_meta(bcx, lldestptr));
-                llarg_idx += 2;
+                let meta = &fcx.fn_ty.args[arg_idx];
+                arg_idx += 1;
+                arg.store_fn_arg(b, &mut llarg_idx, expr::get_dataptr(bcx, lldestptr));
+                meta.store_fn_arg(b, &mut llarg_idx, expr::get_meta(bcx, lldestptr));
             } else {
-                let arg = get_param(fcx.llfn, llarg_idx);
-                llarg_idx += 1;
-
-                if arg_is_indirect(ccx, arg_ty) {
-                    memcpy_ty(bcx, lldestptr, arg, arg_ty);
-                } else {
-                    store_ty(bcx, arg, lldestptr, arg_ty);
-                }
+                arg.store_fn_arg(b, &mut llarg_idx, lldestptr);
             }
         }
         adt::trans_set_discr(bcx, &repr, dest, disr);
     }
 
-    finish_fn(&fcx, bcx, result_ty, DebugLoc::None);
+    fcx.finish(bcx, DebugLoc::None);
 }
 
 fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &hir::EnumDef, sp: Span, id: ast::NodeId) {
@@ -2493,10 +2246,11 @@ fn set_global_section(ccx: &CrateContext, llval: ValueRef, i: &hir::Item) {
 pub fn trans_item(ccx: &CrateContext, item: &hir::Item) {
     let _icx = push_ctxt("trans_item");
 
+    let tcx = ccx.tcx();
     let from_external = ccx.external_srcs().borrow().contains_key(&item.id);
 
     match item.node {
-        hir::ItemFn(ref decl, _, _, abi, ref generics, ref body) => {
+        hir::ItemFn(ref decl, _, _, _, ref generics, ref body) => {
             if !generics.is_type_parameterized() {
                 let trans_everywhere = attr::requests_inline(&item.attrs);
                 // Ignore `trans_everywhere` for cross-crate inlined items
@@ -2504,26 +2258,10 @@ pub fn trans_item(ccx: &CrateContext, item: &hir::Item) {
                 // compilation unit that references the item, so it will still get
                 // translated everywhere it's needed.
                 for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) {
-                    let llfn = get_item_val(ccx, item.id);
-                    let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
-                    if abi != Abi::Rust {
-                        foreign::trans_rust_fn_with_foreign_abi(ccx,
-                                                                &decl,
-                                                                &body,
-                                                                &item.attrs,
-                                                                llfn,
-                                                                empty_substs,
-                                                                item.id,
-                                                                None);
-                    } else {
-                        trans_fn(ccx,
-                                 &decl,
-                                 &body,
-                                 llfn,
-                                 empty_substs,
-                                 item.id,
-                                 &item.attrs);
-                    }
+                    let empty_substs = tcx.mk_substs(Substs::trans_empty());
+                    let def_id = tcx.map.local_def_id(item.id);
+                    let llfn = Callee::def(ccx, def_id, empty_substs).reify(ccx).val;
+                    trans_fn(ccx, &decl, &body, llfn, empty_substs, item.id);
                     set_global_section(ccx, llfn, item);
                     update_linkage(ccx,
                                    llfn,
@@ -2540,29 +2278,46 @@ pub fn trans_item(ccx: &CrateContext, item: &hir::Item) {
                         // error in trans. This is used to write compile-fail tests
                         // that actually test that compilation succeeds without
                         // reporting an error.
-                        let item_def_id = ccx.tcx().map.local_def_id(item.id);
-                        if ccx.tcx().has_attr(item_def_id, "rustc_error") {
-                            ccx.tcx().sess.span_fatal(item.span, "compilation successful");
+                        if tcx.has_attr(def_id, "rustc_error") {
+                            tcx.sess.span_fatal(item.span, "compilation successful");
                         }
                     }
                 }
             }
         }
         hir::ItemImpl(_, _, ref generics, _, _, ref impl_items) => {
-            meth::trans_impl(ccx, item.name, impl_items, generics, item.id);
-        }
-        hir::ItemMod(_) => {
-            // modules have no equivalent at runtime, they just affect
-            // the mangled names of things contained within
+            // Both here and below with generic methods, be sure to recurse and look for
+            // items that we need to translate.
+            if !generics.ty_params.is_empty() {
+                return;
+            }
+
+            for impl_item in impl_items {
+                if let hir::ImplItemKind::Method(ref sig, ref body) = impl_item.node {
+                    if sig.generics.ty_params.is_empty() {
+                        let trans_everywhere = attr::requests_inline(&impl_item.attrs);
+                        for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) {
+                            let empty_substs = tcx.mk_substs(Substs::trans_empty());
+                            let def_id = tcx.map.local_def_id(impl_item.id);
+                            let llfn = Callee::def(ccx, def_id, empty_substs).reify(ccx).val;
+                            trans_fn(ccx, &sig.decl, body, llfn, empty_substs, impl_item.id);
+                            update_linkage(ccx, llfn, Some(impl_item.id),
+                                if is_origin {
+                                    OriginalTranslation
+                                } else {
+                                    InlinedCopy
+                                });
+                        }
+                    }
+                }
+            }
         }
         hir::ItemEnum(ref enum_definition, ref gens) => {
             if gens.ty_params.is_empty() {
                 // sizes only make sense for non-generic types
-
                 enum_variant_size_lint(ccx, enum_definition, item.span, item.id);
             }
         }
-        hir::ItemConst(..) => {}
         hir::ItemStatic(_, m, ref expr) => {
             let g = match consts::trans_static(ccx, m, expr, item.id, &item.attrs) {
                 Ok(g) => g,
@@ -2571,62 +2326,17 @@ pub fn trans_item(ccx: &CrateContext, item: &hir::Item) {
             set_global_section(ccx, g, item);
             update_linkage(ccx, g, Some(item.id), OriginalTranslation);
         }
-        hir::ItemForeignMod(ref foreign_mod) => {
-            foreign::trans_foreign_mod(ccx, foreign_mod);
-        }
-        hir::ItemTrait(..) => {}
-        _ => {
-            // fall through
-        }
-    }
-}
-
-// only use this for foreign function ABIs and glue, use `register_fn` for Rust functions
-pub fn register_fn_llvmty(ccx: &CrateContext,
-                          sp: Span,
-                          sym: String,
-                          node_id: ast::NodeId,
-                          cc: llvm::CallConv,
-                          llfty: Type)
-                          -> ValueRef {
-    debug!("register_fn_llvmty id={} sym={}", node_id, sym);
-
-    let llfn = declare::define_fn(ccx, &sym[..], cc, llfty,
-                                   ty::FnConverging(ccx.tcx().mk_nil())).unwrap_or_else(||{
-        ccx.sess().span_fatal(sp, &format!("symbol `{}` is already defined", sym));
-    });
-    finish_register_fn(ccx, sym, node_id);
-    llfn
-}
-
-fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId) {
-    ccx.item_symbols().borrow_mut().insert(node_id, sym);
-}
-
-fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                         sp: Span,
-                         sym: String,
-                         node_id: ast::NodeId,
-                         node_type: Ty<'tcx>)
-                         -> ValueRef {
-    if let ty::TyFnDef(_, _, ref f) = node_type.sty {
-        if f.abi != Abi::Rust && f.abi != Abi::RustCall {
-            ccx.sess().span_bug(sp,
-                                &format!("only the `{}` or `{}` calling conventions are valid \
-                                          for this function; `{}` was specified",
-                                         Abi::Rust.name(),
-                                         Abi::RustCall.name(),
-                                         f.abi.name()));
+        hir::ItemForeignMod(ref m) => {
+            if m.abi == Abi::RustIntrinsic || m.abi == Abi::PlatformIntrinsic {
+                return;
+            }
+            for fi in &m.items {
+                let lname = imported_name(fi.name, &fi.attrs).to_string();
+                ccx.item_symbols().borrow_mut().insert(fi.id, lname);
+            }
         }
-    } else {
-        ccx.sess().span_bug(sp, "expected bare rust function")
+        _ => {}
     }
-
-    let llfn = declare::define_rust_fn(ccx, &sym[..], node_type).unwrap_or_else(|| {
-        ccx.sess().span_fatal(sp, &format!("symbol `{}` is already defined", sym));
-    });
-    finish_register_fn(ccx, sym, node_id);
-    llfn
 }
 
 pub fn is_entry_fn(sess: &Session, node_id: ast::NodeId) -> bool {
@@ -2654,14 +2364,15 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) {
                        use_start_lang_item: bool) {
         let llfty = Type::func(&[ccx.int_type(), Type::i8p(ccx).ptr_to()], &ccx.int_type());
 
-        let llfn = declare::define_cfn(ccx, "main", llfty, ccx.tcx().mk_nil()).unwrap_or_else(|| {
+        if declare::get_defined_value(ccx, "main").is_some() {
             // FIXME: We should be smart and show a better diagnostic here.
             ccx.sess().struct_span_err(sp, "entry symbol `main` defined multiple times")
                       .help("did you use #[no_mangle] on `fn main`? Use #[start] instead")
                       .emit();
             ccx.sess().abort_if_errors();
             panic!();
-        });
+        }
+        let llfn = declare::declare_cfn(ccx, "main", llfty);
 
         let llbb = unsafe {
             llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn, "top\0".as_ptr() as *const _)
@@ -2675,17 +2386,10 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) {
             let (start_fn, args) = if use_start_lang_item {
                 let start_def_id = match ccx.tcx().lang_items.require(StartFnLangItem) {
                     Ok(id) => id,
-                    Err(s) => {
-                        ccx.sess().fatal(&s[..]);
-                    }
-                };
-                let start_fn = if let Some(start_node_id) = ccx.tcx()
-                                                               .map
-                                                               .as_local_node_id(start_def_id) {
-                    get_item_val(ccx, start_node_id)
-                } else {
-                    get_extern_fn(ccx, start_def_id).val
+                    Err(s) => ccx.sess().fatal(&s)
                 };
+                let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
+                let start_fn = Callee::def(ccx, start_def_id, empty_substs).reify(ccx).val;
                 let args = {
                     let opaque_rust_main =
                         llvm::LLVMBuildPointerCast(bld,
@@ -2715,11 +2419,11 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) {
     }
 }
 
-fn exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                           id: ast::NodeId,
-                           ty: Ty<'tcx>,
-                           attrs: &[ast::Attribute])
-                           -> String {
+pub fn exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                               id: ast::NodeId,
+                               ty: Ty<'tcx>,
+                               attrs: &[ast::Attribute])
+                               -> String {
     match ccx.external_srcs().borrow().get(&id) {
         Some(&did) => {
             let sym = ccx.sess().cstore.item_symbol(did);
@@ -2750,180 +2454,18 @@ fn exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     }
 }
 
-fn contains_null(s: &str) -> bool {
-    s.bytes().any(|b| b == 0)
-}
-
-pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
-    debug!("get_item_val(id=`{}`)", id);
-
-    if let Some(v) = ccx.item_vals().borrow().get(&id).cloned() {
-        return v;
-    }
-
-    let item = ccx.tcx().map.get(id);
-    debug!("get_item_val: id={} item={:?}", id, item);
-    let val = match item {
-        hir_map::NodeItem(i) => {
-            let ty = ccx.tcx().node_id_to_type(i.id);
-            let sym = || exported_name(ccx, id, ty, &i.attrs);
-
-            let v = match i.node {
-                hir::ItemStatic(..) => {
-                    // If this static came from an external crate, then
-                    // we need to get the symbol from metadata instead of
-                    // using the current crate's name/version
-                    // information in the hash of the symbol
-                    let sym = sym();
-                    debug!("making {}", sym);
-
-                    // Create the global before evaluating the initializer;
-                    // this is necessary to allow recursive statics.
-                    let llty = type_of(ccx, ty);
-                    let g = declare::define_global(ccx, &sym[..], llty).unwrap_or_else(|| {
-                        ccx.sess()
-                           .span_fatal(i.span, &format!("symbol `{}` is already defined", sym))
-                    });
-
-                    ccx.item_symbols().borrow_mut().insert(i.id, sym);
-                    g
-                }
-
-                hir::ItemFn(_, _, _, abi, _, _) => {
-                    let sym = sym();
-                    let llfn = if abi == Abi::Rust {
-                        register_fn(ccx, i.span, sym, i.id, ty)
-                    } else {
-                        foreign::register_rust_fn_with_foreign_abi(ccx, i.span, sym, i.id)
-                    };
-                    attributes::from_fn_attrs(ccx, &i.attrs, llfn);
-                    llfn
-                }
-
-                _ => ccx.sess().bug("get_item_val: weird result in table"),
-            };
-
-            v
-        }
-
-        hir_map::NodeTraitItem(trait_item) => {
-            debug!("get_item_val(): processing a NodeTraitItem");
-            match trait_item.node {
-                hir::MethodTraitItem(_, Some(_)) => {
-                    register_method(ccx, id, &trait_item.attrs, trait_item.span)
-                }
-                _ => {
-                    ccx.sess().span_bug(trait_item.span,
-                                        "unexpected variant: trait item other than a provided \
-                                         method in get_item_val()");
-                }
-            }
+pub fn imported_name(name: ast::Name, attrs: &[ast::Attribute]) -> InternedString {
+    match attr::first_attr_value_str_by_name(attrs, "link_name") {
+        Some(ln) => ln.clone(),
+        None => match weak_lang_items::link_name(attrs) {
+            Some(name) => name,
+            None => name.as_str(),
         }
-
-        hir_map::NodeImplItem(impl_item) => {
-            match impl_item.node {
-                hir::ImplItemKind::Method(..) => {
-                    register_method(ccx, id, &impl_item.attrs, impl_item.span)
-                }
-                _ => {
-                    ccx.sess().span_bug(impl_item.span,
-                                        "unexpected variant: non-method impl item in \
-                                         get_item_val()");
-                }
-            }
-        }
-
-        hir_map::NodeForeignItem(ni) => {
-            match ni.node {
-                hir::ForeignItemFn(..) => {
-                    let abi = ccx.tcx().map.get_foreign_abi(id);
-                    let ty = ccx.tcx().node_id_to_type(ni.id);
-                    let name = foreign::link_name(&ni);
-                    foreign::register_foreign_item_fn(ccx, abi, ty, &name, &ni.attrs)
-                }
-                hir::ForeignItemStatic(..) => {
-                    foreign::register_static(ccx, &ni)
-                }
-            }
-        }
-
-        hir_map::NodeVariant(ref v) => {
-            let llfn;
-            let fields = if v.node.data.is_struct() {
-                ccx.sess().bug("struct variant kind unexpected in get_item_val")
-            } else {
-                v.node.data.fields()
-            };
-            assert!(!fields.is_empty());
-            let ty = ccx.tcx().node_id_to_type(id);
-            let parent = ccx.tcx().map.get_parent(id);
-            let enm = ccx.tcx().map.expect_item(parent);
-            let sym = exported_name(ccx, id, ty, &enm.attrs);
-
-            llfn = match enm.node {
-                hir::ItemEnum(_, _) => {
-                    register_fn(ccx, (*v).span, sym, id, ty)
-                }
-                _ => ccx.sess().bug("NodeVariant, shouldn't happen"),
-            };
-            attributes::inline(llfn, attributes::InlineAttr::Hint);
-            llfn
-        }
-
-        hir_map::NodeStructCtor(struct_def) => {
-            // Only register the constructor if this is a tuple-like struct.
-            let ctor_id = if struct_def.is_struct() {
-                ccx.sess().bug("attempt to register a constructor of a non-tuple-like struct")
-            } else {
-                struct_def.id()
-            };
-            let parent = ccx.tcx().map.get_parent(id);
-            let struct_item = ccx.tcx().map.expect_item(parent);
-            let ty = ccx.tcx().node_id_to_type(ctor_id);
-            let sym = exported_name(ccx, id, ty, &struct_item.attrs);
-            let llfn = register_fn(ccx, struct_item.span, sym, ctor_id, ty);
-            attributes::inline(llfn, attributes::InlineAttr::Hint);
-            llfn
-        }
-
-        ref variant => {
-            ccx.sess().bug(&format!("get_item_val(): unexpected variant: {:?}", variant))
-        }
-    };
-
-    // All LLVM globals and functions are initially created as external-linkage
-    // declarations.  If `trans_item`/`trans_fn` later turns the declaration
-    // into a definition, it adjusts the linkage then (using `update_linkage`).
-    //
-    // The exception is foreign items, which have their linkage set inside the
-    // call to `foreign::register_*` above.  We don't touch the linkage after
-    // that (`foreign::trans_foreign_mod` doesn't adjust the linkage like the
-    // other item translation functions do).
-
-    ccx.item_vals().borrow_mut().insert(id, val);
-    val
+    }
 }
 
-fn register_method(ccx: &CrateContext,
-                   id: ast::NodeId,
-                   attrs: &[ast::Attribute],
-                   span: Span)
-                   -> ValueRef {
-    let mty = ccx.tcx().node_id_to_type(id);
-
-    let sym = exported_name(ccx, id, mty, &attrs);
-
-    if let ty::TyFnDef(_, _, ref f) = mty.sty {
-        let llfn = if f.abi == Abi::Rust || f.abi == Abi::RustCall {
-            register_fn(ccx, span, sym, id, mty)
-        } else {
-            foreign::register_rust_fn_with_foreign_abi(ccx, span, sym, id)
-        };
-        attributes::from_fn_attrs(ccx, &attrs, llfn);
-        return llfn;
-    } else {
-        ccx.sess().span_bug(span, "expected bare rust function");
-    }
+fn contains_null(s: &str) -> bool {
+    s.bytes().any(|b| b == 0)
 }
 
 pub fn write_metadata<'a, 'tcx>(cx: &SharedCrateContext<'a, 'tcx>,
diff --git a/src/librustc_trans/trans/build.rs b/src/librustc_trans/trans/build.rs
index 2b4fcf436cb..53e64c086a8 100644
--- a/src/librustc_trans/trans/build.rs
+++ b/src/librustc_trans/trans/build.rs
@@ -12,7 +12,7 @@
 #![allow(non_snake_case)]
 
 use llvm;
-use llvm::{CallConv, AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect, AttrBuilder};
+use llvm::{AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect};
 use llvm::{Opcode, IntPredicate, RealPredicate};
 use llvm::{ValueRef, BasicBlockRef};
 use trans::common::*;
@@ -20,6 +20,7 @@ use syntax::codemap::Span;
 
 use trans::builder::Builder;
 use trans::type_::Type;
+use trans::value::Value;
 use trans::debuginfo::DebugLoc;
 
 use libc::{c_uint, c_char};
@@ -138,7 +139,6 @@ pub fn Invoke(cx: Block,
               args: &[ValueRef],
               then: BasicBlockRef,
               catch: BasicBlockRef,
-              attributes: Option<AttrBuilder>,
               debug_loc: DebugLoc)
               -> ValueRef {
     if cx.unreachable.get() {
@@ -146,12 +146,14 @@ pub fn Invoke(cx: Block,
     }
     check_not_terminated(cx);
     terminate(cx, "Invoke");
-    debug!("Invoke({} with arguments ({}))",
-           cx.val_to_string(fn_),
-           args.iter().map(|a| cx.val_to_string(*a)).collect::<Vec<String>>().join(", "));
+    debug!("Invoke({:?} with arguments ({}))",
+           Value(fn_),
+           args.iter().map(|a| {
+                format!("{:?}", Value(*a))
+           }).collect::<Vec<String>>().join(", "));
     debug_loc.apply(cx.fcx);
     let bundle = cx.lpad().and_then(|b| b.bundle());
-    B(cx).invoke(fn_, args, then, catch, bundle, attributes)
+    B(cx).invoke(fn_, args, then, catch, bundle)
 }
 
 pub fn Unreachable(cx: Block) {
@@ -219,6 +221,18 @@ pub fn FAdd(cx: Block,
     B(cx).fadd(lhs, rhs)
 }
 
+pub fn FAddFast(cx: Block,
+                lhs: ValueRef,
+                rhs: ValueRef,
+                debug_loc: DebugLoc)
+            -> ValueRef {
+    if cx.unreachable.get() {
+        return _Undef(lhs);
+    }
+    debug_loc.apply(cx.fcx);
+    B(cx).fadd_fast(lhs, rhs)
+}
+
 pub fn Sub(cx: Block,
            lhs: ValueRef,
            rhs: ValueRef,
@@ -267,6 +281,18 @@ pub fn FSub(cx: Block,
     B(cx).fsub(lhs, rhs)
 }
 
+pub fn FSubFast(cx: Block,
+                lhs: ValueRef,
+                rhs: ValueRef,
+                debug_loc: DebugLoc)
+            -> ValueRef {
+    if cx.unreachable.get() {
+        return _Undef(lhs);
+    }
+    debug_loc.apply(cx.fcx);
+    B(cx).fsub_fast(lhs, rhs)
+}
+
 pub fn Mul(cx: Block,
            lhs: ValueRef,
            rhs: ValueRef,
@@ -315,6 +341,18 @@ pub fn FMul(cx: Block,
     B(cx).fmul(lhs, rhs)
 }
 
+pub fn FMulFast(cx: Block,
+                lhs: ValueRef,
+                rhs: ValueRef,
+                debug_loc: DebugLoc)
+            -> ValueRef {
+    if cx.unreachable.get() {
+        return _Undef(lhs);
+    }
+    debug_loc.apply(cx.fcx);
+    B(cx).fmul_fast(lhs, rhs)
+}
+
 pub fn UDiv(cx: Block,
             lhs: ValueRef,
             rhs: ValueRef,
@@ -363,6 +401,18 @@ pub fn FDiv(cx: Block,
     B(cx).fdiv(lhs, rhs)
 }
 
+pub fn FDivFast(cx: Block,
+                lhs: ValueRef,
+                rhs: ValueRef,
+                debug_loc: DebugLoc)
+            -> ValueRef {
+    if cx.unreachable.get() {
+        return _Undef(lhs);
+    }
+    debug_loc.apply(cx.fcx);
+    B(cx).fdiv_fast(lhs, rhs)
+}
+
 pub fn URem(cx: Block,
             lhs: ValueRef,
             rhs: ValueRef,
@@ -399,6 +449,18 @@ pub fn FRem(cx: Block,
     B(cx).frem(lhs, rhs)
 }
 
+pub fn FRemFast(cx: Block,
+                lhs: ValueRef,
+                rhs: ValueRef,
+                debug_loc: DebugLoc)
+            -> ValueRef {
+    if cx.unreachable.get() {
+        return _Undef(lhs);
+    }
+    debug_loc.apply(cx.fcx);
+    B(cx).frem_fast(lhs, rhs)
+}
+
 pub fn Shl(cx: Block,
            lhs: ValueRef,
            rhs: ValueRef,
@@ -908,7 +970,6 @@ pub fn InlineAsmCall(cx: Block, asm: *const c_char, cons: *const c_char,
 pub fn Call(cx: Block,
             fn_: ValueRef,
             args: &[ValueRef],
-            attributes: Option<AttrBuilder>,
             debug_loc: DebugLoc)
             -> ValueRef {
     if cx.unreachable.get() {
@@ -916,22 +977,7 @@ pub fn Call(cx: Block,
     }
     debug_loc.apply(cx.fcx);
     let bundle = cx.lpad.get().and_then(|b| b.bundle());
-    B(cx).call(fn_, args, bundle, attributes)
-}
-
-pub fn CallWithConv(cx: Block,
-                    fn_: ValueRef,
-                    args: &[ValueRef],
-                    conv: CallConv,
-                    attributes: Option<AttrBuilder>,
-                    debug_loc: DebugLoc)
-                    -> ValueRef {
-    if cx.unreachable.get() {
-        return _UndefReturn(cx, fn_);
-    }
-    debug_loc.apply(cx.fcx);
-    let bundle = cx.lpad.get().and_then(|b| b.bundle());
-    B(cx).call_with_conv(fn_, args, conv, bundle, attributes)
+    B(cx).call(fn_, args, bundle)
 }
 
 pub fn AtomicFence(cx: Block, order: AtomicOrdering, scope: SynchronizationScope) {
diff --git a/src/librustc_trans/trans/builder.rs b/src/librustc_trans/trans/builder.rs
index 3b4a67cb089..869e9212b1c 100644
--- a/src/librustc_trans/trans/builder.rs
+++ b/src/librustc_trans/trans/builder.rs
@@ -11,13 +11,14 @@
 #![allow(dead_code)] // FFI wrappers
 
 use llvm;
-use llvm::{CallConv, AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect, AttrBuilder};
+use llvm::{AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect};
 use llvm::{Opcode, IntPredicate, RealPredicate, False, OperandBundleDef};
 use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef};
 use trans::base;
 use trans::common::*;
 use trans::machine::llalign_of_pref;
 use trans::type_::Type;
+use trans::value::Value;
 use util::nodemap::FnvHashMap;
 use libc::{c_uint, c_char};
 
@@ -164,33 +165,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                   args: &[ValueRef],
                   then: BasicBlockRef,
                   catch: BasicBlockRef,
-                  bundle: Option<&OperandBundleDef>,
-                  attributes: Option<AttrBuilder>)
+                  bundle: Option<&OperandBundleDef>)
                   -> ValueRef {
         self.count_insn("invoke");
 
-        debug!("Invoke {} with args ({})",
-               self.ccx.tn().val_to_string(llfn),
+        debug!("Invoke {:?} with args ({})",
+               Value(llfn),
                args.iter()
-                   .map(|&v| self.ccx.tn().val_to_string(v))
+                   .map(|&v| format!("{:?}", Value(v)))
                    .collect::<Vec<String>>()
                    .join(", "));
 
         let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(0 as *mut _);
 
         unsafe {
-            let v = llvm::LLVMRustBuildInvoke(self.llbuilder,
-                                              llfn,
-                                              args.as_ptr(),
-                                              args.len() as c_uint,
-                                              then,
-                                              catch,
-                                              bundle,
-                                              noname());
-            if let Some(a) = attributes {
-                a.apply_callsite(v);
-            }
-            v
+            llvm::LLVMRustBuildInvoke(self.llbuilder,
+                                      llfn,
+                                      args.as_ptr(),
+                                      args.len() as c_uint,
+                                      then,
+                                      catch,
+                                      bundle,
+                                      noname())
         }
     }
 
@@ -230,6 +226,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
     }
 
+    pub fn fadd_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
+        self.count_insn("fadd");
+        unsafe {
+            let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, noname());
+            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            instr
+        }
+    }
+
     pub fn sub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
         self.count_insn("sub");
         unsafe {
@@ -258,6 +263,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
     }
 
+    pub fn fsub_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
+        self.count_insn("sub");
+        unsafe {
+            let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, noname());
+            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            instr
+        }
+    }
+
     pub fn mul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
         self.count_insn("mul");
         unsafe {
@@ -286,6 +300,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
     }
 
+    pub fn fmul_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
+        self.count_insn("fmul");
+        unsafe {
+            let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, noname());
+            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            instr
+        }
+    }
+
+
     pub fn udiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
         self.count_insn("udiv");
         unsafe {
@@ -314,6 +338,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
     }
 
+    pub fn fdiv_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
+        self.count_insn("fdiv");
+        unsafe {
+            let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, noname());
+            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            instr
+        }
+    }
+
     pub fn urem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
         self.count_insn("urem");
         unsafe {
@@ -335,6 +368,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
     }
 
+    pub fn frem_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
+        self.count_insn("frem");
+        unsafe {
+            let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, noname());
+            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            instr
+        }
+    }
+
     pub fn shl(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
         self.count_insn("shl");
         unsafe {
@@ -497,9 +539,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     }
 
     pub fn store(&self, val: ValueRef, ptr: ValueRef) -> ValueRef {
-        debug!("Store {} -> {}",
-               self.ccx.tn().val_to_string(val),
-               self.ccx.tn().val_to_string(ptr));
+        debug!("Store {:?} -> {:?}", Value(val), Value(ptr));
         assert!(!self.llbuilder.is_null());
         self.count_insn("store");
         unsafe {
@@ -508,9 +548,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     }
 
     pub fn volatile_store(&self, val: ValueRef, ptr: ValueRef) -> ValueRef {
-        debug!("Store {} -> {}",
-               self.ccx.tn().val_to_string(val),
-               self.ccx.tn().val_to_string(ptr));
+        debug!("Store {:?} -> {:?}", Value(val), Value(ptr));
         assert!(!self.llbuilder.is_null());
         self.count_insn("store.volatile");
         unsafe {
@@ -521,9 +559,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     }
 
     pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, order: AtomicOrdering) {
-        debug!("Store {} -> {}",
-               self.ccx.tn().val_to_string(val),
-               self.ccx.tn().val_to_string(ptr));
+        debug!("Store {:?} -> {:?}", Value(val), Value(ptr));
         self.count_insn("store.atomic");
         unsafe {
             let ty = Type::from_ref(llvm::LLVMTypeOf(ptr));
@@ -780,7 +816,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                                          comment_text.as_ptr(), noname(), False,
                                          False)
             };
-            self.call(asm, &[], None, None);
+            self.call(asm, &[], None);
         }
     }
 
@@ -796,28 +832,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                          else          { llvm::False };
 
         let argtys = inputs.iter().map(|v| {
-            debug!("Asm Input Type: {}", self.ccx.tn().val_to_string(*v));
+            debug!("Asm Input Type: {:?}", Value(*v));
             val_ty(*v)
         }).collect::<Vec<_>>();
 
-        debug!("Asm Output Type: {}", self.ccx.tn().type_to_string(output));
+        debug!("Asm Output Type: {:?}", output);
         let fty = Type::func(&argtys[..], &output);
         unsafe {
             let v = llvm::LLVMInlineAsm(
                 fty.to_ref(), asm, cons, volatile, alignstack, dia as c_uint);
-            self.call(v, inputs, None, None)
+            self.call(v, inputs, None)
         }
     }
 
     pub fn call(&self, llfn: ValueRef, args: &[ValueRef],
-                bundle: Option<&OperandBundleDef>,
-                attributes: Option<AttrBuilder>) -> ValueRef {
+                bundle: Option<&OperandBundleDef>) -> ValueRef {
         self.count_insn("call");
 
-        debug!("Call {} with args ({})",
-               self.ccx.tn().val_to_string(llfn),
+        debug!("Call {:?} with args ({})",
+               Value(llfn),
                args.iter()
-                   .map(|&v| self.ccx.tn().val_to_string(v))
+                   .map(|&v| format!("{:?}", Value(v)))
                    .collect::<Vec<String>>()
                    .join(", "));
 
@@ -838,11 +873,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             if expected_ty != actual_ty {
                 self.ccx.sess().bug(
                     &format!(
-                        "Type mismatch in function call of {}.  Expected {} for param {}, got {}",
-                        self.ccx.tn().val_to_string(llfn),
-                        self.ccx.tn().type_to_string(expected_ty),
-                        i,
-                        self.ccx.tn().type_to_string(actual_ty)));
+                        "Type mismatch in function call of {:?}. \
+                         Expected {:?} for param {}, got {:?}",
+                        Value(llfn),
+                        expected_ty, i, actual_ty));
 
             }
         }
@@ -850,26 +884,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(0 as *mut _);
 
         unsafe {
-            let v = llvm::LLVMRustBuildCall(self.llbuilder, llfn, args.as_ptr(),
-                                            args.len() as c_uint, bundle,
-                                            noname());
-            if let Some(a) = attributes {
-                a.apply_callsite(v);
-            }
-            v
+            llvm::LLVMRustBuildCall(self.llbuilder, llfn, args.as_ptr(),
+                                    args.len() as c_uint, bundle, noname())
         }
     }
 
-    pub fn call_with_conv(&self, llfn: ValueRef, args: &[ValueRef],
-                          conv: CallConv,
-                          bundle: Option<&OperandBundleDef>,
-                          attributes: Option<AttrBuilder>) -> ValueRef {
-        self.count_insn("callwithconv");
-        let v = self.call(llfn, args, bundle, attributes);
-        llvm::SetInstructionCallConv(v, conv);
-        v
-    }
-
     pub fn select(&self, cond: ValueRef, then_val: ValueRef, else_val: ValueRef) -> ValueRef {
         self.count_insn("select");
         unsafe {
diff --git a/src/librustc_trans/trans/cabi.rs b/src/librustc_trans/trans/cabi.rs
deleted file mode 100644
index 8c10be44ffd..00000000000
--- a/src/librustc_trans/trans/cabi.rs
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2012-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.
-
-pub use self::ArgKind::*;
-
-use llvm::Attribute;
-use std::option;
-use trans::context::CrateContext;
-use trans::cabi_x86;
-use trans::cabi_x86_64;
-use trans::cabi_x86_win64;
-use trans::cabi_arm;
-use trans::cabi_aarch64;
-use trans::cabi_powerpc;
-use trans::cabi_powerpc64;
-use trans::cabi_mips;
-use trans::cabi_asmjs;
-use trans::type_::Type;
-
-#[derive(Clone, Copy, PartialEq)]
-pub enum ArgKind {
-    /// Pass the argument directly using the normal converted
-    /// LLVM type or by coercing to another specified type
-    Direct,
-    /// Pass the argument indirectly via a hidden pointer
-    Indirect,
-    /// Ignore the argument (useful for empty struct)
-    Ignore,
-}
-
-/// Information about how a specific C type
-/// should be passed to or returned from a function
-///
-/// This is borrowed from clang's ABIInfo.h
-#[derive(Clone, Copy)]
-pub struct ArgType {
-    pub kind: ArgKind,
-    /// Original LLVM type
-    pub ty: Type,
-    /// Coerced LLVM Type
-    pub cast: option::Option<Type>,
-    /// Dummy argument, which is emitted before the real argument
-    pub pad: option::Option<Type>,
-    /// LLVM attribute of argument
-    pub attr: option::Option<Attribute>
-}
-
-impl ArgType {
-    pub fn direct(ty: Type, cast: option::Option<Type>,
-                            pad: option::Option<Type>,
-                            attr: option::Option<Attribute>) -> ArgType {
-        ArgType {
-            kind: Direct,
-            ty: ty,
-            cast: cast,
-            pad: pad,
-            attr: attr
-        }
-    }
-
-    pub fn indirect(ty: Type, attr: option::Option<Attribute>) -> ArgType {
-        ArgType {
-            kind: Indirect,
-            ty: ty,
-            cast: option::Option::None,
-            pad: option::Option::None,
-            attr: attr
-        }
-    }
-
-    pub fn ignore(ty: Type) -> ArgType {
-        ArgType {
-            kind: Ignore,
-            ty: ty,
-            cast: None,
-            pad: None,
-            attr: None,
-        }
-    }
-
-    pub fn is_indirect(&self) -> bool {
-        return self.kind == Indirect;
-    }
-
-    pub fn is_ignore(&self) -> bool {
-        return self.kind == Ignore;
-    }
-}
-
-/// Metadata describing how the arguments to a native function
-/// should be passed in order to respect the native ABI.
-///
-/// I will do my best to describe this structure, but these
-/// comments are reverse-engineered and may be inaccurate. -NDM
-pub struct FnType {
-    /// The LLVM types of each argument.
-    pub arg_tys: Vec<ArgType> ,
-
-    /// LLVM return type.
-    pub ret_ty: ArgType,
-}
-
-pub fn compute_abi_info(ccx: &CrateContext,
-                        atys: &[Type],
-                        rty: Type,
-                        ret_def: bool) -> FnType {
-    match &ccx.sess().target.target.arch[..] {
-        "x86" => cabi_x86::compute_abi_info(ccx, atys, rty, ret_def),
-        "x86_64" => if ccx.sess().target.target.options.is_like_windows {
-            cabi_x86_win64::compute_abi_info(ccx, atys, rty, ret_def)
-        } else {
-            cabi_x86_64::compute_abi_info(ccx, atys, rty, ret_def)
-        },
-        "aarch64" => cabi_aarch64::compute_abi_info(ccx, atys, rty, ret_def),
-        "arm" => {
-            let flavor = if ccx.sess().target.target.target_os == "ios" {
-                cabi_arm::Flavor::Ios
-            } else {
-                cabi_arm::Flavor::General
-            };
-            cabi_arm::compute_abi_info(ccx, atys, rty, ret_def, flavor)
-        },
-        "mips" => cabi_mips::compute_abi_info(ccx, atys, rty, ret_def),
-        "powerpc" => cabi_powerpc::compute_abi_info(ccx, atys, rty, ret_def),
-        "powerpc64" => cabi_powerpc64::compute_abi_info(ccx, atys, rty, ret_def),
-        "asmjs" => cabi_asmjs::compute_abi_info(ccx, atys, rty, ret_def),
-        a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a)
-                              ),
-    }
-}
diff --git a/src/librustc_trans/trans/cabi_aarch64.rs b/src/librustc_trans/trans/cabi_aarch64.rs
index f2434ceee2b..6eea8d472ba 100644
--- a/src/librustc_trans/trans/cabi_aarch64.rs
+++ b/src/librustc_trans/trans/cabi_aarch64.rs
@@ -10,8 +10,8 @@
 
 #![allow(non_upper_case_globals)]
 
-use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector, Attribute};
-use trans::cabi::{FnType, ArgType};
+use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
+use trans::abi::{FnType, ArgType};
 use trans::context::CrateContext;
 use trans::type_::Type;
 
@@ -161,16 +161,15 @@ fn is_homogenous_aggregate_ty(ty: Type) -> Option<(Type, u64)> {
     })
 }
 
-fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType {
-    if is_reg_ty(ty) {
-        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        return ArgType::direct(ty, None, None, attr);
+fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
+    if is_reg_ty(ret.ty) {
+        return;
     }
-    if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ty) {
-        let llty = Type::array(&base_ty, members);
-        return ArgType::direct(ty, Some(llty), None, None);
+    if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ret.ty) {
+        ret.cast = Some(Type::array(&base_ty, members));
+        return;
     }
-    let size = ty_size(ty);
+    let size = ty_size(ret.ty);
     if size <= 16 {
         let llty = if size <= 1 {
             Type::i8(ccx)
@@ -183,21 +182,21 @@ fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType {
         } else {
             Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64)
         };
-        return ArgType::direct(ty, Some(llty), None, None);
+        ret.cast = Some(llty);
+        return;
     }
-    ArgType::indirect(ty, Some(Attribute::StructRet))
+    ret.make_indirect(ccx);
 }
 
-fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType {
-    if is_reg_ty(ty) {
-        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        return ArgType::direct(ty, None, None, attr);
+fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
+    if is_reg_ty(arg.ty) {
+        return;
     }
-    if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ty) {
-        let llty = Type::array(&base_ty, members);
-        return ArgType::direct(ty, Some(llty), None, None);
+    if let Some((base_ty, members)) = is_homogenous_aggregate_ty(arg.ty) {
+        arg.cast = Some(Type::array(&base_ty, members));
+        return;
     }
-    let size = ty_size(ty);
+    let size = ty_size(arg.ty);
     if size <= 16 {
         let llty = if size == 0 {
             Type::array(&Type::i64(ccx), 0)
@@ -212,9 +211,10 @@ fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType {
         } else {
             Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64)
         };
-        return ArgType::direct(ty, Some(llty), None, None);
+        arg.cast = Some(llty);
+        return;
     }
-    ArgType::indirect(ty, None)
+    arg.make_indirect(ccx);
 }
 
 fn is_reg_ty(ty: Type) -> bool {
@@ -228,24 +228,13 @@ fn is_reg_ty(ty: Type) -> bool {
     }
 }
 
-pub fn compute_abi_info(ccx: &CrateContext,
-                        atys: &[Type],
-                        rty: Type,
-                        ret_def: bool) -> FnType {
-    let mut arg_tys = Vec::new();
-    for &aty in atys {
-        let ty = classify_arg_ty(ccx, aty);
-        arg_tys.push(ty);
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+    if !fty.ret.is_ignore() {
+        classify_ret_ty(ccx, &mut fty.ret);
     }
 
-    let ret_ty = if ret_def {
-        classify_ret_ty(ccx, rty)
-    } else {
-        ArgType::direct(Type::void(ccx), None, None, None)
-    };
-
-    return FnType {
-        arg_tys: arg_tys,
-        ret_ty: ret_ty,
-    };
+    for arg in &mut fty.args {
+        if arg.is_ignore() { continue; }
+        classify_arg_ty(ccx, arg);
+    }
 }
diff --git a/src/librustc_trans/trans/cabi_arm.rs b/src/librustc_trans/trans/cabi_arm.rs
index c5116e73804..14ca77836c2 100644
--- a/src/librustc_trans/trans/cabi_arm.rs
+++ b/src/librustc_trans/trans/cabi_arm.rs
@@ -10,8 +10,8 @@
 
 #![allow(non_upper_case_globals)]
 
-use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector, Attribute};
-use trans::cabi::{FnType, ArgType};
+use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
+use trans::abi::{FnType, ArgType};
 use trans::context::CrateContext;
 use trans::type_::Type;
 
@@ -129,12 +129,11 @@ fn ty_size(ty: Type, align_fn: TyAlignFn) -> usize {
     }
 }
 
-fn classify_ret_ty(ccx: &CrateContext, ty: Type, align_fn: TyAlignFn) -> ArgType {
-    if is_reg_ty(ty) {
-        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        return ArgType::direct(ty, None, None, attr);
+fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType, align_fn: TyAlignFn) {
+    if is_reg_ty(ret.ty) {
+        return;
     }
-    let size = ty_size(ty, align_fn);
+    let size = ty_size(ret.ty, align_fn);
     if size <= 4 {
         let llty = if size <= 1 {
             Type::i8(ccx)
@@ -143,24 +142,24 @@ fn classify_ret_ty(ccx: &CrateContext, ty: Type, align_fn: TyAlignFn) -> ArgType
         } else {
             Type::i32(ccx)
         };
-        return ArgType::direct(ty, Some(llty), None, None);
+        ret.cast = Some(llty);
+        return;
     }
-    ArgType::indirect(ty, Some(Attribute::StructRet))
+    ret.make_indirect(ccx);
 }
 
-fn classify_arg_ty(ccx: &CrateContext, ty: Type, align_fn: TyAlignFn) -> ArgType {
-    if is_reg_ty(ty) {
-        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        return ArgType::direct(ty, None, None, attr);
+fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, align_fn: TyAlignFn) {
+    if is_reg_ty(arg.ty) {
+        return;
     }
-    let align = align_fn(ty);
-    let size = ty_size(ty, align_fn);
+    let align = align_fn(arg.ty);
+    let size = ty_size(arg.ty, align_fn);
     let llty = if align <= 4 {
         Type::array(&Type::i32(ccx), ((size + 3) / 4) as u64)
     } else {
         Type::array(&Type::i64(ccx), ((size + 7) / 8) as u64)
     };
-    ArgType::direct(ty, Some(llty), None, None)
+    arg.cast = Some(llty);
 }
 
 fn is_reg_ty(ty: Type) -> bool {
@@ -174,30 +173,18 @@ fn is_reg_ty(ty: Type) -> bool {
     }
 }
 
-pub fn compute_abi_info(ccx: &CrateContext,
-                        atys: &[Type],
-                        rty: Type,
-                        ret_def: bool,
-                        flavor: Flavor) -> FnType {
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType, flavor: Flavor) {
     let align_fn = match flavor {
         Flavor::General => general_ty_align as TyAlignFn,
         Flavor::Ios => ios_ty_align as TyAlignFn,
     };
 
-    let mut arg_tys = Vec::new();
-    for &aty in atys {
-        let ty = classify_arg_ty(ccx, aty, align_fn);
-        arg_tys.push(ty);
+    if !fty.ret.is_ignore() {
+        classify_ret_ty(ccx, &mut fty.ret, align_fn);
     }
 
-    let ret_ty = if ret_def {
-        classify_ret_ty(ccx, rty, align_fn)
-    } else {
-        ArgType::direct(Type::void(ccx), None, None, None)
-    };
-
-    return FnType {
-        arg_tys: arg_tys,
-        ret_ty: ret_ty,
-    };
+    for arg in &mut fty.args {
+        if arg.is_ignore() { continue; }
+        classify_arg_ty(ccx, arg, align_fn);
+    }
 }
diff --git a/src/librustc_trans/trans/cabi_asmjs.rs b/src/librustc_trans/trans/cabi_asmjs.rs
index 3a4a6b9960e..c901442f485 100644
--- a/src/librustc_trans/trans/cabi_asmjs.rs
+++ b/src/librustc_trans/trans/cabi_asmjs.rs
@@ -11,62 +11,45 @@
 #![allow(non_upper_case_globals)]
 
 use llvm::{Struct, Array, Attribute};
-use trans::cabi::{FnType, ArgType};
+use trans::abi::{FnType, ArgType};
 use trans::context::CrateContext;
-use trans::type_::Type;
 
 // Data layout: e-p:32:32-i64:64-v128:32:128-n32-S128
 
 // See the https://github.com/kripken/emscripten-fastcomp-clang repository.
 // The class `EmscriptenABIInfo` in `/lib/CodeGen/TargetInfo.cpp` contains the ABI definitions.
 
-fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType {
-    match ty.kind() {
+fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
+    match ret.ty.kind() {
         Struct => {
-            let field_types = ty.field_types();
+            let field_types = ret.ty.field_types();
             if field_types.len() == 1 {
-                ArgType::direct(ty, Some(field_types[0]), None, None)
+                ret.cast = Some(field_types[0]);
             } else {
-                ArgType::indirect(ty, Some(Attribute::StructRet))
+                ret.make_indirect(ccx);
             }
-        },
+        }
         Array => {
-            ArgType::indirect(ty, Some(Attribute::StructRet))
-        },
-        _ => {
-            let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-            ArgType::direct(ty, None, None, attr)
+            ret.make_indirect(ccx);
         }
+        _ => {}
     }
 }
 
-fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType {
-    if ty.is_aggregate() {
-        ArgType::indirect(ty, Some(Attribute::ByVal))
-    } else {
-        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        ArgType::direct(ty, None, None, attr)
+fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
+    if arg.ty.is_aggregate() {
+        arg.make_indirect(ccx);
+        arg.attrs.set(Attribute::ByVal);
     }
 }
 
-pub fn compute_abi_info(ccx: &CrateContext,
-                        atys: &[Type],
-                        rty: Type,
-                        ret_def: bool) -> FnType {
-    let mut arg_tys = Vec::new();
-    for &aty in atys {
-        let ty = classify_arg_ty(ccx, aty);
-        arg_tys.push(ty);
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+    if !fty.ret.is_ignore() {
+        classify_ret_ty(ccx, &mut fty.ret);
     }
 
-    let ret_ty = if ret_def {
-        classify_ret_ty(ccx, rty)
-    } else {
-        ArgType::direct(Type::void(ccx), None, None, None)
-    };
-
-    return FnType {
-        arg_tys: arg_tys,
-        ret_ty: ret_ty,
-    };
+    for arg in &mut fty.args {
+        if arg.is_ignore() { continue; }
+        classify_arg_ty(ccx, arg);
+    }
 }
diff --git a/src/librustc_trans/trans/cabi_mips.rs b/src/librustc_trans/trans/cabi_mips.rs
index bcffb238f59..0d5e81be921 100644
--- a/src/librustc_trans/trans/cabi_mips.rs
+++ b/src/librustc_trans/trans/cabi_mips.rs
@@ -13,8 +13,8 @@
 use libc::c_uint;
 use std::cmp;
 use llvm;
-use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector, Attribute};
-use trans::cabi::{ArgType, FnType};
+use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
+use trans::abi::{ArgType, FnType};
 use trans::context::CrateContext;
 use trans::type_::Type;
 
@@ -86,34 +86,18 @@ fn ty_size(ty: Type) -> usize {
     }
 }
 
-fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType {
-    if is_reg_ty(ty) {
-        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        ArgType::direct(ty, None, None, attr)
-    } else {
-        ArgType::indirect(ty, Some(Attribute::StructRet))
-    }
-}
-
-fn classify_arg_ty(ccx: &CrateContext, ty: Type, offset: &mut usize) -> ArgType {
+fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut usize) {
     let orig_offset = *offset;
-    let size = ty_size(ty) * 8;
-    let mut align = ty_align(ty);
+    let size = ty_size(arg.ty) * 8;
+    let mut align = ty_align(arg.ty);
 
     align = cmp::min(cmp::max(align, 4), 8);
     *offset = align_up_to(*offset, align);
     *offset += align_up_to(size, align * 8) / 8;
 
-    if is_reg_ty(ty) {
-        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        ArgType::direct(ty, None, None, attr)
-    } else {
-        ArgType::direct(
-            ty,
-            Some(struct_ty(ccx, ty)),
-            padding_ty(ccx, align, orig_offset),
-            None
-        )
+    if !is_reg_ty(arg.ty) {
+        arg.cast = Some(struct_ty(ccx, arg.ty));
+        arg.pad = padding_ty(ccx, align, orig_offset);
     }
 }
 
@@ -161,27 +145,14 @@ fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
     Type::struct_(ccx, &coerce_to_int(ccx, size), false)
 }
 
-pub fn compute_abi_info(ccx: &CrateContext,
-                        atys: &[Type],
-                        rty: Type,
-                        ret_def: bool) -> FnType {
-    let ret_ty = if ret_def {
-        classify_ret_ty(ccx, rty)
-    } else {
-        ArgType::direct(Type::void(ccx), None, None, None)
-    };
-
-    let sret = ret_ty.is_indirect();
-    let mut arg_tys = Vec::new();
-    let mut offset = if sret { 4 } else { 0 };
-
-    for aty in atys {
-        let ty = classify_arg_ty(ccx, *aty, &mut offset);
-        arg_tys.push(ty);
-    };
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+    if !fty.ret.is_ignore() && !is_reg_ty(fty.ret.ty) {
+        fty.ret.make_indirect(ccx);
+    }
 
-    return FnType {
-        arg_tys: arg_tys,
-        ret_ty: ret_ty,
-    };
+    let mut offset = if fty.ret.is_indirect() { 4 } else { 0 };
+    for arg in &mut fty.args {
+        if arg.is_ignore() { continue; }
+        classify_arg_ty(ccx, arg, &mut offset);
+    }
 }
diff --git a/src/librustc_trans/trans/cabi_powerpc.rs b/src/librustc_trans/trans/cabi_powerpc.rs
index 1bcc8fd6bbb..9aa89a04d0a 100644
--- a/src/librustc_trans/trans/cabi_powerpc.rs
+++ b/src/librustc_trans/trans/cabi_powerpc.rs
@@ -10,8 +10,8 @@
 
 use libc::c_uint;
 use llvm;
-use llvm::{Integer, Pointer, Float, Double, Struct, Array, Attribute};
-use trans::cabi::{FnType, ArgType};
+use llvm::{Integer, Pointer, Float, Double, Struct, Array};
+use trans::abi::{FnType, ArgType};
 use trans::context::CrateContext;
 use trans::type_::Type;
 
@@ -82,34 +82,18 @@ fn ty_size(ty: Type) -> usize {
     }
 }
 
-fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType {
-    if is_reg_ty(ty) {
-        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        ArgType::direct(ty, None, None, attr)
-    } else {
-        ArgType::indirect(ty, Some(Attribute::StructRet))
-    }
-}
-
-fn classify_arg_ty(ccx: &CrateContext, ty: Type, offset: &mut usize) -> ArgType {
+fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut usize) {
     let orig_offset = *offset;
-    let size = ty_size(ty) * 8;
-    let mut align = ty_align(ty);
+    let size = ty_size(arg.ty) * 8;
+    let mut align = ty_align(arg.ty);
 
     align = cmp::min(cmp::max(align, 4), 8);
     *offset = align_up_to(*offset, align);
     *offset += align_up_to(size, align * 8) / 8;
 
-    if is_reg_ty(ty) {
-        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        ArgType::direct(ty, None, None, attr)
-    } else {
-        ArgType::direct(
-            ty,
-            Some(struct_ty(ccx, ty)),
-            padding_ty(ccx, align, orig_offset),
-            None
-        )
+    if !is_reg_ty(arg.ty) {
+        arg.cast = Some(struct_ty(ccx, arg.ty));
+        arg.pad = padding_ty(ccx, align, orig_offset);
     }
 }
 
@@ -156,27 +140,14 @@ fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
     Type::struct_(ccx, &coerce_to_int(ccx, size), false)
 }
 
-pub fn compute_abi_info(ccx: &CrateContext,
-                        atys: &[Type],
-                        rty: Type,
-                        ret_def: bool) -> FnType {
-    let ret_ty = if ret_def {
-        classify_ret_ty(ccx, rty)
-    } else {
-        ArgType::direct(Type::void(ccx), None, None, None)
-    };
-
-    let sret = ret_ty.is_indirect();
-    let mut arg_tys = Vec::new();
-    let mut offset = if sret { 4 } else { 0 };
-
-    for aty in atys {
-        let ty = classify_arg_ty(ccx, *aty, &mut offset);
-        arg_tys.push(ty);
-    };
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+    if !fty.ret.is_ignore() && !is_reg_ty(fty.ret.ty) {
+        fty.ret.make_indirect(ccx);
+    }
 
-    return FnType {
-        arg_tys: arg_tys,
-        ret_ty: ret_ty,
-    };
+    let mut offset = if fty.ret.is_indirect() { 4 } else { 0 };
+    for arg in &mut fty.args {
+        if arg.is_ignore() { continue; }
+        classify_arg_ty(ccx, arg, &mut offset);
+    }
 }
diff --git a/src/librustc_trans/trans/cabi_powerpc64.rs b/src/librustc_trans/trans/cabi_powerpc64.rs
index f76bb4f9eeb..340de235732 100644
--- a/src/librustc_trans/trans/cabi_powerpc64.rs
+++ b/src/librustc_trans/trans/cabi_powerpc64.rs
@@ -15,8 +15,8 @@
 // Alignment of 128 bit types is not currently handled, this will
 // need to be fixed when PowerPC vector support is added.
 
-use llvm::{Integer, Pointer, Float, Double, Struct, Array, Attribute};
-use trans::cabi::{FnType, ArgType};
+use llvm::{Integer, Pointer, Float, Double, Struct, Array};
+use trans::abi::{FnType, ArgType};
 use trans::context::CrateContext;
 use trans::type_::Type;
 
@@ -151,22 +151,21 @@ fn is_homogenous_aggregate_ty(ty: Type) -> Option<(Type, u64)> {
     })
 }
 
-fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType {
-    if is_reg_ty(ty) {
-        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        return ArgType::direct(ty, None, None, attr);
+fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
+    if is_reg_ty(ret.ty) {
+        return;
     }
 
     // The PowerPC64 big endian ABI doesn't return aggregates in registers
     if ccx.sess().target.target.target_endian == "big" {
-        return ArgType::indirect(ty, Some(Attribute::StructRet))
+        ret.make_indirect(ccx);
     }
 
-    if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ty) {
-        let llty = Type::array(&base_ty, members);
-        return ArgType::direct(ty, Some(llty), None, None);
+    if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ret.ty) {
+        ret.cast = Some(Type::array(&base_ty, members));
+        return;
     }
-    let size = ty_size(ty);
+    let size = ty_size(ret.ty);
     if size <= 16 {
         let llty = if size <= 1 {
             Type::i8(ccx)
@@ -179,28 +178,24 @@ fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType {
         } else {
             Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64)
         };
-        return ArgType::direct(ty, Some(llty), None, None);
+        ret.cast = Some(llty);
+        return;
     }
 
-    ArgType::indirect(ty, Some(Attribute::StructRet))
+    ret.make_indirect(ccx);
 }
 
-fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType {
-    if is_reg_ty(ty) {
-        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        return ArgType::direct(ty, None, None, attr);
+fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
+    if is_reg_ty(arg.ty) {
+        return;
     }
-    if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ty) {
-        let llty = Type::array(&base_ty, members);
-        return ArgType::direct(ty, Some(llty), None, None);
+
+    if let Some((base_ty, members)) = is_homogenous_aggregate_ty(arg.ty) {
+        arg.cast = Some(Type::array(&base_ty, members));
+        return;
     }
 
-    ArgType::direct(
-        ty,
-        Some(struct_ty(ccx, ty)),
-        None,
-        None
-    )
+    arg.cast = Some(struct_ty(ccx, arg.ty));
 }
 
 fn is_reg_ty(ty: Type) -> bool {
@@ -236,24 +231,13 @@ fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
     Type::struct_(ccx, &coerce_to_long(ccx, size), false)
 }
 
-pub fn compute_abi_info(ccx: &CrateContext,
-                        atys: &[Type],
-                        rty: Type,
-                        ret_def: bool) -> FnType {
-    let ret_ty = if ret_def {
-        classify_ret_ty(ccx, rty)
-    } else {
-        ArgType::direct(Type::void(ccx), None, None, None)
-    };
-
-    let mut arg_tys = Vec::new();
-    for &aty in atys {
-        let ty = classify_arg_ty(ccx, aty);
-        arg_tys.push(ty);
-    };
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+    if !fty.ret.is_ignore() {
+        classify_ret_ty(ccx, &mut fty.ret);
+    }
 
-    return FnType {
-        arg_tys: arg_tys,
-        ret_ty: ret_ty,
-    };
+    for arg in &mut fty.args {
+        if arg.is_ignore() { continue; }
+        classify_arg_ty(ccx, arg);
+    }
 }
diff --git a/src/librustc_trans/trans/cabi_x86.rs b/src/librustc_trans/trans/cabi_x86.rs
index 50a3095dea1..9e6c4d9af42 100644
--- a/src/librustc_trans/trans/cabi_x86.rs
+++ b/src/librustc_trans/trans/cabi_x86.rs
@@ -8,24 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use self::Strategy::*;
 use llvm::*;
-use trans::cabi::{ArgType, FnType};
+use trans::abi::FnType;
 use trans::type_::Type;
 use super::common::*;
 use super::machine::*;
 
-enum Strategy { RetValue(Type), RetPointer }
-pub fn compute_abi_info(ccx: &CrateContext,
-                        atys: &[Type],
-                        rty: Type,
-                        ret_def: bool) -> FnType {
-    let mut arg_tys = Vec::new();
-
-    let ret_ty;
-    if !ret_def {
-        ret_ty = ArgType::direct(Type::void(ccx), None, None, None);
-    } else if rty.kind() == Struct {
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+    if !fty.ret.is_ignore() && fty.ret.ty.kind() == Struct {
         // Returning a structure. Most often, this will use
         // a hidden first argument. On some platforms, though,
         // small structs are returned as integers.
@@ -33,53 +23,25 @@ pub fn compute_abi_info(ccx: &CrateContext,
         // Some links:
         // http://www.angelcode.com/dev/callconv/callconv.html
         // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp
-
         let t = &ccx.sess().target.target;
-        let strategy = if t.options.is_like_osx || t.options.is_like_windows {
-            match llsize_of_alloc(ccx, rty) {
-                1 => RetValue(Type::i8(ccx)),
-                2 => RetValue(Type::i16(ccx)),
-                4 => RetValue(Type::i32(ccx)),
-                8 => RetValue(Type::i64(ccx)),
-                _ => RetPointer
+        if t.options.is_like_osx || t.options.is_like_windows {
+            match llsize_of_alloc(ccx, fty.ret.ty) {
+                1 => fty.ret.cast = Some(Type::i8(ccx)),
+                2 => fty.ret.cast = Some(Type::i16(ccx)),
+                4 => fty.ret.cast = Some(Type::i32(ccx)),
+                8 => fty.ret.cast = Some(Type::i64(ccx)),
+                _ => fty.ret.make_indirect(ccx)
             }
         } else {
-            RetPointer
-        };
-
-        match strategy {
-            RetValue(t) => {
-                ret_ty = ArgType::direct(rty, Some(t), None, None);
-            }
-            RetPointer => {
-                ret_ty = ArgType::indirect(rty, Some(Attribute::StructRet));
-            }
+            fty.ret.make_indirect(ccx);
         }
-    } else {
-        let attr = if rty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        ret_ty = ArgType::direct(rty, None, None, attr);
     }
 
-    for &t in atys {
-        let ty = match t.kind() {
-            Struct => {
-                let size = llsize_of_alloc(ccx, t);
-                if size == 0 {
-                    ArgType::ignore(t)
-                } else {
-                    ArgType::indirect(t, Some(Attribute::ByVal))
-                }
-            }
-            _ => {
-                let attr = if t == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-                ArgType::direct(t, None, None, attr)
-            }
-        };
-        arg_tys.push(ty);
+    for arg in &mut fty.args {
+        if arg.is_ignore() { continue; }
+        if arg.ty.kind() == Struct {
+            arg.make_indirect(ccx);
+            arg.attrs.set(Attribute::ByVal);
+        }
     }
-
-    return FnType {
-        arg_tys: arg_tys,
-        ret_ty: ret_ty,
-    };
 }
diff --git a/src/librustc_trans/trans/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs
index 00d8fdad32d..47d373f9043 100644
--- a/src/librustc_trans/trans/cabi_x86_64.rs
+++ b/src/librustc_trans/trans/cabi_x86_64.rs
@@ -16,7 +16,7 @@ use self::RegClass::*;
 
 use llvm::{Integer, Pointer, Float, Double};
 use llvm::{Struct, Array, Attribute, Vector};
-use trans::cabi::{ArgType, FnType};
+use trans::abi::{ArgType, FnType};
 use trans::context::CrateContext;
 use trans::type_::Type;
 
@@ -383,38 +383,31 @@ fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type {
     }
 }
 
-pub fn compute_abi_info(ccx: &CrateContext,
-                        atys: &[Type],
-                        rty: Type,
-                        ret_def: bool) -> FnType {
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
     fn x86_64_ty<F>(ccx: &CrateContext,
-                    ty: Type,
+                    arg: &mut ArgType,
                     is_mem_cls: F,
-                    ind_attr: Attribute)
-                    -> ArgType where
-        F: FnOnce(&[RegClass]) -> bool,
+                    ind_attr: Option<Attribute>)
+        where F: FnOnce(&[RegClass]) -> bool
     {
-        if !ty.is_reg_ty() {
-            let cls = classify_ty(ty);
+        if !arg.ty.is_reg_ty() {
+            let cls = classify_ty(arg.ty);
             if is_mem_cls(&cls) {
-                ArgType::indirect(ty, Some(ind_attr))
+                arg.make_indirect(ccx);
+                if let Some(attr) = ind_attr {
+                    arg.attrs.set(attr);
+                }
             } else {
-                ArgType::direct(ty,
-                                Some(llreg_ty(ccx, &cls)),
-                                None,
-                                None)
+                arg.cast = Some(llreg_ty(ccx, &cls));
             }
-        } else {
-            let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-            ArgType::direct(ty, None, None, attr)
         }
     }
 
     let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9
     let mut sse_regs = 8; // XMM0-7
 
-    let ret_ty = if ret_def {
-        x86_64_ty(ccx, rty, |cls| {
+    if !fty.ret.is_ignore() {
+        x86_64_ty(ccx, &mut fty.ret, |cls| {
             if cls.is_ret_bysret() {
                 // `sret` parameter thus one less register available
                 int_regs -= 1;
@@ -422,14 +415,12 @@ pub fn compute_abi_info(ccx: &CrateContext,
             } else {
                 false
             }
-        }, Attribute::StructRet)
-    } else {
-        ArgType::direct(Type::void(ccx), None, None, None)
-    };
+        }, None);
+    }
 
-    let mut arg_tys = Vec::new();
-    for t in atys {
-        let ty = x86_64_ty(ccx, *t, |cls| {
+    for arg in &mut fty.args {
+        if arg.is_ignore() { continue; }
+        x86_64_ty(ccx, arg, |cls| {
             let needed_int = cls.iter().filter(|&&c| c == Int).count() as isize;
             let needed_sse = cls.iter().filter(|c| c.is_sse()).count() as isize;
             let in_mem = cls.is_pass_byval() ||
@@ -444,21 +435,15 @@ pub fn compute_abi_info(ccx: &CrateContext,
                 sse_regs -= needed_sse;
             }
             in_mem
-        }, Attribute::ByVal);
-        arg_tys.push(ty);
+        }, Some(Attribute::ByVal));
 
         // An integer, pointer, double or float parameter
         // thus the above closure passed to `x86_64_ty` won't
         // get called.
-        if t.kind() == Integer || t.kind() == Pointer {
-            int_regs -= 1;
-        } else if t.kind() == Double || t.kind() == Float {
-            sse_regs -= 1;
+        match arg.ty.kind() {
+            Integer | Pointer => int_regs -= 1,
+            Double | Float => sse_regs -= 1,
+            _ => {}
         }
     }
-
-    return FnType {
-        arg_tys: arg_tys,
-        ret_ty: ret_ty,
-    };
 }
diff --git a/src/librustc_trans/trans/cabi_x86_win64.rs b/src/librustc_trans/trans/cabi_x86_win64.rs
index 120c8dc0384..43f72c454cf 100644
--- a/src/librustc_trans/trans/cabi_x86_win64.rs
+++ b/src/librustc_trans/trans/cabi_x86_win64.rs
@@ -11,54 +11,29 @@
 use llvm::*;
 use super::common::*;
 use super::machine::*;
-use trans::cabi::{ArgType, FnType};
+use trans::abi::{ArgType, FnType};
 use trans::type_::Type;
 
 // Win64 ABI: http://msdn.microsoft.com/en-us/library/zthk2dkh.aspx
 
-pub fn compute_abi_info(ccx: &CrateContext,
-                          atys: &[Type],
-                          rty: Type,
-                          ret_def: bool) -> FnType {
-    let mut arg_tys = Vec::new();
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+    let fixup = |a: &mut ArgType| {
+        if a.ty.kind() == Struct {
+            match llsize_of_alloc(ccx, a.ty) {
+                1 => a.cast = Some(Type::i8(ccx)),
+                2 => a.cast = Some(Type::i16(ccx)),
+                4 => a.cast = Some(Type::i32(ccx)),
+                8 => a.cast = Some(Type::i64(ccx)),
+                _ => a.make_indirect(ccx)
+            }
+        }
+    };
 
-    let ret_ty;
-    if !ret_def {
-        ret_ty = ArgType::direct(Type::void(ccx), None, None, None);
-    } else if rty.kind() == Struct {
-        ret_ty = match llsize_of_alloc(ccx, rty) {
-            1 => ArgType::direct(rty, Some(Type::i8(ccx)), None, None),
-            2 => ArgType::direct(rty, Some(Type::i16(ccx)), None, None),
-            4 => ArgType::direct(rty, Some(Type::i32(ccx)), None, None),
-            8 => ArgType::direct(rty, Some(Type::i64(ccx)), None, None),
-            _ => ArgType::indirect(rty, Some(Attribute::StructRet))
-        };
-    } else {
-        let attr = if rty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-        ret_ty = ArgType::direct(rty, None, None, attr);
+    if !fty.ret.is_ignore() {
+        fixup(&mut fty.ret);
     }
-
-    for &t in atys {
-        let ty = match t.kind() {
-            Struct => {
-                match llsize_of_alloc(ccx, t) {
-                    1 => ArgType::direct(t, Some(Type::i8(ccx)), None, None),
-                    2 => ArgType::direct(t, Some(Type::i16(ccx)), None, None),
-                    4 => ArgType::direct(t, Some(Type::i32(ccx)), None, None),
-                    8 => ArgType::direct(t, Some(Type::i64(ccx)), None, None),
-                    _ => ArgType::indirect(t, None)
-                }
-            }
-            _ => {
-                let attr = if t == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
-                ArgType::direct(t, None, None, attr)
-            }
-        };
-        arg_tys.push(ty);
+    for arg in &mut fty.args {
+        if arg.is_ignore() { continue; }
+        fixup(arg);
     }
-
-    return FnType {
-        arg_tys: arg_tys,
-        ret_ty: ret_ty,
-    };
 }
diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs
index 05e5ac808d0..17d08cd6c2f 100644
--- a/src/librustc_trans/trans/callee.rs
+++ b/src/librustc_trans/trans/callee.rs
@@ -25,15 +25,19 @@ use middle::def_id::DefId;
 use middle::infer;
 use middle::subst;
 use middle::subst::{Substs};
+use middle::traits;
 use rustc::front::map as hir_map;
+use trans::abi::{Abi, FnType};
 use trans::adt;
+use trans::attributes;
 use trans::base;
 use trans::base::*;
 use trans::build::*;
 use trans::cleanup;
 use trans::cleanup::CleanupMethods;
-use trans::common::{self, Block, Result, NodeIdAndSpan, ExprId, CrateContext,
-                    ExprOrMethodCall, FunctionContext, MethodCallKey};
+use trans::closure;
+use trans::common::{self, Block, Result, CrateContext, FunctionContext};
+use trans::common::{C_uint, C_undef};
 use trans::consts;
 use trans::datum::*;
 use trans::debuginfo::DebugLoc;
@@ -41,37 +45,40 @@ use trans::declare;
 use trans::expr;
 use trans::glue;
 use trans::inline;
-use trans::foreign;
 use trans::intrinsic;
+use trans::machine::{llalign_of_min, llsize_of_store};
 use trans::meth;
-use trans::monomorphize;
+use trans::monomorphize::{self, Instance};
 use trans::type_::Type;
 use trans::type_of;
+use trans::value::Value;
 use trans::Disr;
 use middle::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc_front::hir;
 
-use syntax::abi::Abi;
-use syntax::ast;
 use syntax::codemap::DUMMY_SP;
 use syntax::errors;
 use syntax::ptr::P;
 
-pub enum CalleeData<'tcx> {
+use std::cmp;
+
+#[derive(Debug)]
+pub enum CalleeData {
     /// Constructor for enum variant/tuple-like-struct.
     NamedTupleConstructor(Disr),
 
     /// Function pointer.
     Fn(ValueRef),
 
-    Intrinsic(ast::NodeId, &'tcx subst::Substs<'tcx>),
+    Intrinsic,
 
     /// Trait object found in the vtable at that index.
     Virtual(usize)
 }
 
+#[derive(Debug)]
 pub struct Callee<'tcx> {
-    pub data: CalleeData<'tcx>,
+    pub data: CalleeData,
     pub ty: Ty<'tcx>
 }
 
@@ -96,26 +103,19 @@ impl<'tcx> Callee<'tcx> {
     pub fn method<'blk>(bcx: Block<'blk, 'tcx>,
                         method: ty::MethodCallee<'tcx>) -> Callee<'tcx> {
         let substs = bcx.tcx().mk_substs(bcx.fcx.monomorphize(&method.substs));
-        let ty = bcx.fcx.monomorphize(&method.ty);
-        Callee::def(bcx.ccx(), method.def_id, substs, ty)
+        Callee::def(bcx.ccx(), method.def_id, substs)
     }
 
     /// Function or method definition.
     pub fn def<'a>(ccx: &CrateContext<'a, 'tcx>,
                    def_id: DefId,
-                   substs: &'tcx subst::Substs<'tcx>,
-                   ty: Ty<'tcx>)
+                   substs: &'tcx subst::Substs<'tcx>)
                    -> Callee<'tcx> {
         let tcx = ccx.tcx();
 
         if substs.self_ty().is_some() {
             // Only trait methods can have a Self parameter.
-            let method_item = tcx.impl_or_trait_item(def_id);
-            let trait_id = method_item.container().id();
-            let trait_ref = ty::Binder(substs.to_trait_ref(tcx, trait_id));
-            let vtbl = common::fulfill_obligation(ccx, DUMMY_SP, trait_ref);
-            return meth::callee_for_trait_impl(ccx, def_id, substs,
-                                               trait_id, ty, vtbl);
+            return Callee::trait_method(ccx, def_id, substs);
         }
 
         let maybe_node_id = inline::get_local_instance(ccx, def_id)
@@ -123,34 +123,112 @@ impl<'tcx> Callee<'tcx> {
         let maybe_ast_node = maybe_node_id.and_then(|node_id| {
             tcx.map.find(node_id)
         });
-        match maybe_ast_node {
+
+        let data = match maybe_ast_node {
             Some(hir_map::NodeStructCtor(_)) => {
-                return Callee {
-                    data: NamedTupleConstructor(Disr(0)),
-                    ty: ty
-                };
+                NamedTupleConstructor(Disr(0))
             }
             Some(hir_map::NodeVariant(_)) => {
                 let vinfo = common::inlined_variant_def(ccx, maybe_node_id.unwrap());
-                assert_eq!(vinfo.kind(), ty::VariantKind::Tuple);
+                NamedTupleConstructor(Disr::from(vinfo.disr_val))
+            }
+            Some(hir_map::NodeForeignItem(fi)) if {
+                let abi = tcx.map.get_foreign_abi(fi.id);
+                abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic
+            } => Intrinsic,
 
-                return Callee {
-                    data: NamedTupleConstructor(Disr::from(vinfo.disr_val)),
-                    ty: ty
+            _ => return Callee::ptr(get_fn(ccx, def_id, substs))
+        };
+
+        Callee {
+            data: data,
+            ty: def_ty(tcx, def_id, substs)
+        }
+    }
+
+    /// Trait method, which has to be resolved to an impl method.
+    pub fn trait_method<'a>(ccx: &CrateContext<'a, 'tcx>,
+                            def_id: DefId,
+                            substs: &'tcx subst::Substs<'tcx>)
+                            -> Callee<'tcx> {
+        let tcx = ccx.tcx();
+
+        let method_item = tcx.impl_or_trait_item(def_id);
+        let trait_id = method_item.container().id();
+        let trait_ref = ty::Binder(substs.to_trait_ref(tcx, trait_id));
+        match common::fulfill_obligation(ccx, DUMMY_SP, trait_ref) {
+            traits::VtableImpl(vtable_impl) => {
+                let impl_did = vtable_impl.impl_def_id;
+                let mname = tcx.item_name(def_id);
+                // create a concatenated set of substitutions which includes
+                // those from the impl and those from the method:
+                let impl_substs = vtable_impl.substs.with_method_from(&substs);
+                let substs = tcx.mk_substs(impl_substs);
+                let mth = meth::get_impl_method(tcx, impl_did, substs, mname);
+
+                // Translate the function, bypassing Callee::def.
+                // That is because default methods have the same ID as the
+                // trait method used to look up the impl method that ended
+                // up here, so calling Callee::def would infinitely recurse.
+                Callee::ptr(get_fn(ccx, mth.method.def_id, mth.substs))
+            }
+            traits::VtableClosure(vtable_closure) => {
+                // The substitutions should have no type parameters remaining
+                // after passing through fulfill_obligation
+                let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
+                let llfn = closure::trans_closure_method(ccx,
+                                                         vtable_closure.closure_def_id,
+                                                         vtable_closure.substs,
+                                                         trait_closure_kind);
+
+                let method_ty = def_ty(tcx, def_id, substs);
+                let fn_ptr_ty = match method_ty.sty {
+                    ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)),
+                    _ => unreachable!("expected fn item type, found {}",
+                                      method_ty)
                 };
+                Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
             }
-            Some(hir_map::NodeForeignItem(fi)) => {
-                let abi = tcx.map.get_foreign_abi(fi.id);
-                if abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
-                    return Callee {
-                        data: Intrinsic(fi.id, substs),
-                        ty: ty
-                    };
+            traits::VtableFnPointer(fn_ty) => {
+                let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
+                let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty);
+
+                let method_ty = def_ty(tcx, def_id, substs);
+                let fn_ptr_ty = match method_ty.sty {
+                    ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)),
+                    _ => unreachable!("expected fn item type, found {}",
+                                      method_ty)
+                };
+                Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
+            }
+            traits::VtableObject(ref data) => {
+                Callee {
+                    data: Virtual(traits::get_vtable_index_of_object_method(
+                        tcx, data, def_id)),
+                    ty: def_ty(tcx, def_id, substs)
                 }
             }
-            _ => {}
+            vtable => {
+                unreachable!("resolved vtable bad vtable {:?} in trans", vtable);
+            }
+        }
+    }
+
+    /// Get the abi::FnType for a direct call. Mainly deals with the fact
+    /// that a Virtual call doesn't take the vtable, like its shim does.
+    /// The extra argument types are for variadic (extern "C") functions.
+    pub fn direct_fn_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>,
+                              extra_args: &[Ty<'tcx>]) -> FnType {
+        let abi = self.ty.fn_abi();
+        let sig = ccx.tcx().erase_late_bound_regions(self.ty.fn_sig());
+        let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
+        let mut fn_ty = FnType::unadjusted(ccx, abi, &sig, extra_args);
+        if let Virtual(_) = self.data {
+            // Don't pass the vtable, it's not an argument of the virtual fn.
+            fn_ty.args[1].ignore();
         }
-        Callee::ptr(trans_fn_ref_with_substs(ccx, def_id, Some(ty), substs))
+        fn_ty.adjust_for_abi(ccx, abi, &sig);
+        fn_ty
     }
 
     /// This behemoth of a function translates function calls. Unfortunately, in
@@ -175,51 +253,36 @@ impl<'tcx> Callee<'tcx> {
     /// Turn the callee into a function pointer.
     pub fn reify<'a>(self, ccx: &CrateContext<'a, 'tcx>)
                      -> Datum<'tcx, Rvalue> {
+        let fn_ptr_ty = match self.ty.sty {
+            ty::TyFnDef(_, _, f) => ccx.tcx().mk_ty(ty::TyFnPtr(f)),
+            _ => self.ty
+        };
         match self.data {
             Fn(llfn) => {
-                let fn_ptr_ty = match self.ty.sty {
-                    ty::TyFnDef(_, _, f) => ccx.tcx().mk_ty(ty::TyFnPtr(f)),
-                    _ => self.ty
-                };
                 immediate_rvalue(llfn, fn_ptr_ty)
             }
-            Virtual(idx) => meth::trans_object_shim(ccx, self.ty, idx),
+            Virtual(idx) => {
+                let llfn = meth::trans_object_shim(ccx, self.ty, idx);
+                immediate_rvalue(llfn, fn_ptr_ty)
+            }
             NamedTupleConstructor(_) => match self.ty.sty {
                 ty::TyFnDef(def_id, substs, _) => {
-                    return trans_fn_ref_with_substs(ccx, def_id, Some(self.ty), substs);
+                    return get_fn(ccx, def_id, substs);
                 }
                 _ => unreachable!("expected fn item type, found {}", self.ty)
             },
-            Intrinsic(..) => unreachable!("intrinsic {} getting reified", self.ty)
+            Intrinsic => unreachable!("intrinsic {} getting reified", self.ty)
         }
     }
 }
 
-/// Translates a reference (with id `ref_id`) to the fn/method with id `def_id` into a function
-/// pointer. This may require monomorphization or inlining.
-pub fn trans_fn_ref<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                              def_id: DefId,
-                              node: ExprOrMethodCall,
-                              param_substs: &'tcx subst::Substs<'tcx>)
-                              -> Datum<'tcx, Rvalue> {
-    let _icx = push_ctxt("trans_fn_ref");
-
-    let substs = common::node_id_substs(ccx, node, param_substs);
-    debug!("trans_fn_ref(def_id={:?}, node={:?}, substs={:?})",
-           def_id,
-           node,
-           substs);
-    let ref_ty = match node {
-        ExprId(0) => return trans_fn_ref_with_substs(ccx, def_id, None, substs),
-        ExprId(id) => ccx.tcx().node_id_to_type(id),
-        MethodCallKey(method_call) => {
-            ccx.tcx().tables.borrow().method_map[&method_call].ty
-        }
-    };
-    let ref_ty = monomorphize::apply_param_substs(ccx.tcx(),
-                                                  param_substs,
-                                                  &ref_ty);
-    trans_fn_ref_with_substs(ccx, def_id, Some(ref_ty), substs)
+/// Given a DefId and some Substs, produces the monomorphic item type.
+fn def_ty<'tcx>(tcx: &TyCtxt<'tcx>,
+                def_id: DefId,
+                substs: &'tcx subst::Substs<'tcx>)
+                -> Ty<'tcx> {
+    let ty = tcx.lookup_item_type(def_id).ty;
+    monomorphize::apply_param_substs(tcx, substs, &ty)
 }
 
 /// Translates an adapter that implements the `Fn` trait for a fn
@@ -251,6 +314,21 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
         ty::ClosureKind::Fn | ty::ClosureKind::FnMut => true,
         ty::ClosureKind::FnOnce => false,
     };
+
+    let llfnpointer = match bare_fn_ty.sty {
+        ty::TyFnDef(def_id, substs, _) => {
+            // Function definitions have to be turned into a pointer.
+            let llfn = Callee::def(ccx, def_id, substs).reify(ccx).val;
+            if !is_by_ref {
+                // A by-value fn item is ignored, so the shim has
+                // the same signature as the original function.
+                return llfn;
+            }
+            Some(llfn)
+        }
+        _ => None
+    };
+
     let bare_fn_ty_maybe_ref = if is_by_ref {
         tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), bare_fn_ty)
     } else {
@@ -285,58 +363,48 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
     let sig = tcx.erase_late_bound_regions(sig);
     let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
     let tuple_input_ty = tcx.mk_tup(sig.inputs.to_vec());
+    let sig = ty::FnSig {
+        inputs: vec![bare_fn_ty_maybe_ref,
+                     tuple_input_ty],
+        output: sig.output,
+        variadic: false
+    };
+    let fn_ty = FnType::new(ccx, Abi::RustCall, &sig, &[]);
     let tuple_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy {
         unsafety: hir::Unsafety::Normal,
         abi: Abi::RustCall,
-        sig: ty::Binder(ty::FnSig {
-            inputs: vec![bare_fn_ty_maybe_ref,
-                         tuple_input_ty],
-            output: sig.output,
-            variadic: false
-        })
+        sig: ty::Binder(sig)
     });
     debug!("tuple_fn_ty: {:?}", tuple_fn_ty);
 
     //
     let function_name = link::mangle_internal_name_by_type_and_seq(ccx, bare_fn_ty,
                                                                    "fn_pointer_shim");
-    let llfn = declare::declare_internal_rust_fn(ccx, &function_name[..], tuple_fn_ty);
+    let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty);
 
     //
     let empty_substs = tcx.mk_substs(Substs::trans_empty());
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
     block_arena = TypedArena::new();
-    fcx = new_fn_ctxt(ccx,
-                      llfn,
-                      ast::DUMMY_NODE_ID,
-                      false,
-                      sig.output,
-                      empty_substs,
-                      None,
-                      &block_arena);
-    let mut bcx = init_function(&fcx, false, sig.output);
+    fcx = FunctionContext::new(ccx, llfn, fn_ty, None, empty_substs, &block_arena);
+    let mut bcx = fcx.init(false, None);
 
     let llargs = get_params(fcx.llfn);
 
-    let self_idx = fcx.arg_offset();
-    let llfnpointer = match bare_fn_ty.sty {
-        ty::TyFnDef(def_id, substs, _) => {
-            // Function definitions have to be turned into a pointer.
-            Callee::def(ccx, def_id, substs, bare_fn_ty).reify(ccx).val
-        }
-
+    let self_idx = fcx.fn_ty.ret.is_indirect() as usize;
+    let llfnpointer = llfnpointer.unwrap_or_else(|| {
         // the first argument (`self`) will be ptr to the fn pointer
-        _ => if is_by_ref {
+        if is_by_ref {
             Load(bcx, llargs[self_idx])
         } else {
             llargs[self_idx]
         }
-    };
+    });
 
     assert!(!fcx.needs_ret_allocas);
 
     let dest = fcx.llretslotptr.get().map(|_|
-        expr::SaveIn(fcx.get_ret_slot(bcx, sig.output, "ret_slot"))
+        expr::SaveIn(fcx.get_ret_slot(bcx, "ret_slot"))
     );
 
     let callee = Callee {
@@ -345,7 +413,7 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
     };
     bcx = callee.call(bcx, DebugLoc::None, ArgVals(&llargs[(self_idx + 1)..]), dest).bcx;
 
-    finish_fn(&fcx, bcx, sig.output, DebugLoc::None);
+    fcx.finish(bcx, DebugLoc::None);
 
     ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty_maybe_ref, llfn);
 
@@ -359,25 +427,14 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
 ///
 /// - `ccx`: the crate context
 /// - `def_id`: def id of the fn or method item being referenced
-/// - `node`: node id of the reference to the fn/method, if applicable.
-///   This parameter may be zero; but, if so, the resulting value may not
-///   have the right type, so it must be cast before being used.
-/// - `ref_ty`: monotype of the reference to the fn/method, if applicable.
-///   This parameter may be None; but, if so, the resulting value may not
-///   have the right type, so it must be cast before being used.
 /// - `substs`: values for each of the fn/method's parameters
-pub fn trans_fn_ref_with_substs<'a, 'tcx>(
-    ccx: &CrateContext<'a, 'tcx>,
-    def_id: DefId,
-    ref_ty: Option<Ty<'tcx>>,
-    substs: &'tcx subst::Substs<'tcx>)
-    -> Datum<'tcx, Rvalue>
-{
-    let _icx = push_ctxt("trans_fn_ref_with_substs");
+fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                    def_id: DefId,
+                    substs: &'tcx subst::Substs<'tcx>)
+                    -> Datum<'tcx, Rvalue> {
     let tcx = ccx.tcx();
 
-    debug!("trans_fn_ref_with_substs(def_id={:?}, ref_ty={:?}, substs={:?})",
-           def_id, ref_ty, substs);
+    debug!("get_fn(def_id={:?}, substs={:?})", def_id, substs);
 
     assert!(!substs.types.needs_infer());
     assert!(!substs.types.has_escaping_regions());
@@ -407,7 +464,7 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>(
     let must_monomorphise =
         !substs.types.is_empty() || is_named_tuple_constructor(tcx, def_id);
 
-    debug!("trans_fn_ref_with_substs({:?}) must_monomorphise: {}",
+    debug!("get_fn({:?}) must_monomorphise: {}",
            def_id, must_monomorphise);
 
     // Create a monomorphic version of generic functions
@@ -416,9 +473,7 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>(
         assert_eq!(def_id.krate, LOCAL_CRATE);
 
         let substs = tcx.mk_substs(substs.clone().erase_regions());
-        let (mut val, fn_ty, must_cast) =
-            monomorphize::monomorphic_fn(ccx, def_id, substs);
-        let fn_ty = ref_ty.unwrap_or(fn_ty);
+        let (val, fn_ty) = monomorphize::monomorphic_fn(ccx, def_id, substs);
         let fn_ptr_ty = match fn_ty.sty {
             ty::TyFnDef(_, _, fty) => {
                 // Create a fn pointer with the substituted signature.
@@ -426,36 +481,72 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>(
             }
             _ => unreachable!("expected fn item type, found {}", fn_ty)
         };
-        if must_cast && ref_ty.is_some() {
-            let llptrty = type_of::type_of(ccx, fn_ptr_ty);
-            if llptrty != common::val_ty(val) {
-                val = consts::ptrcast(val, llptrty);
-            }
-        }
+        assert_eq!(type_of::type_of(ccx, fn_ptr_ty), common::val_ty(val));
         return immediate_rvalue(val, fn_ptr_ty);
     }
 
     // Find the actual function pointer.
-    let local_node = ccx.tcx().map.as_local_node_id(def_id);
-    let mut datum = if let Some(node_id) = local_node {
-        // Type scheme of the function item (may have type params)
-        let fn_type_scheme = tcx.lookup_item_type(def_id);
-        let fn_type = match fn_type_scheme.ty.sty {
-            ty::TyFnDef(_, _, fty) => {
-                // Create a fn pointer with the normalized signature.
-                tcx.mk_fn_ptr(infer::normalize_associated_type(tcx, fty))
+    let ty = ccx.tcx().lookup_item_type(def_id).ty;
+    let fn_ptr_ty = match ty.sty {
+        ty::TyFnDef(_, _, fty) => {
+            // Create a fn pointer with the normalized signature.
+            tcx.mk_fn_ptr(infer::normalize_associated_type(tcx, fty))
+        }
+        _ => unreachable!("expected fn item type, found {}", ty)
+    };
+
+    let instance = Instance::mono(ccx.tcx(), def_id);
+    if let Some(&llfn) = ccx.instances().borrow().get(&instance) {
+        return immediate_rvalue(llfn, fn_ptr_ty);
+    }
+
+    let attrs;
+    let local_id = ccx.tcx().map.as_local_node_id(def_id);
+    let maybe_node = local_id.and_then(|id| tcx.map.find(id));
+    let (sym, attrs, local_item) = match maybe_node {
+        Some(hir_map::NodeItem(&hir::Item {
+            ref attrs, id, span, node: hir::ItemFn(..), ..
+        })) |
+        Some(hir_map::NodeTraitItem(&hir::TraitItem {
+            ref attrs, id, span, node: hir::MethodTraitItem(_, Some(_)), ..
+        })) |
+        Some(hir_map::NodeImplItem(&hir::ImplItem {
+            ref attrs, id, span, node: hir::ImplItemKind::Method(..), ..
+        })) => {
+            let sym = exported_name(ccx, id, ty, attrs);
+
+            if declare::get_defined_value(ccx, &sym).is_some() {
+                ccx.sess().span_fatal(span,
+                    &format!("symbol `{}` is already defined", sym));
             }
-            _ => unreachable!("expected fn item type, found {}",
-                              fn_type_scheme.ty)
-        };
 
-        // Internal reference.
-        immediate_rvalue(get_item_val(ccx, node_id), fn_type)
-    } else {
-        // External reference.
-        get_extern_fn(ccx, def_id)
+            (sym, &attrs[..], Some(id))
+        }
+
+        Some(hir_map::NodeForeignItem(&hir::ForeignItem {
+            ref attrs, name, node: hir::ForeignItemFn(..), ..
+        })) => {
+            (imported_name(name, attrs).to_string(), &attrs[..], None)
+        }
+
+        None => {
+            attrs = ccx.sess().cstore.item_attrs(def_id);
+            (ccx.sess().cstore.item_symbol(def_id), &attrs[..], None)
+        }
+
+        ref variant => {
+            ccx.sess().bug(&format!("get_fn: unexpected variant: {:?}", variant))
+        }
     };
 
+    let llfn = declare::declare_fn(ccx, &sym, ty);
+    attributes::from_fn_attrs(ccx, attrs, llfn);
+    if let Some(id) = local_item {
+        // FIXME(eddyb) Doubt all extern fn should allow unwinding.
+        attributes::unwind(llfn, true);
+        ccx.item_symbols().borrow_mut().insert(id, sym);
+    }
+
     // This is subtle and surprising, but sometimes we have to bitcast
     // the resulting fn pointer.  The reason has to do with external
     // functions.  If you have two crates that both bind the same C
@@ -479,30 +570,23 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>(
     // This can occur on either a crate-local or crate-external
     // reference. It also occurs when testing libcore and in some
     // other weird situations. Annoying.
-    let llptrty = type_of::type_of(ccx, datum.ty);
-    if common::val_ty(datum.val) != llptrty {
-        debug!("trans_fn_ref_with_substs(): casting pointer!");
-        datum.val = consts::ptrcast(datum.val, llptrty);
+    let llptrty = type_of::type_of(ccx, fn_ptr_ty);
+    let llfn = if common::val_ty(llfn) != llptrty {
+        debug!("get_fn: casting {:?} to {:?}", llfn, llptrty);
+        consts::ptrcast(llfn, llptrty)
     } else {
-        debug!("trans_fn_ref_with_substs(): not casting pointer!");
-    }
+        debug!("get_fn: not casting pointer!");
+        llfn
+    };
+
+    ccx.instances().borrow_mut().insert(instance, llfn);
 
-    datum
+    immediate_rvalue(llfn, fn_ptr_ty)
 }
 
 // ______________________________________________________________________
 // Translating calls
 
-pub fn trans_lang_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                   did: DefId,
-                                   args: &[ValueRef],
-                                   dest: Option<expr::Dest>,
-                                   debug_loc: DebugLoc)
-                                   -> Result<'blk, 'tcx> {
-    let datum = trans_fn_ref(bcx.ccx(), did, ExprId(0), bcx.fcx.param_substs);
-    Callee::ptr(datum).call(bcx, debug_loc, ArgVals(args), dest)
-}
-
 fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                                     debug_loc: DebugLoc,
                                     callee: Callee<'tcx>,
@@ -519,33 +603,29 @@ fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     let fcx = bcx.fcx;
     let ccx = fcx.ccx;
 
-    let (abi, ret_ty) = match callee.ty.sty {
-        ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => {
-            let sig = bcx.tcx().erase_late_bound_regions(&f.sig);
-            let sig = infer::normalize_associated_type(bcx.tcx(), &sig);
-            (f.abi, sig.output)
+    let abi = callee.ty.fn_abi();
+    let sig = callee.ty.fn_sig();
+    let output = bcx.tcx().erase_late_bound_regions(&sig.output());
+    let output = infer::normalize_associated_type(bcx.tcx(), &output);
+
+    let extra_args = match args {
+        ArgExprs(args) if abi != Abi::RustCall => {
+            args[sig.0.inputs.len()..].iter().map(|expr| {
+                common::expr_ty_adjusted(bcx, expr)
+            }).collect()
         }
-        _ => panic!("expected fn item or ptr in Callee::call")
+        _ => vec![]
     };
+    let fn_ty = callee.direct_fn_type(ccx, &extra_args);
 
-    match callee.data {
-        Intrinsic(node, substs) => {
+    let mut callee = match callee.data {
+        Intrinsic => {
             assert!(abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic);
             assert!(dest.is_some());
 
-            let call_info = match debug_loc {
-                DebugLoc::At(id, span) => NodeIdAndSpan { id: id, span: span },
-                DebugLoc::None => {
-                    bcx.sess().bug("No call info for intrinsic call?")
-                }
-            };
-
-            let arg_cleanup_scope = fcx.push_custom_cleanup_scope();
-            return intrinsic::trans_intrinsic_call(bcx, node, callee.ty,
-                                                   arg_cleanup_scope, args,
-                                                   dest.unwrap(),
-                                                   substs,
-                                                   call_info);
+            return intrinsic::trans_intrinsic_call(bcx, callee.ty, &fn_ty,
+                                                   args, dest.unwrap(),
+                                                   debug_loc);
         }
         NamedTupleConstructor(disr) => {
             assert!(dest.is_some());
@@ -557,34 +637,25 @@ fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                                                        dest.unwrap(),
                                                        debug_loc);
         }
-        _ => {}
-    }
-
-    // Intrinsics should not become actual functions.
-    // We trans them in place in `trans_intrinsic_call`
-    assert!(abi != Abi::RustIntrinsic && abi != Abi::PlatformIntrinsic);
-
-    let is_rust_fn = abi == Abi::Rust || abi == Abi::RustCall;
+        f => f
+    };
 
     // Generate a location to store the result. If the user does
     // not care about the result, just make a stack slot.
     let opt_llretslot = dest.and_then(|dest| match dest {
         expr::SaveIn(dst) => Some(dst),
         expr::Ignore => {
-            let ret_ty = match ret_ty {
-                ty::FnConverging(ret_ty) => ret_ty,
-                ty::FnDiverging => ccx.tcx().mk_nil()
+            let needs_drop = || match output {
+                ty::FnConverging(ret_ty) => bcx.fcx.type_needs_drop(ret_ty),
+                ty::FnDiverging => false
             };
-            if !is_rust_fn ||
-              type_of::return_uses_outptr(ccx, ret_ty) ||
-              bcx.fcx.type_needs_drop(ret_ty) {
+            if fn_ty.ret.is_indirect() || fn_ty.ret.cast.is_some() || needs_drop() {
                 // Push the out-pointer if we use an out-pointer for this
                 // return type, otherwise push "undef".
-                if common::type_is_zero_size(ccx, ret_ty) {
-                    let llty = type_of::type_of(ccx, ret_ty);
-                    Some(common::C_undef(llty.ptr_to()))
+                if fn_ty.ret.is_ignore() {
+                    Some(C_undef(fn_ty.ret.original_ty.ptr_to()))
                 } else {
-                    let llresult = alloc_ty(bcx, ret_ty, "__llret");
+                    let llresult = alloca(bcx, fn_ty.ret.original_ty, "__llret");
                     call_lifetime_start(bcx, llresult);
                     Some(llresult)
                 }
@@ -594,128 +665,98 @@ fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         }
     });
 
-    let mut llresult = unsafe {
-        llvm::LLVMGetUndef(Type::nil(ccx).ptr_to().to_ref())
-    };
+    // If there no destination, return must be direct, with no cast.
+    if opt_llretslot.is_none() {
+        assert!(!fn_ty.ret.is_indirect() && fn_ty.ret.cast.is_none());
+    }
 
-    let arg_cleanup_scope = fcx.push_custom_cleanup_scope();
+    let mut llargs = Vec::new();
 
-    // The code below invokes the function, using either the Rust
-    // conventions (if it is a rust fn) or the native conventions
-    // (otherwise).  The important part is that, when all is said
-    // and done, either the return value of the function will have been
-    // written in opt_llretslot (if it is Some) or `llresult` will be
-    // set appropriately (otherwise).
-    if is_rust_fn {
-        let mut llargs = Vec::new();
-
-        if let (ty::FnConverging(ret_ty), Some(mut llretslot)) = (ret_ty, opt_llretslot) {
-            if type_of::return_uses_outptr(ccx, ret_ty) {
-                let llformal_ret_ty = type_of::type_of(ccx, ret_ty).ptr_to();
-                let llret_ty = common::val_ty(llretslot);
-                if llformal_ret_ty != llret_ty {
-                    // this could happen due to e.g. subtyping
-                    debug!("casting actual return type ({}) to match formal ({})",
-                        bcx.llty_str(llret_ty), bcx.llty_str(llformal_ret_ty));
-                    llretslot = PointerCast(bcx, llretslot, llformal_ret_ty);
-                }
-                llargs.push(llretslot);
-            }
+    if fn_ty.ret.is_indirect() {
+        let mut llretslot = opt_llretslot.unwrap();
+        if let Some(ty) = fn_ty.ret.cast {
+            llretslot = PointerCast(bcx, llretslot, ty.ptr_to());
         }
+        llargs.push(llretslot);
+    }
 
-        let arg_start = llargs.len();
-
-        // Push the arguments.
-        bcx = trans_args(bcx,
-                         args,
-                         callee.ty,
-                         &mut llargs,
-                         cleanup::CustomScope(arg_cleanup_scope),
-                         abi);
+    let arg_cleanup_scope = fcx.push_custom_cleanup_scope();
+    bcx = trans_args(bcx, abi, &fn_ty, &mut callee, args, &mut llargs,
+                     cleanup::CustomScope(arg_cleanup_scope));
+    fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean();
 
-        fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean();
+    let llfn = match callee {
+        Fn(f) => f,
+        _ => unreachable!("expected fn pointer callee, found {:?}", callee)
+    };
 
-        let datum = match callee.data {
-            Fn(f) => immediate_rvalue(f, callee.ty),
-            Virtual(idx) => {
-                // The data and vtable pointers were split by trans_arg_datum.
-                let vtable = llargs.remove(arg_start + 1);
-                meth::get_virtual_method(bcx, vtable, idx, callee.ty)
-            }
-            _ => unreachable!()
-        };
+    let (llret, mut bcx) = base::invoke(bcx, llfn, &llargs, debug_loc);
+    if !bcx.unreachable.get() {
+        fn_ty.apply_attrs_callsite(llret);
+    }
 
-        // Invoke the actual rust fn and update bcx/llresult.
-        let (llret, b) = base::invoke(bcx,
-                                      datum.val,
-                                      &llargs[..],
-                                      datum.ty,
-                                      debug_loc);
-        bcx = b;
-        llresult = llret;
-
-        // If the Rust convention for this type is return via
-        // the return value, copy it into llretslot.
-        match (opt_llretslot, ret_ty) {
-            (Some(llretslot), ty::FnConverging(ret_ty)) => {
-                if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) &&
-                    !common::type_is_zero_size(bcx.ccx(), ret_ty)
-                {
-                    store_ty(bcx, llret, llretslot, ret_ty)
-                }
+    // If the function we just called does not use an outpointer,
+    // store the result into the rust outpointer. Cast the outpointer
+    // type to match because some ABIs will use a different type than
+    // the Rust type. e.g., a {u32,u32} struct could be returned as
+    // u64.
+    if !fn_ty.ret.is_ignore() && !fn_ty.ret.is_indirect() {
+        if let Some(llforeign_ret_ty) = fn_ty.ret.cast {
+            let llrust_ret_ty = fn_ty.ret.original_ty;
+            let llretslot = opt_llretslot.unwrap();
+
+            // The actual return type is a struct, but the ABI
+            // adaptation code has cast it into some scalar type.  The
+            // code that follows is the only reliable way I have
+            // found to do a transform like i64 -> {i32,i32}.
+            // Basically we dump the data onto the stack then memcpy it.
+            //
+            // Other approaches I tried:
+            // - Casting rust ret pointer to the foreign type and using Store
+            //   is (a) unsafe if size of foreign type > size of rust type and
+            //   (b) runs afoul of strict aliasing rules, yielding invalid
+            //   assembly under -O (specifically, the store gets removed).
+            // - Truncating foreign type to correct integral type and then
+            //   bitcasting to the struct type yields invalid cast errors.
+            let llscratch = base::alloca(bcx, llforeign_ret_ty, "__cast");
+            base::call_lifetime_start(bcx, llscratch);
+            Store(bcx, llret, llscratch);
+            let llscratch_i8 = PointerCast(bcx, llscratch, Type::i8(ccx).ptr_to());
+            let llretptr_i8 = PointerCast(bcx, llretslot, Type::i8(ccx).ptr_to());
+            let llrust_size = llsize_of_store(ccx, llrust_ret_ty);
+            let llforeign_align = llalign_of_min(ccx, llforeign_ret_ty);
+            let llrust_align = llalign_of_min(ccx, llrust_ret_ty);
+            let llalign = cmp::min(llforeign_align, llrust_align);
+            debug!("llrust_size={}", llrust_size);
+
+            if !bcx.unreachable.get() {
+                base::call_memcpy(&B(bcx), llretptr_i8, llscratch_i8,
+                                  C_uint(ccx, llrust_size), llalign as u32);
             }
-            (_, _) => {}
+            base::call_lifetime_end(bcx, llscratch);
+        } else if let Some(llretslot) = opt_llretslot {
+            base::store_ty(bcx, llret, llretslot, output.unwrap());
         }
-    } else {
-        // Lang items are the only case where dest is None, and
-        // they are always Rust fns.
-        assert!(dest.is_some());
-
-        let mut llargs = Vec::new();
-        let (llfn, arg_tys) = match (callee.data, &args) {
-            (Fn(f), &ArgExprs(a)) => {
-                (f, a.iter().map(|x| common::expr_ty_adjusted(bcx, &x)).collect())
-            }
-            _ => panic!("expected fn ptr and arg exprs.")
-        };
-        bcx = trans_args(bcx,
-                         args,
-                         callee.ty,
-                         &mut llargs,
-                         cleanup::CustomScope(arg_cleanup_scope),
-                         abi);
-        fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean();
-
-        bcx = foreign::trans_native_call(bcx,
-                                         callee.ty,
-                                         llfn,
-                                         opt_llretslot.unwrap(),
-                                         &llargs[..],
-                                         arg_tys,
-                                         debug_loc);
     }
 
     fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_cleanup_scope);
 
     // If the caller doesn't care about the result of this fn call,
     // drop the temporary slot we made.
-    match (dest, opt_llretslot, ret_ty) {
+    match (dest, opt_llretslot, output) {
         (Some(expr::Ignore), Some(llretslot), ty::FnConverging(ret_ty)) => {
             // drop the value if it is not being saved.
-            bcx = glue::drop_ty(bcx,
-                                llretslot,
-                                ret_ty,
-                                debug_loc);
+            bcx = glue::drop_ty(bcx, llretslot, ret_ty, debug_loc);
             call_lifetime_end(bcx, llretslot);
         }
         _ => {}
     }
 
-    if ret_ty == ty::FnDiverging {
+    if output == ty::FnDiverging {
         Unreachable(bcx);
     }
 
-    Result::new(bcx, llresult)
+    Result::new(bcx, llret)
 }
 
 pub enum CallArgs<'a, 'tcx> {
@@ -741,20 +782,19 @@ pub enum CallArgs<'a, 'tcx> {
 fn trans_args_under_call_abi<'blk, 'tcx>(
                              mut bcx: Block<'blk, 'tcx>,
                              arg_exprs: &[P<hir::Expr>],
-                             fn_ty: Ty<'tcx>,
+                             callee: &mut CalleeData,
+                             fn_ty: &FnType,
                              llargs: &mut Vec<ValueRef>,
                              arg_cleanup_scope: cleanup::ScopeId)
                              -> Block<'blk, 'tcx>
 {
-    let sig = bcx.tcx().erase_late_bound_regions(&fn_ty.fn_sig());
-    let sig = infer::normalize_associated_type(bcx.tcx(), &sig);
-    let args = sig.inputs;
+    let mut arg_idx = 0;
 
     // Translate the `self` argument first.
     let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &arg_exprs[0]));
     bcx = trans_arg_datum(bcx,
-                          args[0],
                           arg_datum,
+                          callee, fn_ty, &mut arg_idx,
                           arg_cleanup_scope,
                           llargs);
 
@@ -781,8 +821,8 @@ fn trans_args_under_call_abi<'blk, 'tcx>(
                         adt::trans_field_ptr(bcx, repr_ptr, srcval, Disr(0), i)
                     }).to_expr_datum();
                 bcx = trans_arg_datum(bcx,
-                                      field_type,
                                       arg_datum,
+                                      callee, fn_ty, &mut arg_idx,
                                       arg_cleanup_scope,
                                       llargs);
             }
@@ -796,64 +836,20 @@ fn trans_args_under_call_abi<'blk, 'tcx>(
     bcx
 }
 
-fn trans_overloaded_call_args<'blk, 'tcx>(
-                              mut bcx: Block<'blk, 'tcx>,
-                              arg_exprs: Vec<&hir::Expr>,
-                              fn_ty: Ty<'tcx>,
-                              llargs: &mut Vec<ValueRef>,
-                              arg_cleanup_scope: cleanup::ScopeId)
-                              -> Block<'blk, 'tcx> {
-    // Translate the `self` argument first.
-    let sig = bcx.tcx().erase_late_bound_regions(&fn_ty.fn_sig());
-    let sig = infer::normalize_associated_type(bcx.tcx(), &sig);
-    let arg_tys = sig.inputs;
-
-    let arg_datum = unpack_datum!(bcx, expr::trans(bcx, arg_exprs[0]));
-    bcx = trans_arg_datum(bcx,
-                          arg_tys[0],
-                          arg_datum,
-                          arg_cleanup_scope,
-                          llargs);
-
-    // Now untuple the rest of the arguments.
-    let tuple_type = arg_tys[1];
-    match tuple_type.sty {
-        ty::TyTuple(ref field_types) => {
-            for (i, &field_type) in field_types.iter().enumerate() {
-                let arg_datum =
-                    unpack_datum!(bcx, expr::trans(bcx, arg_exprs[i + 1]));
-                bcx = trans_arg_datum(bcx,
-                                      field_type,
-                                      arg_datum,
-                                      arg_cleanup_scope,
-                                      llargs);
-            }
-        }
-        _ => {
-            bcx.sess().span_bug(arg_exprs[0].span,
-                                "argument to `.call()` wasn't a tuple?!")
-        }
-    };
-
-    bcx
-}
-
-pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>,
+pub fn trans_args<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                                  abi: Abi,
+                                  fn_ty: &FnType,
+                                  callee: &mut CalleeData,
                                   args: CallArgs<'a, 'tcx>,
-                                  fn_ty: Ty<'tcx>,
                                   llargs: &mut Vec<ValueRef>,
-                                  arg_cleanup_scope: cleanup::ScopeId,
-                                  abi: Abi)
+                                  arg_cleanup_scope: cleanup::ScopeId)
                                   -> Block<'blk, 'tcx> {
     debug!("trans_args(abi={})", abi);
 
     let _icx = push_ctxt("trans_args");
-    let sig = cx.tcx().erase_late_bound_regions(&fn_ty.fn_sig());
-    let sig = infer::normalize_associated_type(cx.tcx(), &sig);
-    let arg_tys = sig.inputs;
-    let variadic = sig.variadic;
 
-    let mut bcx = cx;
+    let mut bcx = bcx;
+    let mut arg_idx = 0;
 
     // First we figure out the caller's view of the types of the arguments.
     // This will be needed if this is a generic call, because the callee has
@@ -863,79 +859,90 @@ pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>,
             if abi == Abi::RustCall {
                 // This is only used for direct calls to the `call`,
                 // `call_mut` or `call_once` functions.
-                return trans_args_under_call_abi(cx,
-                                                 arg_exprs,
-                                                 fn_ty,
+                return trans_args_under_call_abi(bcx,
+                                                 arg_exprs, callee, fn_ty,
                                                  llargs,
                                                  arg_cleanup_scope)
             }
 
-            let num_formal_args = arg_tys.len();
-            for (i, arg_expr) in arg_exprs.iter().enumerate() {
-                let arg_ty = if i >= num_formal_args {
-                    assert!(variadic);
-                    common::expr_ty_adjusted(cx, &arg_expr)
-                } else {
-                    arg_tys[i]
-                };
-
+            for arg_expr in arg_exprs {
                 let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &arg_expr));
-                bcx = trans_arg_datum(bcx, arg_ty, arg_datum,
+                bcx = trans_arg_datum(bcx,
+                                      arg_datum,
+                                      callee, fn_ty, &mut arg_idx,
                                       arg_cleanup_scope,
                                       llargs);
             }
         }
         ArgOverloadedCall(arg_exprs) => {
-            return trans_overloaded_call_args(cx,
-                                              arg_exprs,
-                                              fn_ty,
-                                              llargs,
-                                              arg_cleanup_scope)
+            for expr in arg_exprs {
+                let arg_datum =
+                    unpack_datum!(bcx, expr::trans(bcx, expr));
+                bcx = trans_arg_datum(bcx,
+                                      arg_datum,
+                                      callee, fn_ty, &mut arg_idx,
+                                      arg_cleanup_scope,
+                                      llargs);
+            }
         }
         ArgOverloadedOp(lhs, rhs) => {
-            assert!(!variadic);
-
-            bcx = trans_arg_datum(bcx, arg_tys[0], lhs,
+            bcx = trans_arg_datum(bcx, lhs,
+                                  callee, fn_ty, &mut arg_idx,
                                   arg_cleanup_scope,
                                   llargs);
 
             if let Some(rhs) = rhs {
-                assert_eq!(arg_tys.len(), 2);
-                bcx = trans_arg_datum(bcx, arg_tys[1], rhs,
+                bcx = trans_arg_datum(bcx, rhs,
+                                      callee, fn_ty, &mut arg_idx,
                                       arg_cleanup_scope,
                                       llargs);
-            } else {
-                assert_eq!(arg_tys.len(), 1);
             }
         }
         ArgVals(vs) => {
-            llargs.extend_from_slice(vs);
+            match *callee {
+                Virtual(idx) => {
+                    llargs.push(vs[0]);
+
+                    let fn_ptr = meth::get_virtual_method(bcx, vs[1], idx);
+                    let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to();
+                    *callee = Fn(PointerCast(bcx, fn_ptr, llty));
+                    llargs.extend_from_slice(&vs[2..]);
+                }
+                _ => llargs.extend_from_slice(vs)
+            }
         }
     }
 
     bcx
 }
 
-pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                   formal_arg_ty: Ty<'tcx>,
-                                   arg_datum: Datum<'tcx, Expr>,
-                                   arg_cleanup_scope: cleanup::ScopeId,
-                                   llargs: &mut Vec<ValueRef>)
-                                   -> Block<'blk, 'tcx> {
+fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                               arg_datum: Datum<'tcx, Expr>,
+                               callee: &mut CalleeData,
+                               fn_ty: &FnType,
+                               next_idx: &mut usize,
+                               arg_cleanup_scope: cleanup::ScopeId,
+                               llargs: &mut Vec<ValueRef>)
+                               -> Block<'blk, 'tcx> {
     let _icx = push_ctxt("trans_arg_datum");
     let mut bcx = bcx;
-    let ccx = bcx.ccx();
 
-    debug!("trans_arg_datum({:?})",
-           formal_arg_ty);
+    debug!("trans_arg_datum({:?})", arg_datum);
 
-    let arg_datum_ty = arg_datum.ty;
+    let arg = &fn_ty.args[*next_idx];
+    *next_idx += 1;
+
+    // Fill padding with undef value, where applicable.
+    if let Some(ty) = arg.pad {
+        llargs.push(C_undef(ty));
+    }
 
-    debug!("   arg datum: {}", arg_datum.to_string(bcx.ccx()));
+    // Determine whether we want a by-ref datum even if not appropriate.
+    let want_by_ref = arg.is_indirect() || arg.cast.is_some();
 
-    let mut val = if common::type_is_fat_ptr(bcx.tcx(), arg_datum_ty) &&
-                     !bcx.fcx.type_needs_drop(arg_datum_ty) {
-        arg_datum.val
+    let fat_ptr = common::type_is_fat_ptr(bcx.tcx(), arg_datum.ty);
+    let (by_ref, val) = if fat_ptr && !bcx.fcx.type_needs_drop(arg_datum.ty) {
+        (true, arg_datum.val)
     } else {
         // Make this an rvalue, since we are going to be
         // passing ownership.
@@ -944,33 +951,70 @@ pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
         // Now that arg_datum is owned, get it into the appropriate
         // mode (ref vs value).
-        let arg_datum = unpack_datum!(
-            bcx, arg_datum.to_appropriate_datum(bcx));
+        let arg_datum = unpack_datum!(bcx, if want_by_ref {
+            arg_datum.to_ref_datum(bcx)
+        } else {
+            arg_datum.to_appropriate_datum(bcx)
+        });
 
         // Technically, ownership of val passes to the callee.
         // However, we must cleanup should we panic before the
         // callee is actually invoked.
-        arg_datum.add_clean(bcx.fcx, arg_cleanup_scope)
+        (arg_datum.kind.is_by_ref(),
+         arg_datum.add_clean(bcx.fcx, arg_cleanup_scope))
     };
 
-    if type_of::arg_is_indirect(ccx, formal_arg_ty) && formal_arg_ty != arg_datum_ty {
-        // this could happen due to e.g. subtyping
-        let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty);
-        debug!("casting actual type ({}) to match formal ({})",
-               bcx.val_to_string(val), bcx.llty_str(llformal_arg_ty));
-        debug!("Rust types: {:?}; {:?}", arg_datum_ty,
-                                     formal_arg_ty);
-        val = PointerCast(bcx, val, llformal_arg_ty);
+    if arg.is_ignore() {
+        return bcx;
     }
 
-    debug!("--- trans_arg_datum passing {}", bcx.val_to_string(val));
+    debug!("--- trans_arg_datum passing {:?}", Value(val));
 
-    if common::type_is_fat_ptr(bcx.tcx(), formal_arg_ty) {
+    if fat_ptr {
+        // Fat pointers should be passed without any transformations.
+        assert!(!arg.is_indirect() && arg.cast.is_none());
         llargs.push(Load(bcx, expr::get_dataptr(bcx, val)));
-        llargs.push(Load(bcx, expr::get_meta(bcx, val)));
-    } else {
-        llargs.push(val);
+
+        let info_arg = &fn_ty.args[*next_idx];
+        *next_idx += 1;
+        assert!(!info_arg.is_indirect() && info_arg.cast.is_none());
+        let info = Load(bcx, expr::get_meta(bcx, val));
+
+        if let Virtual(idx) = *callee {
+            // We have to grab the fn pointer from the vtable when
+            // handling the first argument, ensure that here.
+            assert_eq!(*next_idx, 2);
+            assert!(info_arg.is_ignore());
+            let fn_ptr = meth::get_virtual_method(bcx, info, idx);
+            let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to();
+            *callee = Fn(PointerCast(bcx, fn_ptr, llty));
+        } else {
+            assert!(!info_arg.is_ignore());
+            llargs.push(info);
+        }
+        return bcx;
+    }
+
+    let mut val = val;
+    if by_ref && !arg.is_indirect() {
+        // Have to load the argument, maybe while casting it.
+        if arg.original_ty == Type::i1(bcx.ccx()) {
+            // We store bools as i8 so we need to truncate to i1.
+            val = LoadRangeAssert(bcx, val, 0, 2, llvm::False);
+            val = Trunc(bcx, val, arg.original_ty);
+        } else if let Some(ty) = arg.cast {
+            val = Load(bcx, PointerCast(bcx, val, ty.ptr_to()));
+            if !bcx.unreachable.get() {
+                let llalign = llalign_of_min(bcx.ccx(), arg.ty);
+                unsafe {
+                    llvm::LLVMSetAlignment(val, llalign);
+                }
+            }
+        } else {
+            val = Load(bcx, val);
+        }
     }
 
+    llargs.push(val);
     bcx
 }
diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs
index 416d951e4b5..89a126e7932 100644
--- a/src/librustc_trans/trans/cleanup.rs
+++ b/src/librustc_trans/trans/cleanup.rs
@@ -129,7 +129,9 @@ use trans::debuginfo::{DebugLoc, ToDebugLoc};
 use trans::glue;
 use middle::region;
 use trans::type_::Type;
+use trans::value::Value;
 use middle::ty::{Ty, TyCtxt};
+
 use std::fmt;
 use syntax::ast;
 
@@ -401,9 +403,8 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
             ptr: val,
         };
 
-        debug!("schedule_lifetime_end({:?}, val={})",
-               cleanup_scope,
-               self.ccx.tn().val_to_string(val));
+        debug!("schedule_lifetime_end({:?}, val={:?})",
+               cleanup_scope, Value(val));
 
         self.schedule_clean(cleanup_scope, drop as CleanupObj);
     }
@@ -426,9 +427,9 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
             drop_hint: drop_hint,
         };
 
-        debug!("schedule_drop_mem({:?}, val={}, ty={:?}) fill_on_drop={} skip_dtor={}",
+        debug!("schedule_drop_mem({:?}, val={:?}, ty={:?}) fill_on_drop={} skip_dtor={}",
                cleanup_scope,
-               self.ccx.tn().val_to_string(val),
+               Value(val),
                ty,
                drop.fill_on_drop,
                drop.skip_dtor);
@@ -454,10 +455,10 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
             drop_hint: drop_hint,
         };
 
-        debug!("schedule_drop_and_fill_mem({:?}, val={}, ty={:?},
+        debug!("schedule_drop_and_fill_mem({:?}, val={:?}, ty={:?},
                 fill_on_drop={}, skip_dtor={}, has_drop_hint={})",
                cleanup_scope,
-               self.ccx.tn().val_to_string(val),
+               Value(val),
                ty,
                drop.fill_on_drop,
                drop.skip_dtor,
@@ -488,9 +489,9 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
             drop_hint: None,
         };
 
-        debug!("schedule_drop_adt_contents({:?}, val={}, ty={:?}) fill_on_drop={} skip_dtor={}",
+        debug!("schedule_drop_adt_contents({:?}, val={:?}, ty={:?}) fill_on_drop={} skip_dtor={}",
                cleanup_scope,
-               self.ccx.tn().val_to_string(val),
+               Value(val),
                ty,
                drop.fill_on_drop,
                drop.skip_dtor);
@@ -514,9 +515,9 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
             drop_hint: None,
         });
 
-        debug!("schedule_drop_immediate({:?}, val={}, ty={:?}) fill_on_drop={} skip_dtor={}",
+        debug!("schedule_drop_immediate({:?}, val={:?}, ty={:?}) fill_on_drop={} skip_dtor={}",
                cleanup_scope,
-               self.ccx.tn().val_to_string(val),
+               Value(val),
                ty,
                drop.fill_on_drop,
                drop.skip_dtor);
@@ -532,10 +533,8 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
                            content_ty: Ty<'tcx>) {
         let drop = box FreeValue { ptr: val, heap: heap, content_ty: content_ty };
 
-        debug!("schedule_free_value({:?}, val={}, heap={:?})",
-               cleanup_scope,
-               self.ccx.tn().val_to_string(val),
-               heap);
+        debug!("schedule_free_value({:?}, val={:?}, heap={:?})",
+               cleanup_scope, Value(val), heap);
 
         self.schedule_clean(cleanup_scope, drop as CleanupObj);
     }
diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs
index 11c03fe7a7d..2036feb31a2 100644
--- a/src/librustc_trans/trans/closure.rs
+++ b/src/librustc_trans/trans/closure.rs
@@ -10,10 +10,11 @@
 
 use arena::TypedArena;
 use back::link::{self, mangle_internal_name_by_path_and_seq};
-use llvm::{ValueRef, get_params};
+use llvm::{ValueRef, get_param, get_params};
 use middle::def_id::DefId;
 use middle::infer;
 use middle::traits::ProjectionMode;
+use trans::abi::{Abi, FnType};
 use trans::adt;
 use trans::attributes;
 use trans::base::*;
@@ -21,44 +22,41 @@ use trans::build::*;
 use trans::callee::{self, ArgVals, Callee};
 use trans::cleanup::{CleanupMethods, CustomScope, ScopeId};
 use trans::common::*;
-use trans::datum::{self, Datum, rvalue_scratch_datum, Rvalue};
+use trans::datum::{ByRef, Datum, lvalue_scratch_datum};
+use trans::datum::{rvalue_scratch_datum, Rvalue};
 use trans::debuginfo::{self, DebugLoc};
 use trans::declare;
 use trans::expr;
-use trans::monomorphize::{MonoId};
-use trans::type_of::*;
+use trans::monomorphize::{Instance};
+use trans::value::Value;
 use trans::Disr;
-use middle::ty;
+use middle::ty::{self, Ty, TyCtxt};
 use session::config::FullDebugInfo;
 
-use syntax::abi::Abi::RustCall;
 use syntax::ast;
-use syntax::attr::{ThinAttributes, ThinAttributesExt};
 
 use rustc_front::hir;
 
+use libc::c_uint;
 
 fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                         closure_def_id: DefId,
                                         arg_scope_id: ScopeId,
-                                        freevars: &[ty::Freevar])
-                                        -> Block<'blk, 'tcx>
-{
+                                        id: ast::NodeId) {
     let _icx = push_ctxt("closure::load_closure_environment");
+    let kind = kind_for_closure(bcx.ccx(), closure_def_id);
+
+    let env_arg = &bcx.fcx.fn_ty.args[0];
+    let mut env_idx = bcx.fcx.fn_ty.ret.is_indirect() as usize;
 
     // Special case for small by-value selfs.
-    let closure_ty = node_id_type(bcx, bcx.fcx.id);
-    let self_type = self_type_for_closure(bcx.ccx(), closure_def_id, closure_ty);
-    let kind = kind_for_closure(bcx.ccx(), closure_def_id);
-    let llenv = if kind == ty::ClosureKind::FnOnce &&
-            !arg_is_indirect(bcx.ccx(), self_type) {
-        let datum = rvalue_scratch_datum(bcx,
-                                         self_type,
-                                         "closure_env");
-        store_ty(bcx, bcx.fcx.llenv.unwrap(), datum.val, self_type);
-        datum.val
+    let llenv = if kind == ty::ClosureKind::FnOnce && !env_arg.is_indirect() {
+        let closure_ty = node_id_type(bcx, id);
+        let llenv = rvalue_scratch_datum(bcx, closure_ty, "closure_env").val;
+        env_arg.store_fn_arg(&bcx.build(), &mut env_idx, llenv);
+        llenv
     } else {
-        bcx.fcx.llenv.unwrap()
+        get_param(bcx.fcx.llfn, env_idx as c_uint)
     };
 
     // Store the pointer to closure data in an alloca for debug info because that's what the
@@ -71,100 +69,120 @@ fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         None
     };
 
-    for (i, freevar) in freevars.iter().enumerate() {
-        let upvar_id = ty::UpvarId { var_id: freevar.def.var_id(),
-                                     closure_expr_id: bcx.fcx.id };
-        let upvar_capture = bcx.tcx().upvar_capture(upvar_id).unwrap();
-        let mut upvar_ptr = StructGEP(bcx, llenv, i);
-        let captured_by_ref = match upvar_capture {
-            ty::UpvarCapture::ByValue => false,
-            ty::UpvarCapture::ByRef(..) => {
-                upvar_ptr = Load(bcx, upvar_ptr);
-                true
+    bcx.tcx().with_freevars(id, |fv| {
+        for (i, freevar) in fv.iter().enumerate() {
+            let upvar_id = ty::UpvarId { var_id: freevar.def.var_id(),
+                                        closure_expr_id: id };
+            let upvar_capture = bcx.tcx().upvar_capture(upvar_id).unwrap();
+            let mut upvar_ptr = StructGEP(bcx, llenv, i);
+            let captured_by_ref = match upvar_capture {
+                ty::UpvarCapture::ByValue => false,
+                ty::UpvarCapture::ByRef(..) => {
+                    upvar_ptr = Load(bcx, upvar_ptr);
+                    true
+                }
+            };
+            let node_id = freevar.def.var_id();
+            bcx.fcx.llupvars.borrow_mut().insert(node_id, upvar_ptr);
+
+            if kind == ty::ClosureKind::FnOnce && !captured_by_ref {
+                let hint = bcx.fcx.lldropflag_hints.borrow().hint_datum(upvar_id.var_id);
+                bcx.fcx.schedule_drop_mem(arg_scope_id,
+                                        upvar_ptr,
+                                        node_id_type(bcx, node_id),
+                                        hint)
             }
-        };
-        let node_id = freevar.def.var_id();
-        bcx.fcx.llupvars.borrow_mut().insert(node_id, upvar_ptr);
-
-        if kind == ty::ClosureKind::FnOnce && !captured_by_ref {
-            let hint = bcx.fcx.lldropflag_hints.borrow().hint_datum(upvar_id.var_id);
-            bcx.fcx.schedule_drop_mem(arg_scope_id,
-                                      upvar_ptr,
-                                      node_id_type(bcx, node_id),
-                                      hint)
-        }
 
-        if let Some(env_pointer_alloca) = env_pointer_alloca {
-            debuginfo::create_captured_var_metadata(
-                bcx,
-                node_id,
-                env_pointer_alloca,
-                i,
-                captured_by_ref,
-                freevar.span);
+            if let Some(env_pointer_alloca) = env_pointer_alloca {
+                debuginfo::create_captured_var_metadata(
+                    bcx,
+                    node_id,
+                    env_pointer_alloca,
+                    i,
+                    captured_by_ref,
+                    freevar.span);
+            }
         }
-    }
-
-    bcx
+    })
 }
 
-pub enum ClosureEnv<'a> {
+pub enum ClosureEnv {
     NotClosure,
-    Closure(DefId, &'a [ty::Freevar]),
+    Closure(DefId, ast::NodeId),
 }
 
-impl<'a> ClosureEnv<'a> {
-    pub fn load<'blk,'tcx>(self, bcx: Block<'blk, 'tcx>, arg_scope: ScopeId)
-                           -> Block<'blk, 'tcx>
-    {
-        match self {
-            ClosureEnv::NotClosure => bcx,
-            ClosureEnv::Closure(def_id, freevars) => {
-                if freevars.is_empty() {
-                    bcx
-                } else {
-                    load_closure_environment(bcx, def_id, arg_scope, freevars)
-                }
-            }
+impl ClosureEnv {
+    pub fn load<'blk,'tcx>(self, bcx: Block<'blk, 'tcx>, arg_scope: ScopeId) {
+        if let ClosureEnv::Closure(def_id, id) = self {
+            load_closure_environment(bcx, def_id, arg_scope, id);
         }
     }
 }
 
+fn get_self_type<'tcx>(tcx: &TyCtxt<'tcx>,
+                       closure_id: DefId,
+                       fn_ty: Ty<'tcx>)
+                       -> Ty<'tcx> {
+    match tcx.closure_kind(closure_id) {
+        ty::ClosureKind::Fn => {
+            tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), fn_ty)
+        }
+        ty::ClosureKind::FnMut => {
+            tcx.mk_mut_ref(tcx.mk_region(ty::ReStatic), fn_ty)
+        }
+        ty::ClosureKind::FnOnce => fn_ty,
+    }
+}
+
 /// Returns the LLVM function declaration for a closure, creating it if
 /// necessary. If the ID does not correspond to a closure ID, returns None.
-pub fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                                   closure_id: DefId,
-                                                   substs: &ty::ClosureSubsts<'tcx>)
-                                                   -> ValueRef {
+fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                                               closure_id: DefId,
+                                               substs: &ty::ClosureSubsts<'tcx>)
+                                               -> ValueRef {
     // Normalize type so differences in regions and typedefs don't cause
     // duplicate declarations
-    let substs = ccx.tcx().erase_regions(substs);
-    let mono_id = MonoId {
+    let tcx = ccx.tcx();
+    let substs = tcx.erase_regions(substs);
+    let instance = Instance {
         def: closure_id,
         params: &substs.func_substs.types
     };
 
-    if let Some(&llfn) = ccx.closure_vals().borrow().get(&mono_id) {
+    if let Some(&llfn) = ccx.instances().borrow().get(&instance) {
         debug!("get_or_create_closure_declaration(): found closure {:?}: {:?}",
-               mono_id, ccx.tn().val_to_string(llfn));
+               instance, Value(llfn));
         return llfn;
     }
 
-    let path = ccx.tcx().def_path(closure_id);
+    let path = tcx.def_path(closure_id);
     let symbol = mangle_internal_name_by_path_and_seq(path, "closure");
 
-    let function_type = ccx.tcx().mk_closure_from_closure_substs(closure_id, Box::new(substs));
-    let llfn = declare::define_internal_rust_fn(ccx, &symbol[..], function_type);
+    // Compute the rust-call form of the closure call method.
+    let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any);
+    let sig = &infcx.closure_type(closure_id, &substs).sig;
+    let sig = tcx.erase_late_bound_regions(sig);
+    let sig = infer::normalize_associated_type(tcx, &sig);
+    let closure_type = tcx.mk_closure_from_closure_substs(closure_id, Box::new(substs));
+    let function_type = tcx.mk_fn_ptr(ty::BareFnTy {
+        unsafety: hir::Unsafety::Normal,
+        abi: Abi::RustCall,
+        sig: ty::Binder(ty::FnSig {
+            inputs: Some(get_self_type(tcx, closure_id, closure_type))
+                        .into_iter().chain(sig.inputs).collect(),
+            output: sig.output,
+            variadic: false
+        })
+    });
+    let llfn = declare::define_internal_fn(ccx, &symbol, function_type);
 
     // set an inline hint for all closures
     attributes::inline(llfn, attributes::InlineAttr::Hint);
 
     debug!("get_or_create_declaration_if_closure(): inserting new \
-            closure {:?} (type {}): {:?}",
-           mono_id,
-           ccx.tn().type_to_string(val_ty(llfn)),
-           ccx.tn().val_to_string(llfn));
-    ccx.closure_vals().borrow_mut().insert(mono_id, llfn);
+            closure {:?}: {:?}",
+           instance, Value(llfn));
+    ccx.instances().borrow_mut().insert(instance, llfn);
 
     llfn
 }
@@ -179,8 +197,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
                                     body: &hir::Block,
                                     id: ast::NodeId,
                                     closure_def_id: DefId, // (*)
-                                    closure_substs: &'tcx ty::ClosureSubsts<'tcx>,
-                                    closure_expr_attrs: &ThinAttributes)
+                                    closure_substs: &ty::ClosureSubsts<'tcx>)
                                     -> Option<Block<'a, 'tcx>>
 {
     // (*) Note that in the case of inlined functions, the `closure_def_id` will be the
@@ -210,22 +227,29 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
     let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables, ProjectionMode::Any);
     let function_type = infcx.closure_type(closure_def_id, closure_substs);
 
-    let freevars: Vec<ty::Freevar> =
-        tcx.with_freevars(id, |fv| fv.iter().cloned().collect());
-
     let sig = tcx.erase_late_bound_regions(&function_type.sig);
     let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
 
+    let closure_type = tcx.mk_closure_from_closure_substs(closure_def_id,
+        Box::new(closure_substs.clone()));
+    let sig = ty::FnSig {
+        inputs: Some(get_self_type(tcx, closure_def_id, closure_type))
+                    .into_iter().chain(sig.inputs).collect(),
+        output: sig.output,
+        variadic: false
+    };
+    let fn_ty = FnType::new(ccx, Abi::RustCall, &sig, &[]);
+
     trans_closure(ccx,
                   decl,
                   body,
                   llfn,
                   param_substs,
+                  closure_def_id,
                   id,
-                  closure_expr_attrs.as_attr_slice(),
-                  sig.output,
-                  function_type.abi,
-                  ClosureEnv::Closure(closure_def_id, &freevars));
+                  fn_ty,
+                  Abi::RustCall,
+                  ClosureEnv::Closure(closure_def_id, id));
 
     // Don't hoist this to the top of the function. It's perfectly legitimate
     // to have a zero-size closure (in which case dest will be `Ignore`) and
@@ -241,21 +265,23 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
     let repr = adt::represent_type(ccx, node_id_type(bcx, id));
 
     // Create the closure.
-    for (i, freevar) in freevars.iter().enumerate() {
-        let datum = expr::trans_local_var(bcx, freevar.def);
-        let upvar_slot_dest = adt::trans_field_ptr(
-            bcx, &repr, adt::MaybeSizedValue::sized(dest_addr), Disr(0), i);
-        let upvar_id = ty::UpvarId { var_id: freevar.def.var_id(),
-                                     closure_expr_id: id };
-        match tcx.upvar_capture(upvar_id).unwrap() {
-            ty::UpvarCapture::ByValue => {
-                bcx = datum.store_to(bcx, upvar_slot_dest);
-            }
-            ty::UpvarCapture::ByRef(..) => {
-                Store(bcx, datum.to_llref(), upvar_slot_dest);
+    tcx.with_freevars(id, |fv| {
+        for (i, freevar) in fv.iter().enumerate() {
+            let datum = expr::trans_var(bcx, freevar.def);
+            let upvar_slot_dest = adt::trans_field_ptr(
+                bcx, &repr, adt::MaybeSizedValue::sized(dest_addr), Disr(0), i);
+            let upvar_id = ty::UpvarId { var_id: freevar.def.var_id(),
+                                        closure_expr_id: id };
+            match tcx.upvar_capture(upvar_id).unwrap() {
+                ty::UpvarCapture::ByValue => {
+                    bcx = datum.store_to(bcx, upvar_slot_dest);
+                }
+                ty::UpvarCapture::ByRef(..) => {
+                    Store(bcx, datum.to_llref(), upvar_slot_dest);
+                }
             }
         }
-    }
+    });
     adt::trans_set_discr(bcx, &repr, dest_addr, Disr(0));
 
     Some(bcx)
@@ -278,11 +304,8 @@ pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
     let tcx = ccx.tcx();
 
     debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \
-           trait_closure_kind={:?}, \
-           llfn={})",
-           llfn_closure_kind,
-           trait_closure_kind,
-           ccx.tn().val_to_string(llfn));
+           trait_closure_kind={:?}, llfn={:?})",
+           llfn_closure_kind, trait_closure_kind, Value(llfn));
 
     match (llfn_closure_kind, trait_closure_kind) {
         (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
@@ -324,10 +347,8 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
     llreffn: ValueRef)
     -> ValueRef
 {
-    debug!("trans_fn_once_adapter_shim(closure_def_id={:?}, substs={:?}, llreffn={})",
-           closure_def_id,
-           substs,
-           ccx.tn().val_to_string(llreffn));
+    debug!("trans_fn_once_adapter_shim(closure_def_id={:?}, substs={:?}, llreffn={:?})",
+           closure_def_id, substs, Value(llreffn));
 
     let tcx = ccx.tcx();
     let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables, ProjectionMode::Any);
@@ -348,56 +369,70 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
     debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}",
            llref_fn_ty);
 
-    let ret_ty = tcx.erase_late_bound_regions(&sig.output());
-    let ret_ty = infer::normalize_associated_type(ccx.tcx(), &ret_ty);
 
     // Make a version of the closure type with the same arguments, but
     // with argument #0 being by value.
-    assert_eq!(abi, RustCall);
+    assert_eq!(abi, Abi::RustCall);
     sig.0.inputs[0] = closure_ty;
+
+    let sig = tcx.erase_late_bound_regions(&sig);
+    let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
+    let fn_ty = FnType::new(ccx, abi, &sig, &[]);
+
     let llonce_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy {
         unsafety: unsafety,
         abi: abi,
-        sig: sig
+        sig: ty::Binder(sig)
     });
 
     // Create the by-value helper.
     let function_name = link::mangle_internal_name_by_type_and_seq(ccx, llonce_fn_ty, "once_shim");
-    let lloncefn = declare::define_internal_rust_fn(ccx, &function_name,
-                                                    llonce_fn_ty);
+    let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
 
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
     block_arena = TypedArena::new();
-    fcx = new_fn_ctxt(ccx,
-                      lloncefn,
-                      ast::DUMMY_NODE_ID,
-                      false,
-                      ret_ty,
-                      substs.func_substs,
-                      None,
-                      &block_arena);
-    let mut bcx = init_function(&fcx, false, ret_ty);
+    fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, substs.func_substs, &block_arena);
+    let mut bcx = fcx.init(false, None);
 
-    let mut llargs = get_params(fcx.llfn);
 
     // the first argument (`self`) will be the (by value) closure env.
     let self_scope = fcx.push_custom_cleanup_scope();
     let self_scope_id = CustomScope(self_scope);
-    let rvalue_mode = datum::appropriate_rvalue_mode(ccx, closure_ty);
-    let self_idx = fcx.arg_offset();
-    let llself = llargs[self_idx];
-    let env_datum = Datum::new(llself, closure_ty, Rvalue::new(rvalue_mode));
-    let env_datum = unpack_datum!(bcx,
-                                  env_datum.to_lvalue_datum_in_scope(bcx, "self",
-                                                                     self_scope_id));
-
-    debug!("trans_fn_once_adapter_shim: env_datum={}",
-           bcx.val_to_string(env_datum.val));
-    llargs[self_idx] = env_datum.val;
+
+    let mut llargs = get_params(fcx.llfn);
+    let mut self_idx = fcx.fn_ty.ret.is_indirect() as usize;
+    let env_arg = &fcx.fn_ty.args[0];
+    let llenv = if env_arg.is_indirect() {
+        Datum::new(llargs[self_idx], closure_ty, Rvalue::new(ByRef))
+            .add_clean(&fcx, self_scope_id)
+    } else {
+        unpack_datum!(bcx, lvalue_scratch_datum(bcx, closure_ty, "self",
+                                                InitAlloca::Dropped,
+                                                self_scope_id, |bcx, llval| {
+            let mut llarg_idx = self_idx;
+            env_arg.store_fn_arg(&bcx.build(), &mut llarg_idx, llval);
+            bcx.fcx.schedule_lifetime_end(self_scope_id, llval);
+            bcx
+        })).val
+    };
+
+    debug!("trans_fn_once_adapter_shim: env={:?}", Value(llenv));
+    // Adjust llargs such that llargs[self_idx..] has the call arguments.
+    // For zero-sized closures that means sneaking in a new argument.
+    if env_arg.is_ignore() {
+        if self_idx > 0 {
+            self_idx -= 1;
+            llargs[self_idx] = llenv;
+        } else {
+            llargs.insert(0, llenv);
+        }
+    } else {
+        llargs[self_idx] = llenv;
+    }
 
     let dest =
         fcx.llretslotptr.get().map(
-            |_| expr::SaveIn(fcx.get_ret_slot(bcx, ret_ty, "ret_slot")));
+            |_| expr::SaveIn(fcx.get_ret_slot(bcx, "ret_slot")));
 
     let callee = Callee {
         data: callee::Fn(llreffn),
@@ -407,7 +442,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
 
     fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope);
 
-    finish_fn(&fcx, bcx, ret_ty, DebugLoc::None);
+    fcx.finish(bcx, DebugLoc::None);
 
     lloncefn
 }
diff --git a/src/librustc_trans/trans/collector.rs b/src/librustc_trans/trans/collector.rs
index cea97c1a1e7..2c996c032ca 100644
--- a/src/librustc_trans/trans/collector.rs
+++ b/src/librustc_trans/trans/collector.rs
@@ -194,10 +194,10 @@ use rustc_front::intravisit as hir_visit;
 use rustc::front::map as hir_map;
 use rustc::middle::def_id::DefId;
 use rustc::middle::lang_items::{ExchangeFreeFnLangItem, ExchangeMallocFnLangItem};
-use rustc::middle::{ty, traits};
+use rustc::middle::traits;
 use rustc::middle::subst::{self, Substs, Subst};
+use rustc::middle::ty::{self, Ty, TypeFoldable};
 use rustc::middle::ty::adjustment::CustomCoerceUnsized;
-use rustc::middle::ty::fold::TypeFoldable;
 use rustc::mir::repr as mir;
 use rustc::mir::visit as mir_visit;
 use rustc::mir::visit::Visitor as MirVisitor;
@@ -213,11 +213,10 @@ use trans::common::{fulfill_obligation, normalize_and_test_predicates,
                     type_is_sized};
 use trans::glue;
 use trans::meth;
-use trans::monomorphize;
+use trans::monomorphize::{self, Instance};
 use util::nodemap::{FnvHashSet, FnvHashMap, DefIdMap};
 
 use std::hash::{Hash, Hasher};
-use std::rc::Rc;
 
 #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
 pub enum TransItemCollectionMode {
@@ -225,13 +224,10 @@ pub enum TransItemCollectionMode {
     Lazy
 }
 
-#[derive(Eq, Clone, Copy, Debug)]
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
 pub enum TransItem<'tcx> {
-    DropGlue(ty::Ty<'tcx>),
-    Fn {
-        def_id: DefId,
-        substs: &'tcx Substs<'tcx>
-    },
+    DropGlue(Ty<'tcx>),
+    Fn(Instance<'tcx>),
     Static(NodeId)
 }
 
@@ -242,35 +238,19 @@ impl<'tcx> Hash for TransItem<'tcx> {
                 0u8.hash(s);
                 t.hash(s);
             },
-            TransItem::Fn { def_id, substs } => {
+            TransItem::Fn(instance) => {
                 1u8.hash(s);
-                def_id.hash(s);
-                (substs as *const Substs<'tcx> as usize).hash(s);
+                instance.def.hash(s);
+                (instance.params as *const _ as usize).hash(s);
             }
             TransItem::Static(node_id) => {
-                3u8.hash(s);
+                2u8.hash(s);
                 node_id.hash(s);
             }
         };
     }
 }
 
-impl<'tcx> PartialEq for TransItem<'tcx> {
-    fn eq(&self, other: &Self) -> bool {
-        match (*self, *other) {
-            (TransItem::DropGlue(t1), TransItem::DropGlue(t2)) => t1 == t2,
-            (TransItem::Fn { def_id: def_id1, substs: substs1 },
-             TransItem::Fn { def_id: def_id2, substs: substs2 }) => {
-                def_id1 == def_id2 && substs1 == substs2
-            },
-            (TransItem::Static(node_id1), TransItem::Static(node_id2)) => {
-                node_id1 == node_id2
-            },
-            _ => false
-        }
-    }
-}
-
 pub fn collect_crate_translation_items<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                                  mode: TransItemCollectionMode)
                                                  -> FnvHashSet<TransItem<'tcx>> {
@@ -282,14 +262,9 @@ pub fn collect_crate_translation_items<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         debug!("Building translation item graph, beginning at roots");
         let mut visited = FnvHashSet();
         let mut recursion_depths = DefIdMap();
-        let mut mir_cache = DefIdMap();
 
         for root in roots {
-            collect_items_rec(ccx,
-                              root,
-                              &mut visited,
-                              &mut recursion_depths,
-                              &mut mir_cache);
+            collect_items_rec(ccx, root, &mut visited, &mut recursion_depths);
         }
 
         visited
@@ -319,27 +294,11 @@ fn collect_roots<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     roots
 }
 
-#[derive(Clone)]
-enum CachedMir<'mir, 'tcx: 'mir> {
-    Ref(&'mir mir::Mir<'tcx>),
-    Owned(Rc<mir::Mir<'tcx>>)
-}
-
-impl<'mir, 'tcx: 'mir> CachedMir<'mir, 'tcx> {
-    fn get_ref<'a>(&'a self) -> &'a mir::Mir<'tcx> {
-        match *self {
-            CachedMir::Ref(r) => r,
-            CachedMir::Owned(ref rc) => &rc,
-        }
-    }
-}
-
 // Collect all monomorphized translation items reachable from `starting_point`
 fn collect_items_rec<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
                                    starting_point: TransItem<'tcx>,
                                    visited: &mut FnvHashSet<TransItem<'tcx>>,
-                                   recursion_depths: &mut DefIdMap<usize>,
-                                   mir_cache: &mut DefIdMap<CachedMir<'a, 'tcx>>) {
+                                   recursion_depths: &mut DefIdMap<usize>) {
     if !visited.insert(starting_point.clone()) {
         // We've been here already, no need to search again.
         return;
@@ -357,29 +316,33 @@ fn collect_items_rec<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
         TransItem::Static(_) => {
             recursion_depth_reset = None;
         }
-        TransItem::Fn { def_id, substs: ref param_substs } => {
+        TransItem::Fn(instance) => {
             // Keep track of the monomorphization recursion depth
             recursion_depth_reset = Some(check_recursion_limit(ccx,
-                                                               def_id,
+                                                               instance,
                                                                recursion_depths));
 
             // Scan the MIR in order to find function calls, closures, and
             // drop-glue
-            let mir = load_mir(ccx, def_id, mir_cache);
+            let mir = errors::expect(ccx.sess().diagnostic(), ccx.get_mir(instance.def),
+                || format!("Could not find MIR for function: {}", instance));
 
             let mut visitor = MirNeighborCollector {
                 ccx: ccx,
-                mir: mir.get_ref(),
+                mir: &mir,
                 output: &mut neighbors,
-                param_substs: param_substs
+                param_substs: ccx.tcx().mk_substs(Substs {
+                    types: instance.params.clone(),
+                    regions: subst::ErasedRegions
+                })
             };
 
-            visitor.visit_mir(mir.get_ref());
+            visitor.visit_mir(&mir);
         }
     }
 
     for neighbour in neighbors {
-        collect_items_rec(ccx, neighbour, visited, recursion_depths, mir_cache);
+        collect_items_rec(ccx, neighbour, visited, recursion_depths);
     }
 
     if let Some((def_id, depth)) = recursion_depth_reset {
@@ -389,42 +352,11 @@ fn collect_items_rec<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
     debug!("END collect_items_rec({})", starting_point.to_string(ccx));
 }
 
-fn load_mir<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
-                          def_id: DefId,
-                          mir_cache: &mut DefIdMap<CachedMir<'a, 'tcx>>)
-                          -> CachedMir<'a, 'tcx> {
-    let mir_not_found_error_message = || {
-        format!("Could not find MIR for function: {}",
-                ccx.tcx().item_path_str(def_id))
-    };
-
-    if def_id.is_local() {
-        let node_id = ccx.tcx().map.as_local_node_id(def_id).unwrap();
-        let mir_opt = ccx.mir_map().map.get(&node_id);
-        let mir = errors::expect(ccx.sess().diagnostic(),
-                             mir_opt,
-                             mir_not_found_error_message);
-        CachedMir::Ref(mir)
-    } else {
-        if let Some(mir) = mir_cache.get(&def_id) {
-            return mir.clone();
-        }
-
-        let mir_opt = ccx.sess().cstore.maybe_get_item_mir(ccx.tcx(), def_id);
-        let mir = errors::expect(ccx.sess().diagnostic(),
-                                 mir_opt,
-                                 mir_not_found_error_message);
-        let cached = CachedMir::Owned(Rc::new(mir));
-        mir_cache.insert(def_id, cached.clone());
-        cached
-    }
-}
-
 fn check_recursion_limit<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
-                                       def_id: DefId,
+                                       instance: Instance<'tcx>,
                                        recursion_depths: &mut DefIdMap<usize>)
                                        -> (DefId, usize) {
-    let recursion_depth = recursion_depths.get(&def_id)
+    let recursion_depth = recursion_depths.get(&instance.def)
                                           .map(|x| *x)
                                           .unwrap_or(0);
     debug!(" => recursion depth={}", recursion_depth);
@@ -433,20 +365,18 @@ fn check_recursion_limit<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
     // more than the recursion limit is assumed to be causing an
     // infinite expansion.
     if recursion_depth > ccx.sess().recursion_limit.get() {
-        if let Some(node_id) = ccx.tcx().map.as_local_node_id(def_id) {
-            ccx.sess().span_fatal(ccx.tcx().map.span(node_id),
-                "reached the recursion limit during monomorphization");
+        let error = format!("reached the recursion limit while instantiating `{}`",
+                            instance);
+        if let Some(node_id) = ccx.tcx().map.as_local_node_id(instance.def) {
+            ccx.sess().span_fatal(ccx.tcx().map.span(node_id), &error);
         } else {
-            let error = format!("reached the recursion limit during \
-                                monomorphization of '{}'",
-                                ccx.tcx().item_path_str(def_id));
-            ccx.sess().fatal(&error[..]);
+            ccx.sess().fatal(&error);
         }
     }
 
-    recursion_depths.insert(def_id, recursion_depth + 1);
+    recursion_depths.insert(instance.def, recursion_depth + 1);
 
-    (def_id, recursion_depth)
+    (instance.def, recursion_depth)
 }
 
 struct MirNeighborCollector<'a, 'tcx: 'a> {
@@ -750,7 +680,7 @@ fn do_static_dispatch<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 param_substs: &'tcx Substs<'tcx>)
                                 -> Option<(DefId, &'tcx Substs<'tcx>)> {
     debug!("do_static_dispatch(fn_def_id={}, fn_substs={:?}, param_substs={:?})",
-           def_id_to_string(ccx, fn_def_id, None),
+           def_id_to_string(ccx, fn_def_id),
            fn_substs,
            param_substs);
 
@@ -798,8 +728,8 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                             trait_id={}, \
                                             callee_substs={:?}, \
                                             param_substs={:?}",
-           def_id_to_string(ccx, trait_method.def_id, None),
-           def_id_to_string(ccx, trait_id, None),
+           def_id_to_string(ccx, trait_method.def_id),
+           def_id_to_string(ccx, trait_id),
            callee_substs,
            param_substs);
 
@@ -933,7 +863,7 @@ fn create_fn_trans_item<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                   -> TransItem<'tcx>
 {
     debug!("create_fn_trans_item(def_id={}, fn_substs={:?}, param_substs={:?})",
-            def_id_to_string(ccx, def_id, None),
+            def_id_to_string(ccx, def_id),
             fn_substs,
             param_substs);
 
@@ -945,10 +875,10 @@ fn create_fn_trans_item<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                                            fn_substs);
     let concrete_substs = ccx.tcx().erase_regions(&concrete_substs);
 
-    let trans_item = TransItem::Fn {
-        def_id: def_id,
-        substs: ccx.tcx().mk_substs(concrete_substs),
-    };
+    let trans_item = TransItem::Fn(Instance {
+        def: def_id,
+        params: &ccx.tcx().mk_substs(concrete_substs).types,
+    });
 
     return trans_item;
 }
@@ -1048,8 +978,7 @@ impl<'b, 'a, 'v> hir_visit::Visitor<'v> for RootCollector<'b, 'a, 'v> {
                     if self.mode == TransItemCollectionMode::Eager {
                         debug!("RootCollector: ADT drop-glue for {}",
                                def_id_to_string(self.ccx,
-                                                self.ccx.tcx().map.local_def_id(item.id),
-                                                None));
+                                                self.ccx.tcx().map.local_def_id(item.id)));
 
                         let ty = glue::get_drop_glue_type(self.ccx, ty);
                         self.output.push(TransItem::DropGlue(ty));
@@ -1059,8 +988,7 @@ impl<'b, 'a, 'v> hir_visit::Visitor<'v> for RootCollector<'b, 'a, 'v> {
             hir::ItemStatic(..) => {
                 debug!("RootCollector: ItemStatic({})",
                        def_id_to_string(self.ccx,
-                                        self.ccx.tcx().map.local_def_id(item.id),
-                                        None));
+                                        self.ccx.tcx().map.local_def_id(item.id)));
                 self.output.push(TransItem::Static(item.id));
             }
             hir::ItemFn(_, _, constness, _, ref generics, _) => {
@@ -1069,12 +997,10 @@ impl<'b, 'a, 'v> hir_visit::Visitor<'v> for RootCollector<'b, 'a, 'v> {
                     let def_id = self.ccx.tcx().map.local_def_id(item.id);
 
                     debug!("RootCollector: ItemFn({})",
-                           def_id_to_string(self.ccx, def_id, None));
+                           def_id_to_string(self.ccx, def_id));
 
-                    self.output.push(TransItem::Fn {
-                        def_id: def_id,
-                        substs: self.trans_empty_substs
-                    });
+                    let instance = Instance::mono(self.ccx.tcx(), def_id);
+                    self.output.push(TransItem::Fn(instance));
                 }
             }
         }
@@ -1108,12 +1034,10 @@ impl<'b, 'a, 'v> hir_visit::Visitor<'v> for RootCollector<'b, 'a, 'v> {
                     let def_id = self.ccx.tcx().map.local_def_id(ii.id);
 
                     debug!("RootCollector: MethodImplItem({})",
-                           def_id_to_string(self.ccx, def_id, None));
+                           def_id_to_string(self.ccx, def_id));
 
-                    self.output.push(TransItem::Fn {
-                        def_id: def_id,
-                        substs: self.trans_empty_substs
-                    });
+                    let instance = Instance::mono(self.ccx.tcx(), def_id);
+                    self.output.push(TransItem::Fn(instance));
                 }
             }
             _ => { /* Nothing to do here */ }
@@ -1142,7 +1066,7 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
             let impl_def_id = tcx.map.local_def_id(item.id);
 
             debug!("create_trans_items_for_default_impls(item={})",
-                   def_id_to_string(ccx, impl_def_id, None));
+                   def_id_to_string(ccx, impl_def_id));
 
             if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) {
                 let default_impls = tcx.provided_trait_methods(trait_ref.def_id);
@@ -1225,7 +1149,7 @@ pub fn push_unique_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         ty::TyStruct(adt_def, substs) |
         ty::TyEnum(adt_def, substs) => {
             push_item_name(cx, adt_def.did, output);
-            push_type_params(cx, substs, &[], output);
+            push_type_params(cx, &substs.types, &[], output);
         },
         ty::TyTuple(ref component_types) => {
             output.push('(');
@@ -1275,7 +1199,7 @@ pub fn push_unique_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         ty::TyTrait(ref trait_data) => {
             push_item_name(cx, trait_data.principal.skip_binder().def_id, output);
             push_type_params(cx,
-                             &trait_data.principal.skip_binder().substs,
+                             &trait_data.principal.skip_binder().substs.types,
                              &trait_data.bounds.projection_bounds,
                              output);
         },
@@ -1285,7 +1209,7 @@ pub fn push_unique_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 output.push_str("unsafe ");
             }
 
-            if abi != ::syntax::abi::Abi::Rust {
+            if abi != ::trans::abi::Abi::Rust {
                 output.push_str("extern \"");
                 output.push_str(abi.name());
                 output.push_str("\" ");
@@ -1329,7 +1253,7 @@ pub fn push_unique_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             output.push_str("{");
             output.push_str(&format!("{}:{}", def_id.krate, def_id.index.as_usize()));
             output.push_str("}");
-            push_type_params(cx, closure_substs.func_substs, &[], output);
+            push_type_params(cx, &closure_substs.func_substs.types, &[], output);
         }
         ty::TyError |
         ty::TyInfer(_) |
@@ -1371,16 +1295,16 @@ fn push_item_name(ccx: &CrateContext,
 }
 
 fn push_type_params<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
-                              substs: &Substs<'tcx>,
+                              types: &'tcx subst::VecPerParamSpace<Ty<'tcx>>,
                               projections: &[ty::PolyProjectionPredicate<'tcx>],
                               output: &mut String) {
-    if substs.types.is_empty() && projections.is_empty() {
+    if types.is_empty() && projections.is_empty() {
         return;
     }
 
     output.push('<');
 
-    for &type_parameter in &substs.types {
+    for &type_parameter in types {
         push_unique_type_name(cx, type_parameter, output);
         output.push_str(", ");
     }
@@ -1400,23 +1324,16 @@ fn push_type_params<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     output.push('>');
 }
 
-fn push_def_id_as_string<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                              def_id: DefId,
-                              substs: Option<&Substs<'tcx>>,
-                              output: &mut String) {
-    push_item_name(ccx, def_id, output);
-
-    if let Some(substs) = substs {
-        push_type_params(ccx, substs, &[], output);
-    }
+fn push_instance_as_string<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                                     instance: Instance<'tcx>,
+                                     output: &mut String) {
+    push_item_name(ccx, instance.def, output);
+    push_type_params(ccx, instance.params, &[], output);
 }
 
-fn def_id_to_string<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                              def_id: DefId,
-                              substs: Option<&Substs<'tcx>>)
-                              -> String {
+fn def_id_to_string(ccx: &CrateContext, def_id: DefId) -> String {
     let mut output = String::new();
-    push_def_id_as_string(ccx, def_id, substs, &mut output);
+    push_item_name(ccx, def_id, &mut output);
     output
 }
 
@@ -1440,23 +1357,23 @@ impl<'tcx> TransItem<'tcx> {
                 push_unique_type_name(ccx, t, &mut s);
                 s
             }
-            TransItem::Fn { def_id, ref substs } => {
-                to_string_internal(ccx, "fn ", def_id, Some(substs))
+            TransItem::Fn(instance) => {
+                to_string_internal(ccx, "fn ", instance)
             },
             TransItem::Static(node_id) => {
                 let def_id = hir_map.local_def_id(node_id);
-                to_string_internal(ccx, "static ", def_id, None)
+                let instance = Instance::mono(ccx.tcx(), def_id);
+                to_string_internal(ccx, "static ", instance)
             },
         };
 
         fn to_string_internal<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                         prefix: &str,
-                                        def_id: DefId,
-                                        substs: Option<&Substs<'tcx>>)
+                                        instance: Instance<'tcx>)
                                         -> String {
             let mut result = String::with_capacity(32);
             result.push_str(prefix);
-            push_def_id_as_string(ccx, def_id, substs, &mut result);
+            push_instance_as_string(ccx, instance, &mut result);
             result
         }
     }
@@ -1466,10 +1383,10 @@ impl<'tcx> TransItem<'tcx> {
             TransItem::DropGlue(t) => {
                 format!("DropGlue({})", t as *const _ as usize)
             }
-            TransItem::Fn { def_id, substs } => {
+            TransItem::Fn(instance) => {
                 format!("Fn({:?}, {})",
-                         def_id,
-                         substs as *const _ as usize)
+                         instance.def,
+                         instance.params as *const _ as usize)
             }
             TransItem::Static(id) => {
                 format!("Static({:?})", id)
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index 0aa69dec253..db06b2352fd 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -12,8 +12,6 @@
 
 //! Code that is useful in various trans modules.
 
-pub use self::ExprOrMethodCall::*;
-
 use session::Session;
 use llvm;
 use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef, TypeKind};
@@ -23,33 +21,34 @@ use middle::def::Def;
 use middle::def_id::DefId;
 use middle::infer;
 use middle::lang_items::LangItem;
-use middle::subst::{self, Substs};
+use middle::subst::Substs;
+use trans::abi::{Abi, FnType};
 use trans::base;
 use trans::build;
 use trans::builder::Builder;
-use trans::callee;
+use trans::callee::Callee;
 use trans::cleanup;
 use trans::consts;
 use trans::datum;
 use trans::debuginfo::{self, DebugLoc};
 use trans::declare;
 use trans::machine;
+use trans::mir::CachedMir;
 use trans::monomorphize;
 use trans::type_::Type;
-use trans::type_of;
+use trans::value::Value;
 use middle::ty::{self, Ty, TyCtxt};
 use middle::traits::{self, SelectionContext, ProjectionMode};
 use middle::ty::fold::{TypeFolder, TypeFoldable};
 use rustc_front::hir;
-use rustc::mir::repr::Mir;
-use util::nodemap::{FnvHashMap, NodeMap};
+use util::nodemap::NodeMap;
 
 use arena::TypedArena;
 use libc::{c_uint, c_char};
 use std::ops::Deref;
 use std::ffi::CString;
 use std::cell::{Cell, RefCell};
-use std::vec::Vec;
+
 use syntax::ast;
 use syntax::codemap::{DUMMY_SP, Span};
 use syntax::parse::token::InternedString;
@@ -75,18 +74,6 @@ pub fn type_is_fat_ptr<'tcx>(cx: &TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
     }
 }
 
-fn type_is_newtype_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
-    match ty.sty {
-        ty::TyStruct(def, substs) => {
-            let fields = &def.struct_variant().fields;
-            fields.len() == 1 && {
-                type_is_immediate(ccx, monomorphize::field_ty(ccx.tcx(), substs, &fields[0]))
-            }
-        }
-        _ => false
-    }
-}
-
 pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
     use trans::machine::llsize_of_alloc;
     use trans::type_of::sizing_type_of;
@@ -94,7 +81,6 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -
     let tcx = ccx.tcx();
     let simple = ty.is_scalar() ||
         ty.is_unique() || ty.is_region_ptr() ||
-        type_is_newtype_immediate(ccx, ty) ||
         ty.is_simd();
     if simple && !type_is_fat_ptr(tcx, ty) {
         return true;
@@ -120,12 +106,6 @@ pub fn type_is_zero_size<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -
     llsize_of_alloc(ccx, llty) == 0
 }
 
-/// Identifies types which we declare to be equivalent to `void` in C for the purpose of function
-/// return types. These are `()`, bot, uninhabited enums and all other zero-sized types.
-pub fn return_type_is_void<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
-    ty.is_nil() || ty.is_empty(ccx.tcx()) || type_is_zero_size(ccx, ty)
-}
-
 /// Generates a unique symbol based off the name given. This is used to create
 /// unique symbols for things like closures.
 pub fn gensym_name(name: &str) -> ast::Name {
@@ -252,8 +232,6 @@ pub fn BuilderRef_res(b: BuilderRef) -> BuilderRef_res {
     }
 }
 
-pub type ExternMap = FnvHashMap<String, ValueRef>;
-
 pub fn validate_substs(substs: &Substs) {
     assert!(!substs.types.needs_infer());
 }
@@ -295,7 +273,7 @@ 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>>,
+    pub mir: Option<CachedMir<'a, 'tcx>>,
 
     // The ValueRef returned from a call to llvm::LLVMAddFunction; the
     // address of the first instruction in the sequence of
@@ -306,9 +284,6 @@ pub struct FunctionContext<'a, 'tcx: 'a> {
     // always an empty parameter-environment NOTE: @jroesch another use of ParamEnv
     pub param_env: ty::ParameterEnvironment<'a, 'tcx>,
 
-    // The environment argument in a closure.
-    pub llenv: Option<ValueRef>,
-
     // A pointer to where to store the return value. If the return type is
     // immediate, this points to an alloca in the function. Otherwise, it's a
     // pointer to the hidden first parameter of the function. After function
@@ -336,11 +311,6 @@ pub struct FunctionContext<'a, 'tcx: 'a> {
     // Note that for cleanuppad-based exceptions this is not used.
     pub landingpad_alloca: Cell<Option<ValueRef>>,
 
-    // True if the caller expects this fn to use the out pointer to
-    // return. Either way, your code should write into the slot llretslotptr
-    // points to, but if this value is false, that slot will be a local alloca.
-    pub caller_expects_out_pointer: bool,
-
     // Maps the DefId's for local variables to the allocas created for
     // them in llallocas.
     pub lllocals: RefCell<NodeMap<LvalueDatum<'tcx>>>,
@@ -352,9 +322,8 @@ pub struct FunctionContext<'a, 'tcx: 'a> {
     // paths) for the code being compiled.
     pub lldropflag_hints: RefCell<DropFlagHintsMap<'tcx>>,
 
-    // The NodeId of the function, or -1 if it doesn't correspond to
-    // a user-defined function.
-    pub id: ast::NodeId,
+    // Describes the return/argument LLVM types and their ABI handling.
+    pub fn_ty: FnType,
 
     // If this function is being monomorphized, this contains the type
     // substitutions used.
@@ -383,20 +352,8 @@ 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 }
-    }
-
-    pub fn env_arg_pos(&self) -> usize {
-        if self.caller_expects_out_pointer {
-            1
-        } else {
-            0
-        }
+    pub fn mir(&self) -> CachedMir<'a, 'tcx> {
+        self.mir.clone().expect("fcx.mir was empty")
     }
 
     pub fn cleanup(&self) {
@@ -419,14 +376,9 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
         self.llreturn.get().unwrap()
     }
 
-    pub fn get_ret_slot(&self, bcx: Block<'a, 'tcx>,
-                        output: ty::FnOutput<'tcx>,
-                        name: &str) -> ValueRef {
+    pub fn get_ret_slot(&self, bcx: Block<'a, 'tcx>, name: &str) -> ValueRef {
         if self.needs_ret_allocas {
-            base::alloca(bcx, match output {
-                ty::FnConverging(output_type) => type_of::type_of(bcx.ccx(), output_type),
-                ty::FnDiverging => Type::void(bcx.ccx())
-            }, name)
+            base::alloca(bcx, self.fn_ty.ret.memory_ty(self.ccx), name)
         } else {
             self.llretslotptr.get().unwrap()
         }
@@ -511,62 +463,60 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
         // `rust_eh_personality` function, but rather we wired it up to the
         // CRT's custom personality function, which forces LLVM to consider
         // landing pads as "landing pads for SEH".
-        let target = &self.ccx.sess().target.target;
-        match self.ccx.tcx().lang_items.eh_personality() {
-            Some(def_id) if !base::wants_msvc_seh(self.ccx.sess()) => {
-                callee::trans_fn_ref(self.ccx, def_id, ExprId(0),
-                                     self.param_substs).val
+        let ccx = self.ccx;
+        let tcx = ccx.tcx();
+        let target = &ccx.sess().target.target;
+        match tcx.lang_items.eh_personality() {
+            Some(def_id) if !base::wants_msvc_seh(ccx.sess()) => {
+                Callee::def(ccx, def_id, tcx.mk_substs(Substs::empty())).reify(ccx).val
             }
-            _ => {
-                let mut personality = self.ccx.eh_personality().borrow_mut();
-                match *personality {
-                    Some(llpersonality) => llpersonality,
-                    None => {
-                        let name = if !base::wants_msvc_seh(self.ccx.sess()) {
-                            "rust_eh_personality"
-                        } else if target.arch == "x86" {
-                            "_except_handler3"
-                        } else {
-                            "__C_specific_handler"
-                        };
-                        let fty = Type::variadic_func(&[], &Type::i32(self.ccx));
-                        let f = declare::declare_cfn(self.ccx, name, fty,
-                                                     self.ccx.tcx().types.i32);
-                        *personality = Some(f);
-                        f
-                    }
-                }
+            _ => if let Some(llpersonality) = ccx.eh_personality().get() {
+                llpersonality
+            } else {
+                let name = if !base::wants_msvc_seh(ccx.sess()) {
+                    "rust_eh_personality"
+                } else if target.arch == "x86" {
+                    "_except_handler3"
+                } else {
+                    "__C_specific_handler"
+                };
+                let fty = Type::variadic_func(&[], &Type::i32(ccx));
+                let f = declare::declare_cfn(ccx, name, fty);
+                ccx.eh_personality().set(Some(f));
+                f
             }
         }
     }
 
     // Returns a ValueRef of the "eh_unwind_resume" lang item if one is defined,
     // otherwise declares it as an external function.
-    pub fn eh_unwind_resume(&self) -> ValueRef {
+    pub fn eh_unwind_resume(&self) -> Callee<'tcx> {
         use trans::attributes;
-        assert!(self.ccx.sess().target.target.options.custom_unwind_resume);
-        match self.ccx.tcx().lang_items.eh_unwind_resume() {
-            Some(def_id) => {
-                callee::trans_fn_ref(self.ccx, def_id, ExprId(0),
-                                     self.param_substs).val
-            }
-            None => {
-                let mut unwresume = self.ccx.eh_unwind_resume().borrow_mut();
-                match *unwresume {
-                    Some(llfn) => llfn,
-                    None => {
-                        let fty = Type::func(&[Type::i8p(self.ccx)], &Type::void(self.ccx));
-                        let llfn = declare::declare_fn(self.ccx,
-                                                       "rust_eh_unwind_resume",
-                                                       llvm::CCallConv,
-                                                       fty, ty::FnDiverging);
-                        attributes::unwind(llfn, true);
-                        *unwresume = Some(llfn);
-                        llfn
-                    }
-                }
-            }
+        let ccx = self.ccx;
+        let tcx = ccx.tcx();
+        assert!(ccx.sess().target.target.options.custom_unwind_resume);
+        if let Some(def_id) = tcx.lang_items.eh_unwind_resume() {
+            return Callee::def(ccx, def_id, tcx.mk_substs(Substs::empty()));
         }
+
+        let ty = tcx.mk_fn_ptr(ty::BareFnTy {
+            unsafety: hir::Unsafety::Unsafe,
+            abi: Abi::C,
+            sig: ty::Binder(ty::FnSig {
+                inputs: vec![tcx.mk_mut_ptr(tcx.types.u8)],
+                output: ty::FnDiverging,
+                variadic: false
+            }),
+        });
+
+        let unwresume = ccx.eh_unwind_resume();
+        if let Some(llfn) = unwresume.get() {
+            return Callee::ptr(datum::immediate_rvalue(llfn, ty));
+        }
+        let llfn = declare::declare_fn(ccx, "rust_eh_unwind_resume", ty);
+        attributes::unwind(llfn, true);
+        unwresume.set(Some(llfn));
+        Callee::ptr(datum::immediate_rvalue(llfn, ty))
     }
 }
 
@@ -630,7 +580,7 @@ impl<'blk, 'tcx> BlockS<'blk, 'tcx> {
         self.lpad.get()
     }
 
-    pub fn mir(&self) -> &'blk Mir<'tcx> {
+    pub fn mir(&self) -> CachedMir<'blk, 'tcx> {
         self.fcx.mir()
     }
 
@@ -652,14 +602,6 @@ impl<'blk, 'tcx> BlockS<'blk, 'tcx> {
         }
     }
 
-    pub fn val_to_string(&self, val: ValueRef) -> String {
-        self.ccx().tn().val_to_string(val)
-    }
-
-    pub fn llty_str(&self, ty: Type) -> String {
-        self.ccx().tn().type_to_string(ty)
-    }
-
     pub fn to_str(&self) -> String {
         format!("[block {:p}]", self)
     }
@@ -746,6 +688,10 @@ impl<'blk, 'tcx> BlockAndBuilder<'blk, 'tcx> {
 
     // Methods delegated to bcx
 
+    pub fn is_unreachable(&self) -> bool {
+        self.bcx.unreachable.get()
+    }
+
     pub fn ccx(&self) -> &'blk CrateContext<'blk, 'tcx> {
         self.bcx.ccx()
     }
@@ -763,14 +709,10 @@ impl<'blk, 'tcx> BlockAndBuilder<'blk, 'tcx> {
         self.bcx.llbb
     }
 
-    pub fn mir(&self) -> &'blk Mir<'tcx> {
+    pub fn mir(&self) -> CachedMir<'blk, 'tcx> {
         self.bcx.mir()
     }
 
-    pub fn val_to_string(&self, val: ValueRef) -> String {
-        self.bcx.val_to_string(val)
-    }
-
     pub fn monomorphize<T>(&self, value: &T) -> T
         where T: TypeFoldable<'tcx>
     {
@@ -1028,15 +970,15 @@ pub fn C_bytes_in_context(llcx: ContextRef, bytes: &[u8]) -> ValueRef {
     }
 }
 
-pub fn const_get_elt(cx: &CrateContext, v: ValueRef, us: &[c_uint])
+pub fn const_get_elt(v: ValueRef, us: &[c_uint])
               -> ValueRef {
     unsafe {
         let r = llvm::LLVMConstExtractValue(v, us.as_ptr(), us.len() as c_uint);
 
-        debug!("const_get_elt(v={}, us={:?}, r={})",
-               cx.tn().val_to_string(v), us, cx.tn().val_to_string(r));
+        debug!("const_get_elt(v={:?}, us={:?}, r={:?})",
+               Value(v), us, Value(r));
 
-        return r;
+        r
     }
 }
 
@@ -1215,41 +1157,6 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()).is_ok()
 }
 
-// Key used to lookup values supplied for type parameters in an expr.
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum ExprOrMethodCall {
-    // Type parameters for a path like `None::<int>`
-    ExprId(ast::NodeId),
-
-    // Type parameters for a method call like `a.foo::<int>()`
-    MethodCallKey(ty::MethodCall)
-}
-
-pub fn node_id_substs<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                node: ExprOrMethodCall,
-                                param_substs: &subst::Substs<'tcx>)
-                                -> &'tcx subst::Substs<'tcx> {
-    let tcx = ccx.tcx();
-
-    let substs = match node {
-        ExprId(id) => {
-            tcx.node_id_item_substs(id).substs
-        }
-        MethodCallKey(method_call) => {
-            tcx.tables.borrow().method_map[&method_call].substs.clone()
-        }
-    };
-
-    if substs.types.needs_infer() {
-        tcx.sess.bug(&format!("type parameters for node {:?} include inference types: {:?}",
-                              node, substs));
-    }
-
-    ccx.tcx().mk_substs(monomorphize::apply_param_substs(tcx,
-                                                         param_substs,
-                                                         &substs.erase_regions()))
-}
-
 pub fn langcall(bcx: Block,
                 span: Option<Span>,
                 msg: &str,
@@ -1351,14 +1258,3 @@ pub fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         _ => panic!("shift_mask_val: expected Integer or Vector, found {:?}", kind),
     }
 }
-
-pub fn get_static_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                            did: DefId,
-                            ty: Ty<'tcx>)
-                            -> ValueRef {
-    if let Some(node_id) = ccx.tcx().map.as_local_node_id(did) {
-        base::get_item_val(ccx, node_id)
-    } else {
-        base::get_extern_const(ccx, did, ty)
-    }
-}
diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs
index 7d37627ad0e..82cd6aace0a 100644
--- a/src/librustc_trans/trans/consts.rs
+++ b/src/librustc_trans/trans/consts.rs
@@ -9,27 +9,28 @@
 // except according to those terms.
 
 
-use back::abi;
 use llvm;
 use llvm::{ConstFCmp, ConstICmp, SetLinkage, SetUnnamedAddr};
 use llvm::{InternalLinkage, ValueRef, Bool, True};
 use middle::const_qualif::ConstQualif;
-use middle::cstore::LOCAL_CRATE;
 use middle::const_eval::{self, ConstEvalErr};
 use middle::def::Def;
 use middle::def_id::DefId;
-use trans::{adt, closure, debuginfo, expr, inline, machine};
-use trans::base::{self, push_ctxt};
+use rustc::front::map as hir_map;
+use trans::{abi, adt, closure, debuginfo, expr, machine};
+use trans::base::{self, exported_name, imported_name, push_ctxt};
 use trans::callee::Callee;
 use trans::collector::{self, TransItem};
-use trans::common::{self, type_is_sized, ExprOrMethodCall, node_id_substs, C_nil, const_get_elt};
+use trans::common::{type_is_sized, C_nil, const_get_elt};
 use trans::common::{CrateContext, C_integral, C_floating, C_bool, C_str_slice, C_bytes, val_ty};
 use trans::common::{C_struct, C_undef, const_to_opt_int, const_to_opt_uint, VariantInfo, C_uint};
-use trans::common::{type_is_fat_ptr, Field, C_vector, C_array, C_null, ExprId, MethodCallKey};
+use trans::common::{type_is_fat_ptr, Field, C_vector, C_array, C_null};
+use trans::datum::{Datum, Lvalue};
 use trans::declare;
-use trans::monomorphize;
+use trans::monomorphize::{self, Instance};
 use trans::type_::Type;
 use trans::type_of;
+use trans::value::Value;
 use trans::Disr;
 use middle::subst::Substs;
 use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer};
@@ -45,7 +46,7 @@ use std::ffi::{CStr, CString};
 use std::borrow::Cow;
 use libc::c_uint;
 use syntax::ast::{self, LitKind};
-use syntax::attr;
+use syntax::attr::{self, AttrMetaMethods};
 use syntax::parse::token;
 use syntax::ptr::P;
 
@@ -191,27 +192,31 @@ fn const_deref<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 }
 
 fn const_fn_call<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                           node: ExprOrMethodCall,
                            def_id: DefId,
+                           substs: Substs<'tcx>,
                            arg_vals: &[ValueRef],
                            param_substs: &'tcx Substs<'tcx>,
                            trueconst: TrueConst) -> Result<ValueRef, ConstEvalFailure> {
     let fn_like = const_eval::lookup_const_fn_by_id(ccx.tcx(), def_id);
     let fn_like = fn_like.expect("lookup_const_fn_by_id failed in const_fn_call");
 
+    let body = match fn_like.body().expr {
+        Some(ref expr) => expr,
+        None => return Ok(C_nil(ccx))
+    };
+
     let args = &fn_like.decl().inputs;
     assert_eq!(args.len(), arg_vals.len());
 
     let arg_ids = args.iter().map(|arg| arg.pat.id);
     let fn_args = arg_ids.zip(arg_vals.iter().cloned()).collect();
 
-    let substs = node_id_substs(ccx, node, param_substs);
-    match fn_like.body().expr {
-        Some(ref expr) => {
-            const_expr(ccx, &expr, substs, Some(&fn_args), trueconst).map(|(res, _)| res)
-        },
-        None => Ok(C_nil(ccx)),
-    }
+    let substs = monomorphize::apply_param_substs(ccx.tcx(),
+                                                  param_substs,
+                                                  &substs.erase_regions());
+    let substs = ccx.tcx().mk_substs(substs);
+
+    const_expr(ccx, body, substs, Some(&fn_args), trueconst).map(|(res, _)| res)
 }
 
 pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
@@ -219,14 +224,11 @@ pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 ref_expr: &hir::Expr,
                                 param_substs: &'tcx Substs<'tcx>)
                                 -> &'tcx hir::Expr {
-    let def_id = inline::maybe_instantiate_inline(ccx, def_id);
-
-    if def_id.krate != LOCAL_CRATE {
-        ccx.sess().span_bug(ref_expr.span,
-                            "cross crate constant could not be inlined");
-    }
-
-    match const_eval::lookup_const_by_id(ccx.tcx(), def_id, Some(ref_expr.id), Some(param_substs)) {
+    let substs = ccx.tcx().node_id_item_substs(ref_expr.id).substs;
+    let substs = monomorphize::apply_param_substs(ccx.tcx(),
+                                                  param_substs,
+                                                  &substs.erase_regions());
+    match const_eval::lookup_const_by_id(ccx.tcx(), def_id, Some(substs)) {
         Some((ref expr, _ty)) => expr,
         None => {
             ccx.sess().span_bug(ref_expr.span, "constant item not found")
@@ -351,9 +353,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         Some(AdjustReifyFnPointer) => {
             match ety.sty {
                 ty::TyFnDef(def_id, substs, _) => {
-                    let datum = Callee::def(cx, def_id, substs, ety).reify(cx);
-                    llconst = datum.val;
-                    ety_adjusted = datum.ty;
+                    llconst = Callee::def(cx, def_id, substs).reify(cx).val;
                 }
                 _ => {
                     unreachable!("{} cannot be reified to a fn ptr", ety)
@@ -405,8 +405,8 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     // to use a different vtable. In that case, we want to
                     // load out the original data pointer so we can repackage
                     // it.
-                    (const_get_elt(cx, llconst, &[abi::FAT_PTR_ADDR as u32]),
-                     Some(const_get_elt(cx, llconst, &[abi::FAT_PTR_EXTRA as u32])))
+                    (const_get_elt(llconst, &[abi::FAT_PTR_ADDR as u32]),
+                     Some(const_get_elt(llconst, &[abi::FAT_PTR_EXTRA as u32])))
                 } else {
                     (llconst, None)
                 };
@@ -595,17 +595,15 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             /* Neither type is bottom, and we expect them to be unified
              * already, so the following is safe. */
             let (te1, ty) = try!(const_expr(cx, &e1, param_substs, fn_args, trueconst));
-            debug!("const_expr_unadjusted: te1={}, ty={:?}",
-                   cx.tn().val_to_string(te1),
-                   ty);
+            debug!("const_expr_unadjusted: te1={:?}, ty={:?}",
+                   Value(te1), ty);
             assert!(!ty.is_simd());
             let is_float = ty.is_fp();
             let signed = ty.is_signed();
 
             let (te2, ty2) = try!(const_expr(cx, &e2, param_substs, fn_args, trueconst));
-            debug!("const_expr_unadjusted: te2={}, ty={:?}",
-                   cx.tn().val_to_string(te2),
-                   ty2);
+            debug!("const_expr_unadjusted: te2={:?}, ty={:?}",
+                   Value(te2), ty2);
 
             try!(check_binary_expr_validity(cx, e, ty, te1, te2, trueconst));
 
@@ -689,8 +687,8 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             let (arr, len) = match bt.sty {
                 ty::TyArray(_, u) => (bv, C_uint(cx, u)),
                 ty::TySlice(..) | ty::TyStr => {
-                    let e1 = const_get_elt(cx, bv, &[0]);
-                    (load_const(cx, e1, bt), const_get_elt(cx, bv, &[1]))
+                    let e1 = const_get_elt(bv, &[0]);
+                    (load_const(cx, e1, bt), const_get_elt(bv, &[1]))
                 },
                 ty::TyRef(_, mt) => match mt.ty.sty {
                     ty::TyArray(_, u) => {
@@ -725,7 +723,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                           "const index-expr is out of bounds");
                 C_undef(val_ty(arr).element_type())
             } else {
-                const_get_elt(cx, arr, &[iv as c_uint])
+                const_get_elt(arr, &[iv as c_uint])
             }
         },
         hir::ExprCast(ref base, _) => {
@@ -741,10 +739,10 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 let t_cast_inner =
                     t_cast.builtin_deref(true, ty::NoPreference).expect("cast to non-pointer").ty;
                 let ptr_ty = type_of::in_memory_type_of(cx, t_cast_inner).ptr_to();
-                let addr = ptrcast(const_get_elt(cx, v, &[abi::FAT_PTR_ADDR as u32]),
+                let addr = ptrcast(const_get_elt(v, &[abi::FAT_PTR_ADDR as u32]),
                                    ptr_ty);
                 if type_is_fat_ptr(cx.tcx(), t_cast) {
-                    let info = const_get_elt(cx, v, &[abi::FAT_PTR_EXTRA as u32]);
+                    let info = const_get_elt(v, &[abi::FAT_PTR_EXTRA as u32]);
                     return Ok(C_struct(cx, &[addr, info], false))
                 } else {
                     return Ok(addr);
@@ -756,7 +754,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             ) {
                 (CastTy::Int(IntTy::CEnum), CastTy::Int(_)) => {
                     let repr = adt::represent_type(cx, t_expr);
-                    let discr = adt::const_get_discrim(cx, &repr, v);
+                    let discr = adt::const_get_discrim(&repr, v);
                     let iv = C_integral(cx.int_type(), discr.0, false);
                     let s = adt::is_discr_signed(&repr) as Bool;
                     llvm::LLVMConstIntCast(iv, llty.to_ref(), s)
@@ -809,7 +807,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             }
             let opt_def = cx.tcx().def_map.borrow().get(&cur.id).map(|d| d.full_def());
             if let Some(Def::Static(def_id, _)) = opt_def {
-                common::get_static_val(cx, def_id, ety)
+                get_static(cx, def_id).val
             } else {
                 // If this isn't the address of a static, then keep going through
                 // normal constant evaluation.
@@ -947,8 +945,8 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 Def::Fn(did) | Def::Method(did) => {
                     try!(const_fn_call(
                         cx,
-                        ExprId(callee.id),
                         did,
+                        cx.tcx().node_id_item_substs(callee.id).substs,
                         &arg_vals,
                         param_substs,
                         trueconst,
@@ -976,9 +974,9 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         hir::ExprMethodCall(_, _, ref args) => {
             let arg_vals = try!(map_list(args));
             let method_call = ty::MethodCall::expr(e.id);
-            let method_did = cx.tcx().tables.borrow().method_map[&method_call].def_id;
-            try!(const_fn_call(cx, MethodCallKey(method_call),
-                               method_did, &arg_vals, param_substs, trueconst))
+            let method = cx.tcx().tables.borrow().method_map[&method_call];
+            try!(const_fn_call(cx, method.def_id, method.substs.clone(),
+                               &arg_vals, param_substs, trueconst))
         },
         hir::ExprType(ref e, _) => try!(const_expr(cx, &e, param_substs, fn_args, trueconst)).0,
         hir::ExprBlock(ref block) => {
@@ -1001,8 +999,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                                 body,
                                                 e.id,
                                                 def_id,
-                                                substs,
-                                                &e.attrs);
+                                                substs);
                 }
                 _ =>
                     cx.sess().span_bug(
@@ -1016,6 +1013,125 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     })
 }
 
+pub fn get_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId)
+                            -> Datum<'tcx, Lvalue> {
+    let ty = ccx.tcx().lookup_item_type(def_id).ty;
+
+    let instance = Instance::mono(ccx.tcx(), def_id);
+    if let Some(&g) = ccx.instances().borrow().get(&instance) {
+        return Datum::new(g, ty, Lvalue::new("static"));
+    }
+
+    let g = if let Some(id) = ccx.tcx().map.as_local_node_id(def_id) {
+        let llty = type_of::type_of(ccx, ty);
+        match ccx.tcx().map.get(id) {
+            hir_map::NodeItem(&hir::Item {
+                ref attrs, span, node: hir::ItemStatic(..), ..
+            }) => {
+                // If this static came from an external crate, then
+                // we need to get the symbol from metadata instead of
+                // using the current crate's name/version
+                // information in the hash of the symbol
+                let sym = exported_name(ccx, id, ty, attrs);
+                debug!("making {}", sym);
+
+                // Create the global before evaluating the initializer;
+                // this is necessary to allow recursive statics.
+                let g = declare::define_global(ccx, &sym, llty).unwrap_or_else(|| {
+                    ccx.sess().span_fatal(span,
+                        &format!("symbol `{}` is already defined", sym))
+                });
+
+                ccx.item_symbols().borrow_mut().insert(id, sym);
+                g
+            }
+
+            hir_map::NodeForeignItem(&hir::ForeignItem {
+                ref attrs, name, span, node: hir::ForeignItemStatic(..), ..
+            }) => {
+                let ident = imported_name(name, attrs);
+                let g = if let Some(name) =
+                        attr::first_attr_value_str_by_name(&attrs, "linkage") {
+                    // If this is a static with a linkage specified, then we need to handle
+                    // it a little specially. The typesystem prevents things like &T and
+                    // extern "C" fn() from being non-null, so we can't just declare a
+                    // static and call it a day. Some linkages (like weak) will make it such
+                    // that the static actually has a null value.
+                    let linkage = match base::llvm_linkage_by_name(&name) {
+                        Some(linkage) => linkage,
+                        None => {
+                            ccx.sess().span_fatal(span, "invalid linkage specified");
+                        }
+                    };
+                    let llty2 = match ty.sty {
+                        ty::TyRawPtr(ref mt) => type_of::type_of(ccx, mt.ty),
+                        _ => {
+                            ccx.sess().span_fatal(span, "must have type `*const T` or `*mut T`");
+                        }
+                    };
+                    unsafe {
+                        // Declare a symbol `foo` with the desired linkage.
+                        let g1 = declare::declare_global(ccx, &ident, llty2);
+                        llvm::SetLinkage(g1, linkage);
+
+                        // Declare an internal global `extern_with_linkage_foo` which
+                        // is initialized with the address of `foo`.  If `foo` is
+                        // discarded during linking (for example, if `foo` has weak
+                        // linkage and there are no definitions), then
+                        // `extern_with_linkage_foo` will instead be initialized to
+                        // zero.
+                        let mut real_name = "_rust_extern_with_linkage_".to_string();
+                        real_name.push_str(&ident);
+                        let g2 = declare::define_global(ccx, &real_name, llty).unwrap_or_else(||{
+                            ccx.sess().span_fatal(span,
+                                &format!("symbol `{}` is already defined", ident))
+                        });
+                        llvm::SetLinkage(g2, llvm::InternalLinkage);
+                        llvm::LLVMSetInitializer(g2, g1);
+                        g2
+                    }
+                } else {
+                    // Generate an external declaration.
+                    declare::declare_global(ccx, &ident, llty)
+                };
+
+                for attr in attrs {
+                    if attr.check_name("thread_local") {
+                        llvm::set_thread_local(g, true);
+                    }
+                }
+
+                g
+            }
+
+            item => unreachable!("get_static: expected static, found {:?}", item)
+        }
+    } else {
+        // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow?
+        // FIXME(nagisa): investigate whether it can be changed into define_global
+        let name = ccx.sess().cstore.item_symbol(def_id);
+        let g = declare::declare_global(ccx, &name, type_of::type_of(ccx, ty));
+        // Thread-local statics in some other crate need to *always* be linked
+        // against in a thread-local fashion, so we need to be sure to apply the
+        // thread-local attribute locally if it was present remotely. If we
+        // don't do this then linker errors can be generated where the linker
+        // complains that one object files has a thread local version of the
+        // symbol and another one doesn't.
+        for attr in ccx.tcx().get_attrs(def_id).iter() {
+            if attr.check_name("thread_local") {
+                llvm::set_thread_local(g, true);
+            }
+        }
+        if ccx.use_dll_storage_attrs() {
+            llvm::SetDLLStorageClass(g, llvm::DLLImportStorageClass);
+        }
+        g
+    };
+
+    ccx.instances().borrow_mut().insert(instance, g);
+    Datum::new(g, ty, Lvalue::new("static"))
+}
+
 pub fn trans_static(ccx: &CrateContext,
                     m: hir::Mutability,
                     expr: &hir::Expr,
@@ -1029,7 +1145,8 @@ pub fn trans_static(ccx: &CrateContext,
 
     unsafe {
         let _icx = push_ctxt("trans_static");
-        let g = base::get_item_val(ccx, id);
+        let def_id = ccx.tcx().map.local_def_id(id);
+        let datum = get_static(ccx, def_id);
 
         let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
         let (v, _) = try!(const_expr(
@@ -1042,40 +1159,39 @@ pub fn trans_static(ccx: &CrateContext,
 
         // boolean SSA values are i1, but they have to be stored in i8 slots,
         // otherwise some LLVM optimization passes don't work as expected
-        let mut val_llty = llvm::LLVMTypeOf(v);
-        let v = if val_llty == Type::i1(ccx).to_ref() {
-            val_llty = Type::i8(ccx).to_ref();
-            llvm::LLVMConstZExt(v, val_llty)
+        let mut val_llty = val_ty(v);
+        let v = if val_llty == Type::i1(ccx) {
+            val_llty = Type::i8(ccx);
+            llvm::LLVMConstZExt(v, val_llty.to_ref())
         } else {
             v
         };
 
-        let ty = ccx.tcx().node_id_to_type(id);
-        let llty = type_of::type_of(ccx, ty);
-        let g = if val_llty == llty.to_ref() {
-            g
+        let llty = type_of::type_of(ccx, datum.ty);
+        let g = if val_llty == llty {
+            datum.val
         } else {
             // If we created the global with the wrong type,
             // correct the type.
             let empty_string = CString::new("").unwrap();
-            let name_str_ref = CStr::from_ptr(llvm::LLVMGetValueName(g));
+            let name_str_ref = CStr::from_ptr(llvm::LLVMGetValueName(datum.val));
             let name_string = CString::new(name_str_ref.to_bytes()).unwrap();
-            llvm::LLVMSetValueName(g, empty_string.as_ptr());
+            llvm::LLVMSetValueName(datum.val, empty_string.as_ptr());
             let new_g = llvm::LLVMGetOrInsertGlobal(
-                ccx.llmod(), name_string.as_ptr(), val_llty);
+                ccx.llmod(), name_string.as_ptr(), val_llty.to_ref());
             // To avoid breaking any invariants, we leave around the old
             // global for the moment; we'll replace all references to it
             // with the new global later. (See base::trans_crate.)
-            ccx.statics_to_rauw().borrow_mut().push((g, new_g));
+            ccx.statics_to_rauw().borrow_mut().push((datum.val, new_g));
             new_g
         };
-        llvm::LLVMSetAlignment(g, type_of::align_of(ccx, ty));
+        llvm::LLVMSetAlignment(g, type_of::align_of(ccx, datum.ty));
         llvm::LLVMSetInitializer(g, v);
 
         // As an optimization, all shared statics which do not have interior
         // mutability are placed into read-only memory.
         if m != hir::MutMutable {
-            let tcontents = ty.type_contents(ccx.tcx());
+            let tcontents = datum.ty.type_contents(ccx.tcx());
             if !tcontents.interior_unsafe() {
                 llvm::LLVMSetGlobalConstant(g, llvm::True);
             }
diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs
index eb5ca7722fd..8f5572f5c4e 100644
--- a/src/librustc_trans/trans/context.rs
+++ b/src/librustc_trans/trans/context.rs
@@ -16,14 +16,16 @@ use middle::def::ExportMap;
 use middle::def_id::DefId;
 use middle::traits;
 use rustc::mir::mir_map::MirMap;
+use rustc::mir::repr as mir;
 use trans::adt;
 use trans::base;
 use trans::builder::Builder;
-use trans::common::{ExternMap,BuilderRef_res};
+use trans::common::BuilderRef_res;
 use trans::debuginfo;
 use trans::declare;
 use trans::glue::DropGlueKind;
-use trans::monomorphize::MonoId;
+use trans::mir::CachedMir;
+use trans::monomorphize::Instance;
 use trans::collector::{TransItem, TransItemState};
 use trans::type_::{Type, TypeNames};
 use middle::subst::Substs;
@@ -75,6 +77,7 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> {
     check_overflow: bool,
     check_drop_flag_for_sanity: bool,
     mir_map: &'a MirMap<'tcx>,
+    mir_cache: RefCell<DefIdMap<Rc<mir::Mir<'tcx>>>>,
 
     available_drop_glues: RefCell<FnvHashMap<DropGlueKind<'tcx>, String>>,
     use_dll_storage_attrs: bool,
@@ -90,8 +93,6 @@ pub struct LocalCrateContext<'tcx> {
     llmod: ModuleRef,
     llcx: ContextRef,
     tn: TypeNames,
-    externs: RefCell<ExternMap>,
-    item_vals: RefCell<NodeMap<ValueRef>>,
     needs_unwind_cleanup_cache: RefCell<FnvHashMap<Ty<'tcx>, bool>>,
     fn_pointer_shims: RefCell<FnvHashMap<Ty<'tcx>, ValueRef>>,
     drop_glues: RefCell<FnvHashMap<DropGlueKind<'tcx>, ValueRef>>,
@@ -100,8 +101,8 @@ pub struct LocalCrateContext<'tcx> {
     /// Backwards version of the `external` map (inlined items to where they
     /// came from)
     external_srcs: RefCell<NodeMap<DefId>>,
-    /// Cache instances of monomorphized functions
-    monomorphized: RefCell<FnvHashMap<MonoId<'tcx>, ValueRef>>,
+    /// Cache instances of monomorphic and polymorphic items
+    instances: RefCell<FnvHashMap<Instance<'tcx>, ValueRef>>,
     monomorphizing: RefCell<DefIdMap<usize>>,
     available_monomorphizations: RefCell<FnvHashSet<String>>,
     /// Cache generated vtables
@@ -148,13 +149,13 @@ pub struct LocalCrateContext<'tcx> {
     builder: BuilderRef_res,
 
     /// Holds the LLVM values for closure IDs.
-    closure_vals: RefCell<FnvHashMap<MonoId<'tcx>, ValueRef>>,
+    closure_vals: RefCell<FnvHashMap<Instance<'tcx>, ValueRef>>,
 
     dbg_cx: Option<debuginfo::CrateDebugContext<'tcx>>,
 
-    eh_personality: RefCell<Option<ValueRef>>,
-    eh_unwind_resume: RefCell<Option<ValueRef>>,
-    rust_try_fn: RefCell<Option<ValueRef>>,
+    eh_personality: Cell<Option<ValueRef>>,
+    eh_unwind_resume: Cell<Option<ValueRef>>,
+    rust_try_fn: Cell<Option<ValueRef>>,
 
     intrinsics: RefCell<FnvHashMap<&'static str, ValueRef>>,
 
@@ -340,6 +341,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
             symbol_hasher: RefCell::new(symbol_hasher),
             tcx: tcx,
             mir_map: mir_map,
+            mir_cache: RefCell::new(DefIdMap()),
             stats: Stats {
                 n_glues_created: Cell::new(0),
                 n_null_glues: Cell::new(0),
@@ -464,14 +466,12 @@ impl<'tcx> LocalCrateContext<'tcx> {
                 llmod: llmod,
                 llcx: llcx,
                 tn: TypeNames::new(),
-                externs: RefCell::new(FnvHashMap()),
-                item_vals: RefCell::new(NodeMap()),
                 needs_unwind_cleanup_cache: RefCell::new(FnvHashMap()),
                 fn_pointer_shims: RefCell::new(FnvHashMap()),
                 drop_glues: RefCell::new(FnvHashMap()),
                 external: RefCell::new(DefIdMap()),
                 external_srcs: RefCell::new(NodeMap()),
-                monomorphized: RefCell::new(FnvHashMap()),
+                instances: RefCell::new(FnvHashMap()),
                 monomorphizing: RefCell::new(DefIdMap()),
                 available_monomorphizations: RefCell::new(FnvHashSet()),
                 vtables: RefCell::new(FnvHashMap()),
@@ -492,9 +492,9 @@ impl<'tcx> LocalCrateContext<'tcx> {
                 builder: BuilderRef_res(llvm::LLVMCreateBuilderInContext(llcx)),
                 closure_vals: RefCell::new(FnvHashMap()),
                 dbg_cx: dbg_cx,
-                eh_personality: RefCell::new(None),
-                eh_unwind_resume: RefCell::new(None),
-                rust_try_fn: RefCell::new(None),
+                eh_personality: Cell::new(None),
+                eh_unwind_resume: Cell::new(None),
+                rust_try_fn: Cell::new(None),
                 intrinsics: RefCell::new(FnvHashMap()),
                 n_llvm_insns: Cell::new(0),
                 type_of_depth: Cell::new(0),
@@ -616,14 +616,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
         &self.local.tn
     }
 
-    pub fn externs<'a>(&'a self) -> &'a RefCell<ExternMap> {
-        &self.local.externs
-    }
-
-    pub fn item_vals<'a>(&'a self) -> &'a RefCell<NodeMap<ValueRef>> {
-        &self.local.item_vals
-    }
-
     pub fn export_map<'a>(&'a self) -> &'a ExportMap {
         &self.shared.export_map
     }
@@ -660,8 +652,8 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
         &self.local.external_srcs
     }
 
-    pub fn monomorphized<'a>(&'a self) -> &'a RefCell<FnvHashMap<MonoId<'tcx>, ValueRef>> {
-        &self.local.monomorphized
+    pub fn instances<'a>(&'a self) -> &'a RefCell<FnvHashMap<Instance<'tcx>, ValueRef>> {
+        &self.local.instances
     }
 
     pub fn monomorphizing<'a>(&'a self) -> &'a RefCell<DefIdMap<usize>> {
@@ -746,7 +738,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
         self.local.opaque_vec_type
     }
 
-    pub fn closure_vals<'a>(&'a self) -> &'a RefCell<FnvHashMap<MonoId<'tcx>, ValueRef>> {
+    pub fn closure_vals<'a>(&'a self) -> &'a RefCell<FnvHashMap<Instance<'tcx>, ValueRef>> {
         &self.local.closure_vals
     }
 
@@ -754,15 +746,15 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
         &self.local.dbg_cx
     }
 
-    pub fn eh_personality<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
+    pub fn eh_personality<'a>(&'a self) -> &'a Cell<Option<ValueRef>> {
         &self.local.eh_personality
     }
 
-    pub fn eh_unwind_resume<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
+    pub fn eh_unwind_resume<'a>(&'a self) -> &'a Cell<Option<ValueRef>> {
         &self.local.eh_unwind_resume
     }
 
-    pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
+    pub fn rust_try_fn<'a>(&'a self) -> &'a Cell<Option<ValueRef>> {
         &self.local.rust_try_fn
     }
 
@@ -829,8 +821,22 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
         self.shared.use_dll_storage_attrs()
     }
 
-    pub fn mir_map(&self) -> &'b MirMap<'tcx> {
-        self.shared.mir_map
+    pub fn get_mir(&self, def_id: DefId) -> Option<CachedMir<'b, 'tcx>> {
+        if def_id.is_local() {
+            let node_id = self.tcx().map.as_local_node_id(def_id).unwrap();
+            self.shared.mir_map.map.get(&node_id).map(CachedMir::Ref)
+        } else {
+            if let Some(mir) = self.shared.mir_cache.borrow().get(&def_id).cloned() {
+                return Some(CachedMir::Owned(mir));
+            }
+
+            let mir = self.sess().cstore.maybe_get_item_mir(self.tcx(), def_id);
+            let cached = mir.map(Rc::new);
+            if let Some(ref mir) = cached {
+                self.shared.mir_cache.borrow_mut().insert(def_id, mir.clone());
+            }
+            cached.map(CachedMir::Owned)
+        }
     }
 
     pub fn translation_items(&self) -> &RefCell<FnvHashMap<TransItem<'tcx>, TransItemState>> {
@@ -865,8 +871,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: &str) -> Option<ValueRef> {
     macro_rules! ifn {
         ($name:expr, fn() -> $ret:expr) => (
             if key == $name {
-                let f = declare::declare_cfn(ccx, $name, Type::func(&[], &$ret),
-                                             ccx.tcx().mk_nil());
+                let f = declare::declare_cfn(ccx, $name, Type::func(&[], &$ret));
                 llvm::SetUnnamedAddr(f, false);
                 ccx.intrinsics().borrow_mut().insert($name, f.clone());
                 return Some(f);
@@ -874,9 +879,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: &str) -> Option<ValueRef> {
         );
         ($name:expr, fn(...) -> $ret:expr) => (
             if key == $name {
-                let f = declare::declare_cfn(ccx, $name,
-                                             Type::variadic_func(&[], &$ret),
-                                             ccx.tcx().mk_nil());
+                let f = declare::declare_cfn(ccx, $name, Type::variadic_func(&[], &$ret));
                 llvm::SetUnnamedAddr(f, false);
                 ccx.intrinsics().borrow_mut().insert($name, f.clone());
                 return Some(f);
@@ -884,8 +887,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: &str) -> Option<ValueRef> {
         );
         ($name:expr, fn($($arg:expr),*) -> $ret:expr) => (
             if key == $name {
-                let f = declare::declare_cfn(ccx, $name, Type::func(&[$($arg),*], &$ret),
-                                             ccx.tcx().mk_nil());
+                let f = declare::declare_cfn(ccx, $name, Type::func(&[$($arg),*], &$ret));
                 llvm::SetUnnamedAddr(f, false);
                 ccx.intrinsics().borrow_mut().insert($name, f.clone());
                 return Some(f);
@@ -1032,8 +1034,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: &str) -> Option<ValueRef> {
                 ifn!($name, fn($($arg),*) -> void);
             } else if key == $name {
                 let f = declare::declare_cfn(ccx, stringify!($cname),
-                                             Type::func(&[$($arg),*], &void),
-                                             ccx.tcx().mk_nil());
+                                             Type::func(&[$($arg),*], &void));
                 llvm::SetLinkage(f, llvm::InternalLinkage);
 
                 let bld = ccx.builder();
@@ -1055,8 +1056,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: &str) -> Option<ValueRef> {
                 ifn!($name, fn($($arg),*) -> $ret);
             } else if key == $name {
                 let f = declare::declare_cfn(ccx, stringify!($cname),
-                                             Type::func(&[$($arg),*], &$ret),
-                                             ccx.tcx().mk_nil());
+                                             Type::func(&[$($arg),*], &$ret));
                 ccx.intrinsics().borrow_mut().insert($name, f.clone());
                 return Some(f);
             }
diff --git a/src/librustc_trans/trans/controlflow.rs b/src/librustc_trans/trans/controlflow.rs
index e84e1b45ced..91454df1566 100644
--- a/src/librustc_trans/trans/controlflow.rs
+++ b/src/librustc_trans/trans/controlflow.rs
@@ -11,10 +11,11 @@
 use llvm::ValueRef;
 use middle::def::Def;
 use middle::lang_items::{PanicFnLangItem, PanicBoundsCheckFnLangItem};
+use middle::subst::Substs;
 use trans::base::*;
 use trans::basic_block::BasicBlock;
 use trans::build::*;
-use trans::callee;
+use trans::callee::{Callee, ArgVals};
 use trans::cleanup::CleanupMethods;
 use trans::cleanup;
 use trans::common::*;
@@ -24,7 +25,6 @@ use trans::debuginfo::{DebugLoc, ToDebugLoc};
 use trans::expr;
 use trans::machine;
 use trans;
-use middle::ty;
 
 use rustc_front::hir;
 use rustc_front::util as ast_util;
@@ -152,9 +152,8 @@ pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                             els: Option<&hir::Expr>,
                             dest: expr::Dest)
                             -> Block<'blk, 'tcx> {
-    debug!("trans_if(bcx={}, if_id={}, cond={:?}, thn={}, dest={})",
-           bcx.to_str(), if_id, cond, thn.id,
-           dest.to_string(bcx.ccx()));
+    debug!("trans_if(bcx={}, if_id={}, cond={:?}, thn={}, dest={:?})",
+           bcx.to_str(), if_id, cond, thn.id, dest);
     let _icx = push_ctxt("trans_if");
 
     if bcx.unreachable.get() {
@@ -363,14 +362,12 @@ pub fn trans_ret<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
     let fcx = bcx.fcx;
     let mut bcx = bcx;
-    let dest = match (fcx.llretslotptr.get(), retval_expr) {
-        (Some(_), Some(retval_expr)) => {
-            let ret_ty = expr_ty_adjusted(bcx, &retval_expr);
-            expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(ret_ty), "ret_slot"))
-        }
-        _ => expr::Ignore,
-    };
     if let Some(x) = retval_expr {
+        let dest = if fcx.llretslotptr.get().is_some() {
+            expr::SaveIn(fcx.get_ret_slot(bcx, "ret_slot"))
+        } else {
+            expr::Ignore
+        };
         bcx = expr::trans_into(bcx, &x, dest);
         match dest {
             expr::SaveIn(slot) if fcx.needs_ret_allocas => {
@@ -406,13 +403,8 @@ pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let expr_file_line = consts::addr_of(ccx, expr_file_line_const, align, "panic_loc");
     let args = vec!(expr_file_line);
     let did = langcall(bcx, Some(call_info.span), "", PanicFnLangItem);
-    let bcx = callee::trans_lang_call(bcx,
-                                      did,
-                                      &args[..],
-                                      Some(expr::Ignore),
-                                      call_info.debug_loc()).bcx;
-    Unreachable(bcx);
-    return bcx;
+    Callee::def(ccx, did, ccx.tcx().mk_substs(Substs::empty()))
+        .call(bcx, call_info.debug_loc(), ArgVals(&args), None).bcx
 }
 
 pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
@@ -439,11 +431,6 @@ pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let file_line = consts::addr_of(ccx, file_line_const, align, "panic_bounds_check_loc");
     let args = vec!(file_line, index, len);
     let did = langcall(bcx, Some(call_info.span), "", PanicBoundsCheckFnLangItem);
-    let bcx = callee::trans_lang_call(bcx,
-                                      did,
-                                      &args[..],
-                                      Some(expr::Ignore),
-                                      call_info.debug_loc()).bcx;
-    Unreachable(bcx);
-    return bcx;
+    Callee::def(ccx, did, ccx.tcx().mk_substs(Substs::empty()))
+        .call(bcx, call_info.debug_loc(), ArgVals(&args), None).bcx
 }
diff --git a/src/librustc_trans/trans/datum.rs b/src/librustc_trans/trans/datum.rs
index 32f263746d3..649f6180de2 100644
--- a/src/librustc_trans/trans/datum.rs
+++ b/src/librustc_trans/trans/datum.rs
@@ -101,6 +101,7 @@ use trans::cleanup;
 use trans::cleanup::{CleanupMethods, DropHintDatum, DropHintMethods};
 use trans::expr;
 use trans::tvec;
+use trans::value::Value;
 use middle::ty::Ty;
 
 use std::fmt;
@@ -111,7 +112,7 @@ use syntax::codemap::DUMMY_SP;
 /// describes where the value is stored, what Rust type the value has,
 /// whether it is addressed by reference, and so forth. Please refer
 /// the section on datums in `README.md` for more details.
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy)]
 pub struct Datum<'tcx, K> {
     /// The llvm value.  This is either a pointer to the Rust value or
     /// the value itself, depending on `kind` below.
@@ -124,6 +125,13 @@ pub struct Datum<'tcx, K> {
     pub kind: K,
 }
 
+impl<'tcx, K: fmt::Debug> fmt::Debug for Datum<'tcx, K> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "Datum({:?}, {:?}, {:?})",
+               Value(self.val), self.ty, self.kind)
+    }
+}
+
 pub struct DatumBlock<'blk, 'tcx: 'blk, K> {
     pub bcx: Block<'blk, 'tcx>,
     pub datum: Datum<'tcx, K>,
@@ -298,24 +306,23 @@ pub fn immediate_rvalue_bcx<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 /// caller can prove that either (1.) the code injected by `populate`
 /// onto `bcx` always dominates the end of `scope`, or (2.) the data
 /// being allocated has no associated destructor.
-pub fn lvalue_scratch_datum<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>,
-                                              ty: Ty<'tcx>,
-                                              name: &str,
-                                              zero: InitAlloca,
-                                              scope: cleanup::ScopeId,
-                                              arg: A,
-                                              populate: F)
-                                              -> DatumBlock<'blk, 'tcx, Lvalue> where
-    F: FnOnce(A, Block<'blk, 'tcx>, ValueRef) -> Block<'blk, 'tcx>,
+pub fn lvalue_scratch_datum<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
+                                           ty: Ty<'tcx>,
+                                           name: &str,
+                                           zero: InitAlloca,
+                                           scope: cleanup::ScopeId,
+                                           populate: F)
+                                           -> DatumBlock<'blk, 'tcx, Lvalue> where
+    F: FnOnce(Block<'blk, 'tcx>, ValueRef) -> Block<'blk, 'tcx>,
 {
     // Very subtle: potentially initialize the scratch memory at point where it is alloca'ed.
     // (See discussion at Issue 30530.)
     let scratch = alloc_ty_init(bcx, ty, zero, name);
-    debug!("lvalue_scratch_datum scope={:?} scratch={} ty={:?}",
-           scope, bcx.ccx().tn().val_to_string(scratch), ty);
+    debug!("lvalue_scratch_datum scope={:?} scratch={:?} ty={:?}",
+           scope, Value(scratch), ty);
 
     // Subtle. Populate the scratch memory *before* scheduling cleanup.
-    let bcx = populate(arg, bcx, scratch);
+    let bcx = populate(bcx, scratch);
     bcx.fcx.schedule_drop_mem(scope, scratch, ty, None);
 
     DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue::new("datum::lvalue_scratch_datum")))
@@ -351,8 +358,8 @@ fn add_rvalue_clean<'a, 'tcx>(mode: RvalueMode,
                               scope: cleanup::ScopeId,
                               val: ValueRef,
                               ty: Ty<'tcx>) {
-    debug!("add_rvalue_clean scope={:?} val={} ty={:?}",
-           scope, fcx.ccx.tn().val_to_string(val), ty);
+    debug!("add_rvalue_clean scope={:?} val={:?} ty={:?}",
+           scope, Value(val), ty);
     match mode {
         ByValue => { fcx.schedule_drop_immediate(scope, val, ty); }
         ByRef => {
@@ -509,14 +516,14 @@ impl<'tcx> Datum<'tcx, Rvalue> {
 
             ByValue => {
                 lvalue_scratch_datum(
-                    bcx, self.ty, name, InitAlloca::Dropped, scope, self,
-                    |this, bcx, llval| {
+                    bcx, self.ty, name, InitAlloca::Dropped, scope,
+                    |bcx, llval| {
                         debug!("populate call for Datum::to_lvalue_datum_in_scope \
-                                self.ty={:?}", this.ty);
+                                self.ty={:?}", self.ty);
                         // do not call_lifetime_start here; the
                         // `InitAlloc::Dropped` will start scratch
                         // value's lifetime at open of function body.
-                        let bcx = this.store_to(bcx, llval);
+                        let bcx = self.store_to(bcx, llval);
                         bcx.fcx.schedule_lifetime_end(scope, llval);
                         bcx
                     })
@@ -617,7 +624,7 @@ impl<'tcx> Datum<'tcx, Expr> {
                                  name: &str,
                                  expr_id: ast::NodeId)
                                  -> DatumBlock<'blk, 'tcx, Lvalue> {
-        debug!("to_lvalue_datum self: {}", self.to_string(bcx.ccx()));
+        debug!("to_lvalue_datum self: {:?}", self);
 
         self.match_kind(
             |l| DatumBlock::new(bcx, l),
@@ -767,14 +774,6 @@ impl<'tcx, K: KindOps + fmt::Debug> Datum<'tcx, K> {
         self.shallow_copy_raw(bcx, dst)
     }
 
-    #[allow(dead_code)] // useful for debugging
-    pub fn to_string<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> String {
-        format!("Datum({}, {:?}, {:?})",
-                ccx.tn().val_to_string(self.val),
-                self.ty,
-                self.kind)
-    }
-
     /// See the `appropriate_rvalue_mode()` function
     pub fn appropriate_rvalue_mode<'a>(&self, ccx: &CrateContext<'a, 'tcx>)
                                        -> RvalueMode {
diff --git a/src/librustc_trans/trans/debuginfo/create_scope_map.rs b/src/librustc_trans/trans/debuginfo/create_scope_map.rs
index 4ba103c0c0d..41fed12e7bf 100644
--- a/src/librustc_trans/trans/debuginfo/create_scope_map.rs
+++ b/src/librustc_trans/trans/debuginfo/create_scope_map.rs
@@ -468,16 +468,13 @@ fn walk_expr(cx: &CrateContext,
             }
         }
 
-        hir::ExprInlineAsm(hir::InlineAsm { ref inputs,
-                                            ref outputs,
-                                            .. }) => {
-            // inputs, outputs: Vec<(String, P<Expr>)>
-            for &(_, ref exp) in inputs {
-                walk_expr(cx, &exp, scope_stack, scope_map);
+        hir::ExprInlineAsm(_, ref outputs, ref inputs) => {
+            for output in outputs {
+                walk_expr(cx, output, scope_stack, scope_map);
             }
 
-            for out in outputs {
-                walk_expr(cx, &out.expr, scope_stack, scope_map);
+            for input in inputs {
+                walk_expr(cx, input, scope_stack, scope_map);
             }
         }
     }
diff --git a/src/librustc_trans/trans/debuginfo/mod.rs b/src/librustc_trans/trans/debuginfo/mod.rs
index 15275a46e9b..40eb29ed250 100644
--- a/src/librustc_trans/trans/debuginfo/mod.rs
+++ b/src/librustc_trans/trans/debuginfo/mod.rs
@@ -32,9 +32,10 @@ use middle::subst::{self, Substs};
 use rustc_front;
 use rustc_front::hir;
 
+use trans::abi::Abi;
 use trans::common::{NodeIdAndSpan, CrateContext, FunctionContext, Block};
 use trans;
-use trans::{monomorphize, type_of};
+use trans::monomorphize;
 use middle::infer;
 use middle::ty::{self, Ty};
 use session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo};
@@ -49,7 +50,6 @@ use std::rc::Rc;
 
 use syntax::codemap::{Span, Pos};
 use syntax::{ast, codemap};
-use syntax::abi::Abi;
 use syntax::attr::IntType;
 use syntax::parse::token::{self, special_idents};
 
@@ -456,10 +456,10 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             ty::FnDiverging => diverging_type_metadata(cx)
         });
 
-        let inputs = &if abi == Abi::RustCall {
-            type_of::untuple_arguments(cx, &sig.inputs)
+        let inputs = if abi == Abi::RustCall {
+            &sig.inputs[..sig.inputs.len()-1]
         } else {
-            sig.inputs
+            &sig.inputs[..]
         };
 
         // Arguments types
@@ -467,6 +467,14 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             signature.push(type_metadata(cx, argument_type, codemap::DUMMY_SP));
         }
 
+        if abi == Abi::RustCall && !sig.inputs.is_empty() {
+            if let ty::TyTuple(ref args) = sig.inputs[sig.inputs.len() - 1].sty {
+                for &argument_type in args {
+                    signature.push(type_metadata(cx, argument_type, codemap::DUMMY_SP));
+                }
+            }
+        }
+
         return create_DIArray(DIB(cx), &signature[..]);
     }
 
diff --git a/src/librustc_trans/trans/debuginfo/type_names.rs b/src/librustc_trans/trans/debuginfo/type_names.rs
index cc9067677b2..b71b7789aff 100644
--- a/src/librustc_trans/trans/debuginfo/type_names.rs
+++ b/src/librustc_trans/trans/debuginfo/type_names.rs
@@ -107,7 +107,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 output.push_str("unsafe ");
             }
 
-            if abi != ::syntax::abi::Abi::Rust {
+            if abi != ::trans::abi::Abi::Rust {
                 output.push_str("extern \"");
                 output.push_str(abi.name());
                 output.push_str("\" ");
diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs
index 0c512200ff3..e63f17770bc 100644
--- a/src/librustc_trans/trans/declare.rs
+++ b/src/librustc_trans/trans/declare.rs
@@ -22,13 +22,10 @@
 use llvm::{self, ValueRef};
 use middle::ty;
 use middle::infer;
-use middle::traits::ProjectionMode;
-use syntax::abi::Abi;
+use trans::abi::{Abi, FnType};
 use trans::attributes;
-use trans::base;
 use trans::context::CrateContext;
 use trans::type_::Type;
-use trans::type_of;
 
 use std::ffi::CString;
 use libc::c_uint;
@@ -51,13 +48,10 @@ pub fn declare_global(ccx: &CrateContext, name: &str, ty: Type) -> llvm::ValueRe
 
 /// Declare a function.
 ///
-/// For rust functions use `declare_rust_fn` instead.
-///
 /// If there’s a value with the same name already declared, the function will
 /// update the declaration and return existing ValueRef instead.
-pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv,
-                  ty: Type, output: ty::FnOutput) -> ValueRef {
-    debug!("declare_fn(name={:?})", name);
+fn declare_raw_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty: Type) -> ValueRef {
+    debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty);
     let namebuf = CString::new(name).unwrap_or_else(|_|{
         ccx.sess().bug(&format!("name {:?} contains an interior null byte", name))
     });
@@ -70,10 +64,6 @@ pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv,
     // be merged.
     llvm::SetUnnamedAddr(llfn, true);
 
-    if output == ty::FnDiverging {
-        llvm::SetFunctionAttribute(llfn, llvm::Attribute::NoReturn);
-    }
-
     if ccx.tcx().sess.opts.cg.no_redzone
         .unwrap_or(ccx.tcx().sess.target.target.options.disable_redzone) {
         llvm::SetFunctionAttribute(llfn, llvm::Attribute::NoRedZone)
@@ -86,13 +76,12 @@ pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv,
 /// Declare a C ABI function.
 ///
 /// Only use this for foreign function ABIs and glue. For Rust functions use
-/// `declare_rust_fn` instead.
+/// `declare_fn` instead.
 ///
 /// If there’s a value with the same name already declared, the function will
 /// update the declaration and return existing ValueRef instead.
-pub fn declare_cfn(ccx: &CrateContext, name: &str, fn_type: Type,
-                   output: ty::Ty) -> ValueRef {
-    declare_fn(ccx, name, llvm::CCallConv, fn_type, ty::FnConverging(output))
+pub fn declare_cfn(ccx: &CrateContext, name: &str, fn_type: Type) -> ValueRef {
+    declare_raw_fn(ccx, name, llvm::CCallConv, fn_type)
 }
 
 
@@ -100,53 +89,27 @@ pub fn declare_cfn(ccx: &CrateContext, name: &str, fn_type: Type,
 ///
 /// If there’s a value with the same name already declared, the function will
 /// update the declaration and return existing ValueRef instead.
-pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str,
-                                 fn_type: ty::Ty<'tcx>) -> ValueRef {
-    debug!("declare_rust_fn(name={:?}, fn_type={:?})", name,
-           fn_type);
-
-    let function_type; // placeholder so that the memory ownership works out ok
-    let (sig, abi, env) = match fn_type.sty {
-        ty::TyFnDef(_, _, f) |
-        ty::TyFnPtr(f) => {
-            (&f.sig, f.abi, None)
-        }
-        ty::TyClosure(closure_did, ref substs) => {
-            let infcx = infer::normalizing_infer_ctxt(ccx.tcx(),
-                                                      &ccx.tcx().tables,
-                                                      ProjectionMode::Any);
-            function_type = infcx.closure_type(closure_did, substs);
-            let self_type = base::self_type_for_closure(ccx, closure_did, fn_type);
-            let llenvironment_type = type_of::type_of_explicit_arg(ccx, self_type);
-            debug!("declare_rust_fn function_type={:?} self_type={:?}",
-                   function_type, self_type);
-            (&function_type.sig, Abi::RustCall, Some(llenvironment_type))
-        }
-        _ => ccx.sess().bug("expected closure or fn")
-    };
-
-    let sig = ccx.tcx().erase_late_bound_regions(sig);
+pub fn declare_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str,
+                            fn_type: ty::Ty<'tcx>) -> ValueRef {
+    debug!("declare_rust_fn(name={:?}, fn_type={:?})", name, fn_type);
+    let abi = fn_type.fn_abi();
+    let sig = ccx.tcx().erase_late_bound_regions(fn_type.fn_sig());
     let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
     debug!("declare_rust_fn (after region erasure) sig={:?}", sig);
-    let llfty = type_of::type_of_rust_fn(ccx, env, &sig, abi);
-    debug!("declare_rust_fn llfty={}", ccx.tn().type_to_string(llfty));
 
-    // it is ok to directly access sig.0.output because we erased all
-    // late-bound-regions above
-    let llfn = declare_fn(ccx, name, llvm::CCallConv, llfty, sig.output);
-    attributes::from_fn_type(ccx, fn_type).apply_llfn(llfn);
-    llfn
-}
+    let fty = FnType::new(ccx, abi, &sig, &[]);
+    let llfn = declare_raw_fn(ccx, name, fty.cconv, fty.llvm_type(ccx));
 
+    if sig.output == ty::FnDiverging {
+        llvm::SetFunctionAttribute(llfn, llvm::Attribute::NoReturn);
+    }
+
+    if abi != Abi::Rust && abi != Abi::RustCall {
+        attributes::unwind(llfn, false);
+    }
+
+    fty.apply_attrs_llfn(llfn);
 
-/// Declare a Rust function with internal linkage.
-///
-/// If there’s a value with the same name already declared, the function will
-/// update the declaration and return existing ValueRef instead.
-pub fn declare_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str,
-                                          fn_type: ty::Ty<'tcx>) -> ValueRef {
-    let llfn = declare_rust_fn(ccx, name, fn_type);
-    llvm::SetLinkage(llfn, llvm::InternalLinkage);
     llfn
 }
 
@@ -166,78 +129,27 @@ pub fn define_global(ccx: &CrateContext, name: &str, ty: Type) -> Option<ValueRe
 }
 
 
-/// Declare a function with an intention to define it.
-///
-/// For rust functions use `define_rust_fn` instead.
-///
-/// Use this function when you intend to define a function. This function will
-/// return None if the name already has a definition associated with it. In that
-/// case an error should be reported to the user, because it usually happens due
-/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes).
-pub fn define_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv,
-                 fn_type: Type, output: ty::FnOutput) -> Option<ValueRef> {
-    if get_defined_value(ccx, name).is_some() {
-        None
-    } else {
-        Some(declare_fn(ccx, name, callconv, fn_type, output))
-    }
-}
-
-
-/// Declare a C ABI function with an intention to define it.
-///
-/// Use this function when you intend to define a function. This function will
-/// return None if the name already has a definition associated with it. In that
-/// case an error should be reported to the user, because it usually happens due
-/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes).
-///
-/// Only use this for foreign function ABIs and glue. For Rust functions use
-/// `declare_rust_fn` instead.
-pub fn define_cfn(ccx: &CrateContext, name: &str, fn_type: Type,
-                  output: ty::Ty) -> Option<ValueRef> {
-    if get_defined_value(ccx, name).is_some() {
-        None
-    } else {
-        Some(declare_cfn(ccx, name, fn_type, output))
-    }
-}
-
-
-/// Declare a Rust function with an intention to define it.
-///
-/// Use this function when you intend to define a function. This function will
-/// return None if the name already has a definition associated with it. In that
-/// case an error should be reported to the user, because it usually happens due
-/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes).
-pub fn define_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str,
-                                fn_type: ty::Ty<'tcx>) -> Option<ValueRef> {
-    if get_defined_value(ccx, name).is_some() {
-        None
-    } else {
-        Some(declare_rust_fn(ccx, name, fn_type))
-    }
-}
-
-
 /// Declare a Rust function with an intention to define it.
 ///
 /// Use this function when you intend to define a function. This function will
 /// return panic if the name already has a definition associated with it. This
 /// can happen with #[no_mangle] or #[export_name], for example.
-pub fn define_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                         name: &str,
-                                         fn_type: ty::Ty<'tcx>) -> ValueRef {
+pub fn define_internal_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                                    name: &str,
+                                    fn_type: ty::Ty<'tcx>) -> ValueRef {
     if get_defined_value(ccx, name).is_some() {
         ccx.sess().fatal(&format!("symbol `{}` already defined", name))
     } else {
-        declare_internal_rust_fn(ccx, name, fn_type)
+        let llfn = declare_fn(ccx, name, fn_type);
+        llvm::SetLinkage(llfn, llvm::InternalLinkage);
+        llfn
     }
 }
 
 
 /// Get defined or externally defined (AvailableExternally linkage) value by
 /// name.
-fn get_defined_value(ccx: &CrateContext, name: &str) -> Option<ValueRef> {
+pub fn get_defined_value(ccx: &CrateContext, name: &str) -> Option<ValueRef> {
     debug!("get_defined_value(name={:?})", name);
     let namebuf = CString::new(name).unwrap_or_else(|_|{
         ccx.sess().bug(&format!("name {:?} contains an interior null byte", name))
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index ae03f58bce0..abcd703f33b 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -44,19 +44,18 @@
 //!   expression and ensures that the result has a cleanup associated with it,
 //!   creating a temporary stack slot if necessary.
 //!
-//! - `trans_local_var -> Datum`: looks up a local variable or upvar.
+//! - `trans_var -> Datum`: looks up a local variable, upvar or static.
 
 #![allow(non_camel_case_types)]
 
 pub use self::Dest::*;
 use self::lazy_binop_ty::*;
 
-use back::abi;
 use llvm::{self, ValueRef, TypeKind};
 use middle::const_qualif::ConstQualif;
 use middle::def::Def;
 use middle::subst::Substs;
-use trans::{_match, adt, asm, base, closure, consts, controlflow};
+use trans::{_match, abi, adt, asm, base, closure, consts, controlflow};
 use trans::base::*;
 use trans::build::*;
 use trans::callee::{Callee, ArgExprs, ArgOverloadedCall, ArgOverloadedOp};
@@ -69,6 +68,7 @@ use trans::glue;
 use trans::machine;
 use trans::tvec;
 use trans::type_of;
+use trans::value::Value;
 use trans::Disr;
 use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer};
 use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
@@ -85,6 +85,7 @@ use rustc_front::hir;
 
 use syntax::{ast, codemap};
 use syntax::parse::token::InternedString;
+use std::fmt;
 use std::mem;
 
 // Destinations
@@ -98,11 +99,11 @@ pub enum Dest {
     Ignore,
 }
 
-impl Dest {
-    pub fn to_string(&self, ccx: &CrateContext) -> String {
+impl fmt::Debug for Dest {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
-            SaveIn(v) => format!("SaveIn({})", ccx.tn().val_to_string(v)),
-            Ignore => "Ignore".to_string()
+            SaveIn(v) => write!(f, "SaveIn({:?})", Value(v)),
+            Ignore => f.write_str("Ignore")
         }
     }
 }
@@ -377,15 +378,13 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         }
         Some(adj) => { adj }
     };
-    debug!("unadjusted datum for expr {:?}: {} adjustment={:?}",
-           expr,
-           datum.to_string(bcx.ccx()),
-           adjustment);
+    debug!("unadjusted datum for expr {:?}: {:?} adjustment={:?}",
+           expr, datum, adjustment);
     match adjustment {
         AdjustReifyFnPointer => {
             match datum.ty.sty {
                 ty::TyFnDef(def_id, substs, _) => {
-                    datum = Callee::def(bcx.ccx(), def_id, substs, datum.ty)
+                    datum = Callee::def(bcx.ccx(), def_id, substs)
                         .reify(bcx.ccx()).to_expr_datum();
                 }
                 _ => {
@@ -452,7 +451,7 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             }
         }
     }
-    debug!("after adjustments, datum={}", datum.to_string(bcx.ccx()));
+    debug!("after adjustments, datum={:?}", datum);
     DatumBlock::new(bcx, datum)
 }
 
@@ -462,9 +461,7 @@ fn coerce_unsized<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                               target: Datum<'tcx, Rvalue>)
                               -> Block<'blk, 'tcx> {
     let mut bcx = bcx;
-    debug!("coerce_unsized({} -> {})",
-           source.to_string(bcx.ccx()),
-           target.to_string(bcx.ccx()));
+    debug!("coerce_unsized({:?} -> {:?})", source, target);
 
     match (&source.ty.sty, &target.ty.sty) {
         (&ty::TyBox(a), &ty::TyBox(b)) |
@@ -654,7 +651,8 @@ fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             trans(bcx, &e)
         }
         hir::ExprPath(..) => {
-            trans_def(bcx, expr, bcx.def(expr.id))
+            let var = trans_var(bcx, bcx.def(expr.id));
+            DatumBlock::new(bcx, var.to_expr_datum())
         }
         hir::ExprField(ref base, name) => {
             trans_rec_field(bcx, &base, name.node)
@@ -854,8 +852,8 @@ fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
             let (base, len) = base_datum.get_vec_base_and_len(bcx);
 
-            debug!("trans_index: base {}", bcx.val_to_string(base));
-            debug!("trans_index: len {}", bcx.val_to_string(len));
+            debug!("trans_index: base {:?}", Value(base));
+            debug!("trans_index: len {:?}", Value(len));
 
             let bounds_check = ICmp(bcx,
                                     llvm::IntUGE,
@@ -866,7 +864,6 @@ fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             let expected = Call(bcx,
                                 expect,
                                 &[bounds_check, C_bool(ccx, false)],
-                                None,
                                 index_expr_debug_loc);
             bcx = with_cond(bcx, expected, |bcx| {
                 controlflow::trans_fail_bounds_check(bcx,
@@ -884,27 +881,40 @@ fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     DatumBlock::new(bcx, elt_datum)
 }
 
-fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                         ref_expr: &hir::Expr,
-                         def: Def)
-                         -> DatumBlock<'blk, 'tcx, Expr> {
-    //! Translates a reference to a path.
+/// Translates a reference to a variable.
+pub fn trans_var<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, def: Def)
+                             -> Datum<'tcx, Lvalue> {
 
-    let _icx = push_ctxt("trans_def_lvalue");
     match def {
-        Def::Static(did, _) => {
-            let const_ty = expr_ty(bcx, ref_expr);
-            let val = get_static_val(bcx.ccx(), did, const_ty);
-            let lval = Lvalue::new("expr::trans_def");
-            DatumBlock::new(bcx, Datum::new(val, const_ty, LvalueExpr(lval)))
-        }
-        Def::Local(..) | Def::Upvar(..) => {
-            DatumBlock::new(bcx, trans_local_var(bcx, def).to_expr_datum())
+        Def::Static(did, _) => consts::get_static(bcx.ccx(), did),
+        Def::Upvar(_, nid, _, _) => {
+            // Can't move upvars, so this is never a ZeroMemLastUse.
+            let local_ty = node_id_type(bcx, nid);
+            let lval = Lvalue::new_with_hint("expr::trans_var (upvar)",
+                                             bcx, nid, HintKind::ZeroAndMaintain);
+            match bcx.fcx.llupvars.borrow().get(&nid) {
+                Some(&val) => Datum::new(val, local_ty, lval),
+                None => {
+                    bcx.sess().bug(&format!(
+                        "trans_var: no llval for upvar {} found",
+                        nid));
+                }
+            }
         }
-        _ => {
-            bcx.sess().span_bug(ref_expr.span,
-                &format!("{:?} should not reach expr::trans_def", def))
+        Def::Local(_, nid) => {
+            let datum = match bcx.fcx.lllocals.borrow().get(&nid) {
+                Some(&v) => v,
+                None => {
+                    bcx.sess().bug(&format!(
+                        "trans_var: no datum for local/arg {} found",
+                        nid));
+                }
+            };
+            debug!("take_local(nid={}, v={:?}, ty={})",
+                   nid, Value(datum.val), datum.ty);
+            datum
         }
+        _ => unreachable!("{:?} should not reach expr::trans_var", def)
     }
 }
 
@@ -1027,8 +1037,18 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 trans_assign_op(bcx, expr, op, &dst, &src)
             }
         }
-        hir::ExprInlineAsm(ref a) => {
-            asm::trans_inline_asm(bcx, a)
+        hir::ExprInlineAsm(ref a, ref outputs, ref inputs) => {
+            let outputs = outputs.iter().map(|output| {
+                let out_datum = unpack_datum!(bcx, trans(bcx, output));
+                unpack_datum!(bcx, out_datum.to_lvalue_datum(bcx, "out", expr.id))
+            }).collect();
+            let inputs = inputs.iter().map(|input| {
+                let input = unpack_datum!(bcx, trans(bcx, input));
+                let input = unpack_datum!(bcx, input.to_rvalue_datum(bcx, "in"));
+                input.to_llscalarish(bcx)
+            }).collect();
+            asm::trans_inline_asm(bcx, a, outputs, inputs);
+            bcx
         }
         _ => {
             bcx.tcx().sess.span_bug(
@@ -1131,8 +1151,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                         body,
                                         expr.id,
                                         def_id,
-                                        substs,
-                                        &expr.attrs).unwrap_or(bcx)
+                                        substs).unwrap_or(bcx)
         }
         hir::ExprCall(ref f, ref args) => {
             let method = bcx.tcx().tables.borrow().method_map.get(&method_call).cloned();
@@ -1145,7 +1164,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 let f = unpack_datum!(bcx, trans(bcx, f));
                 (match f.ty.sty {
                     ty::TyFnDef(def_id, substs, _) => {
-                        Callee::def(bcx.ccx(), def_id, substs, f.ty)
+                        Callee::def(bcx.ccx(), def_id, substs)
                     }
                     ty::TyFnPtr(_) => {
                         let f = unpack_datum!(bcx,
@@ -1249,48 +1268,6 @@ fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     }
 }
 
-/// Translates a reference to a local variable or argument. This always results in an lvalue datum.
-pub fn trans_local_var<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                   def: Def)
-                                   -> Datum<'tcx, Lvalue> {
-    let _icx = push_ctxt("trans_local_var");
-
-    match def {
-        Def::Upvar(_, nid, _, _) => {
-            // Can't move upvars, so this is never a ZeroMemLastUse.
-            let local_ty = node_id_type(bcx, nid);
-            let lval = Lvalue::new_with_hint("expr::trans_local_var (upvar)",
-                                             bcx, nid, HintKind::ZeroAndMaintain);
-            match bcx.fcx.llupvars.borrow().get(&nid) {
-                Some(&val) => Datum::new(val, local_ty, lval),
-                None => {
-                    bcx.sess().bug(&format!(
-                        "trans_local_var: no llval for upvar {} found",
-                        nid));
-                }
-            }
-        }
-        Def::Local(_, nid) => {
-            let datum = match bcx.fcx.lllocals.borrow().get(&nid) {
-                Some(&v) => v,
-                None => {
-                    bcx.sess().bug(&format!(
-                        "trans_local_var: no datum for local/arg {} found",
-                        nid));
-                }
-            };
-            debug!("take_local(nid={}, v={}, ty={})",
-                   nid, bcx.val_to_string(datum.val), datum.ty);
-            datum
-        }
-        _ => {
-            bcx.sess().unimpl(&format!(
-                "unsupported def type in trans_local_var: {:?}",
-                def));
-        }
-    }
-}
-
 fn trans_struct<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                             fields: &[hir::Field],
                             base: Option<&hir::Expr>,
@@ -1708,15 +1685,14 @@ fn trans_scalar_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             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);
+                let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty);
                 if lhs_t == tcx.types.f32 {
                     let lhs = FPExt(bcx, lhs, f64t);
                     let rhs = FPExt(bcx, rhs, f64t);
-                    let res = Call(bcx, llfn, &[lhs, rhs], None, binop_debug_loc);
+                    let res = Call(bcx, llfn, &[lhs, rhs], binop_debug_loc);
                     FPTrunc(bcx, res, Type::f32(bcx.ccx()))
                 } else {
-                    Call(bcx, llfn, &[lhs, rhs], None, binop_debug_loc)
+                    Call(bcx, llfn, &[lhs, rhs], binop_debug_loc)
                 }
             } else {
                 FRem(bcx, lhs, rhs, binop_debug_loc)
@@ -1829,12 +1805,10 @@ fn trans_binary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
             let lhs = unpack_datum!(bcx, trans(bcx, lhs));
             let lhs = unpack_datum!(bcx, lhs.to_rvalue_datum(bcx, "binop_lhs"));
-            debug!("trans_binary (expr {}): lhs={}",
-                   expr.id, lhs.to_string(ccx));
+            debug!("trans_binary (expr {}): lhs={:?}", expr.id, lhs);
             let rhs = unpack_datum!(bcx, trans(bcx, rhs));
             let rhs = unpack_datum!(bcx, rhs.to_rvalue_datum(bcx, "binop_rhs"));
-            debug!("trans_binary (expr {}): rhs={}",
-                   expr.id, rhs.to_string(ccx));
+            debug!("trans_binary (expr {}): rhs={:?}", expr.id, rhs);
 
             if type_is_fat_ptr(ccx.tcx(), lhs.ty) {
                 assert!(type_is_fat_ptr(ccx.tcx(), rhs.ty),
@@ -1933,8 +1907,8 @@ fn trans_imm_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let t_out = node_id_type(bcx, id);
 
     debug!("trans_cast({:?} as {:?})", t_in, t_out);
-    let mut ll_t_in = type_of::arg_type_of(ccx, t_in);
-    let ll_t_out = type_of::arg_type_of(ccx, t_out);
+    let mut ll_t_in = type_of::immediate_type_of(ccx, t_in);
+    let ll_t_out = type_of::immediate_type_of(ccx, t_out);
     // Convert the value to be cast into a ValueRef, either by-ref or
     // by-value as appropriate given its type:
     let mut datum = unpack_datum!(bcx, trans(bcx, expr));
@@ -2085,10 +2059,8 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                           -> DatumBlock<'blk, 'tcx, Expr> {
     let ccx = bcx.ccx();
 
-    debug!("deref_once(expr={:?}, datum={}, method_call={:?})",
-           expr,
-           datum.to_string(ccx),
-           method_call);
+    debug!("deref_once(expr={:?}, datum={:?}, method_call={:?})",
+           expr, datum, method_call);
 
     let mut bcx = bcx;
 
@@ -2175,8 +2147,8 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         }
     };
 
-    debug!("deref_once(expr={}, method_call={:?}, result={})",
-           expr.id, method_call, r.datum.to_string(ccx));
+    debug!("deref_once(expr={}, method_call={:?}, result={:?})",
+           expr.id, method_call, r.datum);
 
     return r;
 }
@@ -2291,7 +2263,7 @@ impl OverflowOpViaIntrinsic {
                                         -> (Block<'blk, 'tcx>, ValueRef) {
         let llfn = self.to_intrinsic(bcx, lhs_t);
 
-        let val = Call(bcx, llfn, &[lhs, rhs], None, binop_debug_loc);
+        let val = Call(bcx, llfn, &[lhs, rhs], binop_debug_loc);
         let result = ExtractValue(bcx, val, 0); // iN operation result
         let overflow = ExtractValue(bcx, val, 1); // i1 "did it overflow?"
 
@@ -2300,7 +2272,7 @@ impl OverflowOpViaIntrinsic {
 
         let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1");
         Call(bcx, expect, &[cond, C_integral(Type::i1(bcx.ccx()), 0, false)],
-             None, binop_debug_loc);
+             binop_debug_loc);
 
         let bcx =
             base::with_cond(bcx, cond, |bcx|
diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs
deleted file mode 100644
index cace98a230f..00000000000
--- a/src/librustc_trans/trans/foreign.rs
+++ /dev/null
@@ -1,1072 +0,0 @@
-// 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 back::{abi, link};
-use llvm::{ValueRef, CallConv, get_param};
-use llvm;
-use middle::weak_lang_items;
-use trans::attributes;
-use trans::base::{llvm_linkage_by_name, push_ctxt};
-use trans::base;
-use trans::build::*;
-use trans::cabi;
-use trans::common::*;
-use trans::debuginfo::DebugLoc;
-use trans::declare;
-use trans::expr;
-use trans::machine;
-use trans::monomorphize;
-use trans::type_::Type;
-use trans::type_of::*;
-use trans::type_of;
-use middle::infer;
-use middle::ty::{self, Ty, TyCtxt};
-use middle::subst::Substs;
-
-use std::cmp;
-use std::iter::once;
-use libc::c_uint;
-use syntax::abi::Abi;
-use syntax::attr;
-use syntax::codemap::Span;
-use syntax::parse::token::{InternedString, special_idents};
-use syntax::ast;
-use syntax::attr::AttrMetaMethods;
-
-use rustc_front::print::pprust;
-use rustc_front::hir;
-
-///////////////////////////////////////////////////////////////////////////
-// Type definitions
-
-struct ForeignTypes<'tcx> {
-    /// Rust signature of the function
-    fn_sig: ty::FnSig<'tcx>,
-
-    /// Adapter object for handling native ABI rules (trust me, you
-    /// don't want to know)
-    fn_ty: cabi::FnType,
-
-    /// LLVM types that will appear on the foreign function
-    llsig: LlvmSignature,
-}
-
-struct LlvmSignature {
-    // LLVM versions of the types of this function's arguments.
-    llarg_tys: Vec<Type> ,
-
-    // LLVM version of the type that this function returns.  Note that
-    // this *may not be* the declared return type of the foreign
-    // function, because the foreign function may opt to return via an
-    // out pointer.
-    llret_ty: Type,
-
-    /// True if there is a return value (not bottom, not unit)
-    ret_def: bool,
-}
-
-
-///////////////////////////////////////////////////////////////////////////
-// Calls to external functions
-
-pub fn llvm_calling_convention(ccx: &CrateContext,
-                               abi: Abi) -> CallConv {
-    use syntax::abi::Abi::*;
-    match ccx.sess().target.target.adjust_abi(abi) {
-        RustIntrinsic => {
-            // Intrinsics are emitted at the call site
-            ccx.sess().bug("asked to register intrinsic fn");
-        }
-        PlatformIntrinsic => {
-            // Intrinsics are emitted at the call site
-            ccx.sess().bug("asked to register platform intrinsic fn");
-        }
-
-        Rust => {
-            // FIXME(#3678) Implement linking to foreign fns with Rust ABI
-            ccx.sess().unimpl("foreign functions with Rust ABI");
-        }
-
-        RustCall => {
-            // FIXME(#3678) Implement linking to foreign fns with Rust ABI
-            ccx.sess().unimpl("foreign functions with RustCall ABI");
-        }
-
-        // It's the ABI's job to select this, not us.
-        System => ccx.sess().bug("system abi should be selected elsewhere"),
-
-        Stdcall => llvm::X86StdcallCallConv,
-        Fastcall => llvm::X86FastcallCallConv,
-        Vectorcall => llvm::X86_VectorCall,
-        C => llvm::CCallConv,
-        Win64 => llvm::X86_64_Win64,
-
-        // These API constants ought to be more specific...
-        Cdecl => llvm::CCallConv,
-        Aapcs => llvm::CCallConv,
-    }
-}
-
-pub fn register_static(ccx: &CrateContext,
-                       foreign_item: &hir::ForeignItem) -> ValueRef {
-    let ty = ccx.tcx().node_id_to_type(foreign_item.id);
-    let llty = type_of::type_of(ccx, ty);
-
-    let ident = link_name(foreign_item);
-    let c = match attr::first_attr_value_str_by_name(&foreign_item.attrs,
-                                                     "linkage") {
-        // If this is a static with a linkage specified, then we need to handle
-        // it a little specially. The typesystem prevents things like &T and
-        // extern "C" fn() from being non-null, so we can't just declare a
-        // static and call it a day. Some linkages (like weak) will make it such
-        // that the static actually has a null value.
-        Some(name) => {
-            let linkage = match llvm_linkage_by_name(&name) {
-                Some(linkage) => linkage,
-                None => {
-                    ccx.sess().span_fatal(foreign_item.span,
-                                          "invalid linkage specified");
-                }
-            };
-            let llty2 = match ty.sty {
-                ty::TyRawPtr(ref mt) => type_of::type_of(ccx, mt.ty),
-                _ => {
-                    ccx.sess().span_fatal(foreign_item.span,
-                                          "must have type `*T` or `*mut T`");
-                }
-            };
-            unsafe {
-                // Declare a symbol `foo` with the desired linkage.
-                let g1 = declare::declare_global(ccx, &ident[..], llty2);
-                llvm::SetLinkage(g1, linkage);
-
-                // Declare an internal global `extern_with_linkage_foo` which
-                // is initialized with the address of `foo`.  If `foo` is
-                // discarded during linking (for example, if `foo` has weak
-                // linkage and there are no definitions), then
-                // `extern_with_linkage_foo` will instead be initialized to
-                // zero.
-                let mut real_name = "_rust_extern_with_linkage_".to_string();
-                real_name.push_str(&ident);
-                let g2 = declare::define_global(ccx, &real_name[..], llty).unwrap_or_else(||{
-                    ccx.sess().span_fatal(foreign_item.span,
-                                          &format!("symbol `{}` is already defined", ident))
-                });
-                llvm::SetLinkage(g2, llvm::InternalLinkage);
-                llvm::LLVMSetInitializer(g2, g1);
-                g2
-            }
-        }
-        None => // Generate an external declaration.
-            declare::declare_global(ccx, &ident[..], llty),
-    };
-
-    // Handle thread-local external statics.
-    for attr in foreign_item.attrs.iter() {
-        if attr.check_name("thread_local") {
-            llvm::set_thread_local(c, true);
-        }
-    }
-
-    return c;
-}
-
-// only use this for foreign function ABIs and glue, use `get_extern_rust_fn` for Rust functions
-pub fn get_extern_fn(ccx: &CrateContext,
-                     externs: &mut ExternMap,
-                     name: &str,
-                     cc: llvm::CallConv,
-                     ty: Type,
-                     output: Ty)
-                     -> ValueRef {
-    match externs.get(name) {
-        Some(n) => return *n,
-        None => {}
-    }
-    let f = declare::declare_fn(ccx, name, cc, ty, ty::FnConverging(output));
-    externs.insert(name.to_string(), f);
-    f
-}
-
-/// Registers a foreign function found in a library. Just adds a LLVM global.
-pub fn register_foreign_item_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                          abi: Abi, fty: Ty<'tcx>,
-                                          name: &str,
-                                          attrs: &[ast::Attribute])-> ValueRef {
-    debug!("register_foreign_item_fn(abi={:?}, \
-            ty={:?}, \
-            name={})",
-           abi,
-           fty,
-           name);
-
-    let cc = llvm_calling_convention(ccx, abi);
-
-    // Register the function as a C extern fn
-    let tys = foreign_types_for_fn_ty(ccx, fty);
-
-    // Make sure the calling convention is right for variadic functions
-    // (should've been caught if not in typeck)
-    if tys.fn_sig.variadic {
-        assert!(cc == llvm::CCallConv);
-    }
-
-    // Create the LLVM value for the C extern fn
-    let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys);
-
-    let llfn = get_extern_fn(ccx, &mut *ccx.externs().borrow_mut(), name, cc, llfn_ty, fty);
-    attributes::unwind(llfn, false);
-    add_argument_attributes(&tys, llfn);
-    attributes::from_fn_attrs(ccx, attrs, llfn);
-    llfn
-}
-
-/// Prepares a call to a native function. This requires adapting
-/// from the Rust argument passing rules to the native rules.
-///
-/// # Parameters
-///
-/// - `callee_ty`: Rust type for the function we are calling
-/// - `llfn`: the function pointer we are calling
-/// - `llretptr`: where to store the return value of the function
-/// - `llargs_rust`: a list of the argument values, prepared
-///   as they would be if calling a Rust function
-/// - `passed_arg_tys`: Rust type for the arguments. Normally we
-///   can derive these from callee_ty but in the case of variadic
-///   functions passed_arg_tys will include the Rust type of all
-///   the arguments including the ones not specified in the fn's signature.
-pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                     callee_ty: Ty<'tcx>,
-                                     llfn: ValueRef,
-                                     llretptr: ValueRef,
-                                     llargs_rust: &[ValueRef],
-                                     passed_arg_tys: Vec<Ty<'tcx>>,
-                                     call_debug_loc: DebugLoc)
-                                     -> Block<'blk, 'tcx>
-{
-    let ccx = bcx.ccx();
-
-    debug!("trans_native_call(callee_ty={:?}, \
-            llfn={}, \
-            llretptr={})",
-           callee_ty,
-           ccx.tn().val_to_string(llfn),
-           ccx.tn().val_to_string(llretptr));
-
-    let (fn_abi, fn_sig) = match callee_ty.sty {
-        ty::TyFnDef(_, _, ref fn_ty) |
-        ty::TyFnPtr(ref fn_ty) => (fn_ty.abi, &fn_ty.sig),
-        _ => ccx.sess().bug("trans_native_call called on non-function type")
-    };
-    let fn_sig = ccx.tcx().erase_late_bound_regions(fn_sig);
-    let fn_sig = infer::normalize_associated_type(ccx.tcx(), &fn_sig);
-    let llsig = foreign_signature(ccx, &fn_sig, &passed_arg_tys[..]);
-    let fn_type = cabi::compute_abi_info(ccx,
-                                         &llsig.llarg_tys,
-                                         llsig.llret_ty,
-                                         llsig.ret_def);
-
-    let arg_tys: &[cabi::ArgType] = &fn_type.arg_tys;
-
-    let mut llargs_foreign = Vec::new();
-
-    // If the foreign ABI expects return value by pointer, supply the
-    // pointer that Rust gave us. Sometimes we have to bitcast
-    // because foreign fns return slightly different (but equivalent)
-    // views on the same type (e.g., i64 in place of {i32,i32}).
-    if fn_type.ret_ty.is_indirect() {
-        match fn_type.ret_ty.cast {
-            Some(ty) => {
-                let llcastedretptr =
-                    BitCast(bcx, llretptr, ty.ptr_to());
-                llargs_foreign.push(llcastedretptr);
-            }
-            None => {
-                llargs_foreign.push(llretptr);
-            }
-        }
-    }
-
-    let mut offset = 0;
-    for (i, arg_ty) in arg_tys.iter().enumerate() {
-        let mut llarg_rust = llargs_rust[i + offset];
-
-        if arg_ty.is_ignore() {
-            continue;
-        }
-
-        // Does Rust pass this argument by pointer?
-        let rust_indirect = type_of::arg_is_indirect(ccx, passed_arg_tys[i]);
-
-        debug!("argument {}, llarg_rust={}, rust_indirect={}, arg_ty={}",
-               i,
-               ccx.tn().val_to_string(llarg_rust),
-               rust_indirect,
-               ccx.tn().type_to_string(arg_ty.ty));
-
-        // Ensure that we always have the Rust value indirectly,
-        // because it makes bitcasting easier.
-        if !rust_indirect {
-            let scratch = base::alloc_ty(bcx, passed_arg_tys[i], "__arg");
-            if type_is_fat_ptr(ccx.tcx(), passed_arg_tys[i]) {
-                Store(bcx, llargs_rust[i + offset], expr::get_dataptr(bcx, scratch));
-                Store(bcx, llargs_rust[i + offset + 1], expr::get_meta(bcx, scratch));
-                offset += 1;
-            } else {
-                base::store_ty(bcx, llarg_rust, scratch, passed_arg_tys[i]);
-            }
-            llarg_rust = scratch;
-        }
-
-        debug!("llarg_rust={} (after indirection)",
-               ccx.tn().val_to_string(llarg_rust));
-
-        // Check whether we need to do any casting
-        match arg_ty.cast {
-            Some(ty) => llarg_rust = BitCast(bcx, llarg_rust, ty.ptr_to()),
-            None => ()
-        }
-
-        debug!("llarg_rust={} (after casting)",
-               ccx.tn().val_to_string(llarg_rust));
-
-        // Finally, load the value if needed for the foreign ABI
-        let foreign_indirect = arg_ty.is_indirect();
-        let llarg_foreign = if foreign_indirect {
-            llarg_rust
-        } else {
-            if passed_arg_tys[i].is_bool() {
-                let val = LoadRangeAssert(bcx, llarg_rust, 0, 2, llvm::False);
-                Trunc(bcx, val, Type::i1(bcx.ccx()))
-            } else {
-                Load(bcx, llarg_rust)
-            }
-        };
-
-        debug!("argument {}, llarg_foreign={}",
-               i, ccx.tn().val_to_string(llarg_foreign));
-
-        // fill padding with undef value
-        match arg_ty.pad {
-            Some(ty) => llargs_foreign.push(C_undef(ty)),
-            None => ()
-        }
-        llargs_foreign.push(llarg_foreign);
-    }
-
-    let cc = llvm_calling_convention(ccx, fn_abi);
-
-    // A function pointer is called without the declaration available, so we have to apply
-    // any attributes with ABI implications directly to the call instruction.
-    let mut attrs = llvm::AttrBuilder::new();
-
-    // Add attributes that are always applicable, independent of the concrete foreign ABI
-    if fn_type.ret_ty.is_indirect() {
-        let llret_sz = machine::llsize_of_real(ccx, fn_type.ret_ty.ty);
-
-        // The outptr can be noalias and nocapture because it's entirely
-        // invisible to the program. We also know it's nonnull as well
-        // as how many bytes we can dereference
-        attrs.arg(1, llvm::Attribute::NoAlias)
-             .arg(1, llvm::Attribute::NoCapture)
-             .arg(1, llvm::DereferenceableAttribute(llret_sz));
-    };
-
-    // Add attributes that depend on the concrete foreign ABI
-    let mut arg_idx = if fn_type.ret_ty.is_indirect() { 1 } else { 0 };
-    match fn_type.ret_ty.attr {
-        Some(attr) => { attrs.arg(arg_idx, attr); },
-        _ => ()
-    }
-
-    arg_idx += 1;
-    for arg_ty in &fn_type.arg_tys {
-        if arg_ty.is_ignore() {
-            continue;
-        }
-        // skip padding
-        if arg_ty.pad.is_some() { arg_idx += 1; }
-
-        if let Some(attr) = arg_ty.attr {
-            attrs.arg(arg_idx, attr);
-        }
-
-        arg_idx += 1;
-    }
-
-    let llforeign_retval = CallWithConv(bcx,
-                                        llfn,
-                                        &llargs_foreign[..],
-                                        cc,
-                                        Some(attrs),
-                                        call_debug_loc);
-
-    // If the function we just called does not use an outpointer,
-    // store the result into the rust outpointer. Cast the outpointer
-    // type to match because some ABIs will use a different type than
-    // the Rust type. e.g., a {u32,u32} struct could be returned as
-    // u64.
-    if llsig.ret_def && !fn_type.ret_ty.is_indirect() {
-        let llrust_ret_ty = llsig.llret_ty;
-        let llforeign_ret_ty = match fn_type.ret_ty.cast {
-            Some(ty) => ty,
-            None => fn_type.ret_ty.ty
-        };
-
-        debug!("llretptr={}", ccx.tn().val_to_string(llretptr));
-        debug!("llforeign_retval={}", ccx.tn().val_to_string(llforeign_retval));
-        debug!("llrust_ret_ty={}", ccx.tn().type_to_string(llrust_ret_ty));
-        debug!("llforeign_ret_ty={}", ccx.tn().type_to_string(llforeign_ret_ty));
-
-        if llrust_ret_ty == llforeign_ret_ty {
-            match fn_sig.output {
-                ty::FnConverging(result_ty) => {
-                    base::store_ty(bcx, llforeign_retval, llretptr, result_ty)
-                }
-                ty::FnDiverging => {}
-            }
-        } else {
-            // The actual return type is a struct, but the ABI
-            // adaptation code has cast it into some scalar type.  The
-            // code that follows is the only reliable way I have
-            // found to do a transform like i64 -> {i32,i32}.
-            // Basically we dump the data onto the stack then memcpy it.
-            //
-            // Other approaches I tried:
-            // - Casting rust ret pointer to the foreign type and using Store
-            //   is (a) unsafe if size of foreign type > size of rust type and
-            //   (b) runs afoul of strict aliasing rules, yielding invalid
-            //   assembly under -O (specifically, the store gets removed).
-            // - Truncating foreign type to correct integral type and then
-            //   bitcasting to the struct type yields invalid cast errors.
-            let llscratch = base::alloca(bcx, llforeign_ret_ty, "__cast");
-            base::call_lifetime_start(bcx, llscratch);
-            Store(bcx, llforeign_retval, llscratch);
-            let llscratch_i8 = BitCast(bcx, llscratch, Type::i8(ccx).ptr_to());
-            let llretptr_i8 = BitCast(bcx, llretptr, Type::i8(ccx).ptr_to());
-            let llrust_size = machine::llsize_of_store(ccx, llrust_ret_ty);
-            let llforeign_align = machine::llalign_of_min(ccx, llforeign_ret_ty);
-            let llrust_align = machine::llalign_of_min(ccx, llrust_ret_ty);
-            let llalign = cmp::min(llforeign_align, llrust_align);
-            debug!("llrust_size={}", llrust_size);
-            base::call_memcpy(bcx, llretptr_i8, llscratch_i8,
-                              C_uint(ccx, llrust_size), llalign as u32);
-            base::call_lifetime_end(bcx, llscratch);
-        }
-    }
-
-    return bcx;
-}
-
-// feature gate SIMD types in FFI, since I (huonw) am not sure the
-// ABIs are handled at all correctly.
-fn gate_simd_ffi(tcx: &TyCtxt, decl: &hir::FnDecl, ty: &ty::BareFnTy) {
-    if !tcx.sess.features.borrow().simd_ffi {
-        let check = |ast_ty: &hir::Ty, ty: ty::Ty| {
-            if ty.is_simd() {
-                tcx.sess.struct_span_err(ast_ty.span,
-                              &format!("use of SIMD type `{}` in FFI is highly experimental and \
-                                        may result in invalid code",
-                                       pprust::ty_to_string(ast_ty)))
-                    .fileline_help(ast_ty.span,
-                                   "add #![feature(simd_ffi)] to the crate attributes to enable")
-                    .emit();
-            }
-        };
-        let sig = &ty.sig.0;
-        for (input, ty) in decl.inputs.iter().zip(&sig.inputs) {
-            check(&input.ty, *ty)
-        }
-        if let hir::Return(ref ty) = decl.output {
-            check(&ty, sig.output.unwrap())
-        }
-    }
-}
-
-pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &hir::ForeignMod) {
-    let _icx = push_ctxt("foreign::trans_foreign_mod");
-    for foreign_item in &foreign_mod.items {
-        let lname = link_name(foreign_item);
-
-        if let hir::ForeignItemFn(ref decl, _) = foreign_item.node {
-            match foreign_mod.abi {
-                Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic => {}
-                abi => {
-                    let ty = ccx.tcx().node_id_to_type(foreign_item.id);
-                    match ty.sty {
-                        ty::TyFnDef(_, _, bft) |
-                        ty::TyFnPtr(bft) => gate_simd_ffi(ccx.tcx(), &decl, bft),
-                        _ => ccx.tcx().sess.span_bug(foreign_item.span,
-                                                     "foreign fn's sty isn't a bare_fn_ty?")
-                    }
-
-                    register_foreign_item_fn(ccx, abi, ty, &lname, &foreign_item.attrs);
-                    // Unlike for other items, we shouldn't call
-                    // `base::update_linkage` here.  Foreign items have
-                    // special linkage requirements, which are handled
-                    // inside `foreign::register_*`.
-                }
-            }
-        }
-
-        ccx.item_symbols().borrow_mut().insert(foreign_item.id,
-                                             lname.to_string());
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Rust functions with foreign ABIs
-//
-// These are normal Rust functions defined with foreign ABIs.  For
-// now, and perhaps forever, we translate these using a "layer of
-// indirection". That is, given a Rust declaration like:
-//
-//     extern "C" fn foo(i: u32) -> u32 { ... }
-//
-// we will generate a function like:
-//
-//     S foo(T i) {
-//         S r;
-//         foo0(&r, NULL, i);
-//         return r;
-//     }
-//
-//     #[inline_always]
-//     void foo0(uint32_t *r, void *env, uint32_t i) { ... }
-//
-// Here the (internal) `foo0` function follows the Rust ABI as normal,
-// where the `foo` function follows the C ABI. We rely on LLVM to
-// inline the one into the other. Of course we could just generate the
-// correct code in the first place, but this is much simpler.
-
-pub fn decl_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                               t: Ty<'tcx>,
-                                               name: &str)
-                                               -> ValueRef {
-    let tys = foreign_types_for_fn_ty(ccx, t);
-    let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys);
-    let cconv = match t.sty {
-        ty::TyFnDef(_, _, ref fn_ty) | ty::TyFnPtr(ref fn_ty) => {
-            llvm_calling_convention(ccx, fn_ty.abi)
-        }
-        _ => panic!("expected bare fn in decl_rust_fn_with_foreign_abi")
-    };
-    let llfn = declare::declare_fn(ccx, name, cconv, llfn_ty,
-                                   ty::FnConverging(ccx.tcx().mk_nil()));
-    add_argument_attributes(&tys, llfn);
-    debug!("decl_rust_fn_with_foreign_abi(llfn_ty={}, llfn={})",
-           ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn));
-    llfn
-}
-
-pub fn register_rust_fn_with_foreign_abi(ccx: &CrateContext,
-                                         sp: Span,
-                                         sym: String,
-                                         node_id: ast::NodeId)
-                                         -> ValueRef {
-    let _icx = push_ctxt("foreign::register_foreign_fn");
-
-    let t = ccx.tcx().node_id_to_type(node_id);
-    let cconv = match t.sty {
-        ty::TyFnDef(_, _, ref fn_ty) | ty::TyFnPtr(ref fn_ty) => {
-            llvm_calling_convention(ccx, fn_ty.abi)
-        }
-        _ => panic!("expected bare fn in register_rust_fn_with_foreign_abi")
-    };
-    let tys = foreign_types_for_fn_ty(ccx, t);
-    let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys);
-    let llfn = base::register_fn_llvmty(ccx, sp, sym, node_id, cconv, llfn_ty);
-    add_argument_attributes(&tys, llfn);
-    debug!("register_rust_fn_with_foreign_abi(node_id={}, llfn_ty={}, llfn={})",
-           node_id, ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn));
-    llfn
-}
-
-pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                                decl: &hir::FnDecl,
-                                                body: &hir::Block,
-                                                attrs: &[ast::Attribute],
-                                                llwrapfn: ValueRef,
-                                                param_substs: &'tcx Substs<'tcx>,
-                                                id: ast::NodeId,
-                                                hash: Option<&str>) {
-    let _icx = push_ctxt("foreign::build_foreign_fn");
-
-    let fnty = ccx.tcx().node_id_to_type(id);
-    let mty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, &fnty);
-    let tys = foreign_types_for_fn_ty(ccx, mty);
-
-    unsafe { // unsafe because we call LLVM operations
-        // Build up the Rust function (`foo0` above).
-        let llrustfn = build_rust_fn(ccx, decl, body, param_substs, attrs, id, hash);
-
-        // Build up the foreign wrapper (`foo` above).
-        return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys, mty);
-    }
-
-    fn build_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                               decl: &hir::FnDecl,
-                               body: &hir::Block,
-                               param_substs: &'tcx Substs<'tcx>,
-                               attrs: &[ast::Attribute],
-                               id: ast::NodeId,
-                               hash: Option<&str>)
-                               -> ValueRef
-    {
-        let _icx = push_ctxt("foreign::foreign::build_rust_fn");
-        let tcx = ccx.tcx();
-        let t = tcx.node_id_to_type(id);
-        let t = monomorphize::apply_param_substs(tcx, param_substs, &t);
-
-        let path =
-            tcx.map.def_path_from_id(id)
-                   .into_iter()
-                   .map(|e| e.data.as_interned_str())
-                   .chain(once(special_idents::clownshoe_abi.name.as_str()));
-        let ps = link::mangle(path, hash);
-
-        // Compute the type that the function would have if it were just a
-        // normal Rust function. This will be the type of the wrappee fn.
-        match t.sty {
-            ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f)=> {
-                assert!(f.abi != Abi::Rust);
-                assert!(f.abi != Abi::RustIntrinsic);
-                assert!(f.abi != Abi::PlatformIntrinsic);
-            }
-            _ => {
-                ccx.sess().bug(&format!("build_rust_fn: extern fn {} has ty {:?}, \
-                                        expected a bare fn ty",
-                                       ccx.tcx().map.path_to_string(id),
-                                       t));
-            }
-        };
-
-        debug!("build_rust_fn: path={} id={} t={:?}",
-               ccx.tcx().map.path_to_string(id),
-               id, t);
-
-        let llfn = declare::define_internal_rust_fn(ccx, &ps, t);
-        attributes::from_fn_attrs(ccx, attrs, llfn);
-        base::trans_fn(ccx, decl, body, llfn, param_substs, id, attrs);
-        llfn
-    }
-
-    unsafe fn build_wrap_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                      llrustfn: ValueRef,
-                                      llwrapfn: ValueRef,
-                                      tys: &ForeignTypes<'tcx>,
-                                      t: Ty<'tcx>) {
-        let _icx = push_ctxt(
-            "foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn");
-
-        debug!("build_wrap_fn(llrustfn={}, llwrapfn={}, t={:?})",
-               ccx.tn().val_to_string(llrustfn),
-               ccx.tn().val_to_string(llwrapfn),
-               t);
-
-        // Avoid all the Rust generation stuff and just generate raw
-        // LLVM here.
-        //
-        // We want to generate code like this:
-        //
-        //     S foo(T i) {
-        //         S r;
-        //         foo0(&r, NULL, i);
-        //         return r;
-        //     }
-
-        if llvm::LLVMCountBasicBlocks(llwrapfn) != 0 {
-            ccx.sess().bug("wrapping a function inside non-empty wrapper, most likely cause is \
-                           multiple functions being wrapped");
-        }
-
-        let ptr = "the block\0".as_ptr();
-        let the_block = llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llwrapfn,
-                                                            ptr as *const _);
-
-        let builder = ccx.builder();
-        builder.position_at_end(the_block);
-
-        // Array for the arguments we will pass to the rust function.
-        let mut llrust_args = Vec::new();
-        let mut next_foreign_arg_counter: c_uint = 0;
-        let mut next_foreign_arg = |pad: bool| -> c_uint {
-            next_foreign_arg_counter += if pad {
-                2
-            } else {
-                1
-            };
-            next_foreign_arg_counter - 1
-        };
-
-        // If there is an out pointer on the foreign function
-        let foreign_outptr = {
-            if tys.fn_ty.ret_ty.is_indirect() {
-                Some(get_param(llwrapfn, next_foreign_arg(false)))
-            } else {
-                None
-            }
-        };
-
-        let rustfn_ty = Type::from_ref(llvm::LLVMTypeOf(llrustfn)).element_type();
-        let mut rust_param_tys = rustfn_ty.func_params().into_iter();
-        // Push Rust return pointer, using null if it will be unused.
-        let rust_uses_outptr = match tys.fn_sig.output {
-            ty::FnConverging(ret_ty) => type_of::return_uses_outptr(ccx, ret_ty),
-            ty::FnDiverging => false
-        };
-        let return_alloca: Option<ValueRef>;
-        let llrust_ret_ty = if rust_uses_outptr {
-            rust_param_tys.next().expect("Missing return type!").element_type()
-        } else {
-            rustfn_ty.return_type()
-        };
-        if rust_uses_outptr {
-            // Rust expects to use an outpointer. If the foreign fn
-            // also uses an outpointer, we can reuse it, but the types
-            // may vary, so cast first to the Rust type. If the
-            // foreign fn does NOT use an outpointer, we will have to
-            // alloca some scratch space on the stack.
-            match foreign_outptr {
-                Some(llforeign_outptr) => {
-                    debug!("out pointer, foreign={}",
-                           ccx.tn().val_to_string(llforeign_outptr));
-                    let llrust_retptr =
-                        builder.bitcast(llforeign_outptr, llrust_ret_ty.ptr_to());
-                    debug!("out pointer, foreign={} (casted)",
-                           ccx.tn().val_to_string(llrust_retptr));
-                    llrust_args.push(llrust_retptr);
-                    return_alloca = None;
-                }
-
-                None => {
-                    let slot = builder.alloca(llrust_ret_ty, "return_alloca");
-                    debug!("out pointer, \
-                            allocad={}, \
-                            llrust_ret_ty={}, \
-                            return_ty={:?}",
-                           ccx.tn().val_to_string(slot),
-                           ccx.tn().type_to_string(llrust_ret_ty),
-                           tys.fn_sig.output);
-                    llrust_args.push(slot);
-                    return_alloca = Some(slot);
-                }
-            }
-        } else {
-            // Rust does not expect an outpointer. If the foreign fn
-            // does use an outpointer, then we will do a store of the
-            // value that the Rust fn returns.
-            return_alloca = None;
-        };
-
-        // Build up the arguments to the call to the rust function.
-        // Careful to adapt for cases where the native convention uses
-        // a pointer and Rust does not or vice versa.
-        for i in 0..tys.fn_sig.inputs.len() {
-            let rust_ty = tys.fn_sig.inputs[i];
-            let rust_indirect = type_of::arg_is_indirect(ccx, rust_ty);
-            let llty = rust_param_tys.next().expect("Not enough parameter types!");
-            let llrust_ty = if rust_indirect {
-                llty.element_type()
-            } else {
-                llty
-            };
-            let llforeign_arg_ty = tys.fn_ty.arg_tys[i];
-            let foreign_indirect = llforeign_arg_ty.is_indirect();
-
-            if llforeign_arg_ty.is_ignore() {
-                debug!("skipping ignored arg #{}", i);
-                llrust_args.push(C_undef(llrust_ty));
-                continue;
-            }
-
-            // skip padding
-            let foreign_index = next_foreign_arg(llforeign_arg_ty.pad.is_some());
-            let mut llforeign_arg = get_param(llwrapfn, foreign_index);
-
-            debug!("llforeign_arg {}{}: {}", "#",
-                   i, ccx.tn().val_to_string(llforeign_arg));
-            debug!("rust_indirect = {}, foreign_indirect = {}",
-                   rust_indirect, foreign_indirect);
-
-            // Ensure that the foreign argument is indirect (by
-            // pointer).  It makes adapting types easier, since we can
-            // always just bitcast pointers.
-            if !foreign_indirect {
-                llforeign_arg = if rust_ty.is_bool() {
-                    let lltemp = builder.alloca(Type::bool(ccx), "");
-                    builder.store(builder.zext(llforeign_arg, Type::bool(ccx)), lltemp);
-                    lltemp
-                } else {
-                    let lltemp = builder.alloca(val_ty(llforeign_arg), "");
-                    builder.store(llforeign_arg, lltemp);
-                    lltemp
-                }
-            }
-
-            // If the types in the ABI and the Rust types don't match,
-            // bitcast the llforeign_arg pointer so it matches the types
-            // Rust expects.
-            if llforeign_arg_ty.cast.is_some() && !type_is_fat_ptr(ccx.tcx(), rust_ty){
-                assert!(!foreign_indirect);
-                llforeign_arg = builder.bitcast(llforeign_arg, llrust_ty.ptr_to());
-            }
-
-            let llrust_arg = if rust_indirect || type_is_fat_ptr(ccx.tcx(), rust_ty) {
-                llforeign_arg
-            } else {
-                if rust_ty.is_bool() {
-                    let tmp = builder.load_range_assert(llforeign_arg, 0, 2, llvm::False);
-                    builder.trunc(tmp, Type::i1(ccx))
-                } else if type_of::type_of(ccx, rust_ty).is_aggregate() {
-                    // We want to pass small aggregates as immediate values, but using an aggregate
-                    // LLVM type for this leads to bad optimizations, so its arg type is an
-                    // appropriately sized integer and we have to convert it
-                    let tmp = builder.bitcast(llforeign_arg,
-                                              type_of::arg_type_of(ccx, rust_ty).ptr_to());
-                    let load = builder.load(tmp);
-                    llvm::LLVMSetAlignment(load, type_of::align_of(ccx, rust_ty));
-                    load
-                } else {
-                    builder.load(llforeign_arg)
-                }
-            };
-
-            debug!("llrust_arg {}{}: {}", "#",
-                   i, ccx.tn().val_to_string(llrust_arg));
-            if type_is_fat_ptr(ccx.tcx(), rust_ty) {
-                let next_llrust_ty = rust_param_tys.next().expect("Not enough parameter types!");
-                llrust_args.push(builder.load(builder.bitcast(builder.struct_gep(
-                                llrust_arg, abi::FAT_PTR_ADDR), llrust_ty.ptr_to())));
-                llrust_args.push(builder.load(builder.bitcast(builder.struct_gep(
-                                llrust_arg, abi::FAT_PTR_EXTRA), next_llrust_ty.ptr_to())));
-            } else {
-                llrust_args.push(llrust_arg);
-            }
-        }
-
-        // Perform the call itself
-        debug!("calling llrustfn = {}, t = {:?}",
-               ccx.tn().val_to_string(llrustfn), t);
-        let attributes = attributes::from_fn_type(ccx, t);
-        let llrust_ret_val = builder.call(llrustfn, &llrust_args,
-                                          None, Some(attributes));
-
-        // Get the return value where the foreign fn expects it.
-        let llforeign_ret_ty = match tys.fn_ty.ret_ty.cast {
-            Some(ty) => ty,
-            None => tys.fn_ty.ret_ty.ty
-        };
-        match foreign_outptr {
-            None if !tys.llsig.ret_def => {
-                // Function returns `()` or `bot`, which in Rust is the LLVM
-                // type "{}" but in foreign ABIs is "Void".
-                builder.ret_void();
-            }
-
-            None if rust_uses_outptr => {
-                // Rust uses an outpointer, but the foreign ABI does not. Load.
-                let llrust_outptr = return_alloca.unwrap();
-                let llforeign_outptr_casted =
-                    builder.bitcast(llrust_outptr, llforeign_ret_ty.ptr_to());
-                let llforeign_retval = builder.load(llforeign_outptr_casted);
-                builder.ret(llforeign_retval);
-            }
-
-            None if llforeign_ret_ty != llrust_ret_ty => {
-                // Neither ABI uses an outpointer, but the types don't
-                // quite match. Must cast. Probably we should try and
-                // examine the types and use a concrete llvm cast, but
-                // right now we just use a temp memory location and
-                // bitcast the pointer, which is the same thing the
-                // old wrappers used to do.
-                let lltemp = builder.alloca(llforeign_ret_ty, "");
-                let lltemp_casted = builder.bitcast(lltemp, llrust_ret_ty.ptr_to());
-                builder.store(llrust_ret_val, lltemp_casted);
-                let llforeign_retval = builder.load(lltemp);
-                builder.ret(llforeign_retval);
-            }
-
-            None => {
-                // Neither ABI uses an outpointer, and the types
-                // match. Easy peasy.
-                builder.ret(llrust_ret_val);
-            }
-
-            Some(llforeign_outptr) if !rust_uses_outptr => {
-                // Foreign ABI requires an out pointer, but Rust doesn't.
-                // Store Rust return value.
-                let llforeign_outptr_casted =
-                    builder.bitcast(llforeign_outptr, llrust_ret_ty.ptr_to());
-                builder.store(llrust_ret_val, llforeign_outptr_casted);
-                builder.ret_void();
-            }
-
-            Some(_) => {
-                // Both ABIs use outpointers. Easy peasy.
-                builder.ret_void();
-            }
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// General ABI Support
-//
-// This code is kind of a confused mess and needs to be reworked given
-// the massive simplifications that have occurred.
-
-pub fn link_name(i: &hir::ForeignItem) -> InternedString {
-    match attr::first_attr_value_str_by_name(&i.attrs, "link_name") {
-        Some(ln) => ln.clone(),
-        None => match weak_lang_items::link_name(&i.attrs) {
-            Some(name) => name,
-            None => i.name.as_str(),
-        }
-    }
-}
-
-/// The ForeignSignature is the LLVM types of the arguments/return type of a function. Note that
-/// these LLVM types are not quite the same as the LLVM types would be for a native Rust function
-/// because foreign functions just plain ignore modes. They also don't pass aggregate values by
-/// pointer like we do.
-fn foreign_signature<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                               fn_sig: &ty::FnSig<'tcx>,
-                               arg_tys: &[Ty<'tcx>])
-                               -> LlvmSignature {
-    let llarg_tys = arg_tys.iter().map(|&arg| foreign_arg_type_of(ccx, arg)).collect();
-    let (llret_ty, ret_def) = match fn_sig.output {
-        ty::FnConverging(ret_ty) =>
-            (type_of::foreign_arg_type_of(ccx, ret_ty), !return_type_is_void(ccx, ret_ty)),
-        ty::FnDiverging =>
-            (Type::nil(ccx), false)
-    };
-    LlvmSignature {
-        llarg_tys: llarg_tys,
-        llret_ty: llret_ty,
-        ret_def: ret_def
-    }
-}
-
-fn foreign_types_for_fn_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                     ty: Ty<'tcx>) -> ForeignTypes<'tcx> {
-    let fn_sig = match ty.sty {
-        ty::TyFnDef(_, _, ref fn_ty) | ty::TyFnPtr(ref fn_ty) => &fn_ty.sig,
-        _ => ccx.sess().bug("foreign_types_for_fn_ty called on non-function type")
-    };
-    let fn_sig = ccx.tcx().erase_late_bound_regions(fn_sig);
-    let fn_sig = infer::normalize_associated_type(ccx.tcx(), &fn_sig);
-    let llsig = foreign_signature(ccx, &fn_sig, &fn_sig.inputs);
-    let fn_ty = cabi::compute_abi_info(ccx,
-                                       &llsig.llarg_tys,
-                                       llsig.llret_ty,
-                                       llsig.ret_def);
-    debug!("foreign_types_for_fn_ty(\
-           ty={:?}, \
-           llsig={} -> {}, \
-           fn_ty={} -> {}, \
-           ret_def={}",
-           ty,
-           ccx.tn().types_to_str(&llsig.llarg_tys),
-           ccx.tn().type_to_string(llsig.llret_ty),
-           ccx.tn().types_to_str(&fn_ty.arg_tys.iter().map(|t| t.ty).collect::<Vec<_>>()),
-           ccx.tn().type_to_string(fn_ty.ret_ty.ty),
-           llsig.ret_def);
-
-    ForeignTypes {
-        fn_sig: fn_sig,
-        llsig: llsig,
-        fn_ty: fn_ty
-    }
-}
-
-fn lltype_for_fn_from_foreign_types(ccx: &CrateContext, tys: &ForeignTypes) -> Type {
-    let mut llargument_tys = Vec::new();
-
-    let ret_ty = tys.fn_ty.ret_ty;
-    let llreturn_ty = if ret_ty.is_indirect() {
-        llargument_tys.push(ret_ty.ty.ptr_to());
-        Type::void(ccx)
-    } else {
-        match ret_ty.cast {
-            Some(ty) => ty,
-            None => ret_ty.ty
-        }
-    };
-
-    for &arg_ty in &tys.fn_ty.arg_tys {
-        if arg_ty.is_ignore() {
-            continue;
-        }
-        // add padding
-        match arg_ty.pad {
-            Some(ty) => llargument_tys.push(ty),
-            None => ()
-        }
-
-        let llarg_ty = if arg_ty.is_indirect() {
-            arg_ty.ty.ptr_to()
-        } else {
-            match arg_ty.cast {
-                Some(ty) => ty,
-                None => arg_ty.ty
-            }
-        };
-
-        llargument_tys.push(llarg_ty);
-    }
-
-    if tys.fn_sig.variadic {
-        Type::variadic_func(&llargument_tys, &llreturn_ty)
-    } else {
-        Type::func(&llargument_tys[..], &llreturn_ty)
-    }
-}
-
-pub fn lltype_for_foreign_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                       ty: Ty<'tcx>) -> Type {
-    lltype_for_fn_from_foreign_types(ccx, &foreign_types_for_fn_ty(ccx, ty))
-}
-
-fn add_argument_attributes(tys: &ForeignTypes,
-                           llfn: ValueRef) {
-    let mut i = if tys.fn_ty.ret_ty.is_indirect() {
-        1
-    } else {
-        0
-    };
-
-    match tys.fn_ty.ret_ty.attr {
-        Some(attr) => unsafe {
-            llvm::LLVMAddFunctionAttribute(llfn, i as c_uint, attr.bits() as u64);
-        },
-        None => {}
-    }
-
-    i += 1;
-
-    for &arg_ty in &tys.fn_ty.arg_tys {
-        if arg_ty.is_ignore() {
-            continue;
-        }
-        // skip padding
-        if arg_ty.pad.is_some() { i += 1; }
-
-        match arg_ty.attr {
-            Some(attr) => unsafe {
-                llvm::LLVMAddFunctionAttribute(llfn, i as c_uint, attr.bits() as u64);
-            },
-            None => ()
-        }
-
-        i += 1;
-    }
-}
diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs
index d5f8cff4956..4ca5fb07c98 100644
--- a/src/librustc_trans/trans/glue.rs
+++ b/src/librustc_trans/trans/glue.rs
@@ -14,18 +14,19 @@
 
 use std;
 
-use back::link::*;
+use back::link;
 use llvm;
 use llvm::{ValueRef, get_param};
 use middle::lang_items::ExchangeFreeFnLangItem;
 use middle::subst::{Substs};
 use middle::traits;
 use middle::ty::{self, Ty, TyCtxt};
+use trans::abi::{Abi, FnType};
 use trans::adt;
 use trans::adt::GetDtorType; // for tcx.dtor_type()
 use trans::base::*;
 use trans::build::*;
-use trans::callee;
+use trans::callee::{Callee, ArgVals};
 use trans::cleanup;
 use trans::cleanup::CleanupMethods;
 use trans::collector::{self, TransItem};
@@ -37,25 +38,23 @@ use trans::machine::*;
 use trans::monomorphize;
 use trans::type_of::{type_of, sizing_type_of, align_of};
 use trans::type_::Type;
+use trans::value::Value;
 
 use arena::TypedArena;
-use libc::c_uint;
-use syntax::ast;
 use syntax::codemap::DUMMY_SP;
 
-pub fn trans_exchange_free_dyn<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
+pub fn trans_exchange_free_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                            v: ValueRef,
                                            size: ValueRef,
                                            align: ValueRef,
                                            debug_loc: DebugLoc)
                                            -> Block<'blk, 'tcx> {
     let _icx = push_ctxt("trans_exchange_free");
-    let ccx = cx.ccx();
-    callee::trans_lang_call(cx,
-        langcall(cx, None, "", ExchangeFreeFnLangItem),
-        &[PointerCast(cx, v, Type::i8p(ccx)), size, align],
-        Some(expr::Ignore),
-        debug_loc).bcx
+
+    let def_id = langcall(bcx, None, "", ExchangeFreeFnLangItem);
+    let args = [PointerCast(bcx, v, Type::i8p(bcx.ccx())), size, align];
+    Callee::def(bcx.ccx(), def_id, bcx.tcx().mk_substs(Substs::empty()))
+        .call(bcx, debug_loc, ArgVals(&args), None).bcx
 }
 
 pub fn trans_exchange_free<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
@@ -170,13 +169,13 @@ pub fn drop_ty_core<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 let may_need_drop =
                     ICmp(bcx, llvm::IntNE, hint_val, moved_val, DebugLoc::None);
                 bcx = with_cond(bcx, may_need_drop, |cx| {
-                    Call(cx, glue, &[ptr], None, debug_loc);
+                    Call(cx, glue, &[ptr], debug_loc);
                     cx
                 })
             }
             None => {
                 // No drop-hint ==> call standard drop glue
-                Call(bcx, glue, &[ptr], None, debug_loc);
+                Call(bcx, glue, &[ptr], debug_loc);
             }
         }
     }
@@ -240,38 +239,40 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     }
     let t = g.ty();
 
-    let llty = if type_is_sized(ccx.tcx(), t) {
-        type_of(ccx, t).ptr_to()
-    } else {
-        type_of(ccx, ccx.tcx().mk_box(t)).ptr_to()
+    let tcx = ccx.tcx();
+    let sig = ty::FnSig {
+        inputs: vec![tcx.mk_mut_ptr(tcx.types.i8)],
+        output: ty::FnOutput::FnConverging(tcx.mk_nil()),
+        variadic: false,
     };
-
-    let llfnty = Type::glue_fn(ccx, llty);
+    // Create a FnType for fn(*mut i8) and substitute the real type in
+    // later - that prevents FnType from splitting fat pointers up.
+    let mut fn_ty = FnType::new(ccx, Abi::Rust, &sig, &[]);
+    fn_ty.args[0].original_ty = type_of(ccx, t).ptr_to();
+    let llfnty = fn_ty.llvm_type(ccx);
 
     // To avoid infinite recursion, don't `make_drop_glue` until after we've
     // added the entry to the `drop_glues` cache.
     if let Some(old_sym) = ccx.available_drop_glues().borrow().get(&g) {
-        let llfn = declare::declare_cfn(ccx, &old_sym, llfnty, ccx.tcx().mk_nil());
+        let llfn = declare::declare_cfn(ccx, &old_sym, llfnty);
         ccx.drop_glues().borrow_mut().insert(g, llfn);
         return llfn;
     };
 
-    let fn_nm = mangle_internal_name_by_type_and_seq(ccx, t, "drop");
-    let llfn = declare::define_cfn(ccx, &fn_nm, llfnty, ccx.tcx().mk_nil()).unwrap_or_else(||{
-       ccx.sess().bug(&format!("symbol `{}` already defined", fn_nm));
-    });
+    let fn_nm = link::mangle_internal_name_by_type_and_seq(ccx, t, "drop");
+    assert!(declare::get_defined_value(ccx, &fn_nm).is_none());
+    let llfn = declare::declare_cfn(ccx, &fn_nm, llfnty);
     ccx.available_drop_glues().borrow_mut().insert(g, fn_nm);
+    ccx.drop_glues().borrow_mut().insert(g, llfn);
 
     let _s = StatRecorder::new(ccx, format!("drop {:?}", t));
 
-    let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
+    let empty_substs = tcx.mk_substs(Substs::trans_empty());
     let (arena, fcx): (TypedArena<_>, FunctionContext);
     arena = TypedArena::new();
-    fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false,
-                      ty::FnConverging(ccx.tcx().mk_nil()),
-                      empty_substs, None, &arena);
+    fcx = FunctionContext::new(ccx, llfn, fn_ty, None, empty_substs, &arena);
 
-    let bcx = init_function(&fcx, false, ty::FnConverging(ccx.tcx().mk_nil()));
+    let bcx = fcx.init(false, None);
 
     update_linkage(ccx, llfn, None, OriginalTranslation);
 
@@ -284,9 +285,8 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     // llfn is expected be declared to take a parameter of the appropriate
     // type, so we don't need to explicitly cast the function parameter.
 
-    let llrawptr0 = get_param(llfn, fcx.arg_offset() as c_uint);
-    let bcx = make_drop_glue(bcx, llrawptr0, g);
-    finish_fn(&fcx, bcx, ty::FnConverging(ccx.tcx().mk_nil()), DebugLoc::None);
+    let bcx = make_drop_glue(bcx, get_param(llfn, 0), g);
+    fcx.finish(bcx, DebugLoc::None);
 
     llfn
 }
@@ -314,7 +314,7 @@ fn trans_struct_drop_flag<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             And(bcx, not_init, not_done, DebugLoc::None);
         with_cond(bcx, drop_flag_neither_initialized_nor_cleared, |cx| {
             let llfn = cx.ccx().get_intrinsic(&("llvm.debugtrap"));
-            Call(cx, llfn, &[], None, DebugLoc::None);
+            Call(cx, llfn, &[], DebugLoc::None);
             cx
         })
     };
@@ -365,27 +365,31 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         _ => tcx.sess.bug(&format!("dtor for {:?} is not an impl???", t))
     };
     let dtor_did = def.destructor().unwrap();
-    bcx = callee::Callee::ptr(callee::trans_fn_ref_with_substs(
-            bcx.ccx(), dtor_did, None, vtbl.substs))
-        .call(bcx, DebugLoc::None, callee::ArgVals(args), Some(expr::Ignore)).bcx;
+    bcx = Callee::def(bcx.ccx(), dtor_did, vtbl.substs)
+        .call(bcx, DebugLoc::None, ArgVals(args), None).bcx;
 
     bcx.fcx.pop_and_trans_custom_cleanup_scope(bcx, contents_scope)
 }
 
-pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, info: ValueRef)
+pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
+                                         t: Ty<'tcx>, info: ValueRef)
                                          -> (ValueRef, ValueRef) {
-    debug!("calculate size of DST: {}; with lost info: {}",
-           t, bcx.val_to_string(info));
+    debug!("calculate size of DST: {}; with lost info: {:?}",
+           t, Value(info));
     if type_is_sized(bcx.tcx(), t) {
         let sizing_type = sizing_type_of(bcx.ccx(), t);
         let size = llsize_of_alloc(bcx.ccx(), sizing_type);
         let align = align_of(bcx.ccx(), t);
-        debug!("size_and_align_of_dst t={} info={} size: {} align: {}",
-               t, bcx.val_to_string(info), size, align);
+        debug!("size_and_align_of_dst t={} info={:?} size: {} align: {}",
+               t, Value(info), size, align);
         let size = C_uint(bcx.ccx(), size);
         let align = C_uint(bcx.ccx(), align);
         return (size, align);
     }
+    if bcx.is_unreachable() {
+        let llty = Type::int(bcx.ccx());
+        return (C_undef(llty), C_undef(llty));
+    }
     match t.sty {
         ty::TyStruct(def, substs) => {
             let ccx = bcx.ccx();
@@ -394,7 +398,7 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in
             assert!(!t.is_simd());
             let repr = adt::represent_type(ccx, t);
             let sizing_type = adt::sizing_type_context_of(ccx, &repr, true);
-            debug!("DST {} sizing_type: {}", t, sizing_type.to_string());
+            debug!("DST {} sizing_type: {:?}", t, sizing_type);
             let sized_size = llsize_of_alloc(ccx, sizing_type.prefix());
             let sized_align = llalign_of_min(ccx, sizing_type.prefix());
             debug!("DST {} statically sized prefix size: {} align: {}",
@@ -408,8 +412,6 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in
             let field_ty = monomorphize::field_ty(bcx.tcx(), substs, last_field);
             let (unsized_size, unsized_align) = size_and_align_of_dst(bcx, field_ty, info);
 
-            let dbloc = DebugLoc::None;
-
             // FIXME (#26403, #27023): We should be adding padding
             // to `sized_size` (to accommodate the `unsized_align`
             // required of the unsized field that follows) before
@@ -418,14 +420,14 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in
             // here. But this is where the add would go.)
 
             // Return the sum of sizes and max of aligns.
-            let mut size = Add(bcx, sized_size, unsized_size, dbloc);
+            let mut size = bcx.add(sized_size, unsized_size);
 
             // Issue #27023: If there is a drop flag, *now* we add 1
             // to the size.  (We can do this without adding any
             // padding because drop flags do not have any alignment
             // constraints.)
             if sizing_type.needs_drop_flag() {
-                size = Add(bcx, size, C_uint(bcx.ccx(), 1_u64), dbloc);
+                size = bcx.add(size, C_uint(bcx.ccx(), 1_u64));
             }
 
             // Choose max of two known alignments (combined value must
@@ -436,14 +438,9 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in
                     // pick the correct alignment statically.
                     C_uint(ccx, std::cmp::max(sized_align, unsized_align))
                 }
-                _ => Select(bcx,
-                            ICmp(bcx,
-                                 llvm::IntUGT,
-                                 sized_align,
-                                 unsized_align,
-                                 dbloc),
-                            sized_align,
-                            unsized_align)
+                _ => bcx.select(bcx.icmp(llvm::IntUGT, sized_align, unsized_align),
+                                sized_align,
+                                unsized_align)
             };
 
             // Issue #27023: must add any necessary padding to `size`
@@ -457,19 +454,18 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in
             //
             //   `(size + (align-1)) & -align`
 
-            let addend = Sub(bcx, align, C_uint(bcx.ccx(), 1_u64), dbloc);
-            let size = And(
-                bcx, Add(bcx, size, addend, dbloc), Neg(bcx, align, dbloc), dbloc);
+            let addend = bcx.sub(align, C_uint(bcx.ccx(), 1_u64));
+            let size = bcx.and(bcx.add(size, addend), bcx.neg(align));
 
             (size, align)
         }
         ty::TyTrait(..) => {
             // info points to the vtable and the second entry in the vtable is the
             // dynamic size of the object.
-            let info = PointerCast(bcx, info, Type::int(bcx.ccx()).ptr_to());
-            let size_ptr = GEPi(bcx, info, &[1]);
-            let align_ptr = GEPi(bcx, info, &[2]);
-            (Load(bcx, size_ptr), Load(bcx, align_ptr))
+            let info = bcx.pointercast(info, Type::int(bcx.ccx()).ptr_to());
+            let size_ptr = bcx.gepi(info, &[1]);
+            let align_ptr = bcx.gepi(info, &[2]);
+            (bcx.load(size_ptr), bcx.load(align_ptr))
         }
         ty::TySlice(_) | ty::TyStr => {
             let unit_ty = t.sequence_element_type(bcx.tcx());
@@ -478,7 +474,7 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in
             let llunit_ty = sizing_type_of(bcx.ccx(), unit_ty);
             let unit_align = llalign_of_min(bcx.ccx(), llunit_ty);
             let unit_size = llsize_of_alloc(bcx.ccx(), llunit_ty);
-            (Mul(bcx, info, C_uint(bcx.ccx(), unit_size), DebugLoc::None),
+            (bcx.mul(info, C_uint(bcx.ccx(), unit_size)),
              C_uint(bcx.ccx(), unit_align))
         }
         _ => bcx.sess().bug(&format!("Unexpected unsized type, found {}", t))
@@ -523,7 +519,8 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
                     let bcx = drop_ty(bcx, v0, content_ty, DebugLoc::None);
                     let info = expr::get_meta(bcx, v0);
                     let info = Load(bcx, info);
-                    let (llsize, llalign) = size_and_align_of_dst(bcx, content_ty, info);
+                    let (llsize, llalign) =
+                        size_and_align_of_dst(&bcx.build(), content_ty, info);
 
                     // `Box<ZeroSizeType>` does not allocate.
                     let needs_free = ICmp(bcx,
@@ -585,7 +582,6 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
             Call(bcx,
                  dtor,
                  &[PointerCast(bcx, Load(bcx, data_ptr), Type::i8p(bcx.ccx()))],
-                 None,
                  DebugLoc::None);
             bcx
         }
diff --git a/src/librustc_trans/trans/inline.rs b/src/librustc_trans/trans/inline.rs
index 4c647152458..ad32870444a 100644
--- a/src/librustc_trans/trans/inline.rs
+++ b/src/librustc_trans/trans/inline.rs
@@ -12,7 +12,8 @@ use llvm::{AvailableExternallyLinkage, InternalLinkage, SetLinkage};
 use middle::cstore::{CrateStore, FoundAst, InlinedItem};
 use middle::def_id::DefId;
 use middle::subst::Substs;
-use trans::base::{push_ctxt, trans_item, get_item_val, trans_fn};
+use trans::base::{push_ctxt, trans_item, trans_fn};
+use trans::callee::Callee;
 use trans::common::*;
 
 use rustc::dep_graph::DepNode;
@@ -21,14 +22,15 @@ use rustc_front::hir;
 fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
     debug!("instantiate_inline({:?})", fn_id);
     let _icx = push_ctxt("instantiate_inline");
-    let _task = ccx.tcx().dep_graph.in_task(DepNode::TransInlinedItem(fn_id));
+    let tcx = ccx.tcx();
+    let _task = tcx.dep_graph.in_task(DepNode::TransInlinedItem(fn_id));
 
     match ccx.external().borrow().get(&fn_id) {
         Some(&Some(node_id)) => {
             // Already inline
             debug!("instantiate_inline({}): already inline as node id {}",
-                   ccx.tcx().item_path_str(fn_id), node_id);
-            let node_def_id = ccx.tcx().map.local_def_id(node_id);
+                   tcx.item_path_str(fn_id), node_id);
+            let node_def_id = tcx.map.local_def_id(node_id);
             return Some(node_def_id);
         }
         Some(&None) => {
@@ -39,7 +41,7 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
         }
     }
 
-    let inlined = ccx.tcx().sess.cstore.maybe_get_item_ast(ccx.tcx(), fn_id);
+    let inlined = tcx.sess.cstore.maybe_get_item_ast(tcx, fn_id);
     let inline_id = match inlined {
         FoundAst::NotFound => {
             ccx.external().borrow_mut().insert(fn_id, None);
@@ -52,38 +54,27 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
             ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1);
             trans_item(ccx, item);
 
-            let linkage = match item.node {
-                hir::ItemFn(_, _, _, _, ref generics, _) => {
-                    if generics.is_type_parameterized() {
-                        // Generics have no symbol, so they can't be given any
-                        // linkage.
-                        None
+            if let hir::ItemFn(_, _, _, _, ref generics, _) = item.node {
+                // Generics have no symbol, so they can't be given any linkage.
+                if !generics.is_type_parameterized() {
+                    let linkage = if ccx.sess().opts.cg.codegen_units == 1 {
+                        // We could use AvailableExternallyLinkage here,
+                        // but InternalLinkage allows LLVM to optimize more
+                        // aggressively (at the cost of sometimes
+                        // duplicating code).
+                        InternalLinkage
                     } else {
-                        if ccx.sess().opts.cg.codegen_units == 1 {
-                            // We could use AvailableExternallyLinkage here,
-                            // but InternalLinkage allows LLVM to optimize more
-                            // aggressively (at the cost of sometimes
-                            // duplicating code).
-                            Some(InternalLinkage)
-                        } else {
-                            // With multiple compilation units, duplicated code
-                            // is more of a problem.  Also, `codegen_units > 1`
-                            // means the user is okay with losing some
-                            // performance.
-                            Some(AvailableExternallyLinkage)
-                        }
-                    }
-                }
-                hir::ItemConst(..) => None,
-                _ => unreachable!(),
-            };
-
-            match linkage {
-                Some(linkage) => {
-                    let g = get_item_val(ccx, item.id);
-                    SetLinkage(g, linkage);
+                        // With multiple compilation units, duplicated code
+                        // is more of a problem.  Also, `codegen_units > 1`
+                        // means the user is okay with losing some
+                        // performance.
+                        AvailableExternallyLinkage
+                    };
+                    let empty_substs = tcx.mk_substs(Substs::trans_empty());
+                    let def_id = tcx.map.local_def_id(item.id);
+                    let llfn = Callee::def(ccx, def_id, empty_substs).reify(ccx).val;
+                    SetLinkage(llfn, linkage);
                 }
-                None => {}
             }
 
             item.id
@@ -93,7 +84,7 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
             ccx.external_srcs().borrow_mut().insert(item.id, fn_id);
             item.id
         }
-        FoundAst::FoundParent(parent_id, &InlinedItem::Item(ref item)) => {
+        FoundAst::FoundParent(parent_id, item) => {
             ccx.external().borrow_mut().insert(parent_id, Some(item.id));
             ccx.external_srcs().borrow_mut().insert(item.id, parent_id);
 
@@ -101,7 +92,7 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
             match item.node {
                 hir::ItemEnum(ref ast_def, _) => {
                     let ast_vs = &ast_def.variants;
-                    let ty_vs = &ccx.tcx().lookup_adt_def(parent_id).variants;
+                    let ty_vs = &tcx.lookup_adt_def(parent_id).variants;
                     assert_eq!(ast_vs.len(), ty_vs.len());
                     for (ast_v, ty_v) in ast_vs.iter().zip(ty_vs.iter()) {
                         if ty_v.did == fn_id { my_id = ast_v.node.data.id(); }
@@ -120,13 +111,8 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
                 _ => ccx.sess().bug("instantiate_inline: item has a \
                                  non-enum, non-struct parent")
             }
-            trans_item(ccx, &item);
             my_id
         }
-        FoundAst::FoundParent(_, _) => {
-            ccx.sess().bug("maybe_get_item_ast returned a FoundParent \
-                            with a non-item parent");
-        }
         FoundAst::Found(&InlinedItem::TraitItem(_, ref trait_item)) => {
             ccx.external().borrow_mut().insert(fn_id, Some(trait_item.id));
             ccx.external_srcs().borrow_mut().insert(trait_item.id, fn_id);
@@ -137,10 +123,10 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
             // the logic to do that already exists in `middle`. In order to
             // reuse that code, it needs to be able to look up the traits for
             // inlined items.
-            let ty_trait_item = ccx.tcx().impl_or_trait_item(fn_id).clone();
-            let trait_item_def_id = ccx.tcx().map.local_def_id(trait_item.id);
-            ccx.tcx().impl_or_trait_items.borrow_mut()
-                     .insert(trait_item_def_id, ty_trait_item);
+            let ty_trait_item = tcx.impl_or_trait_item(fn_id).clone();
+            let trait_item_def_id = tcx.map.local_def_id(trait_item.id);
+            tcx.impl_or_trait_items.borrow_mut()
+               .insert(trait_item_def_id, ty_trait_item);
 
             // If this is a default method, we can't look up the
             // impl type. But we aren't going to translate anyways, so
@@ -155,18 +141,18 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
 
             // Translate monomorphic impl methods immediately.
             if let hir::ImplItemKind::Method(ref sig, ref body) = impl_item.node {
-                let impl_tpt = ccx.tcx().lookup_item_type(impl_did);
+                let impl_tpt = tcx.lookup_item_type(impl_did);
                 if impl_tpt.generics.types.is_empty() &&
                         sig.generics.ty_params.is_empty() {
-                    let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
-                    let llfn = get_item_val(ccx, impl_item.id);
+                    let empty_substs = tcx.mk_substs(Substs::trans_empty());
+                    let def_id = tcx.map.local_def_id(impl_item.id);
+                    let llfn = Callee::def(ccx, def_id, empty_substs).reify(ccx).val;
                     trans_fn(ccx,
                              &sig.decl,
                              body,
                              llfn,
                              empty_substs,
-                             impl_item.id,
-                             &impl_item.attrs);
+                             impl_item.id);
                     // See linkage comments on items.
                     if ccx.sess().opts.cg.codegen_units == 1 {
                         SetLinkage(llfn, InternalLinkage);
@@ -180,7 +166,7 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
         }
     };
 
-    let inline_def_id = ccx.tcx().map.local_def_id(inline_id);
+    let inline_def_id = tcx.map.local_def_id(inline_id);
     Some(inline_def_id)
 }
 
diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs
index ca9833f6f9d..49cacaac766 100644
--- a/src/librustc_trans/trans/intrinsic.rs
+++ b/src/librustc_trans/trans/intrinsic.rs
@@ -18,6 +18,7 @@ use llvm::{ValueRef, TypeKind};
 use middle::infer;
 use middle::subst;
 use middle::subst::FnSpace;
+use trans::abi::{Abi, FnType};
 use trans::adt;
 use trans::attributes;
 use trans::base::*;
@@ -40,19 +41,18 @@ use trans::Disr;
 use middle::subst::Substs;
 use rustc::dep_graph::DepNode;
 use rustc_front::hir;
-use syntax::abi::Abi;
 use syntax::ast;
 use syntax::ptr::P;
 use syntax::parse::token;
 
 use rustc::lint;
 use rustc::session::Session;
-use syntax::codemap::Span;
+use syntax::codemap::{Span, DUMMY_SP};
 
 use std::cmp::Ordering;
 
-pub fn get_simple_intrinsic(ccx: &CrateContext, item: &hir::ForeignItem) -> Option<ValueRef> {
-    let name = match &*item.name.as_str() {
+fn get_simple_intrinsic(ccx: &CrateContext, name: &str) -> Option<ValueRef> {
+    let llvm_name = match name {
         "sqrtf32" => "llvm.sqrt.f32",
         "sqrtf64" => "llvm.sqrt.f64",
         "powif32" => "llvm.powi.f32",
@@ -94,7 +94,7 @@ pub fn get_simple_intrinsic(ccx: &CrateContext, item: &hir::ForeignItem) -> Opti
         "assume" => "llvm.assume",
         _ => return None
     };
-    Some(ccx.get_intrinsic(&name))
+    Some(ccx.get_intrinsic(&llvm_name))
 }
 
 pub fn span_transmute_size_error(a: &Session, b: Span, msg: &str) {
@@ -171,13 +171,11 @@ pub fn check_intrinsics(ccx: &CrateContext) {
 /// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics,
 /// add them to librustc_trans/trans/context.rs
 pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
-                                            node: ast::NodeId,
                                             callee_ty: Ty<'tcx>,
-                                            cleanup_scope: cleanup::CustomScopeIndex,
+                                            fn_ty: &FnType,
                                             args: callee::CallArgs<'a, 'tcx>,
                                             dest: expr::Dest,
-                                            substs: &'tcx subst::Substs<'tcx>,
-                                            call_info: NodeIdAndSpan)
+                                            call_debug_location: DebugLoc)
                                             -> Result<'blk, 'tcx> {
     let fcx = bcx.fcx;
     let ccx = fcx.ccx;
@@ -185,14 +183,23 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
 
     let _icx = push_ctxt("trans_intrinsic_call");
 
-    let sig = ccx.tcx().erase_late_bound_regions(callee_ty.fn_sig());
-    let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
+    let (def_id, substs, sig) = match callee_ty.sty {
+        ty::TyFnDef(def_id, substs, fty) => {
+            let sig = tcx.erase_late_bound_regions(&fty.sig);
+            (def_id, substs, infer::normalize_associated_type(tcx, &sig))
+        }
+        _ => unreachable!("expected fn item type, found {}", callee_ty)
+    };
     let arg_tys = sig.inputs;
     let ret_ty = sig.output;
-    let foreign_item = tcx.map.expect_foreign_item(node);
-    let name = foreign_item.name.as_str();
+    let name = tcx.item_name(def_id).as_str();
 
-    let call_debug_location = DebugLoc::At(call_info.id, call_info.span);
+    let span = match call_debug_location {
+        DebugLoc::At(_, span) => span,
+        DebugLoc::None => fcx.span.unwrap_or(DUMMY_SP)
+    };
+
+    let cleanup_scope = fcx.push_custom_cleanup_scope();
 
     // For `transmute` we can just trans the input expr directly into dest
     if name == "transmute" {
@@ -213,7 +220,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                     if out_type_size != 0 {
                         // FIXME #19925 Remove this hack after a release cycle.
                         let _ = unpack_datum!(bcx, expr::trans(bcx, &arg_exprs[0]));
-                        let llfn = Callee::def(ccx, def_id, substs, in_type).reify(ccx).val;
+                        let llfn = Callee::def(ccx, def_id, substs).reify(ccx).val;
                         let llfnty = val_ty(llfn);
                         let llresult = match dest {
                             expr::SaveIn(d) => d,
@@ -267,7 +274,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                     let val = if datum.kind.is_by_ref() {
                         load_ty(bcx, datum.val, datum.ty)
                     } else {
-                        from_arg_ty(bcx, datum.val, datum.ty)
+                        from_immediate(bcx, datum.val)
                     };
 
                     let cast_val = BitCast(bcx, val, llret_ty);
@@ -347,43 +354,6 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         }
     }
 
-    // For `try` we need some custom control flow
-    if &name[..] == "try" {
-        if let callee::ArgExprs(ref exprs) = args {
-            let (func, data, local_ptr) = if exprs.len() != 3 {
-                ccx.sess().bug("expected three exprs as arguments for \
-                                `try` intrinsic");
-            } else {
-                (&exprs[0], &exprs[1], &exprs[2])
-            };
-
-            // translate arguments
-            let func = unpack_datum!(bcx, expr::trans(bcx, func));
-            let func = unpack_datum!(bcx, func.to_rvalue_datum(bcx, "func"));
-            let data = unpack_datum!(bcx, expr::trans(bcx, data));
-            let data = unpack_datum!(bcx, data.to_rvalue_datum(bcx, "data"));
-            let local_ptr = unpack_datum!(bcx, expr::trans(bcx, local_ptr));
-            let local_ptr = local_ptr.to_rvalue_datum(bcx, "local_ptr");
-            let local_ptr = unpack_datum!(bcx, local_ptr);
-
-            let dest = match dest {
-                expr::SaveIn(d) => d,
-                expr::Ignore => alloc_ty(bcx, tcx.mk_mut_ptr(tcx.types.i8),
-                                         "try_result"),
-            };
-
-            // do the invoke
-            bcx = try_intrinsic(bcx, func.val, data.val, local_ptr.val, dest,
-                                call_debug_location);
-
-            fcx.pop_and_trans_custom_cleanup_scope(bcx, cleanup_scope);
-            return Result::new(bcx, dest);
-        } else {
-            ccx.sess().bug("expected two exprs as arguments for \
-                            `try` intrinsic");
-        }
-    }
-
     // save the actual AST arguments for later (some places need to do
     // const-evaluation on them)
     let expr_arguments = match args {
@@ -394,18 +364,19 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     // Push the arguments.
     let mut llargs = Vec::new();
     bcx = callee::trans_args(bcx,
+                             Abi::RustIntrinsic,
+                             fn_ty,
+                             &mut callee::Intrinsic,
                              args,
-                             callee_ty,
                              &mut llargs,
-                             cleanup::CustomScope(cleanup_scope),
-                             Abi::RustIntrinsic);
+                             cleanup::CustomScope(cleanup_scope));
 
     fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean();
 
     // These are the only intrinsic functions that diverge.
     if name == "abort" {
         let llfn = ccx.get_intrinsic(&("llvm.trap"));
-        Call(bcx, llfn, &[], None, call_debug_location);
+        Call(bcx, llfn, &[], call_debug_location);
         fcx.pop_and_trans_custom_cleanup_scope(bcx, cleanup_scope);
         Unreachable(bcx);
         return Result::new(bcx, C_undef(Type::nil(ccx).ptr_to()));
@@ -437,14 +408,19 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         }
     };
 
-    let simple = get_simple_intrinsic(ccx, &foreign_item);
-    let llval = match (simple, &*name) {
+    let simple = get_simple_intrinsic(ccx, &name);
+    let llval = match (simple, &name[..]) {
         (Some(llfn), _) => {
-            Call(bcx, llfn, &llargs, None, call_debug_location)
+            Call(bcx, llfn, &llargs, call_debug_location)
+        }
+        (_, "try") => {
+            bcx = try_intrinsic(bcx, llargs[0], llargs[1], llargs[2], llresult,
+                                call_debug_location);
+            C_nil(ccx)
         }
         (_, "breakpoint") => {
             let llfn = ccx.get_intrinsic(&("llvm.debugtrap"));
-            Call(bcx, llfn, &[], None, call_debug_location)
+            Call(bcx, llfn, &[], call_debug_location)
         }
         (_, "size_of") => {
             let tp_ty = *substs.types.get(FnSpace, 0);
@@ -454,7 +430,8 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         (_, "size_of_val") => {
             let tp_ty = *substs.types.get(FnSpace, 0);
             if !type_is_sized(tcx, tp_ty) {
-                let (llsize, _) = glue::size_and_align_of_dst(bcx, tp_ty, llargs[1]);
+                let (llsize, _) =
+                    glue::size_and_align_of_dst(&bcx.build(), tp_ty, llargs[1]);
                 llsize
             } else {
                 let lltp_ty = type_of::type_of(ccx, tp_ty);
@@ -468,7 +445,8 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         (_, "min_align_of_val") => {
             let tp_ty = *substs.types.get(FnSpace, 0);
             if !type_is_sized(tcx, tp_ty) {
-                let (_, llalign) = glue::size_and_align_of_dst(bcx, tp_ty, llargs[1]);
+                let (_, llalign) =
+                    glue::size_and_align_of_dst(&bcx.build(), tp_ty, llargs[1]);
                 llalign
             } else {
                 C_uint(ccx, type_of::align_of(ccx, tp_ty))
@@ -505,14 +483,14 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         }
         (_, "init_dropped") => {
             let tp_ty = *substs.types.get(FnSpace, 0);
-            if !return_type_is_void(ccx, tp_ty) {
+            if !type_is_zero_size(ccx, tp_ty) {
                 drop_done_fill_mem(bcx, llresult, tp_ty);
             }
             C_nil(ccx)
         }
         (_, "init") => {
             let tp_ty = *substs.types.get(FnSpace, 0);
-            if !return_type_is_void(ccx, tp_ty) {
+            if !type_is_zero_size(ccx, tp_ty) {
                 // Just zero out the stack slot. (See comment on base::memzero for explanation)
                 init_zero_mem(bcx, llresult, tp_ty);
             }
@@ -599,24 +577,32 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         }
         (_, "volatile_load") => {
             let tp_ty = *substs.types.get(FnSpace, 0);
-            let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty);
+            let mut ptr = llargs[0];
+            if let Some(ty) = fn_ty.ret.cast {
+                ptr = PointerCast(bcx, ptr, ty.ptr_to());
+            }
             let load = VolatileLoad(bcx, ptr);
             unsafe {
                 llvm::LLVMSetAlignment(load, type_of::align_of(ccx, tp_ty));
             }
-            to_arg_ty(bcx, load, tp_ty)
+            to_immediate(bcx, load, tp_ty)
         },
         (_, "volatile_store") => {
             let tp_ty = *substs.types.get(FnSpace, 0);
-            let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty);
-            let val = if type_is_immediate(bcx.ccx(), tp_ty) {
-                from_arg_ty(bcx, llargs[1], tp_ty)
+            if type_is_fat_ptr(bcx.tcx(), tp_ty) {
+                VolatileStore(bcx, llargs[1], expr::get_dataptr(bcx, llargs[0]));
+                VolatileStore(bcx, llargs[2], expr::get_meta(bcx, llargs[0]));
             } else {
-                Load(bcx, llargs[1])
-            };
-            let store = VolatileStore(bcx, val, ptr);
-            unsafe {
-                llvm::LLVMSetAlignment(store, type_of::align_of(ccx, tp_ty));
+                let val = if fn_ty.args[1].is_indirect() {
+                    Load(bcx, llargs[1])
+                } else {
+                    from_immediate(bcx, llargs[1])
+                };
+                let ptr = PointerCast(bcx, llargs[0], val_ty(val).ptr_to());
+                let store = VolatileStore(bcx, val, ptr);
+                unsafe {
+                    llvm::LLVMSetAlignment(store, type_of::align_of(ccx, tp_ty));
+                }
             }
             C_nil(ccx)
         },
@@ -634,13 +620,13 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                         "cttz" => count_zeros_intrinsic(bcx, &format!("llvm.cttz.i{}", width),
                                                         llargs[0], call_debug_location),
                         "ctpop" => Call(bcx, ccx.get_intrinsic(&format!("llvm.ctpop.i{}", width)),
-                                        &llargs, None, call_debug_location),
+                                        &llargs, call_debug_location),
                         "bswap" => {
                             if width == 8 {
                                 llargs[0] // byte swap a u8/i8 is just a no-op
                             } else {
                                 Call(bcx, ccx.get_intrinsic(&format!("llvm.bswap.i{}", width)),
-                                        &llargs, None, call_debug_location)
+                                        &llargs, call_debug_location)
                             }
                         }
                         "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => {
@@ -669,7 +655,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                     },
                 None => {
                     span_invalid_monomorphization_error(
-                        tcx.sess, call_info.span,
+                        tcx.sess, span,
                         &format!("invalid monomorphization of `{}` intrinsic: \
                                   expected basic integer type, found `{}`", name, sty));
                         C_null(llret_ty)
@@ -677,11 +663,34 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             }
 
         },
+        (_, "fadd_fast") | (_, "fsub_fast") | (_, "fmul_fast") | (_, "fdiv_fast") |
+        (_, "frem_fast") => {
+            let sty = &arg_tys[0].sty;
+            match float_type_width(sty) {
+                Some(_width) =>
+                    match &*name {
+                        "fadd_fast" => FAddFast(bcx, llargs[0], llargs[1], call_debug_location),
+                        "fsub_fast" => FSubFast(bcx, llargs[0], llargs[1], call_debug_location),
+                        "fmul_fast" => FMulFast(bcx, llargs[0], llargs[1], call_debug_location),
+                        "fdiv_fast" => FDivFast(bcx, llargs[0], llargs[1], call_debug_location),
+                        "frem_fast" => FRemFast(bcx, llargs[0], llargs[1], call_debug_location),
+                        _ => unreachable!(),
+                    },
+                None => {
+                    span_invalid_monomorphization_error(
+                        tcx.sess, span,
+                        &format!("invalid monomorphization of `{}` intrinsic: \
+                                  expected basic float type, found `{}`", name, sty));
+                        C_null(llret_ty)
+                }
+            }
+
+        },
 
 
         (_, "return_address") => {
-            if !fcx.caller_expects_out_pointer {
-                span_err!(tcx.sess, call_info.span, E0510,
+            if !fcx.fn_ty.ret.is_indirect() {
+                span_err!(tcx.sess, span, E0510,
                           "invalid use of `return_address` intrinsic: function \
                            does not use out pointer");
                 C_null(Type::i8p(ccx))
@@ -709,7 +718,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                                    &llargs,
                                    ret_ty, llret_ty,
                                    call_debug_location,
-                                   call_info)
+                                   span)
         }
         // This requires that atomic intrinsics follow a specific naming pattern:
         // "atomic_<operation>[_<ordering>]", and no ordering means SeqCst
@@ -742,19 +751,17 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
 
             match split[1] {
                 "cxchg" => {
-                    let tp_ty = *substs.types.get(FnSpace, 0);
-                    let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty);
-                    let cmp = from_arg_ty(bcx, llargs[1], tp_ty);
-                    let src = from_arg_ty(bcx, llargs[2], tp_ty);
+                    let cmp = from_immediate(bcx, llargs[1]);
+                    let src = from_immediate(bcx, llargs[2]);
+                    let ptr = PointerCast(bcx, llargs[0], val_ty(src).ptr_to());
                     let res = AtomicCmpXchg(bcx, ptr, cmp, src, order, failorder, llvm::False);
                     ExtractValue(bcx, res, 0)
                 }
 
                 "cxchgweak" => {
-                    let tp_ty = *substs.types.get(FnSpace, 0);
-                    let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty);
-                    let cmp = from_arg_ty(bcx, llargs[1], tp_ty);
-                    let src = from_arg_ty(bcx, llargs[2], tp_ty);
+                    let cmp = from_immediate(bcx, llargs[1]);
+                    let src = from_immediate(bcx, llargs[2]);
+                    let ptr = PointerCast(bcx, llargs[0], val_ty(src).ptr_to());
                     let val = AtomicCmpXchg(bcx, ptr, cmp, src, order, failorder, llvm::True);
                     let result = ExtractValue(bcx, val, 0);
                     let success = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool(bcx.ccx()));
@@ -765,13 +772,15 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
 
                 "load" => {
                     let tp_ty = *substs.types.get(FnSpace, 0);
-                    let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty);
-                    to_arg_ty(bcx, AtomicLoad(bcx, ptr, order), tp_ty)
+                    let mut ptr = llargs[0];
+                    if let Some(ty) = fn_ty.ret.cast {
+                        ptr = PointerCast(bcx, ptr, ty.ptr_to());
+                    }
+                    to_immediate(bcx, AtomicLoad(bcx, ptr, order), tp_ty)
                 }
                 "store" => {
-                    let tp_ty = *substs.types.get(FnSpace, 0);
-                    let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty);
-                    let val = from_arg_ty(bcx, llargs[1], tp_ty);
+                    let val = from_immediate(bcx, llargs[1]);
+                    let ptr = PointerCast(bcx, llargs[0], val_ty(val).ptr_to());
                     AtomicStore(bcx, val, ptr, order);
                     C_nil(ccx)
                 }
@@ -803,9 +812,8 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                         _ => ccx.sess().fatal("unknown atomic operation")
                     };
 
-                    let tp_ty = *substs.types.get(FnSpace, 0);
-                    let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty);
-                    let val = from_arg_ty(bcx, llargs[1], tp_ty);
+                    let val = from_immediate(bcx, llargs[1]);
+                    let ptr = PointerCast(bcx, llargs[0], val_ty(val).ptr_to());
                     AtomicRMW(bcx, atom_op, ptr, val, order)
                 }
             }
@@ -815,8 +823,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         (_, _) => {
             let intr = match Intrinsic::find(tcx, &name) {
                 Some(intr) => intr,
-                None => ccx.sess().span_bug(foreign_item.span,
-                                            &format!("unknown intrinsic '{}'", name)),
+                None => unreachable!("unknown intrinsic '{}'", name),
             };
             fn one<T>(x: Vec<T>) -> T {
                 assert_eq!(x.len(), 1);
@@ -949,9 +956,8 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                 intrinsics::IntrinsicDef::Named(name) => {
                     let f = declare::declare_cfn(ccx,
                                                  name,
-                                                 Type::func(&inputs, &outputs),
-                                                 tcx.mk_nil());
-                    Call(bcx, f, &llargs, None, call_debug_location)
+                                                 Type::func(&inputs, &outputs));
+                    Call(bcx, f, &llargs, call_debug_location)
                 }
             };
 
@@ -973,7 +979,15 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
 
     if val_ty(llval) != Type::void(ccx) &&
        machine::llsize_of_alloc(ccx, val_ty(llval)) != 0 {
-        store_ty(bcx, llval, llresult, ret_ty);
+        if let Some(ty) = fn_ty.ret.cast {
+            let ptr = PointerCast(bcx, llresult, ty.ptr_to());
+            let store = Store(bcx, llval, ptr);
+            unsafe {
+                llvm::LLVMSetAlignment(store, type_of::align_of(ccx, ret_ty));
+            }
+        } else {
+            store_ty(bcx, llval, llresult, ret_ty);
+        }
     }
 
     // If we made a temporary stack slot, let's clean it up
@@ -1024,7 +1038,6 @@ fn copy_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
            Mul(bcx, size, count, DebugLoc::None),
            align,
            C_bool(ccx, volatile)],
-         None,
          call_debug_location)
 }
 
@@ -1054,7 +1067,6 @@ fn memset_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
            Mul(bcx, size, count, DebugLoc::None),
            align,
            C_bool(ccx, volatile)],
-         None,
          call_debug_location)
 }
 
@@ -1065,7 +1077,7 @@ fn count_zeros_intrinsic(bcx: Block,
                          -> ValueRef {
     let y = C_bool(bcx.ccx(), false);
     let llfn = bcx.ccx().get_intrinsic(&name);
-    Call(bcx, llfn, &[val, y], None, call_debug_location)
+    Call(bcx, llfn, &[val, y], call_debug_location)
 }
 
 fn with_overflow_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
@@ -1078,7 +1090,7 @@ fn with_overflow_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let llfn = bcx.ccx().get_intrinsic(&name);
 
     // Convert `i1` to a `bool`, and write it to the out parameter
-    let val = Call(bcx, llfn, &[a, b], None, call_debug_location);
+    let val = Call(bcx, llfn, &[a, b], call_debug_location);
     let result = ExtractValue(bcx, val, 0);
     let overflow = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool(bcx.ccx()));
     Store(bcx, result, StructGEP(bcx, out, 0));
@@ -1094,7 +1106,7 @@ fn try_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                              dest: ValueRef,
                              dloc: DebugLoc) -> Block<'blk, 'tcx> {
     if bcx.sess().no_landing_pads() {
-        Call(bcx, func, &[data], None, dloc);
+        Call(bcx, func, &[data], dloc);
         Store(bcx, C_null(Type::i8p(bcx.ccx())), dest);
         bcx
     } else if wants_msvc_seh(bcx.sess()) {
@@ -1165,9 +1177,9 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         // More information can be found in libstd's seh.rs implementation.
         let slot = Alloca(bcx, Type::i8p(ccx), "slot");
         let localescape = ccx.get_intrinsic(&"llvm.localescape");
-        Call(bcx, localescape, &[slot], None, dloc);
+        Call(bcx, localescape, &[slot], dloc);
         Store(bcx, local_ptr, slot);
-        Invoke(bcx, func, &[data], normal.llbb, catchswitch.llbb, None, dloc);
+        Invoke(bcx, func, &[data], normal.llbb, catchswitch.llbb, dloc);
 
         Ret(normal, C_i32(ccx, 0), dloc);
 
@@ -1184,7 +1196,7 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
     // Note that no invoke is used here because by definition this function
     // can't panic (that's what it's catching).
-    let ret = Call(bcx, llfn, &[func, data, local_ptr], None, dloc);
+    let ret = Call(bcx, llfn, &[func, data, local_ptr], dloc);
     Store(bcx, ret, dest);
     return bcx
 }
@@ -1208,6 +1220,7 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                              dloc: DebugLoc) -> Block<'blk, 'tcx> {
     let llfn = get_rust_try_fn(bcx.fcx, &mut |bcx| {
         let ccx = bcx.ccx();
+        let tcx = ccx.tcx();
         let dloc = DebugLoc::None;
 
         // Translates the shims described above:
@@ -1228,10 +1241,11 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         // managed by the standard library.
 
         attributes::emit_uwtable(bcx.fcx.llfn, true);
-        let catch_pers = match bcx.tcx().lang_items.eh_personality_catch() {
-            Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0),
-                                              bcx.fcx.param_substs).val,
-            None => bcx.tcx().sess.bug("eh_personality_catch not defined"),
+        let catch_pers = match tcx.lang_items.eh_personality_catch() {
+            Some(did) => {
+                Callee::def(ccx, did, tcx.mk_substs(Substs::empty())).reify(ccx).val
+            }
+            None => ccx.sess().bug("eh_personality_catch not defined"),
         };
 
         let then = bcx.fcx.new_temp_block("then");
@@ -1240,7 +1254,7 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         let func = llvm::get_param(bcx.fcx.llfn, 0);
         let data = llvm::get_param(bcx.fcx.llfn, 1);
         let local_ptr = llvm::get_param(bcx.fcx.llfn, 2);
-        Invoke(bcx, func, &[data], then.llbb, catch.llbb, None, dloc);
+        Invoke(bcx, func, &[data], then.llbb, catch.llbb, dloc);
         Ret(then, C_i32(ccx, 0), dloc);
 
         // Type indicator for the exception being thrown.
@@ -1260,7 +1274,7 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
     // Note that no invoke is used here because by definition this function
     // can't panic (that's what it's catching).
-    let ret = Call(bcx, llfn, &[func, data, local_ptr], None, dloc);
+    let ret = Call(bcx, llfn, &[func, data, local_ptr], dloc);
     Store(bcx, ret, dest);
     return bcx;
 }
@@ -1269,21 +1283,32 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 // This is currently primarily used for the `try` intrinsic functions above.
 fn gen_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
                     name: &str,
-                    ty: Ty<'tcx>,
+                    inputs: Vec<Ty<'tcx>>,
                     output: ty::FnOutput<'tcx>,
                     trans: &mut for<'b> FnMut(Block<'b, 'tcx>))
                     -> ValueRef {
     let ccx = fcx.ccx;
-    let llfn = declare::define_internal_rust_fn(ccx, name, ty);
+    let sig = ty::FnSig {
+        inputs: inputs,
+        output: output,
+        variadic: false,
+    };
+    let fn_ty = FnType::new(ccx, Abi::Rust, &sig, &[]);
+
+    let rust_fn_ty = ccx.tcx().mk_fn_ptr(ty::BareFnTy {
+        unsafety: hir::Unsafety::Unsafe,
+        abi: Abi::Rust,
+        sig: ty::Binder(sig)
+    });
+    let llfn = declare::define_internal_fn(ccx, name, rust_fn_ty);
+    let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
     let (fcx, block_arena);
     block_arena = TypedArena::new();
-    fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false,
-                      output, ccx.tcx().mk_substs(Substs::trans_empty()),
-                      None, &block_arena);
-    let bcx = init_function(&fcx, true, output);
+    fcx = FunctionContext::new(ccx, llfn, fn_ty, None, empty_substs, &block_arena);
+    let bcx = fcx.init(true, None);
     trans(bcx);
     fcx.cleanup();
-    return llfn
+    llfn
 }
 
 // Helper function used to get a handle to the `__rust_try` function used to
@@ -1294,8 +1319,8 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
                              trans: &mut for<'b> FnMut(Block<'b, 'tcx>))
                              -> ValueRef {
     let ccx = fcx.ccx;
-    if let Some(llfn) = *ccx.rust_try_fn().borrow() {
-        return llfn
+    if let Some(llfn) = ccx.rust_try_fn().get() {
+        return llfn;
     }
 
     // Define the type up front for the signature of the rust_try function.
@@ -1311,18 +1336,8 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
         }),
     });
     let output = ty::FnOutput::FnConverging(tcx.types.i32);
-    let try_fn_ty  = ty::BareFnTy {
-        unsafety: hir::Unsafety::Unsafe,
-        abi: Abi::Rust,
-        sig: ty::Binder(ty::FnSig {
-            inputs: vec![fn_ty, i8p, i8p],
-            output: output,
-            variadic: false,
-        }),
-    };
-    let rust_try = gen_fn(fcx, "__rust_try", tcx.mk_fn_ptr(try_fn_ty), output,
-                          trans);
-    *ccx.rust_try_fn().borrow_mut() = Some(rust_try);
+    let rust_try = gen_fn(fcx, "__rust_try", vec![fn_ty, i8p, i8p], output, trans);
+    ccx.rust_try_fn().set(Some(rust_try));
     return rust_try
 }
 
@@ -1341,9 +1356,10 @@ fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
     let tcx = ccx.tcx();
     let dloc = DebugLoc::None;
 
-    let rust_try_filter = match ccx.tcx().lang_items.msvc_try_filter() {
-        Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0),
-                                          fcx.param_substs).val,
+    let rust_try_filter = match tcx.lang_items.msvc_try_filter() {
+        Some(did) => {
+            Callee::def(ccx, did, tcx.mk_substs(Substs::empty())).reify(ccx).val
+        }
         None => ccx.sess().bug("msvc_try_filter not defined"),
     };
 
@@ -1373,11 +1389,10 @@ fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
     // For more info, see seh.rs in the standard library.
     let do_trans = |bcx: Block, ehptrs, base_pointer| {
         let rust_try_fn = BitCast(bcx, rust_try_fn, Type::i8p(ccx));
-        let parentfp = Call(bcx, recoverfp, &[rust_try_fn, base_pointer],
-                            None, dloc);
+        let parentfp = Call(bcx, recoverfp, &[rust_try_fn, base_pointer], dloc);
         let arg = Call(bcx, localrecover,
-                       &[rust_try_fn, parentfp, C_i32(ccx, 0)], None, dloc);
-        let ret = Call(bcx, rust_try_filter, &[ehptrs, arg], None, dloc);
+                       &[rust_try_fn, parentfp, C_i32(ccx, 0)], dloc);
+        let ret = Call(bcx, rust_try_filter, &[ehptrs, arg], dloc);
         Ret(bcx, ret, dloc);
     };
 
@@ -1389,17 +1404,8 @@ fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
         // going on here, all I can say is that there's a few tests cases in
         // LLVM's test suite which follow this pattern of instructions, so we
         // just do the same.
-        let filter_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy {
-            unsafety: hir::Unsafety::Unsafe,
-            abi: Abi::Rust,
-            sig: ty::Binder(ty::FnSig {
-                inputs: vec![],
-                output: output,
-                variadic: false,
-            }),
-        });
-        gen_fn(fcx, "__rustc_try_filter", filter_fn_ty, output, &mut |bcx| {
-            let ebp = Call(bcx, frameaddress, &[C_i32(ccx, 1)], None, dloc);
+        gen_fn(fcx, "__rustc_try_filter", vec![], output, &mut |bcx| {
+            let ebp = Call(bcx, frameaddress, &[C_i32(ccx, 1)], dloc);
             let exn = InBoundsGEP(bcx, ebp, &[C_i32(ccx, -20)]);
             let exn = Load(bcx, BitCast(bcx, exn, Type::i8p(ccx).ptr_to()));
             do_trans(bcx, exn, ebp);
@@ -1408,16 +1414,7 @@ fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
         // Conveniently on x86_64 the EXCEPTION_POINTERS handle and base pointer
         // are passed in as arguments to the filter function, so we just pass
         // those along.
-        let filter_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy {
-            unsafety: hir::Unsafety::Unsafe,
-            abi: Abi::Rust,
-            sig: ty::Binder(ty::FnSig {
-                inputs: vec![i8p, i8p],
-                output: output,
-                variadic: false,
-            }),
-        });
-        gen_fn(fcx, "__rustc_try_filter", filter_fn_ty, output, &mut |bcx| {
+        gen_fn(fcx, "__rustc_try_filter", vec![i8p, i8p], output, &mut |bcx| {
             let exn = llvm::get_param(bcx.fcx.llfn, 0);
             let rbp = llvm::get_param(bcx.fcx.llfn, 1);
             do_trans(bcx, exn, rbp);
@@ -1441,7 +1438,7 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a>
      ret_ty: Ty<'tcx>,
      llret_ty: Type,
      call_debug_location: DebugLoc,
-     call_info: NodeIdAndSpan) -> ValueRef
+     span: Span) -> ValueRef
 {
     // macros for error handling:
     macro_rules! emit_error {
@@ -1450,7 +1447,7 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a>
         };
         ($msg: tt, $($fmt: tt)*) => {
             span_invalid_monomorphization_error(
-                bcx.sess(), call_info.span,
+                bcx.sess(), span,
                 &format!(concat!("invalid monomorphization of `{}` intrinsic: ",
                                  $msg),
                          name, $($fmt)*));
@@ -1519,7 +1516,7 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a>
     if name.starts_with("simd_shuffle") {
         let n: usize = match name["simd_shuffle".len()..].parse() {
             Ok(n) => n,
-            Err(_) => tcx.sess.span_bug(call_info.span,
+            Err(_) => tcx.sess.span_bug(span,
                                         "bad `simd_shuffle` instruction only caught in trans?")
         };
 
@@ -1537,22 +1534,26 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a>
 
         let total_len = in_len as u64 * 2;
 
-        let vector = match args {
-            Some(args) => &args[2],
-            None => bcx.sess().span_bug(call_info.span,
-                                        "intrinsic call with unexpected argument shape"),
-        };
-        let vector = match consts::const_expr(bcx.ccx(), vector, substs, None,
-            consts::TrueConst::Yes, // this should probably help simd error reporting
-        ) {
-            Ok((vector, _)) => vector,
-            Err(err) => bcx.sess().span_fatal(call_info.span, &err.description()),
+        let (vector, indirect) = match args {
+            Some(args) => {
+                match consts::const_expr(bcx.ccx(), &args[2], substs, None,
+                                         // this should probably help simd error reporting
+                                         consts::TrueConst::Yes) {
+                    Ok((vector, _)) => (vector, false),
+                    Err(err) => bcx.sess().span_fatal(span, &err.description()),
+                }
+            }
+            None => (llargs[2], !type_is_immediate(bcx.ccx(), arg_tys[2]))
         };
 
         let indices: Option<Vec<_>> = (0..n)
             .map(|i| {
                 let arg_idx = i;
-                let val = const_get_elt(bcx.ccx(), vector, &[i as libc::c_uint]);
+                let val = if indirect {
+                    Load(bcx, StructGEP(bcx, vector, i))
+                } else {
+                    const_get_elt(vector, &[i as libc::c_uint])
+                };
                 let c = const_to_opt_uint(val);
                 match c {
                     None => {
@@ -1689,7 +1690,7 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a>
         simd_or: TyUint, TyInt => Or;
         simd_xor: TyUint, TyInt => Xor;
     }
-    bcx.sess().span_bug(call_info.span, "unknown SIMD intrinsic");
+    bcx.sess().span_bug(span, "unknown SIMD intrinsic");
 }
 
 // Returns the width of an int TypeVariant, and if it's signed or not
@@ -1727,3 +1728,17 @@ fn int_type_width_signed<'tcx>(sty: &ty::TypeVariants<'tcx>, ccx: &CrateContext)
         _ => None,
     }
 }
+
+// Returns the width of a float TypeVariant
+// Returns None if the type is not a float
+fn float_type_width<'tcx>(sty: &ty::TypeVariants<'tcx>)
+        -> Option<u64> {
+    use rustc::middle::ty::TyFloat;
+    match *sty {
+        TyFloat(t) => Some(match t {
+            ast::FloatTy::F32 => 32,
+            ast::FloatTy::F64 => 64,
+        }),
+        _ => None,
+    }
+}
diff --git a/src/librustc_trans/trans/llrepr.rs b/src/librustc_trans/trans/llrepr.rs
deleted file mode 100644
index 6b785e7edfd..00000000000
--- a/src/librustc_trans/trans/llrepr.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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.
-
-use trans::context::CrateContext;
-use trans::type_::Type;
-use llvm::ValueRef;
-
-pub trait LlvmRepr {
-    fn llrepr(&self, ccx: &CrateContext) -> String;
-}
-
-impl<T:LlvmRepr> LlvmRepr for [T] {
-    fn llrepr(&self, ccx: &CrateContext) -> String {
-        let reprs: Vec<String> = self.iter().map(|t| t.llrepr(ccx)).collect();
-        format!("[{}]", reprs.join(","))
-    }
-}
-
-impl LlvmRepr for Type {
-    fn llrepr(&self, ccx: &CrateContext) -> String {
-        ccx.tn().type_to_string(*self)
-    }
-}
-
-impl LlvmRepr for ValueRef {
-    fn llrepr(&self, ccx: &CrateContext) -> String {
-        ccx.tn().val_to_string(*self)
-    }
-}
diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs
index 7397ccc2505..30560ec05c4 100644
--- a/src/librustc_trans/trans/meth.rs
+++ b/src/librustc_trans/trans/meth.rs
@@ -18,14 +18,13 @@ use middle::infer;
 use middle::subst::{Subst, Substs};
 use middle::subst;
 use middle::traits::{self, ProjectionMode};
+use trans::abi::FnType;
 use trans::base::*;
 use trans::build::*;
-use trans::callee::{Callee, Virtual, ArgVals,
-                    trans_fn_pointer_shim, trans_fn_ref_with_substs};
+use trans::callee::{Callee, Virtual, ArgVals, trans_fn_pointer_shim};
 use trans::closure;
 use trans::common::*;
 use trans::consts;
-use trans::datum::*;
 use trans::debuginfo::DebugLoc;
 use trans::declare;
 use trans::expr;
@@ -33,158 +32,25 @@ use trans::glue;
 use trans::machine;
 use trans::type_::Type;
 use trans::type_of::*;
+use trans::value::Value;
 use middle::ty::{self, Ty, TyCtxt, TypeFoldable};
 
-use syntax::ast::{self, Name};
-use syntax::attr;
+use syntax::ast::Name;
 use syntax::codemap::DUMMY_SP;
 
-use rustc_front::hir;
-
 // drop_glue pointer, size, align.
 const VTABLE_OFFSET: usize = 3;
 
-/// The main "translation" pass for methods.  Generates code
-/// for non-monomorphized methods only.  Other methods will
-/// be generated once they are invoked with specific type parameters,
-/// see `trans::base::lval_static_fn()` or `trans::base::monomorphic_fn()`.
-pub fn trans_impl(ccx: &CrateContext,
-                  name: ast::Name,
-                  impl_items: &[hir::ImplItem],
-                  generics: &hir::Generics,
-                  id: ast::NodeId) {
-    let _icx = push_ctxt("meth::trans_impl");
-    let tcx = ccx.tcx();
-
-    debug!("trans_impl(name={}, id={})", name, id);
-
-    // Both here and below with generic methods, be sure to recurse and look for
-    // items that we need to translate.
-    if !generics.ty_params.is_empty() {
-        return;
-    }
-
-    for impl_item in impl_items {
-        match impl_item.node {
-            hir::ImplItemKind::Method(ref sig, ref body) => {
-                if sig.generics.ty_params.is_empty() {
-                    let trans_everywhere = attr::requests_inline(&impl_item.attrs);
-                    for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) {
-                        let llfn = get_item_val(ccx, impl_item.id);
-                        let empty_substs = tcx.mk_substs(Substs::trans_empty());
-                        trans_fn(ccx,
-                                 &sig.decl,
-                                 body,
-                                 llfn,
-                                 empty_substs,
-                                 impl_item.id,
-                                 &impl_item.attrs);
-                        update_linkage(ccx,
-                                       llfn,
-                                       Some(impl_item.id),
-                                       if is_origin { OriginalTranslation } else { InlinedCopy });
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
-}
-
-/// Compute the appropriate callee, give na method's ID, trait ID,
-/// substitutions and a Vtable for that trait.
-pub fn callee_for_trait_impl<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                       method_id: DefId,
-                                       substs: &'tcx subst::Substs<'tcx>,
-                                       trait_id: DefId,
-                                       method_ty: Ty<'tcx>,
-                                       vtable: traits::Vtable<'tcx, ()>)
-                                       -> Callee<'tcx> {
-    let _icx = push_ctxt("meth::callee_for_trait_impl");
-    match vtable {
-        traits::VtableImpl(vtable_impl) => {
-            let impl_did = vtable_impl.impl_def_id;
-            let mname = ccx.tcx().item_name(method_id);
-            // create a concatenated set of substitutions which includes
-            // those from the impl and those from the method:
-            let impl_substs = vtable_impl.substs.with_method_from(&substs);
-            let substs = ccx.tcx().mk_substs(impl_substs);
-            let mth = get_impl_method(ccx.tcx(), impl_did, substs, mname);
-
-            // Translate the function, bypassing Callee::def.
-            // That is because default methods have the same ID as the
-            // trait method used to look up the impl method that ended
-            // up here, so calling Callee::def would infinitely recurse.
-            Callee::ptr(trans_fn_ref_with_substs(ccx, mth.method.def_id,
-                                                 Some(method_ty), mth.substs))
-        }
-        traits::VtableClosure(vtable_closure) => {
-            // The substitutions should have no type parameters remaining
-            // after passing through fulfill_obligation
-            let trait_closure_kind = ccx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
-            let llfn = closure::trans_closure_method(ccx,
-                                                     vtable_closure.closure_def_id,
-                                                     vtable_closure.substs,
-                                                     trait_closure_kind);
-            let fn_ptr_ty = match method_ty.sty {
-                ty::TyFnDef(_, _, fty) => ccx.tcx().mk_ty(ty::TyFnPtr(fty)),
-                _ => unreachable!("expected fn item type, found {}",
-                                  method_ty)
-            };
-            Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
-        }
-        traits::VtableFnPointer(fn_ty) => {
-            let trait_closure_kind = ccx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
-            let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty);
-            let fn_ptr_ty = match method_ty.sty {
-                ty::TyFnDef(_, _, fty) => ccx.tcx().mk_ty(ty::TyFnPtr(fty)),
-                _ => unreachable!("expected fn item type, found {}",
-                                  method_ty)
-            };
-            Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
-        }
-        traits::VtableObject(ref data) => {
-            Callee {
-                data: Virtual(traits::get_vtable_index_of_object_method(
-                    ccx.tcx(), data, method_id)),
-                ty: method_ty
-            }
-        }
-        traits::VtableBuiltin(..) |
-        traits::VtableDefaultImpl(..) |
-        traits::VtableParam(..) => {
-            ccx.sess().bug(
-                &format!("resolved vtable bad vtable {:?} in trans",
-                        vtable));
-        }
-    }
-}
-
-/// Extracts a method from a trait object's vtable, at the
-/// specified index, and casts it to the given type.
+/// Extracts a method from a trait object's vtable, at the specified index.
 pub fn get_virtual_method<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                       llvtable: ValueRef,
-                                      vtable_index: usize,
-                                      method_ty: Ty<'tcx>)
-                                      -> Datum<'tcx, Rvalue> {
-    let _icx = push_ctxt("meth::get_virtual_method");
-    let ccx = bcx.ccx();
-
+                                      vtable_index: usize)
+                                      -> ValueRef {
     // Load the data pointer from the object.
-    debug!("get_virtual_method(callee_ty={}, vtable_index={}, llvtable={})",
-           method_ty,
-           vtable_index,
-           bcx.val_to_string(llvtable));
+    debug!("get_virtual_method(vtable_index={}, llvtable={:?})",
+           vtable_index, Value(llvtable));
 
-    let mptr = Load(bcx, GEPi(bcx, llvtable, &[vtable_index + VTABLE_OFFSET]));
-
-    // Replace the self type (&Self or Box<Self>) with an opaque pointer.
-    if let ty::TyFnDef(_, _, fty) = method_ty.sty {
-        let opaque_ty = opaque_method_ty(ccx.tcx(), fty);
-        immediate_rvalue(PointerCast(bcx, mptr, type_of(ccx, opaque_ty)), opaque_ty)
-    } else {
-        immediate_rvalue(mptr, method_ty)
-    }
+    Load(bcx, GEPi(bcx, llvtable, &[vtable_index + VTABLE_OFFSET]))
 }
 
 /// Generate a shim function that allows an object type like `SomeTrait` to
@@ -211,7 +77,7 @@ pub fn get_virtual_method<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
                                    method_ty: Ty<'tcx>,
                                    vtable_index: usize)
-                                   -> Datum<'tcx, Rvalue> {
+                                   -> ValueRef {
     let _icx = push_ctxt("trans_object_shim");
     let tcx = ccx.tcx();
 
@@ -219,58 +85,40 @@ pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
            vtable_index,
            method_ty);
 
-    let ret_ty = tcx.erase_late_bound_regions(&method_ty.fn_ret());
-    let ret_ty = infer::normalize_associated_type(tcx, &ret_ty);
+    let sig = tcx.erase_late_bound_regions(&method_ty.fn_sig());
+    let sig = infer::normalize_associated_type(tcx, &sig);
+    let fn_ty = FnType::new(ccx, method_ty.fn_abi(), &sig, &[]);
 
-    let shim_fn_ty = match method_ty.sty {
-        ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)),
-        _ => unreachable!("expected fn item type, found {}", method_ty)
-    };
-
-    //
-    let function_name = link::mangle_internal_name_by_type_and_seq(ccx, shim_fn_ty, "object_shim");
-    let llfn = declare::define_internal_rust_fn(ccx, &function_name, shim_fn_ty);
+    let function_name = link::mangle_internal_name_by_type_and_seq(ccx, method_ty, "object_shim");
+    let llfn = declare::define_internal_fn(ccx, &function_name, method_ty);
 
     let empty_substs = tcx.mk_substs(Substs::trans_empty());
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
     block_arena = TypedArena::new();
-    fcx = new_fn_ctxt(ccx,
-                      llfn,
-                      ast::DUMMY_NODE_ID,
-                      false,
-                      ret_ty,
-                      empty_substs,
-                      None,
-                      &block_arena);
-    let mut bcx = init_function(&fcx, false, ret_ty);
-
-    let llargs = get_params(fcx.llfn);
-
-    let self_idx = fcx.arg_offset();
-    let llself = llargs[self_idx];
-    let llvtable = llargs[self_idx + 1];
-
-    debug!("trans_object_shim: llself={}, llvtable={}",
-           bcx.val_to_string(llself), bcx.val_to_string(llvtable));
-
+    fcx = FunctionContext::new(ccx, llfn, fn_ty, None, empty_substs, &block_arena);
+    let mut bcx = fcx.init(false, None);
     assert!(!fcx.needs_ret_allocas);
 
+
     let dest =
         fcx.llretslotptr.get().map(
-            |_| expr::SaveIn(fcx.get_ret_slot(bcx, ret_ty, "ret_slot")));
+            |_| expr::SaveIn(fcx.get_ret_slot(bcx, "ret_slot")));
 
     debug!("trans_object_shim: method_offset_in_vtable={}",
            vtable_index);
 
+    let llargs = get_params(fcx.llfn);
+    let args = ArgVals(&llargs[fcx.fn_ty.ret.is_indirect() as usize..]);
+
     let callee = Callee {
         data: Virtual(vtable_index),
         ty: method_ty
     };
-    bcx = callee.call(bcx, DebugLoc::None, ArgVals(&llargs[self_idx..]), dest).bcx;
+    bcx = callee.call(bcx, DebugLoc::None, args, dest).bcx;
 
-    finish_fn(&fcx, bcx, ret_ty, DebugLoc::None);
+    fcx.finish(bcx, DebugLoc::None);
 
-    immediate_rvalue(llfn, shim_fn_ty)
+    llfn
 }
 
 /// Creates a returns a dynamic vtable for the given type and vtable origin.
@@ -311,17 +159,9 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                 let nullptr = C_null(Type::nil(ccx).ptr_to());
                 get_vtable_methods(ccx, id, substs)
                     .into_iter()
-                    .map(|opt_mth| {
-                        match opt_mth {
-                            Some(mth) => {
-                                trans_fn_ref_with_substs(ccx,
-                                                         mth.method.def_id,
-                                                         None,
-                                                         &mth.substs).val
-                            }
-                            None => nullptr
-                        }
-                    })
+                    .map(|opt_mth| opt_mth.map_or(nullptr, |mth| {
+                        Callee::def(ccx, mth.method.def_id, &mth.substs).reify(ccx).val
+                    }))
                     .collect::<Vec<_>>()
                     .into_iter()
             }
@@ -452,23 +292,6 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         .collect()
 }
 
-/// Replace the self type (&Self or Box<Self>) with an opaque pointer.
-fn opaque_method_ty<'tcx>(tcx: &TyCtxt<'tcx>, method_ty: &ty::BareFnTy<'tcx>)
-                          -> Ty<'tcx> {
-    let mut inputs = method_ty.sig.0.inputs.clone();
-    inputs[0] = tcx.mk_mut_ptr(tcx.mk_mach_int(ast::IntTy::I8));
-
-    tcx.mk_fn_ptr(ty::BareFnTy {
-        unsafety: method_ty.unsafety,
-        abi: method_ty.abi,
-        sig: ty::Binder(ty::FnSig {
-            inputs: inputs,
-            output: method_ty.sig.0.output,
-            variadic: method_ty.sig.0.variadic,
-        }),
-    })
-}
-
 #[derive(Debug)]
 pub struct ImplMethod<'tcx> {
     pub method: Rc<ty::Method<'tcx>>,
diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs
index 50283c0959c..080547952a5 100644
--- a/src/librustc_trans/trans/mir/block.rs
+++ b/src/librustc_trans/trans/mir/block.rs
@@ -8,33 +8,35 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use llvm::{BasicBlockRef, ValueRef, OperandBundleDef};
+use llvm::{self, BasicBlockRef, ValueRef, OperandBundleDef};
 use rustc::middle::ty;
 use rustc::mir::repr as mir;
-use syntax::abi::Abi;
+use trans::abi::{Abi, FnType};
 use trans::adt;
-use trans::attributes;
 use trans::base;
 use trans::build;
-use trans::callee::{Callee, Fn, Virtual};
-use trans::common::{self, Block, BlockAndBuilder};
+use trans::callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual};
+use trans::common::{self, Block, BlockAndBuilder, C_undef};
 use trans::debuginfo::DebugLoc;
 use trans::Disr;
-use trans::foreign;
+use trans::machine::{llalign_of_min, llbitsize_of_real};
 use trans::meth;
 use trans::type_of;
 use trans::glue;
 use trans::type_::Type;
 
 use super::{MirContext, drop};
-use super::operand::OperandValue::{FatPtr, Immediate, Ref};
+use super::lvalue::{LvalueRef, load_fat_ptr};
+use super::operand::OperandRef;
+use super::operand::OperandValue::{self, FatPtr, Immediate, Ref};
 
 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);
+        let mir = self.mir.clone();
+        let data = mir.basic_block_data(bb);
 
         // MSVC SEH bits
         let (cleanup_pad, cleanup_bundle) = if let Some((cp, cb)) = self.make_cleanup_pad(bb) {
@@ -104,6 +106,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             mir::Terminator::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
                 let (otherwise, targets) = targets.split_last().unwrap();
                 let discr = bcx.load(self.trans_lvalue(&bcx, discr).llval);
+                let discr = bcx.with_block(|bcx| base::to_immediate(bcx, discr, switch_ty));
                 let switch = bcx.switch(discr, self.llblock(*otherwise), values.len());
                 for (value, target) in values.iter().zip(targets) {
                     let llval = self.trans_constval(&bcx, value, switch_ty).immediate();
@@ -113,9 +116,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             }
 
             mir::Terminator::Return => {
-                let return_ty = bcx.monomorphize(&self.mir.return_ty);
                 bcx.with_block(|bcx| {
-                    base::build_return_block(self.fcx, bcx, return_ty, DebugLoc::None);
+                    self.fcx.build_return_block(bcx, DebugLoc::None);
                 })
             }
 
@@ -141,11 +143,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                &[llvalue],
                                self.llblock(target),
                                unwind.llbb(),
-                               cleanup_bundle.as_ref(),
-                               None);
+                               cleanup_bundle.as_ref());
                     self.bcx(target).at_start(|bcx| drop::drop_fill(bcx, lvalue.llval, ty));
                 } else {
-                    bcx.call(drop_fn, &[llvalue], cleanup_bundle.as_ref(), None);
+                    bcx.call(drop_fn, &[llvalue], cleanup_bundle.as_ref());
                     drop::drop_fill(&bcx, lvalue.llval, ty);
                     funclet_br(bcx, self.llblock(target));
                 }
@@ -154,190 +155,341 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             mir::Terminator::Call { ref func, ref args, ref destination, ref cleanup } => {
                 // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
                 let callee = self.trans_operand(&bcx, func);
-                let debugloc = DebugLoc::None;
-                // The arguments we'll be passing. Plus one to account for outptr, if used.
-                let mut llargs = Vec::with_capacity(args.len() + 1);
-                // Types of the arguments. We do not preallocate, because this vector is only
-                // filled when `is_foreign` is `true` and foreign calls are minority of the cases.
-                let mut arg_tys = Vec::new();
 
-                let (callee, fty) = match callee.ty.sty {
+                let (mut callee, abi, sig) = match callee.ty.sty {
                     ty::TyFnDef(def_id, substs, f) => {
-                        (Callee::def(bcx.ccx(), def_id, substs, callee.ty), f)
+                        (Callee::def(bcx.ccx(), def_id, substs), f.abi, &f.sig)
                     }
                     ty::TyFnPtr(f) => {
                         (Callee {
                             data: Fn(callee.immediate()),
                             ty: callee.ty
-                        }, f)
+                        }, f.abi, &f.sig)
                     }
                     _ => unreachable!("{} is not callable", callee.ty)
                 };
 
-                // We do not translate intrinsics here (they shouldn’t be functions)
-                assert!(fty.abi != Abi::RustIntrinsic && fty.abi != Abi::PlatformIntrinsic);
-                // Foreign-ABI functions are translated differently
-                let is_foreign = fty.abi != Abi::Rust && fty.abi != Abi::RustCall;
+                // Handle intrinsics old trans wants Expr's for, ourselves.
+                let intrinsic = match (&callee.ty.sty, &callee.data) {
+                    (&ty::TyFnDef(def_id, _, _), &Intrinsic) => {
+                        Some(bcx.tcx().item_name(def_id).as_str())
+                    }
+                    _ => None
+                };
+                let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
+
+                if intrinsic == Some("move_val_init") {
+                    let &(_, target) = destination.as_ref().unwrap();
+                    // The first argument is a thin destination pointer.
+                    let llptr = self.trans_operand(&bcx, &args[0]).immediate();
+                    let val = self.trans_operand(&bcx, &args[1]);
+                    self.store_operand(&bcx, llptr, val);
+                    self.set_operand_dropped(&bcx, &args[1]);
+                    funclet_br(bcx, self.llblock(target));
+                    return;
+                }
+
+                if intrinsic == Some("transmute") {
+                    let &(ref dest, target) = destination.as_ref().unwrap();
+                    let dst = self.trans_lvalue(&bcx, dest);
+                    let mut val = self.trans_operand(&bcx, &args[0]);
+                    if let ty::TyFnDef(def_id, substs, _) = val.ty.sty {
+                        let llouttype = type_of::type_of(bcx.ccx(), dst.ty.to_ty(bcx.tcx()));
+                        let out_type_size = llbitsize_of_real(bcx.ccx(), llouttype);
+                        if out_type_size != 0 {
+                            // FIXME #19925 Remove this hack after a release cycle.
+                            let f = Callee::def(bcx.ccx(), def_id, substs);
+                            let datum = f.reify(bcx.ccx());
+                            val = OperandRef {
+                                val: OperandValue::Immediate(datum.val),
+                                ty: datum.ty
+                            };
+                        }
+                    }
+
+                    let llty = type_of::type_of(bcx.ccx(), val.ty);
+                    let cast_ptr = bcx.pointercast(dst.llval, llty.ptr_to());
+                    self.store_operand(&bcx, cast_ptr, val);
+                    self.set_operand_dropped(&bcx, &args[0]);
+                    funclet_br(bcx, self.llblock(target));
+                    return;
+                }
+
+                let extra_args = &args[sig.0.inputs.len()..];
+                let extra_args = extra_args.iter().map(|op_arg| {
+                    self.mir.operand_ty(bcx.tcx(), op_arg)
+                }).collect::<Vec<_>>();
+                let fn_ty = callee.direct_fn_type(bcx.ccx(), &extra_args);
+
+                // The arguments we'll be passing. Plus one to account for outptr, if used.
+                let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize;
+                let mut llargs = Vec::with_capacity(arg_count);
 
                 // Prepare the return value destination
-                let (ret_dest_ty, must_copy_dest) = if let Some((ref d, _)) = *destination {
+                let ret_dest = if let Some((ref d, _)) = *destination {
                     let dest = self.trans_lvalue(&bcx, d);
-                    let ret_ty = dest.ty.to_ty(bcx.tcx());
-                    if !is_foreign && type_of::return_uses_outptr(bcx.ccx(), ret_ty) {
+                    if fn_ty.ret.is_indirect() {
                         llargs.push(dest.llval);
-                        (Some((dest, ret_ty)), false)
+                        None
+                    } else if fn_ty.ret.is_ignore() {
+                        None
                     } else {
-                        (Some((dest, ret_ty)), !common::type_is_zero_size(bcx.ccx(), ret_ty))
+                        Some(dest)
                     }
                 } else {
-                    (None, false)
+                    None
                 };
 
                 // Split the rust-call tupled arguments off.
-                let (args, rest) = if fty.abi == Abi::RustCall && !args.is_empty() {
+                let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() {
                     let (tup, args) = args.split_last().unwrap();
-                    // we can reorder safely because of MIR
-                    (args, self.trans_operand_untupled(&bcx, tup))
+                    (args, Some(tup))
                 } else {
-                    (&args[..], vec![])
+                    (&args[..], None)
                 };
 
-                let datum = {
-                    let mut arg_ops = args.iter().map(|arg| {
-                        self.trans_operand(&bcx, arg)
-                    }).chain(rest.into_iter());
-
-                    // Get the actual pointer we can call.
-                    // This can involve vtable accesses or reification.
-                    let datum = if let Virtual(idx) = callee.data {
-                        assert!(!is_foreign);
-
-                        // Grab the first argument which is a trait object.
-                        let vtable = match arg_ops.next().unwrap().val {
-                            FatPtr(data, vtable) => {
-                                llargs.push(data);
-                                vtable
-                            }
-                            _ => unreachable!("expected FatPtr for Virtual call")
+                let mut idx = 0;
+                for arg in first_args {
+                    let val = self.trans_operand(&bcx, arg).val;
+                    self.trans_argument(&bcx, val, &mut llargs, &fn_ty,
+                                        &mut idx, &mut callee.data);
+                }
+                if let Some(tup) = untuple {
+                    self.trans_arguments_untupled(&bcx, tup, &mut llargs, &fn_ty,
+                                                  &mut idx, &mut callee.data)
+                }
+
+                let fn_ptr = match callee.data {
+                    NamedTupleConstructor(_) => {
+                        // FIXME translate this like mir::Rvalue::Aggregate.
+                        callee.reify(bcx.ccx()).val
+                    }
+                    Intrinsic => {
+                        use trans::callee::ArgVals;
+                        use trans::expr::{Ignore, SaveIn};
+                        use trans::intrinsic::trans_intrinsic_call;
+
+                        let (dest, llargs) = if fn_ty.ret.is_indirect() {
+                            (SaveIn(llargs[0]), &llargs[1..])
+                        } else if let Some(dest) = ret_dest {
+                            (SaveIn(dest.llval), &llargs[..])
+                        } else {
+                            (Ignore, &llargs[..])
                         };
 
                         bcx.with_block(|bcx| {
-                            meth::get_virtual_method(bcx, vtable, idx, callee.ty)
-                        })
-                    } else {
-                        callee.reify(bcx.ccx())
-                    };
-
-                    // Process the rest of the args.
-                    for operand in arg_ops {
-                        match operand.val {
-                            Ref(llval) | Immediate(llval) => llargs.push(llval),
-                            FatPtr(b, e) => {
-                                llargs.push(b);
-                                llargs.push(e);
+                            let res = trans_intrinsic_call(bcx, callee.ty, &fn_ty,
+                                                           ArgVals(llargs), dest,
+                                                           DebugLoc::None);
+                            let bcx = res.bcx.build();
+                            if let Some((_, target)) = *destination {
+                                for op in args {
+                                    self.set_operand_dropped(&bcx, op);
+                                }
+                                funclet_br(bcx, self.llblock(target));
+                            } else {
+                                // trans_intrinsic_call already used Unreachable.
+                                // bcx.unreachable();
                             }
-                        }
-                        if is_foreign {
-                            arg_tys.push(operand.ty);
-                        }
+                        });
+                        return;
                     }
-
-                    datum
+                    Fn(f) => f,
+                    Virtual(_) => unreachable!("Virtual fn ptr not extracted")
                 };
-                let attrs = attributes::from_fn_type(bcx.ccx(), datum.ty);
 
                 // Many different ways to call a function handled here
-                match (is_foreign, cleanup, destination) {
-                    // The two cases below are the only ones to use LLVM’s `invoke`.
-                    (false, &Some(cleanup), &None) => {
-                        let cleanup = self.bcx(cleanup);
-                        let landingpad = self.make_landing_pad(cleanup);
-                        let unreachable_blk = self.unreachable_block();
-                        bcx.invoke(datum.val,
-                                   &llargs[..],
-                                   unreachable_blk.llbb,
-                                   landingpad.llbb(),
-                                   cleanup_bundle.as_ref(),
-                                   Some(attrs));
-                        landingpad.at_start(|bcx| for op in args {
-                            self.set_operand_dropped(bcx, op);
-                        });
-                    },
-                    (false, &Some(cleanup), &Some((_, success))) => {
-                        let cleanup = self.bcx(cleanup);
-                        let landingpad = self.make_landing_pad(cleanup);
-                        let invokeret = bcx.invoke(datum.val,
-                                                   &llargs[..],
-                                                   self.llblock(success),
-                                                   landingpad.llbb(),
-                                                   cleanup_bundle.as_ref(),
-                                                   Some(attrs));
-                        if must_copy_dest {
-                            let (ret_dest, ret_ty) = ret_dest_ty
-                                .expect("return destination and type not set");
-                            // We translate the copy straight into the beginning of the target
-                            // block.
-                            self.bcx(success).at_start(|bcx| bcx.with_block( |bcx| {
-                                base::store_ty(bcx, invokeret, ret_dest.llval, ret_ty);
-                            }));
+                if let Some(cleanup) = cleanup.map(|bb| self.bcx(bb)) {
+                    // We translate the copy into a temporary block. The temporary block is
+                    // necessary because the current block has already been terminated (by
+                    // `invoke`) and we cannot really translate into the target block
+                    // because:
+                    //  * The target block may have more than a single precedesor;
+                    //  * Some LLVM insns cannot have a preceeding store insn (phi,
+                    //    cleanuppad), and adding/prepending the store now may render
+                    //    those other instructions invalid.
+                    //
+                    // NB: This approach still may break some LLVM code. For example if the
+                    // target block starts with a `phi` (which may only match on immediate
+                    // precedesors), it cannot know about this temporary block thus
+                    // resulting in an invalid code:
+                    //
+                    // this:
+                    //     …
+                    //     %0 = …
+                    //     %1 = invoke to label %temp …
+                    // temp:
+                    //     store ty %1, ty* %dest
+                    //     br label %actualtargetblock
+                    // actualtargetblock:            ; preds: %temp, …
+                    //     phi … [%this, …], [%0, …] ; ERROR: phi requires to match only on
+                    //                               ; immediate precedesors
+
+                    let ret_bcx = if destination.is_some() {
+                        self.fcx.new_block("", None)
+                    } else {
+                        self.unreachable_block()
+                    };
+                    let landingpad = self.make_landing_pad(cleanup);
+
+                    let invokeret = bcx.invoke(fn_ptr,
+                                               &llargs,
+                                               ret_bcx.llbb,
+                                               landingpad.llbb(),
+                                               cleanup_bundle.as_ref());
+                    fn_ty.apply_attrs_callsite(invokeret);
+
+                    landingpad.at_start(|bcx| for op in args {
+                        self.set_operand_dropped(bcx, op);
+                    });
+
+                    if let Some((_, target)) = *destination {
+                        let ret_bcx = ret_bcx.build();
+                        if let Some(ret_dest) = ret_dest {
+                            fn_ty.ret.store(&ret_bcx, invokeret, ret_dest.llval);
                         }
-                        self.bcx(success).at_start(|bcx| for op in args {
-                            self.set_operand_dropped(bcx, op);
-                        });
-                        landingpad.at_start(|bcx| for op in args {
-                            self.set_operand_dropped(bcx, op);
-                        });
-                    },
-                    (false, _, &None) => {
-                        bcx.call(datum.val,
-                                 &llargs[..],
-                                 cleanup_bundle.as_ref(),
-                                 Some(attrs));
-                        // no need to drop args, because the call never returns
-                        bcx.unreachable();
+                        for op in args {
+                            self.set_operand_dropped(&ret_bcx, op);
+                        }
+                        ret_bcx.br(self.llblock(target));
                     }
-                    (false, _, &Some((_, target))) => {
-                        let llret = bcx.call(datum.val,
-                                             &llargs[..],
-                                             cleanup_bundle.as_ref(),
-                                             Some(attrs));
-                        if must_copy_dest {
-                            let (ret_dest, ret_ty) = ret_dest_ty
-                                .expect("return destination and type not set");
-                            bcx.with_block(|bcx| {
-                                base::store_ty(bcx, llret, ret_dest.llval, ret_ty);
-                            });
+                } else {
+                    let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle.as_ref());
+                    fn_ty.apply_attrs_callsite(llret);
+                    if let Some((_, target)) = *destination {
+                        if let Some(ret_dest) = ret_dest {
+                            fn_ty.ret.store(&bcx, llret, ret_dest.llval);
                         }
                         for op in args {
                             self.set_operand_dropped(&bcx, op);
                         }
                         funclet_br(bcx, self.llblock(target));
+                    } else {
+                        // no need to drop args, because the call never returns
+                        bcx.unreachable();
                     }
-                    // Foreign functions
-                    (true, _, destination) => {
-                        let (dest, _) = ret_dest_ty
-                            .expect("return destination is not set");
-                        bcx = bcx.map_block(|bcx| {
-                            foreign::trans_native_call(bcx,
-                                                       datum.ty,
-                                                       datum.val,
-                                                       dest.llval,
-                                                       &llargs[..],
-                                                       arg_tys,
-                                                       debugloc)
-                        });
-                        if let Some((_, target)) = *destination {
-                            for op in args {
-                                self.set_operand_dropped(&bcx, op);
-                            }
-                            funclet_br(bcx, self.llblock(target));
-                        }
-                    },
                 }
             }
         }
     }
 
+    fn trans_argument(&mut self,
+                      bcx: &BlockAndBuilder<'bcx, 'tcx>,
+                      val: OperandValue,
+                      llargs: &mut Vec<ValueRef>,
+                      fn_ty: &FnType,
+                      next_idx: &mut usize,
+                      callee: &mut CalleeData) {
+        // Treat the values in a fat pointer separately.
+        if let FatPtr(ptr, meta) = val {
+            if *next_idx == 0 {
+                if let Virtual(idx) = *callee {
+                    let llfn = bcx.with_block(|bcx| {
+                        meth::get_virtual_method(bcx, meta, idx)
+                    });
+                    let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to();
+                    *callee = Fn(bcx.pointercast(llfn, llty));
+                }
+            }
+            self.trans_argument(bcx, Immediate(ptr), llargs, fn_ty, next_idx, callee);
+            self.trans_argument(bcx, Immediate(meta), llargs, fn_ty, next_idx, callee);
+            return;
+        }
+
+        let arg = &fn_ty.args[*next_idx];
+        *next_idx += 1;
+
+        // Fill padding with undef value, where applicable.
+        if let Some(ty) = arg.pad {
+            llargs.push(C_undef(ty));
+        }
+
+        if arg.is_ignore() {
+            return;
+        }
+
+        // Force by-ref if we have to load through a cast pointer.
+        let (mut llval, by_ref) = match val {
+            Immediate(llval) if arg.is_indirect() || arg.cast.is_some() => {
+                let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg");
+                bcx.store(llval, llscratch);
+                (llscratch, true)
+            }
+            Immediate(llval) => (llval, false),
+            Ref(llval) => (llval, true),
+            FatPtr(_, _) => unreachable!("fat pointers handled above")
+        };
+
+        if by_ref && !arg.is_indirect() {
+            // Have to load the argument, maybe while casting it.
+            if arg.original_ty == Type::i1(bcx.ccx()) {
+                // We store bools as i8 so we need to truncate to i1.
+                llval = bcx.load_range_assert(llval, 0, 2, llvm::False);
+                llval = bcx.trunc(llval, arg.original_ty);
+            } else if let Some(ty) = arg.cast {
+                llval = bcx.load(bcx.pointercast(llval, ty.ptr_to()));
+                let llalign = llalign_of_min(bcx.ccx(), arg.ty);
+                unsafe {
+                    llvm::LLVMSetAlignment(llval, llalign);
+                }
+            } else {
+                llval = bcx.load(llval);
+            }
+        }
+
+        llargs.push(llval);
+    }
+
+    fn trans_arguments_untupled(&mut self,
+                                bcx: &BlockAndBuilder<'bcx, 'tcx>,
+                                operand: &mir::Operand<'tcx>,
+                                llargs: &mut Vec<ValueRef>,
+                                fn_ty: &FnType,
+                                next_idx: &mut usize,
+                                callee: &mut CalleeData) {
+        // FIXME: consider having some optimization to avoid tupling/untupling
+        // (and storing/loading in the case of immediates)
+
+        // avoid trans_operand for pointless copying
+        let lv = match *operand {
+            mir::Operand::Consume(ref lvalue) => self.trans_lvalue(bcx, lvalue),
+            mir::Operand::Constant(ref constant) => {
+                // FIXME: consider being less pessimized
+                if constant.ty.is_nil() {
+                    return;
+                }
+
+                let ty = bcx.monomorphize(&constant.ty);
+                let lv = LvalueRef::alloca(bcx, ty, "__untuple_alloca");
+                let constant = self.trans_constant(bcx, constant);
+                self.store_operand(bcx, lv.llval, constant);
+                lv
+           }
+        };
+
+        let lv_ty = lv.ty.to_ty(bcx.tcx());
+        let result_types = match lv_ty.sty {
+            ty::TyTuple(ref tys) => tys,
+            _ => bcx.tcx().sess.span_bug(
+                self.mir.span,
+                &format!("bad final argument to \"rust-call\" fn {:?}", lv_ty))
+        };
+
+        let base_repr = adt::represent_type(bcx.ccx(), lv_ty);
+        let base = adt::MaybeSizedValue::sized(lv.llval);
+        for (n, &ty) in result_types.iter().enumerate() {
+            let ptr = adt::trans_field_ptr_builder(bcx, &base_repr, base, Disr(0), n);
+            let val = if common::type_is_fat_ptr(bcx.tcx(), ty) {
+                let (lldata, llextra) = load_fat_ptr(bcx, ptr);
+                FatPtr(lldata, llextra)
+            } else {
+                // Don't bother loading the value, trans_argument will.
+                Ref(ptr)
+            };
+            self.trans_argument(bcx, val, llargs, fn_ty, next_idx, callee);
+        }
+    }
+
     fn get_personality_slot(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>) -> ValueRef {
         let ccx = bcx.ccx();
         if let Some(slot) = self.llpersonalityslot {
diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs
index c20d8b01eb7..d4934718d75 100644
--- a/src/librustc_trans/trans/mir/constant.rs
+++ b/src/librustc_trans/trans/mir/constant.rs
@@ -8,17 +8,17 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use back::abi;
 use llvm::ValueRef;
 use middle::ty::{Ty, TypeFoldable};
 use rustc::middle::const_eval::{self, ConstVal};
 use rustc_const_eval::ConstInt::*;
 use rustc::mir::repr as mir;
+use trans::abi;
 use trans::common::{self, BlockAndBuilder, C_bool, C_bytes, C_floating_f64, C_integral,
-                    C_str_slice, C_nil, C_undef};
+                    C_str_slice, C_undef};
 use trans::consts;
+use trans::datum;
 use trans::expr;
-use trans::inline;
 use trans::type_of;
 use trans::type_::Type;
 
@@ -38,8 +38,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         let val = if common::type_is_immediate(ccx, ty) {
             OperandValue::Immediate(val)
         } else if common::type_is_fat_ptr(bcx.tcx(), ty) {
-            let data = common::const_get_elt(ccx, val, &[abi::FAT_PTR_ADDR as u32]);
-            let extra = common::const_get_elt(ccx, val, &[abi::FAT_PTR_EXTRA as u32]);
+            let data = common::const_get_elt(val, &[abi::FAT_PTR_ADDR as u32]);
+            let extra = common::const_get_elt(val, &[abi::FAT_PTR_EXTRA as u32]);
             OperandValue::FatPtr(data, extra)
         } else {
             OperandValue::Ref(val)
@@ -85,16 +85,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             ConstVal::Integral(InferSigned(v)) => C_integral(llty, v as u64, true),
             ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
             ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"),
-            ConstVal::Struct(id) | ConstVal::Tuple(id) |
-            ConstVal::Array(id, _) | ConstVal::Repeat(id, _) => {
-                let expr = bcx.tcx().map.expect_expr(id);
-                bcx.with_block(|bcx| {
-                    expr::trans(bcx, expr).datum.val
-                })
-            },
+            ConstVal::Struct(_) | ConstVal::Tuple(_) |
+            ConstVal::Array(..) | ConstVal::Repeat(..) |
+            ConstVal::Function(_) => {
+                unreachable!("MIR must not use {:?} (which refers to a local ID)", cv)
+            }
             ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false),
             ConstVal::Dummy => unreachable!(),
-            ConstVal::Function(_) => C_nil(ccx)
         }
     }
 
@@ -116,16 +113,26 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     };
                 }
 
-                let substs = bcx.tcx().mk_substs(bcx.monomorphize(&substs));
-                let def_id = inline::maybe_instantiate_inline(bcx.ccx(), def_id);
-                let expr = const_eval::lookup_const_by_id(bcx.tcx(), def_id, None, Some(substs))
+                let substs = Some(bcx.monomorphize(substs));
+                let expr = const_eval::lookup_const_by_id(bcx.tcx(), def_id, substs)
                             .expect("def was const, but lookup_const_by_id failed").0;
                 // FIXME: this is falling back to translating from HIR. This is not easy to fix,
                 // because we would have somehow adapt const_eval to work on MIR rather than HIR.
                 let d = bcx.with_block(|bcx| {
                     expr::trans(bcx, expr)
                 });
-                OperandRef::from_rvalue_datum(d.datum.to_rvalue_datum(d.bcx, "").datum)
+
+                let datum = d.datum.to_rvalue_datum(d.bcx, "").datum;
+
+                match datum.kind.mode {
+                    datum::RvalueMode::ByValue => {
+                        OperandRef {
+                            ty: datum.ty,
+                            val: OperandValue::Immediate(datum.val)
+                        }
+                    }
+                    datum::RvalueMode::ByRef => self.trans_load(bcx, datum.val, datum.ty)
+                }
             }
             mir::Literal::Value { ref value } => {
                 self.trans_constval(bcx, value, ty)
diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs
index 8d97708ca26..ffc3b120674 100644
--- a/src/librustc_trans/trans/mir/lvalue.rs
+++ b/src/librustc_trans/trans/mir/lvalue.rs
@@ -12,11 +12,13 @@ use llvm::ValueRef;
 use rustc::middle::ty::{self, Ty, TypeFoldable};
 use rustc::mir::repr as mir;
 use rustc::mir::tcx::LvalueTy;
+use trans::abi;
 use trans::adt;
 use trans::base;
-use trans::common::{self, BlockAndBuilder};
+use trans::builder::Builder;
+use trans::common::{self, BlockAndBuilder, C_uint};
+use trans::consts;
 use trans::machine;
-use trans::type_of;
 use trans::mir::drop;
 use llvm;
 use trans::Disr;
@@ -49,11 +51,25 @@ impl<'tcx> LvalueRef<'tcx> {
     {
         assert!(!ty.has_erasable_regions());
         let lltemp = bcx.with_block(|bcx| base::alloc_ty(bcx, ty, name));
-        drop::drop_fill(bcx, lltemp, ty);
+        if bcx.fcx().type_needs_drop(ty) {
+            drop::drop_fill(bcx, lltemp, ty);
+        }
         LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty))
     }
 }
 
+pub fn get_meta(b: &Builder, fat_ptr: ValueRef) -> ValueRef {
+    b.struct_gep(fat_ptr, abi::FAT_PTR_EXTRA)
+}
+
+pub fn get_dataptr(b: &Builder, fat_ptr: ValueRef) -> ValueRef {
+    b.struct_gep(fat_ptr, abi::FAT_PTR_ADDR)
+}
+
+pub fn load_fat_ptr(b: &Builder, fat_ptr: ValueRef) -> (ValueRef, ValueRef) {
+    (b.load(get_dataptr(b, fat_ptr)), b.load(get_meta(b, fat_ptr)))
+}
+
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn lvalue_len(&mut self,
                       bcx: &BlockAndBuilder<'bcx, 'tcx>,
@@ -89,16 +105,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             mir::Lvalue::Arg(index) => self.args[index as usize],
             mir::Lvalue::Static(def_id) => {
                 let const_ty = self.mir.lvalue_ty(tcx, lvalue);
-                LvalueRef::new_sized(
-                    common::get_static_val(ccx, def_id, const_ty.to_ty(tcx)),
-                    const_ty)
+                LvalueRef::new_sized(consts::get_static(ccx, def_id).val, const_ty)
             },
             mir::Lvalue::ReturnPointer => {
-                let fn_return_ty = bcx.monomorphize(&self.mir.return_ty);
-                let return_ty = fn_return_ty.unwrap();
-                let llval = if !common::return_type_is_void(bcx.ccx(), return_ty) {
+                let llval = if !fcx.fn_ty.ret.is_ignore() {
                     bcx.with_block(|bcx| {
-                        fcx.get_ret_slot(bcx, fn_return_ty, "")
+                        fcx.get_ret_slot(bcx, "")
                     })
                 } else {
                     // This is a void return; that is, there’s no place to store the value and
@@ -106,27 +118,40 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     // Ergo, we return an undef ValueRef, so we do not have to special-case every
                     // place using lvalues, and could use it the same way you use a regular
                     // ReturnPointer LValue (i.e. store into it, load from it etc).
-                    let llty = type_of::type_of(bcx.ccx(), return_ty).ptr_to();
+                    let llty = fcx.fn_ty.ret.original_ty.ptr_to();
                     unsafe {
                         llvm::LLVMGetUndef(llty.to_ref())
                     }
                 };
+                let fn_return_ty = bcx.monomorphize(&self.mir.return_ty);
+                let return_ty = fn_return_ty.unwrap();
                 LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty))
             },
             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 projected_ty = bcx.monomorphize(&projected_ty);
+
+                let project_index = |llindex| {
+                    let element = if let ty::TySlice(_) = tr_base.ty.to_ty(tcx).sty {
+                        // Slices already point to the array element type.
+                        bcx.inbounds_gep(tr_base.llval, &[llindex])
+                    } else {
+                        let zero = common::C_uint(bcx.ccx(), 0u64);
+                        bcx.inbounds_gep(tr_base.llval, &[zero, llindex])
+                    };
+                    (element, ptr::null_mut())
+                };
+
                 let (llprojected, llextra) = match projection.elem {
                     mir::ProjectionElem::Deref => {
                         let base_ty = tr_base.ty.to_ty(tcx);
-                        bcx.with_block(|bcx| {
-                            if common::type_is_sized(tcx, projected_ty.to_ty(tcx)) {
-                                (base::load_ty(bcx, tr_base.llval, base_ty),
-                                 ptr::null_mut())
-                            } else {
-                                base::load_fat_ptr(bcx, tr_base.llval, base_ty)
-                            }
-                        })
+                        if common::type_is_sized(tcx, projected_ty.to_ty(tcx)) {
+                            (base::load_ty_builder(bcx, tr_base.llval, base_ty),
+                             ptr::null_mut())
+                        } else {
+                            load_fat_ptr(bcx, tr_base.llval)
+                        }
                     }
                     mir::ProjectionElem::Field(ref field, _) => {
                         let base_ty = tr_base.ty.to_ty(tcx);
@@ -142,9 +167,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         } else {
                             adt::MaybeSizedValue::unsized_(tr_base.llval, tr_base.llextra)
                         };
-                        let llprojected = bcx.with_block(|bcx| {
-                            adt::trans_field_ptr(bcx, &base_repr, base, Disr(discr), field.index())
-                        });
+                        let llprojected = adt::trans_field_ptr_builder(bcx, &base_repr, base,
+                                                                       Disr(discr), field.index());
                         let llextra = if is_sized {
                             ptr::null_mut()
                         } else {
@@ -154,30 +178,21 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     }
                     mir::ProjectionElem::Index(ref index) => {
                         let index = self.trans_operand(bcx, index);
-                        let llindex = self.prepare_index(bcx, index.immediate());
-                        let zero = common::C_uint(bcx.ccx(), 0u64);
-                        (bcx.inbounds_gep(tr_base.llval, &[zero, llindex]),
-                         ptr::null_mut())
+                        project_index(self.prepare_index(bcx, index.immediate()))
                     }
                     mir::ProjectionElem::ConstantIndex { offset,
                                                          from_end: false,
                                                          min_length: _ } => {
-                        let lloffset = common::C_u32(bcx.ccx(), offset);
-                        let llindex = self.prepare_index(bcx, lloffset);
-                        let zero = common::C_uint(bcx.ccx(), 0u64);
-                        (bcx.inbounds_gep(tr_base.llval, &[zero, llindex]),
-                         ptr::null_mut())
+                        let lloffset = C_uint(bcx.ccx(), offset);
+                        project_index(self.prepare_index(bcx, lloffset))
                     }
                     mir::ProjectionElem::ConstantIndex { offset,
                                                          from_end: true,
                                                          min_length: _ } => {
-                        let lloffset = common::C_u32(bcx.ccx(), offset);
+                        let lloffset = C_uint(bcx.ccx(), offset);
                         let lllen = self.lvalue_len(bcx, tr_base);
                         let llindex = bcx.sub(lllen, lloffset);
-                        let llindex = self.prepare_index(bcx, llindex);
-                        let zero = common::C_uint(bcx.ccx(), 0u64);
-                        (bcx.inbounds_gep(tr_base.llval, &[zero, llindex]),
-                         ptr::null_mut())
+                        project_index(self.prepare_index(bcx, llindex))
                     }
                     mir::ProjectionElem::Downcast(..) => {
                         (tr_base.llval, tr_base.llextra)
diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs
index 4ad2e035945..9df2cb712b1 100644
--- a/src/librustc_trans/trans/mir/mod.rs
+++ b/src/librustc_trans/trans/mir/mod.rs
@@ -10,21 +10,39 @@
 
 use libc::c_uint;
 use llvm::{self, ValueRef};
+use middle::ty;
 use rustc::mir::repr as mir;
 use rustc::mir::tcx::LvalueTy;
 use trans::base;
-use trans::common::{self, Block, BlockAndBuilder};
-use trans::expr;
-use trans::type_of;
+use trans::common::{self, Block, BlockAndBuilder, FunctionContext};
 
-use self::lvalue::LvalueRef;
+use std::ops::Deref;
+use std::rc::Rc;
+
+use self::lvalue::{LvalueRef, get_dataptr, get_meta};
 use self::operand::OperandRef;
 
+#[derive(Clone)]
+pub enum CachedMir<'mir, 'tcx: 'mir> {
+    Ref(&'mir mir::Mir<'tcx>),
+    Owned(Rc<mir::Mir<'tcx>>)
+}
+
+impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> {
+    type Target = mir::Mir<'tcx>;
+    fn deref(&self) -> &mir::Mir<'tcx> {
+        match *self {
+            CachedMir::Ref(r) => r,
+            CachedMir::Owned(ref rc) => rc
+        }
+    }
+}
+
 // FIXME DebugLoc is always None right now
 
 /// Master context for translating MIR.
 pub struct MirContext<'bcx, 'tcx:'bcx> {
-    mir: &'bcx mir::Mir<'tcx>,
+    mir: CachedMir<'bcx, 'tcx>,
 
     /// Function context
     fcx: &'bcx common::FunctionContext<'bcx, 'tcx>,
@@ -77,16 +95,16 @@ enum TempRef<'tcx> {
 
 ///////////////////////////////////////////////////////////////////////////
 
-pub fn trans_mir<'bcx, 'tcx>(bcx: BlockAndBuilder<'bcx, 'tcx>) {
-    let fcx = bcx.fcx();
+pub fn trans_mir<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
+    let bcx = fcx.init(false, None).build();
     let mir = bcx.mir();
 
-    let mir_blocks = bcx.mir().all_basic_blocks();
+    let mir_blocks = mir.all_basic_blocks();
 
     // Analyze the temps to determine which must be lvalues
     // FIXME
     let lvalue_temps = bcx.with_block(|bcx| {
-      analyze::lvalue_temps(bcx, mir)
+      analyze::lvalue_temps(bcx, &mir)
     });
 
     // Allocate variable and temp allocas
@@ -108,10 +126,10 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: BlockAndBuilder<'bcx, 'tcx>) {
                                   TempRef::Operand(None)
                               })
                               .collect();
-    let args = arg_value_refs(&bcx, mir);
+    let args = arg_value_refs(&bcx, &mir);
 
     // Allocate a `Block` for every basic block
-    let block_bcxs: Vec<Block<'bcx,'tcx>> =
+    let block_bcxs: Vec<Block<'blk,'tcx>> =
         mir_blocks.iter()
                   .map(|&bb|{
                       // FIXME(#30941) this doesn't handle msvc-style exceptions
@@ -138,6 +156,8 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: BlockAndBuilder<'bcx, 'tcx>) {
     for &bb in &mir_blocks {
         mircx.trans_block(bb);
     }
+
+    fcx.cleanup();
 }
 
 /// Produce, for each argument, a `ValueRef` pointing at the
@@ -146,51 +166,75 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: BlockAndBuilder<'bcx, 'tcx>) {
 fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'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, dataptr, meta) = bcx.with_block(|bcx| {
-                   let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index));
-                   (lltemp, expr::get_dataptr(bcx, lltemp), expr::get_meta(bcx, lltemp))
-               });
-               bcx.store(lldata, dataptr);
-               bcx.store(llextra, meta);
-               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;
-               bcx.with_block(|bcx| {
-                   let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index));
-                   base::store_ty(bcx, llarg, lltemp, arg_ty);
-                   lltemp
-               })
-           };
-           LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))
-       })
-       .collect()
+    let mut idx = 0;
+    let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize;
+    mir.arg_decls.iter().enumerate().map(|(arg_index, arg_decl)| {
+        let arg_ty = bcx.monomorphize(&arg_decl.ty);
+        if arg_decl.spread {
+            // This argument (e.g. the last argument in the "rust-call" ABI)
+            // is a tuple that was spread at the ABI level and now we have
+            // to reconstruct it into a tuple local variable, from multiple
+            // individual LLVM function arguments.
+
+            let tupled_arg_tys = match arg_ty.sty {
+                ty::TyTuple(ref tys) => tys,
+                _ => unreachable!("spread argument isn't a tuple?!")
+            };
+
+            let lltemp = bcx.with_block(|bcx| {
+                base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index))
+            });
+            for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() {
+                let dst = bcx.struct_gep(lltemp, i);
+                let arg = &fcx.fn_ty.args[idx];
+                    idx += 1;
+                if common::type_is_fat_ptr(tcx, tupled_arg_ty) {
+                        // We pass fat pointers as two words, but inside the tuple
+                        // they are the two sub-fields of a single aggregate field.
+                    let meta = &fcx.fn_ty.args[idx];
+                    idx += 1;
+                    arg.store_fn_arg(bcx, &mut llarg_idx, get_dataptr(bcx, dst));
+                    meta.store_fn_arg(bcx, &mut llarg_idx, get_meta(bcx, dst));
+                } else {
+                    arg.store_fn_arg(bcx, &mut llarg_idx, dst);
+                }
+            }
+            return LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty));
+        }
+
+        let arg = &fcx.fn_ty.args[idx];
+        idx += 1;
+        let llval = if arg.is_indirect() {
+            // 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, llarg_idx as c_uint);
+            llarg_idx += 1;
+            llarg
+        } else {
+            let lltemp = bcx.with_block(|bcx| {
+                base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index))
+            });
+            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 meta = &fcx.fn_ty.args[idx];
+                idx += 1;
+                arg.store_fn_arg(bcx, &mut llarg_idx, get_dataptr(bcx, lltemp));
+                meta.store_fn_arg(bcx, &mut llarg_idx, get_meta(bcx, lltemp));
+            } else  {
+                // otherwise, arg is passed by value, so make a
+                // temporary and store it there
+                arg.store_fn_arg(bcx, &mut llarg_idx, lltemp);
+            }
+            lltemp
+        };
+        LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))
+    }).collect()
 }
 
 mod analyze;
diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs
index 5db5fc82c1f..6df4502fbc8 100644
--- a/src/librustc_trans/trans/mir/operand.rs
+++ b/src/librustc_trans/trans/mir/operand.rs
@@ -9,17 +9,18 @@
 // except according to those terms.
 
 use llvm::ValueRef;
-use rustc::middle::ty::{self, Ty};
+use rustc::middle::ty::Ty;
 use rustc::mir::repr as mir;
-use trans::adt;
 use trans::base;
 use trans::common::{self, Block, BlockAndBuilder};
 use trans::datum;
-use trans::Disr;
+use trans::value::Value;
 use trans::glue;
 
+use std::fmt;
+
+use super::lvalue::load_fat_ptr;
 use super::{MirContext, TempRef, drop};
-use super::lvalue::LvalueRef;
 
 /// The representation of a Rust value. The enum variant is in fact
 /// uniquely determined by the value's type, but is kept as a
@@ -53,42 +54,32 @@ pub struct OperandRef<'tcx> {
     pub ty: Ty<'tcx>
 }
 
-impl<'tcx> OperandRef<'tcx> {
-    /// Asserts that this operand refers to a scalar and returns
-    /// a reference to its value.
-    pub fn immediate(self) -> ValueRef {
-        match self.val {
-            OperandValue::Immediate(s) => s,
-            _ => unreachable!()
-        }
-    }
-
-    pub fn repr<'bcx>(self, bcx: &BlockAndBuilder<'bcx, 'tcx>) -> String {
+impl<'tcx> fmt::Debug for OperandRef<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self.val {
             OperandValue::Ref(r) => {
-                format!("OperandRef(Ref({}) @ {:?})",
-                        bcx.val_to_string(r), self.ty)
+                write!(f, "OperandRef(Ref({:?}) @ {:?})",
+                       Value(r), self.ty)
             }
             OperandValue::Immediate(i) => {
-                format!("OperandRef(Immediate({}) @ {:?})",
-                        bcx.val_to_string(i), self.ty)
+                write!(f, "OperandRef(Immediate({:?}) @ {:?})",
+                       Value(i), self.ty)
             }
             OperandValue::FatPtr(a, d) => {
-                format!("OperandRef(FatPtr({}, {}) @ {:?})",
-                        bcx.val_to_string(a),
-                        bcx.val_to_string(d),
-                        self.ty)
+                write!(f, "OperandRef(FatPtr({:?}, {:?}) @ {:?})",
+                       Value(a), Value(d), self.ty)
             }
         }
     }
+}
 
-    pub fn from_rvalue_datum(datum: datum::Datum<'tcx, datum::Rvalue>) -> OperandRef {
-        OperandRef {
-            ty: datum.ty,
-            val: match datum.kind.mode {
-                datum::RvalueMode::ByRef => OperandValue::Ref(datum.val),
-                datum::RvalueMode::ByValue => OperandValue::Immediate(datum.val),
-            }
+impl<'tcx> OperandRef<'tcx> {
+    /// Asserts that this operand refers to a scalar and returns
+    /// a reference to its value.
+    pub fn immediate(self) -> ValueRef {
+        match self.val {
+            OperandValue::Immediate(s) => s,
+            _ => unreachable!()
         }
     }
 }
@@ -100,18 +91,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                       ty: Ty<'tcx>)
                       -> OperandRef<'tcx>
     {
-        debug!("trans_load: {} @ {:?}", bcx.val_to_string(llval), ty);
+        debug!("trans_load: {:?} @ {:?}", Value(llval), ty);
 
         let val = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) {
             datum::ByValue => {
-                bcx.with_block(|bcx| {
-                    OperandValue::Immediate(base::load_ty(bcx, llval, ty))
-                })
+                OperandValue::Immediate(base::load_ty_builder(bcx, llval, ty))
             }
             datum::ByRef if common::type_is_fat_ptr(bcx.tcx(), ty) => {
-                let (lldata, llextra) = bcx.with_block(|bcx| {
-                    base::load_fat_ptr(bcx, llval, ty)
-                });
+                let (lldata, llextra) = load_fat_ptr(bcx, llval);
                 OperandValue::FatPtr(lldata, llextra)
             }
             datum::ByRef => OperandValue::Ref(llval)
@@ -164,7 +151,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                          lldest: ValueRef,
                          operand: OperandRef<'tcx>)
     {
-        debug!("store_operand: operand={}", operand.repr(bcx));
+        debug!("store_operand: operand={:?}", operand);
         bcx.with_block(|bcx| self.store_operand_direct(bcx, lldest, operand))
     }
 
@@ -187,48 +174,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         }
     }
 
-    pub fn trans_operand_untupled(&mut self,
-                                  bcx: &BlockAndBuilder<'bcx, 'tcx>,
-                                  operand: &mir::Operand<'tcx>)
-                                  -> Vec<OperandRef<'tcx>>
-    {
-        // FIXME: consider having some optimization to avoid tupling/untupling
-        // (and storing/loading in the case of immediates)
-
-        // avoid trans_operand for pointless copying
-        let lv = match *operand {
-            mir::Operand::Consume(ref lvalue) => self.trans_lvalue(bcx, lvalue),
-            mir::Operand::Constant(ref constant) => {
-                // FIXME: consider being less pessimized
-                if constant.ty.is_nil() {
-                    return vec![];
-                }
-
-                let ty = bcx.monomorphize(&constant.ty);
-                let lv = LvalueRef::alloca(bcx, ty, "__untuple_alloca");
-                let constant = self.trans_constant(bcx, constant);
-                self.store_operand(bcx, lv.llval, constant);
-                lv
-           }
-        };
-
-        let lv_ty = lv.ty.to_ty(bcx.tcx());
-        let result_types = match lv_ty.sty {
-            ty::TyTuple(ref tys) => tys,
-            _ => bcx.tcx().sess.span_bug(
-                self.mir.span,
-                &format!("bad final argument to \"rust-call\" fn {:?}", lv_ty))
-        };
-
-        let base_repr = adt::represent_type(bcx.ccx(), lv_ty);
-        let base = adt::MaybeSizedValue::sized(lv.llval);
-        result_types.iter().enumerate().map(|(n, &ty)| {
-            self.trans_load(bcx, bcx.with_block(|bcx| {
-                adt::trans_field_ptr(bcx, &base_repr, base, Disr(0), n)
-            }), ty)
-        }).collect()
-    }
-
     pub fn set_operand_dropped(&mut self,
                                bcx: &BlockAndBuilder<'bcx, 'tcx>,
                                operand: &mir::Operand<'tcx>) {
diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs
index ea80af14f1f..17c8aef244b 100644
--- a/src/librustc_trans/trans/mir/rvalue.rs
+++ b/src/librustc_trans/trans/mir/rvalue.rs
@@ -18,20 +18,21 @@ use rustc::mir::repr as mir;
 use trans::asm;
 use trans::base;
 use trans::callee::Callee;
-use trans::common::{self, BlockAndBuilder, Result};
+use trans::common::{self, C_uint, BlockAndBuilder, Result};
+use trans::datum::{Datum, Lvalue};
 use trans::debuginfo::DebugLoc;
 use trans::declare;
-use trans::expr;
 use trans::adt;
 use trans::machine;
 use trans::type_::Type;
 use trans::type_of;
 use trans::tvec;
+use trans::value::Value;
 use trans::Disr;
 
 use super::MirContext;
 use super::operand::{OperandRef, OperandValue};
-use super::lvalue::LvalueRef;
+use super::lvalue::{LvalueRef, get_dataptr, get_meta};
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn trans_rvalue(&mut self,
@@ -40,9 +41,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         rvalue: &mir::Rvalue<'tcx>)
                         -> BlockAndBuilder<'bcx, 'tcx>
     {
-        debug!("trans_rvalue(dest.llval={}, rvalue={:?})",
-               bcx.val_to_string(dest.llval),
-               rvalue);
+        debug!("trans_rvalue(dest.llval={:?}, rvalue={:?})",
+               Value(dest.llval), rvalue);
 
         match *rvalue {
            mir::Rvalue::Use(ref operand) => {
@@ -54,7 +54,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                bcx
            }
 
-            mir::Rvalue::Cast(mir::CastKind::Unsize, ref operand, cast_ty) => {
+            mir::Rvalue::Cast(mir::CastKind::Unsize, ref source, cast_ty) => {
                 if common::type_is_fat_ptr(bcx.tcx(), cast_ty) {
                     // into-coerce of a thin pointer to a fat pointer - just
                     // use the operand path.
@@ -67,7 +67,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 // this to be eliminated by MIR translation, but
                 // `CoerceUnsized` can be passed by a where-clause,
                 // so the (generic) MIR may not be able to expand it.
-                let operand = self.trans_operand(&bcx, operand);
+                let operand = self.trans_operand(&bcx, source);
                 bcx.with_block(|bcx| {
                     match operand.val {
                         OperandValue::FatPtr(..) => unreachable!(),
@@ -92,6 +92,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         }
                     }
                 });
+                self.set_operand_dropped(&bcx, source);
                 bcx
             }
 
@@ -99,8 +100,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 let tr_elem = self.trans_operand(&bcx, elem);
                 let count = ConstVal::Integral(ConstInt::Usize(count.value));
                 let size = self.trans_constval(&bcx, &count, bcx.tcx().types.usize).immediate();
+                let base = get_dataptr(&bcx, dest.llval);
                 let bcx = bcx.map_block(|block| {
-                    let base = expr::get_dataptr(block, dest.llval);
                     tvec::iter_vec_raw(block, base, tr_elem.ty, size, |block, llslot, _| {
                         self.store_operand_direct(block, llslot, tr_elem);
                         block
@@ -123,15 +124,39 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                             // Do not generate stores and GEPis for zero-sized fields.
                             if !common::type_is_zero_size(bcx.ccx(), op.ty) {
                                 let val = adt::MaybeSizedValue::sized(dest.llval);
-                                let lldest_i = bcx.with_block(|bcx| {
-                                    adt::trans_field_ptr(bcx, &repr, val, disr, i)
-                                });
+                                let lldest_i = adt::trans_field_ptr_builder(&bcx, &repr,
+                                                                            val, disr, i);
                                 self.store_operand(&bcx, lldest_i, op);
-                                self.set_operand_dropped(&bcx, operand);
                             }
+                            self.set_operand_dropped(&bcx, operand);
                         }
                     },
                     _ => {
+                        // FIXME Shouldn't need to manually trigger closure instantiations.
+                        if let mir::AggregateKind::Closure(def_id, substs) = *kind {
+                            use rustc_front::hir;
+                            use syntax::ast::DUMMY_NODE_ID;
+                            use syntax::codemap::DUMMY_SP;
+                            use syntax::ptr::P;
+                            use trans::closure;
+
+                            closure::trans_closure_expr(closure::Dest::Ignore(bcx.ccx()),
+                                                        &hir::FnDecl {
+                                                            inputs: P::new(),
+                                                            output: hir::NoReturn(DUMMY_SP),
+                                                            variadic: false
+                                                        },
+                                                        &hir::Block {
+                                                            stmts: P::new(),
+                                                            expr: None,
+                                                            id: DUMMY_NODE_ID,
+                                                            rules: hir::DefaultBlock,
+                                                            span: DUMMY_SP
+                                                        },
+                                                        DUMMY_NODE_ID, def_id,
+                                                        &bcx.monomorphize(substs));
+                        }
+
                         for (i, operand) in operands.iter().enumerate() {
                             let op = self.trans_operand(&bcx, operand);
                             // Do not generate stores and GEPis for zero-sized fields.
@@ -141,8 +166,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                 // not be structs but arrays.
                                 let dest = bcx.gepi(dest.llval, &[0, i]);
                                 self.store_operand(&bcx, dest, op);
-                                self.set_operand_dropped(&bcx, operand);
                             }
+                            self.set_operand_dropped(&bcx, operand);
                         }
                     }
                 }
@@ -152,26 +177,42 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             mir::Rvalue::Slice { ref input, from_start, from_end } => {
                 let ccx = bcx.ccx();
                 let input = self.trans_lvalue(&bcx, input);
-                let (llbase, lllen) = bcx.with_block(|bcx| {
-                    tvec::get_base_and_len(bcx,
-                                           input.llval,
-                                           input.ty.to_ty(bcx.tcx()))
-                });
-                let llbase1 = bcx.gepi(llbase, &[from_start]);
-                let adj = common::C_uint(ccx, from_start + from_end);
+                let ty = input.ty.to_ty(bcx.tcx());
+                let (llbase1, lllen) = match ty.sty {
+                    ty::TyArray(_, n) => {
+                        (bcx.gepi(input.llval, &[0, from_start]), C_uint(ccx, n))
+                    }
+                    ty::TySlice(_) | ty::TyStr => {
+                        (bcx.gepi(input.llval, &[from_start]), input.llextra)
+                    }
+                    _ => unreachable!("cannot slice {}", ty)
+                };
+                let adj = C_uint(ccx, from_start + from_end);
                 let lllen1 = bcx.sub(lllen, adj);
-                let (lladdrdest, llmetadest) = bcx.with_block(|bcx| {
-                    (expr::get_dataptr(bcx, dest.llval), expr::get_meta(bcx, dest.llval))
-                });
-                bcx.store(llbase1, lladdrdest);
-                bcx.store(lllen1, llmetadest);
+                bcx.store(llbase1, get_dataptr(&bcx, dest.llval));
+                bcx.store(lllen1, get_meta(&bcx, dest.llval));
                 bcx
             }
 
-            mir::Rvalue::InlineAsm(ref inline_asm) => {
-                bcx.map_block(|bcx| {
-                    asm::trans_inline_asm(bcx, inline_asm)
-                })
+            mir::Rvalue::InlineAsm { ref asm, ref outputs, ref inputs } => {
+                let outputs = outputs.iter().map(|output| {
+                    let lvalue = self.trans_lvalue(&bcx, output);
+                    Datum::new(lvalue.llval, lvalue.ty.to_ty(bcx.tcx()),
+                               Lvalue::new("out"))
+                }).collect();
+
+                let input_vals = inputs.iter().map(|input| {
+                    self.trans_operand(&bcx, input).immediate()
+                }).collect();
+
+                bcx.with_block(|bcx| {
+                    asm::trans_inline_asm(bcx, asm, outputs, input_vals);
+                });
+
+                for input in inputs {
+                    self.set_operand_dropped(&bcx, input);
+                }
+                bcx
             }
 
             _ => {
@@ -191,9 +232,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);
 
         match *rvalue {
-            mir::Rvalue::Cast(ref kind, ref operand, cast_ty) => {
-                let operand = self.trans_operand(&bcx, operand);
-                debug!("cast operand is {}", operand.repr(&bcx));
+            mir::Rvalue::Cast(ref kind, ref source, cast_ty) => {
+                let operand = self.trans_operand(&bcx, source);
+                debug!("cast operand is {:?}", operand);
                 let cast_ty = bcx.monomorphize(&cast_ty);
 
                 let val = match *kind {
@@ -201,7 +242,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         match operand.ty.sty {
                             ty::TyFnDef(def_id, substs, _) => {
                                 OperandValue::Immediate(
-                                    Callee::def(bcx.ccx(), def_id, substs, operand.ty)
+                                    Callee::def(bcx.ccx(), def_id, substs)
                                         .reify(bcx.ccx()).val)
                             }
                             _ => {
@@ -225,6 +266,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                 // example,
                                 //   &'a fmt::Debug+Send => &'a fmt::Debug,
                                 // and is a no-op at the LLVM level
+                                self.set_operand_dropped(&bcx, source);
                                 operand.val
                             }
                             OperandValue::Immediate(lldata) => {
@@ -233,12 +275,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                     base::unsize_thin_ptr(bcx, lldata,
                                                           operand.ty, cast_ty)
                                 });
+                                self.set_operand_dropped(&bcx, source);
                                 OperandValue::FatPtr(lldata, llextra)
                             }
                             OperandValue::Ref(_) => {
                                 bcx.sess().bug(
-                                    &format!("by-ref operand {} in trans_rvalue_operand",
-                                             operand.repr(&bcx)));
+                                    &format!("by-ref operand {:?} in trans_rvalue_operand",
+                                             operand));
                             }
                         }
                     }
@@ -246,17 +289,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         debug_assert!(common::type_is_immediate(bcx.ccx(), cast_ty));
                         let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast");
                         let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
-                        let ll_t_in = type_of::arg_type_of(bcx.ccx(), operand.ty);
-                        let ll_t_out = type_of::arg_type_of(bcx.ccx(), cast_ty);
-                        let (llval, ll_t_in, signed) = if let CastTy::Int(IntTy::CEnum) = r_t_in {
+                        let ll_t_in = type_of::immediate_type_of(bcx.ccx(), operand.ty);
+                        let ll_t_out = type_of::immediate_type_of(bcx.ccx(), cast_ty);
+                        let llval = operand.immediate();
+                        let signed = if let CastTy::Int(IntTy::CEnum) = r_t_in {
                             let repr = adt::represent_type(bcx.ccx(), operand.ty);
-                            let llval = operand.immediate();
-                            let discr = bcx.with_block(|bcx| {
-                                adt::trans_get_discr(bcx, &repr, llval, None, true)
-                            });
-                            (discr, common::val_ty(discr), adt::is_discr_signed(&repr))
+                            adt::is_discr_signed(&repr)
                         } else {
-                            (operand.immediate(), ll_t_in, operand.ty.is_signed())
+                            operand.ty.is_signed()
                         };
 
                         let newval = match (r_t_in, r_t_out) {
@@ -308,8 +348,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         OperandValue::Immediate(newval)
                     }
                     mir::CastKind::Misc => { // Casts from a fat-ptr.
-                        let ll_cast_ty = type_of::arg_type_of(bcx.ccx(), cast_ty);
-                        let ll_from_ty = type_of::arg_type_of(bcx.ccx(), operand.ty);
+                        let ll_cast_ty = type_of::immediate_type_of(bcx.ccx(), cast_ty);
+                        let ll_from_ty = type_of::immediate_type_of(bcx.ccx(), operand.ty);
                         if let OperandValue::FatPtr(data_ptr, meta_ptr) = operand.val {
                             if common::type_is_fat_ptr(bcx.tcx(), cast_ty) {
                                 let ll_cft = ll_cast_ty.field_types();
@@ -423,7 +463,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 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 llalign = C_uint(bcx.ccx(), align);
                 let llty_ptr = llty.ptr_to();
                 let box_ty = bcx.tcx().mk_box(content_ty);
                 let mut llval = None;
@@ -448,7 +488,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             mir::Rvalue::Repeat(..) |
             mir::Rvalue::Aggregate(..) |
             mir::Rvalue::Slice { .. } |
-            mir::Rvalue::InlineAsm(..) => {
+            mir::Rvalue::InlineAsm { .. } => {
                 bcx.tcx().sess.bug(&format!("cannot generate operand from rvalue {:?}", rvalue));
             }
         }
@@ -511,15 +551,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 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);
+                    let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty);
                     if input_ty == tcx.types.f32 {
                         let lllhs = bcx.fpext(lhs, f64t);
                         let llrhs = bcx.fpext(rhs, f64t);
-                        let llres = bcx.call(llfn, &[lllhs, llrhs], None, None);
+                        let llres = bcx.call(llfn, &[lllhs, llrhs], None);
                         bcx.fptrunc(llres, Type::f32(bcx.ccx()))
                     } else {
-                        bcx.call(llfn, &[lhs, rhs], None, None)
+                        bcx.call(llfn, &[lhs, rhs], None)
                     }
                 } else {
                     bcx.frem(lhs, rhs)
@@ -573,7 +612,7 @@ pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool {
         mir::Rvalue::Repeat(..) |
         mir::Rvalue::Aggregate(..) |
         mir::Rvalue::Slice { .. } |
-        mir::Rvalue::InlineAsm(..) =>
+        mir::Rvalue::InlineAsm { .. } =>
             false,
     }
 
diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs
index 1b8bab7a4ee..c5ab0d4e744 100644
--- a/src/librustc_trans/trans/mod.rs
+++ b/src/librustc_trans/trans/mod.rs
@@ -19,6 +19,7 @@ pub use self::disr::Disr;
 #[macro_use]
 mod macros;
 
+mod abi;
 mod adt;
 mod asm;
 mod assert_dep_graph;
@@ -27,7 +28,6 @@ mod base;
 mod basic_block;
 mod build;
 mod builder;
-mod cabi;
 mod cabi_aarch64;
 mod cabi_arm;
 mod cabi_asmjs;
@@ -49,11 +49,9 @@ mod debuginfo;
 mod declare;
 mod disr;
 mod expr;
-mod foreign;
 mod glue;
 mod inline;
 mod intrinsic;
-mod llrepr;
 mod machine;
 mod _match;
 mod meth;
diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs
index c6119416e47..2e75439ffc3 100644
--- a/src/librustc_trans/trans/monomorphize.rs
+++ b/src/librustc_trans/trans/monomorphize.rs
@@ -17,38 +17,35 @@ use middle::subst;
 use middle::subst::{Subst, Substs};
 use middle::ty::fold::{TypeFolder, TypeFoldable};
 use trans::attributes;
-use trans::base::{trans_enum_variant, push_ctxt, get_item_val};
+use trans::base::{push_ctxt};
 use trans::base::trans_fn;
 use trans::base;
 use trans::common::*;
 use trans::declare;
-use trans::foreign;
 use middle::ty::{self, Ty, TyCtxt};
 use trans::Disr;
 use rustc::front::map as hir_map;
+use rustc::util::ppaux;
 
 use rustc_front::hir;
 
-use syntax::abi::Abi;
-use syntax::ast;
 use syntax::attr;
 use syntax::errors;
+
+use std::fmt;
 use std::hash::{Hasher, Hash, SipHasher};
 
 pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 fn_id: DefId,
                                 psubsts: &'tcx subst::Substs<'tcx>)
-                                -> (ValueRef, Ty<'tcx>, bool) {
+                                -> (ValueRef, Ty<'tcx>) {
     debug!("monomorphic_fn(fn_id={:?}, real_substs={:?})", fn_id, psubsts);
 
     assert!(!psubsts.types.needs_infer() && !psubsts.types.has_param_types());
 
-    // we can only monomorphize things in this crate (or inlined into it)
-    let fn_node_id = ccx.tcx().map.as_local_node_id(fn_id).unwrap();
-
     let _icx = push_ctxt("monomorphic_fn");
 
-    let hash_id = MonoId {
+    let instance = Instance {
         def: fn_id,
         params: &psubsts.types
     };
@@ -59,41 +56,15 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     let mono_ty = apply_param_substs(ccx.tcx(), psubsts, &item_ty);
     debug!("mono_ty = {:?} (post-substitution)", mono_ty);
 
-    match ccx.monomorphized().borrow().get(&hash_id) {
+    match ccx.instances().borrow().get(&instance) {
         Some(&val) => {
-            debug!("leaving monomorphic fn {}",
-            ccx.tcx().item_path_str(fn_id));
-            return (val, mono_ty, false);
+            debug!("leaving monomorphic fn {:?}", instance);
+            return (val, mono_ty);
         }
         None => ()
     }
 
-    debug!("monomorphic_fn(\
-            fn_id={:?}, \
-            psubsts={:?}, \
-            hash_id={:?})",
-           fn_id,
-           psubsts,
-           hash_id);
-
-
-    let map_node = errors::expect(
-        ccx.sess().diagnostic(),
-        ccx.tcx().map.find(fn_node_id),
-        || {
-            format!("while monomorphizing {:?}, couldn't find it in \
-                     the item map (may have attempted to monomorphize \
-                     an item defined in a different crate?)",
-                    fn_id)
-        });
-
-    if let hir_map::NodeForeignItem(_) = map_node {
-        let abi = ccx.tcx().map.get_foreign_abi(fn_node_id);
-        if abi != Abi::RustIntrinsic && abi != Abi::PlatformIntrinsic {
-            // Foreign externs don't have to be monomorphized.
-            return (get_item_val(ccx, fn_node_id), mono_ty, true);
-        }
-    }
+    debug!("monomorphic_fn({:?})", instance);
 
     ccx.stats().n_monos.set(ccx.stats().n_monos.get() + 1);
 
@@ -110,8 +81,13 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         // recursively more than thirty times can probably safely be assumed
         // to be causing an infinite expansion.
         if depth > ccx.sess().recursion_limit.get() {
-            ccx.sess().span_fatal(ccx.tcx().map.span(fn_node_id),
-                "reached the recursion limit during monomorphization");
+            let error = format!("reached the recursion limit while instantiating `{}`",
+                                instance);
+            if let Some(id) = ccx.tcx().map.as_local_node_id(fn_id) {
+                ccx.sess().span_fatal(ccx.tcx().map.span(id), &error);
+            } else {
+                ccx.sess().fatal(&error);
+            }
         }
 
         monomorphizing.insert(fn_id, depth + 1);
@@ -120,173 +96,112 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     let hash;
     let s = {
         let mut state = SipHasher::new();
-        hash_id.hash(&mut state);
+        instance.hash(&mut state);
         mono_ty.hash(&mut state);
 
         hash = format!("h{}", state.finish());
-        let path = ccx.tcx().map.def_path_from_id(fn_node_id);
+        let path = ccx.tcx().map.def_path(fn_id);
         exported_name(path, &hash[..])
     };
 
     debug!("monomorphize_fn mangled to {}", s);
+    assert!(declare::get_defined_value(ccx, &s).is_none());
 
-    // This shouldn't need to option dance.
-    let mut hash_id = Some(hash_id);
-    let mut mk_lldecl = |abi: Abi| {
-        let lldecl = if abi != Abi::Rust {
-            foreign::decl_rust_fn_with_foreign_abi(ccx, mono_ty, &s)
-        } else {
-            // FIXME(nagisa): perhaps needs a more fine grained selection? See
-            // setup_lldecl below.
-            declare::define_internal_rust_fn(ccx, &s, mono_ty)
-        };
+    // FIXME(nagisa): perhaps needs a more fine grained selection?
+    let lldecl = declare::define_internal_fn(ccx, &s, mono_ty);
+    // FIXME(eddyb) Doubt all extern fn should allow unwinding.
+    attributes::unwind(lldecl, true);
 
-        ccx.monomorphized().borrow_mut().insert(hash_id.take().unwrap(), lldecl);
-        lldecl
-    };
-    let setup_lldecl = |lldecl, attrs: &[ast::Attribute]| {
-        base::update_linkage(ccx, lldecl, None, base::OriginalTranslation);
-        attributes::from_fn_attrs(ccx, attrs, lldecl);
-
-        let is_first = !ccx.available_monomorphizations().borrow().contains(&s);
-        if is_first {
-            ccx.available_monomorphizations().borrow_mut().insert(s.clone());
-        }
+    ccx.instances().borrow_mut().insert(instance, lldecl);
 
-        let trans_everywhere = attr::requests_inline(attrs);
-        if trans_everywhere && !is_first {
-            llvm::SetLinkage(lldecl, llvm::AvailableExternallyLinkage);
-        }
-
-        // If `true`, then `lldecl` should be given a function body.
-        // Otherwise, it should be left as a declaration of an external
-        // function, with no definition in the current compilation unit.
-        trans_everywhere || is_first
-    };
+    // we can only monomorphize things in this crate (or inlined into it)
+    let fn_node_id = ccx.tcx().map.as_local_node_id(fn_id).unwrap();
+    let map_node = errors::expect(
+        ccx.sess().diagnostic(),
+        ccx.tcx().map.find(fn_node_id),
+        || {
+            format!("while instantiating `{}`, couldn't find it in \
+                     the item map (may have attempted to monomorphize \
+                     an item defined in a different crate?)",
+                    instance)
+        });
+    match map_node {
+        hir_map::NodeItem(&hir::Item {
+            ref attrs, node: hir::ItemFn(ref decl, _, _, _, _, ref body), ..
+        }) |
+        hir_map::NodeTraitItem(&hir::TraitItem {
+            ref attrs, node: hir::MethodTraitItem(
+                hir::MethodSig { ref decl, .. }, Some(ref body)), ..
+        }) |
+        hir_map::NodeImplItem(&hir::ImplItem {
+            ref attrs, node: hir::ImplItemKind::Method(
+                hir::MethodSig { ref decl, .. }, ref body), ..
+        }) => {
+            base::update_linkage(ccx, lldecl, None, base::OriginalTranslation);
+            attributes::from_fn_attrs(ccx, attrs, lldecl);
+
+            let is_first = !ccx.available_monomorphizations().borrow().contains(&s);
+            if is_first {
+                ccx.available_monomorphizations().borrow_mut().insert(s.clone());
+            }
 
-    let lldecl = match map_node {
-        hir_map::NodeItem(i) => {
-            match *i {
-              hir::Item {
-                  node: hir::ItemFn(ref decl, _, _, abi, _, ref body),
-                  ..
-              } => {
-                  let d = mk_lldecl(abi);
-                  let needs_body = setup_lldecl(d, &i.attrs);
-                  if needs_body {
-                      if abi != Abi::Rust {
-                          foreign::trans_rust_fn_with_foreign_abi(
-                              ccx, &decl, &body, &[], d, psubsts, fn_node_id,
-                              Some(&hash[..]));
-                      } else {
-                          trans_fn(ccx,
-                                   &decl,
-                                   &body,
-                                   d,
-                                   psubsts,
-                                   fn_node_id,
-                                   &i.attrs);
-                      }
-                  }
-
-                  d
-              }
-              _ => {
-                ccx.sess().bug("Can't monomorphize this kind of item")
-              }
+            let trans_everywhere = attr::requests_inline(attrs);
+            if trans_everywhere && !is_first {
+                llvm::SetLinkage(lldecl, llvm::AvailableExternallyLinkage);
             }
-        }
-        hir_map::NodeVariant(v) => {
-            let variant = inlined_variant_def(ccx, fn_node_id);
-            assert_eq!(v.node.name, variant.name);
-            let d = mk_lldecl(Abi::Rust);
-            attributes::inline(d, attributes::InlineAttr::Hint);
-            trans_enum_variant(ccx, fn_node_id, Disr::from(variant.disr_val), psubsts, d);
-            d
-        }
-        hir_map::NodeImplItem(impl_item) => {
-            match impl_item.node {
-                hir::ImplItemKind::Method(ref sig, ref body) => {
-                    let d = mk_lldecl(Abi::Rust);
-                    let needs_body = setup_lldecl(d, &impl_item.attrs);
-                    if needs_body {
-                        trans_fn(ccx,
-                                 &sig.decl,
-                                 body,
-                                 d,
-                                 psubsts,
-                                 impl_item.id,
-                                 &impl_item.attrs);
-                    }
-                    d
-                }
-                _ => {
-                    ccx.sess().bug(&format!("can't monomorphize a {:?}",
-                                           map_node))
-                }
+
+            if trans_everywhere || is_first {
+                trans_fn(ccx, decl, body, lldecl, psubsts, fn_node_id);
             }
         }
-        hir_map::NodeTraitItem(trait_item) => {
-            match trait_item.node {
-                hir::MethodTraitItem(ref sig, Some(ref body)) => {
-                    let d = mk_lldecl(Abi::Rust);
-                    let needs_body = setup_lldecl(d, &trait_item.attrs);
-                    if needs_body {
-                        trans_fn(ccx,
-                                 &sig.decl,
-                                 body,
-                                 d,
-                                 psubsts,
-                                 trait_item.id,
-                                 &trait_item.attrs);
-                    }
-                    d
-                }
-                _ => {
-                    ccx.sess().bug(&format!("can't monomorphize a {:?}",
-                                           map_node))
+
+        hir_map::NodeVariant(_) | hir_map::NodeStructCtor(_) => {
+            let disr = match map_node {
+                hir_map::NodeVariant(_) => {
+                    Disr::from(inlined_variant_def(ccx, fn_node_id).disr_val)
                 }
-            }
-        }
-        hir_map::NodeStructCtor(struct_def) => {
-            let d = mk_lldecl(Abi::Rust);
-            attributes::inline(d, attributes::InlineAttr::Hint);
-            if struct_def.is_struct() {
-                panic!("ast-mapped struct didn't have a ctor id")
-            }
-            base::trans_tuple_struct(ccx,
-                                     struct_def.id(),
-                                     psubsts,
-                                     d);
-            d
+                hir_map::NodeStructCtor(_) => Disr(0),
+                _ => unreachable!()
+            };
+            attributes::inline(lldecl, attributes::InlineAttr::Hint);
+            base::trans_ctor_shim(ccx, fn_node_id, disr, psubsts, lldecl);
         }
 
-        // Ugh -- but this ensures any new variants won't be forgotten
-        hir_map::NodeForeignItem(..) |
-        hir_map::NodeLifetime(..) |
-        hir_map::NodeTyParam(..) |
-        hir_map::NodeExpr(..) |
-        hir_map::NodeStmt(..) |
-        hir_map::NodeBlock(..) |
-        hir_map::NodePat(..) |
-        hir_map::NodeLocal(..) => {
-            ccx.sess().bug(&format!("can't monomorphize a {:?}",
-                                   map_node))
-        }
+        _ => unreachable!("can't monomorphize a {:?}", map_node)
     };
 
     ccx.monomorphizing().borrow_mut().insert(fn_id, depth);
 
     debug!("leaving monomorphic fn {}", ccx.tcx().item_path_str(fn_id));
-    (lldecl, mono_ty, true)
+    (lldecl, mono_ty)
 }
 
-#[derive(PartialEq, Eq, Hash, Debug)]
-pub struct MonoId<'tcx> {
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct Instance<'tcx> {
     pub def: DefId,
     pub params: &'tcx subst::VecPerParamSpace<Ty<'tcx>>
 }
 
+impl<'tcx> fmt::Display for Instance<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let substs = Substs {
+            types: self.params.clone(),
+            regions: subst::ErasedRegions
+        };
+        ppaux::parameterized(f, &substs, self.def, ppaux::Ns::Value, &[],
+                             |tcx| tcx.lookup_item_type(self.def).generics)
+    }
+}
+
+impl<'tcx> Instance<'tcx> {
+    pub fn mono(tcx: &TyCtxt<'tcx>, def_id: DefId) -> Instance<'tcx> {
+        Instance {
+            def: def_id,
+            params: &tcx.mk_substs(Substs::trans_empty()).types
+        }
+    }
+}
+
 /// Monomorphizes a type from the AST by first applying the in-scope
 /// substitutions and then normalizing any associated types.
 pub fn apply_param_substs<'tcx,T>(tcx: &TyCtxt<'tcx>,
diff --git a/src/librustc_trans/trans/tvec.rs b/src/librustc_trans/trans/tvec.rs
index d6573e96b02..7a0ca86f5a2 100644
--- a/src/librustc_trans/trans/tvec.rs
+++ b/src/librustc_trans/trans/tvec.rs
@@ -26,6 +26,7 @@ use trans::expr;
 use trans::machine::llsize_of_alloc;
 use trans::type_::Type;
 use trans::type_of;
+use trans::value::Value;
 use middle::ty::{self, Ty};
 
 use rustc_front::hir;
@@ -33,20 +34,12 @@ use rustc_front::hir;
 use syntax::ast;
 use syntax::parse::token::InternedString;
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
 struct VecTypes<'tcx> {
     unit_ty: Ty<'tcx>,
     llunit_ty: Type
 }
 
-impl<'tcx> VecTypes<'tcx> {
-    pub fn to_string<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> String {
-        format!("VecTypes {{unit_ty={}, llunit_ty={}}}",
-                self.unit_ty,
-                ccx.tn().type_to_string(self.llunit_ty))
-    }
-}
-
 pub fn trans_fixed_vstore<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                       expr: &hir::Expr,
                                       dest: expr::Dest)
@@ -58,8 +51,7 @@ pub fn trans_fixed_vstore<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     // to store the array of the suitable size, so all we have to do is
     // generate the content.
 
-    debug!("trans_fixed_vstore(expr={:?}, dest={})",
-           expr, dest.to_string(bcx.ccx()));
+    debug!("trans_fixed_vstore(expr={:?}, dest={:?})", expr, dest);
 
     let vt = vec_types_from_expr(bcx, expr);
 
@@ -82,7 +74,6 @@ pub fn trans_slice_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                    content_expr: &hir::Expr)
                                    -> DatumBlock<'blk, 'tcx, Expr> {
     let fcx = bcx.fcx;
-    let ccx = fcx.ccx;
     let mut bcx = bcx;
 
     debug!("trans_slice_vec(slice_expr={:?})",
@@ -105,7 +96,7 @@ pub fn trans_slice_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     // Handle the &[...] case:
     let vt = vec_types_from_expr(bcx, content_expr);
     let count = elements_required(bcx, content_expr);
-    debug!("    vt={}, count={}", vt.to_string(ccx), count);
+    debug!("    vt={:?}, count={}", vt, count);
 
     let fixed_ty = bcx.tcx().mk_array(vt.unit_ty, count);
 
@@ -144,9 +135,7 @@ pub fn trans_lit_str<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                  str_lit: InternedString,
                                  dest: Dest)
                                  -> Block<'blk, 'tcx> {
-    debug!("trans_lit_str(lit_expr={:?}, dest={})",
-           lit_expr,
-           dest.to_string(bcx.ccx()));
+    debug!("trans_lit_str(lit_expr={:?}, dest={:?})", lit_expr, dest);
 
     match dest {
         Ignore => bcx,
@@ -172,10 +161,8 @@ fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let fcx = bcx.fcx;
     let mut bcx = bcx;
 
-    debug!("write_content(vt={}, dest={}, vstore_expr={:?})",
-           vt.to_string(bcx.ccx()),
-           dest.to_string(bcx.ccx()),
-           vstore_expr);
+    debug!("write_content(vt={:?}, dest={:?}, vstore_expr={:?})",
+           vt, dest, vstore_expr);
 
     match content_expr.node {
         hir::ExprLit(ref lit) => {
@@ -187,11 +174,9 @@ fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                             let bytes = s.len();
                             let llbytes = C_uint(bcx.ccx(), bytes);
                             let llcstr = C_cstr(bcx.ccx(), (*s).clone(), false);
-                            base::call_memcpy(bcx,
-                                              lldest,
-                                              llcstr,
-                                              llbytes,
-                                              1);
+                            if !bcx.unreachable.get() {
+                                base::call_memcpy(&B(bcx), lldest, llcstr, llbytes, 1);
+                            }
                             return bcx;
                         }
                     }
@@ -214,8 +199,8 @@ fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                     let temp_scope = fcx.push_custom_cleanup_scope();
                     for (i, element) in elements.iter().enumerate() {
                         let lleltptr = GEPi(bcx, lldest, &[i]);
-                        debug!("writing index {} with lleltptr={}",
-                               i, bcx.val_to_string(lleltptr));
+                        debug!("writing index {} with lleltptr={:?}",
+                               i, Value(lleltptr));
                         bcx = expr::trans_into(bcx, &element,
                                                SaveIn(lleltptr));
                         let scope = cleanup::CustomScope(temp_scope);
diff --git a/src/librustc_trans/trans/type_.rs b/src/librustc_trans/trans/type_.rs
index 17300f356c4..57bd0ba8158 100644
--- a/src/librustc_trans/trans/type_.rs
+++ b/src/librustc_trans/trans/type_.rs
@@ -11,7 +11,7 @@
 #![allow(non_upper_case_globals)]
 
 use llvm;
-use llvm::{TypeRef, Bool, False, True, TypeKind, ValueRef};
+use llvm::{TypeRef, Bool, False, True, TypeKind};
 use llvm::{Float, Double, X86_FP80, PPC_FP128, FP128};
 
 use trans::context::CrateContext;
@@ -20,18 +20,27 @@ use util::nodemap::FnvHashMap;
 use syntax::ast;
 
 use std::ffi::CString;
+use std::fmt;
 use std::mem;
 use std::ptr;
 use std::cell::RefCell;
 
 use libc::c_uint;
 
-#[derive(Clone, Copy, PartialEq, Debug)]
+#[derive(Clone, Copy, PartialEq)]
 #[repr(C)]
 pub struct Type {
     rf: TypeRef
 }
 
+impl fmt::Debug for Type {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.write_str(&llvm::build_string(|s| unsafe {
+            llvm::LLVMWriteTypeToString(self.to_ref(), s);
+        }).expect("non-UTF8 type description from LLVM"))
+    }
+}
+
 macro_rules! ty {
     ($e:expr) => ( Type::from_ref(unsafe { $e }))
 }
@@ -50,12 +59,6 @@ impl Type {
         self.rf
     }
 
-    pub fn to_string(self: Type) -> String {
-        llvm::build_string(|s| unsafe {
-            llvm::LLVMWriteTypeToString(self.to_ref(), s);
-        }).expect("non-UTF8 type description from LLVM")
-    }
-
     pub fn to_ref_slice(slice: &[Type]) -> &[TypeRef] {
         unsafe { mem::transmute(slice) }
     }
@@ -180,10 +183,6 @@ impl Type {
         Type::struct_(ccx, &[], false)
     }
 
-    pub fn glue_fn(ccx: &CrateContext, t: Type) -> Type {
-        Type::func(&[t], &Type::void(ccx))
-    }
-
     pub fn array(ty: &Type, len: u64) -> Type {
         ty!(llvm::LLVMRustArrayType(ty.to_ref(), len))
     }
@@ -203,7 +202,7 @@ impl Type {
     }
 
     pub fn vtable_ptr(ccx: &CrateContext) -> Type {
-        Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to().ptr_to()
+        Type::func(&[Type::i8p(ccx)], &Type::void(ccx)).ptr_to().ptr_to()
     }
 
     pub fn kind(&self) -> TypeKind {
@@ -301,7 +300,6 @@ impl Type {
     }
 }
 
-
 /* Memory-managed object interface to type handles. */
 
 pub struct TypeNames {
@@ -323,19 +321,4 @@ impl TypeNames {
     pub fn find_type(&self, s: &str) -> Option<Type> {
         self.named_types.borrow().get(s).map(|x| Type::from_ref(*x))
     }
-
-    pub fn type_to_string(&self, ty: Type) -> String {
-        ty.to_string()
-    }
-
-    pub fn types_to_str(&self, tys: &[Type]) -> String {
-        let strs: Vec<String> = tys.iter().map(|t| self.type_to_string(*t)).collect();
-        format!("[{}]", strs.join(","))
-    }
-
-    pub fn val_to_string(&self, val: ValueRef) -> String {
-        llvm::build_string(|s| unsafe {
-                llvm::LLVMWriteValueToString(val, s);
-            }).expect("nun-UTF8 value description from LLVM")
-    }
 }
diff --git a/src/librustc_trans/trans/type_of.rs b/src/librustc_trans/trans/type_of.rs
index b78bf9bfc3f..7d5218d84da 100644
--- a/src/librustc_trans/trans/type_of.rs
+++ b/src/librustc_trans/trans/type_of.rs
@@ -13,15 +13,14 @@
 use middle::def_id::DefId;
 use middle::infer;
 use middle::subst;
+use trans::abi::FnType;
 use trans::adt;
 use trans::common::*;
-use trans::foreign;
 use trans::machine;
 use middle::ty::{self, Ty, TypeFoldable};
 
 use trans::type_::Type;
 
-use syntax::abi::Abi;
 use syntax::ast;
 
 // LLVM doesn't like objects that are too big. Issue #17913
@@ -36,120 +35,6 @@ fn ensure_array_fits_in_address_space<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     }
 }
 
-pub fn arg_is_indirect<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                 arg_ty: Ty<'tcx>) -> bool {
-    !type_is_immediate(ccx, arg_ty) && !type_is_fat_ptr(ccx.tcx(), arg_ty)
-}
-
-pub fn return_uses_outptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                    ty: Ty<'tcx>) -> bool {
-    arg_is_indirect(ccx, ty)
-}
-
-pub fn type_of_explicit_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                      arg_ty: Ty<'tcx>) -> Type {
-    let llty = arg_type_of(ccx, arg_ty);
-    if arg_is_indirect(ccx, arg_ty) {
-        llty.ptr_to()
-    } else {
-        llty
-    }
-}
-
-/// Yields the types of the "real" arguments for a function using the `RustCall`
-/// ABI by untupling the arguments of the function.
-pub fn untuple_arguments<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                   inputs: &[Ty<'tcx>])
-                                   -> Vec<Ty<'tcx>> {
-    if inputs.is_empty() {
-        return Vec::new()
-    }
-
-    let mut result = Vec::new();
-    for (i, &arg_prior_to_tuple) in inputs.iter().enumerate() {
-        if i < inputs.len() - 1 {
-            result.push(arg_prior_to_tuple);
-        }
-    }
-
-    match inputs[inputs.len() - 1].sty {
-        ty::TyTuple(ref tupled_arguments) => {
-            debug!("untuple_arguments(): untupling arguments");
-            for &tupled_argument in tupled_arguments {
-                result.push(tupled_argument);
-            }
-        }
-        _ => {
-            ccx.tcx().sess.bug("argument to function with \"rust-call\" ABI \
-                                is neither a tuple nor unit")
-        }
-    }
-
-    result
-}
-
-pub fn type_of_rust_fn<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
-                                 llenvironment_type: Option<Type>,
-                                 sig: &ty::FnSig<'tcx>,
-                                 abi: Abi)
-                                 -> Type
-{
-    debug!("type_of_rust_fn(sig={:?},abi={:?})",
-           sig,
-           abi);
-
-    assert!(!sig.variadic); // rust fns are never variadic
-
-    let mut atys: Vec<Type> = Vec::new();
-
-    // First, munge the inputs, if this has the `rust-call` ABI.
-    let inputs_temp;
-    let inputs = if abi == Abi::RustCall {
-        inputs_temp = untuple_arguments(cx, &sig.inputs);
-        &inputs_temp
-    } else {
-        &sig.inputs
-    };
-
-    // Arg 0: Output pointer.
-    // (if the output type is non-immediate)
-    let lloutputtype = match sig.output {
-        ty::FnConverging(output) => {
-            let use_out_pointer = return_uses_outptr(cx, output);
-            let lloutputtype = arg_type_of(cx, output);
-            // Use the output as the actual return value if it's immediate.
-            if use_out_pointer {
-                atys.push(lloutputtype.ptr_to());
-                Type::void(cx)
-            } else if return_type_is_void(cx, output) {
-                Type::void(cx)
-            } else {
-                lloutputtype
-            }
-        }
-        ty::FnDiverging => Type::void(cx)
-    };
-
-    // Arg 1: Environment
-    match llenvironment_type {
-        None => {}
-        Some(llenvironment_type) => atys.push(llenvironment_type),
-    }
-
-    // ... then explicit args.
-    for input in inputs {
-        let arg_ty = type_of_explicit_arg(cx, input);
-
-        if type_is_fat_ptr(cx.tcx(), input) {
-            atys.extend(arg_ty.field_types());
-        } else {
-            atys.push(arg_ty);
-        }
-    }
-
-    Type::func(&atys[..], &lloutputtype)
-}
-
 // A "sizing type" is an LLVM type, the size and alignment of which are
 // guaranteed to be equivalent to what you would get out of `type_of()`. It's
 // useful because:
@@ -171,7 +56,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
 
     let llsizingty = match t.sty {
         _ if !type_is_sized(cx.tcx(), t) => {
-            Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false)
+            Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, t)], false)
         }
 
         ty::TyBool => Type::bool(cx),
@@ -186,7 +71,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
             if type_is_sized(cx.tcx(), ty) {
                 Type::i8p(cx)
             } else {
-                Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false)
+                Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, ty)], false)
             }
         }
 
@@ -234,32 +119,27 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
         ty::TySlice(_) | ty::TyTrait(..) | ty::TyStr => unreachable!()
     };
 
-    debug!("--> mapped t={:?} to llsizingty={}",
-            t,
-            cx.tn().type_to_string(llsizingty));
+    debug!("--> mapped t={:?} to llsizingty={:?}", t, llsizingty);
 
     cx.llsizingtypes().borrow_mut().insert(t, llsizingty);
     llsizingty
 }
 
-pub fn foreign_arg_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
-    if t.is_bool() {
-        Type::i1(cx)
-    } else {
-        type_of(cx, t)
+fn unsized_info_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type {
+    let unsized_part = ccx.tcx().struct_tail(ty);
+    match unsized_part.sty {
+        ty::TyStr | ty::TyArray(..) | ty::TySlice(_) => {
+            Type::uint_from_ty(ccx, ast::UintTy::Us)
+        }
+        ty::TyTrait(_) => Type::vtable_ptr(ccx),
+        _ => unreachable!("Unexpected tail in unsized_info_ty: {:?} for ty={:?}",
+                          unsized_part, ty)
     }
 }
 
-pub fn arg_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
+pub fn immediate_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
     if t.is_bool() {
         Type::i1(cx)
-    } else if type_is_immediate(cx, t) && type_of(cx, t).is_aggregate() {
-        // We want to pass small aggregates as immediate values, but using an aggregate LLVM type
-        // for this leads to bad optimizations, so its arg type is an appropriately sized integer
-        match machine::llsize_of_alloc(cx, sizing_type_of(cx, t)) {
-            0 => type_of(cx, t),
-            n => Type::ix(cx, n * 8),
-        }
     } else {
         type_of(cx, t)
     }
@@ -314,12 +194,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
 
     if t != t_norm {
         let llty = in_memory_type_of(cx, t_norm);
-        debug!("--> normalized {:?} {:?} to {:?} {:?} llty={}",
-                t,
-                t,
-                t_norm,
-                t_norm,
-                cx.tn().type_to_string(llty));
+        debug!("--> normalized {:?} to {:?} llty={:?}", t, t_norm, llty);
         cx.lltypes().borrow_mut().insert(t, llty);
         return llty;
     }
@@ -361,16 +236,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
                   cx.tn().find_type("str_slice").unwrap()
               } else {
                   let ptr_ty = in_memory_type_of(cx, ty).ptr_to();
-                  let unsized_part = cx.tcx().struct_tail(ty);
-                  let info_ty = match unsized_part.sty {
-                      ty::TyStr | ty::TyArray(..) | ty::TySlice(_) => {
-                          Type::uint_from_ty(cx, ast::UintTy::Us)
-                      }
-                      ty::TyTrait(_) => Type::vtable_ptr(cx),
-                      _ => panic!("Unexpected type returned from \
-                                   struct_tail: {:?} for ty={:?}",
-                                  unsized_part, ty)
-                  };
+                  let info_ty = unsized_info_ty(cx, ty);
                   Type::struct_(cx, &[ptr_ty, info_ty], false)
               }
           } else {
@@ -398,13 +264,9 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
 
       ty::TyFnDef(..) => Type::nil(cx),
       ty::TyFnPtr(f) => {
-        if f.abi == Abi::Rust || f.abi == Abi::RustCall {
-            let sig = cx.tcx().erase_late_bound_regions(&f.sig);
-            let sig = infer::normalize_associated_type(cx.tcx(), &sig);
-            type_of_rust_fn(cx, None, &sig, f.abi).ptr_to()
-        } else {
-            foreign::lltype_for_foreign_fn(cx, t).ptr_to()
-        }
+        let sig = cx.tcx().erase_late_bound_regions(&f.sig);
+        let sig = infer::normalize_associated_type(cx.tcx(), &sig);
+        FnType::new(cx, f.abi, &sig, &[]).llvm_type(cx).ptr_to()
       }
       ty::TyTuple(ref tys) if tys.is_empty() => Type::nil(cx),
       ty::TyTuple(..) => {
@@ -440,9 +302,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
       ty::TyError => cx.sess().bug("type_of with TyError"),
     };
 
-    debug!("--> mapped t={:?} to llty={}",
-            t,
-            cx.tn().type_to_string(llty));
+    debug!("--> mapped t={:?} to llty={:?}", t, llty);
 
     cx.lltypes().borrow_mut().insert(t, llty);
 
diff --git a/src/librustc_trans/trans/value.rs b/src/librustc_trans/trans/value.rs
index bc71278c157..a33b7d62d2d 100644
--- a/src/librustc_trans/trans/value.rs
+++ b/src/librustc_trans/trans/value.rs
@@ -12,11 +12,22 @@ use llvm;
 use llvm::{UseRef, ValueRef};
 use trans::basic_block::BasicBlock;
 use trans::common::Block;
+
+use std::fmt;
+
 use libc::c_uint;
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, PartialEq)]
 pub struct Value(pub ValueRef);
 
+impl fmt::Debug for Value {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.write_str(&llvm::build_string(|s| unsafe {
+            llvm::LLVMWriteValueToString(self.0, s);
+        }).expect("nun-UTF8 value description from LLVM"))
+    }
+}
+
 macro_rules! opt_val { ($e:expr) => (
     unsafe {
         match $e {
diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs
index a05329bc4a4..3282d17d3a0 100644
--- a/src/librustc_typeck/check/intrinsic.rs
+++ b/src/librustc_typeck/check/intrinsic.rs
@@ -280,6 +280,8 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &hir::ForeignItem) {
 
             "overflowing_add" | "overflowing_sub" | "overflowing_mul" =>
                 (1, vec![param(ccx, 0), param(ccx, 0)], param(ccx, 0)),
+            "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" =>
+                (1, vec![param(ccx, 0), param(ccx, 0)], param(ccx, 0)),
 
             "return_address" => (0, vec![], tcx.mk_imm_ptr(tcx.types.u8)),
 
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 0d5e25efd68..903fc458d81 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3416,12 +3416,12 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
               fcx.add_wf_bounds(&item_substs.substs, expr);
           });
       }
-      hir::ExprInlineAsm(ref ia) => {
-          for &(_, ref input) in &ia.inputs {
-              check_expr(fcx, &input);
+      hir::ExprInlineAsm(_, ref outputs, ref inputs) => {
+          for output in outputs {
+              check_expr(fcx, output);
           }
-          for out in &ia.outputs {
-              check_expr(fcx, &out.expr);
+          for input in inputs {
+              check_expr(fcx, input);
           }
           fcx.write_nil(id);
       }
diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs
index f7fa3e1b142..78ae8fa65f8 100644
--- a/src/librustc_typeck/coherence/overlap.rs
+++ b/src/librustc_typeck/coherence/overlap.rs
@@ -18,11 +18,11 @@ use middle::traits::{self, ProjectionMode};
 use middle::infer;
 use middle::ty::{self, TyCtxt};
 use syntax::ast;
-use syntax::codemap::Span;
 use rustc::dep_graph::DepNode;
 use rustc_front::hir;
 use rustc_front::intravisit;
 use util::nodemap::DefIdMap;
+use lint;
 
 pub fn check(tcx: &TyCtxt) {
     let mut overlap = OverlapChecker { tcx: tcx,
@@ -41,11 +41,6 @@ struct OverlapChecker<'cx, 'tcx:'cx> {
 }
 
 impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
-    fn span_of_def_id(&self, did: DefId) -> Span {
-        let node_id = self.tcx.map.as_local_node_id(did).unwrap();
-        self.tcx.map.span(node_id)
-    }
-
     fn check_for_common_items_in_impls(&self, impl1: DefId, impl2: DefId) {
         #[derive(Copy, Clone, PartialEq)]
         enum Namespace { Type, Value }
@@ -68,11 +63,12 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
 
             for item2 in &impl_items[&impl2] {
                 if (name, namespace) == name_and_namespace(&self.tcx, item2) {
-                    let mut err = super::report_duplicate_item(
-                        &self.tcx, self.span_of_def_id(item1.def_id()), name);
-                    span_note!(&mut err, self.span_of_def_id(item2.def_id()),
-                               "conflicting definition is here:");
-                    err.emit();
+                    let msg = format!("duplicate definitions with name `{}`", name);
+                    let node_id = self.tcx.map.as_local_node_id(item1.def_id()).unwrap();
+                    self.tcx.sess.add_lint(lint::builtin::OVERLAPPING_INHERENT_IMPLS,
+                                           node_id,
+                                           self.tcx.span_of_impl(item1.def_id()).unwrap(),
+                                           msg);
                 }
             }
         }
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 0f88640b629..258c7af1316 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -2155,7 +2155,7 @@ fn compute_type_scheme_of_foreign_fn_decl<'a, 'tcx>(
     let input_tys = decl.inputs
                         .iter()
                         .map(|a| ty_of_arg(&ccx.icx(ast_generics), &rb, a, None))
-                        .collect();
+                        .collect::<Vec<_>>();
 
     let output = match decl.output {
         hir::Return(ref ty) =>
@@ -2166,6 +2166,29 @@ fn compute_type_scheme_of_foreign_fn_decl<'a, 'tcx>(
             ty::FnDiverging
     };
 
+    // feature gate SIMD types in FFI, since I (huonw) am not sure the
+    // ABIs are handled at all correctly.
+    if abi != abi::Abi::RustIntrinsic && abi != abi::Abi::PlatformIntrinsic
+            && !ccx.tcx.sess.features.borrow().simd_ffi {
+        let check = |ast_ty: &hir::Ty, ty: ty::Ty| {
+            if ty.is_simd() {
+                ccx.tcx.sess.struct_span_err(ast_ty.span,
+                              &format!("use of SIMD type `{}` in FFI is highly experimental and \
+                                        may result in invalid code",
+                                       pprust::ty_to_string(ast_ty)))
+                    .fileline_help(ast_ty.span,
+                                   "add #![feature(simd_ffi)] to the crate attributes to enable")
+                    .emit();
+            }
+        };
+        for (input, ty) in decl.inputs.iter().zip(&input_tys) {
+            check(&input.ty, ty)
+        }
+        if let hir::Return(ref ty) = decl.output {
+            check(&ty, output.unwrap())
+        }
+    }
+
     let substs = ccx.tcx.mk_substs(mk_item_substs(ccx, &ty_generics));
     let t_fn = ccx.tcx.mk_fn_def(id, substs, ty::BareFnTy {
         abi: abi,
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index ef54be72037..49091a6c2bc 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -336,7 +336,7 @@ pub fn build_impl(cx: &DocContext,
                 let did = assoc_const.def_id;
                 let type_scheme = tcx.lookup_item_type(did);
                 let default = if assoc_const.has_value {
-                    Some(const_eval::lookup_const_by_id(tcx, did, None, None)
+                    Some(const_eval::lookup_const_by_id(tcx, did, None)
                          .unwrap().0.span.to_src(cx))
                 } else {
                     None
@@ -479,7 +479,7 @@ fn build_const(cx: &DocContext, tcx: &TyCtxt,
     use rustc::middle::const_eval;
     use rustc_front::print::pprust;
 
-    let (expr, ty) = const_eval::lookup_const_by_id(tcx, did, None, None).unwrap_or_else(|| {
+    let (expr, ty) = const_eval::lookup_const_by_id(tcx, did, None).unwrap_or_else(|| {
         panic!("expected lookup_const_by_id to succeed for {:?}", did);
     });
     debug!("converting constant expr {:?} to snippet", expr);
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index f3efbcb1db3..8fb58f58e8a 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -740,7 +740,11 @@
             $(".search-input").on("keyup input",function() {
                 clearTimeout(searchTimeout);
                 if ($(this).val().length === 0) {
-                    window.history.replaceState("", "std - Rust", "?search=");
+                    if (browserSupportsHistoryApi()) {
+                        history.replaceState("", "std - Rust", "?search=");
+                    } else {
+                        location.replace("?search=");
+                    }
                     $('#main.content').removeClass('hidden');
                     $('#search.content').addClass('hidden');
                 } else {
@@ -996,7 +1000,7 @@
         var prev_id = 0;
 
         function set_fragment(name) {
-            if (history.replaceState) {
+            if (browserSupportsHistoryApi()) {
                 history.replaceState(null, null, '#' + name);
                 $(window).trigger('hashchange');
             } else {
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 7d4061b6559..331e3431cee 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -257,10 +257,10 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
     }
 
     match {
-        let b_sess = AssertRecoverSafe::new(&sess);
-        let b_cstore = AssertRecoverSafe::new(&cstore);
-        let b_cfg = AssertRecoverSafe::new(cfg.clone());
-        let b_control = AssertRecoverSafe::new(&control);
+        let b_sess = AssertRecoverSafe(&sess);
+        let b_cstore = AssertRecoverSafe(&cstore);
+        let b_cfg = AssertRecoverSafe(cfg.clone());
+        let b_control = AssertRecoverSafe(&control);
 
         panic::recover(|| {
             driver::compile_input(&b_sess, &b_cstore, (*b_cfg).clone(),
diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs
index ec5629038a4..56d95401a9a 100644
--- a/src/libstd/collections/hash/map.rs
+++ b/src/libstd/collections/hash/map.rs
@@ -1554,6 +1554,12 @@ impl<'a, K, V> Entry<'a, K, V> {
 }
 
 impl<'a, K, V> OccupiedEntry<'a, K, V> {
+    /// Gets a reference to the key in the entry.
+    #[unstable(feature = "map_entry_keys", issue = "32281")]
+    pub fn key(&self) -> &K {
+        self.elem.read().0
+    }
+
     /// Gets a reference to the value in the entry.
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn get(&self) -> &V {
@@ -1589,6 +1595,13 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
 }
 
 impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
+    /// Gets a reference to the key that would be used when inserting a value
+    /// through the VacantEntry.
+    #[unstable(feature = "map_entry_keys", issue = "32281")]
+    pub fn key(&self) -> &K {
+        &self.key
+    }
+
     /// Sets the value of the entry with the VacantEntry's key,
     /// and returns a mutable reference to it
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -2434,4 +2447,40 @@ mod test_map {
         a.insert(item, 0);
         assert!(a.capacity() > a.len());
     }
+
+    #[test]
+    fn test_occupied_entry_key() {
+        let mut a = HashMap::new();
+        let key = "hello there";
+        let value = "value goes here";
+        assert!(a.is_empty());
+        a.insert(key.clone(), value.clone());
+        assert_eq!(a.len(), 1);
+        assert_eq!(a[key], value);
+
+        match a.entry(key.clone()) {
+            Vacant(_) => panic!(),
+            Occupied(e) => assert_eq!(key, *e.key()),
+        }
+        assert_eq!(a.len(), 1);
+        assert_eq!(a[key], value);
+    }
+
+    #[test]
+    fn test_vacant_entry_key() {
+        let mut a = HashMap::new();
+        let key = "hello there";
+        let value = "value goes here";
+
+        assert!(a.is_empty());
+        match a.entry(key.clone()) {
+            Occupied(_) => panic!(),
+            Vacant(e) => {
+                assert_eq!(key, *e.key());
+                e.insert(value.clone());
+            },
+        }
+        assert_eq!(a.len(), 1);
+        assert_eq!(a[key], value);
+    }
 }
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index cd0e3a030bd..85e48f85d3d 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -251,6 +251,7 @@
 #![feature(raw)]
 #![feature(repr_simd)]
 #![feature(reflect_marker)]
+#![feature(rustc_attrs)]
 #![feature(shared)]
 #![feature(slice_bytes)]
 #![feature(slice_concat_ext)]
diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs
index bb688ab484a..934d0468a38 100644
--- a/src/libstd/net/ip.rs
+++ b/src/libstd/net/ip.rs
@@ -279,6 +279,13 @@ impl From<u32> for Ipv4Addr {
     }
 }
 
+#[stable(feature = "from_slice_v4", since = "1.9.0")]
+impl From<[u8; 4]> for Ipv4Addr {
+    fn from(octets: [u8; 4]) -> Ipv4Addr {
+        Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3])
+    }
+}
+
 impl Ipv6Addr {
     /// Creates a new IPv6 address from eight 16-bit segments.
     ///
@@ -363,6 +370,12 @@ impl Ipv6Addr {
         (self.segments()[0] & 0xffc0) == 0xfec0
     }
 
+    /// Returns true if this is an address reserved for documentation
+    /// This is defined to be 2001:db8::/32 in RFC RFC 3849
+    pub fn is_documentation(&self) -> bool {
+        (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8)
+    }
+
     /// Returns true if the address is a globally routable unicast address.
     ///
     /// The following return false:
@@ -372,11 +385,12 @@ impl Ipv6Addr {
     /// - the (deprecated) site-local addresses
     /// - unique local addresses
     /// - the unspecified address
+    /// - the address range reserved for documentation
     pub fn is_unicast_global(&self) -> bool {
         !self.is_multicast()
             && !self.is_loopback() && !self.is_unicast_link_local()
             && !self.is_unicast_site_local() && !self.is_unique_local()
-            && !self.is_unspecified()
+            && !self.is_unspecified() && !self.is_documentation()
     }
 
     /// Returns the address's multicast scope if the address is multicast.
@@ -752,7 +766,7 @@ mod tests {
     fn ipv6_properties() {
         fn check(str_addr: &str, unspec: bool, loopback: bool,
                  unique_local: bool, global: bool,
-                 u_link_local: bool, u_site_local: bool, u_global: bool,
+                 u_link_local: bool, u_site_local: bool, u_global: bool, u_doc: bool,
                  m_scope: Option<Ipv6MulticastScope>) {
             let ip: Ipv6Addr = str_addr.parse().unwrap();
             assert_eq!(str_addr, ip.to_string());
@@ -764,43 +778,46 @@ mod tests {
             assert_eq!(ip.is_unicast_link_local(), u_link_local);
             assert_eq!(ip.is_unicast_site_local(), u_site_local);
             assert_eq!(ip.is_unicast_global(), u_global);
+            assert_eq!(ip.is_documentation(), u_doc);
             assert_eq!(ip.multicast_scope(), m_scope);
             assert_eq!(ip.is_multicast(), m_scope.is_some());
         }
 
-        //    unspec loopbk uniqlo global unill  unisl  uniglo mscope
+        //    unspec loopbk uniqlo global unill  unisl  uniglo  doc    mscope
         check("::",
-              true,  false, false, false,  false, false, false,  None);
+              true,  false, false, false,  false, false, false, false, None);
         check("::1",
-              false, true,  false, false, false, false, false, None);
+              false, true,  false, false, false, false,  false, false, None);
         check("::0.0.0.2",
-              false, false, false, true,  false, false, true,  None);
+              false, false, false, true,  false, false, true,  false, None);
         check("1::",
-              false, false, false, true,  false, false, true,  None);
+              false, false, false, true,  false, false, true,  false, None);
         check("fc00::",
-              false, false, true,  false, false, false, false, None);
+              false, false, true,  false, false, false, false, false, None);
         check("fdff:ffff::",
-              false, false, true,  false, false, false, false, None);
+              false, false, true,  false, false, false, false, false, None);
         check("fe80:ffff::",
-              false, false, false, false, true,  false, false, None);
+              false, false, false, false, true,  false, false, false, None);
         check("febf:ffff::",
-              false, false, false, false, true,  false, false, None);
+              false, false, false, false, true,  false, false, false, None);
         check("fec0::",
-              false, false, false, false, false, true,  false, None);
+              false, false, false, false, false, true,  false, false, None);
         check("ff01::",
-              false, false, false, false, false, false, false, Some(InterfaceLocal));
+              false, false, false, false, false, false, false, false, Some(InterfaceLocal));
         check("ff02::",
-              false, false, false, false, false, false, false, Some(LinkLocal));
+              false, false, false, false, false, false, false, false, Some(LinkLocal));
         check("ff03::",
-              false, false, false, false, false, false, false, Some(RealmLocal));
+              false, false, false, false, false, false, false, false, Some(RealmLocal));
         check("ff04::",
-              false, false, false, false, false, false, false, Some(AdminLocal));
+              false, false, false, false, false, false, false, false, Some(AdminLocal));
         check("ff05::",
-              false, false, false, false, false, false, false, Some(SiteLocal));
+              false, false, false, false, false, false, false, false, Some(SiteLocal));
         check("ff08::",
-              false, false, false, false, false, false, false, Some(OrganizationLocal));
+              false, false, false, false, false, false, false, false, Some(OrganizationLocal));
         check("ff0e::",
-              false, false, false, true,  false, false, false, Some(Global));
+              false, false, false, true,  false, false, false, false, Some(Global));
+        check("2001:db8:85a3::8a2e:370:7334",
+              false, false, false, false, false, false, false, true, None);
     }
 
     #[test]
@@ -822,6 +839,11 @@ mod tests {
     }
 
     #[test]
+    fn ipv4_from_u32_slice() {
+        assert_eq!(Ipv4Addr::from([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1))
+    }
+
+    #[test]
     fn ord() {
         assert!(Ipv4Addr::new(100, 64, 3, 3) < Ipv4Addr::new(192, 0, 2, 2));
         assert!("2001:db8:f00::1002".parse::<Ipv6Addr>().unwrap() <
diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs
index 37053025924..e78d46b22e9 100644
--- a/src/libstd/num/f32.rs
+++ b/src/libstd/num/f32.rs
@@ -1371,6 +1371,7 @@ mod tests {
     }
 
     #[test]
+    #[rustc_no_mir] // FIXME #27840 MIR NAN ends up negative.
     fn test_integer_decode() {
         assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1));
         assert_eq!((-8573.5918555f32).integer_decode(), (8779358, -10, -1));
diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs
index 446e22a20ad..cea5a9edd68 100644
--- a/src/libstd/num/f64.rs
+++ b/src/libstd/num/f64.rs
@@ -1264,6 +1264,7 @@ mod tests {
     }
 
     #[test]
+    #[rustc_no_mir] // FIXME #27840 MIR NAN ends up negative.
     fn test_integer_decode() {
         assert_eq!(3.14159265359f64.integer_decode(), (7074237752028906, -51, 1));
         assert_eq!((-8573.5918555f64).integer_decode(), (4713381968463931, -39, -1));
diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs
index 5c2e36623cb..f2789949887 100644
--- a/src/libstd/panic.rs
+++ b/src/libstd/panic.rs
@@ -23,7 +23,21 @@ use sync::{Arc, Mutex, RwLock};
 use sys_common::unwind;
 use thread::Result;
 
-pub use panicking::{take_handler, set_handler, PanicInfo, Location};
+pub use panicking::{take_hook, set_hook, PanicInfo, Location};
+
+///
+#[rustc_deprecated(since = "1.9.0", reason = "renamed to set_hook")]
+#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
+pub fn set_handler<F>(handler: F) where F: Fn(&PanicInfo) + 'static + Sync + Send {
+    set_hook(Box::new(handler))
+}
+
+///
+#[rustc_deprecated(since = "1.9.0", reason = "renamed to take_hook")]
+#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
+pub fn take_handler() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
+    take_hook()
+}
 
 /// A marker trait which represents "panic safe" types in Rust.
 ///
@@ -147,7 +161,7 @@ pub trait RefRecoverSafe {}
 /// // });
 ///
 /// // This, however, will compile due to the `AssertRecoverSafe` wrapper
-/// let result = panic::recover(AssertRecoverSafe::new(|| {
+/// let result = panic::recover(AssertRecoverSafe(|| {
 ///     variable += 3;
 /// }));
 /// // ...
@@ -171,7 +185,7 @@ pub trait RefRecoverSafe {}
 /// let other_capture = 3;
 ///
 /// let result = {
-///     let mut wrapper = AssertRecoverSafe::new(&mut variable);
+///     let mut wrapper = AssertRecoverSafe(&mut variable);
 ///     panic::recover(move || {
 ///         **wrapper += other_capture;
 ///     })
@@ -179,7 +193,7 @@ pub trait RefRecoverSafe {}
 /// // ...
 /// ```
 #[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
-pub struct AssertRecoverSafe<T>(T);
+pub struct AssertRecoverSafe<T>(pub T);
 
 // Implementations of the `RecoverSafe` trait:
 //
@@ -216,12 +230,16 @@ impl<T> RefRecoverSafe for AssertRecoverSafe<T> {}
 impl<T> AssertRecoverSafe<T> {
     /// Creates a new `AssertRecoverSafe` wrapper around the provided type.
     #[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
+    #[rustc_deprecated(reason = "the type's field is now public, construct it directly",
+                       since = "1.9.0")]
     pub fn new(t: T) -> AssertRecoverSafe<T> {
         AssertRecoverSafe(t)
     }
 
     /// Consumes the `AssertRecoverSafe`, returning the wrapped value.
     #[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
+    #[rustc_deprecated(reason = "the type's field is now public, access it directly",
+                       since = "1.9.0")]
     pub fn into_inner(self) -> T {
         self.0
     }
diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs
index 490c5f4b352..fd6a15b0f69 100644
--- a/src/libstd/panicking.rs
+++ b/src/libstd/panicking.rs
@@ -32,73 +32,71 @@ thread_local! {
 }
 
 #[derive(Copy, Clone)]
-enum Handler {
+enum Hook {
     Default,
     Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)),
 }
 
-static HANDLER_LOCK: StaticRwLock = StaticRwLock::new();
-static mut HANDLER: Handler = Handler::Default;
+static HOOK_LOCK: StaticRwLock = StaticRwLock::new();
+static mut HOOK: Hook = Hook::Default;
 static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
 
-/// Registers a custom panic handler, replacing any that was previously
-/// registered.
+/// Registers a custom panic hook, replacing any that was previously registered.
 ///
-/// The panic handler is invoked when a thread panics, but before it begins
-/// unwinding the stack. The default handler prints a message to standard error
+/// The panic hook is invoked when a thread panics, but before it begins
+/// unwinding the stack. The default hook prints a message to standard error
 /// and generates a backtrace if requested, but this behavior can be customized
-/// with the `set_handler` and `take_handler` functions.
+/// with the `set_hook` and `take_hook` functions.
 ///
-/// The handler is provided with a `PanicInfo` struct which contains information
+/// The hook is provided with a `PanicInfo` struct which contains information
 /// about the origin of the panic, including the payload passed to `panic!` and
 /// the source code location from which the panic originated.
 ///
-/// The panic handler is a global resource.
+/// The panic hook is a global resource.
 ///
 /// # Panics
 ///
 /// Panics if called from a panicking thread.
 #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
-pub fn set_handler<F>(handler: F) where F: Fn(&PanicInfo) + 'static + Sync + Send {
+pub fn set_hook(hook: Box<Fn(&PanicInfo) + 'static + Sync + Send>) {
     if thread::panicking() {
-        panic!("cannot modify the panic handler from a panicking thread");
+        panic!("cannot modify the panic hook from a panicking thread");
     }
 
-    let handler = Box::new(handler);
     unsafe {
-        let lock = HANDLER_LOCK.write();
-        let old_handler = HANDLER;
-        HANDLER = Handler::Custom(Box::into_raw(handler));
+        let lock = HOOK_LOCK.write();
+        let old_hook = HOOK;
+        HOOK = Hook::Custom(Box::into_raw(hook));
         drop(lock);
 
-        if let Handler::Custom(ptr) = old_handler {
+        if let Hook::Custom(ptr) = old_hook {
             Box::from_raw(ptr);
         }
     }
 }
 
-/// Unregisters the current panic handler, returning it.
+/// Unregisters the current panic hook, returning it.
 ///
-/// If no custom handler is registered, the default handler will be returned.
+/// If no custom hook is registered, the default hook will be returned.
 ///
 /// # Panics
 ///
 /// Panics if called from a panicking thread.
 #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
-pub fn take_handler() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
+pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
     if thread::panicking() {
-        panic!("cannot modify the panic handler from a panicking thread");
+        panic!("cannot modify the panic hook from a panicking thread");
     }
 
     unsafe {
-        let lock = HANDLER_LOCK.write();
-        let handler = HANDLER;
-        HANDLER = Handler::Default;
+        let lock = HOOK_LOCK.write();
+        let hook = HOOK;
+        HOOK = Hook::Default;
         drop(lock);
 
-        match handler {
-            Handler::Default => Box::new(default_handler),
-            Handler::Custom(ptr) => {Box::from_raw(ptr)} // FIXME #30530
+        match hook {
+            Hook::Default => Box::new(default_hook),
+            Hook::Custom(ptr) => {Box::from_raw(ptr)} // FIXME #30530
         }
     }
 }
@@ -151,7 +149,7 @@ impl<'a> Location<'a> {
     }
 }
 
-fn default_handler(info: &PanicInfo) {
+fn default_hook(info: &PanicInfo) {
     let panics = PANIC_COUNT.with(|s| s.get());
 
     // If this is a double panic, make sure that we print a backtrace
@@ -224,10 +222,10 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
     };
 
     unsafe {
-        let _lock = HANDLER_LOCK.read();
-        match HANDLER {
-            Handler::Default => default_handler(&info),
-            Handler::Custom(ptr) => (*ptr)(&info),
+        let _lock = HOOK_LOCK.read();
+        match HOOK {
+            Hook::Default => default_hook(&info),
+            Hook::Custom(ptr) => (*ptr)(&info),
         }
     }
 
diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs
index 38da478b5ed..77aeaf8459a 100644
--- a/src/libsyntax/ext/quote.rs
+++ b/src/libsyntax/ext/quote.rs
@@ -121,6 +121,12 @@ pub mod rt {
         }
     }
 
+    impl ToTokens for P<ast::ImplItem> {
+        fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
+            vec![TokenTree::Token(self.span, token::Interpolated(token::NtImplItem(self.clone())))]
+        }
+    }
+
     impl ToTokens for ast::TraitItem {
         fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
             vec![TokenTree::Token(self.span,
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index fbaf28332c4..a017e62d546 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -350,10 +350,14 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
                                            "the `#[rustc_move_fragments]` attribute \
                                             is just used for rustc unit tests \
                                             and will never be stable")),
-    ("rustc_mir", Normal, Gated("rustc_attrs",
-                                "the `#[rustc_mir]` attribute \
-                                 is just used for rustc unit tests \
-                                 and will never be stable")),
+    ("rustc_mir", Whitelisted, Gated("rustc_attrs",
+                                     "the `#[rustc_mir]` attribute \
+                                      is just used for rustc unit tests \
+                                      and will never be stable")),
+    ("rustc_no_mir", Whitelisted, Gated("rustc_attrs",
+                                        "the `#[rustc_no_mir]` attribute \
+                                         is just used to make tests pass \
+                                         and will never be stable")),
 
     ("allow_internal_unstable", Normal, Gated("allow_internal_unstable",
                                               EXPLAIN_ALLOW_INTERNAL_UNSTABLE)),
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 533487ae1c5..430d13b87fc 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -752,7 +752,8 @@ pub trait PrintState<'a> {
         }
         try!(self.maybe_print_comment(attr.span.lo));
         if attr.node.is_sugared_doc {
-            word(self.writer(), &attr.value_str().unwrap())
+            try!(word(self.writer(), &attr.value_str().unwrap()));
+            hardbreak(self.writer())
         } else {
             match attr.node.style {
                 ast::AttrStyle::Inner => try!(word(self.writer(), "#![")),
diff --git a/src/libsyntax_ext/deriving/cmp/ord.rs b/src/libsyntax_ext/deriving/cmp/ord.rs
index a7e156c5f68..74706c47087 100644
--- a/src/libsyntax_ext/deriving/cmp/ord.rs
+++ b/src/libsyntax_ext/deriving/cmp/ord.rs
@@ -64,7 +64,7 @@ pub fn ordering_collapsed(cx: &mut ExtCtxt,
 
 pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
               substr: &Substructure) -> P<Expr> {
-    let test_id = cx.ident_of("cmp");
+    let test_id = cx.ident_of("__cmp");
     let equals_path = cx.path_global(span,
                                      cx.std_path(&["cmp", "Ordering", "Equal"]));
 
@@ -79,9 +79,9 @@ pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
                 ::std::cmp::Ordering::Equal => {
                     ...
                 }
-                cmp => cmp
+                __cmp => __cmp
             },
-        cmp => cmp
+        __cmp => __cmp
     }
     */
     cs_fold(
@@ -91,7 +91,7 @@ pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
         |cx, span, old, self_f, other_fs| {
             // match new {
             //     ::std::cmp::Ordering::Equal => old,
-            //     cmp => cmp
+            //     __cmp => __cmp
             // }
 
             let new = {
diff --git a/src/libsyntax_ext/deriving/cmp/partial_eq.rs b/src/libsyntax_ext/deriving/cmp/partial_eq.rs
index 0150a073b07..6406ee59a5e 100644
--- a/src/libsyntax_ext/deriving/cmp/partial_eq.rs
+++ b/src/libsyntax_ext/deriving/cmp/partial_eq.rs
@@ -80,6 +80,14 @@ pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt,
         } }
     }
 
+    // avoid defining `ne` if we can
+    // c-like enums, enums without any fields and structs without fields
+    // can safely define only `eq`.
+    let mut methods = vec![md!("eq", cs_eq)];
+    if !is_type_without_fields(item) {
+        methods.push(md!("ne", cs_ne));
+    }
+
     let trait_def = TraitDef {
         span: span,
         attributes: Vec::new(),
@@ -87,10 +95,7 @@ pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt,
         additional_bounds: Vec::new(),
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
-        methods: vec!(
-            md!("eq", cs_eq),
-            md!("ne", cs_ne)
-        ),
+        methods: methods,
         associated_types: Vec::new(),
     };
     trait_def.expand(cx, mitem, item, push)
diff --git a/src/libsyntax_ext/deriving/cmp/partial_ord.rs b/src/libsyntax_ext/deriving/cmp/partial_ord.rs
index b3864a6c2e7..e49c77285ab 100644
--- a/src/libsyntax_ext/deriving/cmp/partial_ord.rs
+++ b/src/libsyntax_ext/deriving/cmp/partial_ord.rs
@@ -67,6 +67,21 @@ pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
         }))
     };
 
+    // avoid defining extra methods if we can
+    // c-like enums, enums without any fields and structs without fields
+    // can safely define only `partial_cmp`.
+    let methods = if is_type_without_fields(item) {
+        vec![partial_cmp_def]
+    } else {
+        vec![
+            partial_cmp_def,
+            md!("lt", true, false),
+            md!("le", true, true),
+            md!("gt", false, false),
+            md!("ge", false, true)
+        ]
+    };
+
     let trait_def = TraitDef {
         span: span,
         attributes: vec![],
@@ -74,13 +89,7 @@ pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
         additional_bounds: vec![],
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
-        methods: vec![
-            partial_cmp_def,
-            md!("lt", true, false),
-            md!("le", true, true),
-            md!("gt", false, false),
-            md!("ge", false, true)
-        ],
+        methods: methods,
         associated_types: Vec::new(),
     };
     trait_def.expand(cx, mitem, item, push)
@@ -107,7 +116,7 @@ pub fn some_ordering_collapsed(cx: &mut ExtCtxt,
 
 pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span,
               substr: &Substructure) -> P<Expr> {
-    let test_id = cx.ident_of("cmp");
+    let test_id = cx.ident_of("__cmp");
     let ordering = cx.path_global(span,
                                   cx.std_path(&["cmp", "Ordering", "Equal"]));
     let ordering_expr = cx.expr_path(ordering.clone());
@@ -124,9 +133,9 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span,
                 ::std::option::Option::Some(::std::cmp::Ordering::Equal) => {
                     ...
                 }
-                cmp => cmp
+                __cmp => __cmp
             },
-        cmp => cmp
+        __cmp => __cmp
     }
     */
     cs_fold(
@@ -136,7 +145,7 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span,
         |cx, span, old, self_f, other_fs| {
             // match new {
             //     Some(::std::cmp::Ordering::Equal) => old,
-            //     cmp => cmp
+            //     __cmp => __cmp
             // }
 
             let new = {
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index 58c84e68795..6654afd3664 100644
--- a/src/libsyntax_ext/deriving/generic/mod.rs
+++ b/src/libsyntax_ext/deriving/generic/mod.rs
@@ -156,14 +156,14 @@
 //!
 //! ```{.text}
 //! EnumNonMatchingCollapsed(
-//!     vec![<ident of self>, <ident of arg_1>],
+//!     vec![<ident of self>, <ident of __arg_1>],
 //!     &[<ast::Variant for C0>, <ast::Variant for C1>],
-//!     &[<ident for self index value>, <ident of arg_1 index value>])
+//!     &[<ident for self index value>, <ident of __arg_1 index value>])
 //! ```
 //!
 //! It is the same for when the arguments are flipped to `C1 {x}` and
 //! `C0(a)`; the only difference is what the values of the identifiers
-//! <ident for self index value> and <ident of arg_1 index value> will
+//! <ident for self index value> and <ident of __arg_1 index value> will
 //! be in the generated code.
 //!
 //! `EnumNonMatchingCollapsed` deliberately provides far less information
@@ -843,7 +843,7 @@ impl<'a> MethodDef<'a> {
 
         for (i, ty) in self.args.iter().enumerate() {
             let ast_ty = ty.to_ty(cx, trait_.span, type_ident, generics);
-            let ident = cx.ident_of(&format!("arg_{}", i));
+            let ident = cx.ident_of(&format!("__arg_{}", i));
             arg_tys.push((ident, ast_ty));
 
             let arg_expr = cx.expr_ident(trait_.span, ident);
@@ -929,12 +929,12 @@ impl<'a> MethodDef<'a> {
     ///
     /// // equivalent to:
     /// impl PartialEq for A {
-    ///     fn eq(&self, arg_1: &A) -> bool {
+    ///     fn eq(&self, __arg_1: &A) -> bool {
     ///         match *self {
-    ///             A {x: ref self_0_0, y: ref self_0_1} => {
-    ///                 match *arg_1 {
-    ///                     A {x: ref self_1_0, y: ref self_1_1} => {
-    ///                         self_0_0.eq(self_1_0) && self_0_1.eq(self_1_1)
+    ///             A {x: ref __self_0_0, y: ref __self_0_1} => {
+    ///                 match *__arg_1 {
+    ///                     A {x: ref __self_1_0, y: ref __self_1_1} => {
+    ///                         __self_0_0.eq(__self_1_0) && __self_0_1.eq(__self_1_1)
     ///                     }
     ///                 }
     ///             }
@@ -960,7 +960,7 @@ impl<'a> MethodDef<'a> {
                 trait_.create_struct_pattern(cx,
                                              struct_path,
                                              struct_def,
-                                             &format!("self_{}",
+                                             &format!("__self_{}",
                                                      i),
                                              ast::Mutability::Immutable);
             patterns.push(pat);
@@ -1038,14 +1038,14 @@ impl<'a> MethodDef<'a> {
     /// // is equivalent to
     ///
     /// impl PartialEq for A {
-    ///     fn eq(&self, arg_1: &A) -> ::bool {
-    ///         match (&*self, &*arg_1) {
+    ///     fn eq(&self, __arg_1: &A) -> ::bool {
+    ///         match (&*self, &*__arg_1) {
     ///             (&A1, &A1) => true,
     ///             (&A2(ref self_0),
-    ///              &A2(ref arg_1_0)) => (*self_0).eq(&(*arg_1_0)),
+    ///              &A2(ref __arg_1_0)) => (*self_0).eq(&(*__arg_1_0)),
     ///             _ => {
-    ///                 let self_vi = match *self { A1(..) => 0, A2(..) => 1 };
-    ///                 let arg_1_vi = match *arg_1 { A1(..) => 0, A2(..) => 1 };
+    ///                 let __self_vi = match *self { A1(..) => 0, A2(..) => 1 };
+    ///                 let __arg_1_vi = match *__arg_1 { A1(..) => 0, A2(..) => 1 };
     ///                 false
     ///             }
     ///         }
@@ -1053,10 +1053,10 @@ impl<'a> MethodDef<'a> {
     /// }
     /// ```
     ///
-    /// (Of course `self_vi` and `arg_1_vi` are unused for
+    /// (Of course `__self_vi` and `__arg_1_vi` are unused for
     /// `PartialEq`, and those subcomputations will hopefully be removed
-    /// as their results are unused.  The point of `self_vi` and
-    /// `arg_1_vi` is for `PartialOrd`; see #15503.)
+    /// as their results are unused.  The point of `__self_vi` and
+    /// `__arg_1_vi` is for `PartialOrd`; see #15503.)
     fn expand_enum_method_body<'b>(&self,
                                cx: &mut ExtCtxt,
                                trait_: &TraitDef<'b>,
@@ -1087,14 +1087,14 @@ impl<'a> MethodDef<'a> {
     /// for each of the self-args, carried in precomputed variables.
 
     /// ```{.text}
-    /// let self0_vi = unsafe {
+    /// let __self0_vi = unsafe {
     ///     std::intrinsics::discriminant_value(&self) } as i32;
-    /// let self1_vi = unsafe {
+    /// let __self1_vi = unsafe {
     ///     std::intrinsics::discriminant_value(&arg1) } as i32;
-    /// let self2_vi = unsafe {
+    /// let __self2_vi = unsafe {
     ///     std::intrinsics::discriminant_value(&arg2) } as i32;
     ///
-    /// if self0_vi == self1_vi && self0_vi == self2_vi && ... {
+    /// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... {
     ///     match (...) {
     ///         (Variant1, Variant1, ...) => Body1
     ///         (Variant2, Variant2, ...) => Body2,
@@ -1122,9 +1122,9 @@ impl<'a> MethodDef<'a> {
         let self_arg_names = self_args.iter().enumerate()
             .map(|(arg_count, _self_arg)| {
                 if arg_count == 0 {
-                    "self".to_string()
+                    "__self".to_string()
                 } else {
-                    format!("arg_{}", arg_count)
+                    format!("__arg_{}", arg_count)
                 }
             })
             .collect::<Vec<String>>();
@@ -1261,17 +1261,17 @@ impl<'a> MethodDef<'a> {
             // with three Self args, builds three statements:
             //
             // ```
-            // let self0_vi = unsafe {
+            // let __self0_vi = unsafe {
             //     std::intrinsics::discriminant_value(&self) } as i32;
-            // let self1_vi = unsafe {
+            // let __self1_vi = unsafe {
             //     std::intrinsics::discriminant_value(&arg1) } as i32;
-            // let self2_vi = unsafe {
+            // let __self2_vi = unsafe {
             //     std::intrinsics::discriminant_value(&arg2) } as i32;
             // ```
             let mut index_let_stmts: Vec<ast::Stmt> = Vec::new();
 
             //We also build an expression which checks whether all discriminants are equal
-            // discriminant_test = self0_vi == self1_vi && self0_vi == self2_vi && ...
+            // discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ...
             let mut discriminant_test = cx.expr_bool(sp, true);
 
             let target_type_name =
@@ -1321,7 +1321,7 @@ impl<'a> MethodDef<'a> {
             // down to desired l-values, but we cannot actually deref
             // them when they are fed as r-values into a tuple
             // expression; here add a layer of borrowing, turning
-            // `(*self, *arg_0, ...)` into `(&*self, &*arg_0, ...)`.
+            // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
             let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg));
             let match_arg = cx.expr(sp, ast::ExprKind::Tup(borrowed_self_args));
 
@@ -1335,7 +1335,7 @@ impl<'a> MethodDef<'a> {
             //      }
             //  }
             //  else {
-            //      <delegated expression referring to self0_vi, et al.>
+            //      <delegated expression referring to __self0_vi, et al.>
             //  }
             let all_match = cx.expr_match(sp, match_arg, match_arms);
             let arm_expr = cx.expr_if(sp, discriminant_test, all_match, Some(arm_expr));
@@ -1359,8 +1359,8 @@ impl<'a> MethodDef<'a> {
             // error-prone, since the catch-all as defined above would
             // generate code like this:
             //
-            //     _ => { let self0 = match *self { };
-            //            let self1 = match *arg_0 { };
+            //     _ => { let __self0 = match *self { };
+            //            let __self1 = match *__arg_0 { };
             //            <catch-all-expr> }
             //
             // Which is yields bindings for variables which type
@@ -1399,7 +1399,7 @@ impl<'a> MethodDef<'a> {
             // down to desired l-values, but we cannot actually deref
             // them when they are fed as r-values into a tuple
             // expression; here add a layer of borrowing, turning
-            // `(*self, *arg_0, ...)` into `(&*self, &*arg_0, ...)`.
+            // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
             let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg));
             let match_arg = cx.expr(sp, ast::ExprKind::Tup(borrowed_self_args));
             cx.expr_match(sp, match_arg, match_arms)
@@ -1613,8 +1613,8 @@ pub fn cs_fold<F>(use_foldl: bool,
 /// process the collected results. i.e.
 ///
 /// ```ignore
-/// f(cx, span, vec![self_1.method(arg_1_1, arg_2_1),
-///                  self_2.method(arg_1_2, arg_2_2)])
+/// f(cx, span, vec![self_1.method(__arg_1_1, __arg_2_1),
+///                  self_2.method(__arg_1_2, __arg_2_2)])
 /// ```
 #[inline]
 pub fn cs_same_method<F>(f: F,
@@ -1647,3 +1647,21 @@ pub fn cs_same_method<F>(f: F,
         }
     }
 }
+
+/// Return true if the type has no value fields
+/// (for an enum, no variant has any fields)
+pub fn is_type_without_fields(item: &Annotatable) -> bool {
+    if let Annotatable::Item(ref item) = *item {
+        match item.node {
+            ast::ItemKind::Enum(ref enum_def, _) => {
+                enum_def.variants.iter().all(|v| v.node.data.fields().is_empty())
+            }
+            ast::ItemKind::Struct(ref variant_data, _) => {
+                variant_data.fields().is_empty()
+            }
+            _ => false
+        }
+    } else {
+        false
+    }
+}
diff --git a/src/llvm b/src/llvm
-Subproject 63f3a1bfcd78355398a460712db25922247756b
+Subproject 25c7dc370359433ca02f29919dfa36f94432155
diff --git a/src/rustc/Cargo.lock b/src/rustc/Cargo.lock
index 0e7537a9cbd..03e7aaca0f7 100644
--- a/src/rustc/Cargo.lock
+++ b/src/rustc/Cargo.lock
@@ -78,6 +78,7 @@ dependencies = [
  "rbml 0.0.0",
  "rustc_back 0.0.0",
  "rustc_bitflags 0.0.0",
+ "rustc_const_eval 0.0.0",
  "rustc_data_structures 0.0.0",
  "rustc_front 0.0.0",
  "rustc_llvm 0.0.0",
@@ -112,6 +113,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "rustc_const_eval"
+version = "0.0.0"
+dependencies = [
+ "log 0.0.0",
+ "serialize 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
 name = "rustc_data_structures"
 version = "0.0.0"
 dependencies = [
@@ -187,6 +197,7 @@ dependencies = [
  "rustc 0.0.0",
  "rustc_back 0.0.0",
  "rustc_bitflags 0.0.0",
+ "rustc_const_eval 0.0.0",
  "rustc_front 0.0.0",
  "rustc_llvm 0.0.0",
  "serialize 0.0.0",
@@ -201,6 +212,7 @@ dependencies = [
  "log 0.0.0",
  "rustc 0.0.0",
  "rustc_back 0.0.0",
+ "rustc_const_eval 0.0.0",
  "rustc_data_structures 0.0.0",
  "rustc_front 0.0.0",
  "syntax 0.0.0",
@@ -230,6 +242,7 @@ version = "0.0.0"
 dependencies = [
  "log 0.0.0",
  "rustc 0.0.0",
+ "rustc_back 0.0.0",
  "rustc_bitflags 0.0.0",
  "rustc_front 0.0.0",
  "rustc_metadata 0.0.0",
@@ -270,6 +283,7 @@ dependencies = [
  "log 0.0.0",
  "rustc 0.0.0",
  "rustc_back 0.0.0",
+ "rustc_const_eval 0.0.0",
  "rustc_data_structures 0.0.0",
  "rustc_front 0.0.0",
  "rustc_llvm 0.0.0",
@@ -288,6 +302,7 @@ dependencies = [
  "log 0.0.0",
  "rustc 0.0.0",
  "rustc_back 0.0.0",
+ "rustc_const_eval 0.0.0",
  "rustc_front 0.0.0",
  "rustc_platform_intrinsics 0.0.0",
  "syntax 0.0.0",
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index 91cf4aa1da9..f488a517b23 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -164,6 +164,11 @@ extern "C" void LLVMRemoveFunctionAttrString(LLVMValueRef fn, unsigned index, co
                                           to_remove));
 }
 
+// enable fpmath flag UnsafeAlgebra
+extern "C" void LLVMRustSetHasUnsafeAlgebra(LLVMValueRef Instr) {
+    unwrap<Instruction>(Instr)->setHasUnsafeAlgebra(true);
+}
+
 extern "C" LLVMValueRef LLVMBuildAtomicLoad(LLVMBuilderRef B,
                                             LLVMValueRef source,
                                             const char* Name,
diff --git a/src/rustllvm/llvm-auto-clean-trigger b/src/rustllvm/llvm-auto-clean-trigger
index 502ac53978e..d6e8852cfec 100644
--- a/src/rustllvm/llvm-auto-clean-trigger
+++ b/src/rustllvm/llvm-auto-clean-trigger
@@ -1,4 +1,4 @@
 # If this file is modified, then llvm will be forcibly cleaned and then rebuilt.
 # The actual contents of this file do not matter, but to trigger a change on the
 # build bots then the contents should be changed so git updates the mtime.
-2016-03-15
+2016-03-18
diff --git a/src/test/codegen/adjustments.rs b/src/test/codegen/adjustments.rs
index a61fa84398e..20d04939434 100644
--- a/src/test/codegen/adjustments.rs
+++ b/src/test/codegen/adjustments.rs
@@ -11,6 +11,7 @@
 // compile-flags: -C no-prepopulate-passes
 
 #![crate_type = "lib"]
+#![feature(rustc_attrs)]
 
 // Hack to get the correct size for the length part in slices
 // CHECK: @helper([[USIZE:i[0-9]+]])
@@ -20,6 +21,7 @@ fn helper(_: usize) {
 
 // CHECK-LABEL: @no_op_slice_adjustment
 #[no_mangle]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 pub fn no_op_slice_adjustment(x: &[u8]) -> &[u8] {
     // We used to generate an extra alloca and memcpy for the block's trailing expression value, so
     // check that we copy directly to the return value slot
diff --git a/src/test/codegen/coercions.rs b/src/test/codegen/coercions.rs
index c8c9f5b407c..74c7192259a 100644
--- a/src/test/codegen/coercions.rs
+++ b/src/test/codegen/coercions.rs
@@ -11,12 +11,14 @@
 // compile-flags: -C no-prepopulate-passes
 
 #![crate_type = "lib"]
+#![feature(rustc_attrs)]
 
 static X: i32 = 5;
 
 // CHECK-LABEL: @raw_ptr_to_raw_ptr_noop
 // CHECK-NOT: alloca
 #[no_mangle]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 pub fn raw_ptr_to_raw_ptr_noop() -> *const i32{
     &X as *const i32
 }
@@ -24,6 +26,7 @@ pub fn raw_ptr_to_raw_ptr_noop() -> *const i32{
 // CHECK-LABEL: @reference_to_raw_ptr_noop
 // CHECK-NOT: alloca
 #[no_mangle]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 pub fn reference_to_raw_ptr_noop() -> *const i32 {
     &X
 }
diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs
index 6b4e626df92..ea4c932d435 100644
--- a/src/test/codegen/consts.rs
+++ b/src/test/codegen/consts.rs
@@ -11,6 +11,7 @@
 // compile-flags: -C no-prepopulate-passes
 
 #![crate_type = "lib"]
+#![feature(rustc_attrs)]
 
 // Below, these constants are defined as enum variants that by itself would
 // have a lower alignment than the enum type. Ensure that we mark them
@@ -39,18 +40,21 @@ pub static STATIC: E<i16, i32> = E::A(0);
 
 // CHECK-LABEL: @static_enum_const
 #[no_mangle]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 pub fn static_enum_const() -> E<i16, i32> {
    STATIC
 }
 
 // CHECK-LABEL: @inline_enum_const
 #[no_mangle]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 pub fn inline_enum_const() -> E<i8, i16> {
     E::A(0)
 }
 
 // CHECK-LABEL: @low_align_const
 #[no_mangle]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 pub fn low_align_const() -> E<i16, [i16; 3]> {
 // Check that low_align_const and high_align_const use the same constant
 // CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{[0-9]+}}, i8* {{.*}} [[LOW_HIGH:@const[0-9]+]]
@@ -59,6 +63,7 @@ pub fn low_align_const() -> E<i16, [i16; 3]> {
 
 // CHECK-LABEL: @high_align_const
 #[no_mangle]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 pub fn high_align_const() -> E<i16, i32> {
 // Check that low_align_const and high_align_const use the same constant
 // CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{[0-9]}}, i8* {{.*}} [[LOW_HIGH]]
diff --git a/src/test/codegen/drop.rs b/src/test/codegen/drop.rs
index 2ac8de6d802..83dd6a3b002 100644
--- a/src/test/codegen/drop.rs
+++ b/src/test/codegen/drop.rs
@@ -11,6 +11,7 @@
 // compile-flags: -C no-prepopulate-passes
 
 #![crate_type = "lib"]
+#![feature(rustc_attrs)]
 
 struct SomeUniqueName;
 
@@ -24,6 +25,7 @@ pub fn possibly_unwinding() {
 
 // CHECK-LABEL: @droppy
 #[no_mangle]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 pub fn droppy() {
 // Check that there are exactly 6 drop calls. The cleanups for the unwinding should be reused, so
 // that's one new drop call per call to possibly_unwinding(), and finally 3 drop calls for the
diff --git a/src/test/codegen/extern-functions.rs b/src/test/codegen/extern-functions.rs
index ff9d54e67e4..7ee31070b26 100644
--- a/src/test/codegen/extern-functions.rs
+++ b/src/test/codegen/extern-functions.rs
@@ -22,3 +22,8 @@ extern {
     #[unwind]
     fn unwinding_extern_fn();
 }
+
+pub unsafe fn force_declare() {
+    extern_fn();
+    unwinding_extern_fn();
+}
diff --git a/src/test/codegen/float_math.rs b/src/test/codegen/float_math.rs
new file mode 100644
index 00000000000..bc458d45446
--- /dev/null
+++ b/src/test/codegen/float_math.rs
@@ -0,0 +1,60 @@
+// Copyright 2016 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.
+
+// compile-flags: -C no-prepopulate-passes
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+
+use std::intrinsics::{fadd_fast, fsub_fast, fmul_fast, fdiv_fast, frem_fast};
+
+// CHECK-LABEL: @add
+#[no_mangle]
+pub fn add(x: f32, y: f32) -> f32 {
+// CHECK: fadd float
+// CHECK-NOT fast
+    x + y
+}
+
+// CHECK-LABEL: @addition
+#[no_mangle]
+pub fn addition(x: f32, y: f32) -> f32 {
+// CHECK: fadd fast float
+    unsafe {
+        fadd_fast(x, y)
+    }
+}
+
+// CHECK-LABEL: @subtraction
+#[no_mangle]
+pub fn subtraction(x: f32, y: f32) -> f32 {
+// CHECK: fsub fast float
+    unsafe {
+        fsub_fast(x, y)
+    }
+}
+
+// CHECK-LABEL: @multiplication
+#[no_mangle]
+pub fn multiplication(x: f32, y: f32) -> f32 {
+// CHECK: fmul fast float
+    unsafe {
+        fmul_fast(x, y)
+    }
+}
+
+// CHECK-LABEL: @division
+#[no_mangle]
+pub fn division(x: f32, y: f32) -> f32 {
+// CHECK: fdiv fast float
+    unsafe {
+        fdiv_fast(x, y)
+    }
+}
diff --git a/src/test/codegen/refs.rs b/src/test/codegen/refs.rs
index 08eec0045f7..36c83412e4f 100644
--- a/src/test/codegen/refs.rs
+++ b/src/test/codegen/refs.rs
@@ -11,6 +11,7 @@
 // compile-flags: -C no-prepopulate-passes
 
 #![crate_type = "lib"]
+#![feature(rustc_attrs)]
 
 // Hack to get the correct size for the length part in slices
 // CHECK: @helper([[USIZE:i[0-9]+]])
@@ -20,6 +21,7 @@ fn helper(_: usize) {
 
 // CHECK-LABEL: @ref_dst
 #[no_mangle]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 pub fn ref_dst(s: &[u8]) {
     // We used to generate an extra alloca and memcpy to ref the dst, so check that we copy
     // directly to the alloca for "x"
diff --git a/src/test/codegen/stores.rs b/src/test/codegen/stores.rs
index 5d2d47e1bf3..f849a6c9b18 100644
--- a/src/test/codegen/stores.rs
+++ b/src/test/codegen/stores.rs
@@ -11,6 +11,7 @@
 // compile-flags: -C no-prepopulate-passes
 
 #![crate_type = "lib"]
+#![feature(rustc_attrs)]
 
 pub struct Bytes {
   a: u8,
@@ -23,25 +24,20 @@ pub struct Bytes {
 // The array is stored as i32, but its alignment is lower, go with 1 byte to avoid target
 // dependent alignment
 #[no_mangle]
-pub fn small_array_alignment(x: &mut [i8; 4]) {
-// CHECK: [[VAR:%[0-9]+]] = load {{(\[4 x i8\]\*, )?}}[4 x i8]** %x
-// CHECK: [[VAR2:%[0-9]+]] = bitcast [4 x i8]* [[VAR]] to i32*
-// CHECK: store i32 %{{.*}}, i32* [[VAR2]], align 1
-    *x = [0; 4];
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
+pub fn small_array_alignment(x: &mut [i8; 4], y: [i8; 4]) {
+// CHECK: [[VAR:%[0-9]+]] = bitcast [4 x i8]* %y to i32*
+// CHECK: store i32 %{{.*}}, i32* [[VAR]], align 1
+    *x = y;
 }
 
 // CHECK-LABEL: small_struct_alignment
 // The struct is stored as i32, but its alignment is lower, go with 1 byte to avoid target
 // dependent alignment
 #[no_mangle]
-pub fn small_struct_alignment(x: &mut Bytes) {
-// CHECK: [[VAR:%[0-9]+]] = load {{(%Bytes\*, )?}}%Bytes** %x
-// CHECK: [[VAR2:%[0-9]+]] = bitcast %Bytes* [[VAR]] to i32*
-// CHECK: store i32 %{{.*}}, i32* [[VAR2]], align 1
-    *x = Bytes {
-        a: 0,
-        b: 0,
-        c: 0,
-        d: 0,
-    };
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
+pub fn small_struct_alignment(x: &mut Bytes, y: Bytes) {
+// CHECK: [[VAR:%[0-9]+]] = bitcast %Bytes* %y to i32*
+// CHECK: store i32 %{{.*}}, i32* [[VAR]], align 1
+    *x = y;
 }
diff --git a/src/test/compile-fail/asm-out-assign-imm.rs b/src/test/compile-fail/asm-out-assign-imm.rs
index 8c8451623d5..c1c72a5519b 100644
--- a/src/test/compile-fail/asm-out-assign-imm.rs
+++ b/src/test/compile-fail/asm-out-assign-imm.rs
@@ -23,6 +23,7 @@ pub fn main() {
     unsafe {
         asm!("mov $1, $0" : "=r"(x) : "r"(5));
         //~^ ERROR re-assignment of immutable variable `x`
+        //~| NOTE in this expansion of asm!
     }
     foo(x);
 }
diff --git a/src/test/compile-fail/blind-item-block-item-shadow.rs b/src/test/compile-fail/blind-item-block-item-shadow.rs
index d4adaa042b2..03af0d51ec2 100644
--- a/src/test/compile-fail/blind-item-block-item-shadow.rs
+++ b/src/test/compile-fail/blind-item-block-item-shadow.rs
@@ -14,7 +14,7 @@ fn main() {
     {
         struct Bar;
         use foo::Bar;
-        //~^ ERROR import `Bar` conflicts with type in this module
-        //~^^ ERROR import `Bar` conflicts with value in this module
+        //~^ ERROR a type named `Bar` has already been defined in this block
+        //~^^ ERROR a value named `Bar` has already been defined in this block
     }
 }
diff --git a/src/test/compile-fail/blind-item-item-shadow.rs b/src/test/compile-fail/blind-item-item-shadow.rs
index 9f21d6a9234..b08c78e9060 100644
--- a/src/test/compile-fail/blind-item-item-shadow.rs
+++ b/src/test/compile-fail/blind-item-item-shadow.rs
@@ -8,8 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-mod foo { pub mod foo {  } }
+mod foo { pub mod foo {  } } //~ NOTE previous definition of `foo` here
 
-use foo::foo; //~ ERROR import `foo` conflicts with existing submodule
+use foo::foo; //~ ERROR a module named `foo` has already been defined in this module
 
 fn main() {}
diff --git a/src/test/compile-fail/borrowck/borrowck-box-insensitivity.rs b/src/test/compile-fail/borrowck/borrowck-box-insensitivity.rs
index c980e77df6f..7c3d632078f 100644
--- a/src/test/compile-fail/borrowck/borrowck-box-insensitivity.rs
+++ b/src/test/compile-fail/borrowck/borrowck-box-insensitivity.rs
@@ -54,80 +54,97 @@ fn borrow_after_move() {
 fn move_after_borrow() {
     let a: Box<_> = box B { x: box 0, y: box 1 };
     let _x = &a.x;
+    //~^ NOTE borrow of `a.x` occurs here
     let _y = a.y; //~ ERROR cannot move
 }
 
 fn copy_after_mut_borrow() {
     let mut a: Box<_> = box A { x: box 0, y: 1 };
     let _x = &mut a.x;
+    //~^ NOTE borrow of `a.x` occurs here
     let _y = a.y; //~ ERROR cannot use
 }
 
 fn move_after_mut_borrow() {
     let mut a: Box<_> = box B { x: box 0, y: box 1 };
     let _x = &mut a.x;
+    //~^ NOTE borrow of `a.x` occurs here
     let _y = a.y; //~ ERROR cannot move
 }
 
 fn borrow_after_mut_borrow() {
     let mut a: Box<_> = box A { x: box 0, y: 1 };
     let _x = &mut a.x;
+    //~^ NOTE previous borrow of `a` occurs here (through borrowing `a.x`);
     let _y = &a.y; //~ ERROR cannot borrow
 }
+//~^ NOTE previous borrow ends here
 
 fn mut_borrow_after_borrow() {
     let mut a: Box<_> = box A { x: box 0, y: 1 };
     let _x = &a.x;
+    //~^ NOTE previous borrow of `a` occurs here (through borrowing `a.x`)
     let _y = &mut a.y; //~ ERROR cannot borrow
 }
+//~^ NOTE previous borrow ends here
 
 fn copy_after_move_nested() {
     let a: Box<_> = box C { x: box A { x: box 0, y: 1 }, y: 2 };
     let _x = a.x.x;
+    //~^ NOTE `a.x.x` moved here because it has type `Box<isize>`, which is moved by default
     let _y = a.y; //~ ERROR use of collaterally moved
 }
 
 fn move_after_move_nested() {
     let a: Box<_> = box D { x: box A { x: box 0, y: 1 }, y: box 2 };
     let _x = a.x.x;
+    //~^ NOTE `a.x.x` moved here because it has type `Box<isize>`, which is moved by default
     let _y = a.y; //~ ERROR use of collaterally moved
 }
 
 fn borrow_after_move_nested() {
     let a: Box<_> = box C { x: box A { x: box 0, y: 1 }, y: 2 };
     let _x = a.x.x;
+    //~^ NOTE `a.x.x` moved here because it has type `Box<isize>`, which is moved by default
     let _y = &a.y; //~ ERROR use of collaterally moved
 }
 
 fn move_after_borrow_nested() {
     let a: Box<_> = box D { x: box A { x: box 0, y: 1 }, y: box 2 };
     let _x = &a.x.x;
+    //~^ NOTE borrow of `a.x.x` occurs here
     let _y = a.y; //~ ERROR cannot move
 }
 
 fn copy_after_mut_borrow_nested() {
     let mut a: Box<_> = box C { x: box A { x: box 0, y: 1 }, y: 2 };
     let _x = &mut a.x.x;
+    //~^ NOTE borrow of `a.x.x` occurs here
     let _y = a.y; //~ ERROR cannot use
 }
 
 fn move_after_mut_borrow_nested() {
     let mut a: Box<_> = box D { x: box A { x: box 0, y: 1 }, y: box 2 };
     let _x = &mut a.x.x;
+    //~^ NOTE borrow of `a.x.x` occurs here
     let _y = a.y; //~ ERROR cannot move
 }
 
 fn borrow_after_mut_borrow_nested() {
     let mut a: Box<_> = box C { x: box A { x: box 0, y: 1 }, y: 2 };
     let _x = &mut a.x.x;
+    //~^ NOTE previous borrow of `a.x.x` occurs here; the mutable borrow prevents
     let _y = &a.y; //~ ERROR cannot borrow
 }
+//~^ NOTE previous borrow ends here
 
 fn mut_borrow_after_borrow_nested() {
     let mut a: Box<_> = box C { x: box A { x: box 0, y: 1 }, y: 2 };
     let _x = &a.x.x;
+    //~^ NOTE previous borrow of `a.x.x` occurs here; the immutable borrow prevents
     let _y = &mut a.y; //~ ERROR cannot borrow
 }
+//~^ NOTE previous borrow ends here
 
 fn main() {
     copy_after_move();
diff --git a/src/test/compile-fail/borrowck/borrowck-let-suggestion.rs b/src/test/compile-fail/borrowck/borrowck-let-suggestion.rs
index d760f3db0c2..7e9d448275d 100644
--- a/src/test/compile-fail/borrowck/borrowck-let-suggestion.rs
+++ b/src/test/compile-fail/borrowck/borrowck-let-suggestion.rs
@@ -12,6 +12,7 @@ fn f() {
     let x = [1].iter(); //~ ERROR borrowed value does not live long enough
                          //~^ NOTE reference must be valid for the block suffix following statement
                          //~^^ HELP consider using a `let` binding to increase its lifetime
+                         //~^^^ NOTE ...but borrowed value is only valid for the statement at 12:4
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck/borrowck-report-with-custom-diagnostic.rs b/src/test/compile-fail/borrowck/borrowck-report-with-custom-diagnostic.rs
index 61bf2c11a1f..2b1ff47ee3d 100644
--- a/src/test/compile-fail/borrowck/borrowck-report-with-custom-diagnostic.rs
+++ b/src/test/compile-fail/borrowck/borrowck-report-with-custom-diagnostic.rs
@@ -13,6 +13,7 @@ fn main() {
     // Original borrow ends at end of function
     let mut x = 1;
     let y = &mut x;
+    //~^ previous borrow of `x` occurs here; the mutable borrow prevents
     let z = &x; //~ ERROR cannot borrow
 }
 //~^ NOTE previous borrow ends here
@@ -23,6 +24,7 @@ fn foo() {
             // Original borrow ends at end of match arm
             let mut x = 1;
             let y = &x;
+            //~^ previous borrow of `x` occurs here; the immutable borrow prevents
             let z = &mut x; //~ ERROR cannot borrow
         }
      //~^ NOTE previous borrow ends here
@@ -35,6 +37,7 @@ fn bar() {
     || {
         let mut x = 1;
         let y = &mut x;
+        //~^ previous borrow of `x` occurs here; the mutable borrow prevents
         let z = &mut x; //~ ERROR cannot borrow
     };
  //~^ NOTE previous borrow ends here
diff --git a/src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs b/src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs
index a69ce0cb365..1a21b03a457 100644
--- a/src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs
+++ b/src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs
@@ -17,6 +17,7 @@ fn a() {
     let mut vec = [box 1, box 2, box 3];
     match vec {
         [box ref _a, _, _] => {
+        //~^ borrow of `vec[..]` occurs here
             vec[0] = box 4; //~ ERROR cannot assign
         }
     }
@@ -27,6 +28,7 @@ fn b() {
     let vec: &mut [Box<isize>] = &mut vec;
     match vec {
         [_b..] => {
+        //~^ borrow of `vec[..]` occurs here
             vec[0] = box 4; //~ ERROR cannot assign
         }
     }
@@ -48,6 +50,7 @@ fn c() {
         _ => {}
     }
     let a = vec[0]; //~ ERROR cannot move out
+    //~^ NOTE attempting to move value to here
 }
 
 fn d() {
@@ -59,6 +62,7 @@ fn d() {
         _ => {}
     }
     let a = vec[0]; //~ ERROR cannot move out
+    //~^ NOTE attempting to move value to here
 }
 
 fn e() {
diff --git a/src/test/compile-fail/cast-as-bool.rs b/src/test/compile-fail/cast-as-bool.rs
index 52a4950022d..4764ae380ff 100644
--- a/src/test/compile-fail/cast-as-bool.rs
+++ b/src/test/compile-fail/cast-as-bool.rs
@@ -12,4 +12,5 @@ fn main() {
     let u = 5 as bool;
     //~^ ERROR cannot cast as `bool`
     //~^^ HELP compare with zero instead
+    //~^^^ HELP run `rustc --explain E0054` to see a detailed explanation
 }
diff --git a/src/test/compile-fail/cast-rfc0401.rs b/src/test/compile-fail/cast-rfc0401.rs
index 8e129722722..b458334006a 100644
--- a/src/test/compile-fail/cast-rfc0401.rs
+++ b/src/test/compile-fail/cast-rfc0401.rs
@@ -61,9 +61,11 @@ fn main()
     let _ = 3 as bool;
     //~^ ERROR cannot cast as `bool`
     //~^^ HELP compare with zero
+    //~^^^ HELP run `rustc --explain E0054` to see a detailed explanation
     let _ = E::A as bool;
     //~^ ERROR cannot cast as `bool`
     //~^^ HELP compare with zero
+    //~^^^ HELP run `rustc --explain E0054` to see a detailed explanation
     let _ = 0x61u32 as char; //~ ERROR only `u8` can be cast
 
     let _ = false as f32;
@@ -90,6 +92,9 @@ fn main()
     let _ = v as *const [u8]; //~ ERROR cannot cast
     let _ = fat_v as *const Foo;
     //~^ ERROR `core::marker::Sized` is not implemented for the type `[u8]`
+    //~^^ HELP run `rustc --explain E0277` to see a detailed explanation
+    //~^^^ NOTE `[u8]` does not have a constant size known at compile-time
+    //~^^^^ NOTE required for the cast to the object type `Foo`
     let _ = foo as *const str; //~ ERROR casting
     let _ = foo as *mut str; //~ ERROR casting
     let _ = main as *mut str; //~ ERROR casting
@@ -102,6 +107,9 @@ fn main()
     let a : *const str = "hello";
     let _ = a as *const Foo;
     //~^ ERROR `core::marker::Sized` is not implemented for the type `str`
+    //~^^ HELP run `rustc --explain E0277` to see a detailed explanation
+    //~^^^ NOTE `str` does not have a constant size known at compile-time
+    //~^^^^ NOTE required for the cast to the object type `Foo`
 
     // check no error cascade
     let _ = main.f as *const u32; //~ ERROR attempted access of field
diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs
index 4d156a49192..882e4cb2d47 100644
--- a/src/test/compile-fail/const-err.rs
+++ b/src/test/compile-fail/const-err.rs
@@ -8,8 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[allow(exceeding_bitshifts)]
-#[deny(const_err)]
+#![feature(rustc_attrs)]
+#![allow(exceeding_bitshifts)]
+#![deny(const_err)]
 
 fn black_box<T>(_: T) {
     unimplemented!()
@@ -18,6 +19,7 @@ fn black_box<T>(_: T) {
 const BLA: u8 = 200u8 + 200u8;
 //~^ ERROR attempted to add with overflow
 
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let a = -std::i8::MIN;
     //~^ WARN attempted to negate with overflow
diff --git a/src/test/compile-fail/const-eval-overflow.rs b/src/test/compile-fail/const-eval-overflow.rs
index 3dfcb5bb29a..96013551ef4 100644
--- a/src/test/compile-fail/const-eval-overflow.rs
+++ b/src/test/compile-fail/const-eval-overflow.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(rustc_attrs)]
 #![allow(unused_imports)]
 
 // Note: the relevant lint pass here runs before some of the constant
@@ -103,6 +104,7 @@ const VALS_U64: (u64, u64, u64, u64) =
      //~^ ERROR attempted to multiply with overflow
      );
 
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     foo(VALS_I8);
     foo(VALS_I16);
diff --git a/src/test/compile-fail/enum-and-module-in-same-scope.rs b/src/test/compile-fail/enum-and-module-in-same-scope.rs
index 28e969b2149..a6793ee8b9f 100644
--- a/src/test/compile-fail/enum-and-module-in-same-scope.rs
+++ b/src/test/compile-fail/enum-and-module-in-same-scope.rs
@@ -8,11 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-enum Foo {
+enum Foo { //~ NOTE previous definition
     X
 }
 
-mod Foo { //~ ERROR duplicate definition of type or module `Foo`
+mod Foo { //~ ERROR a type named `Foo` has already been defined
     pub static X: isize = 42;
     fn f() { f() } // Check that this does not result in a resolution error
 }
diff --git a/src/test/compile-fail/fat-ptr-cast.rs b/src/test/compile-fail/fat-ptr-cast.rs
index c920b6c171e..b2fd11d4b39 100644
--- a/src/test/compile-fail/fat-ptr-cast.rs
+++ b/src/test/compile-fail/fat-ptr-cast.rs
@@ -18,6 +18,7 @@ fn main() {
     let q = a.as_ptr();
 
     a as usize; //~ ERROR casting
+    //~^ HELP cast through a raw pointer first
     b as usize; //~ ERROR non-scalar cast
     p as usize;
     //~^ ERROR casting
diff --git a/src/test/compile-fail/feature-gate-negate-unsigned.rs b/src/test/compile-fail/feature-gate-negate-unsigned.rs
index 546fc5e3c60..4330a4cbeab 100644
--- a/src/test/compile-fail/feature-gate-negate-unsigned.rs
+++ b/src/test/compile-fail/feature-gate-negate-unsigned.rs
@@ -23,5 +23,6 @@ const _MAX: usize = -1;
 fn main() {
     let x = 5u8;
     let _y = -x; //~ ERROR unary negation of unsigned integer
+    //~^ HELP use a cast or the `!` operator
     -S; // should not trigger the gate; issue 26840
 }
diff --git a/src/test/compile-fail/fn-item-type.rs b/src/test/compile-fail/fn-item-type.rs
index 2fbd1ddb1e6..949000dfed6 100644
--- a/src/test/compile-fail/fn-item-type.rs
+++ b/src/test/compile-fail/fn-item-type.rs
@@ -42,6 +42,6 @@ fn main() {
     // Make sure we distinguish between trait methods correctly.
     eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
     //~^ ERROR mismatched types
-    //~|  expected `fn() {Foo::foo}`
-    //~|  found `fn() {Foo::foo}`
+    //~|  expected `fn() {<u8 as Foo>::foo}`
+    //~|  found `fn() {<u16 as Foo>::foo}`
 }
diff --git a/src/test/compile-fail/infinite-instantiation.rs b/src/test/compile-fail/infinite-instantiation.rs
index 28806b6e2ab..d293a1816d8 100644
--- a/src/test/compile-fail/infinite-instantiation.rs
+++ b/src/test/compile-fail/infinite-instantiation.rs
@@ -31,7 +31,7 @@ impl<T:Clone> ToOpt for Option<T> {
 }
 
 fn function<T:ToOpt + Clone>(counter: usize, t: T) {
-//~^ ERROR reached the recursion limit during monomorphization
+//~^ ERROR reached the recursion limit while instantiating `function::<core::option::Option<
     if counter > 0 {
         function(counter - 1, t.to_option());
         // FIXME(#4287) Error message should be here. It should be
diff --git a/src/test/compile-fail/inherent-overlap.rs b/src/test/compile-fail/inherent-overlap.rs
index 5b014dbfd22..333a4ee04b2 100644
--- a/src/test/compile-fail/inherent-overlap.rs
+++ b/src/test/compile-fail/inherent-overlap.rs
@@ -11,10 +11,14 @@
 // Test that you cannot define items with the same name in overlapping inherent
 // impl blocks.
 
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
 struct Foo;
 
 impl Foo {
-    fn id() {} //~ ERROR E0201
+    fn id() {} //~ WARN duplicate definitions
+    //~^ WARN previously accepted
 }
 
 impl Foo {
@@ -24,7 +28,8 @@ impl Foo {
 struct Bar<T>(T);
 
 impl<T> Bar<T> {
-    fn bar(&self) {} //~ ERROR E0201
+    fn bar(&self) {} //~ WARN duplicate definitions
+    //~^ WARN previously accepted
 }
 
 impl Bar<u32> {
@@ -34,11 +39,13 @@ impl Bar<u32> {
 struct Baz<T>(T);
 
 impl<T: Copy> Baz<T> {
-    fn baz(&self) {} //~ ERROR E0201
+    fn baz(&self) {} //~ WARN duplicate definitions
+    //~^ WARN previously accepted
 }
 
 impl<T> Baz<Vec<T>> {
     fn baz(&self) {}
 }
 
-fn main() {}
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
diff --git a/src/test/compile-fail/intrinsic-return-address.rs b/src/test/compile-fail/intrinsic-return-address.rs
index b83f0f73436..906056896be 100644
--- a/src/test/compile-fail/intrinsic-return-address.rs
+++ b/src/test/compile-fail/intrinsic-return-address.rs
@@ -15,15 +15,10 @@ extern "rust-intrinsic" {
     fn return_address() -> *const u8;
 }
 
-unsafe fn f() {
-    let _ = return_address();
-    //~^ ERROR invalid use of `return_address` intrinsic: function does not use out pointer
-}
+unsafe fn f() { let _ = return_address(); }
+//~^ ERROR invalid use of `return_address` intrinsic: function does not use out pointer
 
-unsafe fn g() -> isize {
-    let _ = return_address();
-    //~^ ERROR invalid use of `return_address` intrinsic: function does not use out pointer
-    0
-}
+unsafe fn g() -> isize { let _ = return_address(); 0 }
+//~^ ERROR invalid use of `return_address` intrinsic: function does not use out pointer
 
 fn main() {}
diff --git a/src/test/compile-fail/issue-11714.rs b/src/test/compile-fail/issue-11714.rs
index 998576097a0..6dde59d4a2e 100644
--- a/src/test/compile-fail/issue-11714.rs
+++ b/src/test/compile-fail/issue-11714.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 fn blah() -> i32 { //~ ERROR not all control paths return a value
+    //~^ HELP run `rustc --explain E0269` to see a detailed explanation
     1
 
     ; //~ HELP consider removing this semicolon:
diff --git a/src/test/compile-fail/issue-13058.rs b/src/test/compile-fail/issue-13058.rs
index 8886dd80be5..503ccbd1cab 100644
--- a/src/test/compile-fail/issue-13058.rs
+++ b/src/test/compile-fail/issue-13058.rs
@@ -40,4 +40,5 @@ fn main() {
 //~| found `(_, _)`
 //~| expected &-ptr
 //~| found tuple
+//~| HELP run `rustc --explain E0308` to see a detailed explanation
 }
diff --git a/src/test/compile-fail/issue-13428.rs b/src/test/compile-fail/issue-13428.rs
index c771970650d..5b8ab08aefc 100644
--- a/src/test/compile-fail/issue-13428.rs
+++ b/src/test/compile-fail/issue-13428.rs
@@ -11,6 +11,7 @@
 // Regression test for #13428
 
 fn foo() -> String {  //~ ERROR not all control paths return a value
+    //~^ HELP run `rustc --explain E0269` to see a detailed explanation
     format!("Hello {}",
             "world")
     // Put the trailing semicolon on its own line to test that the
@@ -19,6 +20,7 @@ fn foo() -> String {  //~ ERROR not all control paths return a value
 }
 
 fn bar() -> String {  //~ ERROR not all control paths return a value
+    //~^ HELP run `rustc --explain E0269` to see a detailed explanation
     "foobar".to_string()
     ;   //~ HELP consider removing this semicolon
 }
diff --git a/src/test/compile-fail/issue-15260.rs b/src/test/compile-fail/issue-15260.rs
index 2228b6d3779..5ec82326d6c 100644
--- a/src/test/compile-fail/issue-15260.rs
+++ b/src/test/compile-fail/issue-15260.rs
@@ -25,6 +25,7 @@ fn main() {
 
     let Foo {
         a, //~ NOTE field `a` previously bound here
+        //~^ NOTE field `a` previously bound here
         a: _, //~ ERROR field `a` bound multiple times in the pattern
         a: x //~ ERROR field `a` bound multiple times in the pattern
     } = Foo { a: 29 };
diff --git a/src/test/compile-fail/issue-16747.rs b/src/test/compile-fail/issue-16747.rs
index dd7e8a869ec..0fdb5f74e82 100644
--- a/src/test/compile-fail/issue-16747.rs
+++ b/src/test/compile-fail/issue-16747.rs
@@ -19,6 +19,7 @@ struct List<'a, T: ListItem<'a>> {
 //~^ ERROR the parameter type `T` may not live long enough
 //~| HELP consider adding an explicit lifetime bound
 //~| NOTE ...so that the reference type `&'a [T]` does not outlive the data it points at
+//~| HELP run `rustc --explain E0309` to see a detailed explanation
 }
 impl<'a, T: ListItem<'a>> Collection for List<'a, T> {
     fn len(&self) -> usize {
diff --git a/src/test/compile-fail/issue-17263.rs b/src/test/compile-fail/issue-17263.rs
index f40d51f1d2f..2320bc02baf 100644
--- a/src/test/compile-fail/issue-17263.rs
+++ b/src/test/compile-fail/issue-17263.rs
@@ -23,3 +23,5 @@ fn main() {
     //~^ ERROR cannot borrow `foo` (here through borrowing `foo.b`) as immutable
     //~^^ NOTE previous borrow of `foo` occurs here (through borrowing `foo.a`)
 }
+//~^ NOTE previous borrow ends here
+//~^^ NOTE previous borrow ends here
diff --git a/src/test/compile-fail/issue-19498.rs b/src/test/compile-fail/issue-19498.rs
index 02b9c42b65b..87b79b5cd67 100644
--- a/src/test/compile-fail/issue-19498.rs
+++ b/src/test/compile-fail/issue-19498.rs
@@ -8,14 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use self::A; //~ ERROR import `A` conflicts with existing submodule
-use self::B; //~ ERROR import `B` conflicts with existing submodule
-mod A {}
-pub mod B {}
+use self::A; //~ NOTE previous import of `A` here
+use self::B; //~ NOTE previous import of `B` here
+mod A {} //~ ERROR a module named `A` has already been imported in this module
+pub mod B {} //~ ERROR a module named `B` has already been imported in this module
 
 mod C {
-    use C::D; //~ ERROR import `D` conflicts with existing submodule
-    mod D {}
+    use C::D; //~ NOTE previous import of `D` here
+    mod D {} //~ ERROR a module named `D` has already been imported in this module
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/issue-19707.rs b/src/test/compile-fail/issue-19707.rs
index 9affb44b744..814c1a4131d 100644
--- a/src/test/compile-fail/issue-19707.rs
+++ b/src/test/compile-fail/issue-19707.rs
@@ -13,8 +13,10 @@
 
 type foo = fn(&u8, &u8) -> &u8; //~ ERROR missing lifetime specifier
 //~^ HELP the signature does not say whether it is borrowed from argument 1 or argument 2
+//~^^ HELP run `rustc --explain E0106` to see a detailed explanation
 
 fn bar<F: Fn(&u8, &u8) -> &u8>(f: &F) {} //~ ERROR missing lifetime specifier
 //~^ HELP the signature does not say whether it is borrowed from argument 1 or argument 2
+//~^^ HELP run `rustc --explain E0106` to see a detailed explanation
 
 fn main() {}
diff --git a/src/test/compile-fail/issue-21221-1.rs b/src/test/compile-fail/issue-21221-1.rs
index 03dd7b6754a..c53d5a0922e 100644
--- a/src/test/compile-fail/issue-21221-1.rs
+++ b/src/test/compile-fail/issue-21221-1.rs
@@ -55,6 +55,8 @@ impl Mul for Foo {
 //~| HELP `mul1::Mul`
 //~| HELP `mul2::Mul`
 //~| HELP `std::ops::Mul`
+//~| HELP run `rustc --explain E0405` to see a detailed explanation
+//~| HELP you can import several candidates into scope (`use ...;`):
 }
 
 // BEFORE, we got:
@@ -75,17 +77,22 @@ fn getMul() -> Mul {
 //~| HELP `mul3::Mul`
 //~| HELP `mul4::Mul`
 //~| HELP and 2 other candidates
+//~| HELP run `rustc --explain E0412` to see a detailed explanation
+//~| HELP you can import several candidates into scope (`use ...;`):
 }
 
 // Let's also test what happens if the trait doesn't exist:
 impl ThisTraitReallyDoesntExistInAnyModuleReally for Foo {
 //~^ ERROR trait `ThisTraitReallyDoesntExistInAnyModuleReally` is not in scope
+//~^^ HELP run `rustc --explain E0405` to see a detailed explanation
+//~^^^ HELP no candidates by the name of `ThisTraitReallyDoesntExistInAnyModuleReally` found
 }
 
 // Let's also test what happens if there's just one alternative:
 impl Div for Foo {
 //~^ ERROR trait `Div` is not in scope
 //~| HELP `use std::ops::Div;`
+//~| HELP run `rustc --explain E0405` to see a detailed explanation
 }
 
 fn main() {
diff --git a/src/test/compile-fail/issue-21221-2.rs b/src/test/compile-fail/issue-21221-2.rs
index 8c2c27694d9..f031b62214d 100644
--- a/src/test/compile-fail/issue-21221-2.rs
+++ b/src/test/compile-fail/issue-21221-2.rs
@@ -28,3 +28,4 @@ struct Foo;
 impl T for Foo { }
 //~^ ERROR trait `T` is not in scope
 //~| HELP you can to import it into scope: `use foo::bar::T;`.
+//~| HELP run `rustc --explain E0405` to see a detailed explanation
diff --git a/src/test/compile-fail/issue-21221-3.rs b/src/test/compile-fail/issue-21221-3.rs
index ba66496b930..eee2c016451 100644
--- a/src/test/compile-fail/issue-21221-3.rs
+++ b/src/test/compile-fail/issue-21221-3.rs
@@ -25,6 +25,7 @@ struct Foo;
 impl OuterTrait for Foo {}
 //~^ ERROR trait `OuterTrait` is not in scope
 //~| HELP you can to import it into scope: `use issue_21221_3::outer::OuterTrait;`.
+//~| HELP run `rustc --explain E0405` to see a detailed explanation
 fn main() {
     println!("Hello, world!");
 }
diff --git a/src/test/compile-fail/issue-21221-4.rs b/src/test/compile-fail/issue-21221-4.rs
index 8d09510ae09..6a76264dff7 100644
--- a/src/test/compile-fail/issue-21221-4.rs
+++ b/src/test/compile-fail/issue-21221-4.rs
@@ -20,6 +20,7 @@ struct Foo;
 impl T for Foo {}
 //~^ ERROR trait `T` is not in scope
 //~| HELP you can to import it into scope: `use issue_21221_4::T;`.
+//~| HELP run `rustc --explain E0405` to see a detailed explanation
 
 fn main() {
     println!("Hello, world!");
diff --git a/src/test/compile-fail/issue-21546.rs b/src/test/compile-fail/issue-21546.rs
index 535630e0824..11d05ceb9a0 100644
--- a/src/test/compile-fail/issue-21546.rs
+++ b/src/test/compile-fail/issue-21546.rs
@@ -12,54 +12,54 @@
 
 #[allow(non_snake_case)]
 mod Foo { }
-//~^ NOTE first definition of type or module `Foo`
+//~^ NOTE previous definition of `Foo` here
 
 #[allow(dead_code)]
 struct Foo;
-//~^ ERROR duplicate definition of type or module `Foo`
-
+//~^ ERROR a module named `Foo` has already been defined in this module
 
 #[allow(non_snake_case)]
 mod Bar { }
-//~^ NOTE first definition of type or module `Bar`
+//~^ NOTE previous definition of `Bar` here
 
 #[allow(dead_code)]
 struct Bar(i32);
-//~^ ERROR duplicate definition of type or module `Bar`
+//~^ ERROR a module named `Bar` has already been defined
 
 
 #[allow(dead_code)]
 struct Baz(i32);
-//~^ NOTE first definition of type or module
+//~^ NOTE previous definition
 
 #[allow(non_snake_case)]
 mod Baz { }
-//~^ ERROR duplicate definition of type or module `Baz`
+//~^ ERROR a type named `Baz` has already been defined
 
 
 #[allow(dead_code)]
 struct Qux { x: bool }
-//~^ NOTE first definition of type or module
+//~^ NOTE previous definition
 
 #[allow(non_snake_case)]
 mod Qux { }
-//~^ ERROR duplicate definition of type or module `Qux`
+//~^ ERROR a type named `Qux` has already been defined
 
 
 #[allow(dead_code)]
 struct Quux;
-//~^ NOTE first definition of type or module
+//~^ NOTE previous definition
 
 #[allow(non_snake_case)]
 mod Quux { }
-//~^ ERROR duplicate definition of type or module `Quux`
+//~^ ERROR a type named `Quux` has already been defined
 
 
 #[allow(dead_code)]
 enum Corge { A, B }
+//~^ NOTE previous definition
 
 #[allow(non_snake_case)]
 mod Corge { }
-//~^ ERROR duplicate definition of type or module `Corge`
+//~^ ERROR a type named `Corge` has already been defined
 
 fn main() { }
diff --git a/src/test/compile-fail/issue-21600.rs b/src/test/compile-fail/issue-21600.rs
index f9a79dbb9c3..d9dcebfda6a 100644
--- a/src/test/compile-fail/issue-21600.rs
+++ b/src/test/compile-fail/issue-21600.rs
@@ -23,5 +23,8 @@ fn main() {
         call_it(|| x.gen());
         call_it(|| x.gen_mut()); //~ ERROR cannot borrow data mutably in a captured outer
         //~^ ERROR cannot borrow data mutably in a captured outer
+        //~^^ HELP run `rustc --explain E0387` to see a detailed explanation
+        //~^^^ HELP run `rustc --explain E0387` to see a detailed explanation
+        //~^^^^ HELP consider changing this closure to take self by mutable reference
     });
 }
diff --git a/src/test/compile-fail/issue-22638.rs b/src/test/compile-fail/issue-22638.rs
index e8c17ca0b36..0c8c2311dca 100644
--- a/src/test/compile-fail/issue-22638.rs
+++ b/src/test/compile-fail/issue-22638.rs
@@ -17,7 +17,7 @@ struct A (B);
 
 impl A {
     pub fn matches<F: Fn()>(&self, f: &F) {
-        //~^ ERROR reached the recursion limit during monomorphization
+        //~^ ERROR reached the recursion limit while instantiating `A::matches::<[closure
         let &A(ref term) = self;
         term.matches(f);
     }
diff --git a/src/test/compile-fail/issue-24036.rs b/src/test/compile-fail/issue-24036.rs
index 3c8a64eaf7d..28eebea749c 100644
--- a/src/test/compile-fail/issue-24036.rs
+++ b/src/test/compile-fail/issue-24036.rs
@@ -14,17 +14,20 @@ fn closure_to_loc() {
     //~^ ERROR mismatched types
     //~| NOTE no two closures, even if identical, have the same type
     //~| HELP consider boxing your closure and/or using it as a trait object
+    //~| HELP run `rustc --explain E0308` to see a detailed explanation
 }
 
 fn closure_from_match() {
     let x = match 1usize {
         1 => |c| c + 1,
         2 => |c| c - 1,
+        //~^ NOTE match arm with an incompatible type
         _ => |c| c - 1
     };
-    //~^^^^^ ERROR match arms have incompatible types
+    //~^^^^^^ ERROR match arms have incompatible types
     //~| NOTE no two closures, even if identical, have the same type
     //~| HELP consider boxing your closure and/or using it as a trait object
+    //~| HELP run `rustc --explain E0308` to see a detailed explanation
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/issue-24081.rs b/src/test/compile-fail/issue-24081.rs
index 11376cec14e..94fb3008289 100644
--- a/src/test/compile-fail/issue-24081.rs
+++ b/src/test/compile-fail/issue-24081.rs
@@ -8,16 +8,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::ops::Add; //~ ERROR import `Add` conflicts with type in this module
-use std::ops::Sub; //~ ERROR import `Sub` conflicts with type in this module
-use std::ops::Mul; //~ ERROR import `Mul` conflicts with type in this module
-use std::ops::Div; //~ ERROR import `Div` conflicts with existing submodule
-use std::ops::Rem; //~ ERROR import `Rem` conflicts with trait in this module
+use std::ops::Add; //~ NOTE previous import
+use std::ops::Sub; //~ NOTE previous import
+use std::ops::Mul; //~ NOTE previous import
+use std::ops::Div; //~ NOTE previous import
+use std::ops::Rem; //~ NOTE previous import
 
-type Add = bool;
-struct Sub { x: f32 }
-enum Mul { A, B }
-mod Div { }
-trait Rem { }
+type Add = bool; //~ ERROR a trait named `Add` has already been imported in this module
+struct Sub { x: f32 } //~ ERROR a trait named `Sub` has already been imported in this module
+enum Mul { A, B } //~ ERROR a trait named `Mul` has already been imported in this module
+mod Div { } //~ ERROR a trait named `Div` has already been imported in this module
+trait Rem {  } //~ ERROR a trait named `Rem` has already been imported in this module
 
 fn main() {}
diff --git a/src/test/compile-fail/issue-25385.rs b/src/test/compile-fail/issue-25385.rs
index 4aacb6840e9..51d7baaf3e9 100644
--- a/src/test/compile-fail/issue-25385.rs
+++ b/src/test/compile-fail/issue-25385.rs
@@ -21,4 +21,5 @@ fn main() {
 
     foo!(1i32.foo());
     //~^ ERROR no method named `foo` found for type `i32` in the current scope
+    //~^^ NOTE in this expansion of foo!
 }
diff --git a/src/test/compile-fail/issue-25386.rs b/src/test/compile-fail/issue-25386.rs
index 297d3aacfd5..b2775db5e75 100644
--- a/src/test/compile-fail/issue-25386.rs
+++ b/src/test/compile-fail/issue-25386.rs
@@ -35,6 +35,4 @@ macro_rules! check_ptr_exist {
 fn main() {
     let item = stuff::Item::new();
     println!("{}", check_ptr_exist!(item, name));
-    //~^ NOTE in this expansion of check_ptr_exist!
-    //~^^ NOTE in this expansion of check_ptr_exist!
 }
diff --git a/src/test/compile-fail/issue-25793.rs b/src/test/compile-fail/issue-25793.rs
index fd3e3186bc5..44b3ada97fe 100644
--- a/src/test/compile-fail/issue-25793.rs
+++ b/src/test/compile-fail/issue-25793.rs
@@ -27,6 +27,7 @@ impl HasInfo {
     fn get_other(&mut self) -> usize {
         self.get_size(width!(self))
         //~^ NOTE in this expansion of width!
+        //~| NOTE borrow of `*self` occurs here
     }
 }
 
diff --git a/src/test/compile-fail/issue-26638.rs b/src/test/compile-fail/issue-26638.rs
index 010803bf25b..9cbb64c2311 100644
--- a/src/test/compile-fail/issue-26638.rs
+++ b/src/test/compile-fail/issue-26638.rs
@@ -11,13 +11,18 @@
 fn parse_type(iter: Box<Iterator<Item=&str>+'static>) -> &str { iter.next() }
 //~^ ERROR missing lifetime specifier [E0106]
 //~^^ HELP 2 elided lifetimes
+//~^^^ HELP run `rustc --explain E0106` to see a detailed explanation
 
 fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() }
 //~^ ERROR missing lifetime specifier [E0106]
 //~^^ HELP lifetime cannot be derived
+//~^^^ HELP run `rustc --explain E0106` to see a detailed explanation
+//~^^^^ HELP consider giving it an explicit bounded or 'static lifetime
 
 fn parse_type_3() -> &str { unimplemented!() }
 //~^ ERROR missing lifetime specifier [E0106]
 //~^^ HELP no value for it to be borrowed from
+//~^^^ HELP run `rustc --explain E0106` to see a detailed explanation
+//~^^^^ HELP consider giving it a 'static lifetime
 
 fn main() {}
diff --git a/src/test/compile-fail/issue-28472.rs b/src/test/compile-fail/issue-28472.rs
index 23827c55a10..ca5bd9c6717 100644
--- a/src/test/compile-fail/issue-28472.rs
+++ b/src/test/compile-fail/issue-28472.rs
@@ -13,10 +13,10 @@
 extern {
   fn foo();
 
-  pub //~ ERROR duplicate definition
+  pub //~ ERROR a value named `foo` has already been defined
   fn foo();
 
-  pub //~ ERROR duplicate definition
+  pub //~ ERROR a value named `foo` has already been defined
   static mut foo: u32;
 }
 
diff --git a/src/test/compile-fail/issue-30302.rs b/src/test/compile-fail/issue-30302.rs
index 26508a47224..56f0b31da0d 100644
--- a/src/test/compile-fail/issue-30302.rs
+++ b/src/test/compile-fail/issue-30302.rs
@@ -18,8 +18,10 @@ fn is_empty<T>(s: Stack<T>) -> bool {
         Nil => true,
 //~^ WARN pattern binding `Nil` is named the same as one of the variants of the type `Stack`
 //~| HELP consider making the path in the pattern qualified: `Stack::Nil`
+//~| HELP run `rustc --explain E0170` to see a detailed explanation
         _ => false
 //~^ ERROR unreachable pattern
+//~| HELP run `rustc --explain E0001` to see a detailed explanation
     }
 }
 
diff --git a/src/test/compile-fail/issue-3099-a.rs b/src/test/compile-fail/issue-3099-a.rs
index 316199b6730..cc7de01b063 100644
--- a/src/test/compile-fail/issue-3099-a.rs
+++ b/src/test/compile-fail/issue-3099-a.rs
@@ -10,6 +10,6 @@
 
 enum a { b, c }
 
-enum a { d, e } //~ ERROR duplicate definition of type or module `a`
+enum a { d, e } //~ ERROR a type named `a` has already been defined in this module
 
 fn main() {}
diff --git a/src/test/compile-fail/issue-3099-b.rs b/src/test/compile-fail/issue-3099-b.rs
index b3f1b2a32ea..ae667341022 100644
--- a/src/test/compile-fail/issue-3099-b.rs
+++ b/src/test/compile-fail/issue-3099-b.rs
@@ -10,6 +10,6 @@
 
 pub mod a {}
 
-pub mod a {} //~ ERROR duplicate definition of type or module `a`
+pub mod a {} //~ ERROR a module named `a` has already been defined in this module
 
 fn main() {}
diff --git a/src/test/compile-fail/issue-3099.rs b/src/test/compile-fail/issue-3099.rs
index cdc377a0999..34bc21833e5 100644
--- a/src/test/compile-fail/issue-3099.rs
+++ b/src/test/compile-fail/issue-3099.rs
@@ -12,7 +12,7 @@ fn a(x: String) -> String {
     format!("First function with {}", x)
 }
 
-fn a(x: String, y: String) -> String { //~ ERROR duplicate definition of value `a`
+fn a(x: String, y: String) -> String { //~ ERROR a value named `a` has already been defined
     format!("Second function with {} and {}", x, y)
 }
 
diff --git a/src/test/compile-fail/issue-32323.rs b/src/test/compile-fail/issue-32323.rs
new file mode 100644
index 00000000000..e3461e52e1c
--- /dev/null
+++ b/src/test/compile-fail/issue-32323.rs
@@ -0,0 +1,18 @@
+// Copyright 2016 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.
+
+pub trait Tr<'a> {
+    type Out;
+}
+
+pub fn f<'a, T: Tr<'a>>() -> <T as Tr<'a>>::Out {}
+//~^ ERROR not all control paths return a value
+
+pub fn main() {}
diff --git a/src/test/compile-fail/issue-6702.rs b/src/test/compile-fail/issue-6702.rs
index 66ed817ffa8..6cb825a9be7 100644
--- a/src/test/compile-fail/issue-6702.rs
+++ b/src/test/compile-fail/issue-6702.rs
@@ -16,4 +16,5 @@ struct Monster {
 fn main() {
     let _m = Monster(); //~ ERROR `Monster` is the name of a struct or
     //~^ HELP did you mean to write: `Monster { /* fields */ }`?
+    //~| HELP run `rustc --explain E0423` to see a detailed explanation
 }
diff --git a/src/test/compile-fail/issue-6936.rs b/src/test/compile-fail/issue-6936.rs
index f5c879a07ee..c8021a22995 100644
--- a/src/test/compile-fail/issue-6936.rs
+++ b/src/test/compile-fail/issue-6936.rs
@@ -12,17 +12,17 @@ struct T;
 
 mod t1 {
     type Foo = ::T;
-    mod Foo {} //~ ERROR: duplicate definition of type or module `Foo`
+    mod Foo {} //~ ERROR: `Foo` has already been defined
 }
 
 mod t2 {
     type Foo = ::T;
-    struct Foo; //~ ERROR: duplicate definition of type or module `Foo`
+    struct Foo; //~ ERROR: `Foo` has already been defined
 }
 
 mod t3 {
     type Foo = ::T;
-    enum Foo {} //~ ERROR: duplicate definition of type or module `Foo`
+    enum Foo {} //~ ERROR: `Foo` has already been defined
 }
 
 mod t4 {
@@ -32,7 +32,7 @@ mod t4 {
 
 mod t5 {
     type Bar<T> = T;
-    mod Bar {} //~ ERROR: duplicate definition of type or module `Bar`
+    mod Bar {} //~ ERROR: `Bar` has already been defined
 }
 
 mod t6 {
diff --git a/src/test/compile-fail/issue-7044.rs b/src/test/compile-fail/issue-7044.rs
index 6f9fb2e61f2..06573bea13c 100644
--- a/src/test/compile-fail/issue-7044.rs
+++ b/src/test/compile-fail/issue-7044.rs
@@ -9,6 +9,6 @@
 // except according to those terms.
 
 static X: isize = 0;
-struct X; //~ ERROR error: duplicate definition of value `X`
+struct X; //~ ERROR `X` has already been defined
 
 fn main() {}
diff --git a/src/test/compile-fail/issue-8640.rs b/src/test/compile-fail/issue-8640.rs
index 5c1592e6547..e469e05a244 100644
--- a/src/test/compile-fail/issue-8640.rs
+++ b/src/test/compile-fail/issue-8640.rs
@@ -12,8 +12,8 @@
 
 mod foo {
     use baz::bar;
-    //~^ ERROR import `bar` conflicts with existing submodule
     mod bar {}
+    //~^ ERROR a module named `bar` has already been imported
 }
 mod baz { pub mod bar {} }
 
diff --git a/src/test/compile-fail/issue-8727.rs b/src/test/compile-fail/issue-8727.rs
index 72da6dcaa6c..525ec855762 100644
--- a/src/test/compile-fail/issue-8727.rs
+++ b/src/test/compile-fail/issue-8727.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:reached the recursion limit during monomorphization
+// error-pattern:reached the recursion limit while instantiating `generic::<core::option::Option<
 
 // Verify the compiler fails with an error on infinite function
 // recursions.
diff --git a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs
index 1fa7284f6b5..be4166e43b5 100644
--- a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs
+++ b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs
@@ -11,12 +11,15 @@
 // Lifetime annotation needed because we have no arguments.
 fn f() -> &isize {    //~ ERROR missing lifetime specifier
 //~^ HELP there is no value for it to be borrowed from
+//~| HELP run `rustc --explain E0106` to see a detailed explanation
+//~| HELP consider giving it a 'static lifetime
     panic!()
 }
 
 // Lifetime annotation needed because we have two by-reference parameters.
 fn g(_x: &isize, _y: &isize) -> &isize {    //~ ERROR missing lifetime specifier
 //~^ HELP the signature does not say whether it is borrowed from `_x` or `_y`
+//~| HELP run `rustc --explain E0106` to see a detailed explanation
     panic!()
 }
 
@@ -28,11 +31,14 @@ struct Foo<'a> {
 // and one on the reference.
 fn h(_x: &Foo) -> &isize { //~ ERROR missing lifetime specifier
 //~^ HELP the signature does not say which one of `_x`'s 2 elided lifetimes it is borrowed from
+//~| HELP run `rustc --explain E0106` to see a detailed explanation
     panic!()
 }
 
 fn i(_x: isize) -> &isize { //~ ERROR missing lifetime specifier
 //~^ HELP this function's return type contains a borrowed value
+//~| HELP run `rustc --explain E0106` to see a detailed explanation
+//~| HELP consider giving it an explicit bounded or 'static lifetime
     panic!()
 }
 
diff --git a/src/test/compile-fail/linkage2.rs b/src/test/compile-fail/linkage2.rs
index edbeebe882e..2a127d937ea 100644
--- a/src/test/compile-fail/linkage2.rs
+++ b/src/test/compile-fail/linkage2.rs
@@ -12,7 +12,7 @@
 
 extern {
     #[linkage = "extern_weak"] static foo: i32;
-    //~^ ERROR: must have type `*T`
+    //~^ ERROR: must have type `*const T` or `*mut T`
 }
 
 fn main() {
diff --git a/src/test/compile-fail/lint-group-style.rs b/src/test/compile-fail/lint-group-style.rs
index 59ab5be1572..393e46ab539 100644
--- a/src/test/compile-fail/lint-group-style.rs
+++ b/src/test/compile-fail/lint-group-style.rs
@@ -30,6 +30,7 @@ mod test {
     mod warn {
         #![warn(bad_style)]
         //~^ NOTE lint level defined here
+        //~| NOTE lint level defined here
 
         fn CamelCase() {} //~ WARN function `CamelCase` should have a snake case name
 
diff --git a/src/test/compile-fail/lint-no-drop-on-repr-extern.rs b/src/test/compile-fail/lint-no-drop-on-repr-extern.rs
index 2df57b08f28..91e5065517d 100644
--- a/src/test/compile-fail/lint-no-drop-on-repr-extern.rs
+++ b/src/test/compile-fail/lint-no-drop-on-repr-extern.rs
@@ -15,6 +15,8 @@
 
 #![feature(unsafe_no_drop_flag)]
 #![deny(drop_with_repr_extern)]
+//~^ NOTE lint level defined here
+//~| NOTE lint level defined here
 
 #[repr(C)] struct As { x: Box<i8> }
 #[repr(C)] enum Ae { Ae(Box<i8>), _None }
diff --git a/src/test/compile-fail/lint-unconditional-recursion.rs b/src/test/compile-fail/lint-unconditional-recursion.rs
index 6e3a00746f3..94e189aa47f 100644
--- a/src/test/compile-fail/lint-unconditional-recursion.rs
+++ b/src/test/compile-fail/lint-unconditional-recursion.rs
@@ -9,6 +9,20 @@
 // except according to those terms.
 
 #![deny(unconditional_recursion)]
+//~^ NOTE lint level defined here
+//~| NOTE lint level defined here
+//~| NOTE lint level defined here
+//~| NOTE lint level defined here
+//~| NOTE lint level defined here
+//~| NOTE lint level defined here
+//~| NOTE lint level defined here
+//~| NOTE lint level defined here
+//~| NOTE lint level defined here
+//~| NOTE lint level defined here
+//~| NOTE lint level defined here
+//~| NOTE lint level defined here
+//~| NOTE lint level defined here
+//~| NOTE lint level defined here
 #![allow(dead_code)]
 fn foo() { //~ ERROR function cannot return without recurring
     foo(); //~ NOTE recursive call site
diff --git a/src/test/compile-fail/liveness-return-last-stmt-semi.rs b/src/test/compile-fail/liveness-return-last-stmt-semi.rs
index a4eb1630afe..343622c5c1b 100644
--- a/src/test/compile-fail/liveness-return-last-stmt-semi.rs
+++ b/src/test/compile-fail/liveness-return-last-stmt-semi.rs
@@ -13,14 +13,18 @@
 macro_rules! test { () => { fn foo() -> i32 { 1; } } }
                                            //~^ ERROR not all control paths return a value
                                            //~^^ HELP consider removing this semicolon
+                                           //~^^^ HELP run `rustc --explain E0269` to see a
 
 fn no_return() -> i32 {} //~ ERROR  not all control paths return a value
+                         //~^ HELP run `rustc --explain E0269` to see a detailed explanation
 
 fn bar(x: u32) -> u32 { //~ ERROR  not all control paths return a value
+                        //~^ HELP run `rustc --explain E0269` to see a detailed explanation
     x * 2; //~ HELP consider removing this semicolon
 }
 
 fn baz(x: u64) -> u32 { //~ ERROR  not all control paths return a value
+                        //~^ HELP run `rustc --explain E0269` to see a detailed explanation
     x * 2;
 }
 
diff --git a/src/test/compile-fail/macro-backtrace-nested.rs b/src/test/compile-fail/macro-backtrace-nested.rs
index a429681bb21..c935ccef055 100644
--- a/src/test/compile-fail/macro-backtrace-nested.rs
+++ b/src/test/compile-fail/macro-backtrace-nested.rs
@@ -21,9 +21,11 @@ macro_rules! call_nested_expr {
 
 macro_rules! call_nested_expr_sum {
     () => { 1 + nested_expr!(); } //~ ERROR unresolved name
+                                  //~^ NOTE in this expansion of nested_expr!
 }
 
 fn main() {
     1 + call_nested_expr!(); //~ ERROR unresolved name
+                             //~^ NOTE in this expansion of call_nested_expr!
     call_nested_expr_sum!(); //~ NOTE in this expansion of
 }
diff --git a/src/test/compile-fail/macro-backtrace-println.rs b/src/test/compile-fail/macro-backtrace-println.rs
index 294892662d4..a485b9056de 100644
--- a/src/test/compile-fail/macro-backtrace-println.rs
+++ b/src/test/compile-fail/macro-backtrace-println.rs
@@ -22,7 +22,8 @@ macro_rules! myprint {
 
 macro_rules! myprintln {
     ($fmt:expr) => (myprint!(concat!($fmt, "\n"))); //~ ERROR invalid reference to argument `0`
-                                                    //~^ NOTE in this expansion of
+                                                    //~^ NOTE in this expansion of myprint!
+                                                    //~^^ NOTE in this expansion of concat!
 }
 
 fn main() {
diff --git a/src/test/compile-fail/method-suggestion-no-duplication.rs b/src/test/compile-fail/method-suggestion-no-duplication.rs
index e6f3c8ab317..c8c1447fea3 100644
--- a/src/test/compile-fail/method-suggestion-no-duplication.rs
+++ b/src/test/compile-fail/method-suggestion-no-duplication.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 // issue #21405
+// ignore-tidy-linelength
 
 struct Foo;
 
@@ -19,4 +20,5 @@ fn main() {
     //~^ ERROR no method named `is_empty` found
     //~^^ HELP #1: `core::slice::SliceExt`
     //~^^^ HELP #2: `core::str::StrExt`
+    //~^^^^ HELP items from traits can only be used if the trait is implemented and in scope; the following traits define an item `is_empty`, perhaps you need to implement one of them:
 }
diff --git a/src/test/compile-fail/no-std-inject.rs b/src/test/compile-fail/no-std-inject.rs
index 21675434e24..f2a27dc528e 100644
--- a/src/test/compile-fail/no-std-inject.rs
+++ b/src/test/compile-fail/no-std-inject.rs
@@ -10,7 +10,7 @@
 
 #![no_std]
 
-extern crate core; //~ ERROR: an external crate named `core` has already
+extern crate core; //~ ERROR: an extern crate named `core` has already
 extern crate std;
 
 fn main() {}
diff --git a/src/test/compile-fail/object-safety-generics.rs b/src/test/compile-fail/object-safety-generics.rs
index 8e3161ef884..5097e3d7b10 100644
--- a/src/test/compile-fail/object-safety-generics.rs
+++ b/src/test/compile-fail/object-safety-generics.rs
@@ -29,6 +29,7 @@ fn make_bar<T:Bar>(t: &T) -> &Bar {
 
 fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
     //~^ ERROR E0038
+    //~^^ NOTE method `bar` has generic type parameters
     t as &Bar
 }
 
diff --git a/src/test/compile-fail/on-unimplemented.rs b/src/test/compile-fail/on-unimplemented.rs
index c4eb467c4f9..f386e7cdd5a 100644
--- a/src/test/compile-fail/on-unimplemented.rs
+++ b/src/test/compile-fail/on-unimplemented.rs
@@ -34,6 +34,8 @@ pub fn main() {
     let y: Option<Vec<u8>> = collect(x.iter()); // this should give approximately the same error for x.iter().collect()
     //~^ ERROR
     //~^^ NOTE a collection of type `core::option::Option<collections::vec::Vec<u8>>` cannot be built from an iterator over elements of type `&u8`
+    //~^^^ NOTE required by `collect`
     let x: String = foobar(); //~ ERROR
     //~^ NOTE test error `collections::string::String` with `u8` `_` `u32`
+    //~^^ NOTE required by `foobar`
 }
diff --git a/src/test/compile-fail/recursion_limit.rs b/src/test/compile-fail/recursion_limit.rs
index 368269999a2..226a6d57ddb 100644
--- a/src/test/compile-fail/recursion_limit.rs
+++ b/src/test/compile-fail/recursion_limit.rs
@@ -43,4 +43,15 @@ fn main() {
     is_send::<A>();
     //~^ ERROR overflow evaluating
     //~| NOTE consider adding a `#![recursion_limit="20"]` attribute to your crate
+    //~| NOTE required because it appears within the type `A`
+    //~| NOTE required because it appears within the type `B`
+    //~| NOTE required because it appears within the type `C`
+    //~| NOTE required because it appears within the type `D`
+    //~| NOTE required because it appears within the type `E`
+    //~| NOTE required because it appears within the type `F`
+    //~| NOTE required because it appears within the type `G`
+    //~| NOTE required because it appears within the type `H`
+    //~| NOTE required because it appears within the type `I`
+    //~| NOTE required because it appears within the type `J`
+    //~| NOTE required by `is_send`
 }
diff --git a/src/test/compile-fail/ref-suggestion.rs b/src/test/compile-fail/ref-suggestion.rs
index 815f7526632..4625669d5ec 100644
--- a/src/test/compile-fail/ref-suggestion.rs
+++ b/src/test/compile-fail/ref-suggestion.rs
@@ -14,12 +14,14 @@ fn main() {
     //~^ HELP use a `ref` binding as shown
     //~| SUGGESTION let ref y = x;
     x; //~ ERROR use of moved value
+    //~^ HELP run `rustc --explain E0382` to see a detailed explanation
 
     let x = vec![1];
     let mut y = x;
     //~^ HELP use a `ref` binding as shown
     //~| SUGGESTION let ref mut y = x;
     x; //~ ERROR use of moved value
+    //~^ HELP run `rustc --explain E0382` to see a detailed explanation
 
     let x = (Some(vec![1]), ());
 
@@ -30,4 +32,5 @@ fn main() {
         _ => {},
     }
     x; //~ ERROR use of partially moved value
+    //~^ HELP run `rustc --explain E0382` to see a detailed explanation
 }
diff --git a/src/test/compile-fail/resolve-conflict-extern-crate-vs-extern-crate.rs b/src/test/compile-fail/resolve-conflict-extern-crate-vs-extern-crate.rs
index af3ee7f3537..c05d0cc1b0e 100644
--- a/src/test/compile-fail/resolve-conflict-extern-crate-vs-extern-crate.rs
+++ b/src/test/compile-fail/resolve-conflict-extern-crate-vs-extern-crate.rs
@@ -9,6 +9,6 @@
 // except according to those terms.
 
 extern crate std;
-//~^ ERROR an external crate named `std` has already been imported
+//~^ ERROR an extern crate named `std` has already been imported
 
 fn main(){}
diff --git a/src/test/compile-fail/resolve-conflict-import-vs-extern-crate.rs b/src/test/compile-fail/resolve-conflict-import-vs-extern-crate.rs
index 0c601a81178..6cbc728c03e 100644
--- a/src/test/compile-fail/resolve-conflict-import-vs-extern-crate.rs
+++ b/src/test/compile-fail/resolve-conflict-import-vs-extern-crate.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::slice as std; //~ ERROR import `std` conflicts with imported crate
+use std::slice as std; //~ ERROR an extern crate named `std` has already been imported
 
 fn main() {
 }
diff --git a/src/test/compile-fail/resolve-conflict-item-vs-extern-crate.rs b/src/test/compile-fail/resolve-conflict-item-vs-extern-crate.rs
index 07f80cf03d1..b0954ee1571 100644
--- a/src/test/compile-fail/resolve-conflict-item-vs-extern-crate.rs
+++ b/src/test/compile-fail/resolve-conflict-item-vs-extern-crate.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 fn std() {}
-mod std {}    //~ ERROR the name `std` conflicts with an external crate
+mod std {}    //~ ERROR an extern crate named `std` has already been imported
 
 fn main() {
 }
diff --git a/src/test/compile-fail/resolve-conflict-item-vs-import.rs b/src/test/compile-fail/resolve-conflict-item-vs-import.rs
index 1edf815ecae..dbd1ecf44fd 100644
--- a/src/test/compile-fail/resolve-conflict-item-vs-import.rs
+++ b/src/test/compile-fail/resolve-conflict-item-vs-import.rs
@@ -9,9 +9,10 @@
 // except according to those terms.
 
 use std::mem::transmute;
-//~^ ERROR import `transmute` conflicts with value in this module
+//~^ NOTE previous import of `transmute` here
 
 fn transmute() {}
+//~^ ERROR a value named `transmute` has already been imported in this module
 
 fn main() {
 }
diff --git a/src/test/compile-fail/resolve-conflict-type-vs-import.rs b/src/test/compile-fail/resolve-conflict-type-vs-import.rs
index 45b0314d2c0..aa7e47e223f 100644
--- a/src/test/compile-fail/resolve-conflict-type-vs-import.rs
+++ b/src/test/compile-fail/resolve-conflict-type-vs-import.rs
@@ -9,9 +9,9 @@
 // except according to those terms.
 
 use std::slice::Iter;
-//~^ ERROR import `Iter` conflicts with type in this module
 
 struct Iter;
+//~^ ERROR a type named `Iter` has already been imported in this module
 
 fn main() {
 }
diff --git a/src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs b/src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs
index 35c368f4cbe..33954d23b19 100644
--- a/src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs
+++ b/src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(repr_simd, platform_intrinsics)]
+#![feature(repr_simd, platform_intrinsics, rustc_attrs)]
 #![allow(non_camel_case_types)]
 #[repr(simd)]
 #[derive(Copy, Clone)]
@@ -34,6 +34,7 @@ extern "platform-intrinsic" {
     fn simd_xor<T>(x: T, y: T) -> T;
 }
 
+#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 fn main() {
     let x = i32x4(0, 0, 0, 0);
     let y = u32x4(0, 0, 0, 0);
diff --git a/src/test/compile-fail/simd-intrinsic-generic-cast.rs b/src/test/compile-fail/simd-intrinsic-generic-cast.rs
index 4999b790b13..cb3bed7209e 100644
--- a/src/test/compile-fail/simd-intrinsic-generic-cast.rs
+++ b/src/test/compile-fail/simd-intrinsic-generic-cast.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(repr_simd, platform_intrinsics)]
+#![feature(repr_simd, platform_intrinsics, rustc_attrs)]
 
 #[repr(simd)]
 #[derive(Copy, Clone)]
@@ -35,6 +35,7 @@ extern "platform-intrinsic" {
     fn simd_cast<T, U>(x: T) -> U;
 }
 
+#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 fn main() {
     let x = i32x4(0, 0, 0, 0);
 
diff --git a/src/test/compile-fail/simd-intrinsic-generic-comparison.rs b/src/test/compile-fail/simd-intrinsic-generic-comparison.rs
index 617b03a8711..0e7b2bd4904 100644
--- a/src/test/compile-fail/simd-intrinsic-generic-comparison.rs
+++ b/src/test/compile-fail/simd-intrinsic-generic-comparison.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(repr_simd, platform_intrinsics)]
+#![feature(repr_simd, platform_intrinsics, rustc_attrs)]
 
 #[repr(simd)]
 #[derive(Copy, Clone)]
@@ -29,6 +29,7 @@ extern "platform-intrinsic" {
     fn simd_ge<T, U>(x: T, y: T) -> U;
 }
 
+#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 fn main() {
     let x = i32x4(0, 0, 0, 0);
 
diff --git a/src/test/compile-fail/simd-intrinsic-generic-elements.rs b/src/test/compile-fail/simd-intrinsic-generic-elements.rs
index b0198c411d5..1f4cc72ffe7 100644
--- a/src/test/compile-fail/simd-intrinsic-generic-elements.rs
+++ b/src/test/compile-fail/simd-intrinsic-generic-elements.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(repr_simd, platform_intrinsics)]
+#![feature(repr_simd, platform_intrinsics, rustc_attrs)]
 
 #[repr(simd)]
 #[derive(Copy, Clone)]
@@ -56,6 +56,7 @@ extern "platform-intrinsic" {
     fn simd_shuffle8<T, U>(x: T, y: T, idx: [u32; 8]) -> U;
 }
 
+#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 fn main() {
     let x = i32x4(0, 0, 0, 0);
 
diff --git a/src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs b/src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs
index 1d04679fd11..8877377a6ec 100644
--- a/src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs
+++ b/src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs
@@ -27,46 +27,54 @@ fn h1() -> i32 {
     a.I
         //~^ ERROR E0425
         //~| HELP To reference an item from the `a` module, use `a::I`
+        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h2() -> i32 {
     a.g()
         //~^ ERROR E0425
         //~| HELP To call a function from the `a` module, use `a::g(..)`
+        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h3() -> i32 {
     a.b.J
         //~^ ERROR E0425
         //~| HELP To reference an item from the `a` module, use `a::b`
+        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h4() -> i32 {
     a::b.J
         //~^ ERROR E0425
         //~| HELP To reference an item from the `a::b` module, use `a::b::J`
+        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h5() -> i32 {
     a.b.f()
         //~^ ERROR E0425
         //~| HELP To reference an item from the `a` module, use `a::b`
+        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h6() -> i32 {
     a::b.f()
         //~^ ERROR E0425
         //~| HELP To call a function from the `a::b` module, use `a::b::f(..)`
+        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h7() {
     a::b
         //~^ ERROR E0425
         //~| HELP Module `a::b` cannot be the value of an expression
+        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
 
 fn h8() -> i32 {
     a::b()
         //~^ ERROR E0425
         //~| HELP No function corresponds to `a::b(..)`
+        //~| HELP run `rustc --explain E0425` to see a detailed explanation
 }
diff --git a/src/test/compile-fail/suggest-private-fields.rs b/src/test/compile-fail/suggest-private-fields.rs
index 8bc8a7a60bd..9c61f618e69 100644
--- a/src/test/compile-fail/suggest-private-fields.rs
+++ b/src/test/compile-fail/suggest-private-fields.rs
@@ -25,6 +25,7 @@ fn main () {
         aa: 20, //~ ERROR structure `xc::B` has no field named `aa`
         //~^ HELP did you mean `a`?
         bb: 20, //~ ERROR structure `xc::B` has no field named `bb`
+        //~^ HELP did you mean `a`?
     };
     // local crate struct
     let l = A {
diff --git a/src/test/compile-fail/trait-duplicate-methods.rs b/src/test/compile-fail/trait-duplicate-methods.rs
index ba8101d16ab..41700b25bbb 100644
--- a/src/test/compile-fail/trait-duplicate-methods.rs
+++ b/src/test/compile-fail/trait-duplicate-methods.rs
@@ -9,8 +9,8 @@
 // except according to those terms.
 
 trait Foo {
-    fn orange(&self);
-    fn orange(&self);   //~ ERROR error: duplicate definition of value `orange`
+    fn orange(&self); //~ NOTE previous definition of `orange` here
+    fn orange(&self); //~ ERROR a value named `orange` has already been defined in this trait
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/trait-object-reference-without-parens-suggestion.rs b/src/test/compile-fail/trait-object-reference-without-parens-suggestion.rs
index fc2ed83b272..29360e58b5b 100644
--- a/src/test/compile-fail/trait-object-reference-without-parens-suggestion.rs
+++ b/src/test/compile-fail/trait-object-reference-without-parens-suggestion.rs
@@ -13,8 +13,10 @@ fn main() {
     //~^ ERROR expected a path
     //~| HELP try adding parentheses
     //~| SUGGESTION let _: &(Copy + 'static);
+    //~| HELP run `rustc --explain E0178` to see a detailed explanation
     let _: &'static Copy + 'static;
     //~^ ERROR expected a path
     //~| HELP try adding parentheses
     //~| SUGGESTION let _: &'static (Copy + 'static);
+    //~| HELP run `rustc --explain E0178` to see a detailed explanation
 }
diff --git a/src/test/compile-fail/unresolved-extern-mod-suggestion.rs b/src/test/compile-fail/unresolved-extern-mod-suggestion.rs
index c2ee62c195c..cc328d8c9e9 100644
--- a/src/test/compile-fail/unresolved-extern-mod-suggestion.rs
+++ b/src/test/compile-fail/unresolved-extern-mod-suggestion.rs
@@ -10,6 +10,6 @@
 
 extern crate core;
 use core;
-//~^ ERROR import `core` conflicts with imported crate in this module
+//~^ ERROR an extern crate named `core` has already been imported in this module
 
 fn main() {}
diff --git a/src/test/compile-fail/use-mod.rs b/src/test/compile-fail/use-mod.rs
index 9cc3c92e2e3..bbb063770c1 100644
--- a/src/test/compile-fail/use-mod.rs
+++ b/src/test/compile-fail/use-mod.rs
@@ -11,6 +11,7 @@
 use foo::bar::{
     self,
 //~^ ERROR `self` import can only appear once in the list
+//~^^ NOTE previous import of `bar` here
     Bar,
     self
 //~^ NOTE another `self` import appears here
diff --git a/src/test/compile-fail/variance-unused-type-param.rs b/src/test/compile-fail/variance-unused-type-param.rs
index 862d842d62c..f7fed32cb5a 100644
--- a/src/test/compile-fail/variance-unused-type-param.rs
+++ b/src/test/compile-fail/variance-unused-type-param.rs
@@ -16,15 +16,18 @@
 struct SomeStruct<A> { x: u32 }
 //~^ ERROR parameter `A` is never used
 //~| HELP PhantomData
+//~| HELP run `rustc --explain E0392` to see a detailed explanation
 
 enum SomeEnum<A> { Nothing }
 //~^ ERROR parameter `A` is never used
 //~| HELP PhantomData
+//~| HELP run `rustc --explain E0392` to see a detailed explanation
 
 // Here T might *appear* used, but in fact it isn't.
 enum ListCell<T> {
 //~^ ERROR parameter `T` is never used
 //~| HELP PhantomData
+//~| HELP run `rustc --explain E0392` to see a detailed explanation
     Cons(Box<ListCell<T>>),
     Nil
 }
diff --git a/src/test/compile-fail/variant-namespacing.rs b/src/test/compile-fail/variant-namespacing.rs
index 75869d700d3..a8bb94b78fc 100644
--- a/src/test/compile-fail/variant-namespacing.rs
+++ b/src/test/compile-fail/variant-namespacing.rs
@@ -10,22 +10,6 @@
 
 // aux-build:variant-namespacing.rs
 
-extern crate variant_namespacing;
-pub use variant_namespacing::XE::*;
-//~^ ERROR import `XStruct` conflicts with type in this module
-//~| ERROR import `XStruct` conflicts with value in this module
-//~| ERROR import `XTuple` conflicts with type in this module
-//~| ERROR import `XTuple` conflicts with value in this module
-//~| ERROR import `XUnit` conflicts with type in this module
-//~| ERROR import `XUnit` conflicts with value in this module
-pub use E::*;
-//~^ ERROR import `Struct` conflicts with type in this module
-//~| ERROR import `Struct` conflicts with value in this module
-//~| ERROR import `Tuple` conflicts with type in this module
-//~| ERROR import `Tuple` conflicts with value in this module
-//~| ERROR import `Unit` conflicts with type in this module
-//~| ERROR import `Unit` conflicts with value in this module
-
 enum E {
     Struct { a: u8 },
     Tuple(u8),
@@ -46,4 +30,20 @@ const XStruct: u8 = 0;
 const XTuple: u8 = 0;
 const XUnit: u8 = 0;
 
+extern crate variant_namespacing;
+pub use variant_namespacing::XE::*;
+//~^ ERROR `XStruct` has already been defined
+//~| ERROR `XStruct` has already been defined
+//~| ERROR `XTuple` has already been defined
+//~| ERROR `XTuple` has already been defined
+//~| ERROR `XUnit` has already been defined
+//~| ERROR `XUnit` has already been defined
+pub use E::*;
+//~^ ERROR `Struct` has already been defined
+//~| ERROR `Struct` has already been defined
+//~| ERROR `Tuple` has already been defined
+//~| ERROR `Tuple` has already been defined
+//~| ERROR `Unit` has already been defined
+//~| ERROR `Unit` has already been defined
+
 fn main() {}
diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp
index 0347631aeb3..fedb68a26af 100644
--- a/src/test/pretty/issue-4264.pp
+++ b/src/test/pretty/issue-4264.pp
@@ -41,37 +41,37 @@ pub fn bar() {
     ((::std::fmt::format as
          fn(core::fmt::Arguments<'_>) -> collections::string::String {collections::fmt::format})(((::std::fmt::Arguments::new_v1
                                                                                                       as
-                                                                                                      fn(&[&str], &[core::fmt::ArgumentV1<'_>]) -> core::fmt::Arguments<'_> {core::fmt::Arguments<'a>::new_v1})(({
-                                                                                                                                                                                                                     static __STATIC_FMTSTR:
-                                                                                                                                                                                                                            &'static [&'static str]
-                                                                                                                                                                                                                            =
-                                                                                                                                                                                                                         (&([("test"
+                                                                                                      fn(&[&str], &[core::fmt::ArgumentV1<'_>]) -> core::fmt::Arguments<'_> {core::fmt::Arguments<'a><'_>::new_v1})(({
+                                                                                                                                                                                                                         static __STATIC_FMTSTR:
+                                                                                                                                                                                                                                &'static [&'static str]
+                                                                                                                                                                                                                                =
+                                                                                                                                                                                                                             (&([("test"
+                                                                                                                                                                                                                                     as
+                                                                                                                                                                                                                                     &'static str)]
+                                                                                                                                                                                                                                   as
+                                                                                                                                                                                                                                   [&'static str; 1])
                                                                                                                                                                                                                                  as
-                                                                                                                                                                                                                                 &'static str)]
-                                                                                                                                                                                                                               as
-                                                                                                                                                                                                                               [&'static str; 1])
-                                                                                                                                                                                                                             as
-                                                                                                                                                                                                                             &'static [&'static str; 1]);
-                                                                                                                                                                                                                     (__STATIC_FMTSTR
-                                                                                                                                                                                                                         as
-                                                                                                                                                                                                                         &'static [&'static str])
-                                                                                                                                                                                                                 }
-                                                                                                                                                                                                                    as
-                                                                                                                                                                                                                    &[&str]),
-                                                                                                                                                                                                                (&(match (()
+                                                                                                                                                                                                                                 &'static [&'static str; 1]);
+                                                                                                                                                                                                                         (__STATIC_FMTSTR
                                                                                                                                                                                                                              as
-                                                                                                                                                                                                                             ())
-                                                                                                                                                                                                                       {
-                                                                                                                                                                                                                       ()
-                                                                                                                                                                                                                       =>
-                                                                                                                                                                                                                       ([]
-                                                                                                                                                                                                                           as
-                                                                                                                                                                                                                           [core::fmt::ArgumentV1<'_>; 0]),
-                                                                                                                                                                                                                   }
-                                                                                                                                                                                                                      as
-                                                                                                                                                                                                                      [core::fmt::ArgumentV1<'_>; 0])
-                                                                                                                                                                                                                    as
-                                                                                                                                                                                                                    &[core::fmt::ArgumentV1<'_>; 0]))
+                                                                                                                                                                                                                             &'static [&'static str])
+                                                                                                                                                                                                                     }
+                                                                                                                                                                                                                        as
+                                                                                                                                                                                                                        &[&str]),
+                                                                                                                                                                                                                    (&(match (()
+                                                                                                                                                                                                                                 as
+                                                                                                                                                                                                                                 ())
+                                                                                                                                                                                                                           {
+                                                                                                                                                                                                                           ()
+                                                                                                                                                                                                                           =>
+                                                                                                                                                                                                                           ([]
+                                                                                                                                                                                                                               as
+                                                                                                                                                                                                                               [core::fmt::ArgumentV1<'_>; 0]),
+                                                                                                                                                                                                                       }
+                                                                                                                                                                                                                          as
+                                                                                                                                                                                                                          [core::fmt::ArgumentV1<'_>; 0])
+                                                                                                                                                                                                                        as
+                                                                                                                                                                                                                        &[core::fmt::ArgumentV1<'_>; 0]))
                                                                                                      as
                                                                                                      core::fmt::Arguments<'_>))
         as collections::string::String);
diff --git a/src/test/pretty/top-level-doc-comments.rs b/src/test/pretty/top-level-doc-comments.rs
new file mode 100644
index 00000000000..59c7f0fe0de
--- /dev/null
+++ b/src/test/pretty/top-level-doc-comments.rs
@@ -0,0 +1,20 @@
+/// Some doc comment.
+struct X;
+
+// ignore-license
+
+// Copyright 2012-2013 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.
+
+// pp-exact
+
+// Test that rust can properly pretty print a doc comment if it's the first line in a file.  some
+
+fn main() { let x = X; }
diff --git a/src/test/run-fail/divide-by-zero.rs b/src/test/run-fail/divide-by-zero.rs
index de69b7b9fa6..d3817b25d61 100644
--- a/src/test/run-fail/divide-by-zero.rs
+++ b/src/test/run-fail/divide-by-zero.rs
@@ -8,7 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:attempted to divide by zero
+
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let y = 0;
     let _z = 1 / y;
diff --git a/src/test/run-fail/mod-zero.rs b/src/test/run-fail/mod-zero.rs
index 76d4de7ecb0..7a151c8c572 100644
--- a/src/test/run-fail/mod-zero.rs
+++ b/src/test/run-fail/mod-zero.rs
@@ -8,7 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:attempted remainder with a divisor of zero
+
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let y = 0;
     let _z = 1 % y;
diff --git a/src/test/run-fail/overflowing-add.rs b/src/test/run-fail/overflowing-add.rs
index 6c6a41fa6f2..c989cc59453 100644
--- a/src/test/run-fail/overflowing-add.rs
+++ b/src/test/run-fail/overflowing-add.rs
@@ -8,10 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
 // compile-flags: -C debug-assertions
 
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = 200u8 + 200u8 + 200u8;
 }
diff --git a/src/test/run-fail/overflowing-lsh-1.rs b/src/test/run-fail/overflowing-lsh-1.rs
index 62935bacce8..a2721011298 100644
--- a/src/test/run-fail/overflowing-lsh-1.rs
+++ b/src/test/run-fail/overflowing-lsh-1.rs
@@ -8,11 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'shift operation overflowed'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = 1_i32 << 32;
 }
diff --git a/src/test/run-fail/overflowing-lsh-2.rs b/src/test/run-fail/overflowing-lsh-2.rs
index f6e6cb105c5..fe0bcc5b985 100644
--- a/src/test/run-fail/overflowing-lsh-2.rs
+++ b/src/test/run-fail/overflowing-lsh-2.rs
@@ -8,11 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'shift operation overflowed'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = 1 << -1;
 }
diff --git a/src/test/run-fail/overflowing-lsh-3.rs b/src/test/run-fail/overflowing-lsh-3.rs
index a70f31954c6..aac220d32d9 100644
--- a/src/test/run-fail/overflowing-lsh-3.rs
+++ b/src/test/run-fail/overflowing-lsh-3.rs
@@ -8,11 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'shift operation overflowed'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = 1_u64 << 64;
 }
diff --git a/src/test/run-fail/overflowing-lsh-4.rs b/src/test/run-fail/overflowing-lsh-4.rs
index 571feaeb943..7e8b266da49 100644
--- a/src/test/run-fail/overflowing-lsh-4.rs
+++ b/src/test/run-fail/overflowing-lsh-4.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'shift operation overflowed'
 // compile-flags: -C debug-assertions
 
@@ -16,6 +18,8 @@
 
 #![warn(exceeding_bitshifts)]
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     // this signals overflow when checking is on
     let x = 1_i8 << 17;
diff --git a/src/test/run-fail/overflowing-mul.rs b/src/test/run-fail/overflowing-mul.rs
index a413a6f0abf..8cba700bbf9 100644
--- a/src/test/run-fail/overflowing-mul.rs
+++ b/src/test/run-fail/overflowing-mul.rs
@@ -8,9 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
 // compile-flags: -C debug-assertions
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let x = 200u8 * 4;
 }
diff --git a/src/test/run-fail/overflowing-neg.rs b/src/test/run-fail/overflowing-neg.rs
index 7891d1ce9be..2d9d746bef3 100644
--- a/src/test/run-fail/overflowing-neg.rs
+++ b/src/test/run-fail/overflowing-neg.rs
@@ -8,9 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'attempted to negate with overflow'
 // compile-flags: -C debug-assertions
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = -std::i8::MIN;
 }
diff --git a/src/test/run-fail/overflowing-rsh-1.rs b/src/test/run-fail/overflowing-rsh-1.rs
index b58eaf7f836..63c808dc80a 100644
--- a/src/test/run-fail/overflowing-rsh-1.rs
+++ b/src/test/run-fail/overflowing-rsh-1.rs
@@ -8,11 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'shift operation overflowed'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = -1_i32 >> 32;
 }
diff --git a/src/test/run-fail/overflowing-rsh-2.rs b/src/test/run-fail/overflowing-rsh-2.rs
index 40b468a6ad4..8b89e57c85b 100644
--- a/src/test/run-fail/overflowing-rsh-2.rs
+++ b/src/test/run-fail/overflowing-rsh-2.rs
@@ -8,11 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'shift operation overflowed'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = -1_i32 >> -1;
 }
diff --git a/src/test/run-fail/overflowing-rsh-3.rs b/src/test/run-fail/overflowing-rsh-3.rs
index afe6a908cb5..8874587064c 100644
--- a/src/test/run-fail/overflowing-rsh-3.rs
+++ b/src/test/run-fail/overflowing-rsh-3.rs
@@ -8,11 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'shift operation overflowed'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = -1_i64 >> 64;
 }
diff --git a/src/test/run-fail/overflowing-rsh-4.rs b/src/test/run-fail/overflowing-rsh-4.rs
index 585186575f6..d74fd8a6b8e 100644
--- a/src/test/run-fail/overflowing-rsh-4.rs
+++ b/src/test/run-fail/overflowing-rsh-4.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'shift operation overflowed'
 // compile-flags: -C debug-assertions
 
@@ -16,6 +18,8 @@
 
 #![warn(exceeding_bitshifts)]
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     // this signals overflow when checking is on
     let x = 2_i8 >> 17;
diff --git a/src/test/run-fail/overflowing-rsh-5.rs b/src/test/run-fail/overflowing-rsh-5.rs
index 34a7ff833bb..249b952a5dc 100644
--- a/src/test/run-fail/overflowing-rsh-5.rs
+++ b/src/test/run-fail/overflowing-rsh-5.rs
@@ -8,11 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'shift operation overflowed'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _n = 1i64 >> [64][0];
 }
diff --git a/src/test/run-fail/overflowing-rsh-6.rs b/src/test/run-fail/overflowing-rsh-6.rs
index b6f4348b184..1227f35444a 100644
--- a/src/test/run-fail/overflowing-rsh-6.rs
+++ b/src/test/run-fail/overflowing-rsh-6.rs
@@ -8,12 +8,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'shift operation overflowed'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 #![feature(const_indexing)]
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _n = 1i64 >> [64][0];
 }
diff --git a/src/test/run-fail/overflowing-sub.rs b/src/test/run-fail/overflowing-sub.rs
index ece4d37c36e..ce243a50e0b 100644
--- a/src/test/run-fail/overflowing-sub.rs
+++ b/src/test/run-fail/overflowing-sub.rs
@@ -8,9 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
 // compile-flags: -C debug-assertions
 
+#![feature(rustc_attrs)]
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = 42u8 - (42u8 + 1);
 }
diff --git a/src/test/run-make/debug-assertions/debug.rs b/src/test/run-make/debug-assertions/debug.rs
index a0ccc75afd0..fb54161c2c1 100644
--- a/src/test/run-make/debug-assertions/debug.rs
+++ b/src/test/run-make/debug-assertions/debug.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(rustc_attrs)]
 #![deny(warnings)]
 
 use std::env;
@@ -36,6 +37,7 @@ fn debug_assert() {
 }
 
 fn overflow() {
+    #[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
     fn add(a: u8, b: u8) -> u8 { a + b }
 
     add(200u8, 200u8);
diff --git a/src/test/run-make/issue-25581/test.c b/src/test/run-make/issue-25581/test.c
index 5736b173021..ab85d2bb13f 100644
--- a/src/test/run-make/issue-25581/test.c
+++ b/src/test/run-make/issue-25581/test.c
@@ -2,15 +2,10 @@
 #include <stddef.h>
 #include <stdint.h>
 
-struct ByteSlice {
-        uint8_t *data;
-        size_t len;
-};
-
-size_t slice_len(struct ByteSlice bs) {
-        return bs.len;
+size_t slice_len(uint8_t *data, size_t len) {
+    return len;
 }
 
-uint8_t slice_elem(struct ByteSlice bs, size_t idx) {
-        return bs.data[idx];
+uint8_t slice_elem(uint8_t *data, size_t len, size_t idx) {
+    return data[idx];
 }
diff --git a/src/test/run-pass-valgrind/cast-enum-with-dtor.rs b/src/test/run-pass-valgrind/cast-enum-with-dtor.rs
index 247e82c2f09..0de949471c6 100644
--- a/src/test/run-pass-valgrind/cast-enum-with-dtor.rs
+++ b/src/test/run-pass-valgrind/cast-enum-with-dtor.rs
@@ -8,10 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // no-prefer-dynamic
 
 #![allow(dead_code)]
-#![feature(const_fn)]
+#![feature(const_fn, rustc_attrs)]
 
 // check dtor calling order when casting enums.
 
@@ -36,6 +38,7 @@ impl Drop for E {
     }
 }
 
+#[rustc_no_mir] // FIXME #27840 MIR miscompiles this.
 fn main() {
     assert_eq!(FLAG.load(Ordering::SeqCst), 0);
     {
diff --git a/src/test/run-pass/backtrace-debuginfo-aux.rs b/src/test/run-pass/backtrace-debuginfo-aux.rs
index 48df600214a..b80c938fed5 100644
--- a/src/test/run-pass/backtrace-debuginfo-aux.rs
+++ b/src/test/run-pass/backtrace-debuginfo-aux.rs
@@ -11,6 +11,7 @@
 // ignore-test: not a test, used by backtrace-debuginfo.rs to test file!()
 
 #[inline(never)]
+#[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
 pub fn callback<F>(f: F) where F: FnOnce((&'static str, u32)) {
     f((file!(), line!()))
 }
@@ -20,6 +21,7 @@ pub fn callback<F>(f: F) where F: FnOnce((&'static str, u32)) {
 // this case.
 #[cfg_attr(not(target_env = "msvc"), inline(always))]
 #[cfg_attr(target_env = "msvc", inline(never))]
+#[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
 pub fn callback_inlined<F>(f: F) where F: FnOnce((&'static str, u32)) {
     f((file!(), line!()))
 }
diff --git a/src/test/run-pass/backtrace-debuginfo.rs b/src/test/run-pass/backtrace-debuginfo.rs
index 8b2b2694882..fd1c0172339 100644
--- a/src/test/run-pass/backtrace-debuginfo.rs
+++ b/src/test/run-pass/backtrace-debuginfo.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(rustc_attrs)]
+
 // We disable tail merging here because it can't preserve debuginfo and thus
 // potentially breaks the backtraces. Also, subtle changes can decide whether
 // tail merging suceeds, so the test might work today but fail tomorrow due to a
@@ -72,6 +74,7 @@ fn dump_filelines(filelines: &[Pos]) {
 }
 
 #[inline(never)]
+#[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
 fn inner(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
     check!(counter; main_pos, outer_pos);
     check!(counter; main_pos, outer_pos);
@@ -88,6 +91,7 @@ fn inner(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
 // this case.
 #[cfg_attr(not(target_env = "msvc"), inline(always))]
 #[cfg_attr(target_env = "msvc", inline(never))]
+#[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
 fn inner_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
     check!(counter; main_pos, outer_pos);
     check!(counter; main_pos, outer_pos);
@@ -113,6 +117,7 @@ fn inner_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
 }
 
 #[inline(never)]
+#[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
 fn outer(mut counter: i32, main_pos: Pos) {
     inner(&mut counter, main_pos, pos!());
     inner_inlined(&mut counter, main_pos, pos!());
@@ -157,6 +162,7 @@ fn run_test(me: &str) {
 }
 
 #[inline(never)]
+#[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
 fn main() {
     let args: Vec<String> = env::args().collect();
     if args.len() >= 2 {
diff --git a/src/test/run-pass/binary-heap-panic-safe.rs b/src/test/run-pass/binary-heap-panic-safe.rs
index d85fd3a2b6b..7fbd8dc4786 100644
--- a/src/test/run-pass/binary-heap-panic-safe.rs
+++ b/src/test/run-pass/binary-heap-panic-safe.rs
@@ -70,7 +70,7 @@ fn test_integrity() {
             {
                 // push the panicking item to the heap and catch the panic
                 let thread_result = {
-                    let mut heap_ref = AssertRecoverSafe::new(&mut heap);
+                    let mut heap_ref = AssertRecoverSafe(&mut heap);
                     panic::recover(move || {
                         heap_ref.push(panic_item);
                     })
diff --git a/src/test/run-pass/const-bitshift-rhs-inference.rs b/src/test/run-pass/const-bitshift-rhs-inference.rs
new file mode 100644
index 00000000000..b377fd230b9
--- /dev/null
+++ b/src/test/run-pass/const-bitshift-rhs-inference.rs
@@ -0,0 +1,33 @@
+// Copyright 2016 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.
+
+const RHS: u8 = 8;
+const IRHS: i8 = 8;
+const RHS16: u16 = 8;
+const IRHS16: i16 = 8;
+const RHS32: u32 = 8;
+const IRHS32: i32 = 8;
+const RHS64: u64 = 8;
+const IRHS64: i64 = 8;
+const RHSUS: usize = 8;
+const IRHSIS: isize = 8;
+
+fn main() {
+    let _: [&'static str; 1 << RHS] = [""; 256];
+    let _: [&'static str; 1 << IRHS] = [""; 256];
+    let _: [&'static str; 1 << RHS16] = [""; 256];
+    let _: [&'static str; 1 << IRHS16] = [""; 256];
+    let _: [&'static str; 1 << RHS32] = [""; 256];
+    let _: [&'static str; 1 << IRHS32] = [""; 256];
+    let _: [&'static str; 1 << RHS64] = [""; 256];
+    let _: [&'static str; 1 << IRHS64] = [""; 256];
+    let _: [&'static str; 1 << RHSUS] = [""; 256];
+    let _: [&'static str; 1 << IRHSIS] = [""; 256];
+}
diff --git a/src/test/run-pass/const-str-ptr.rs b/src/test/run-pass/const-str-ptr.rs
index 4c5152ff90f..1736ab5bb82 100644
--- a/src/test/run-pass/const-str-ptr.rs
+++ b/src/test/run-pass/const-str-ptr.rs
@@ -8,6 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(rustc_attrs)]
+
+// ignore-pretty : (#23623) problems when  ending with // comments
 
 use std::{str, string};
 
@@ -15,6 +18,7 @@ const A: [u8; 2] = ['h' as u8, 'i' as u8];
 const B: &'static [u8; 2] = &A;
 const C: *const u8 = B as *const u8;
 
+#[rustc_no_mir] // FIXME #27840 MIR can't do rvalue promotion yet.
 pub fn main() {
     unsafe {
         let foo = &A as *const u8;
diff --git a/src/test/run-pass/float_math.rs b/src/test/run-pass/float_math.rs
new file mode 100644
index 00000000000..396b7321f45
--- /dev/null
+++ b/src/test/run-pass/float_math.rs
@@ -0,0 +1,24 @@
+// Copyright 2016 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.
+
+#![feature(core_intrinsics)]
+
+use std::intrinsics::{fadd_fast, fsub_fast, fmul_fast, fdiv_fast, frem_fast};
+
+fn main() {
+    // make sure they all map to the correct operation
+    unsafe {
+        assert_eq!(fadd_fast(1., 2.), 1. + 2.);
+        assert_eq!(fsub_fast(1., 2.), 1. - 2.);
+        assert_eq!(fmul_fast(2., 3.), 2. * 3.);
+        assert_eq!(fdiv_fast(10., 5.), 10. / 5.);
+        assert_eq!(frem_fast(10., 5.), 10. % 5.);
+    }
+}
diff --git a/src/test/run-pass/issue-23338-ensure-param-drop-order.rs b/src/test/run-pass/issue-23338-ensure-param-drop-order.rs
index 507d482febf..73c52a0843c 100644
--- a/src/test/run-pass/issue-23338-ensure-param-drop-order.rs
+++ b/src/test/run-pass/issue-23338-ensure-param-drop-order.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(rustc_attrs)]
+
 // ignore-pretty : (#23623) problems when  ending with // comments
 
 // This test is ensuring that parameters are indeed dropped after
@@ -64,6 +66,7 @@ fn test<'a>(log: d::Log<'a>) {
     d::println(&format!("result {}", result));
 }
 
+#[rustc_no_mir] // FIXME #29855 MIR doesn't handle all drops correctly.
 fn foo<'a>(da0: D<'a>, de1: D<'a>) -> D<'a> {
     d::println("entered foo");
     let de2 = de1.incr();      // creates D(de_2, 2)
diff --git a/src/test/run-pass/issue-31597.rs b/src/test/run-pass/issue-31597.rs
new file mode 100644
index 00000000000..9acd6384b75
--- /dev/null
+++ b/src/test/run-pass/issue-31597.rs
@@ -0,0 +1,37 @@
+// Copyright 2016 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.
+
+trait Make {
+    type Out;
+
+    fn make() -> Self::Out;
+}
+
+impl Make for () {
+    type Out = ();
+
+    fn make() -> Self::Out {}
+}
+
+// Also make sure we don't hit an ICE when the projection can't be known
+fn f<T: Make>() -> <T as Make>::Out { loop {} }
+
+// ...and that it works with a blanket impl
+trait Tr {
+    type Assoc;
+}
+
+impl<T: Make> Tr for T {
+    type Assoc = ();
+}
+
+fn g<T: Make>() -> <T as Tr>::Assoc { }
+
+fn main() {}
diff --git a/src/test/compile-fail/dupe-symbols-8.rs b/src/test/run-pass/issue-32292.rs
index 3c0e545e193..3995caa32bf 100644
--- a/src/test/compile-fail/dupe-symbols-8.rs
+++ b/src/test/run-pass/issue-32292.rs
@@ -7,17 +7,12 @@
 // <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.
-//
-// error-pattern:already defined
 
+#![deny(warnings)]
 
-#![allow(warnings)]
+#[derive(Hash, Ord, PartialOrd, Eq, PartialEq, Debug, Clone, Copy)]
+struct Foo;
 
 fn main() {
-    {
-        extern fn fail() {}
-    }
-    {
-        extern fn fail() {}
-    }
-}
+    let _ = Foo;
+}
\ No newline at end of file
diff --git a/src/test/run-pass/issue-8460.rs b/src/test/run-pass/issue-8460.rs
index 8ec9f8aff8e..7589bce31f4 100644
--- a/src/test/run-pass/issue-8460.rs
+++ b/src/test/run-pass/issue-8460.rs
@@ -9,31 +9,44 @@
 // except according to those terms.
 
 // ignore-emscripten no threads support
+// ignore-pretty : (#23623) problems when  ending with // comments
 
-#![feature(zero_one)]
+#![feature(rustc_attrs, stmt_expr_attributes, zero_one)]
 
 use std::num::Zero;
 use std::thread;
 
+macro_rules! check {
+    ($($e:expr),*) => {
+        $(assert!(thread::spawn({
+            #[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
+            move|| { $e; }
+        }).join().is_err());)*
+    }
+}
+
+#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
-    assert!(thread::spawn(move|| { isize::min_value() / -1; }).join().is_err());
-    assert!(thread::spawn(move|| { i8::min_value() / -1; }).join().is_err());
-    assert!(thread::spawn(move|| { i16::min_value() / -1; }).join().is_err());
-    assert!(thread::spawn(move|| { i32::min_value() / -1; }).join().is_err());
-    assert!(thread::spawn(move|| { i64::min_value() / -1; }).join().is_err());
-    assert!(thread::spawn(move|| { 1isize / isize::zero(); }).join().is_err());
-    assert!(thread::spawn(move|| { 1i8 / i8::zero(); }).join().is_err());
-    assert!(thread::spawn(move|| { 1i16 / i16::zero(); }).join().is_err());
-    assert!(thread::spawn(move|| { 1i32 / i32::zero(); }).join().is_err());
-    assert!(thread::spawn(move|| { 1i64 / i64::zero(); }).join().is_err());
-    assert!(thread::spawn(move|| { isize::min_value() % -1; }).join().is_err());
-    assert!(thread::spawn(move|| { i8::min_value() % -1; }).join().is_err());
-    assert!(thread::spawn(move|| { i16::min_value() % -1; }).join().is_err());
-    assert!(thread::spawn(move|| { i32::min_value() % -1; }).join().is_err());
-    assert!(thread::spawn(move|| { i64::min_value() % -1; }).join().is_err());
-    assert!(thread::spawn(move|| { 1isize % isize::zero(); }).join().is_err());
-    assert!(thread::spawn(move|| { 1i8 % i8::zero(); }).join().is_err());
-    assert!(thread::spawn(move|| { 1i16 % i16::zero(); }).join().is_err());
-    assert!(thread::spawn(move|| { 1i32 % i32::zero(); }).join().is_err());
-    assert!(thread::spawn(move|| { 1i64 % i64::zero(); }).join().is_err());
+    check![
+        isize::min_value() / -1,
+        i8::min_value() / -1,
+        i16::min_value() / -1,
+        i32::min_value() / -1,
+        i64::min_value() / -1,
+        1isize / isize::zero(),
+        1i8 / i8::zero(),
+        1i16 / i16::zero(),
+        1i32 / i32::zero(),
+        1i64 / i64::zero(),
+        isize::min_value() % -1,
+        i8::min_value() % -1,
+        i16::min_value() % -1,
+        i32::min_value() % -1,
+        i64::min_value() % -1,
+        1isize % isize::zero(),
+        1i8 % i8::zero(),
+        1i16 % i16::zero(),
+        1i32 % i32::zero(),
+        1i64 % i64::zero()
+    ];
 }
diff --git a/src/test/run-pass/mir_raw_fat_ptr.rs b/src/test/run-pass/mir_raw_fat_ptr.rs
index 9bbfbb68224..c0ba7a76dba 100644
--- a/src/test/run-pass/mir_raw_fat_ptr.rs
+++ b/src/test/run-pass/mir_raw_fat_ptr.rs
@@ -10,6 +10,8 @@
 
 #![feature(rustc_attrs)]
 
+// ignore-pretty : (#23623) problems when  ending with // comments
+
 // check raw fat pointer ops in mir
 // FIXME: please improve this when we get monomorphization support
 
@@ -119,6 +121,7 @@ impl<T> Foo for T {
 
 struct S<T:?Sized>(u32, T);
 
+#[rustc_no_mir] // FIXME #27840 MIR can't do rvalue promotion yet.
 fn main() {
     let array = [0,1,2,3,4];
     let array2 = [5,6,7,8,9];
diff --git a/src/test/run-pass/panic-handler-chain.rs b/src/test/run-pass/panic-handler-chain.rs
index 1ed592d3d6b..7c2e3f0c91b 100644
--- a/src/test/run-pass/panic-handler-chain.rs
+++ b/src/test/run-pass/panic-handler-chain.rs
@@ -17,12 +17,12 @@ static A: AtomicUsize = AtomicUsize::new(0);
 static B: AtomicUsize = AtomicUsize::new(0);
 
 fn main() {
-    panic::set_handler(|_| { A.fetch_add(1, Ordering::SeqCst); });
-    let handler = panic::take_handler();
-    panic::set_handler(move |info| {
+    panic::set_hook(Box::new(|_| { A.fetch_add(1, Ordering::SeqCst); }));
+    let hook = panic::take_hook();
+    panic::set_hook(Box::new(move |info| {
         B.fetch_add(1, Ordering::SeqCst);
-        handler(info);
-    });
+        hook(info);
+    }));
 
     let _ = thread::spawn(|| {
         panic!();
diff --git a/src/test/run-pass/panic-handler-flail-wildly.rs b/src/test/run-pass/panic-handler-flail-wildly.rs
index 39ea987f71b..311310712df 100644
--- a/src/test/run-pass/panic-handler-flail-wildly.rs
+++ b/src/test/run-pass/panic-handler-flail-wildly.rs
@@ -15,28 +15,28 @@ use std::panic;
 use std::thread;
 
 fn a() {
-    panic::set_handler(|_| println!("hello yes this is a"));
-    panic::take_handler();
-    panic::set_handler(|_| println!("hello yes this is a part 2"));
-    panic::take_handler();
+    panic::set_hook(Box::new(|_| println!("hello yes this is a")));
+    panic::take_hook();
+    panic::set_hook(Box::new(|_| println!("hello yes this is a part 2")));
+    panic::take_hook();
 }
 
 fn b() {
-    panic::take_handler();
-    panic::take_handler();
-    panic::take_handler();
-    panic::take_handler();
-    panic::take_handler();
+    panic::take_hook();
+    panic::take_hook();
+    panic::take_hook();
+    panic::take_hook();
+    panic::take_hook();
     panic!();
 }
 
 fn c() {
-    panic::set_handler(|_| ());
-    panic::set_handler(|_| ());
-    panic::set_handler(|_| ());
-    panic::set_handler(|_| ());
-    panic::set_handler(|_| ());
-    panic::set_handler(|_| ());
+    panic::set_hook(Box::new(|_| ()));
+    panic::set_hook(Box::new(|_| ()));
+    panic::set_hook(Box::new(|_| ()));
+    panic::set_hook(Box::new(|_| ()));
+    panic::set_hook(Box::new(|_| ()));
+    panic::set_hook(Box::new(|_| ()));
     panic!();
 }
 
diff --git a/src/test/run-pass/panic-handler-set-twice.rs b/src/test/run-pass/panic-handler-set-twice.rs
index ed9a02c1d3e..196e08a63a7 100644
--- a/src/test/run-pass/panic-handler-set-twice.rs
+++ b/src/test/run-pass/panic-handler-set-twice.rs
@@ -18,8 +18,8 @@ use std::thread;
 static A: AtomicUsize = AtomicUsize::new(0);
 
 fn main() {
-    panic::set_handler(|_| ());
-    panic::set_handler(|info| { A.fetch_add(1, Ordering::SeqCst); });
+    panic::set_hook(Box::new(|_| ()));
+    panic::set_hook(Box::new(|info| { A.fetch_add(1, Ordering::SeqCst); }));
 
     let _ = thread::spawn(|| {
         panic!();
diff --git a/src/test/run-pass/simd-intrinsic-generic-elements.rs b/src/test/run-pass/simd-intrinsic-generic-elements.rs
index f0444c27170..ffb9e6072df 100644
--- a/src/test/run-pass/simd-intrinsic-generic-elements.rs
+++ b/src/test/run-pass/simd-intrinsic-generic-elements.rs
@@ -8,7 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(repr_simd, platform_intrinsics)]
+#![feature(repr_simd, rustc_attrs, platform_intrinsics)]
+
+// ignore-pretty : (#23623) problems when  ending with // comments
 
 #[repr(simd)]
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -50,6 +52,7 @@ macro_rules! all_eq {
     }}
 }
 
+#[rustc_no_mir] // FIXME #27840 MIR doesn't handle shuffle constants.
 fn main() {
     let x2 = i32x2(20, 21);
     let x3 = i32x3(30, 31, 32);
diff --git a/src/test/run-pass/super-fast-paren-parsing.rs b/src/test/run-pass/super-fast-paren-parsing.rs
index 69ec0a2222d..b764a983a0c 100644
--- a/src/test/run-pass/super-fast-paren-parsing.rs
+++ b/src/test/run-pass/super-fast-paren-parsing.rs
@@ -11,6 +11,7 @@
 // ignore-pretty
 //
 // exec-env:RUST_MIN_STACK=16000000
+// rustc-env:RUST_MIN_STACK=16000000
 //
 // Big stack is needed for pretty printing, a little sad...
 
diff --git a/src/test/run-pass/unique-ffi-symbols.rs b/src/test/run-pass/unique-ffi-symbols.rs
new file mode 100644
index 00000000000..81563f40e94
--- /dev/null
+++ b/src/test/run-pass/unique-ffi-symbols.rs
@@ -0,0 +1,25 @@
+// Copyright 2016 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.
+
+// We used to have a __rust_abi shim that resulted in duplicated symbols
+// whenever the item path wasn't enough to disambiguate between them.
+fn main() {
+    let a = {
+        extern fn good() -> i32 { return 0; }
+        good as extern fn() -> i32
+    };
+    let b = {
+        extern fn good() -> i32 { return 5; }
+        good as extern fn() -> i32
+    };
+
+    assert!(a != b);
+    assert_eq!((a(), b()), (0, 5));
+}
diff --git a/src/test/run-pass/variadic-ffi.rs b/src/test/run-pass/variadic-ffi.rs
index 6351cc76a2e..abd1709825c 100644
--- a/src/test/run-pass/variadic-ffi.rs
+++ b/src/test/run-pass/variadic-ffi.rs
@@ -45,15 +45,15 @@ pub fn main() {
         let x: unsafe extern fn(*mut c_char, *const c_char, ...) -> c_int = sprintf;
 
         // A function that takes a function pointer
-        unsafe fn call(p: unsafe extern fn(*mut c_char, *const c_char, ...) -> c_int) {
+        unsafe fn call(fp: unsafe extern fn(*mut c_char, *const c_char, ...) -> c_int) {
             // Call with just the named parameter
             let c = CString::new(&b"Hello World\n"[..]).unwrap();
-            check("Hello World\n", |s| sprintf(s, c.as_ptr()));
+            check("Hello World\n", |s| fp(s, c.as_ptr()));
 
             // Call with variable number of arguments
             let c = CString::new(&b"%d %f %c %s\n"[..]).unwrap();
             check("42 42.500000 a %d %f %c %s\n\n", |s| {
-                sprintf(s, c.as_ptr(), 42, 42.5f64, 'a' as c_int, c.as_ptr());
+                fp(s, c.as_ptr(), 42, 42.5f64, 'a' as c_int, c.as_ptr());
             });
         }
 
diff --git a/src/test/run-pass/volatile-fat-ptr.rs b/src/test/run-pass/volatile-fat-ptr.rs
new file mode 100644
index 00000000000..03ba5587fce
--- /dev/null
+++ b/src/test/run-pass/volatile-fat-ptr.rs
@@ -0,0 +1,22 @@
+// Copyright 2016 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.
+
+#![feature(volatile)]
+use std::ptr::{read_volatile, write_volatile};
+
+fn main() {
+    let mut x: &'static str = "test";
+    unsafe {
+        let a = read_volatile(&x);
+        assert_eq!(a, "test");
+        write_volatile(&mut x, "foo");
+        assert_eq!(x, "foo");
+    }
+}
diff --git a/src/test/run-pass/zero-size-type-destructors.rs b/src/test/run-pass/zero-size-type-destructors.rs
index fecbeed407c..a663ae650c0 100644
--- a/src/test/run-pass/zero-size-type-destructors.rs
+++ b/src/test/run-pass/zero-size-type-destructors.rs
@@ -8,11 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(rustc_attrs, unsafe_no_drop_flag)]
 
-#![feature(unsafe_no_drop_flag)]
+// ignore-pretty : (#23623) problems when  ending with // comments
 
 static mut destructions : isize = 3;
 
+#[rustc_no_mir] // FIXME #29855 MIR doesn't handle all drops correctly.
 pub fn foo() {
     #[unsafe_no_drop_flag]
     struct Foo;