about summary refs log tree commit diff
path: root/src/test/incremental/thinlto
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2020-09-17 17:36:13 -0400
committerAaron Hill <aa1ronham@gmail.com>2020-09-17 22:04:13 -0400
commitcfe07cd42a92610219d6ffc1ae5eefef42f5254a (patch)
treecb28035e1fde3cd78f24244726f9d5adfe6e841f /src/test/incremental/thinlto
parent7bdb5dee7bac15458b10b148e9e24968e633053e (diff)
downloadrust-cfe07cd42a92610219d6ffc1ae5eefef42f5254a.tar.gz
rust-cfe07cd42a92610219d6ffc1ae5eefef42f5254a.zip
Use llvm::computeLTOCacheKey to determine post-ThinLTO CGU reuse
During incremental ThinLTO compilation, we attempt to re-use the
optimized (post-ThinLTO) bitcode file for a module if it is 'safe' to do
so.

Up until now, 'safe' has meant that the set of modules that our current
modules imports from/exports to is unchanged from the previous
compilation session. See PR #67020 and PR #71131 for more details.

However, this turns out be insufficient to guarantee that it's safe
to reuse the post-LTO module (i.e. that optimizing the pre-LTO module
would produce the same result). When LLVM optimizes a module during
ThinLTO, it may look at other information from the 'module index', such
as whether a (non-imported!) global variable is used. If this
information changes between compilation runs, we may end up re-using an
optimized module that (for example) had dead-code elimination run on a
function that is now used by another module.

Fortunately, LLVM implements its own ThinLTO module cache, which is used
when ThinLTO is performed by a linker plugin (e.g. when clang is used to
compile a C proect). Using this cache directly would require extensive
refactoring of our code - but fortunately for us, LLVM provides a
function that does exactly what we need.

The function `llvm::computeLTOCacheKey` is used to compute a SHA-1 hash
from all data that might influence the result of ThinLTO on a module.
In addition to the module imports/exports that we manually track, it
also hashes information about global variables (e.g. their liveness)
which might be used during optimization. By using this function, we
shouldn't have to worry about new LLVM passes breaking our module re-use
behavior.

In LLVM, the output of this function forms part of the filename used to
store the post-ThinLTO module. To keep our current filename structure
intact, this PR just writes out the mapping 'CGU name -> Hash' to a
file. To determine if a post-LTO module should be reused, we compare
hashes from the previous session.

This should unblock PR #75199 - by sheer chance, it seems to have hit
this issue due to the particular CGU partitioning and optimization
decisions that end up getting made.
Diffstat (limited to 'src/test/incremental/thinlto')
-rw-r--r--src/test/incremental/thinlto/cgu_invalidated_via_import.rs4
-rw-r--r--src/test/incremental/thinlto/cgu_keeps_identical_fn.rs47
-rw-r--r--src/test/incremental/thinlto/independent_cgus_dont_affect_each_other.rs4
3 files changed, 53 insertions, 2 deletions
diff --git a/src/test/incremental/thinlto/cgu_invalidated_via_import.rs b/src/test/incremental/thinlto/cgu_invalidated_via_import.rs
index 8160f8f3a99..5fe435d796f 100644
--- a/src/test/incremental/thinlto/cgu_invalidated_via_import.rs
+++ b/src/test/incremental/thinlto/cgu_invalidated_via_import.rs
@@ -33,7 +33,9 @@ mod foo {
 
     #[cfg(not(cfail1))]
     pub fn inlined_fn() -> u32 {
-        1234
+        // See `cgu_keeps_identical_fn.rs` for why this is different
+        // from the other version of this function.
+        12345
     }
 }
 
diff --git a/src/test/incremental/thinlto/cgu_keeps_identical_fn.rs b/src/test/incremental/thinlto/cgu_keeps_identical_fn.rs
new file mode 100644
index 00000000000..0fd5abee118
--- /dev/null
+++ b/src/test/incremental/thinlto/cgu_keeps_identical_fn.rs
@@ -0,0 +1,47 @@
+// This test is almost identical to `cgu_invalided_via_import`, except that
+// the two versions of `inline_fn` are identical. Neither version of `inlined_fn`
+// ends up with any spans in its LLVM bitecode, so LLVM is able to skip
+// re-building any modules which import 'inlined_fn'
+
+// revisions: cfail1 cfail2 cfail3
+// compile-flags: -Z query-dep-graph -O
+// build-pass (FIXME(62277): could be check-pass?)
+
+#![feature(rustc_attrs)]
+#![crate_type="rlib"]
+
+#![rustc_expected_cgu_reuse(module="cgu_keeps_identical_fn-foo",
+                            cfg="cfail2",
+                            kind="no")]
+#![rustc_expected_cgu_reuse(module="cgu_keeps_identical_fn-foo",
+                            cfg="cfail3",
+                            kind="post-lto")]
+
+#![rustc_expected_cgu_reuse(module="cgu_keeps_identical_fn-bar",
+                            cfg="cfail2",
+                            kind="post-lto")]
+#![rustc_expected_cgu_reuse(module="cgu_keeps_identical_fn-bar",
+                            cfg="cfail3",
+                            kind="post-lto")]
+
+mod foo {
+
+    // Trivial functions like this one are imported very reliably by ThinLTO.
+    #[cfg(cfail1)]
+    pub fn inlined_fn() -> u32 {
+        1234
+    }
+
+    #[cfg(not(cfail1))]
+    pub fn inlined_fn() -> u32 {
+        1234
+    }
+}
+
+pub mod bar {
+    use foo::inlined_fn;
+
+    pub fn caller() -> u32 {
+        inlined_fn()
+    }
+}
diff --git a/src/test/incremental/thinlto/independent_cgus_dont_affect_each_other.rs b/src/test/incremental/thinlto/independent_cgus_dont_affect_each_other.rs
index 24e5d2438bd..045f2011958 100644
--- a/src/test/incremental/thinlto/independent_cgus_dont_affect_each_other.rs
+++ b/src/test/incremental/thinlto/independent_cgus_dont_affect_each_other.rs
@@ -37,7 +37,9 @@ mod foo {
 
     #[cfg(not(cfail1))]
     pub fn inlined_fn() -> u32 {
-        1234
+        // See `cgu_keeps_identical_fn.rs` for why this is different
+        // from the other version of this function.
+        12345
     }
 }