diff --git a/src/test/ui-fulldeps/update-references.sh b/src/test/ui-fulldeps/update-references.sh
index c2c842fcc4984..48e7ec8c25a22 100755
--- a/src/test/ui-fulldeps/update-references.sh
+++ b/src/test/ui-fulldeps/update-references.sh
@@ -31,18 +31,18 @@ MYDIR=$(dirname $0)
 BUILD_DIR="$1"
 shift
 
+shopt -s nullglob
+
 while [[ "$1" != "" ]]; do
-    STDERR_NAME="${1/%.rs/.stderr}"
-    STDOUT_NAME="${1/%.rs/.stdout}"
+    for EXT in "stderr" "stdout"; do
+        for OUT_NAME in $BUILD_DIR/${1%.rs}*/*$EXT; do
+            OUT_DIR=`dirname "$1"`
+            OUT_BASE=`basename "$OUT_NAME"`
+            if ! (diff $OUT_NAME $MYDIR/$OUT_DIR/$OUT_BASE >& /dev/null); then
+                echo updating $MYDIR/$OUT_DIR/$OUT_BASE
+                cp $OUT_NAME $MYDIR/$OUT_DIR
+            fi
+        done
+    done
     shift
-    if [ -f $BUILD_DIR/$STDOUT_NAME ] && \
-           ! (diff $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME >& /dev/null); then
-        echo updating $MYDIR/$STDOUT_NAME
-        cp $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME
-    fi
-    if [ -f $BUILD_DIR/$STDERR_NAME ] && \
-           ! (diff $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME >& /dev/null); then
-        echo updating $MYDIR/$STDERR_NAME
-        cp $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME
-    fi
 done
diff --git a/src/test/ui/update-references.sh b/src/test/ui/update-references.sh
index 00b4b5c5caa78..f3c5997fc3f53 100755
--- a/src/test/ui/update-references.sh
+++ b/src/test/ui/update-references.sh
@@ -35,7 +35,7 @@ shopt -s nullglob
 
 while [[ "$1" != "" ]]; do
     for EXT in "stderr" "stdout" "fixed"; do
-        for OUT_NAME in $BUILD_DIR/${1%.rs}.*$EXT; do
+        for OUT_NAME in $BUILD_DIR/${1%.rs}*/*$EXT; do
             OUT_DIR=`dirname "$1"`
             OUT_BASE=`basename "$OUT_NAME"`
             if ! (diff $OUT_NAME $MYDIR/$OUT_DIR/$OUT_BASE >& /dev/null); then
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 2df5281659934..812e9c5f39d87 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -10,10 +10,11 @@
 pub use self::Mode::*;
 
 use std::fmt;
+use std::path::{Path, PathBuf};
 use std::str::FromStr;
-use std::path::PathBuf;
 
 use test::ColorConfig;
+use util::PathBufExt;
 
 #[derive(Clone, Copy, PartialEq, Debug)]
 pub enum Mode {
@@ -103,7 +104,7 @@ pub enum CompareMode {
 impl CompareMode {
     pub(crate) fn to_str(&self) -> &'static str {
         match *self {
-            CompareMode::Nll => "nll"
+            CompareMode::Nll => "nll",
         }
     }
 
@@ -245,24 +246,28 @@ pub struct Config {
     pub nodejs: Option<String>,
 }
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct TestPaths {
     pub file: PathBuf,         // e.g., compile-test/foo/bar/baz.rs
-    pub base: PathBuf,         // e.g., compile-test, auxiliary
     pub relative_dir: PathBuf, // e.g., foo/bar
 }
 
 /// Used by `ui` tests to generate things like `foo.stderr` from `foo.rs`.
-pub fn expected_output_path(testpaths: &TestPaths,
-                            revision: Option<&str>,
-                            compare_mode: &Option<CompareMode>,
-                            kind: &str) -> PathBuf {
-
+pub fn expected_output_path(
+    testpaths: &TestPaths,
+    revision: Option<&str>,
+    compare_mode: &Option<CompareMode>,
+    kind: &str,
+) -> PathBuf {
     assert!(UI_EXTENSIONS.contains(&kind));
     let mut parts = Vec::new();
 
-    if let Some(x) = revision { parts.push(x); }
-    if let Some(ref x) = *compare_mode { parts.push(x.to_str()); }
+    if let Some(x) = revision {
+        parts.push(x);
+    }
+    if let Some(ref x) = *compare_mode {
+        parts.push(x.to_str());
+    }
     parts.push(kind);
 
     let extension = parts.join(".");
@@ -273,3 +278,38 @@ pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT, UI_FIXED];
 pub const UI_STDERR: &str = "stderr";
 pub const UI_STDOUT: &str = "stdout";
 pub const UI_FIXED: &str = "fixed";
+
+/// Absolute path to the directory where all output for all tests in the given
+/// `relative_dir` group should reside. Example:
+///   /path/to/build/host-triple/test/ui/relative/
+/// This is created early when tests are collected to avoid race conditions.
+pub fn output_relative_path(config: &Config, relative_dir: &Path) -> PathBuf {
+    config.build_base.join(relative_dir)
+}
+
+/// Generates a unique name for the test, such as `testname.revision.mode`.
+pub fn output_testname_unique(
+    config: &Config,
+    testpaths: &TestPaths,
+    revision: Option<&str>,
+) -> PathBuf {
+    let mode = config.compare_mode.as_ref().map_or("", |m| m.to_str());
+    PathBuf::from(&testpaths.file.file_stem().unwrap())
+        .with_extra_extension(revision.unwrap_or(""))
+        .with_extra_extension(mode)
+}
+
+/// Absolute path to the directory where all output for the given
+/// test/revision should reside.  Example:
+///   /path/to/build/host-triple/test/ui/relative/testname.revision.mode/
+pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
+    output_relative_path(config, &testpaths.relative_dir)
+        .join(output_testname_unique(config, testpaths, revision))
+}
+
+/// Absolute path to the base filename used as output for the given
+/// test/revision.  Example:
+///   /path/to/build/host-triple/test/ui/relative/testname.revision.mode/testname
+pub fn output_base_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
+    output_base_dir(config, testpaths, revision).join(testpaths.file.file_stem().unwrap())
+}
diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs
index 251dd4d5edb4e..dd2e5557c16d4 100644
--- a/src/tools/compiletest/src/errors.rs
+++ b/src/tools/compiletest/src/errors.rs
@@ -11,8 +11,8 @@ use self::WhichLine::*;
 
 use std::fmt;
 use std::fs::File;
-use std::io::BufReader;
 use std::io::prelude::*;
+use std::io::BufReader;
 use std::path::Path;
 use std::str::FromStr;
 
@@ -35,8 +35,7 @@ impl FromStr for ErrorKind {
             "ERROR" => Ok(ErrorKind::Error),
             "NOTE" => Ok(ErrorKind::Note),
             "SUGGESTION" => Ok(ErrorKind::Suggestion),
-            "WARN" |
-            "WARNING" => Ok(ErrorKind::Warning),
+            "WARN" | "WARNING" => Ok(ErrorKind::Warning),
             _ => Err(()),
         }
     }
@@ -101,23 +100,25 @@ pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
     rdr.lines()
         .enumerate()
         .filter_map(|(line_num, line)| {
-            parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), &tag)
-                .map(|(which, error)| {
+            parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), &tag).map(
+                |(which, error)| {
                     match which {
                         FollowPrevious(_) => {}
                         _ => last_nonfollow_error = Some(error.line_num),
                     }
                     error
-                })
+                },
+            )
         })
         .collect()
 }
 
