Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cross-language inlining with LTO #45865

Closed
ruuda opened this issue Nov 8, 2017 · 13 comments
Closed

Cross-language inlining with LTO #45865

ruuda opened this issue Nov 8, 2017 · 13 comments
Labels
A-linkage Area: linking into static, shared libraries and binaries C-enhancement Category: An issue proposing an enhancement or a PR with one.

Comments

@ruuda
Copy link
Contributor

ruuda commented Nov 8, 2017

I link against a C library with a very small function, and I would like its code to be inlined in the Rust function that calls it. I had hoped that it would be possible by setting lto = true in Cargo.toml, but there is still a call in the final executable. This comment confirms that at the moment there is no LTO between Rust and C.

I remember seeing an issue comment here that explained how to do it manually by emitting LLVM IR with rustc and clang and linking it together with llvm-link, then opt, but I cannot seem to find it any more.

The call overhead can be substantial. For my current use case that calls a C function in an inner loop, it causes the running time to be 9 times as long as the equivalent program where the function is inlined. (It relies on a number of unstable features to be able to express the function in Rust.)

So for “efficient C bindings”, LTO that can inline across C and Rust objects would be nice to have.

@ruuda ruuda changed the title Cross-compilation unit inlining with LTO Cross-object inlining with LTO Nov 8, 2017
@alkis
Copy link
Contributor

alkis commented Nov 23, 2017

Any comments on this? Is there some technical limitation that blocks enabling LTO for external libs?

@kennytm kennytm added A-linkage Area: linking into static, shared libraries and binaries C-enhancement Category: An issue proposing an enhancement or a PR with one. labels Nov 23, 2017
@alexcrichton alexcrichton changed the title Cross-object inlining with LTO Cross-language inlining with LTO Nov 27, 2017
@alexcrichton
Copy link
Member

I think the best way to implement this for Rust is to basically make Rust object files "LTO compatible" and rely on users to use the right linker. I believe that almost all LTO solutions for C/C++ actually use linker plugins (hence the "link" in "link-time optimization") and Rust is the odd-one-out that uses a compiler-specific implementation for LTO.

I unfortunately do not personally know what the format of the object files are going into the linker plugins for C/C++ LTO. I think the best way to enable this feature, however, is to basically do just that. Users of C/C++ that want LTO are presumably somewhat familiar with how to do it in C/C++ and with Rust we just want to enable the same thing. Rust should be able to just look like C/C++.

I believe the primary change here will be to alter the way that rustc generates object files. This may embed LLVM IR in some special section (not sure) or may even involve not generating an object file at all! (unsure) Some research will need to be done into how C/C++ do this and how its done with LLVM to see what our options are.

After that we can probably start out by adding a flag like -Z lto-compatible-objects or something like that which means that rustc will generate object files and artifacts that are automatically compatible with C/C++'s scheme of LTO.

At that point users will need to probably ensure that they're using clang for C/C++ (sorry Windows) as well as that their LTO plugin version is greater than that of rustc's and clang's LLVM version (or the same). I think once all that is in place then LTO should "just work" as it'll just do the normal C/C++ thing.

@alkis
Copy link
Contributor

alkis commented Dec 9, 2017

Alex: doesn't rustc use ThinLTO which implies LLVM bitcode object files? In theory this should "Just Work (tm)" as long as the cc crate passes -flto=thin to the compiler.

@alexcrichton
Copy link
Member

@alkis getting this to work likely entails storing object files in a particular format, which isn't currently done.

@jdub
Copy link
Contributor

jdub commented Apr 1, 2018

@alexcrichton (Apologies if I'm telling you things you already know.) From what I can tell, clang -flto doesn't use any fancy ELF sections or formats, it just outputs LLVM bitcode files, and expects LLVM bitcode input, e.g.

$ clang-4.0 -flto -c hello.c
$ file hello.o
hello.o: LLVM IR bitcode

$ clang-4.0 -flto -fuse-ld=gold -Wl hello.o pants.o socks.o
$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, not stripped

llvm-ar can produce an archive that includes bitcode -- this may make it easier to provide "foreign" code in a way that the linker can optimise. I don't know if that will "just work" with Rust's current linker invocation, though.

@alexcrichton
Copy link
Member

@jdub oh awesome! That means that this may not be all that hard :)

@jdub
Copy link
Contributor

jdub commented Apr 2, 2018

@alexcrichton Ah, I didn't realise rlibs include compressed bitcode! Makes sense, really. My rlib also includes the machine code objects from the static archive, so perhaps if I produced a static archive of bitcode instead, the linker would "just work"?

@alexcrichton
Copy link
Member

@jdub it does actually sort of sound like it'll "just work" although I haven't tried it myself, would certainly be worth a shot! We've already got target-specific options for things like object_is_bitcode so rustc is already relatively ready for something like this, I think it'd just need to be a boolean plumbed to more places

@jdub
Copy link
Contributor

jdub commented Apr 3, 2018

Here's a silly sample to muck around with.

$ clang-4.0 -g -Os -flto -std=gnu99 -c hello.c
$ clang-4.0 -g -Os -flto -std=gnu99 -c pants.c
$ clang-4.0 -g -Os -flto -std=gnu99 -c socks.c
$ clang-4.0 -g -flto -fuse-ld=gold -Wl hello.o pants.o socks.o -o hello

$ file *.o
hello.o: LLVM IR bitcode
pants.o: LLVM IR bitcode
socks.o: LLVM IR bitcode

With proper LTO optimisation, I would expect everything to be inlined, and no pants symbol:

$ objdump -D hello | grep -c pants
0

Indeed, all the functions are inlined into main.

Let's make a bitcode static archive of the non-hello objects:

$ llvm-ar-4.0 qs libhello.a socks.o pants.o
llvm-ar-4.0: creating libhello.a
$ llvm-ar-4.0 t libhello.a
socks.o
pants.o

And run rustc with, hopefully, all the relevant linker options:

$ rustc hello.rs -L $(pwd) -l static=hello -C lto -C linker=clang-4.0 -C link-args="-fuse-ld=gold -flto" -O

How did we go?

$ objdump -D hello | grep -c pants
9

D'oh.

@alexcrichton
Copy link
Member

@jdub I think that's because Rust code isn't participating in LTO there with C. Rust's LTO option does LTO internally without relying on the linker currently, so the linker still just sees an object file from us

@ruuda
Copy link
Contributor Author

ruuda commented May 9, 2018

For cross-reference: it looks like this issue is being addressed in #49879, and some initial work has been merged in #50000. Hooray!

@leoyvens
Copy link
Contributor

@ruuda Do you think we can close this in favor of #49879?

@ruuda
Copy link
Contributor Author

ruuda commented May 10, 2018

Yes, they are tracking the same thing after all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-linkage Area: linking into static, shared libraries and binaries C-enhancement Category: An issue proposing an enhancement or a PR with one.
Projects
None yet
Development

No branches or pull requests

6 participants