forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
General purpose teest cases contributed by mw.
- Loading branch information
Showing
2 changed files
with
136 additions
and
0 deletions.
There are no files selected for viewing
62 changes: 62 additions & 0 deletions
62
src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 analgous 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(); | ||
} | ||
} | ||
} |
74 changes: 74 additions & 0 deletions
74
src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 a 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-existant 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(); | ||
} | ||
} | ||
} |