about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-01-31 11:07:41 +0000
committerbors <bors@rust-lang.org>2019-01-31 11:07:41 +0000
commitd30b99f9c23f8e1d6ef993cc94da96510ad709b3 (patch)
tree06c4c2ae28a7962efff74b5deda5f6df5443d7f0
parentf40aaa68278ef0879af5fe7ce077c64c6515ea05 (diff)
parentb17c10de46b021cb25855d440b6a0c270d4d1f4e (diff)
Auto merge of #57514 - michaelwoerister:xlto-tests, r=alexcrichton
compiletest: Support opt-in Clang-based run-make tests and use them for testing xLTO.

Some cross-language run-make tests need a Clang compiler that matches the LLVM version of `rustc`. Since such a compiler usually isn't available these tests (marked with the `needs-matching-clang`
directive) are ignored by default.

For some CI jobs we do need these tests to run unconditionally though. In order to support this a `--force-clang-based-tests` flag is added to compiletest. If this flag is specified, `compiletest` will fail if it can't detect an appropriate version of Clang.

@rust-lang/infra The PR doesn't yet enable the tests yet. Do you have any recommendation for which jobs to enable them?

cc #57438

r? @alexcrichton
-rw-r--r--config.toml.example4
-rw-r--r--src/bootstrap/config.rs3
-rw-r--r--src/bootstrap/native.rs4
-rw-r--r--src/bootstrap/test.rs24
-rw-r--r--src/bootstrap/tool.rs41
-rw-r--r--src/ci/docker/x86_64-gnu-debug/Dockerfile27
-rw-r--r--src/test/run-make-fulldeps/cross-lang-lto-clang/Makefile25
-rw-r--r--src/test/run-make-fulldeps/cross-lang-lto-clang/clib.c9
-rw-r--r--src/test/run-make-fulldeps/cross-lang-lto-clang/cmain.c12
-rw-r--r--src/test/run-make-fulldeps/cross-lang-lto-clang/main.rs11
-rw-r--r--src/test/run-make-fulldeps/cross-lang-lto-clang/rustlib.rs12
-rw-r--r--src/tools/compiletest/src/common.rs4
-rw-r--r--src/tools/compiletest/src/header.rs9
-rw-r--r--src/tools/compiletest/src/main.rs7
-rw-r--r--src/tools/compiletest/src/runtest.rs4
15 files changed, 175 insertions, 21 deletions
diff --git a/config.toml.example b/config.toml.example
index 69a38ec8773..f45db37c33b 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -101,6 +101,10 @@
 # that your host compiler ships with libc++.
 #use-libcxx = true
 
+# The value specified here will be passed as `-DLLVM_USE_LINKER` to CMake.
+#use-linker = "lld"
+
+
 # =============================================================================
 # General build configuration options
 # =============================================================================
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index ba339c50fc3..7d3e584f1a6 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -77,6 +77,7 @@ pub struct Config {
     pub llvm_experimental_targets: String,
     pub llvm_link_jobs: Option<u32>,
     pub llvm_version_suffix: Option<String>,
+    pub llvm_use_linker: Option<String>,
 
     pub lld_enabled: bool,
     pub lldb_enabled: bool,
@@ -261,6 +262,7 @@ struct Llvm {
     cxxflags: Option<String>,
     ldflags: Option<String>,
     use_libcxx: Option<bool>,
+    use_linker: Option<String>,
 }
 
 #[derive(Deserialize, Default, Clone)]
@@ -527,6 +529,7 @@ impl Config {
             config.llvm_cxxflags = llvm.cxxflags.clone();
             config.llvm_ldflags = llvm.ldflags.clone();
             set(&mut config.llvm_use_libcxx, llvm.use_libcxx);
+            config.llvm_use_linker = llvm.use_linker.clone();
         }
 
         if let Some(ref rust) = toml.rust {
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index b1bc2e6cad5..f48f9ee752e 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -234,6 +234,10 @@ impl Step for Llvm {
             cfg.define("LLVM_VERSION_SUFFIX", suffix);
         }
 
+        if let Some(ref linker) = builder.config.llvm_use_linker {
+            cfg.define("LLVM_USE_LINKER", linker);
+        }
+
         if let Some(ref python) = builder.config.python {
             cfg.define("PYTHON_EXECUTABLE", python);
         }
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 216649808e2..1a46ebfcabb 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -1088,9 +1088,7 @@ impl Step for Compiletest {
         };
         let lldb_exe = if builder.config.lldb_enabled && !target.contains("emscripten") {
             // Test against the lldb that was just built.
-            builder.llvm_out(target)
-                .join("bin")
-                .join("lldb")
+            builder.llvm_out(target).join("bin").join("lldb")
         } else {
             PathBuf::from("lldb")
         };
@@ -1107,6 +1105,26 @@ impl Step for Compiletest {
             }
         }
 
+        if let Some(var) = env::var_os("RUSTBUILD_FORCE_CLANG_BASED_TESTS") {
+            match &var.to_string_lossy().to_lowercase()[..] {
+                "1" | "yes" | "on" => {
+                    assert!(builder.config.lldb_enabled,
+                        "RUSTBUILD_FORCE_CLANG_BASED_TESTS needs Clang/LLDB to \
+                         be built.");
+                    let clang_exe = builder.llvm_out(target).join("bin").join("clang");
+                    cmd.arg("--run-clang-based-tests-with").arg(clang_exe);
+                }
+                "0" | "no" | "off" => {
+                    // Nothing to do.
+                }
+                other => {
+                    // Let's make sure typos don't get unnoticed
+                    panic!("Unrecognized option '{}' set in \
+                            RUSTBUILD_FORCE_CLANG_BASED_TESTS", other);
+                }
+            }
+        }
+
         // Get paths from cmd args
         let paths = match &builder.config.cmd {
             Subcommand::Test { ref paths, .. } => &paths[..],
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs
index 9f6db73e6f7..cd3afc59e56 100644
--- a/src/bootstrap/tool.rs
+++ b/src/bootstrap/tool.rs
@@ -1,6 +1,5 @@
 use std::fs;
 use std::env;
-use std::iter;
 use std::path::PathBuf;
 use std::process::{Command, exit};
 use std::collections::HashSet;
@@ -666,19 +665,33 @@ impl<'a> Builder<'a> {
 
         // Add the llvm/bin directory to PATH since it contains lots of
         // useful, platform-independent tools
-        if tool.uses_llvm_tools() {
+        if tool.uses_llvm_tools() && !self.config.dry_run {
+            let mut additional_paths = vec![];
+
             if let Some(llvm_bin_path) = self.llvm_bin_path() {
-                if host.contains("windows") {
-                    // On Windows, PATH and the dynamic library path are the same,
-                    // so we just add the LLVM bin path to lib_path
-                    lib_paths.push(llvm_bin_path);
-                } else {
-                    let old_path = env::var_os("PATH").unwrap_or_default();
-                    let new_path = env::join_paths(iter::once(llvm_bin_path)
-                            .chain(env::split_paths(&old_path)))
-                        .expect("Could not add LLVM bin path to PATH");
-                    cmd.env("PATH", new_path);
-                }
+                additional_paths.push(llvm_bin_path);
+            }
+
+            // If LLD is available, add that too.
+            if self.config.lld_enabled {
+                let lld_install_root = self.ensure(native::Lld {
+                    target: self.config.build,
+                });
+
+                let lld_bin_path = lld_install_root.join("bin");
+                additional_paths.push(lld_bin_path);
+            }
+
+            if host.contains("windows") {
+                // On Windows, PATH and the dynamic library path are the same,
+                // so we just add the LLVM bin path to lib_path
+                lib_paths.extend(additional_paths);
+            } else {
+                let old_path = env::var_os("PATH").unwrap_or_default();
+                let new_path = env::join_paths(additional_paths.into_iter()
+                        .chain(env::split_paths(&old_path)))
+                    .expect("Could not add LLVM bin path to PATH");
+                cmd.env("PATH", new_path);
             }
         }
 
@@ -686,7 +699,7 @@ impl<'a> Builder<'a> {
     }
 
     fn llvm_bin_path(&self) -> Option<PathBuf> {
-        if self.config.llvm_enabled && !self.config.dry_run {
+        if self.config.llvm_enabled {
             let llvm_config = self.ensure(native::Llvm {
                 target: self.config.build,
                 emscripten: false,
diff --git a/src/ci/docker/x86_64-gnu-debug/Dockerfile b/src/ci/docker/x86_64-gnu-debug/Dockerfile
index bdde7ad7fe8..1c7eff68adc 100644
--- a/src/ci/docker/x86_64-gnu-debug/Dockerfile
+++ b/src/ci/docker/x86_64-gnu-debug/Dockerfile
@@ -1,4 +1,4 @@
-FROM ubuntu:16.04
+FROM ubuntu:18.10
 
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
@@ -7,18 +7,37 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
   curl \
   ca-certificates \
   python2.7 \
+  python2.7-dev \
+  libxml2-dev \
+  libncurses-dev \
+  libedit-dev \
+  swig \
+  doxygen \
   git \
   cmake \
   sudo \
   gdb \
-  xz-utils
+  xz-utils \
+  lld \
+  clang
 
 COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
+ENV RUSTBUILD_FORCE_CLANG_BASED_TESTS 1
 ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
+
 ENV RUST_CONFIGURE_ARGS \
       --build=x86_64-unknown-linux-gnu \
       --enable-debug \
-      --enable-optimize
-ENV SCRIPT python2.7 ../x.py build
+      --enable-lld \
+      --enable-lldb \
+      --enable-optimize \
+      --set llvm.use-linker=lld \
+      --set target.x86_64-unknown-linux-gnu.linker=clang \
+      --set target.x86_64-unknown-linux-gnu.cc=clang \
+      --set target.x86_64-unknown-linux-gnu.cxx=clang++
+
+ENV SCRIPT \
+  python2.7 ../x.py build && \
+  python2.7 ../x.py test src/test/run-make-fulldeps --test-args clang
diff --git a/src/test/run-make-fulldeps/cross-lang-lto-clang/Makefile b/src/test/run-make-fulldeps/cross-lang-lto-clang/Makefile
new file mode 100644
index 00000000000..cf687070bc2
--- /dev/null
+++ b/src/test/run-make-fulldeps/cross-lang-lto-clang/Makefile
@@ -0,0 +1,25 @@
+# needs-matching-clang
+
+# This test makes sure that cross-language inlining actually works by checking
+# the generated machine code.
+
+-include ../tools.mk
+
+all: cpp-executable rust-executable
+
+cpp-executable:
+	$(RUSTC) -Zcross-lang-lto=on -o $(TMPDIR)/librustlib-xlto.a -Copt-level=2 -Ccodegen-units=1 ./rustlib.rs
+	$(CLANG) -flto=thin -fuse-ld=lld -L $(TMPDIR) -lrustlib-xlto -o $(TMPDIR)/cmain ./cmain.c -O3
+	# Make sure we don't find a call instruction to the function we expect to
+	# always be inlined.
+	llvm-objdump -d $(TMPDIR)/cmain | $(CGREP) -v -e "call.*rust_always_inlined"
+	# As a sanity check, make sure we do find a call instruction to a
+	# non-inlined function
+	llvm-objdump -d $(TMPDIR)/cmain | $(CGREP) -e "call.*rust_never_inlined"
+
+rust-executable:
+	$(CLANG) ./clib.c -flto=thin -c -o $(TMPDIR)/clib.o -O2
+	(cd $(TMPDIR); $(AR) crus ./libxyz.a ./clib.o)
+	$(RUSTC) -Zcross-lang-lto=on -L$(TMPDIR) -Copt-level=2 -Clinker=$(CLANG) -Clink-arg=-fuse-ld=lld ./main.rs -o $(TMPDIR)/rsmain
+	llvm-objdump -d $(TMPDIR)/rsmain | $(CGREP) -e "call.*c_never_inlined"
+	llvm-objdump -d $(TMPDIR)/rsmain | $(CGREP) -v -e "call.*c_always_inlined"
diff --git a/src/test/run-make-fulldeps/cross-lang-lto-clang/clib.c b/src/test/run-make-fulldeps/cross-lang-lto-clang/clib.c
new file mode 100644
index 00000000000..90f33f24dc4
--- /dev/null
+++ b/src/test/run-make-fulldeps/cross-lang-lto-clang/clib.c
@@ -0,0 +1,9 @@
+#include <stdint.h>
+
+uint32_t c_always_inlined() {
+    return 1234;
+}
+
+__attribute__((noinline)) uint32_t c_never_inlined() {
+    return 12345;
+}
diff --git a/src/test/run-make-fulldeps/cross-lang-lto-clang/cmain.c b/src/test/run-make-fulldeps/cross-lang-lto-clang/cmain.c
new file mode 100644
index 00000000000..e62a40117ce
--- /dev/null
+++ b/src/test/run-make-fulldeps/cross-lang-lto-clang/cmain.c
@@ -0,0 +1,12 @@
+#include <stdint.h>
+
+// A trivial function defined in Rust, returning a constant value. This should
+// always be inlined.
+uint32_t rust_always_inlined();
+
+
+uint32_t rust_never_inlined();
+
+int main(int argc, char** argv) {
+    return rust_never_inlined() + rust_always_inlined();
+}
diff --git a/src/test/run-make-fulldeps/cross-lang-lto-clang/main.rs b/src/test/run-make-fulldeps/cross-lang-lto-clang/main.rs
new file mode 100644
index 00000000000..8129dcb85d9
--- /dev/null
+++ b/src/test/run-make-fulldeps/cross-lang-lto-clang/main.rs
@@ -0,0 +1,11 @@
+#[link(name = "xyz")]
+extern "C" {
+    fn c_always_inlined() -> u32;
+    fn c_never_inlined() -> u32;
+}
+
+fn main() {
+    unsafe {
+        println!("blub: {}", c_always_inlined() + c_never_inlined());
+    }
+}
diff --git a/src/test/run-make-fulldeps/cross-lang-lto-clang/rustlib.rs b/src/test/run-make-fulldeps/cross-lang-lto-clang/rustlib.rs
new file mode 100644
index 00000000000..8a74d74a420
--- /dev/null
+++ b/src/test/run-make-fulldeps/cross-lang-lto-clang/rustlib.rs
@@ -0,0 +1,12 @@
+#![crate_type="staticlib"]
+
+#[no_mangle]
+pub extern "C" fn rust_always_inlined() -> u32 {
+    42
+}
+
+#[no_mangle]
+#[inline(never)]
+pub extern "C" fn rust_never_inlined() -> u32 {
+    421
+}
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 98bc6e8ac00..f6f8ef1dff4 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -144,6 +144,10 @@ pub struct Config {
     /// (or, alternatively, to silently run them like regular run-pass tests).
     pub force_valgrind: bool,
 
+    /// The path to the Clang executable to run Clang-based tests with. If
+    /// `None` then these tests will be ignored.
+    pub run_clang_based_tests_with: Option<String>,
+
     /// The directory containing the tests to run
     pub src_base: PathBuf,
 
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 394e408e415..591d92f0cfa 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -111,6 +111,11 @@ impl EarlyProps {
                 if ignore_llvm(config, ln) {
                     props.ignore = Ignore::Ignore;
                 }
+
+                if config.run_clang_based_tests_with.is_none() &&
+                   config.parse_needs_matching_clang(ln) {
+                    props.ignore = Ignore::Ignore;
+                }
             }
 
             if (config.mode == common::DebugInfoGdb || config.mode == common::DebugInfoBoth) &&
@@ -707,6 +712,10 @@ impl Config {
         }
     }
 
+    fn parse_needs_matching_clang(&self, line: &str) -> bool {
+        self.parse_name_directive(line, "needs-matching-clang")
+    }
+
     /// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86`
     /// or `normalize-stderr-32bit`.
     fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective {
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index 94317f541c3..15d53f1e375 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -110,6 +110,12 @@ pub fn parse_config(args: Vec<String>) -> Config {
         )
         .optopt(
             "",
+            "run-clang-based-tests-with",
+            "path to Clang executable",
+            "PATH",
+        )
+        .optopt(
+            "",
             "llvm-filecheck",
             "path to LLVM's FileCheck binary",
             "DIR",
@@ -298,6 +304,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         docck_python: matches.opt_str("docck-python").unwrap(),
         valgrind_path: matches.opt_str("valgrind-path"),
         force_valgrind: matches.opt_present("force-valgrind"),
+        run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"),
         llvm_filecheck: matches.opt_str("llvm-filecheck").map(|s| PathBuf::from(&s)),
         src_base,
         build_base: opt_path(matches, "build-base"),
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 3581c4cfbcb..ff201b03a0b 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -2633,6 +2633,10 @@ impl<'test> TestCx<'test> {
             cmd.env("RUSTC_LINKER", linker);
         }
 
+        if let Some(ref clang) = self.config.run_clang_based_tests_with {
+            cmd.env("CLANG", clang);
+        }
+
         // We don't want RUSTFLAGS set from the outside to interfere with
         // compiler flags set in the test cases:
         cmd.env_remove("RUSTFLAGS");