In the future, we might want to switch GCC not to generate both object code and LTO representation during the build process. For most packages, dual generation is not necessary because no relocatable object files for static linking are included in the RPM (neither as separate ET_REL .o files, nor in static .a archives). Final (non-relocatable) links of any kind will generate object code, so only LTO representation needs to be written by GCC.
But in case relocatable object files are produced by the package (e.g., for a -static subpackage for static linking), it is necessary to generate object code for relocatable files as well. The reason is that only object code (not LTO representation) is a stable format, and it's the only way to achieve cross-toolchain linking.
The way I envisioned LTO-only building for GCC was to replace the brp-strip-lto script
https://src.fedoraproject.org/rpms/redhat-rpm-config/blob/rawhide/f/brp-strip-lto
with somehting that errors out (fails the build) if any relocatable object files (.o) or static archives (.a) by default, and stop producing object code by default, only LTO representation. If a special redhat-rpm-config flag is set, brp-strip-lto comes back, and GCC is configured to produce both object code and LTO representation (basically what we have today).
However, Clang has chosen a different approach: Build object code in the final stages, via the brp-llvm-compile-lto-elf script:
https://src.fedoraproject.org/rpms/redhat-rpm-config/blob/rawhide/f/brp-llvm-compile-lto-elf
This does not really work for GCC in downstream because we have multiple GCCs there, with incompatible LTO representation. We also cannot replicate the exact command line options that have been used during the package-internal build process; we only have the default redhat-rpm-config flags at this point, and whatever has been serialized into the LTO representation.
Fedora has multiple LLVMs (e.g., rust in Fedora 35 is at LLVM 12, but /usr/bin/clang is LLVM 13). LLVM bitcode is supposed to be more compatible:
https://llvm.org/docs/DeveloperPolicy.html#ir-backwards-compatibility
But don't know to what extent we test that.
I'd prefer to use a single mechanism for both toolchains. But it seems that Clang does not support creating ELF object files that contain LLVM IR (GCC's default mode we use today). Given the problems with post-building object code for GCC, I'm not sure if this is feasible.
Thoughts?
Thanks, Florian
PS: I tried to avoid fat/thin references because the terms are inconsistent across the toolchains.
On 11/15/21 05:06, Florian Weimer wrote:
In the future, we might want to switch GCC not to generate both object code and LTO representation during the build process. For most packages, dual generation is not necessary because no relocatable object files for static linking are included in the RPM (neither as separate ET_REL .o files, nor in static .a archives). Final (non-relocatable) links of any kind will generate object code, so only LTO representation needs to be written by GCC.
But in case relocatable object files are produced by the package (e.g., for a -static subpackage for static linking), it is necessary to generate object code for relocatable files as well. The reason is that only object code (not LTO representation) is a stable format, and it's the only way to achieve cross-toolchain linking.
The way I envisioned LTO-only building for GCC was to replace the brp-strip-lto script
https://src.fedoraproject.org/rpms/redhat-rpm-config/blob/rawhide/f/brp-strip-lto
with somehting that errors out (fails the build) if any relocatable object files (.o) or static archives (.a) by default, and stop producing object code by default, only LTO representation. If a special redhat-rpm-config flag is set, brp-strip-lto comes back, and GCC is configured to produce both object code and LTO representation (basically what we have today).
If we produced LTO only static archives, does this mean end-users who want to use them would need to build their applications with LTO enabled?
-Tom
However, Clang has chosen a different approach: Build object code in the final stages, via the brp-llvm-compile-lto-elf script:
https://src.fedoraproject.org/rpms/redhat-rpm-config/blob/rawhide/f/brp-llvm-compile-lto-elf
This does not really work for GCC in downstream because we have multiple GCCs there, with incompatible LTO representation. We also cannot replicate the exact command line options that have been used during the package-internal build process; we only have the default redhat-rpm-config flags at this point, and whatever has been serialized into the LTO representation.
Fedora has multiple LLVMs (e.g., rust in Fedora 35 is at LLVM 12, but /usr/bin/clang is LLVM 13). LLVM bitcode is supposed to be more compatible:
https://llvm.org/docs/DeveloperPolicy.html#ir-backwards-compatibility
But don't know to what extent we test that.
I'd prefer to use a single mechanism for both toolchains. But it seems that Clang does not support creating ELF object files that contain LLVM IR (GCC's default mode we use today). Given the problems with post-building object code for GCC, I'm not sure if this is feasible.
Thoughts?
Thanks, Florian
PS: I tried to avoid fat/thin references because the terms are inconsistent across the toolchains.
* Tom Stellard:
On 11/15/21 05:06, Florian Weimer wrote:
In the future, we might want to switch GCC not to generate both object code and LTO representation during the build process. For most packages, dual generation is not necessary because no relocatable object files for static linking are included in the RPM (neither as separate ET_REL .o files, nor in static .a archives). Final (non-relocatable) links of any kind will generate object code, so only LTO representation needs to be written by GCC. But in case relocatable object files are produced by the package (e.g., for a -static subpackage for static linking), it is necessary to generate object code for relocatable files as well. The reason is that only object code (not LTO representation) is a stable format, and it's the only way to achieve cross-toolchain linking. The way I envisioned LTO-only building for GCC was to replace the brp-strip-lto script https://src.fedoraproject.org/rpms/redhat-rpm-config/blob/rawhide/f/brp-strip-lto with somehting that errors out (fails the build) if any relocatable object files (.o) or static archives (.a) by default, and stop producing object code by default, only LTO representation. If a special redhat-rpm-config flag is set, brp-strip-lto comes back, and GCC is configured to produce both object code and LTO representation (basically what we have today).
If we produced LTO only static archives, does this mean end-users who want to use them would need to build their applications with LTO enabled?
No, when building for LTO-only mode, it would be a hard error (build failure) if an RPM is built that contains .o or .a files. So the situation that an end users sees LTO only static archives after installing a -devel RPM package cannot actually happen.
Thanks, Florian
On 11/15/2021 6:06 AM, Florian Weimer wrote:
In the future, we might want to switch GCC not to generate both object code and LTO representation during the build process. For most packages, dual generation is not necessary because no relocatable object files for static linking are included in the RPM (neither as separate ET_REL .o files, nor in static .a archives). Final (non-relocatable) links of any kind will generate object code, so only LTO representation needs to be written by GCC.
Yup. It's something I wanted to do, but never had the time to complete.
But in case relocatable object files are produced by the package (e.g., for a -static subpackage for static linking), it is necessary to generate object code for relocatable files as well. The reason is that only object code (not LTO representation) is a stable format, and it's the only way to achieve cross-toolchain linking.
The way I envisioned LTO-only building for GCC was to replace the brp-strip-lto script
https://src.fedoraproject.org/rpms/redhat-rpm-config/blob/rawhide/f/brp-strip-lto
with somehting that errors out (fails the build) if any relocatable object files (.o) or static archives (.a) by default, and stop producing object code by default, only LTO representation. If a special redhat-rpm-config flag is set, brp-strip-lto comes back, and GCC is configured to produce both object code and LTO representation (basically what we have today).
Right. In fact, I had a brp-strip-lto which did precisely this and I did a Fedora build with that brp-strip-lto to get a set of packages that wanted to install a .o or .a composed from .o files. I did a build with that, but I don't have the results anymore.
However, Clang has chosen a different approach: Build object code in the final stages, via the brp-llvm-compile-lto-elf script:
https://src.fedoraproject.org/rpms/redhat-rpm-config/blob/rawhide/f/brp-llvm-compile-lto-elf
This does not really work for GCC in downstream because we have multiple GCCs there, with incompatible LTO representation. We also cannot replicate the exact command line options that have been used during the package-internal build process; we only have the default redhat-rpm-config flags at this point, and whatever has been serialized into the LTO representation.
Fedora has multiple LLVMs (e.g., rust in Fedora 35 is at LLVM 12, but /usr/bin/clang is LLVM 13). LLVM bitcode is supposed to be more compatible:
https://llvm.org/docs/DeveloperPolicy.html#ir-backwards-compatibility
But don't know to what extent we test that.
I'd prefer to use a single mechanism for both toolchains. But it seems that Clang does not support creating ELF object files that contain LLVM IR (GCC's default mode we use today). Given the problems with post-building object code for GCC, I'm not sure if this is feasible.
Thoughts?
It'd be nice to have the same approach, but it may not be ultimately feasible.
Jeff
On Mon, Nov 15, 2021 at 12:13:19PM -0700, Jeff Law wrote:
with somehting that errors out (fails the build) if any relocatable object files (.o) or static archives (.a) by default, and stop producing object code by default, only LTO representation. If a special redhat-rpm-config flag is set, brp-strip-lto comes back, and GCC is configured to produce both object code and LTO representation (basically what we have today).
Right. In fact, I had a brp-strip-lto which did precisely this and I did a Fedora build with that brp-strip-lto to get a set of packages that wanted to install a .o or .a composed from .o files. I did a build with that, but I don't have the results anymore.
What we perhaps could try (but not sure how far we could get) if we detect in an installed *.a or *.o GCC LTO bytecode, instead of erroring out and failing the build try to convert it to normal *.o file (for *.a recursively for each *.o file in there with LTO bytecode) - at least if debug info is emitted and there is DW_AT_producer, try to reconstruct gcc command line and gcc -r that_perhaps_slightly_massaged_command_line -o new.o old.o
Or if we recorded all command line options we care about into LTO bytecode (Optimization/Target options are recorded already on a per-function basis but I'm worried about others), just have a gcc driver mode that turns a non-fat LTO object into normal non-LTO object.
Jakub
Jakub Jelinek wrote:
Or if we recorded all command line options we care about into LTO bytecode (Optimization/Target options are recorded already on a per-function basis but I'm worried about others), just have a gcc driver mode that turns a non-fat LTO object into normal non-LTO object.
That sounds to me like the most reasonable thing to do. LTO bytecode is designed to be compiled to object code (at link time) after all, so why should it not be possible to convert (compile) it to an object code object file directly, without having to recompile the source file completely (with -fno-lto, the hack currently done for Clang)?
Kevin Kofler
* Jakub Jelinek:
Or if we recorded all command line options we care about into LTO bytecode (Optimization/Target options are recorded already on a per-function basis but I'm worried about others), just have a gcc driver mode that turns a non-fat LTO object into normal non-LTO object.
I think that would be useful, it would match the LLVM LTO approach. It's not going to be a perfect replay, but it's at least as good as other uses of that LTO data, so I think it will be fine.
Thanks, Florian