From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- .../thinlto/cgu_invalidated_via_import.rs | 48 ++++++++++++++ .../thinlto/cgu_invalidated_when_export_added.rs | 26 ++++++++ .../thinlto/cgu_invalidated_when_export_removed.rs | 26 ++++++++ .../thinlto/cgu_invalidated_when_import_added.rs | 62 ++++++++++++++++++ .../thinlto/cgu_invalidated_when_import_removed.rs | 74 ++++++++++++++++++++++ .../incremental/thinlto/cgu_keeps_identical_fn.rs | 72 +++++++++++++++++++++ .../independent_cgus_dont_affect_each_other.rs | 58 +++++++++++++++++ 7 files changed, 366 insertions(+) create mode 100644 src/test/incremental/thinlto/cgu_invalidated_via_import.rs create mode 100644 src/test/incremental/thinlto/cgu_invalidated_when_export_added.rs create mode 100644 src/test/incremental/thinlto/cgu_invalidated_when_export_removed.rs create mode 100644 src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs create mode 100644 src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs create mode 100644 src/test/incremental/thinlto/cgu_keeps_identical_fn.rs create mode 100644 src/test/incremental/thinlto/independent_cgus_dont_affect_each_other.rs (limited to 'src/test/incremental/thinlto') diff --git a/src/test/incremental/thinlto/cgu_invalidated_via_import.rs b/src/test/incremental/thinlto/cgu_invalidated_via_import.rs new file mode 100644 index 000000000..5fe435d79 --- /dev/null +++ b/src/test/incremental/thinlto/cgu_invalidated_via_import.rs @@ -0,0 +1,48 @@ +// This test checks that the LTO phase is re-done for CGUs that import something +// via ThinLTO and that imported thing changes while the definition of the CGU +// stays untouched. + +// 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_invalidated_via_import-foo", + cfg="cfail2", + kind="no")] +#![rustc_expected_cgu_reuse(module="cgu_invalidated_via_import-foo", + cfg="cfail3", + kind="post-lto")] + +#![rustc_expected_cgu_reuse(module="cgu_invalidated_via_import-bar", + cfg="cfail2", + kind="pre-lto")] +#![rustc_expected_cgu_reuse(module="cgu_invalidated_via_import-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 { + // See `cgu_keeps_identical_fn.rs` for why this is different + // from the other version of this function. + 12345 + } +} + +pub mod bar { + use foo::inlined_fn; + + pub fn caller() -> u32 { + inlined_fn() + } +} diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_export_added.rs b/src/test/incremental/thinlto/cgu_invalidated_when_export_added.rs new file mode 100644 index 000000000..4d48a5f0a --- /dev/null +++ b/src/test/incremental/thinlto/cgu_invalidated_when_export_added.rs @@ -0,0 +1,26 @@ +// revisions: cfail1 cfail2 +// build-pass + +// rust-lang/rust#69798: +// +// This is analgous to cgu_invalidated_when_import_added, but it covers a +// problem uncovered where a change to the *export* set caused a link failure +// when reusing post-LTO optimized object code. + +pub struct Foo {} +impl Drop for Foo { + fn drop(&mut self) { + println!("Dropping Foo"); + } +} +#[no_mangle] +pub extern "C" fn run() { + thread_local! { pub static FOO : Foo = Foo { } ; } + + #[cfg(cfail2)] + { + FOO.with(|_f| ()) + } +} + +pub fn main() { run() } diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_export_removed.rs b/src/test/incremental/thinlto/cgu_invalidated_when_export_removed.rs new file mode 100644 index 000000000..e85b4856f --- /dev/null +++ b/src/test/incremental/thinlto/cgu_invalidated_when_export_removed.rs @@ -0,0 +1,26 @@ +// revisions: cfail1 cfail2 +// build-pass + +// rust-lang/rust#69798: +// +// This is analgous to cgu_invalidated_when_export_added, but it covers the +// other direction. This is analogous to cgu_invalidated_when_import_added: we +// include it, because it may uncover bugs in variant implementation strategies. + +pub struct Foo {} +impl Drop for Foo { + fn drop(&mut self) { + println!("Dropping Foo"); + } +} +#[no_mangle] +pub extern "C" fn run() { + thread_local! { pub static FOO : Foo = Foo { } ; } + + #[cfg(cfail1)] + { + FOO.with(|_f| ()) + } +} + +pub fn main() { run() } diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs b/src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs new file mode 100644 index 000000000..9c17c8745 --- /dev/null +++ b/src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs @@ -0,0 +1,62 @@ +// revisions: cfail1 cfail2 +// compile-flags: -O -Zhuman-readable-cgu-names -Cllvm-args=-import-instr-limit=10 +// build-pass + +// rust-lang/rust#59535: +// +// This is analogous to cgu_invalidated_when_import_removed.rs, but it covers +// the other direction: +// +// We start with a call-graph like `[A] -> [B -> D] [C]` (where the letters are +// functions and the modules are enclosed in `[]`), and add a new call `D <- C`, +// yielding the new call-graph: `[A] -> [B -> D] <- [C]` +// +// The effect of this is that the compiler previously classfied `D` as internal +// and the import-set of `[A]` to be just `B`. But after adding the `D <- C` call, +// `D` is no longer classified as internal, and the import-set of `[A]` becomes +// both `B` and `D`. +// +// We check this case because an early proposed pull request included an +// assertion that the import-sets monotonically decreased over time, a claim +// which this test case proves to be false. + +fn main() { + foo::foo(); + bar::baz(); +} + +mod foo { + + // In cfail1, ThinLTO decides that foo() does not get inlined into main, and + // instead bar() gets inlined into foo(). + // In cfail2, foo() gets inlined into main. + pub fn foo(){ + bar() + } + + // This function needs to be big so that it does not get inlined by ThinLTO + // but *does* get inlined into foo() when it is declared `internal` in + // cfail1 (alone). + pub fn bar(){ + println!("quux1"); + println!("quux2"); + println!("quux3"); + println!("quux4"); + println!("quux5"); + println!("quux6"); + println!("quux7"); + println!("quux8"); + println!("quux9"); + } +} + +mod bar { + + #[inline(never)] + pub fn baz() { + #[cfg(cfail2)] + { + crate::foo::bar(); + } + } +} diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs b/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs new file mode 100644 index 000000000..fc53acf75 --- /dev/null +++ b/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs @@ -0,0 +1,74 @@ +// revisions: cfail1 cfail2 +// compile-flags: -O -Zhuman-readable-cgu-names -Cllvm-args=-import-instr-limit=10 +// build-pass + +// rust-lang/rust#59535: +// +// Consider a call-graph like `[A] -> [B -> D] <- [C]` (where the letters are +// functions and the modules are enclosed in `[]`) +// +// In our specific instance, the earlier compilations were inlining the call +// to`B` into `A`; thus `A` ended up with an external reference to the symbol `D` +// in its object code, to be resolved at subsequent link time. The LTO import +// information provided by LLVM for those runs reflected that information: it +// explicitly says during those runs, `B` definition and `D` declaration were +// imported into `[A]`. +// +// The change between incremental builds was that the call `D <- C` was removed. +// +// That change, coupled with other decisions within `rustc`, made the compiler +// decide to make `D` an internal symbol (since it was no longer accessed from +// other codegen units, this makes sense locally). And then the definition of +// `D` was inlined into `B` and `D` itself was eliminated entirely. +// +// The current LTO import information reported that `B` alone is imported into +// `[A]` for the *current compilation*. So when the Rust compiler surveyed the +// dependence graph, it determined that nothing `[A]` imports changed since the +// last build (and `[A]` itself has not changed either), so it chooses to reuse +// the object code generated during the previous compilation. +// +// But that previous object code has an unresolved reference to `D`, and that +// causes a link time failure! + +fn main() { + foo::foo(); + bar::baz(); +} + +mod foo { + + // In cfail1, foo() gets inlined into main. + // In cfail2, ThinLTO decides that foo() does not get inlined into main, and + // instead bar() gets inlined into foo(). But faulty logic in our incr. + // ThinLTO implementation thought that `main()` is unchanged and thus reused + // the object file still containing a call to the now non-existent bar(). + pub fn foo(){ + bar() + } + + // This function needs to be big so that it does not get inlined by ThinLTO + // but *does* get inlined into foo() once it is declared `internal` in + // cfail2. + pub fn bar(){ + println!("quux1"); + println!("quux2"); + println!("quux3"); + println!("quux4"); + println!("quux5"); + println!("quux6"); + println!("quux7"); + println!("quux8"); + println!("quux9"); + } +} + +mod bar { + + #[inline(never)] + pub fn baz() { + #[cfg(cfail1)] + { + crate::foo::bar(); + } + } +} 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 000000000..31f329a7f --- /dev/null +++ b/src/test/incremental/thinlto/cgu_keeps_identical_fn.rs @@ -0,0 +1,72 @@ +// 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 cfail4 cfail5 cfail6 +// [cfail4]compile-flags: -Zincremental-relative-spans +// [cfail5]compile-flags: -Zincremental-relative-spans +// [cfail6]compile-flags: -Zincremental-relative-spans +// 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-foo", + cfg = "cfail5", + kind = "post-lto" +)] +#![rustc_expected_cgu_reuse( + module = "cgu_keeps_identical_fn-foo", + cfg = "cfail6", + 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" +)] +#![rustc_expected_cgu_reuse( + module = "cgu_keeps_identical_fn-bar", + cfg = "cfail5", + kind = "post-lto" +)] +#![rustc_expected_cgu_reuse( + module = "cgu_keeps_identical_fn-bar", + cfg = "cfail6", + kind = "post-lto" +)] + +mod foo { + + // Trivial functions like this one are imported very reliably by ThinLTO. + #[cfg(any(cfail1, cfail4))] + pub fn inlined_fn() -> u32 { + 1234 + } + + #[cfg(not(any(cfail1, cfail4)))] + 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 new file mode 100644 index 000000000..045f20119 --- /dev/null +++ b/src/test/incremental/thinlto/independent_cgus_dont_affect_each_other.rs @@ -0,0 +1,58 @@ +// This test checks that a change in a CGU does not invalidate an unrelated CGU +// during incremental ThinLTO. + +// 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="independent_cgus_dont_affect_each_other-foo", + cfg="cfail2", + kind="no")] +#![rustc_expected_cgu_reuse(module="independent_cgus_dont_affect_each_other-foo", + cfg="cfail3", + kind="post-lto")] + +#![rustc_expected_cgu_reuse(module="independent_cgus_dont_affect_each_other-bar", + cfg="cfail2", + kind="pre-lto")] +#![rustc_expected_cgu_reuse(module="independent_cgus_dont_affect_each_other-bar", + cfg="cfail3", + kind="post-lto")] + +#![rustc_expected_cgu_reuse(module="independent_cgus_dont_affect_each_other-baz", + cfg="cfail2", + kind="post-lto")] +#![rustc_expected_cgu_reuse(module="independent_cgus_dont_affect_each_other-baz", + cfg="cfail3", + kind="post-lto")] +mod foo { + + #[cfg(cfail1)] + pub fn inlined_fn() -> u32 { + 1234 + } + + #[cfg(not(cfail1))] + pub fn inlined_fn() -> u32 { + // See `cgu_keeps_identical_fn.rs` for why this is different + // from the other version of this function. + 12345 + } +} + +pub mod bar { + use foo::inlined_fn; + + pub fn caller() -> u32 { + inlined_fn() + } +} + +pub mod baz { + pub fn unrelated_to_other_fns() -> u64 { + 0xbeef + } +} -- cgit v1.2.3