-fn parse_expected(last_nonfollow_error: Option<usize>,
-                  line_num: usize,
-                  line: &str,
-                  tag: &str)
-                  -> Option<(WhichLine, Error)> {
+fn parse_expected(
+    last_nonfollow_error: Option<usize>,
+    line_num: usize,
+    line: &str,
+    tag: &str,
+) -> Option<(WhichLine, Error)> {
     let start = match line.find(tag) {
         Some(i) => i,
         None => return None,
@@ -125,7 +126,13 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
     let (follow, adjusts) = if line[start + tag.len()..].chars().next().unwrap() == '|' {
         (true, 0)
     } else {
-        (false, line[start + tag.len()..].chars().take_while(|c| *c == '^').count())
+        (
+            false,
+            line[start + tag.len()..]
+                .chars()
+                .take_while(|c| *c == '^')
+                .count(),
+        )
     };
     let kind_start = start + tag.len() + adjusts + (follow as usize);
     let (kind, msg);
@@ -133,12 +140,14 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
         .split_whitespace()
         .next()
         .expect("Encountered unexpected empty comment")
-        .parse::<ErrorKind>() {
+        .parse::<ErrorKind>()
+    {
         Ok(k) => {
             // If we find `//~ ERROR foo` or something like that:
             kind = Some(k);
             let letters = line[kind_start..].chars();
-            msg = letters.skip_while(|c| c.is_whitespace())
+            msg = letters
+                .skip_while(|c| c.is_whitespace())
                 .skip_while(|c| !c.is_whitespace())
                 .collect::<String>();
         }
@@ -146,7 +155,8 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
             // Otherwise we found `//~ foo`:
             kind = None;
             let letters = line[kind_start..].chars();
-            msg = letters.skip_while(|c| c.is_whitespace())
+            msg = letters
+                .skip_while(|c| c.is_whitespace())
                 .collect::<String>();
         }
     }
@@ -154,8 +164,10 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
 
     let (which, line_num) = if follow {
         assert_eq!(adjusts, 0, "use either //~| or //~^, not both.");
-        let line_num = last_nonfollow_error.expect("encountered //~| without \
-                                                    preceding //~^ line.");
+        let line_num = last_nonfollow_error.expect(
+            "encountered //~| without \
+             preceding //~^ line.",
+        );
         (FollowPrevious(line_num), line_num)
     } else {
         let which = if adjusts > 0 {
@@ -167,16 +179,16 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
         (which, line_num)
     };
 
-    debug!("line={} tag={:?} which={:?} kind={:?} msg={:?}",
-           line_num,
-           tag,
-           which,
-           kind,
-           msg);
-    Some((which,
-          Error {
-        line_num,
-        kind,
-        msg,
-    }))
+    debug!(
+        "line={} tag={:?} which={:?} kind={:?} msg={:?}",
+        line_num, tag, which, kind, msg
+    );
+    Some((
+        which,
+        Error {
+            line_num,
+            kind,
+            msg,
+        },
+    ))
 }
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 32980a513f6f9..06ec9e893f068 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -10,12 +10,12 @@
 
 use std::env;
 use std::fs::File;
-use std::io::BufReader;
 use std::io::prelude::*;
+use std::io::BufReader;
 use std::path::{Path, PathBuf};
 
-use common::Config;
 use common;
+use common::Config;
 use util;
 
 use extract_gdb_version;
@@ -38,19 +38,14 @@ impl EarlyProps {
             revisions: vec![],
         };
 
-        iter_header(testfile,
-                    None,
-                    &mut |ln| {
+        iter_header(testfile, None, &mut |ln| {
             // we should check if any only-<platform> exists and if it exists
             // and does not matches the current platform, skip the test
-            props.ignore =
-                props.ignore ||
-                config.parse_cfg_name_directive(ln, "ignore") ||
-                (config.has_cfg_prefix(ln, "only") &&
-                !config.parse_cfg_name_directive(ln, "only")) ||
-                ignore_gdb(config, ln) ||
-                ignore_lldb(config, ln) ||
-                ignore_llvm(config, ln);
+            props.ignore = props.ignore || config.parse_cfg_name_directive(ln, "ignore")
+                || (config.has_cfg_prefix(ln, "only")
+                    && !config.parse_cfg_name_directive(ln, "only"))
+                || ignore_gdb(config, ln) || ignore_lldb(config, ln)
+                || ignore_llvm(config, ln);
 
             if let Some(s) = config.parse_aux_build(ln) {
                 props.aux.push(s);
@@ -149,7 +144,7 @@ impl EarlyProps {
 
         fn ignore_llvm(config: &Config, line: &str) -> bool {
             if config.system_llvm && line.starts_with("no-system-llvm") {
-                    return true;
+                return true;
             }
             if let Some(ref actual_version) = config.llvm_version {
                 if line.starts_with("min-llvm-version") {
@@ -272,11 +267,7 @@ impl TestProps {
         }
     }
 
-    pub fn from_aux_file(&self,
-                         testfile: &Path,
-                         cfg: Option<&str>,
-                         config: &Config)
-                         -> Self {
+    pub fn from_aux_file(&self, testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
         let mut props = TestProps::new();
 
         // copy over select properties to the aux build:
@@ -296,20 +287,15 @@ impl TestProps {
     /// tied to a particular revision `foo` (indicated by writing
     /// `//[foo]`), then the property is ignored unless `cfg` is
     /// `Some("foo")`.
-    fn load_from(&mut self,
-                 testfile: &Path,
-                 cfg: Option<&str>,
-                 config: &Config) {
-        iter_header(testfile,
-                    cfg,
-                    &mut |ln| {
+    fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) {
+        iter_header(testfile, cfg, &mut |ln| {
             if let Some(ep) = config.parse_error_pattern(ln) {
                 self.error_patterns.push(ep);
             }
 
             if let Some(flags) = config.parse_compile_flags(ln) {
-                self.compile_flags.extend(flags.split_whitespace()
-                    .map(|s| s.to_owned()));
+                self.compile_flags
+                    .extend(flags.split_whitespace().map(|s| s.to_owned()));
             }
 
             if let Some(r) = config.parse_revisions(ln) {
@@ -382,8 +368,7 @@ impl TestProps {
 
             if !self.compile_pass {
                 // run-pass implies must_compile_sucessfully
-                self.compile_pass =
-                    config.parse_compile_pass(ln) || self.run_pass;
+                self.compile_pass = config.parse_compile_pass(ln) || self.run_pass;
             }
 
                         if !self.skip_trans {
@@ -453,7 +438,7 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) {
                     None => false,
                 };
                 if matches {
-                    it(ln[(close_brace + 1) ..].trim_left());
+                    it(ln[(close_brace + 1)..].trim_left());
                 }
             } else {
                 panic!("malformed condition directive: expected `{}foo]`, found `{}`",
@@ -554,9 +539,7 @@ impl Config {
     fn parse_env(&self, line: &str, name: &str) -> Option<(String, String)> {
         self.parse_name_value_directive(line, name).map(|nv| {
             // nv is either FOO or FOO=BAR
-            let mut strs: Vec<String> = nv.splitn(2, '=')
-                .map(str::to_owned)
-                .collect();
+            let mut strs: Vec<String> = nv.splitn(2, '=').map(str::to_owned).collect();
 
             match strs.len() {
                 1 => (strs.pop().unwrap(), "".to_owned()),
@@ -599,7 +582,10 @@ impl Config {
     /// or `normalize-stderr-32bit`. Returns `true` if the line matches it.
     fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> bool {
         if line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-') {
-            let name = line[prefix.len()+1 ..].split(&[':', ' '][..]).next().unwrap();
+            let name = line[prefix.len() + 1..]
+                .split(&[':', ' '][..])
+                .next()
+                .unwrap();
 
             name == "test" ||
                 util::matches_os(&self.target, name) ||             // target
@@ -612,8 +598,7 @@ impl Config {
                     common::DebugInfoLldb => name == "lldb",
                     common::Pretty => name == "pretty",
                     _ => false,
-                } ||
-                (self.target != self.host && name == "cross-compile")
+                } || (self.target != self.host && name == "cross-compile")
         } else {
             false
         }
@@ -631,14 +616,14 @@ impl Config {
         // the line says "ignore-x86_64".
         line.starts_with(directive) && match line.as_bytes().get(directive.len()) {
             None | Some(&b' ') | Some(&b':') => true,
-            _ => false
+            _ => false,
         }
     }
 
     pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option<String> {
         let colon = directive.len();
         if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') {
-            let value = line[(colon + 1) ..].to_owned();
+            let value = line[(colon + 1)..].to_owned();
             debug!("{}: {}", directive, value);
             Some(expand_variables(value, self))
         } else {
@@ -665,8 +650,10 @@ impl Config {
 }
 
 pub fn lldb_version_to_int(version_string: &str) -> isize {
-    let error_string = format!("Encountered LLDB version string with unexpected format: {}",
-                               version_string);
+    let error_string = format!(
+        "Encountered LLDB version string with unexpected format: {}",
+        version_string
+    );
     version_string.parse().expect(&error_string)
 }
 
@@ -713,6 +700,6 @@ fn parse_normalization_string(line: &mut &str) -> Option<String> {
         None => return None,
     };
     let result = line[begin..end].to_owned();
-    *line = &line[end+1..];
+    *line = &line[end + 1..];
     Some(result)
 }
diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs
index 761d1e511d52b..165f2914ae246 100644
--- a/src/tools/compiletest/src/json.rs
+++ b/src/tools/compiletest/src/json.rs
@@ -9,10 +9,10 @@
 // except according to those terms.
 
 use errors::{Error, ErrorKind};
+use runtest::ProcRes;
 use serde_json;
-use std::str::FromStr;
 use std::path::Path;
-use runtest::ProcRes;
+use std::str::FromStr;
 
 // These structs are a subset of the ones found in
 // `syntax::json`.
@@ -58,26 +58,30 @@ struct DiagnosticCode {
 }
 
 pub fn extract_rendered(output: &str, proc_res: &ProcRes) -> String {
-    output.lines()
-        .filter_map(|line| if line.starts_with('{') {
-            match serde_json::from_str::<Diagnostic>(line) {
-                Ok(diagnostic) => diagnostic.rendered,
-                Err(error) => {
-                    proc_res.fatal(Some(&format!("failed to decode compiler output as json: \
-                                                  `{}`\noutput: {}\nline: {}",
-                                                 error,
-                                                 line,
-                                                 output)));
+    output
+        .lines()
+        .filter_map(|line| {
+            if line.starts_with('{') {
+                match serde_json::from_str::<Diagnostic>(line) {
+                    Ok(diagnostic) => diagnostic.rendered,
+                    Err(error) => {
+                        proc_res.fatal(Some(&format!(
+                            "failed to decode compiler output as json: \
+                             `{}`\noutput: {}\nline: {}",
+                            error, line, output
+                        )));
+                    }
                 }
+            } else {
+                None
             }
-        } else {
-            None
         })
         .collect()
 }
 
 pub fn parse_output(file_name: &str, output: &str, proc_res: &ProcRes) -> Vec<Error> {
-    output.lines()
+    output
+        .lines()
         .flat_map(|line| parse_line(file_name, line, output, proc_res))
         .collect()
 }
@@ -93,11 +97,11 @@ fn parse_line(file_name: &str, line: &str, output: &str, proc_res: &ProcRes) ->
                 expected_errors
             }
             Err(error) => {
-                proc_res.fatal(Some(&format!("failed to decode compiler output as json: \
-                                              `{}`\noutput: {}\nline: {}",
-                                             error,
-                                             line,
-                                             output)));
+                proc_res.fatal(Some(&format!(
+                    "failed to decode compiler output as json: \
+                     `{}`\noutput: {}\nline: {}",
+                    error, line, output
+                )));
             }
         }
     } else {
@@ -105,11 +109,14 @@ fn parse_line(file_name: &str, line: &str, output: &str, proc_res: &ProcRes) ->
     }
 }
 
-fn push_expected_errors(expected_errors: &mut Vec<Error>,
-                        diagnostic: &Diagnostic,
-                        default_spans: &[&DiagnosticSpan],
-                        file_name: &str) {
-    let spans_in_this_file: Vec<_> = diagnostic.spans
+fn push_expected_errors(
+    expected_errors: &mut Vec<Error>,
+    diagnostic: &Diagnostic,
+    default_spans: &[&DiagnosticSpan],
+    file_name: &str,
+) {
+    let spans_in_this_file: Vec<_> = diagnostic
+        .spans
         .iter()
         .filter(|span| Path::new(&span.file_name) == Path::new(&file_name))
         .collect();
@@ -204,8 +211,10 @@ fn push_expected_errors(expected_errors: &mut Vec<Error>,
     }
 
     // Add notes for any labels that appear in the message.
-    for span in spans_in_this_file.iter()
-        .filter(|span| span.label.is_some()) {
+    for span in spans_in_this_file
+        .iter()
+        .filter(|span| span.label.is_some())
+    {
         expected_errors.push(Error {
             line_num: span.line_start,
             kind: Some(ErrorKind::Note),
@@ -219,9 +228,11 @@ fn push_expected_errors(expected_errors: &mut Vec<Error>,
     }
 }
 
-fn push_backtrace(expected_errors: &mut Vec<Error>,
-                  expansion: &DiagnosticSpanMacroExpansion,
-                  file_name: &str) {
+fn push_backtrace(
+    expected_errors: &mut Vec<Error>,
+    expansion: &DiagnosticSpanMacroExpansion,
+    file_name: &str,
+) {
     if Path::new(&expansion.span.file_name) == Path::new(&file_name) {
         expected_errors.push(Error {
             line_num: expansion.span.line_start,
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index 0899ebd5d80f7..42a2cdfa55b5a 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -31,31 +31,31 @@ extern crate serde_json;
 extern crate test;
 extern crate rustfix;
 
+use common::CompareMode;
+use common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
+use common::{Config, TestPaths};
+use common::{DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
+use filetime::FileTime;
+use getopts::Options;
 use std::env;
 use std::ffi::OsString;
 use std::fs;
-use std::io;
+use std::io::{self, Read};
 use std::path::{Path, PathBuf};
 use std::process::Command;
-use filetime::FileTime;
-use getopts::Options;
-use common::{Config, TestPaths};
-use common::{DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
-use common::{expected_output_path, UI_EXTENSIONS};
-use common::CompareMode;
 use test::ColorConfig;
 use util::logv;
 
 use self::header::EarlyProps;
 
-pub mod util;
-mod json;
-pub mod header;
-pub mod runtest;
 pub mod common;
 pub mod errors;
+pub mod header;
+mod json;
 mod raise_fd_limit;
 mod read2;
+pub mod runtest;
+pub mod util;
 
 fn main() {
     env_logger::init();
@@ -236,7 +236,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
             "",
             "compare-mode",
             "mode describing what file the actual ui output will be compared to",
-            "COMPARE MODE"
+            "COMPARE MODE",
         )
         .optflag("h", "help", "show this message");
 
@@ -501,7 +501,11 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
         filter: config.filter.clone(),
         filter_exact: config.filter_exact,
         run_ignored: config.run_ignored,
-        format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },
+        format: if config.quiet {
+            test::OutputFormat::Terse
+        } else {
+            test::OutputFormat::Pretty
+        },
         logfile: config.logfile.clone(),
         run_tests: true,
         bench_benchmarks: true,
@@ -548,10 +552,9 @@ fn collect_tests_from_dir(
         if name == *"Makefile" && config.mode == Mode::RunMake {
             let paths = TestPaths {
                 file: dir.to_path_buf(),
-                base: base.to_path_buf(),
                 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
             };
-            tests.push(make_test(config, &paths));
+            tests.extend(make_test(config, &paths));
             return Ok(());
         }
     }
@@ -562,7 +565,7 @@ fn collect_tests_from_dir(
     // sequential loop because otherwise, if we do it in the
     // tests themselves, they race for the privilege of
     // creating the directories and sometimes fail randomly.
-    let build_dir = config.build_base.join(&relative_dir_path);
+    let build_dir = output_relative_path(config, relative_dir_path);
     fs::create_dir_all(&build_dir).unwrap();
 
     // Add each `.rs` file as a test, and recurse further on any
@@ -576,21 +579,12 @@ fn collect_tests_from_dir(
             debug!("found test file: {:?}", file_path.display());
             let paths = TestPaths {
                 file: file_path,
-                base: base.to_path_buf(),
                 relative_dir: relative_dir_path.to_path_buf(),
             };
-            tests.push(make_test(config, &paths))
+            tests.extend(make_test(config, &paths))
         } else if file_path.is_dir() {
             let relative_file_path = relative_dir_path.join(file.file_name());
-            if &file_name == "auxiliary" {
-                // `aux` directories contain other crates used for
-                // cross-crate tests. Don't search them for tests, but
-                // do create a directory in the build dir for them,
-                // since we will dump intermediate output in there
-                // sometimes.
-                let build_dir = config.build_base.join(&relative_file_path);
-                fs::create_dir_all(&build_dir).unwrap();
-            } else {
+            if &file_name != "auxiliary" {
                 debug!("found directory: {:?}", file_path.display());
                 collect_tests_from_dir(config, base, &file_path, &relative_file_path, tests)?;
             }
@@ -613,7 +607,7 @@ pub fn is_test(file_name: &OsString) -> bool {
     !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
 }
 
-pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
+pub fn make_test(config: &Config, testpaths: &TestPaths) -> Vec<test::TestDescAndFn> {
     let early_props = if config.mode == Mode::RunMake {
         // Allow `ignore` directives to be in the Makefile.
         EarlyProps::from_file(config, &testpaths.file.join("Makefile"))
@@ -633,47 +627,68 @@ pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn
         },
     };
 
-    // Debugging emscripten code doesn't make sense today
-    let ignore = early_props.ignore
-        || !up_to_date(config, testpaths, &early_props)
-        || (config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
-            && config.target.contains("emscripten");
-
-    test::TestDescAndFn {
-        desc: test::TestDesc {
-            name: make_test_name(config, testpaths),
-            ignore,
-            should_panic,
-            allow_fail: false,
-        },
-        testfn: make_test_closure(config, testpaths),
-    }
+    // Incremental tests are special, they inherently cannot be run in parallel.
+    // `runtest::run` will be responsible for iterating over revisions.
+    let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
+        vec![None]
+    } else {
+        early_props.revisions.iter().map(|r| Some(r)).collect()
+    };
+    revisions
+        .into_iter()
+        .map(|revision| {
+            // Debugging emscripten code doesn't make sense today
+            let ignore = early_props.ignore
+                || !up_to_date(
+                    config,
+                    testpaths,
+                    &early_props,
+                    revision.map(|s| s.as_str()),
+                )
+                || (config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
+                    && config.target.contains("emscripten");
+            test::TestDescAndFn {
+                desc: test::TestDesc {
+                    name: make_test_name(config, testpaths, revision),
+                    ignore,
+                    should_panic,
+                    allow_fail: false,
+                },
+                testfn: make_test_closure(config, testpaths, revision),
+            }
+        })
+        .collect()
 }
 
-fn stamp(config: &Config, testpaths: &TestPaths) -> PathBuf {
-    let mode_suffix = match config.compare_mode {
-        Some(ref mode) => format!("-{}", mode.to_str()),
-        None => format!(""),
-    };
-    let stamp_name = format!(
-        "{}-{}{}.stamp",
-        testpaths.file.file_name().unwrap().to_str().unwrap(),
-        config.stage_id,
-        mode_suffix
-    );
-    config
-        .build_base
-        .canonicalize()
-        .unwrap_or_else(|_| config.build_base.clone())
-        .join(&testpaths.relative_dir)
-        .join(stamp_name)
+fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
+    output_base_dir(config, testpaths, revision).join("stamp")
 }
 
-fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> bool {
+fn up_to_date(
+    config: &Config,
+    testpaths: &TestPaths,
+    props: &EarlyProps,
+    revision: Option<&str>,
+) -> bool {
+    let stamp_name = stamp(config, testpaths, revision);
+    // Check hash.
+    let mut f = match fs::File::open(&stamp_name) {
+        Ok(f) => f,
+        Err(_) => return true,
+    };
+    let mut contents = String::new();
+    f.read_to_string(&mut contents)
+        .expect("Can't read stamp contents");
+    let expected_hash = runtest::compute_stamp_hash(config);
+    if contents != expected_hash {
+        return true;
+    }
+
+    // Check timestamps.
     let rust_src_dir = config
         .find_rust_src_root()
         .expect("Could not find Rust source root");
-    let stamp = mtime(&stamp(config, testpaths));
+    let stamp = mtime(&stamp_name);
     let mut inputs = vec![mtime(&testpaths.file), mtime(&config.rustc_path)];
     for aux in props.aux.iter() {
         inputs.push(mtime(&testpaths
@@ -694,8 +709,7 @@ fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> boo
     for pretty_printer_file in &pretty_printer_files {
         inputs.push(mtime(&rust_src_dir.join(pretty_printer_file)));
     }
-    let mut entries = config.run_lib_path.read_dir().unwrap()
-        .collect::<Vec<_>>();
+    let mut entries = config.run_lib_path.read_dir().unwrap().collect::<Vec<_>>();
     while let Some(entry) = entries.pop() {
         let entry = entry.unwrap();
         let path = entry.path();
@@ -712,18 +726,8 @@ fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> boo
 
     // UI test files.
     for extension in UI_EXTENSIONS {
-        for revision in &props.revisions {
-            let path = &expected_output_path(testpaths,
-                                             Some(revision),
-                                             &config.compare_mode,
-                                             extension);
-            inputs.push(mtime(path));
-        }
-
-        if props.revisions.is_empty() {
-            let path = &expected_output_path(testpaths, None, &config.compare_mode, extension);
-            inputs.push(mtime(path));
-        }
+        let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
+        inputs.push(mtime(path));
     }
 
     inputs.iter().any(|input| *input > stamp)
@@ -735,7 +739,11 @@ fn mtime(path: &Path) -> FileTime {
         .unwrap_or_else(|_| FileTime::zero())
 }
 
-pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
+fn make_test_name(
+    config: &Config,
+    testpaths: &TestPaths,
+    revision: Option<&String>,
+) -> test::TestName {
     // Convert a complete path to something like
     //
     //    run-pass/foo/bar/baz.rs
@@ -746,13 +754,26 @@ pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName
         Some(ref mode) => format!(" ({})", mode.to_str()),
         None => format!(""),
     };
-    test::DynTestName(format!("[{}{}] {}", config.mode, mode_suffix, path.display()))
+    test::DynTestName(format!(
+        "[{}{}] {}{}",
+        config.mode,
+        mode_suffix,
+        path.display(),
+        revision.map_or("".to_string(), |rev| format!("#{}", rev))
+    ))
 }
 
-pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
+fn make_test_closure(
+    config: &Config,
+    testpaths: &TestPaths,
+    revision: Option<&String>,
+) -> test::TestFn {
     let config = config.clone();
     let testpaths = testpaths.clone();
-    test::DynTestFn(Box::new(move || runtest::run(config, &testpaths)))
+    let revision = revision.cloned();
+    test::DynTestFn(Box::new(move || {
+        runtest::run(config, &testpaths, revision.as_ref().map(|s| s.as_str()))
+    }))
 }
 
 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
diff --git a/src/tools/compiletest/src/raise_fd_limit.rs b/src/tools/compiletest/src/raise_fd_limit.rs
index fcc5a727cf2d6..220082799a8b0 100644
--- a/src/tools/compiletest/src/raise_fd_limit.rs
+++ b/src/tools/compiletest/src/raise_fd_limit.rs
@@ -34,12 +34,15 @@ pub unsafe fn raise_fd_limit() {
     let mut mib: [libc::c_int; 2] = [CTL_KERN, KERN_MAXFILESPERPROC];
     let mut maxfiles: libc::c_int = 0;
     let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t;
-    if libc::sysctl(&mut mib[0],
-                    2,
-                    &mut maxfiles as *mut _ as *mut _,
-                    &mut size,
-                    null_mut(),
-                    0) != 0 {
+    if libc::sysctl(
+        &mut mib[0],
+        2,
+        &mut maxfiles as *mut _ as *mut _,
+        &mut size,
+        null_mut(),
+        0,
+    ) != 0
+    {
         let err = io::Error::last_os_error();
         panic!("raise_fd_limit: error calling sysctl: {}", err);
     }
diff --git a/src/tools/compiletest/src/read2.rs b/src/tools/compiletest/src/read2.rs
index 4f55edc58ef2a..5bf898f5f1baf 100644
--- a/src/tools/compiletest/src/read2.rs
+++ b/src/tools/compiletest/src/read2.rs
@@ -16,11 +16,13 @@ pub use self::imp::read2;
 #[cfg(not(any(unix, windows)))]
 mod imp {
     use std::io::{self, Read};
-    use std::process::{ChildStdout, ChildStderr};
+    use std::process::{ChildStderr, ChildStdout};
 
-    pub fn read2(out_pipe: ChildStdout,
-                 err_pipe: ChildStderr,
-                 data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
+    pub fn read2(
+        out_pipe: ChildStdout,
+        err_pipe: ChildStderr,
+        data: &mut FnMut(bool, &mut Vec<u8>, bool),
+    ) -> io::Result<()> {
         let mut buffer = Vec::new();
         out_pipe.read_to_end(&mut buffer)?;
         data(true, &mut buffer, true);
@@ -33,16 +35,18 @@ mod imp {
 
 #[cfg(unix)]
 mod imp {
-    use std::io::prelude::*;
+    use libc;
     use std::io;
+    use std::io::prelude::*;
     use std::mem;
     use std::os::unix::prelude::*;
-    use std::process::{ChildStdout, ChildStderr};
-    use libc;
+    use std::process::{ChildStderr, ChildStdout};
 
-    pub fn read2(mut out_pipe: ChildStdout,
-                 mut err_pipe: ChildStderr,
-                 data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
+    pub fn read2(
+        mut out_pipe: ChildStdout,
+        mut err_pipe: ChildStderr,
+        data: &mut FnMut(bool, &mut Vec<u8>, bool),
+    ) -> io::Result<()> {
         unsafe {
             libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
             libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
@@ -67,9 +71,9 @@ mod imp {
             if r == -1 {
                 let err = io::Error::last_os_error();
                 if err.kind() == io::ErrorKind::Interrupted {
-                    continue
+                    continue;
                 }
-                return Err(err)
+                return Err(err);
             }
 
             // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
@@ -77,15 +81,13 @@ mod imp {
             // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
             // this case we flip the other fd back into blocking mode and read
             // whatever's leftover on that file descriptor.
-            let handle = |res: io::Result<_>| {
-                match res {
-                    Ok(_) => Ok(true),
-                    Err(e) => {
-                        if e.kind() == io::ErrorKind::WouldBlock {
-                            Ok(false)
-                        } else {
-                            Err(e)
-                        }
+            let handle = |res: io::Result<_>| match res {
+                Ok(_) => Ok(true),
+                Err(e) => {
+                    if e.kind() == io::ErrorKind::WouldBlock {
+                        Ok(false)
+                    } else {
+                        Err(e)
                     }
                 }
             };
@@ -113,7 +115,7 @@ mod imp {
 
     use std::io;
     use std::os::windows::prelude::*;
-    use std::process::{ChildStdout, ChildStderr};
+    use std::process::{ChildStderr, ChildStdout};
     use std::slice;
 
     use self::miow::iocp::{CompletionPort, CompletionStatus};
@@ -128,9 +130,11 @@ mod imp {
         done: bool,
     }
 
-    pub fn read2(out_pipe: ChildStdout,
-                 err_pipe: ChildStderr,
-                 data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
+    pub fn read2(
+        out_pipe: ChildStdout,
+        err_pipe: ChildStderr,
+        data: &mut FnMut(bool, &mut Vec<u8>, bool),
+    ) -> io::Result<()> {
         let mut out = Vec::new();
         let mut err = Vec::new();
 
@@ -206,7 +210,9 @@ mod imp {
         if v.capacity() == v.len() {
             v.reserve(1);
         }
-        slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize),
-                                  v.capacity() - v.len())
+        slice::from_raw_parts_mut(
+            v.as_mut_ptr().offset(v.len() as isize),
+            v.capacity() - v.len(),
+        )
     }
 }
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 4f18f238cab90..59d94e1fa51e1 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -8,28 +8,29 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use common::{Config, TestPaths};
-use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
+use common::CompareMode;
+use common::{expected_output_path, UI_STDERR, UI_STDOUT, UI_FIXED};
+use common::{output_base_dir, output_base_name, output_testname_unique};
 use common::{Codegen, CodegenUnits, DebugInfoGdb, DebugInfoLldb, Rustdoc};
+use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
+use common::{Config, TestPaths};
 use common::{Incremental, MirOpt, RunMake, Ui};
-use common::{expected_output_path, UI_STDERR, UI_STDOUT, UI_FIXED};
-use common::CompareMode;
 use diff;
 use errors::{self, Error, ErrorKind};
 use filetime::FileTime;
-use json;
 use header::TestProps;
-use util::logv;
+use json;
 use regex::Regex;
 use rustfix::{apply_suggestions, get_suggestions_from_json};
+use util::{logv, PathBufExt};
 
-use std::collections::VecDeque;
-use std::collections::HashMap;
-use std::collections::HashSet;
+use std::collections::{HashMap, HashSet, VecDeque};
+use std::collections::hash_map::DefaultHasher;
 use std::env;
-use std::ffi::{OsStr, OsString};
-use std::fs::{self, create_dir_all, File};
+use std::ffi::OsString;
 use std::fmt;
+use std::fs::{self, create_dir_all, File};
+use std::hash::{Hash, Hasher};
 use std::io::prelude::*;
 use std::io::{self, BufReader};
 use std::path::{Path, PathBuf};
@@ -106,26 +107,6 @@ impl Mismatch {
     }
 }
 
-trait PathBufExt {
-    /// Append an extension to the path, even if it already has one.
-    fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf;
-}
-
-impl PathBufExt for PathBuf {
-    fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
-        if extension.as_ref().len() == 0 {
-            self.clone()
-        } else {
-            let mut fname = self.file_name().unwrap().to_os_string();
-            if !extension.as_ref().to_str().unwrap().starts_with(".") {
-                fname.push(".");
-            }
-            fname.push(extension);
-            self.with_file_name(fname)
-        }
-    }
-}
-
 // Produces a diff between the expected output and actual output.
 pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
     let mut line_number = 1;
@@ -186,7 +167,7 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Misma
     results
 }
 
-pub fn run(config: Config, testpaths: &TestPaths) {
+pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) {
     match &*config.target {
         "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
             if !config.adb_device_status {
@@ -207,20 +188,25 @@ pub fn run(config: Config, testpaths: &TestPaths) {
         print!("\n\n");
     }
     debug!("running {:?}", testpaths.file.display());
-    let base_props = TestProps::from_file(&testpaths.file, None, &config);
+    let props = TestProps::from_file(&testpaths.file, revision, &config);
 
-    let base_cx = TestCx {
+    let cx = TestCx {
         config: &config,
-        props: &base_props,
+        props: &props,
         testpaths,
-        revision: None,
+        revision: revision,
     };
-    base_cx.init_all();
+    create_dir_all(&cx.output_base_dir()).unwrap();
 
-    if base_props.revisions.is_empty() {
-        base_cx.run_revision()
-    } else {
-        for revision in &base_props.revisions {
+    if config.mode == Incremental {
+        // Incremental tests are special because they cannot be run in
+        // parallel.
+        assert!(
+            !props.revisions.is_empty(),
+            "Incremental tests require revisions."
+        );
+        cx.init_incremental_test();
+        for revision in &props.revisions {
             let revision_props = TestProps::from_file(&testpaths.file, Some(revision), &config);
             let rev_cx = TestCx {
                 config: &config,
@@ -230,11 +216,17 @@ pub fn run(config: Config, testpaths: &TestPaths) {
             };
             rev_cx.run_revision();
         }
+    } else {
+        cx.run_revision();
     }
 
-    base_cx.complete_all();
+    cx.create_stamp();
+}
 
-    File::create(::stamp(&config, testpaths)).unwrap();
+pub fn compute_stamp_hash(config: &Config) -> String {
+    let mut hash = DefaultHasher::new();
+    config.stage_id.hash(&mut hash);
+    format!("{:x}", hash.finish())
 }
 
 struct TestCx<'test> {
@@ -251,14 +243,6 @@ struct DebuggerCommands {
 }
 
 impl<'test> TestCx<'test> {
-    /// invoked once before any revisions have been processed
-    fn init_all(&self) {
-        assert!(self.revision.is_none(), "init_all invoked for a revision");
-        if let Incremental = self.config.mode {
-            self.init_incremental_test()
-        }
-    }
-
     /// Code executed for each revision in turn (or, if there are no
     /// revisions, exactly once, with revision == None).
     fn run_revision(&self) {
@@ -280,11 +264,6 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    /// Invoked after all revisions have executed.
-    fn complete_all(&self) {
-        assert!(self.revision.is_none(), "init_all invoked for a revision");
-    }
-
     fn check_if_test_should_compile(&self, proc_res: &ProcRes) {
         if self.props.compile_pass {
             if !proc_res.status.success() {
@@ -355,9 +334,10 @@ impl<'test> TestCx<'test> {
 
         if expected_status != received_status {
             self.fatal_proc_rec(
-                &format!("Error: expected failure status ({:?}) but received status {:?}.",
-                         expected_status,
-                         received_status),
+                &format!(
+                    "Error: expected failure status ({:?}) but received status {:?}.",
+                    expected_status, received_status
+                ),
                 proc_res,
             );
         }
@@ -440,8 +420,7 @@ impl<'test> TestCx<'test> {
                 self.config,
                 format!(
                     "pretty-printing round {} revision {:?}",
-                    round,
-                    self.revision
+                    round, self.revision
                 ),
             );
             let proc_res = self.print_source(srcs[round].to_owned(), &self.props.pretty_mode);
@@ -450,8 +429,7 @@ impl<'test> TestCx<'test> {
                 self.fatal_proc_rec(
                     &format!(
                         "pretty-printing failed in round {} revision {:?}",
-                        round,
-                        self.revision
+                        round, self.revision
                     ),
                     &proc_res,
                 );
@@ -555,8 +533,7 @@ impl<'test> TestCx<'test> {
                  {}\n\
                  ------------------------------------------\n\
                  \n",
-                expected,
-                actual
+                expected, actual
             );
         }
     }
@@ -661,8 +638,7 @@ impl<'test> TestCx<'test> {
                 script_str.push_str(&format!(
                     "set solib-search-path \
                      ./{}/stage2/lib/rustlib/{}/lib/\n",
-                    self.config.host,
-                    self.config.target
+                    self.config.host, self.config.target
                 ));
                 for line in &breakpoint_lines {
                     script_str.push_str(
@@ -881,7 +857,6 @@ impl<'test> TestCx<'test> {
             ..self.config.clone()
         };
 
-
         let test_cx = TestCx {
             config: &config,
             ..*self
@@ -952,8 +927,7 @@ impl<'test> TestCx<'test> {
         for line in &breakpoint_lines {
             script_str.push_str(&format!(
                 "breakpoint set --file '{}' --line {}\n",
-                source_file_name,
-                line
+                source_file_name, line
             ));
         }
 
@@ -1028,9 +1002,7 @@ impl<'test> TestCx<'test> {
     fn parse_debugger_commands(&self, debugger_prefixes: &[&str]) -> DebuggerCommands {
         let directives = debugger_prefixes
             .iter()
-            .map(|prefix| {
-                (format!("{}-command", prefix), format!("{}-check", prefix))
-            })
+            .map(|prefix| (format!("{}-command", prefix), format!("{}-check", prefix)))
             .collect::<Vec<_>>();
 
         let mut breakpoint_lines = vec![];
@@ -1041,12 +1013,11 @@ impl<'test> TestCx<'test> {
         for line in reader.lines() {
             match line {
                 Ok(line) => {
-                    let line =
-                        if line.starts_with("//") {
-                            line[2..].trim_left()
-                        } else {
-                            line.as_str()
-                        };
+                    let line = if line.starts_with("//") {
+                        line[2..].trim_left()
+                    } else {
+                        line.as_str()
+                    };
 
                     if line.contains("#break") {
                         breakpoint_lines.push(counter);
@@ -1369,6 +1340,8 @@ impl<'test> TestCx<'test> {
                     testpaths: &aux_testpaths,
                     revision: self.revision,
                 };
+                // Create the directory for the stdout/stderr files.
+                create_dir_all(aux_cx.output_base_dir()).unwrap();
                 let auxres = aux_cx.document(out_dir);
                 if !auxres.status.success() {
                     return auxres;
@@ -1453,7 +1426,7 @@ impl<'test> TestCx<'test> {
                 let mut program = Command::new(&prog);
                 program
                     .args(args)
-                    .current_dir(&self.output_base_name().parent().unwrap())
+                    .current_dir(&self.output_base_dir())
                     .envs(env.clone());
                 self.compose_and_run(
                     program,
@@ -1491,9 +1464,9 @@ impl<'test> TestCx<'test> {
 
         TestPaths {
             file: test_ab,
-            base: self.testpaths.base.clone(),
             relative_dir: self.testpaths
                 .relative_dir
+                .join(self.output_testname_unique())
                 .join("auxiliary")
                 .join(rel_ab)
                 .parent()
@@ -1503,28 +1476,27 @@ impl<'test> TestCx<'test> {
     }
 
     fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
+        let aux_dir = self.aux_output_dir_name();
+
         if !self.props.aux_builds.is_empty() {
-            create_dir_all(&self.aux_output_dir_name()).unwrap();
+            let _ = fs::remove_dir_all(&aux_dir);
+            create_dir_all(&aux_dir).unwrap();
         }
 
-        let aux_dir = self.aux_output_dir_name();
-
         for rel_ab in &self.props.aux_builds {
             let aux_testpaths = self.compute_aux_test_paths(rel_ab);
             let aux_props =
                 self.props
                     .from_aux_file(&aux_testpaths.file, self.revision, self.config);
-            let aux_output = {
-                let f = self.make_lib_name(&self.testpaths.file);
-                let parent = f.parent().unwrap();
-                TargetLocation::ThisDirectory(parent.to_path_buf())
-            };
+            let aux_output = TargetLocation::ThisDirectory(self.aux_output_dir_name());
             let aux_cx = TestCx {
                 config: self.config,
                 props: &aux_props,
                 testpaths: &aux_testpaths,
                 revision: self.revision,
             };
+            // Create the directory for the stdout/stderr files.
+            create_dir_all(aux_cx.output_base_dir()).unwrap();
             let mut aux_rustc = aux_cx.make_compile_args(&aux_testpaths.file, aux_output);
 
             let crate_type = if aux_props.no_prefer_dynamic {
@@ -1645,9 +1617,13 @@ impl<'test> TestCx<'test> {
         let mut rustc = if !is_rustdoc {
             Command::new(&self.config.rustc_path)
         } else {
-            Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet"))
+            Command::new(&self.config
+                .rustdoc_path
+                .clone()
+                .expect("no rustdoc built yet"))
         };
-        rustc.arg(input_file).arg("-L").arg(&self.config.build_base);
+        // FIXME Why is -L here?
+        rustc.arg(input_file);//.arg("-L").arg(&self.config.build_base);
 
         // Optionally prevent default --target if specified in test compile-flags.
         let custom_target = self.props
@@ -1671,10 +1647,7 @@ impl<'test> TestCx<'test> {
 
         if !is_rustdoc {
             if let Some(ref incremental_dir) = self.props.incremental_dir {
-                rustc.args(&[
-                    "-C",
-                    &format!("incremental={}", incremental_dir.display()),
-                ]);
+                rustc.args(&["-C", &format!("incremental={}", incremental_dir.display())]);
                 rustc.args(&["-Z", "incremental-verify-ich"]);
                 rustc.args(&["-Z", "incremental-queries"]);
             }
@@ -1697,7 +1670,11 @@ impl<'test> TestCx<'test> {
                 }
             }
             Ui => {
-                if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) {
+                if !self.props
+                    .compile_flags
+                    .iter()
+                    .any(|s| s.starts_with("--error-format"))
+                {
                     rustc.args(&["--error-format", "json"]);
                 }
                 if !self.props.disable_ui_testing_normalization {
@@ -1720,16 +1697,8 @@ impl<'test> TestCx<'test> {
 
                 rustc.arg(dir_opt);
             }
-            RunPass |
-            RunFail |
-            RunPassValgrind |
-            Pretty |
-            DebugInfoGdb |
-            DebugInfoLldb |
-            Codegen |
-            Rustdoc |
-            RunMake |
-            CodegenUnits => {
+            RunPass | RunFail | RunPassValgrind | Pretty | DebugInfoGdb | DebugInfoLldb
+            | Codegen | Rustdoc | RunMake | CodegenUnits => {
                 // do not use JSON output
             }
         }
@@ -1759,8 +1728,8 @@ impl<'test> TestCx<'test> {
         match self.config.compare_mode {
             Some(CompareMode::Nll) => {
                 rustc.args(&["-Zborrowck=mir", "-Ztwo-phase-borrows"]);
-            },
-            None => {},
+            }
+            None => {}
         }
 
         if self.props.force_host {
@@ -1779,15 +1748,12 @@ impl<'test> TestCx<'test> {
         rustc
     }
 
-    fn make_lib_name(&self, auxfile: &Path) -> PathBuf {
-        // what we return here is not particularly important, as it
-        // happens; rustc ignores everything except for the directory.
-        let auxname = self.output_testname(auxfile);
-        self.aux_output_dir_name().join(&auxname)
-    }
-
     fn make_exe_name(&self) -> PathBuf {
-        let mut f = self.output_base_name_stage();
+        // Using a single letter here to keep the path length down for
+        // Windows.  Some test names get very long.  rustc creates `rcgu`
+        // files with the module name appended to it which can more than
+        // double the length.
+        let mut f = self.output_base_dir().join("a");
         // FIXME: This is using the host architecture exe suffix, not target!
         if self.config.target.contains("emscripten") {
             f = f.with_extra_extension("js");
@@ -1897,33 +1863,47 @@ impl<'test> TestCx<'test> {
             .unwrap();
     }
 
+    /// Create a filename for output with the given extension.  Example:
+    ///   /.../testname.revision.mode/testname.extension
     fn make_out_name(&self, extension: &str) -> PathBuf {
         self.output_base_name().with_extension(extension)
     }
 
+    /// Directory where auxiliary files are written.  Example:
+    ///   /.../testname.revision.mode/auxiliary/
     fn aux_output_dir_name(&self) -> PathBuf {
-        self.output_base_name_stage()
+        self.output_base_dir()
+            .join("auxiliary")
             .with_extra_extension(self.config.mode.disambiguator())
-            .with_extra_extension(".aux")
     }
 
-    fn output_testname(&self, filepath: &Path) -> PathBuf {
-        PathBuf::from(filepath.file_stem().unwrap())
+    /// Generates a unique name for the test, such as `testname.revision.mode`.
+    fn output_testname_unique(&self) -> PathBuf {
+        output_testname_unique(self.config, self.testpaths, self.safe_revision())
     }
 
-    /// Given a test path like `compile-fail/foo/bar.rs` returns a name like
-    /// `/path/to/build/<triple>/test/compile-fail/foo/bar`.
-    fn output_base_name(&self) -> PathBuf {
-        let dir = self.config.build_base.join(&self.testpaths.relative_dir);
+    /// The revision, ignored for Incremental since it wants all revisions in
+    /// the same directory.
+    fn safe_revision(&self) -> Option<&str> {
+        if self.config.mode == Incremental {
+            None
+        } else {
+            self.revision
+        }
+    }
 
-        // Note: The directory `dir` is created during `collect_tests_from_dir`
-        dir.join(&self.output_testname(&self.testpaths.file))
+    /// Absolute path to the directory where all output for the given
+    /// test/revision should reside.  Example:
+    ///   /path/to/build/host-triple/test/ui/relative/testname.revision.mode/
+    fn output_base_dir(&self) -> PathBuf {
+        output_base_dir(self.config, self.testpaths, self.safe_revision())
     }
 
-    /// Same as `output_base_name`, but includes the stage ID as an extension,
-    /// such as: `.../compile-fail/foo/bar.stage1-<triple>`
-    fn output_base_name_stage(&self) -> PathBuf {
-        self.output_base_name().with_extension(&self.config.stage_id)
+    /// Absolute path to the base filename used as output for the given
+    /// test/revision.  Example:
+    ///   /.../relative/testname.revision.mode/testname
+    fn output_base_name(&self) -> PathBuf {
+        output_base_name(self.config, self.testpaths, self.safe_revision())
     }
 
     fn maybe_dump_to_stdout(&self, out: &str, err: &str) {
@@ -1998,8 +1978,7 @@ impl<'test> TestCx<'test> {
     fn compile_test_and_save_ir(&self) -> ProcRes {
         let aux_dir = self.aux_output_dir_name();
 
-        let output_file =
-            TargetLocation::ThisDirectory(self.output_base_name().parent().unwrap().to_path_buf());
+        let output_file = TargetLocation::ThisDirectory(self.output_base_dir());
         let mut rustc = self.make_compile_args(&self.testpaths.file, output_file);
         rustc.arg("-L").arg(aux_dir).arg("--emit=llvm-ir");
 
@@ -2048,7 +2027,7 @@ impl<'test> TestCx<'test> {
     fn run_rustdoc_test(&self) {
         assert!(self.revision.is_none(), "revisions not relevant here");
 
-        let out_dir = self.output_base_name_stage();
+        let out_dir = self.output_base_dir();
         let _ = fs::remove_dir_all(&out_dir);
         create_dir_all(&out_dir).unwrap();
 
@@ -2376,7 +2355,7 @@ impl<'test> TestCx<'test> {
     fn run_incremental_test(&self) {
         // Basic plan for a test incremental/foo/bar.rs:
         // - load list of revisions rpass1, cfail2, rpass3
-        //   - each should begin with `rpass`, `cfail`, or `cfail`
+        //   - each should begin with `rpass`, `cfail`, or `rfail`
         //   - if `rpass`, expect compile and execution to succeed
         //   - if `cfail`, expect compilation to fail
         //   - if `rfail`, expect execution to fail
@@ -2417,8 +2396,7 @@ impl<'test> TestCx<'test> {
         if self.config.verbose {
             print!(
                 "revision={:?} revision_props={:#?}",
-                revision,
-                revision_props
+                revision, revision_props
             );
         }
 
@@ -2450,7 +2428,7 @@ impl<'test> TestCx<'test> {
             .unwrap();
         let src_root = cwd.join(&src_root);
 
-        let tmpdir = cwd.join(self.output_base_name_stage());
+        let tmpdir = cwd.join(self.output_base_name());
         if tmpdir.exists() {
             self.aggressive_rm_rf(&tmpdir).unwrap();
         }
@@ -2731,8 +2709,7 @@ impl<'test> TestCx<'test> {
         if source_time > output_time {
             debug!(
                 "source file time: {:?} output file time: {:?}",
-                source_time,
-                output_time
+                source_time, output_time
             );
             panic!(
                 "test source file `{}` is newer than potentially stale output file `{}`.",
@@ -2906,10 +2883,12 @@ impl<'test> TestCx<'test> {
     }
 
     fn expected_output_path(&self, kind: &str) -> PathBuf {
-        let mut path = expected_output_path(&self.testpaths,
-                                            self.revision,
-                                            &self.config.compare_mode,
-                                            kind);
+        let mut path = expected_output_path(
+            &self.testpaths,
+            self.revision,
+            &self.config.compare_mode,
+            kind,
+        );
         if !path.exists() && self.config.compare_mode.is_some() {
             // fallback!
             path = expected_output_path(&self.testpaths, self.revision, &None, kind);
@@ -2959,14 +2938,14 @@ impl<'test> TestCx<'test> {
                         DiffLine::Expected(e) => {
                             println!("-\t{}", e);
                             line_number += 1;
-                        },
+                        }
                         DiffLine::Context(c) => {
                             println!("{}\t{}", line_number, c);
                             line_number += 1;
-                        },
+                        }
                         DiffLine::Resulting(r) => {
                             println!("+\t{}", r);
-                        },
+                        }
                     }
                 }
                 println!("");
@@ -2993,6 +2972,11 @@ impl<'test> TestCx<'test> {
         println!("Actual {} saved to {}", kind, output_file.display());
         1
     }
+
+    fn create_stamp(&self) {
+        let mut f = File::create(::stamp(&self.config, self.testpaths, self.revision)).unwrap();
+        f.write_all(compute_stamp_hash(&self.config).as_bytes()).unwrap();
+    }
 }
 
 struct ProcArgs {
@@ -3025,10 +3009,7 @@ impl ProcRes {
              {}\n\
              ------------------------------------------\n\
              \n",
-            self.status,
-            self.cmdline,
-            self.stdout,
-            self.stderr
+            self.status, self.cmdline, self.stdout, self.stderr
         );
         panic!();
     }
@@ -3072,8 +3053,8 @@ fn nocomment_mir_line(line: &str) -> &str {
 }
 
 fn read2_abbreviated(mut child: Child) -> io::Result<Output> {
-    use std::mem::replace;
     use read2::read2;
+    use std::mem::replace;
 
     const HEAD_LEN: usize = 160 * 1024;
     const TAIL_LEN: usize = 256 * 1024;
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index c612f0117aaf7..91e7399f1f492 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -8,7 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use std::ffi::OsStr;
 use std::env;
+use std::path::PathBuf;
 use common::Config;
 
 /// Conversion table from triple OS name to Rust SYSNAME
@@ -73,7 +75,7 @@ pub fn matches_os(triple: &str, name: &str) -> bool {
     // For the wasm32 bare target we ignore anything also ignored on emscripten
     // and then we also recognize `wasm32-bare` as the os for the target
     if triple == "wasm32-unknown-unknown" {
-        return name == "emscripten" || name == "wasm32-bare"
+        return name == "emscripten" || name == "wasm32-bare";
     }
     let triple: Vec<_> = triple.split('-').collect();
     for &(triple_os, os) in OS_TABLE {
@@ -128,3 +130,23 @@ pub fn logv(config: &Config, s: String) {
         println!("{}", s);
     }
 }
+
+pub trait PathBufExt {
+    /// Append an extension to the path, even if it already has one.
+    fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf;
+}
+
+impl PathBufExt for PathBuf {
+    fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
+        if extension.as_ref().len() == 0 {
+            self.clone()
+        } else {
+            let mut fname = self.file_name().unwrap().to_os_string();
+            if !extension.as_ref().to_str().unwrap().starts_with(".") {
+                fname.push(".");
+            }
+            fname.push(extension);
+            self.with_file_name(fname)
+        }
+    }
+}