Skip to content

Commit aecb511

Browse files
committed
General purpose teest cases contributed by mw.
1 parent 405720a commit aecb511

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// revisions: cfail1 cfail2
2+
// compile-flags: -O -Zhuman-readable-cgu-names -Cllvm-args=-import-instr-limit=10
3+
// build-pass
4+
5+
// rust-lang/rust#59535:
6+
//
7+
// This is analgous to cgu_invalidated_when_import_removed.rs, but it covers
8+
// the other direction:
9+
//
10+
// We start with a call-graph like `[A] -> [B -> D] [C]` (where the letters are
11+
// functions and the modules are enclosed in `[]`), and add a new call `D <- C`,
12+
// yielding the new call-graph: `[A] -> [B -> D] <- [C]`
13+
//
14+
// The effect of this is that the compiler previously classfied `D` as internal
15+
// and the import-set of `[A]` to be just `B`. But after adding the `D <- C` call,
16+
// `D` is no longer classified as internal, and the import-set of `[A]` becomes
17+
// both `B` and `D`.
18+
//
19+
// We check this case because an early proposed pull request included an
20+
// assertion that the import-sets monotonically decreased over time, a claim
21+
// which this test case proves to be false.
22+
23+
fn main() {
24+
foo::foo();
25+
bar::baz();
26+
}
27+
28+
mod foo {
29+
30+
// In cfail1, ThinLTO decides that foo() does not get inlined into main, and
31+
// instead bar() gets inlined into foo().
32+
// In cfail2, foo() gets inlined into main.
33+
pub fn foo(){
34+
bar()
35+
}
36+
37+
// This function needs to be big so that it does not get inlined by ThinLTO
38+
// but *does* get inlined into foo() when it is declared `internal` in
39+
// cfail1 (alone).
40+
pub fn bar(){
41+
println!("quux1");
42+
println!("quux2");
43+
println!("quux3");
44+
println!("quux4");
45+
println!("quux5");
46+
println!("quux6");
47+
println!("quux7");
48+
println!("quux8");
49+
println!("quux9");
50+
}
51+
}
52+
53+
mod bar {
54+
55+
#[inline(never)]
56+
pub fn baz() {
57+
#[cfg(cfail2)]
58+
{
59+
crate::foo::bar();
60+
}
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// revisions: cfail1 cfail2
2+
// compile-flags: -O -Zhuman-readable-cgu-names -Cllvm-args=-import-instr-limit=10
3+
// build-pass
4+
5+
// rust-lang/rust#59535:
6+
//
7+
// Consider a call-graph like `[A] -> [B -> D] <- [C]` (where the letters are
8+
// functions and the modules are enclosed in `[]`)
9+
//
10+
// In our specific instance, the earlier compilations were inlining the call
11+
// to`B` into `A`; thus `A` ended up with a external reference to the symbol `D`
12+
// in its object code, to be resolved at subsequent link time. The LTO import
13+
// information provided by LLVM for those runs reflected that information: it
14+
// explicitly says during those runs, `B` definition and `D` declaration were
15+
// imported into `[A]`.
16+
//
17+
// The change between incremental builds was that the call `D <- C` was removed.
18+
//
19+
// That change, coupled with other decisions within `rustc`, made the compiler
20+
// decide to make `D` an internal symbol (since it was no longer accessed from
21+
// other codegen units, this makes sense locally). And then the definition of
22+
// `D` was inlined into `B` and `D` itself was eliminated entirely.
23+
//
24+
// The current LTO import information reported that `B` alone is imported into
25+
// `[A]` for the *current compilation*. So when the Rust compiler surveyed the
26+
// dependence graph, it determined that nothing `[A]` imports changed since the
27+
// last build (and `[A]` itself has not changed either), so it chooses to reuse
28+
// the object code generated during the previous compilation.
29+
//
30+
// But that previous object code has an unresolved reference to `D`, and that
31+
// causes a link time failure!
32+
33+
fn main() {
34+
foo::foo();
35+
bar::baz();
36+
}
37+
38+
mod foo {
39+
40+
// In cfail1, foo() gets inlined into main.
41+
// In cfail2, ThinLTO decides that foo() does not get inlined into main, and
42+
// instead bar() gets inlined into foo(). But faulty logic in our incr.
43+
// ThinLTO implementation thought that `main()` is unchanged and thus reused
44+
// the object file still containing a call to the now non-existant bar().
45+
pub fn foo(){
46+
bar()
47+
}
48+
49+
// This function needs to be big so that it does not get inlined by ThinLTO
50+
// but *does* get inlined into foo() once it is declared `internal` in
51+
// cfail2.
52+
pub fn bar(){
53+
println!("quux1");
54+
println!("quux2");
55+
println!("quux3");
56+
println!("quux4");
57+
println!("quux5");
58+
println!("quux6");
59+
println!("quux7");
60+
println!("quux8");
61+
println!("quux9");
62+
}
63+
}
64+
65+
mod bar {
66+
67+
#[inline(never)]
68+
pub fn baz() {
69+
#[cfg(cfail1)]
70+
{
71+
crate::foo::bar();
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)