about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-09-30 19:00:36 -0700
committerGitHub <noreply@github.com>2016-09-30 19:00:36 -0700
commit8b00355119ba0473eae420049f96f5be91be1bab (patch)
tree4a133c1bb38dd054db51c7b326f6ccd9d6f0a701
parentbba3fca20e3ddfdf1173c8ae2851b492ca2c98a6 (diff)
parentafa72b5dd6b75ed4577a4e73c1525dcd58d93b51 (diff)
downloadrust-8b00355119ba0473eae420049f96f5be91be1bab.tar.gz
rust-8b00355119ba0473eae420049f96f5be91be1bab.zip
Auto merge of #36339 - brson:emscripten-new, r=alexcrichton
Working asmjs and wasm targets

This patch set results in a working standard library for the asmjs-unknown-emscripten and wasm32-unknown-emscripten targets. It is based on the work of @badboy and @rschulman.

It does a few things:

- Updates LLVM with the emscripten [fastcomp](https://github.com/rust-lang/llvm/pull/50) patches, which include the pnacl IR legalizer and the asm.js backend. This patch is thought not to have any significant effect on existing targets.
- Teaches rustbuild to correctly link C code with emscripten
- Updates gcc-rs to work correctly with emscripten
- Teaches rustbuild to run crate tests for emscripten with node
- Modifies Thread::new to return an error on emscripten, to facilitate debugging a common failure mode
- Modifies libtest to run in single-threaded mode for emscripten
- Ignores a host of tests that don't work yet, mostly dealing with threads and I/O
- Updates libc with wasm32 definitions (presently the same as asmjs)
- Adds a wasm32-unknown-emscripten target that feeds the output of LLVM's asmjs backend through emcc to generate wasm

Notes and caveats:

- This is only known to work with `--enable-rustbuild`.
- The wasm32 target can't be tested correctly yet because of issues in compiletest and limitations in node https://github.com/kripken/emscripten/issues/4542, but hello.rs does seem to work when run on node via the binaryen interpreter
- This requires an up to date installation of the emscripten sdk from its incoming branch
- Unwinding is very broken
- When enabling the emscripten targets jemalloc is disabled for all targets, which results in test failures for the host

Next steps are to fix the jemalloc issue, start building the two emscripten targets on the auto builders, then start producing nightlies.

https://github.com/rust-lang/rust/issues/36317 tracks work on this.

Fixes https://github.com/rust-lang/rust/issues/36515
Fixes https://github.com/rust-lang/rust/issues/36515
Fixes https://github.com/rust-lang/rust/issues/36356
-rw-r--r--mk/cfg/wasm32-unknown-emscripten.mk24
-rw-r--r--mk/main.mk2
-rw-r--r--src/bootstrap/Cargo.lock48
-rw-r--r--src/bootstrap/check.rs39
-rw-r--r--src/bootstrap/lib.rs3
-rw-r--r--src/bootstrap/native.rs2
-rw-r--r--src/bootstrap/sanity.rs24
-rw-r--r--src/bootstrap/step.rs4
-rw-r--r--src/liballoc/arc.rs1
-rw-r--r--src/liballoc_jemalloc/build.rs18
-rw-r--r--src/liballoc_jemalloc/lib.rs262
-rw-r--r--src/liballoc_system/lib.rs3
-rw-r--r--src/libcollections/linked_list.rs1
-rw-r--r--src/libcollectionstest/slice.rs1
-rw-r--r--src/libcompiler_builtins/build.rs6
-rw-r--r--src/libcoretest/num/flt2dec/estimator.rs5
-rw-r--r--src/libpanic_unwind/Cargo.lock9
-rw-r--r--src/libpanic_unwind/emcc.rs78
-rw-r--r--src/libpanic_unwind/lib.rs8
-rw-r--r--src/librustc_back/target/asmjs_unknown_emscripten.rs1
-rw-r--r--src/librustc_back/target/mod.rs3
-rw-r--r--src/librustc_back/target/wasm32_unknown_emscripten.rs42
-rw-r--r--src/librustc_driver/lib.rs4
-rw-r--r--src/librustc_llvm/build.rs3
-rw-r--r--src/librustc_llvm/lib.rs4
-rw-r--r--src/librustc_trans/abi.rs1
-rw-r--r--src/librustdoc/test.rs2
-rw-r--r--src/libstd/env.rs21
-rw-r--r--src/libstd/fs.rs2
-rw-r--r--src/libstd/io/buffered.rs1
-rw-r--r--src/libstd/io/mod.rs2
-rw-r--r--src/libstd/io/stdio.rs9
-rw-r--r--src/libstd/net/addr.rs2
-rw-r--r--src/libstd/net/ip.rs2
-rw-r--r--src/libstd/net/mod.rs3
-rw-r--r--src/libstd/net/tcp.rs2
-rw-r--r--src/libstd/net/udp.rs2
-rw-r--r--src/libstd/os/linux/raw.rs3
-rw-r--r--src/libstd/process.rs2
-rw-r--r--src/libstd/rand/mod.rs1
-rw-r--r--src/libstd/sync/barrier.rs1
-rw-r--r--src/libstd/sync/condvar.rs4
-rw-r--r--src/libstd/sync/mpsc/mod.rs4
-rw-r--r--src/libstd/sync/mpsc/mpsc_queue.rs2
-rw-r--r--src/libstd/sync/mpsc/select.rs2
-rw-r--r--src/libstd/sync/mpsc/spsc_queue.rs2
-rw-r--r--src/libstd/sync/mutex.rs2
-rw-r--r--src/libstd/sync/once.rs2
-rw-r--r--src/libstd/sync/rwlock.rs2
-rw-r--r--src/libstd/sys/common/io.rs3
-rw-r--r--src/libstd/sys/common/remutex.rs2
-rw-r--r--src/libstd/sys/unix/ext/net.rs2
-rw-r--r--src/libstd/sys/unix/process.rs4
-rw-r--r--src/libstd/sys/unix/thread.rs18
-rw-r--r--src/libstd/thread/local.rs2
-rw-r--r--src/libstd/thread/mod.rs2
-rw-r--r--src/libtest/lib.rs69
-rw-r--r--src/libunwind/libunwind.rs2
m---------src/llvm0
-rw-r--r--src/rustllvm/llvm-auto-clean-trigger2
-rw-r--r--src/test/compile-fail/allocator-dylib-is-system.rs2
-rw-r--r--src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs2
-rw-r--r--src/test/run-fail/panic-task-name-none.rs1
-rw-r--r--src/test/run-fail/panic-task-name-owned.rs1
-rw-r--r--src/test/run-fail/task-spawn-barefn.rs1
-rw-r--r--src/test/run-fail/test-panic.rs1
-rw-r--r--src/test/run-fail/test-should-fail-bad-message.rs1
-rw-r--r--src/test/run-fail/test-tasks-invalid-value.rs1
-rw-r--r--src/test/run-pass/allocator-override.rs1
-rw-r--r--src/test/run-pass/extern-pass-empty.rs1
-rw-r--r--src/test/run-pass/format-no-std.rs2
-rw-r--r--src/test/run-pass/packed-struct-layout.rs1
-rw-r--r--src/test/run-pass/packed-tuple-struct-layout.rs1
-rw-r--r--src/test/run-pass/panic-runtime/abort-link-to-unwinding-crates.rs1
-rw-r--r--src/test/run-pass/panic-runtime/abort.rs1
-rw-r--r--src/test/run-pass/panic-runtime/lto-abort.rs1
-rw-r--r--src/test/run-pass/panic-runtime/lto-unwind.rs1
-rw-r--r--src/test/run-pass/process-status-inherits-stdin.rs1
-rw-r--r--src/test/run-pass/simd-intrinsic-generic-cast.rs1
-rw-r--r--src/test/run-pass/task-stderr.rs2
-rw-r--r--src/tools/compiletest/src/common.rs1
-rw-r--r--src/tools/compiletest/src/main.rs11
-rw-r--r--src/tools/compiletest/src/runtest.rs8
-rw-r--r--src/tools/compiletest/src/util.rs3
84 files changed, 614 insertions, 210 deletions
diff --git a/mk/cfg/wasm32-unknown-emscripten.mk b/mk/cfg/wasm32-unknown-emscripten.mk
new file mode 100644
index 00000000000..997bdfbf03a
--- /dev/null
+++ b/mk/cfg/wasm32-unknown-emscripten.mk
@@ -0,0 +1,24 @@
+# wasm32-unknown-emscripten configuration
+CC_wasm32-unknown-emscripten=emcc
+CXX_wasm32-unknown-emscripten=em++
+CPP_wasm32-unknown-emscripten=$(CPP)
+AR_wasm32-unknown-emscripten=emar
+CFG_LIB_NAME_wasm32-unknown-emscripten=lib$(1).so
+CFG_STATIC_LIB_NAME_wasm32-unknown-emscripten=lib$(1).a
+CFG_LIB_GLOB_wasm32-unknown-emscripten=lib$(1)-*.so
+CFG_LIB_DSYM_GLOB_wasm32-unknown-emscripten=lib$(1)-*.dylib.dSYM
+CFG_JEMALLOC_CFLAGS_wasm32-unknown-emscripten := -m32 $(CFLAGS)
+CFG_GCCISH_CFLAGS_wasm32-unknown-emscripten :=  -g -fPIC -m32 -s BINARYEN=1 $(CFLAGS)
+CFG_GCCISH_CXXFLAGS_wasm32-unknown-emscripten := -fno-rtti -s BINARYEN=1 $(CXXFLAGS)
+CFG_GCCISH_LINK_FLAGS_wasm32-unknown-emscripten := -shared -fPIC -ldl -pthread  -lrt -g -m32 -s BINARYEN=1
+CFG_GCCISH_DEF_FLAG_wasm32-unknown-emscripten := -Wl,--export-dynamic,--dynamic-list=
+CFG_LLC_FLAGS_wasm32-unknown-emscripten :=
+CFG_INSTALL_NAME_wasm32-unknown-emscripten =
+CFG_EXE_SUFFIX_wasm32-unknown-emscripten =
+CFG_WINDOWSY_wasm32-unknown-emscripten :=
+CFG_UNIXY_wasm32-unknown-emscripten := 1
+CFG_LDPATH_wasm32-unknown-emscripten :=
+CFG_RUN_wasm32-unknown-emscripten=$(2)
+CFG_RUN_TARG_wasm32-unknown-emscripten=$(call CFG_RUN_wasm32-unknown-emscripten,,$(2))
+CFG_GNU_TRIPLE_wasm32-unknown-emscripten := wasm32-unknown-emscripten
+CFG_DISABLE_JEMALLOC_wasm32-unknown-emscripten := 1
diff --git a/mk/main.mk b/mk/main.mk
index cc843add50d..e68a8f30055 100644
--- a/mk/main.mk
+++ b/mk/main.mk
@@ -300,7 +300,7 @@ endif
 # LLVM macros
 ######################################################################
 
-LLVM_OPTIONAL_COMPONENTS=x86 arm aarch64 mips powerpc pnacl systemz
+LLVM_OPTIONAL_COMPONENTS=x86 arm aarch64 mips powerpc pnacl systemz jsbackend
 LLVM_REQUIRED_COMPONENTS=ipo bitreader bitwriter linker asmparser mcjit \
                 interpreter instrumentation
 
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
index 36b94e4ebea..babbb6a16ba 100644
--- a/src/bootstrap/Cargo.lock
+++ b/src/bootstrap/Cargo.lock
@@ -5,16 +5,16 @@ dependencies = [
  "build_helper 0.1.0",
  "cmake 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "gcc 0.3.31 (git+https://github.com/alexcrichton/gcc-rs)",
+ "gcc 0.3.35 (git+https://github.com/alexcrichton/gcc-rs)",
  "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "md5 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.1.73 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "toml 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -34,7 +34,7 @@ name = "cmake"
 version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "gcc 0.3.31 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -42,17 +42,17 @@ name = "filetime"
 version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "gcc"
-version = "0.3.31"
-source = "git+https://github.com/alexcrichton/gcc-rs#b8e2400883f1a2749b323354dad372cdd1c838c7"
+version = "0.3.35"
+source = "git+https://github.com/alexcrichton/gcc-rs#8ff5360b6e0dc4f3c9d3f71036f1ff403c68469d"
 
 [[package]]
 name = "gcc"
-version = "0.3.31"
+version = "0.3.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -65,13 +65,13 @@ name = "kernel32-sys"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "libc"
-version = "0.2.10"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -84,15 +84,15 @@ name = "memchr"
 version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "num_cpus"
-version = "0.2.11"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -123,7 +123,7 @@ version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -136,7 +136,7 @@ dependencies = [
 
 [[package]]
 name = "toml"
-version = "0.1.28"
+version = "0.1.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -149,7 +149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "winapi"
-version = "0.2.6"
+version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -161,20 +161,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2b3fb52b09c1710b961acb35390d514be82e4ac96a9969a8e38565a29b878dc9"
 "checksum cmake 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "dfcf5bcece56ef953b8ea042509e9dcbdfe97820b7e20d86beb53df30ed94978"
 "checksum filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5363ab8e4139b8568a6237db5248646e5a8a2f89bd5ccb02092182b11fd3e922"
-"checksum gcc 0.3.31 (git+https://github.com/alexcrichton/gcc-rs)" = "<none>"
-"checksum gcc 0.3.31 (registry+https://github.com/rust-lang/crates.io-index)" = "cfe877476e53690ebb0ce7325d0bf43e198d9500291b54b3c65e518de5039b07"
+"checksum gcc 0.3.35 (git+https://github.com/alexcrichton/gcc-rs)" = "<none>"
+"checksum gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "91ecd03771effb0c968fd6950b37e89476a578aaf1c70297d8e92b6516ec3312"
 "checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-"checksum libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "55f3730be7e803cf350d32061958171731c2395831fbd67a61083782808183e0"
+"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2"
 "checksum md5 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a5539a8dee9b4ae308c9c406a379838b435a8f2c84cf9fedc6d5a576be9888db"
 "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
-"checksum num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "51fedae97a05f7353612fe017ab705a37e6db8f4d67c5c6fe739a9e70d6eed09"
+"checksum num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "cee7e88156f3f9e19bdd598f8d6c9db7bf4078f99f8381f43a55b09648d1a6e3"
 "checksum regex 0.1.73 (registry+https://github.com/rust-lang/crates.io-index)" = "56b7ee9f764ecf412c6e2fff779bca4b22980517ae335a21aeaf4e32625a5df2"
 "checksum regex-syntax 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "31040aad7470ad9d8c46302dcffba337bb4289ca5da2e3cd6e37b64109a85199"
 "checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b"
 "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
 "checksum thread_local 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "55dd963dbaeadc08aa7266bf7f91c3154a7805e32bb94b820b769d2ef3b4744d"
-"checksum toml 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "fcd27a04ca509aff336ba5eb2abc58d456f52c4ff64d9724d88acb85ead560b6"
+"checksum toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "0590d72182e50e879c4da3b11c6488dae18fccb1ae0c7a3eda18e16795844796"
 "checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
-"checksum winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfaaa8fbdaa618fa6914b59b2769d690dd7521920a18d84b42d254678dd5fd4"
+"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
 "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index 2b9d717cbd4..b8417218a22 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -108,6 +108,10 @@ pub fn compiletest(build: &Build,
     cmd.arg("--host").arg(compiler.host);
     cmd.arg("--llvm-filecheck").arg(build.llvm_filecheck(&build.config.build));
 
+    if let Some(nodejs) = build.config.nodejs.as_ref() {
+        cmd.arg("--nodejs").arg(nodejs);
+    }
+
     let mut flags = vec!["-Crpath".to_string()];
     if build.config.rust_optimize_tests {
         flags.push("-O".to_string());
@@ -323,6 +327,9 @@ pub fn krate(build: &Build,
     if target.contains("android") {
         build.run(cargo.arg("--no-run"));
         krate_android(build, compiler, target, mode);
+    } else if target.contains("emscripten") {
+        build.run(cargo.arg("--no-run"));
+        krate_emscripten(build, compiler, target, mode);
     } else {
         cargo.args(&build.flags.args);
         build.run(&mut cargo);
@@ -371,6 +378,35 @@ fn krate_android(build: &Build,
     }
 }
 
+fn krate_emscripten(build: &Build,
+                    compiler: &Compiler,
+                    target: &str,
+                    mode: Mode) {
+     let mut tests = Vec::new();
+     let out_dir = build.cargo_out(compiler, mode, target);
+     find_tests(&out_dir, target, &mut tests);
+     find_tests(&out_dir.join("deps"), target, &mut tests);
+
+     for test in tests {
+         let test_file_name = test.to_string_lossy().into_owned();
+         println!("running {}", test_file_name);
+         let nodejs = build.config.nodejs.as_ref().expect("nodejs not configured");
+         let status = Command::new(nodejs)
+             .arg(&test_file_name)
+             .stderr(::std::process::Stdio::inherit())
+             .status();
+         match status {
+             Ok(status) => {
+                 if !status.success() {
+                     panic!("some tests failed");
+                 }
+             }
+             Err(e) => panic!(format!("failed to execute command: {}", e)),
+         };
+     }
+ }
+
+
 fn find_tests(dir: &Path,
               target: &str,
               dst: &mut Vec<PathBuf>) {
@@ -381,7 +417,8 @@ fn find_tests(dir: &Path,
         }
         let filename = e.file_name().into_string().unwrap();
         if (target.contains("windows") && filename.ends_with(".exe")) ||
-           (!target.contains("windows") && !filename.contains(".")) {
+           (!target.contains("windows") && !filename.contains(".")) ||
+           (target.contains("emscripten") && filename.contains(".js")){
             dst.push(e.path());
         }
     }
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 3d1cf47cb7e..033cefb8dea 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -975,7 +975,8 @@ impl Build {
         // than an entry here.
 
         let mut base = Vec::new();
-        if target != self.config.build && !target.contains("msvc") {
+        if target != self.config.build && !target.contains("msvc") &&
+            !target.contains("emscripten") {
             base.push(format!("-Clinker={}", self.cc(target).display()));
         }
         return base
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index df6408e5fe1..63fc59e4328 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -65,7 +65,7 @@ pub fn llvm(build: &Build, target: &str) {
        .out_dir(&dst)
        .profile(if build.config.llvm_optimize {"Release"} else {"Debug"})
        .define("LLVM_ENABLE_ASSERTIONS", assertions)
-       .define("LLVM_TARGETS_TO_BUILD", "X86;ARM;AArch64;Mips;PowerPC;SystemZ")
+       .define("LLVM_TARGETS_TO_BUILD", "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend")
        .define("LLVM_INCLUDE_EXAMPLES", "OFF")
        .define("LLVM_INCLUDE_TESTS", "OFF")
        .define("LLVM_INCLUDE_DOCS", "OFF")
diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs
index 962d0666f69..c4e6399c2c3 100644
--- a/src/bootstrap/sanity.rs
+++ b/src/bootstrap/sanity.rs
@@ -95,6 +95,13 @@ pub fn check(build: &mut Build) {
     // We're gonna build some custom C code here and there, host triples
     // also build some C++ shims for LLVM so we need a C++ compiler.
     for target in build.config.target.iter() {
+        // On emscripten we don't actually need the C compiler to just
+        // build the target artifacts, only for testing. For the sake
+        // of easier bot configuration, just skip detection.
+        if target.contains("emscripten") {
+            continue;
+        }
+
         need_cmd(build.cc(target).as_ref());
         if let Some(ar) = build.ar(target) {
             need_cmd(ar.as_ref());
@@ -104,6 +111,14 @@ pub fn check(build: &mut Build) {
         need_cmd(build.cxx(host).as_ref());
     }
 
+    // The msvc hosts don't use jemalloc, turn it off globally to
+    // avoid packaging the dummy liballoc_jemalloc on that platform.
+    for host in build.config.host.iter() {
+        if host.contains("msvc") {
+            build.config.use_jemalloc = false;
+        }
+    }
+
     // Externally configured LLVM requires FileCheck to exist
     let filecheck = build.llvm_filecheck(&build.config.build);
     if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests {
@@ -111,15 +126,6 @@ pub fn check(build: &mut Build) {
     }
 
     for target in build.config.target.iter() {
-        // Either can't build or don't want to run jemalloc on these targets
-        if target.contains("rumprun") ||
-           target.contains("bitrig") ||
-           target.contains("openbsd") ||
-           target.contains("msvc") ||
-           target.contains("emscripten") {
-            build.config.use_jemalloc = false;
-        }
-
         // Can't compile for iOS unless we're on OSX
         if target.contains("apple-ios") &&
            !build.config.build.contains("apple-darwin") {
diff --git a/src/bootstrap/step.rs b/src/bootstrap/step.rs
index 4b5a26d205a..8a96cd1b4d2 100644
--- a/src/bootstrap/step.rs
+++ b/src/bootstrap/step.rs
@@ -418,7 +418,6 @@ impl<'a> Step<'a> {
                     self.check_crate_std(compiler),
                     self.check_crate_test(compiler),
                     self.check_debuginfo(compiler),
-                    self.dist(stage),
                 ];
 
                 // If we're testing the build triple, then we know we can
@@ -463,6 +462,9 @@ impl<'a> Step<'a> {
                         // misc
                         self.check_linkcheck(stage),
                         self.check_tidy(stage),
+
+                        // can we make the distributables?
+                        self.dist(stage),
                     ]);
                 }
                 return base
diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs
index e3c92fc1aa8..29e18781ce2 100644
--- a/src/liballoc/arc.rs
+++ b/src/liballoc/arc.rs
@@ -1000,6 +1000,7 @@ mod tests {
     }
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn manually_share_arc() {
         let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
         let arc_v = Arc::new(v);
diff --git a/src/liballoc_jemalloc/build.rs b/src/liballoc_jemalloc/build.rs
index 8b31c5a5577..028d742cc83 100644
--- a/src/liballoc_jemalloc/build.rs
+++ b/src/liballoc_jemalloc/build.rs
@@ -27,6 +27,24 @@ fn main() {
     let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
     let src_dir = env::current_dir().unwrap();
 
+    // FIXME: This is a hack to support building targets that don't
+    // support jemalloc alongside hosts that do. The jemalloc build is
+    // controlled by a feature of the std crate, and if that feature
+    // changes between targets, it invalidates the fingerprint of
+    // std's build script (this is a cargo bug); so we must ensure
+    // that the feature set used by std is the same across all
+    // targets, which means we have to build the alloc_jemalloc crate
+    // for targets like emscripten, even if we don't use it.
+    if target.contains("rumprun") ||
+        target.contains("bitrig") ||
+        target.contains("openbsd") ||
+        target.contains("msvc") ||
+        target.contains("emscripten")
+    {
+        println!("cargo:rustc-cfg=dummy_jemalloc");
+        return;
+    }
+
     if let Some(jemalloc) = env::var_os("JEMALLOC_OVERRIDE") {
         let jemalloc = PathBuf::from(jemalloc);
         println!("cargo:rustc-link-search=native={}",
diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs
index 5bbf1c35e0d..21e45f9c4b2 100644
--- a/src/liballoc_jemalloc/lib.rs
+++ b/src/liballoc_jemalloc/lib.rs
@@ -23,124 +23,170 @@
 
 extern crate libc;
 
-use libc::{c_int, c_void, size_t};
+pub use imp::*;
 
-// Linkage directives to pull in jemalloc and its dependencies.
-//
-// On some platforms we need to be sure to link in `pthread` which jemalloc
-// depends on, and specifically on android we need to also link to libgcc.
-// Currently jemalloc is compiled with gcc which will generate calls to
-// intrinsics that are libgcc specific (e.g. those intrinsics aren't present in
-// libcompiler-rt), so link that in to get that support.
-#[link(name = "jemalloc", kind = "static")]
-#[cfg_attr(target_os = "android", link(name = "gcc"))]
-#[cfg_attr(all(not(windows),
-               not(target_os = "android"),
-               not(target_env = "musl")),
-           link(name = "pthread"))]
-#[cfg(not(cargobuild))]
-extern "C" {}
-
-// Note that the symbols here are prefixed by default on OSX and Windows (we
-// don't explicitly request it), and on Android and DragonFly we explicitly
-// request it as unprefixing cause segfaults (mismatches in allocators).
-extern "C" {
-    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
-                   target_os = "dragonfly", target_os = "windows"),
-               link_name = "je_mallocx")]
-    fn mallocx(size: size_t, flags: c_int) -> *mut c_void;
-    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
-                   target_os = "dragonfly", target_os = "windows"),
-               link_name = "je_rallocx")]
-    fn rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void;
-    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
-                   target_os = "dragonfly", target_os = "windows"),
-               link_name = "je_xallocx")]
-    fn xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t;
-    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
-                   target_os = "dragonfly", target_os = "windows"),
-               link_name = "je_sdallocx")]
-    fn sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
-    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
-                   target_os = "dragonfly", target_os = "windows"),
-               link_name = "je_nallocx")]
-    fn nallocx(size: size_t, flags: c_int) -> size_t;
-}
+// See comments in build.rs for why we sometimes build a crate that does nothing
+#[cfg(not(dummy_jemalloc))]
+mod imp {
+    use libc::{c_int, c_void, size_t};
 
-// The minimum alignment guaranteed by the architecture. This value is used to
-// add fast paths for low alignment values. In practice, the alignment is a
-// constant at the call site and the branch will be optimized out.
-#[cfg(all(any(target_arch = "arm",
-              target_arch = "mips",
-              target_arch = "powerpc")))]
-const MIN_ALIGN: usize = 8;
-#[cfg(all(any(target_arch = "x86",
-              target_arch = "x86_64",
-              target_arch = "aarch64",
-              target_arch = "powerpc64",
-              target_arch = "mips64",
-              target_arch = "s390x")))]
-const MIN_ALIGN: usize = 16;
-
-// MALLOCX_ALIGN(a) macro
-fn mallocx_align(a: usize) -> c_int {
-    a.trailing_zeros() as c_int
-}
+    // Linkage directives to pull in jemalloc and its dependencies.
+    //
+    // On some platforms we need to be sure to link in `pthread` which jemalloc
+    // depends on, and specifically on android we need to also link to libgcc.
+    // Currently jemalloc is compiled with gcc which will generate calls to
+    // intrinsics that are libgcc specific (e.g. those intrinsics aren't present in
+    // libcompiler-rt), so link that in to get that support.
+    #[link(name = "jemalloc", kind = "static")]
+    #[cfg_attr(target_os = "android", link(name = "gcc"))]
+    #[cfg_attr(all(not(windows),
+                   not(target_os = "android"),
+                   not(target_env = "musl")),
+               link(name = "pthread"))]
+    #[cfg(not(cargobuild))]
+    extern "C" {}
+
+    // Note that the symbols here are prefixed by default on OSX and Windows (we
+    // don't explicitly request it), and on Android and DragonFly we explicitly
+    // request it as unprefixing cause segfaults (mismatches in allocators).
+    extern "C" {
+        #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
+                       target_os = "dragonfly", target_os = "windows"),
+                   link_name = "je_mallocx")]
+        fn mallocx(size: size_t, flags: c_int) -> *mut c_void;
+        #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
+                       target_os = "dragonfly", target_os = "windows"),
+                   link_name = "je_rallocx")]
+        fn rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void;
+        #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
+                       target_os = "dragonfly", target_os = "windows"),
+                   link_name = "je_xallocx")]
+        fn xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t;
+        #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
+                       target_os = "dragonfly", target_os = "windows"),
+                   link_name = "je_sdallocx")]
+        fn sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
+        #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
+                       target_os = "dragonfly", target_os = "windows"),
+                   link_name = "je_nallocx")]
+        fn nallocx(size: size_t, flags: c_int) -> size_t;
+    }
+
+    // The minimum alignment guaranteed by the architecture. This value is used to
+    // add fast paths for low alignment values. In practice, the alignment is a
+    // constant at the call site and the branch will be optimized out.
+    #[cfg(all(any(target_arch = "arm",
+                  target_arch = "mips",
+                  target_arch = "powerpc")))]
+    const MIN_ALIGN: usize = 8;
+    #[cfg(all(any(target_arch = "x86",
+                  target_arch = "x86_64",
+                  target_arch = "aarch64",
+                  target_arch = "powerpc64",
+                  target_arch = "mips64",
+                  target_arch = "s390x")))]
+    const MIN_ALIGN: usize = 16;
+
+    // MALLOCX_ALIGN(a) macro
+    fn mallocx_align(a: usize) -> c_int {
+        a.trailing_zeros() as c_int
+    }
+
+    fn align_to_flags(align: usize) -> c_int {
+        if align <= MIN_ALIGN {
+            0
+        } else {
+            mallocx_align(align)
+        }
+    }
+
+    #[no_mangle]
+    pub extern "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
+        let flags = align_to_flags(align);
+        unsafe { mallocx(size as size_t, flags) as *mut u8 }
+    }
+
+    #[no_mangle]
+    pub extern "C" fn __rust_reallocate(ptr: *mut u8,
+                                        _old_size: usize,
+                                        size: usize,
+                                        align: usize)
+                                        -> *mut u8 {
+        let flags = align_to_flags(align);
+        unsafe { rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8 }
+    }
+
+    #[no_mangle]
+    pub extern "C" fn __rust_reallocate_inplace(ptr: *mut u8,
+                                                _old_size: usize,
+                                                size: usize,
+                                                align: usize)
+                                                -> usize {
+        let flags = align_to_flags(align);
+        unsafe { xallocx(ptr as *mut c_void, size as size_t, 0, flags) as usize }
+    }
 
-fn align_to_flags(align: usize) -> c_int {
-    if align <= MIN_ALIGN {
+    #[no_mangle]
+    pub extern "C" fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
+        let flags = align_to_flags(align);
+        unsafe { sdallocx(ptr as *mut c_void, old_size as size_t, flags) }
+    }
+
+    #[no_mangle]
+    pub extern "C" fn __rust_usable_size(size: usize, align: usize) -> usize {
+        let flags = align_to_flags(align);
+        unsafe { nallocx(size as size_t, flags) as usize }
+    }
+
+    // These symbols are used by jemalloc on android but the really old android
+    // we're building on doesn't have them defined, so just make sure the symbols
+    // are available.
+    #[no_mangle]
+    #[cfg(target_os = "android")]
+    pub extern "C" fn pthread_atfork(_prefork: *mut u8,
+                                     _postfork_parent: *mut u8,
+                                     _postfork_child: *mut u8)
+                                     -> i32 {
         0
-    } else {
-        mallocx_align(align)
     }
 }
 
-#[no_mangle]
-pub extern "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
-    let flags = align_to_flags(align);
-    unsafe { mallocx(size as size_t, flags) as *mut u8 }
-}
+#[cfg(dummy_jemalloc)]
+mod imp {
+    fn bogus() -> ! {
+        panic!("jemalloc is not implemented for this platform");
+    }
 
-#[no_mangle]
-pub extern "C" fn __rust_reallocate(ptr: *mut u8,
-                                    _old_size: usize,
-                                    size: usize,
-                                    align: usize)
-                                    -> *mut u8 {
-    let flags = align_to_flags(align);
-    unsafe { rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8 }
-}
+    #[no_mangle]
+    pub extern "C" fn __rust_allocate(_size: usize, _align: usize) -> *mut u8 {
+        bogus()
+    }
 
-#[no_mangle]
-pub extern "C" fn __rust_reallocate_inplace(ptr: *mut u8,
-                                            _old_size: usize,
-                                            size: usize,
-                                            align: usize)
-                                            -> usize {
-    let flags = align_to_flags(align);
-    unsafe { xallocx(ptr as *mut c_void, size as size_t, 0, flags) as usize }
-}
+    #[no_mangle]
+    pub extern "C" fn __rust_reallocate(_ptr: *mut u8,
+                                        _old_size: usize,
+                                        _size: usize,
+                                        _align: usize)
+                                        -> *mut u8 {
+        bogus()
+    }
 
-#[no_mangle]
-pub extern "C" fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
-    let flags = align_to_flags(align);
-    unsafe { sdallocx(ptr as *mut c_void, old_size as size_t, flags) }
-}
+    #[no_mangle]
+    pub extern "C" fn __rust_reallocate_inplace(_ptr: *mut u8,
+                                                _old_size: usize,
+                                                _size: usize,
+                                                _align: usize)
+                                                -> usize {
+        bogus()
+    }
 
