matklad / benchmarks

11 stars 2 forks source link

The problem of non-transitivity of Rust's inline has seemed to be fixed #2

Open crazyboycjr opened 4 months ago

crazyboycjr commented 4 months ago

From https://matklad.github.io/2021/07/09/inline-in-rust.html#Inlining-and-Separate-Compilation

Note that #[inline] is not transitive: if a trivial public function calls a trivial private function, you need to #[inline] both. See this benchmark for details.

This has not been the case now. I am not sure when this behavior has changed, but it is really a good change.

Steps to reproduce:

$ /tmp/benchmarks/rust-inline/main master @ cargo build --release && gdb target/release/main -ex 'disassemble main::main' -ex 'q'
    Finished `release` profile [optimized + debuginfo] target(s) in 0.00s
GNU gdb (GDB) 13.2
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from target/release/main...
warning: Missing auto-load script at offset 0 in section .debug_gdb_scripts
of file /tmp/benchmarks/rust-inline/main/target/release/main.
Use `info auto-load python-scripts [REGEXP]' to list them.
Dump of assembler code for function _ZN4main4main17hc18d1082cf50cb04E:
   0x0000000000008270 <+0>: push   %rax
   0x0000000000008271 <+1>: mov    $0x5c,%edi
   0x0000000000008276 <+6>: call   *0x49b1c(%rip)        # 0x51d98
End of assembler dump.

Looking into the MIR of the dep crate,

fn foo() -> i32 {
    let mut _0: i32;
    scope 1 (inlined bar) {
    }

    bb0: {
        _0 = const 92_i32;
        return;
    }
}

fn bar() -> i32 {
    let mut _0: i32;

    bb0: {
        _0 = const 92_i32;
        return;
    }
}

In MIR, bar has already been inline by foo. So, the transitivity seems to be guaranteed.

mgeier commented 3 months ago

I also just tried the three variants and I'm getting identical assembly in all three cases.

matklad commented 3 months ago

Does anyone want to bisect which version of Rust is the earliest where it is transitive?