diff --git a/.gitmodules b/.gitmodules
index ffa7b321ba6b6..65aafeea17bd9 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -51,3 +51,6 @@
 [submodule "src/doc/rust-by-example"]
 	path = src/doc/rust-by-example
 	url = https://github.com/rust-lang/rust-by-example
+[submodule "src/llvm-emscripten"]
+	path = src/llvm-emscripten
+	url = https://github.com/rust-lang/llvm
diff --git a/.travis.yml b/.travis.yml
index 6e242b74894c5..1007aad925d96 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -81,7 +81,7 @@ matrix:
     # OSX 10.7 and `xcode7` is the latest Xcode able to compile LLVM for 10.7.
     - env: >
         RUST_CHECK_TARGET=dist
-        RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-extended --enable-profiler"
+        RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-extended --enable-profiler --enable-emscripten"
         SRC=.
         DEPLOY=1
         RUSTC_RETRY_LINKER_ON_SEGFAULT=1
@@ -95,7 +95,7 @@ matrix:
 
     - env: >
         RUST_CHECK_TARGET=dist
-        RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-extended --enable-sanitizers --enable-profiler"
+        RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-extended --enable-sanitizers --enable-profiler --enable-emscripten"
         SRC=.
         DEPLOY=1
         RUSTC_RETRY_LINKER_ON_SEGFAULT=1
diff --git a/appveyor.yml b/appveyor.yml
index 1a186c080ce0d..7f1c538a32e46 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -63,6 +63,7 @@ environment:
       --build=x86_64-pc-windows-msvc
       --enable-extended
       --enable-profiler
+      --enable-emscripten
     SCRIPT: python x.py dist
     DEPLOY: 1
   - RUST_CONFIGURE_ARGS: >
@@ -70,10 +71,11 @@ environment:
       --target=i586-pc-windows-msvc
       --enable-extended
       --enable-profiler
+      --enable-emscripten
     SCRIPT: python x.py dist
     DEPLOY: 1
   - MSYS_BITS: 32
-    RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu --enable-extended
+    RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu --enable-extended --enable-emscripten
     SCRIPT: python x.py dist
     MINGW_URL: https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror
     MINGW_ARCHIVE: i686-6.3.0-release-posix-dwarf-rt_v5-rev2.7z
@@ -81,7 +83,7 @@ environment:
     DEPLOY: 1
   - MSYS_BITS: 64
     SCRIPT: python x.py dist
-    RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-extended
+    RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-extended --enable-emscripten
     MINGW_URL: https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror
     MINGW_ARCHIVE: x86_64-6.3.0-release-posix-seh-rt_v5-rev2.7z
     MINGW_DIR: mingw64
diff --git a/config.toml.example b/config.toml.example
index 18c1f160c03d2..1d60d8c949441 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -305,6 +305,13 @@
 # result (broken, compiling, testing) into this JSON file.
 #save-toolstates = "/path/to/toolstates.json"
 
+# This is an array of the codegen backends that will be compiled for the rustc
+# that's being compiled. The default is to only build the LLVM codegen backend,
+# but you can also optionally enable the "emscripten" backend for asm.js or
+# make this an empty array (but that probably won't get too far in the
+# bootstrap)
+#codegen-backends = ["llvm"]
+
 # =============================================================================
 # Options for specific targets
 #
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index ecf9c0a75903e..603a97ddfd412 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -640,14 +640,23 @@ def update_submodules(self):
              os.path.join(self.rust_root, ".gitmodules"),
              "--get-regexp", "path"]
         ).decode(default_encoding).splitlines()]
-        submodules = [module for module in submodules
-                      if not ((module.endswith("llvm") and
-                               self.get_toml('llvm-config')) or
-                              (module.endswith("jemalloc") and
-                               (self.get_toml('use-jemalloc') == "false" or
-                                self.get_toml('jemalloc'))))]
+        filtered_submodules = []
+        for module in submodules:
+            if module.endswith("llvm"):
+                if self.get_toml('llvm-config'):
+                    continue
+            if module.endswith("llvm-emscripten"):
+                backends = self.get_toml('codegen-backends')
+                if backends is None or not 'emscripten' in backends:
+                    continue
+            if module.endswith("jemalloc"):
+                if self.get_toml('use-jemalloc') == 'false':
+                    continue
+                if self.get_toml('jemalloc'):
+                    continue
+            filtered_submodules.append(module)
         run(["git", "submodule", "update",
-             "--init", "--recursive"] + submodules,
+             "--init", "--recursive"] + filtered_submodules,
             cwd=self.rust_root, verbose=self.verbose)
         run(["git", "submodule", "-q", "foreach", "git",
              "reset", "-q", "--hard"],
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index cb236cd7e73eb..fa289bbd76a8b 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -581,24 +581,30 @@ impl Step for RustcLink {
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub struct RustcTrans {
+pub struct CodegenBackend {
     pub compiler: Compiler,
     pub target: Interned<String>,
+    pub backend: Interned<String>,
 }
 
-impl Step for RustcTrans {
+impl Step for CodegenBackend {
     type Output = ();
     const ONLY_HOSTS: bool = true;
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun) -> ShouldRun {
-        run.path("src/librustc_trans").krate("rustc_trans")
+        run.path("src/librustc_trans")
     }
 
     fn make_run(run: RunConfig) {
-        run.builder.ensure(RustcTrans {
+        let backend = run.builder.config.rust_codegen_backends.get(0);
+        let backend = backend.cloned().unwrap_or_else(|| {
+            INTERNER.intern_str("llvm")
+        });
+        run.builder.ensure(CodegenBackend {
             compiler: run.builder.compiler(run.builder.top_stage, run.host),
             target: run.target,
+            backend
         });
     }
 
@@ -609,58 +615,92 @@ impl Step for RustcTrans {
 
         builder.ensure(Rustc { compiler, target });
 
-        // Build LLVM for our target. This will implicitly build the host LLVM
-        // if necessary.
-        builder.ensure(native::Llvm { target });
-
         if build.force_use_stage1(compiler, target) {
-            builder.ensure(RustcTrans {
+            builder.ensure(CodegenBackend {
                 compiler: builder.compiler(1, build.build),
                 target,
+                backend: self.backend,
             });
             return;
         }
 
-        let _folder = build.fold_output(|| format!("stage{}-rustc_trans", compiler.stage));
-        println!("Building stage{} trans artifacts ({} -> {})",
-                 compiler.stage, &compiler.host, target);
-
         let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "build");
+        let mut features = build.rustc_features().to_string();
         cargo.arg("--manifest-path")
-            .arg(build.src.join("src/librustc_trans/Cargo.toml"))
-            .arg("--features").arg(build.rustc_features());
+            .arg(build.src.join("src/librustc_trans/Cargo.toml"));
         rustc_cargo_env(build, &mut cargo);
 
-        // Pass down configuration from the LLVM build into the build of
-        // librustc_llvm and librustc_trans.
-        if build.is_rust_llvm(target) {
-            cargo.env("LLVM_RUSTLLVM", "1");
-        }
-        cargo.env("LLVM_CONFIG", build.llvm_config(target));
-        let target_config = build.config.target_config.get(&target);
-        if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
-            cargo.env("CFG_LLVM_ROOT", s);
-        }
-        // Building with a static libstdc++ is only supported on linux right now,
-        // not for MSVC or macOS
-        if build.config.llvm_static_stdcpp &&
-           !target.contains("freebsd") &&
-           !target.contains("windows") &&
-           !target.contains("apple") {
-            let file = compiler_file(build,
-                                     build.cxx(target).unwrap(),
-                                     target,
-                                     "libstdc++.a");
-            cargo.env("LLVM_STATIC_STDCPP", file);
-        }
-        if build.config.llvm_link_shared {
-            cargo.env("LLVM_LINK_SHARED", "1");
+        match &*self.backend {
+            "llvm" | "emscripten" => {
+                // Build LLVM for our target. This will implicitly build the
+                // host LLVM if necessary.
+                let llvm_config = builder.ensure(native::Llvm {
+                    target,
+                    emscripten: self.backend == "emscripten",
+                });
+
+                if self.backend == "emscripten" {
+                    features.push_str(" emscripten");
+                }
+
+                let _folder = build.fold_output(|| format!("stage{}-rustc_trans", compiler.stage));
+                println!("Building stage{} codegen artifacts ({} -> {}, {})",
+                         compiler.stage, &compiler.host, target, self.backend);
+
+                // Pass down configuration from the LLVM build into the build of
+                // librustc_llvm and librustc_trans.
+                if build.is_rust_llvm(target) {
+                    cargo.env("LLVM_RUSTLLVM", "1");
+                }
+                cargo.env("LLVM_CONFIG", &llvm_config);
+                if self.backend != "emscripten" {
+                    let target_config = build.config.target_config.get(&target);
+                    if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
+                        cargo.env("CFG_LLVM_ROOT", s);
+                    }
+                }
+                // Building with a static libstdc++ is only supported on linux right now,
+                // not for MSVC or macOS
+                if build.config.llvm_static_stdcpp &&
+                   !target.contains("freebsd") &&
+                   !target.contains("windows") &&
+                   !target.contains("apple") {
+                    let file = compiler_file(build,
+                                             build.cxx(target).unwrap(),
+                                             target,
+                                             "libstdc++.a");
+                    cargo.env("LLVM_STATIC_STDCPP", file);
+                }
+                if build.config.llvm_link_shared {
+                    cargo.env("LLVM_LINK_SHARED", "1");
+                }
+            }
+            _ => panic!("unknown backend: {}", self.backend),
         }
 
-        run_cargo(build,
-                  &mut cargo,
-                  &librustc_trans_stamp(build, compiler, target),
-                  false);
+        let tmp_stamp = build.cargo_out(compiler, Mode::Librustc, target)
+            .join(".tmp.stamp");
+        let files = run_cargo(build,
+                              cargo.arg("--features").arg(features),
+                              &tmp_stamp,
+                              false);
+        let mut files = files.into_iter()
+            .filter(|f| {
+                let filename = f.file_name().unwrap().to_str().unwrap();
+                is_dylib(filename) && filename.contains("rustc_trans-")
+            });
+        let codegen_backend = match files.next() {
+            Some(f) => f,
+            None => panic!("no dylibs built for codegen backend?"),
+        };
+        if let Some(f) = files.next() {
+            panic!("codegen backend built two dylibs:\n{}\n{}",
+                   codegen_backend.display(),
+                   f.display());
+        }
+        let stamp = codegen_backend_stamp(build, compiler, target, self.backend);
+        let codegen_backend = codegen_backend.to_str().unwrap();
+        t!(t!(File::create(&stamp)).write_all(codegen_backend.as_bytes()));
     }
 }
 
@@ -682,33 +722,29 @@ fn copy_codegen_backends_to_sysroot(builder: &Builder,
     // not linked into the main compiler by default but is rather dynamically
     // selected at runtime for inclusion.
     //
-    // Here we're looking for the output dylib of the `RustcTrans` step and
+    // Here we're looking for the output dylib of the `CodegenBackend` step and
     // we're copying that into the `codegen-backends` folder.
     let libdir = builder.sysroot_libdir(target_compiler, target);
     let dst = libdir.join("codegen-backends");
     t!(fs::create_dir_all(&dst));
-    let stamp = librustc_trans_stamp(build, compiler, target);
 
-    let mut copied = None;
-    for file in read_stamp_file(&stamp) {
-        let filename = match file.file_name().and_then(|s| s.to_str()) {
-            Some(s) => s,
-            None => continue,
+    for backend in builder.config.rust_codegen_backends.iter() {
+        let stamp = codegen_backend_stamp(build, compiler, target, *backend);
+        let mut dylib = String::new();
+        t!(t!(File::open(&stamp)).read_to_string(&mut dylib));
+        let file = Path::new(&dylib);
+        let filename = file.file_name().unwrap().to_str().unwrap();
+        // change `librustc_trans-xxxxxx.so` to `librustc_trans-llvm.so`
+        let target_filename = {
+            let dash = filename.find("-").unwrap();
+            let dot = filename.find(".").unwrap();
+            format!("{}-{}{}",
+                    &filename[..dash],
+                    backend,
+                    &filename[dot..])
         };
-        if !is_dylib(filename) || !filename.contains("rustc_trans-") {
-            continue
-        }
-        match copied {
-            None => copied = Some(file.clone()),
-            Some(ref s) => {
-                panic!("copied two codegen backends:\n{}\n{}",
-                       s.display(),
-                       file.display());
-            }
-        }
-        copy(&file, &dst.join(filename));
+        copy(&file, &dst.join(target_filename));
     }
-    assert!(copied.is_some(), "failed to find a codegen backend to copy");
 }
 
 /// Cargo's output path for the standard library in a given stage, compiled
@@ -729,10 +765,12 @@ pub fn librustc_stamp(build: &Build, compiler: Compiler, target: Interned<String
     build.cargo_out(compiler, Mode::Librustc, target).join(".librustc.stamp")
 }
 
-pub fn librustc_trans_stamp(build: &Build,
-                            compiler: Compiler,
-                            target: Interned<String>) -> PathBuf {
-    build.cargo_out(compiler, Mode::Librustc, target).join(".librustc_trans.stamp")
+fn codegen_backend_stamp(build: &Build,
+                         compiler: Compiler,
+                         target: Interned<String>,
+                         backend: Interned<String>) -> PathBuf {
+    build.cargo_out(compiler, Mode::Librustc, target)
+        .join(format!(".librustc_trans-{}.stamp", backend))
 }
 
 fn compiler_file(build: &Build,
@@ -849,10 +887,13 @@ impl Step for Assemble {
                 compiler: build_compiler,
                 target: target_compiler.host,
             });
-            builder.ensure(RustcTrans {
-                compiler: build_compiler,
-                target: target_compiler.host,
-            });
+            for &backend in build.config.rust_codegen_backends.iter() {
+                builder.ensure(CodegenBackend {
+                    compiler: build_compiler,
+                    target: target_compiler.host,
+                    backend,
+                });
+            }
         }
 
         let stage = target_compiler.stage;
@@ -922,7 +963,9 @@ fn stderr_isatty() -> bool {
     }
 }
 
-pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: bool) {
+pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: bool)
+    -> Vec<PathBuf>
+{
     // Instruct Cargo to give us json messages on stdout, critically leaving
     // stderr as piped so we can get those pretty colors.
     cargo.arg("--message-format").arg("json")
@@ -1066,8 +1109,8 @@ pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: boo
     let mut new_contents = Vec::new();
     let mut max = None;
     let mut max_path = None;
-    for dep in deps {
-        let mtime = mtime(&dep);
+    for dep in deps.iter() {
+        let mtime = mtime(dep);
         if Some(mtime) > max {
             max = Some(mtime);
             max_path = Some(dep.clone());
@@ -1080,7 +1123,7 @@ pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: boo
     if stamp_contents == new_contents && max <= stamp_mtime {
         build.verbose(&format!("not updating {:?}; contents equal and {} <= {}",
                 stamp, max, stamp_mtime));
-        return
+        return deps
     }
     if max > stamp_mtime {
         build.verbose(&format!("updating {:?} as {:?} changed", stamp, max_path));
@@ -1088,4 +1131,5 @@ pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: boo
         build.verbose(&format!("updating {:?} as deps changed", stamp));
     }
     t!(t!(File::create(stamp)).write_all(&new_contents));
+    deps
 }
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 72e75fddc1942..dbeb27cbfb7d3 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -91,6 +91,7 @@ pub struct Config {
     pub rust_optimize_tests: bool,
     pub rust_debuginfo_tests: bool,
     pub rust_dist_src: bool,
+    pub rust_codegen_backends: Vec<Interned<String>>,
 
     pub build: Interned<String>,
     pub hosts: Vec<Interned<String>>,
@@ -280,6 +281,7 @@ struct Rust {
     quiet_tests: Option<bool>,
     test_miri: Option<bool>,
     save_toolstates: Option<String>,
+    codegen_backends: Option<Vec<String>>,
 }
 
 /// TOML representation of how each build target is configured.
@@ -318,6 +320,7 @@ impl Config {
         config.ignore_git = false;
         config.rust_dist_src = true;
         config.test_miri = false;
+        config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")];
 
         config.on_fail = flags.on_fail;
         config.stage = flags.stage;
@@ -465,6 +468,12 @@ impl Config {
             config.musl_root = rust.musl_root.clone().map(PathBuf::from);
             config.save_toolstates = rust.save_toolstates.clone().map(PathBuf::from);
 
+            if let Some(ref backends) = rust.codegen_backends {
+                config.rust_codegen_backends = backends.iter()
+                    .map(|s| INTERNER.intern_str(s))
+                    .collect();
+            }
+
             match rust.codegen_units {
                 Some(0) => config.rust_codegen_units = Some(num_cpus::get() as u32),
                 Some(n) => config.rust_codegen_units = Some(n),
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index 9a4b5e786e192..bc6f666d0012f 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -65,6 +65,7 @@ def v(*args):
 o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball")
 o("cargo-openssl-static", "build.openssl-static", "static openssl in cargo")
 o("profiler", "build.profiler", "build the profiler runtime")
+o("emscripten", None, "compile the emscripten backend as well as LLVM")
 
 # Optimization and debugging options. These may be overridden by the release
 # channel, etc.
@@ -321,6 +322,8 @@ def set(key, value):
         set('build.host', value.split(','))
     elif option.name == 'target':
         set('build.target', value.split(','))
+    elif option.name == 'emscripten':
+        set('rust.codegen-backends', ['llvm', 'emscripten'])
     elif option.name == 'option-checking':
         # this was handled above
         pass
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 8928bef9faa56..aae0a4f056f08 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -480,6 +480,10 @@ impl Build {
         self.out.join(&*target).join("llvm")
     }
 
+    fn emscripten_llvm_out(&self, target: Interned<String>) -> PathBuf {
+        self.out.join(&*target).join("llvm-emscripten")
+    }
+
     /// Output directory for all documentation for a target
     fn doc_out(&self, target: Interned<String>) -> PathBuf {
         self.out.join(&*target).join("doc")
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 442098a7afa77..3f30756a568ce 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -22,7 +22,7 @@ use std::env;
 use std::ffi::OsString;
 use std::fs::{self, File};
 use std::io::{Read, Write};
-use std::path::Path;
+use std::path::{Path, PathBuf};
 use std::process::Command;
 
 use build_helper::output;
@@ -30,7 +30,7 @@ use cmake;
 use cc;
 
 use Build;
-use util;
+use util::{self, exe};
 use build_helper::up_to_date;
 use builder::{Builder, RunConfig, ShouldRun, Step};
 use cache::Interned;
@@ -38,30 +38,42 @@ use cache::Interned;
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct Llvm {
     pub target: Interned<String>,
+    pub emscripten: bool,
 }
 
 impl Step for Llvm {
-    type Output = ();
+    type Output = PathBuf; // path to llvm-config
+
     const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun) -> ShouldRun {
-        run.path("src/llvm")
+        run.path("src/llvm").path("src/llvm-emscripten")
     }
 
     fn make_run(run: RunConfig) {
-        run.builder.ensure(Llvm { target: run.target })
+        let emscripten = run.path.map(|p| {
+            p.ends_with("llvm-emscripten")
+        }).unwrap_or(false);
+        run.builder.ensure(Llvm {
+            target: run.target,
+            emscripten,
+        });
     }
 
     /// Compile LLVM for `target`.
-    fn run(self, builder: &Builder) {
+    fn run(self, builder: &Builder) -> PathBuf {
         let build = builder.build;
         let target = self.target;
+        let emscripten = self.emscripten;
 
         // If we're using a custom LLVM bail out here, but we can only use a
         // custom LLVM for the build triple.
-        if let Some(config) = build.config.target_config.get(&target) {
-            if let Some(ref s) = config.llvm_config {
-                return check_llvm_version(build, s);
+        if !self.emscripten {
+            if let Some(config) = build.config.target_config.get(&target) {
+                if let Some(ref s) = config.llvm_config {
+                    check_llvm_version(build, s);
+                    return s.to_path_buf()
+                }
             }
         }
 
@@ -69,8 +81,17 @@ impl Step for Llvm {
         let mut rebuild_trigger_contents = String::new();
         t!(t!(File::open(&rebuild_trigger)).read_to_string(&mut rebuild_trigger_contents));
 
-        let out_dir = build.llvm_out(target);
+        let (out_dir, llvm_config_ret_dir) = if emscripten {
+            let dir = build.emscripten_llvm_out(target);
+            let config_dir = dir.join("bin");
+            (dir, config_dir)
+        } else {
+            (build.llvm_out(target),
+                build.llvm_out(build.config.build).join("bin"))
+        };
         let done_stamp = out_dir.join("llvm-finished-building");
+        let build_llvm_config = llvm_config_ret_dir
+            .join(exe("llvm-config", &*build.config.build));
         if done_stamp.exists() {
             let mut done_contents = String::new();
             t!(t!(File::open(&done_stamp)).read_to_string(&mut done_contents));
@@ -78,17 +99,19 @@ impl Step for Llvm {
             // If LLVM was already built previously and contents of the rebuild-trigger file
             // didn't change from the previous build, then no action is required.
             if done_contents == rebuild_trigger_contents {
-                return
+                return build_llvm_config
             }
         }
 
         let _folder = build.fold_output(|| "llvm");
-        println!("Building LLVM for {}", target);
+        let descriptor = if emscripten { "Emscripten " } else { "" };
+        println!("Building {}LLVM for {}", descriptor, target);
         let _time = util::timeit();
         t!(fs::create_dir_all(&out_dir));
 
         // http://llvm.org/docs/CMake.html
-        let mut cfg = cmake::Config::new(build.src.join("src/llvm"));
+        let root = if self.emscripten { "src/llvm-emscripten" } else { "src/llvm" };
+        let mut cfg = cmake::Config::new(build.src.join(root));
         if build.config.ninja {
             cfg.generator("Ninja");
         }
@@ -99,13 +122,22 @@ impl Step for Llvm {
             (true, true) => "RelWithDebInfo",
         };
 
-        // NOTE: remember to also update `config.toml.example` when changing the defaults!
-        let llvm_targets = match build.config.llvm_targets {
-            Some(ref s) => s,
-            None => "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc;NVPTX;Hexagon",
+        // NOTE: remember to also update `config.toml.example` when changing the
+        // defaults!
+        let llvm_targets = if self.emscripten {
+            "JSBackend"
+        } else {
+            match build.config.llvm_targets {
+                Some(ref s) => s,
+                None => "X86;ARM;AArch64;Mips;PowerPC;SystemZ;MSP430;Sparc;NVPTX;Hexagon",
+            }
         };
 
-        let llvm_exp_targets = &build.config.llvm_experimental_targets;
+        let llvm_exp_targets = if self.emscripten {
+            ""
+        } else {
+            &build.config.llvm_experimental_targets[..]
+        };
 
         let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};
 
@@ -155,7 +187,10 @@ impl Step for Llvm {
 
         // http://llvm.org/docs/HowToCrossCompileLLVM.html
         if target != build.build {
-            builder.ensure(Llvm { target: build.build });
+            builder.ensure(Llvm {
+                target: build.build,
+                emscripten: false,
+            });
             // FIXME: if the llvm root for the build triple is overridden then we
             //        should use llvm-tblgen from there, also should verify that it
             //        actually exists most of the time in normal installs of LLVM.
@@ -241,6 +276,8 @@ impl Step for Llvm {
         cfg.build();
 
         t!(t!(File::create(&done_stamp)).write_all(rebuild_trigger_contents.as_bytes()));
+
+        build_llvm_config
     }
 }
 
diff --git a/src/ci/docker/asmjs/Dockerfile b/src/ci/docker/asmjs/Dockerfile
index 07849a20d0045..ff0708459bc89 100644
--- a/src/ci/docker/asmjs/Dockerfile
+++ b/src/ci/docker/asmjs/Dockerfile
@@ -29,6 +29,6 @@ ENV EM_CONFIG=/emsdk-portable/.emscripten
 
 ENV TARGETS=asmjs-unknown-emscripten
 
-ENV RUST_CONFIGURE_ARGS --target=$TARGETS
+ENV RUST_CONFIGURE_ARGS --target=$TARGETS --enable-emscripten
 
 ENV SCRIPT python2.7 ../x.py test --target $TARGETS
diff --git a/src/ci/docker/dist-i686-linux/Dockerfile b/src/ci/docker/dist-i686-linux/Dockerfile
index a5d776af19daf..0fd6af6e10d34 100644
--- a/src/ci/docker/dist-i686-linux/Dockerfile
+++ b/src/ci/docker/dist-i686-linux/Dockerfile
@@ -85,7 +85,8 @@ ENV RUST_CONFIGURE_ARGS \
       --host=$HOSTS \
       --enable-extended \
       --enable-sanitizers \
-      --enable-profiler
+      --enable-profiler \
+      --enable-emscripten
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
 
 # This is the only builder which will create source tarballs
diff --git a/src/ci/docker/dist-various-1/Dockerfile b/src/ci/docker/dist-various-1/Dockerfile
index 64c993860d9e1..c83f101d0ac41 100644
--- a/src/ci/docker/dist-various-1/Dockerfile
+++ b/src/ci/docker/dist-various-1/Dockerfile
@@ -95,7 +95,8 @@ ENV RUST_CONFIGURE_ARGS \
       --musl-root-armv7=/musl-armv7 \
       --musl-root-aarch64=/musl-aarch64 \
       --musl-root-mips=/musl-mips \
-      --musl-root-mipsel=/musl-mipsel
+      --musl-root-mipsel=/musl-mipsel \
+      --enable-emscripten
 
 ENV SCRIPT python2.7 ../x.py dist --target $TARGETS
 
diff --git a/src/ci/docker/dist-x86_64-linux/Dockerfile b/src/ci/docker/dist-x86_64-linux/Dockerfile
index a954fd86a2440..d368a00b55bd5 100644
--- a/src/ci/docker/dist-x86_64-linux/Dockerfile
+++ b/src/ci/docker/dist-x86_64-linux/Dockerfile
@@ -85,7 +85,8 @@ ENV RUST_CONFIGURE_ARGS \
       --host=$HOSTS \
       --enable-extended \
       --enable-sanitizers \
-      --enable-profiler
+      --enable-profiler \
+      --enable-emscripten
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
 
 # This is the only builder which will create source tarballs
diff --git a/src/ci/init_repo.sh b/src/ci/init_repo.sh
index 14a1906ff421d..8ab4276fa3b05 100755
--- a/src/ci/init_repo.sh
+++ b/src/ci/init_repo.sh
@@ -48,7 +48,12 @@ travis_time_start
 # Update the cache (a pristine copy of the rust source master)
 retry sh -c "rm -rf $cache_src_dir && mkdir -p $cache_src_dir && \
     git clone --depth 1 https://github.com/rust-lang/rust.git $cache_src_dir"
-(cd $cache_src_dir && git rm src/llvm)
+if [ -d $cache_src_dir/src/llvm ]; then
+  (cd $cache_src_dir && git rm src/llvm)
+fi
+if [ -d $cache_src_dir/src/llvm-emscripten ]; then
+  (cd $cache_src_dir && git rm src/llvm-emscripten)
+fi
 retry sh -c "cd $cache_src_dir && \
     git submodule deinit -f . && git submodule sync && git submodule update --init"
 
@@ -64,14 +69,14 @@ travis_time_start
 # http://stackoverflow.com/questions/12641469/list-submodules-in-a-git-repository
 modules="$(git config --file .gitmodules --get-regexp '\.path$' | cut -d' ' -f2)"
 for module in $modules; do
-    if [ "$module" = src/llvm ]; then
-        commit="$(git ls-tree HEAD src/llvm | awk '{print $3}')"
-        git rm src/llvm
+    if [ "$module" = src/llvm ] || [ "$module" = src/llvm-emscripten ]; then
+        commit="$(git ls-tree HEAD $module | awk '{print $3}')"
+        git rm $module
         retry sh -c "rm -f $commit.tar.gz && \
             curl -sSL -O https://github.com/rust-lang/llvm/archive/$commit.tar.gz"
         tar -C src/ -xf "$commit.tar.gz"
         rm "$commit.tar.gz"
-        mv "src/llvm-$commit" src/llvm
+        mv "src/llvm-$commit" $module
         continue
     fi
     if [ ! -e "$cache_src_dir/$module/.git" ]; then
diff --git a/src/librustc_back/target/asmjs_unknown_emscripten.rs b/src/librustc_back/target/asmjs_unknown_emscripten.rs
index a54627279b02c..5d9f0f6012bf2 100644
--- a/src/librustc_back/target/asmjs_unknown_emscripten.rs
+++ b/src/librustc_back/target/asmjs_unknown_emscripten.rs
@@ -31,6 +31,7 @@ pub fn target() -> Result<Target, String> {
         max_atomic_width: Some(32),
         post_link_args: args,
         target_family: Some("unix".to_string()),
+        codegen_backend: "emscripten".to_string(),
         .. Default::default()
     };
     Ok(Target {
diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs
index 2e860f940a7a7..3c8a676dcc200 100644
--- a/src/librustc_back/target/mod.rs
+++ b/src/librustc_back/target/mod.rs
@@ -465,6 +465,9 @@ pub struct TargetOptions {
     /// Whether to lower 128-bit operations to compiler_builtins calls.  Use if
     /// your backend only supports 64-bit and smaller math.
     pub i128_lowering: bool,
+
+    /// The codegen backend to use for this target, typically "llvm"
+    pub codegen_backend: String,
 }
 
 impl Default for TargetOptions {
@@ -534,6 +537,7 @@ impl Default for TargetOptions {
             singlethread: false,
             no_builtins: false,
             i128_lowering: false,
+            codegen_backend: "llvm".to_string(),
         }
     }
 }
@@ -780,6 +784,7 @@ impl Target {
         key!(requires_lto, bool);
         key!(singlethread, bool);
         key!(no_builtins, bool);
+        key!(codegen_backend);
 
         if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) {
             for name in array.iter().filter_map(|abi| abi.as_string()) {
@@ -976,6 +981,7 @@ impl ToJson for Target {
         target_option_val!(requires_lto);
         target_option_val!(singlethread);
         target_option_val!(no_builtins);
+        target_option_val!(codegen_backend);
 
         if default.abi_blacklist != self.options.abi_blacklist {
             d.insert("abi-blacklist".to_string(), self.options.abi_blacklist.iter()
diff --git a/src/librustc_back/target/wasm32_unknown_emscripten.rs b/src/librustc_back/target/wasm32_unknown_emscripten.rs
index 197c1f7a4da49..4823541f2262c 100644
--- a/src/librustc_back/target/wasm32_unknown_emscripten.rs
+++ b/src/librustc_back/target/wasm32_unknown_emscripten.rs
@@ -35,6 +35,7 @@ pub fn target() -> Result<Target, String> {
         max_atomic_width: Some(32),
         post_link_args,
         target_family: Some("unix".to_string()),
+        codegen_backend: "emscripten".to_string(),
         .. Default::default()
     };
     Ok(Target {
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 029cceda532e5..6118ee94c84cf 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -218,19 +218,16 @@ pub fn get_trans(sess: &Session) -> Box<TransCrate> {
     static mut LOAD: fn() -> Box<TransCrate> = || unreachable!();
 
     INIT.call_once(|| {
-        let trans_name = sess.opts.debugging_opts.codegen_backend.as_ref();
-        let backend = match trans_name.map(|s| &**s) {
-            None |
-            Some("llvm") => get_trans_default(),
-            Some("metadata_only") => {
+        let trans_name = sess.opts.debugging_opts.codegen_backend.as_ref()
+            .unwrap_or(&sess.target.target.options.codegen_backend);
+        let backend = match &trans_name[..] {
+            "metadata_only" => {
                 rustc_trans_utils::trans_crate::MetadataOnlyTransCrate::new
             }
-            Some(filename) if filename.contains(".") => {
+            filename if filename.contains(".") => {
                 load_backend_from_dylib(filename.as_ref())
             }
-            Some(trans_name) => {
-                sess.fatal(&format!("unknown codegen backend {}", trans_name));
-            }
+            trans_name => get_trans_sysroot(trans_name),
         };
 
         unsafe {
@@ -242,7 +239,7 @@ pub fn get_trans(sess: &Session) -> Box<TransCrate> {
     backend
 }
 
-fn get_trans_default() -> fn() -> Box<TransCrate> {
+fn get_trans_sysroot(backend_name: &str) -> fn() -> Box<TransCrate> {
     // For now we only allow this function to be called once as it'll dlopen a
     // few things, which seems to work best if we only do that once. In
     // general this assertion never trips due to the once guard in `get_trans`,
@@ -324,6 +321,7 @@ fn get_trans_default() -> fn() -> Box<TransCrate> {
 
     let mut file: Option<PathBuf> = None;
 
+    let expected_name = format!("rustc_trans-{}", backend_name);
     for entry in d.filter_map(|e| e.ok()) {
         let path = entry.path();
         let filename = match path.file_name().and_then(|s| s.to_str()) {
@@ -334,7 +332,7 @@ fn get_trans_default() -> fn() -> Box<TransCrate> {
             continue
         }
         let name = &filename[DLL_PREFIX.len() .. filename.len() - DLL_SUFFIX.len()];
-        if !name.starts_with("rustc_trans") {
+        if name != expected_name {
             continue
         }
         if let Some(ref prev) = file {
@@ -350,8 +348,9 @@ fn get_trans_default() -> fn() -> Box<TransCrate> {
     match file {
         Some(ref s) => return load_backend_from_dylib(s),
         None => {
-            let err = format!("failed to load default codegen backend, no appropriate \
-                               codegen dylib found in `{}`", sysroot.display());
+            let err = format!("failed to load default codegen backend for `{}`, \
+                               no appropriate codegen dylib found in `{}`",
+                               backend_name, sysroot.display());
             early_error(ErrorOutputType::default(), &err);
         }
     }
@@ -1072,7 +1071,7 @@ pub fn version(binary: &str, matches: &getopts::Matches) {
         println!("commit-date: {}", unw(commit_date_str()));
         println!("host: {}", config::host_triple());
         println!("release: {}", unw(release_str()));
-        get_trans_default()().print_version();
+        get_trans_sysroot("llvm")().print_version();
     }
 }
 
@@ -1369,7 +1368,7 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
     }
 
     if cg_flags.contains(&"passes=list".to_string()) {
-        get_trans_default()().print_passes();
+        get_trans_sysroot("llvm")().print_passes();
         return None;
     }
 
diff --git a/src/librustc_llvm/Cargo.toml b/src/librustc_llvm/Cargo.toml
index 45e97127ede7f..0978c2ceb141d 100644
--- a/src/librustc_llvm/Cargo.toml
+++ b/src/librustc_llvm/Cargo.toml
@@ -10,6 +10,7 @@ path = "lib.rs"
 
 [features]
 static-libstdcpp = []
+emscripten = []
 
 [dependencies]
 bitflags = "1.0"
diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml
index 14591de31ca96..500c4fdf4e8dd 100644
--- a/src/librustc_trans/Cargo.toml
+++ b/src/librustc_trans/Cargo.toml
@@ -39,4 +39,11 @@ tempdir = "0.3"
 cc = "1.0.1"
 
 [features]
+# Used to communicate the feature to `rustc_back` in the same manner that the
+# `rustc` driver script communicate this.
 jemalloc = ["rustc_back/jemalloc"]
+
+# This is used to convince Cargo to separately cache builds of `rustc_trans`
+# when this option is enabled or not. That way we can build two, cache two
+# artifacts, and have nice speedy rebuilds.
+emscripten = ["rustc_llvm/emscripten"]
diff --git a/src/llvm-emscripten b/src/llvm-emscripten
new file mode 160000
index 0000000000000..2717444753318
--- /dev/null
+++ b/src/llvm-emscripten
@@ -0,0 +1 @@
+Subproject commit 2717444753318e461e0c3b30dacd03ffbac96903
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index eee2902bfb6f7..4d89008d5ca54 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -54,6 +54,7 @@ fn filter_dirs(path: &Path) -> bool {
         "src/dlmalloc",
         "src/jemalloc",
         "src/llvm",
+        "src/llvm-emscripten",
         "src/libbacktrace",
         "src/libcompiler_builtins",
         "src/librustc_data_structures/owning_ref",