-#[no_mangle]
-pub extern "C" fn __rust_usable_size(size: usize, align: usize) -> usize {
-    let flags = align_to_flags(align);
-    unsafe { nallocx(size as size_t, flags) as usize }
-}
+    #[no_mangle]
+    pub extern "C" fn __rust_deallocate(_ptr: *mut u8, _old_size: usize, _align: usize) {
+        bogus()
+    }
 
-// These symbols are used by jemalloc on android but the really old android
-// we're building on doesn't have them defined, so just make sure the symbols
-// are available.
-#[no_mangle]
-#[cfg(target_os = "android")]
-pub extern "C" fn pthread_atfork(_prefork: *mut u8,
-                                 _postfork_parent: *mut u8,
-                                 _postfork_child: *mut u8)
-                                 -> i32 {
-    0
+    #[no_mangle]
+    pub extern "C" fn __rust_usable_size(_size: usize, _align: usize) -> usize {
+        bogus()
+    }
 }
diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs
index 01407d1acd2..dacafe771ed 100644
--- a/src/liballoc_system/lib.rs
+++ b/src/liballoc_system/lib.rs
@@ -29,7 +29,8 @@
               target_arch = "mips",
               target_arch = "powerpc",
               target_arch = "powerpc64",
-              target_arch = "asmjs")))]
+              target_arch = "asmjs",
+              target_arch = "wasm32")))]
 const MIN_ALIGN: usize = 8;
 #[cfg(all(any(target_arch = "x86_64",
               target_arch = "aarch64",
diff --git a/src/libcollections/linked_list.rs b/src/libcollections/linked_list.rs
index 690c4f4af35..67f3708a62b 100644
--- a/src/libcollections/linked_list.rs
+++ b/src/libcollections/linked_list.rs
@@ -1294,6 +1294,7 @@ mod tests {
     }
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn test_send() {
         let n = list_from(&[1, 2, 3]);
         thread::spawn(move || {
diff --git a/src/libcollectionstest/slice.rs b/src/libcollectionstest/slice.rs
index 5b341ab62d0..9580714075a 100644
--- a/src/libcollectionstest/slice.rs
+++ b/src/libcollectionstest/slice.rs
@@ -1116,6 +1116,7 @@ fn test_box_slice_clone() {
 }
 
 #[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
 fn test_box_slice_clone_panics() {
     use std::sync::Arc;
     use std::sync::atomic::{AtomicUsize, Ordering};
diff --git a/src/libcompiler_builtins/build.rs b/src/libcompiler_builtins/build.rs
index 66c683333b9..acbd39bb163 100644
--- a/src/libcompiler_builtins/build.rs
+++ b/src/libcompiler_builtins/build.rs
@@ -73,6 +73,12 @@ impl Sources {
 
 fn main() {
     let target = env::var("TARGET").expect("TARGET was not set");
+
+    // Emscripten's runtime includes all the builtins
+    if target.contains("emscripten") {
+        return;
+    }
+
     let cfg = &mut gcc::Config::new();
 
     if target.contains("msvc") {
diff --git a/src/libcoretest/num/flt2dec/estimator.rs b/src/libcoretest/num/flt2dec/estimator.rs
index 857aae72c8a..0bca616ea9a 100644
--- a/src/libcoretest/num/flt2dec/estimator.rs
+++ b/src/libcoretest/num/flt2dec/estimator.rs
@@ -8,6 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// FIXME https://github.com/kripken/emscripten/issues/4563
+// NB we have to actually not compile this test to avoid
+// an undefined symbol error
+#![cfg(not(target_os = "emscripten"))]
+
 use core::num::flt2dec::estimator::*;
 
 #[test]
diff --git a/src/libpanic_unwind/Cargo.lock b/src/libpanic_unwind/Cargo.lock
index 20d826d4a47..0cf75c94150 100644
--- a/src/libpanic_unwind/Cargo.lock
+++ b/src/libpanic_unwind/Cargo.lock
@@ -5,6 +5,7 @@ dependencies = [
  "alloc 0.0.0",
  "core 0.0.0",
  "libc 0.0.0",
+ "unwind 0.0.0",
 ]
 
 [[package]]
@@ -25,3 +26,11 @@ dependencies = [
  "core 0.0.0",
 ]
 
+[[package]]
+name = "unwind"
+version = "0.0.0"
+dependencies = [
+ "core 0.0.0",
+ "libc 0.0.0",
+]
+
diff --git a/src/libpanic_unwind/emcc.rs b/src/libpanic_unwind/emcc.rs
new file mode 100644
index 00000000000..b3ab1117674
--- /dev/null
+++ b/src/libpanic_unwind/emcc.rs
@@ -0,0 +1,78 @@
+// 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.
+
+//! Unwinding for emscripten
+//!
+//! Whereas Rust's usual unwinding implementation for Unix platforms
+//! calls into the libunwind APIs directly, on emscripten we instead
+//! call into the C++ unwinding APIs. This is just an expedience since
+//! emscripten's runtime always implements those APIs and does not
+//! implement libunwind.
+
+#![allow(private_no_mangle_fns)]
+
+use core::any::Any;
+use core::ptr;
+use alloc::boxed::Box;
+use libc::{self, c_int};
+use unwind as uw;
+use core::mem;
+
+pub fn payload() -> *mut u8 {
+    ptr::null_mut()
+}
+
+pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send> {
+    assert!(!ptr.is_null());
+    let ex = ptr::read(ptr as *mut _);
+    __cxa_free_exception(ptr as *mut _);
+    ex
+}
+
+pub unsafe fn panic(data: Box<Any + Send>) -> u32 {
+    let sz = mem::size_of_val(&data);
+    let exception = __cxa_allocate_exception(sz);
+    if exception == ptr::null_mut() {
+        return uw::_URC_FATAL_PHASE1_ERROR as u32;
+    }
+    let exception = exception as *mut Box<Any + Send>;
+    ptr::write(exception, data);
+    __cxa_throw(exception as *mut _, ptr::null_mut(), ptr::null_mut());
+
+    unreachable!()
+}
+
+#[lang = "eh_personality"]
+#[no_mangle]
+unsafe extern "C" fn rust_eh_personality(version: c_int,
+                                         actions: uw::_Unwind_Action,
+                                         exception_class: uw::_Unwind_Exception_Class,
+                                         exception_object: *mut uw::_Unwind_Exception,
+                                         context: *mut uw::_Unwind_Context)
+                                         -> uw::_Unwind_Reason_Code {
+    __gxx_personality_v0(version, actions,
+                         exception_class,
+                         exception_object,
+                         context)
+}
+
+extern {
+    fn __cxa_allocate_exception(thrown_size: libc::size_t) -> *mut libc::c_void;
+    fn __cxa_free_exception(thrown_exception: *mut libc::c_void);
+    fn __cxa_throw(thrown_exception: *mut libc::c_void,
+                   tinfo: *mut libc::c_void,
+                   dest: *mut libc::c_void);
+    fn __gxx_personality_v0(version: c_int,
+                            actions: uw::_Unwind_Action,
+                            exception_class: uw::_Unwind_Exception_Class,
+                            exception_object: *mut uw::_Unwind_Exception,
+                            context: *mut uw::_Unwind_Context)
+                            -> uw::_Unwind_Reason_Code;
+}
diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs
index 11dd9befe0a..ff483fa823e 100644
--- a/src/libpanic_unwind/lib.rs
+++ b/src/libpanic_unwind/lib.rs
@@ -68,10 +68,16 @@ mod imp;
 mod imp;
 
 // i686-pc-windows-gnu and all others
-#[cfg(any(unix, all(windows, target_arch = "x86", target_env = "gnu")))]
+#[cfg(any(all(unix, not(target_os = "emscripten")),
+          all(windows, target_arch = "x86", target_env = "gnu")))]
 #[path = "gcc.rs"]
 mod imp;
 
+// emscripten
+#[cfg(target_os = "emscripten")]
+#[path = "emcc.rs"]
+mod imp;
+
 mod dwarf;
 mod windows;
 
diff --git a/src/librustc_back/target/asmjs_unknown_emscripten.rs b/src/librustc_back/target/asmjs_unknown_emscripten.rs
index 9ccfdbb129c..667f7cf2c21 100644
--- a/src/librustc_back/target/asmjs_unknown_emscripten.rs
+++ b/src/librustc_back/target/asmjs_unknown_emscripten.rs
@@ -22,6 +22,7 @@ pub fn target() -> Result<Target, String> {
         allow_asm: false,
         obj_is_bitcode: true,
         max_atomic_width: 32,
+        post_link_args: vec!["-s".to_string(), "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string()],
         .. Default::default()
     };
     Ok(Target {
diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs
index 3eddd911749..6e2f7b1bf53 100644
--- a/src/librustc_back/target/mod.rs
+++ b/src/librustc_back/target/mod.rs
@@ -190,7 +190,8 @@ supported_targets! {
     ("i586-pc-windows-msvc", i586_pc_windows_msvc),
 
     ("le32-unknown-nacl", le32_unknown_nacl),
-    ("asmjs-unknown-emscripten", asmjs_unknown_emscripten)
+    ("asmjs-unknown-emscripten", asmjs_unknown_emscripten),
+    ("wasm32-unknown-emscripten", wasm32_unknown_emscripten)
 }
 
 /// Everything `rustc` knows about how to compile for a specific target.
diff --git a/src/librustc_back/target/wasm32_unknown_emscripten.rs b/src/librustc_back/target/wasm32_unknown_emscripten.rs
new file mode 100644
index 00000000000..2923f2eb92e
--- /dev/null
+++ b/src/librustc_back/target/wasm32_unknown_emscripten.rs
@@ -0,0 +1,42 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::{Target, TargetOptions};
+
+pub fn target() -> Result<Target, String> {
+    let opts = TargetOptions {
+        linker: "emcc".to_string(),
+        ar: "emar".to_string(),
+
+        dynamic_linking: false,
+        executables: true,
+        // Today emcc emits two files - a .js file to bootstrap and
+        // possibly interpret the wasm, and a .wasm file
+        exe_suffix: ".js".to_string(),
+        linker_is_gnu: true,
+        allow_asm: false,
+        obj_is_bitcode: true,
+        max_atomic_width: 32,
+        post_link_args: vec!["-s".to_string(), "BINARYEN=1".to_string(),
+                             "-s".to_string(), "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string()],
+        .. Default::default()
+    };
+    Ok(Target {
+        llvm_target: "asmjs-unknown-emscripten".to_string(),
+        target_endian: "little".to_string(),
+        target_pointer_width: "32".to_string(),
+        target_os: "emscripten".to_string(),
+        target_env: "".to_string(),
+        target_vendor: "unknown".to_string(),
+        data_layout: "e-p:32:32-i64:64-v128:32:128-n32-S128".to_string(),
+        arch: "wasm32".to_string(),
+        options: opts,
+    })
+}
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 5ac4512fe39..492165e2f2a 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -1075,7 +1075,7 @@ pub fn monitor<F: FnOnce() + Send + 'static>(f: F) {
     }
 
     let thread = cfg.spawn(move || {
-         io::set_panic(box err);
+         io::set_panic(Some(box err));
          f()
      });
 
@@ -1121,7 +1121,7 @@ fn exit_on_err() -> ! {
     // Panic so the process returns a failure code, but don't pollute the
     // output with some unnecessary panic messages, we've already
     // printed everything that we needed to.
-    io::set_panic(box io::sink());
+    io::set_panic(Some(box io::sink()));
     panic!();
 }
 
diff --git a/src/librustc_llvm/build.rs b/src/librustc_llvm/build.rs
index 3f551476e2b..5257575a94e 100644
--- a/src/librustc_llvm/build.rs
+++ b/src/librustc_llvm/build.rs
@@ -66,7 +66,8 @@ fn main() {
     let host = env::var("HOST").expect("HOST was not set");
     let is_crossed = target != host;
 
-    let optional_components = ["x86", "arm", "aarch64", "mips", "powerpc", "pnacl", "systemz"];
+    let optional_components = ["x86", "arm", "aarch64", "mips", "powerpc", "pnacl", "systemz",
+                               "jsbackend"];
 
     // FIXME: surely we don't need all these components, right? Stuff like mcjit
     //        or interpreter the compiler itself never uses.
diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs
index eb45d3d25c5..3946b44ead5 100644
--- a/src/librustc_llvm/lib.rs
+++ b/src/librustc_llvm/lib.rs
@@ -434,6 +434,10 @@ pub fn initialize_available_targets() {
                  LLVMInitializeSystemZTargetMC,
                  LLVMInitializeSystemZAsmPrinter,
                  LLVMInitializeSystemZAsmParser);
+    init_target!(llvm_component = "jsbackend",
+                 LLVMInitializeJSBackendTargetInfo,
+                 LLVMInitializeJSBackendTarget,
+                 LLVMInitializeJSBackendTargetMC);
 }
 
 pub fn last_error() -> Option<String> {
diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs
index b82c5629f9d..0a5b013c79a 100644
--- a/src/librustc_trans/abi.rs
+++ b/src/librustc_trans/abi.rs
@@ -519,6 +519,7 @@ impl FnType {
             "powerpc64" => cabi_powerpc64::compute_abi_info(ccx, self),
             "s390x" => cabi_s390x::compute_abi_info(ccx, self),
             "asmjs" => cabi_asmjs::compute_abi_info(ccx, self),
+            "wasm32" => cabi_asmjs::compute_abi_info(ccx, self),
             a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a))
         }
 
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 1ab86cf7e89..02f0916de0e 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -228,7 +228,7 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
     let codemap = Rc::new(CodeMap::new());
     let emitter = errors::emitter::EmitterWriter::new(box Sink(data.clone()),
                                                       Some(codemap.clone()));
-    let old = io::set_panic(box Sink(data.clone()));
+    let old = io::set_panic(Some(box Sink(data.clone())));
     let _bomb = Bomb(data.clone(), old.unwrap_or(box io::stdout()));
 
     // Compile the code
diff --git a/src/libstd/env.rs b/src/libstd/env.rs
index 5171fbdf03e..76a2f93c162 100644
--- a/src/libstd/env.rs
+++ b/src/libstd/env.rs
@@ -892,7 +892,18 @@ mod os {
     pub const EXE_EXTENSION: &'static str = "pexe";
 }
 
-#[cfg(target_os = "emscripten")]
+#[cfg(all(target_os = "emscripten", target_arch = "asmjs"))]
+mod os {
+    pub const FAMILY: &'static str = "unix";
+    pub const OS: &'static str = "emscripten";
+    pub const DLL_PREFIX: &'static str = "lib";
+    pub const DLL_SUFFIX: &'static str = ".so";
+    pub const DLL_EXTENSION: &'static str = "so";
+    pub const EXE_SUFFIX: &'static str = ".js";
+    pub const EXE_EXTENSION: &'static str = "js";
+}
+
+#[cfg(all(target_os = "emscripten", target_arch = "wasm32"))]
 mod os {
     pub const FAMILY: &'static str = "unix";
     pub const OS: &'static str = "emscripten";
@@ -969,6 +980,11 @@ mod arch {
     pub const ARCH: &'static str = "asmjs";
 }
 
+#[cfg(target_arch = "wasm32")]
+mod arch {
+    pub const ARCH: &'static str = "wasm32";
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -1017,6 +1033,7 @@ mod tests {
     }
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn test_var_big() {
         let mut s = "".to_string();
         let mut i = 0;
@@ -1030,6 +1047,7 @@ mod tests {
     }
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn test_self_exe_path() {
         let path = current_exe();
         assert!(path.is_ok());
@@ -1040,6 +1058,7 @@ mod tests {
     }
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn test_env_set_get_huge() {
         let n = make_rand_name();
         let s = repeat("x").take(10000).collect::<String>();
diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index 576198564db..2f2969b110d 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -1686,7 +1686,7 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use io::prelude::*;
 
diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs
index 4ff8c6ac128..21a0cc1fb3b 100644
--- a/src/libstd/io/buffered.rs
+++ b/src/libstd/io/buffered.rs
@@ -1107,6 +1107,7 @@ mod tests {
     }
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn panic_in_write_doesnt_flush_in_drop() {
         static WRITES: AtomicUsize = AtomicUsize::new(0);
 
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs
index e308a2d8e03..3becc0a0c9e 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -1757,6 +1757,7 @@ mod tests {
     use super::repeat;
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn read_until() {
         let mut buf = Cursor::new(&b"12"[..]);
         let mut v = Vec::new();
@@ -1967,6 +1968,7 @@ mod tests {
     }
 
     #[bench]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn bench_read_to_end(b: &mut test::Bencher) {
         b.iter(|| {
             let mut lr = repeat(1).take(10000000);
diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs
index 9a782e95f6e..6421595a009 100644
--- a/src/libstd/io/stdio.rs
+++ b/src/libstd/io/stdio.rs
@@ -593,11 +593,11 @@ impl<'a> Write for StderrLock<'a> {
                      with a more general mechanism",
            issue = "0")]
 #[doc(hidden)]
-pub fn set_panic(sink: Box<Write + Send>) -> Option<Box<Write + Send>> {
+pub fn set_panic(sink: Option<Box<Write + Send>>) -> Option<Box<Write + Send>> {
     use panicking::LOCAL_STDERR;
     use mem;
     LOCAL_STDERR.with(move |slot| {
-        mem::replace(&mut *slot.borrow_mut(), Some(sink))
+        mem::replace(&mut *slot.borrow_mut(), sink)
     }).and_then(|mut s| {
         let _ = s.flush();
         Some(s)
@@ -617,10 +617,10 @@ pub fn set_panic(sink: Box<Write + Send>) -> Option<Box<Write + Send>> {
                      with a more general mechanism",
            issue = "0")]
 #[doc(hidden)]
-pub fn set_print(sink: Box<Write + Send>) -> Option<Box<Write + Send>> {
+pub fn set_print(sink: Option<Box<Write + Send>>) -> Option<Box<Write + Send>> {
     use mem;
     LOCAL_STDOUT.with(move |slot| {
-        mem::replace(&mut *slot.borrow_mut(), Some(sink))
+        mem::replace(&mut *slot.borrow_mut(), sink)
     }).and_then(|mut s| {
         let _ = s.flush();
         Some(s)
@@ -668,6 +668,7 @@ mod tests {
     use super::*;
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn panic_doesnt_poison() {
         thread::spawn(|| {
             let _a = stdin();
diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs
index d0b59b42c17..58daa7dbf8d 100644
--- a/src/libstd/net/addr.rs
+++ b/src/libstd/net/addr.rs
@@ -519,7 +519,7 @@ impl<'a, T: ToSocketAddrs + ?Sized> ToSocketAddrs for &'a T {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use net::*;
     use net::test::{tsa, sa6, sa4};
diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs
index 05ef559422f..ba2cd70e0d7 100644
--- a/src/libstd/net/ip.rs
+++ b/src/libstd/net/ip.rs
@@ -669,7 +669,7 @@ impl From<[u8; 16]> for Ipv6Addr {
 }
 
 // Tests for this module
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use net::*;
     use net::Ipv6MulticastScope::*;
diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs
index ad2fe3c1c0d..7dd0e30df03 100644
--- a/src/libstd/net/mod.rs
+++ b/src/libstd/net/mod.rs
@@ -31,7 +31,8 @@ mod addr;
 mod tcp;
 mod udp;
 mod parser;
-#[cfg(test)] mod test;
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod test;
 
 /// Possible values which can be passed to the [`shutdown`] method of
 /// [`TcpStream`].
diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs
index 3c5f07c3e33..0e7c5b06713 100644
--- a/src/libstd/net/tcp.rs
+++ b/src/libstd/net/tcp.rs
@@ -428,7 +428,7 @@ impl fmt::Debug for TcpListener {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use io::ErrorKind;
     use io::prelude::*;
diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs
index 781f026c12c..c03ac496adb 100644
--- a/src/libstd/net/udp.rs
+++ b/src/libstd/net/udp.rs
@@ -353,7 +353,7 @@ impl fmt::Debug for UdpSocket {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use io::ErrorKind;
     use net::*;
diff --git a/src/libstd/os/linux/raw.rs b/src/libstd/os/linux/raw.rs
index 1c19e58818d..e6a95bc831f 100644
--- a/src/libstd/os/linux/raw.rs
+++ b/src/libstd/os/linux/raw.rs
@@ -34,7 +34,8 @@ pub use self::arch::{off_t, ino_t, nlink_t, blksize_t, blkcnt_t, stat, time_t};
           target_arch = "le32",
           target_arch = "powerpc",
           target_arch = "arm",
-          target_arch = "asmjs"))]
+          target_arch = "asmjs",
+          target_arch = "wasm32"))]
 mod arch {
     use os::raw::{c_long, c_short, c_uint};
 
diff --git a/src/libstd/process.rs b/src/libstd/process.rs
index 674b0009537..9d21a76e81b 100644
--- a/src/libstd/process.rs
+++ b/src/libstd/process.rs
@@ -825,7 +825,7 @@ pub fn exit(code: i32) -> ! {
     ::sys::os::exit(code)
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use io::prelude::*;
 
diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs
index 3f14fcd239f..69cd37651d5 100644
--- a/src/libstd/rand/mod.rs
+++ b/src/libstd/rand/mod.rs
@@ -242,6 +242,7 @@ mod tests {
     }
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn test_os_rng_tasks() {
 
         let mut txs = vec!();
diff --git a/src/libstd/sync/barrier.rs b/src/libstd/sync/barrier.rs
index ac0f400379e..f46eab68484 100644
--- a/src/libstd/sync/barrier.rs
+++ b/src/libstd/sync/barrier.rs
@@ -118,6 +118,7 @@ mod tests {
     use thread;
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn test_barrier() {
         const N: usize = 10;
 
diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs
index 3db8b05b954..a983ae716a4 100644
--- a/src/libstd/sync/condvar.rs
+++ b/src/libstd/sync/condvar.rs
@@ -270,6 +270,7 @@ mod tests {
     }
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn notify_one() {
         let m = Arc::new(Mutex::new(()));
         let m2 = m.clone();
@@ -286,6 +287,7 @@ mod tests {
     }
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn notify_all() {
         const N: usize = 10;
 
@@ -322,6 +324,7 @@ mod tests {
     }
 
     #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn wait_timeout_ms() {
         let m = Arc::new(Mutex::new(()));
         let m2 = m.clone();
@@ -343,6 +346,7 @@ mod tests {
 
     #[test]
     #[should_panic]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn two_mutexes() {
         let m = Arc::new(Mutex::new(()));
         let m2 = m.clone();
diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs
index 3d9f81413dc..d9c14ef2f77 100644
--- a/src/libstd/sync/mpsc/mod.rs
+++ b/src/libstd/sync/mpsc/mod.rs
@@ -1268,7 +1268,7 @@ impl error::Error for TryRecvError {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use env;
     use super::*;
@@ -1942,7 +1942,7 @@ mod tests {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod sync_tests {
     use env;
     use thread;
diff --git a/src/libstd/sync/mpsc/mpsc_queue.rs b/src/libstd/sync/mpsc/mpsc_queue.rs
index d926043fbbc..8d80f942ff7 100644
--- a/src/libstd/sync/mpsc/mpsc_queue.rs
+++ b/src/libstd/sync/mpsc/mpsc_queue.rs
@@ -146,7 +146,7 @@ impl<T> Drop for Queue<T> {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use sync::mpsc::channel;
     use super::{Queue, Data, Empty, Inconsistent};
diff --git a/src/libstd/sync/mpsc/select.rs b/src/libstd/sync/mpsc/select.rs
index 51b08bd75c4..91896e1ab85 100644
--- a/src/libstd/sync/mpsc/select.rs
+++ b/src/libstd/sync/mpsc/select.rs
@@ -366,7 +366,7 @@ impl<'rx, T:Send+'rx> fmt::Debug for Handle<'rx, T> {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 #[allow(unused_imports)]
 mod tests {
     use thread;
diff --git a/src/libstd/sync/mpsc/spsc_queue.rs b/src/libstd/sync/mpsc/spsc_queue.rs
index 724d7b1be73..5858e4b6ddb 100644
--- a/src/libstd/sync/mpsc/spsc_queue.rs
+++ b/src/libstd/sync/mpsc/spsc_queue.rs
@@ -231,7 +231,7 @@ impl<T> Drop for Queue<T> {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use sync::Arc;
     use super::Queue;
diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs
index 098a3e44258..812724c7a16 100644
--- a/src/libstd/sync/mutex.rs
+++ b/src/libstd/sync/mutex.rs
@@ -352,7 +352,7 @@ pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Fla
     &guard.__lock.poison
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use sync::mpsc::channel;
     use sync::{Arc, Mutex, Condvar};
diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs
index 86d2986959c..ad9d0b37544 100644
--- a/src/libstd/sync/once.rs
+++ b/src/libstd/sync/once.rs
@@ -367,7 +367,7 @@ impl OnceState {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use panic;
     use sync::mpsc::channel;
diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs
index 7f053c6704b..48ecae185f9 100644
--- a/src/libstd/sync/rwlock.rs
+++ b/src/libstd/sync/rwlock.rs
@@ -380,7 +380,7 @@ impl<'a, T: ?Sized> Drop for RwLockWriteGuard<'a, T> {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     #![allow(deprecated)] // rand
 
diff --git a/src/libstd/sys/common/io.rs b/src/libstd/sys/common/io.rs
index 3cd70eddb85..47cec4ef5c2 100644
--- a/src/libstd/sys/common/io.rs
+++ b/src/libstd/sys/common/io.rs
@@ -50,7 +50,7 @@ pub unsafe fn read_to_end_uninitialized(r: &mut Read, buf: &mut Vec<u8>) -> io::
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 pub mod test {
     use path::{Path, PathBuf};
     use env;
@@ -165,6 +165,7 @@ mod tests {
     }
 
     #[bench]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn bench_uninitialized(b: &mut ::test::Bencher) {
         b.iter(|| {
             let mut lr = repeat(1).take(10000000);
diff --git a/src/libstd/sys/common/remutex.rs b/src/libstd/sys/common/remutex.rs
index cbdeaad7f6b..4d0407ccf6c 100644
--- a/src/libstd/sys/common/remutex.rs
+++ b/src/libstd/sys/common/remutex.rs
@@ -156,7 +156,7 @@ impl<'a, T> Drop for ReentrantMutexGuard<'a, T> {
 }
 
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
     use cell::RefCell;
diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs
index 3f93fce1935..40fe24cf10e 100644
--- a/src/libstd/sys/unix/ext/net.rs
+++ b/src/libstd/sys/unix/ext/net.rs
@@ -786,7 +786,7 @@ impl IntoRawFd for UnixDatagram {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod test {
     use thread;
     use io;
diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs
index 50014f51f6c..dafc11d9cc8 100644
--- a/src/libstd/sys/unix/process.rs
+++ b/src/libstd/sys/unix/process.rs
@@ -369,7 +369,7 @@ impl Command {
         }
 
         // NaCl has no signal support.
-        if cfg!(not(target_os = "nacl")) {
+        if cfg!(not(any(target_os = "nacl", target_os = "emscripten"))) {
             // Reset signal handling so the child process starts in a
             // standardized state. libstd ignores SIGPIPE, and signal-handling
             // libraries often set a mask. Child processes inherit ignored
@@ -589,7 +589,7 @@ impl Process {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use super::*;
 
diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs
index 980ef01f549..1e879117f73 100644
--- a/src/libstd/sys/unix/thread.rs
+++ b/src/libstd/sys/unix/thread.rs
@@ -29,6 +29,20 @@ pub struct Thread {
 unsafe impl Send for Thread {}
 unsafe impl Sync for Thread {}
 
+// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc,
+// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS.
+#[cfg(not(target_os = "emscripten"))]
+unsafe fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
+                                    stack_size: libc::size_t) -> libc::c_int {
+    libc::pthread_attr_setstacksize(attr, stack_size)
+}
+
+#[cfg(target_os = "emscripten")]
+unsafe fn pthread_attr_setstacksize(_attr: *mut libc::pthread_attr_t,
+                                    _stack_size: libc::size_t) -> libc::c_int {
+    panic!()
+}
+
 impl Thread {
     pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>)
                           -> io::Result<Thread> {
@@ -38,8 +52,8 @@ impl Thread {
         assert_eq!(libc::pthread_attr_init(&mut attr), 0);
 
         let stack_size = cmp::max(stack, min_stack_size(&attr));
-        match libc::pthread_attr_setstacksize(&mut attr,
-                                              stack_size as libc::size_t) {
+        match pthread_attr_setstacksize(&mut attr,
+                                        stack_size as libc::size_t) {
             0 => {}
             n => {
                 assert_eq!(n, libc::EINVAL);
diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs
index c44dee49f14..a333a7d967d 100644
--- a/src/libstd/thread/local.rs
+++ b/src/libstd/thread/local.rs
@@ -524,7 +524,7 @@ pub mod os {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use sync::mpsc::{channel, Sender};
     use cell::{Cell, UnsafeCell};
diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs
index a634c8f77a4..901ff98fcb3 100644
--- a/src/libstd/thread/mod.rs
+++ b/src/libstd/thread/mod.rs
@@ -741,7 +741,7 @@ fn _assert_sync_and_send() {
 // Tests
 ////////////////////////////////////////////////////////////////////////////////
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "emscripten")))]
 mod tests {
     use any::Any;
     use sync::mpsc::{channel, Sender};
diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs
index dcfd3f754c7..5949afe0ff1 100644
--- a/src/libtest/lib.rs
+++ b/src/libtest/lib.rs
@@ -1182,26 +1182,59 @@ pub fn run_test(opts: &TestOpts,
             }
         }
 
-        thread::spawn(move || {
-            let data = Arc::new(Mutex::new(Vec::new()));
-            let data2 = data.clone();
-            let cfg = thread::Builder::new().name(match desc.name {
-                DynTestName(ref name) => name.clone(),
-                StaticTestName(name) => name.to_owned(),
+        // If the platform is single-threaded we're just going to run
+        // the test synchronously, regardless of the concurrency
+        // level.
+        let supports_threads = !cfg!(target_os = "emscripten");
+
+        // Buffer for capturing standard I/O
+        let data = Arc::new(Mutex::new(Vec::new()));
+        let data2 = data.clone();
+
+        if supports_threads {
+            thread::spawn(move || {
+                let cfg = thread::Builder::new().name(match desc.name {
+                    DynTestName(ref name) => name.clone(),
+                    StaticTestName(name) => name.to_owned(),
+                });
+
+                let result_guard = cfg.spawn(move || {
+                    if !nocapture {
+                        io::set_print(Some(box Sink(data2.clone())));
+                        io::set_panic(Some(box Sink(data2)));
+                    }
+                    testfn()
+                })
+                    .unwrap();
+                let test_result = calc_result(&desc, result_guard.join());
+                let stdout = data.lock().unwrap().to_vec();
+                monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
             });
+        } else {
+            let oldio = if !nocapture {
+                Some((
+                    io::set_print(Some(box Sink(data2.clone()))),
+                    io::set_panic(Some(box Sink(data2)))
+                ))
+            } else {
+                None
+            };
 
-            let result_guard = cfg.spawn(move || {
-                                      if !nocapture {
-                                          io::set_print(box Sink(data2.clone()));
-                                          io::set_panic(box Sink(data2));
-                                      }
-                                      testfn()
-                                  })
-                                  .unwrap();
-            let test_result = calc_result(&desc, result_guard.join());
+            use std::panic::{catch_unwind, AssertUnwindSafe};
+
+            let result = catch_unwind(AssertUnwindSafe(|| {
+                testfn()
+            }));
+
+            if let Some((printio, panicio)) = oldio {
+                io::set_print(printio);
+                io::set_panic(panicio);
+            };
+
+            let test_result = calc_result(&desc, result);
             let stdout = data.lock().unwrap().to_vec();
             monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
-        });
+        }
     }
 
     match testfn {
@@ -1291,7 +1324,7 @@ impl MetricMap {
 ///
 /// This function is a no-op, and does not even read from `dummy`.
 #[cfg(not(any(all(target_os = "nacl", target_arch = "le32"),
-              target_arch = "asmjs")))]
+              target_arch = "asmjs", target_arch = "wasm32")))]
 pub fn black_box<T>(dummy: T) -> T {
     // we need to "use" the argument in some way LLVM can't
     // introspect.
@@ -1299,7 +1332,7 @@ pub fn black_box<T>(dummy: T) -> T {
     dummy
 }
 #[cfg(any(all(target_os = "nacl", target_arch = "le32"),
-          target_arch = "asmjs"))]
+          target_arch = "asmjs", target_arch = "wasm32"))]
 #[inline(never)]
 pub fn black_box<T>(dummy: T) -> T {
     dummy
diff --git a/src/libunwind/libunwind.rs b/src/libunwind/libunwind.rs
index 30de859f150..c2edf754e49 100644
--- a/src/libunwind/libunwind.rs
+++ b/src/libunwind/libunwind.rs
@@ -65,7 +65,7 @@ pub const unwinder_private_data_size: usize = 2;
 #[cfg(target_arch = "s390x")]
 pub const unwinder_private_data_size: usize = 2;
 
-#[cfg(target_arch = "asmjs")]
+#[cfg(target_os = "emscripten")]
 pub const unwinder_private_data_size: usize = 20;
 
 #[repr(C)]
diff --git a/src/llvm b/src/llvm
-Subproject 7801978ec1f3637fcda1b564048ebc732bf586a
+Subproject 3e03f7374169cd41547d75e62ac2ab8a103a913
diff --git a/src/rustllvm/llvm-auto-clean-trigger b/src/rustllvm/llvm-auto-clean-trigger
index ea8d59290df..979f5f07abe 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-09-17
+2016-09-25
diff --git a/src/test/compile-fail/allocator-dylib-is-system.rs b/src/test/compile-fail/allocator-dylib-is-system.rs
index db7f304227f..4c576de2202 100644
--- a/src/test/compile-fail/allocator-dylib-is-system.rs
+++ b/src/test/compile-fail/allocator-dylib-is-system.rs
@@ -18,6 +18,8 @@
 // system allocator. Do this by linking in jemalloc and making sure that we get
 // an error.
 
+// ignore-emscripten FIXME: What "other allocator" should we use for emcc?
+
 #![feature(alloc_jemalloc)]
 
 extern crate allocator_dylib;
diff --git a/src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs b/src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs
index 46ad226d255..02c271ab24d 100644
--- a/src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs
+++ b/src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs
@@ -16,6 +16,8 @@
 // Ensure that rust dynamic libraries use jemalloc as their allocator, verifying
 // by linking in the system allocator here and ensuring that we get a complaint.
 
+// ignore-emscripten FIXME: What "other allocator" is correct for emscripten?
+
 #![feature(alloc_system)]
 
 extern crate allocator_dylib2;
diff --git a/src/test/run-fail/panic-task-name-none.rs b/src/test/run-fail/panic-task-name-none.rs
index ab505038305..36e2a4b86aa 100644
--- a/src/test/run-fail/panic-task-name-none.rs
+++ b/src/test/run-fail/panic-task-name-none.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 // error-pattern:thread '<unnamed>' panicked at 'test'
+// ignore-emscripten Needs threads
 
 use std::thread;
 
diff --git a/src/test/run-fail/panic-task-name-owned.rs b/src/test/run-fail/panic-task-name-owned.rs
index 2d2371f5ce7..4da40c3158b 100644
--- a/src/test/run-fail/panic-task-name-owned.rs
+++ b/src/test/run-fail/panic-task-name-owned.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 // error-pattern:thread 'owned name' panicked at 'test'
+// ignore-emscripten Needs threads.
 
 use std::thread::Builder;
 
diff --git a/src/test/run-fail/task-spawn-barefn.rs b/src/test/run-fail/task-spawn-barefn.rs
index ede055acd61..108430848b9 100644
--- a/src/test/run-fail/task-spawn-barefn.rs
+++ b/src/test/run-fail/task-spawn-barefn.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 // error-pattern:Ensure that the child thread runs by panicking
+// ignore-emscripten Needs threads.
 
 use std::thread;
 
diff --git a/src/test/run-fail/test-panic.rs b/src/test/run-fail/test-panic.rs
index fa360570253..21ced01d9d6 100644
--- a/src/test/run-fail/test-panic.rs
+++ b/src/test/run-fail/test-panic.rs
@@ -12,6 +12,7 @@
 // error-pattern:thread 'test_foo' panicked at
 // compile-flags: --test
 // ignore-pretty: does not work well with `--test`
+// ignore-emscripten
 
 #[test]
 fn test_foo() {
diff --git a/src/test/run-fail/test-should-fail-bad-message.rs b/src/test/run-fail/test-should-fail-bad-message.rs
index e18c5d9631a..74b5f17bcf9 100644
--- a/src/test/run-fail/test-should-fail-bad-message.rs
+++ b/src/test/run-fail/test-should-fail-bad-message.rs
@@ -12,6 +12,7 @@
 // error-pattern:thread 'test_foo' panicked at
 // compile-flags: --test
 // ignore-pretty: does not work well with `--test`
+// ignore-emscripten
 
 #[test]
 #[should_panic(expected = "foobar")]
diff --git a/src/test/run-fail/test-tasks-invalid-value.rs b/src/test/run-fail/test-tasks-invalid-value.rs
index 94ed641c79c..b5c222764d2 100644
--- a/src/test/run-fail/test-tasks-invalid-value.rs
+++ b/src/test/run-fail/test-tasks-invalid-value.rs
@@ -15,6 +15,7 @@
 // compile-flags: --test
 // exec-env:RUST_TEST_THREADS=foo
 // ignore-pretty: does not work well with `--test`
+// ignore-emscripten
 
 #[test]
 fn do_nothing() {}
diff --git a/src/test/run-pass/allocator-override.rs b/src/test/run-pass/allocator-override.rs
index d7a8e79bfbe..ca2dbdf2b3d 100644
--- a/src/test/run-pass/allocator-override.rs
+++ b/src/test/run-pass/allocator-override.rs
@@ -10,6 +10,7 @@
 
 // no-prefer-dynamic
 // aux-build:allocator-dummy.rs
+// ignore-emscripten
 
 #![feature(test)]
 
diff --git a/src/test/run-pass/extern-pass-empty.rs b/src/test/run-pass/extern-pass-empty.rs
index 21948d2e5ad..801a3c40ab4 100644
--- a/src/test/run-pass/extern-pass-empty.rs
+++ b/src/test/run-pass/extern-pass-empty.rs
@@ -12,6 +12,7 @@
 
 // pretty-expanded FIXME #23616
 // ignore-msvc
+// ignore-emscripten
 
 struct TwoU8s {
     one: u8,
diff --git a/src/test/run-pass/format-no-std.rs b/src/test/run-pass/format-no-std.rs
index 62d54da56b2..1b9b4ab32ca 100644
--- a/src/test/run-pass/format-no-std.rs
+++ b/src/test/run-pass/format-no-std.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-emscripten missing rust_begin_unwind
+
 #![feature(lang_items, start, collections)]
 #![no_std]
 
diff --git a/src/test/run-pass/packed-struct-layout.rs b/src/test/run-pass/packed-struct-layout.rs
index 92308c9fc3e..d1e05e5a018 100644
--- a/src/test/run-pass/packed-struct-layout.rs
+++ b/src/test/run-pass/packed-struct-layout.rs
@@ -7,6 +7,7 @@
 // <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.
+// ignore-emscripten Not sure what's happening here.
 
 
 use std::mem;
diff --git a/src/test/run-pass/packed-tuple-struct-layout.rs b/src/test/run-pass/packed-tuple-struct-layout.rs
index 411c1807a16..ee4eb86ed0d 100644
--- a/src/test/run-pass/packed-tuple-struct-layout.rs
+++ b/src/test/run-pass/packed-tuple-struct-layout.rs
@@ -7,6 +7,7 @@
 // <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.
+// ignore-emscripten
 
 
 use std::mem;
diff --git a/src/test/run-pass/panic-runtime/abort-link-to-unwinding-crates.rs b/src/test/run-pass/panic-runtime/abort-link-to-unwinding-crates.rs
index 71c1a61062d..1c273fcba02 100644
--- a/src/test/run-pass/panic-runtime/abort-link-to-unwinding-crates.rs
+++ b/src/test/run-pass/panic-runtime/abort-link-to-unwinding-crates.rs
@@ -11,6 +11,7 @@
 // compile-flags:-C panic=abort
 // aux-build:exit-success-if-unwind.rs
 // no-prefer-dynamic
+// ignore-emscripten Function not implemented
 
 extern crate exit_success_if_unwind;
 
diff --git a/src/test/run-pass/panic-runtime/abort.rs b/src/test/run-pass/panic-runtime/abort.rs
index 2fc9d6cfd04..be38f6ea364 100644
--- a/src/test/run-pass/panic-runtime/abort.rs
+++ b/src/test/run-pass/panic-runtime/abort.rs
@@ -10,6 +10,7 @@
 
 // compile-flags:-C panic=abort
 // no-prefer-dynamic
+// ignore-emscripten Function not implemented.
 
 use std::process::Command;
 use std::env;
diff --git a/src/test/run-pass/panic-runtime/lto-abort.rs b/src/test/run-pass/panic-runtime/lto-abort.rs
index 09e33b88189..e4cd4e809a4 100644
--- a/src/test/run-pass/panic-runtime/lto-abort.rs
+++ b/src/test/run-pass/panic-runtime/lto-abort.rs
@@ -10,6 +10,7 @@
 
 // compile-flags:-C lto -C panic=abort
 // no-prefer-dynamic
+// ignore-emscripten Function not implemented.
 
 use std::process::Command;
 use std::env;
diff --git a/src/test/run-pass/panic-runtime/lto-unwind.rs b/src/test/run-pass/panic-runtime/lto-unwind.rs
index 10e633b3775..768b88fd09e 100644
--- a/src/test/run-pass/panic-runtime/lto-unwind.rs
+++ b/src/test/run-pass/panic-runtime/lto-unwind.rs
@@ -10,6 +10,7 @@
 
 // compile-flags:-C lto -C panic=unwind
 // no-prefer-dynamic
+// ignore-emscripten Function not implemented.
 
 use std::process::Command;
 use std::env;
diff --git a/src/test/run-pass/process-status-inherits-stdin.rs b/src/test/run-pass/process-status-inherits-stdin.rs
index 2ad47c4f116..ff389bec899 100644
--- a/src/test/run-pass/process-status-inherits-stdin.rs
+++ b/src/test/run-pass/process-status-inherits-stdin.rs
@@ -7,6 +7,7 @@
 // <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.
+// ignore-emscripten Function not implemented.
 
 use std::env;
 use std::io;
diff --git a/src/test/run-pass/simd-intrinsic-generic-cast.rs b/src/test/run-pass/simd-intrinsic-generic-cast.rs
index 2efd9333999..d32fa01c7b9 100644
--- a/src/test/run-pass/simd-intrinsic-generic-cast.rs
+++ b/src/test/run-pass/simd-intrinsic-generic-cast.rs
@@ -7,6 +7,7 @@
 // <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.
+// ignore-emscripten linking with emcc failed
 
 #![feature(repr_simd, platform_intrinsics, concat_idents, test)]
 #![allow(non_camel_case_types)]
diff --git a/src/test/run-pass/task-stderr.rs b/src/test/run-pass/task-stderr.rs
index 1f64f40c525..13d5cc989e9 100644
--- a/src/test/run-pass/task-stderr.rs
+++ b/src/test/run-pass/task-stderr.rs
@@ -30,7 +30,7 @@ fn main() {
     let data = Arc::new(Mutex::new(Vec::new()));
     let sink = Sink(data.clone());
     let res = thread::Builder::new().spawn(move|| -> () {
-        io::set_panic(Box::new(sink));
+        io::set_panic(Some(Box::new(sink)));
         panic!("Hello, world!")
     }).unwrap().join();
     assert!(res.is_err());
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 5d522736089..81cb927f26b 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -183,4 +183,5 @@ pub struct Config {
     pub cflags: String,
     pub llvm_components: String,
     pub llvm_cxxflags: String,
+    pub nodejs: Option<String>,
 }
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index 114cfaf7972..ff91ab7c70b 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -109,6 +109,7 @@ pub fn parse_config(args: Vec<String> ) -> Config {
           reqopt("", "cflags", "flags for the C compiler", "FLAGS"),
           reqopt("", "llvm-components", "list of LLVM components built in", "LIST"),
           reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS"),
+          optopt("", "nodejs", "the name of nodejs", "PATH"),
           optflag("h", "help", "show this message"));
 
     let (argv0, args_) = args.split_first().unwrap();
@@ -190,6 +191,7 @@ pub fn parse_config(args: Vec<String> ) -> Config {
         cflags: matches.opt_str("cflags").unwrap(),
         llvm_components: matches.opt_str("llvm-components").unwrap(),
         llvm_cxxflags: matches.opt_str("llvm-cxxflags").unwrap(),
+        nodejs: matches.opt_str("nodejs"),
     }
 }
 
@@ -431,10 +433,17 @@ pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn
         }
     };
 
+    // Debugging emscripten code doesn't make sense today
+    let mut ignore = early_props.ignore;
+    if (config.mode == DebugInfoGdb || config.mode == DebugInfoLldb) &&
+        config.target.contains("emscripten") {
+        ignore = true;
+    }
+
     test::TestDescAndFn {
         desc: test::TestDesc {
             name: make_test_name(config, testpaths),
-            ignore: early_props.ignore,
+            ignore: ignore,
             should_panic: should_panic,
         },
         testfn: make_test_closure(config, testpaths),
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 8fdb882164a..35b93392baf 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1168,7 +1168,6 @@ actual:\n\
             "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
                 self._arm_exec_compiled_test(env)
             }
-
             _=> {
                 let aux_dir = self.aux_output_dir_name();
                 self.compose_and_run(self.make_run_args(),
@@ -1421,7 +1420,7 @@ actual:\n\
     fn make_exe_name(&self) -> PathBuf {
         let mut f = self.output_base_name();
         // FIXME: This is using the host architecture exe suffix, not target!
-        if self.config.target == "asmjs-unknown-emscripten" {
+        if self.config.target.contains("emscripten") {
             let mut fname = f.file_name().unwrap().to_os_string();
             fname.push(".js");
             f.set_file_name(&fname);
@@ -1439,8 +1438,9 @@ actual:\n\
         let mut args = self.split_maybe_args(&self.config.runtool);
 
         // If this is emscripten, then run tests under nodejs
-        if self.config.target == "asmjs-unknown-emscripten" {
-            args.push("nodejs".to_owned());
+        if self.config.target.contains("emscripten") {
+            let nodejs = self.config.nodejs.clone().unwrap_or("nodejs".to_string());
+            args.push(nodejs);
         }
 
         let exe_file = self.make_exe_name();
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index 428bbcfe576..cad71c59f0a 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -43,7 +43,8 @@ const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[("aarch64", "aarch
                                                               ("sparc", "sparc"),
                                                               ("x86_64", "x86_64"),
                                                               ("xcore", "xcore"),
-                                                              ("asmjs", "asmjs")];
+                                                              ("asmjs", "asmjs"),
+                                                              ("wasm32", "wasm32")];
 
 pub fn get_os(triple: &str) -> &'static str {
     for &(triple_os, os) in OS_TABLE {