General purpose teest cases contributed by mw.
This commit is contained in:
parent
5763627ccd
commit
42b00a4681
2 changed files with 136 additions and 0 deletions
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue