mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-11-03 22:49:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			75 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			75 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
// 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();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |