Rust & LTO: I think I figured out what's wrong #157
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Background
I use the default v3 makepkg file on my Arch Linux computer for the AUR, in addition to using the ALHP repositories. I noticed that any package containing rust code failed, so in the meantime, I just disabled LTO by removing
-Clto=fatfrom theRUSTFLAGSline. I had some extra time on my hands this weekend, so I decided to debug this.I have an x86-64-v3 CPU on my computer.
Results
Instead of using:
RUSTFLAGS="-Copt-level=3 -Ctarget-cpu=x86-64-v<?> -Clto=fat -Ccodegen-units=1 -Clinker-plugin-lto"Use:
RUSTFLAGS="-Copt-level=3 -Ctarget-cpu=x86_64-v<?> -Clto=fat -Ccodegen-units=1 -Clinker-plugin-lto=no -Cembed-bitcode=yes"Process
I used exa from the community repository because it's written entirely in rust, invokes
cargo(i.e., compiles like a normal rust package) and is officially supported by Arch Linux, so the chances of running into a package-specific issue were much lower. This way, one may verify the configuration.Step 1
Using the default LTO v3 makepkg file directly to build
exa, one gets this error:The rust documentation notes here that such an error would occur.
Solution: Add
-C embed-bitcode=yestoRUSTFLAGS(-C embed-bitcode=nois the default without this).Step 2
Rebuilding, one gets a lot of errors like this:
This took me a bit to figure out, but the gist is: the
.ofiles being made by rust are LLVM bitcode files, which thegcclinker (by defaultbfd) doesn't know how to handle. Changing the linker tolldorclangdidn't seem to work either, howeverclangandlldare known to compile more quickly but produce slower binaries, so I'd like to stick togccandbfd,gold, ormoldas much as possible. Arch Linux and ALHP usebfdby default, so I see no reason to change that here.Solution: To make
rustcgenerate normal object files instead of LLVM bitcode, replaceClinker-plugin-ltowithClinker-plugin-lto=noinRUSTFLAGS.Yay! Now
exabuilds, and the binary works!Step 3
I thought that maybe it would be a good idea to try building other packages in the AUR that use rust, just to test. I tested lottieconv, authenticator, and lolcate.
I got this error for all of them:
This is caused by failing to set
--targetwhile invokingcargo fetchorcargo b. As far as everything I have seen, all packages in the official Arch Linux repository specify--target` explicitly, so this shouldn't be an issue.For anyone reading this, one can specify
--targetincargo buildin AUR packages (which may not be specific to one architecture) by harnessing theCHOSTshell variable provided in the defaultmakepkg.conf. Usecargo build --target ${CHOST//-pc-/-unknown-}.I'd understand if this bug makes the ALHP devs hesitant to make this change, however it only affects AUR packages, not official ones.
EDIT: You can add
--target ${CHOST//-pc-/-unknown-}directly to theRUSTFLAGSvariable, but it doesn't seem to fix the bug mentioned above in Step 3.EDIT 2: Adding
CARGO_BUILD_TARGET="${CHOST//-pc-/-unknown-}"to/etc/makepkg.confand then patching/usr/share/makepkg/buildenv.shto exportCARGO_BUILD_TARGETfixes the issue noted in Step 3.Okay, the issue noted in step 3 seems to seriously be an issue with packages building with meson that invoke cargo in the back. This is particularly common among GNOME project stuff. Maybe this isn't such a good idea, but maybe someone has another solution.
@anonfunc According to https://users.rust-lang.org/t/status-of-lto-option/49032/8, we should use
export CARGO_PROFILE_RELEASE_LTO=fatinstead of manually adding-Clto=fatand-Clinker-plugin-lto, thereforecargocan decide whether to use lto. Force enabling lto will make packages that use compile-time code generation fail to be built.@saltedcoffii Thanks for your detailed exploration. ALHP already has detection of those
errors. So we waste only a little bit of buildtime here, and these packages are not build with lto, but that fortunately only affects a few packages. Exporting new envs is currently blocked by #149, so even if we wanted to set a new
CARGO_BUILD_TARGET, its not possible until that issue is fixed. But I'm not sure that is worth it for the few packages this affects. Have you compared packages that are not affected by this bug with and without your newCARGO_BUILD_TARGET?@AvianaCruz Do you have any documentation that explicitly states that
CARGO_PROFILE_RELEASE_LTOlets cargo decide when to use lto? I could not find anything about that, neither on your linked page nor on https://doc.rust-lang.org/cargo/reference/profiles.html.@anonfunc https://doc.rust-lang.org/cargo/reference/environment-variables.html#:~:text=CARGO_PROFILE_%3Cname%3E_LTO
This page also does not state it lets cargo choose which LTO behavior to adopt. It just links to here, where there is also nothing stated, and then to here, where we have the same settings that apply to
-C lto. As far as I understand, the env is just another way to set lto, but not different to-C lto.@anonfunc This is the explanation: https://users.rust-lang.org/t/error-lto-can-only-be-run-for-executables-cdylibs-and-static-library-outputs/73369/4
Also the issue: https://github.com/rust-lang/cargo/issues/6375#issuecomment-560124129
(Cargo profile is a stable feature now so that there is no need to add
-Zconfig-profile.)@anonfunc
cargoplays as a build tool which generates compiler flags. Rust has a feature that generates code at compile time, and the code which generates code doesn't exists in the final binary, so it can not be linked. Letcargodeciding lto avoids the problem that tries to link non-exist code to binary.I mean, it certainly won't hurt if it still enables LTO in cases where we have LTO enabled currently. Let's try it out as soon as a fix for #149 is implemented.
@anonfunc "rust" package should not be compiled with x86-64 level targeting flags, because it results in its shipped stdlibs also being compiled with higher x86-64 level.
Therefore every binaries compiled with the rust toolchain will be not compatible with CPU of lower x86-64 level because rust statically linking everything by default, including the stdlib which is shipped with the rust toolchain and not recompiled during the build stages.
@AvianaCruz That's unfortunate. ALHP does not currently feature a dedicated list for the x86-64-vN flag, so we need to backlist it completely then.
@anonfunc
ripgrepfails to build because of the error:options-C embed-bitcode=noand-C ltoare incompatibletoo and has apparently been the case since December of last year; I thought you said it detects this...?It should detect these errors, yes. But I'm planing on switching to
CARGO_BUILD_TARGETsoon anyways, which should resolve this issue.Together with the new build-queue (
ece8c4c7d9)CARGO_BUILD_TARGETis now also included. We'll see how it goes.It seems to work fine. If there are any suggestions on how to improve it even further, please open a separate issue. Any feedback to
CARGO_BUILD_TARGETcan go in here.