[LTO][ThinLTO] Mixing of LTO and ThinLTO bitcode works in strange way
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
Hello!
Considering the following C files:
- ftn.c:
```c
int ftn() {
return 77;
}
```
- main.c:
```c
int ftn();
int _start() {
return ftn();
}
```
And compiling them in different modes and checks using the following bash script:
```bash
#!/usr/bin/env bash
clang ftn.c -O3 -flto=thin -c -o ftn-thin.o
clang ftn.c -O3 -flto=full -c -o ftn-full.o
clang main.c -O3 -flto=thin -c -o main-thin.o
clang main.c -O3 -flto=full -c -o main-full.o
clang -fuse-ld=lld -nodefaultlibs -nostartfiles -flto=thin ftn-thin.o main-thin.o -o Sttt
clang -fuse-ld=lld -nodefaultlibs -nostartfiles -flto=thin ftn-thin.o main-full.o -o Sttf
clang -fuse-ld=lld -nodefaultlibs -nostartfiles -flto=thin ftn-full.o main-thin.o -o Stft
clang -fuse-ld=lld -nodefaultlibs -nostartfiles -flto=thin ftn-full.o main-full.o -o Stff
clang -fuse-ld=lld -nodefaultlibs -nostartfiles -flto=full ftn-thin.o main-thin.o -o Sftt
clang -fuse-ld=lld -nodefaultlibs -nostartfiles -flto=full ftn-thin.o main-full.o -o Sftf
clang -fuse-ld=lld -nodefaultlibs -nostartfiles -flto=full ftn-full.o main-thin.o -o Sfft
clang -fuse-ld=lld -nodefaultlibs -nostartfiles -flto=full ftn-full.o main-full.o -o Sfff
for filee in S*
do
objdump -S $filee > $filee.S
done
grep "ftn" S*.S -c
```
The output will be the following:
```
Sfff.S:0
Sfft.S:2
Sftf.S:2
Sftt.S:1
Stff.S:0
Stft.S:2
Sttf.S:2
Sttt.S:1
```
0 - `ftn` was inlined; `ftn` code is removed
1 - `ftn` was inlined, but `ftn` code is still presented
2 - `ftn` was not inlined
That means that:
| Binary name | LLD invocation | `ftn.c` mode | `main.c` mode | Resolution |
| :--- | :--- | :--- | :--- | --- |
| `Sfff` | `flto=full` | `flto=full` | `flto=full` | Works as expected |
| `Sfft` | `flto=full` | `flto=full` | `flto=thin` | LTO unexpectedly does not work: fullLTO does not need all information which is presented in ThinLTO bitcode |
| `Sftf` | `flto=full` | `flto=thin` | `flto=full` | LTO unexpectedly does not work: fullLTO does not need all information which is presented in ThinLTO bitcode |
| `Sftt` | `flto=full` | `flto=thin` | `flto=thin` | Works as expected, but it needs `-Wl,--gc-sections` for removing unused symbols |
| `Stff` | `flto=thin` | `flto=full` | `flto=full` | Unexpectedly works. ThinLTO does not have enough information |
| `Stft` | `flto=thin` | `flto=full` | `flto=thin` | LTO does not work as expected. ThinLTO does not have enough information from `main.c` |
| `Sttf` | `flto=thin` | `flto=thin` | `flto=full` | LTO does not work as expected. ThinLTO does not have enough information from `ftn.c` |
| `Sttt` | `flto=thin` | `flto=thin` | `flto=thin` | Works as expected, but it needs `-Wl,--gc-sections` for removing unused symbols |
So, my questions:
- Why linker with `-flto=thin` is able to work with fullLTO bitcode?
- Why linker with `-flto=full` is not able to use ThinLTO bitcode together with FatLTO bitcode?
- Why linker with `-flto=full` needs `-Wl,--gc-sections` to clean unused symbols in the case of `Sftt` binary?
Tested with clang with the most recent commits:
```
clang version 23.0.0git (https://github.com/llvm/llvm-project.git 2e429f7e1f5b490a703de4cf06ef65eed784f7ab)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /tmp/llvm/llvm-project/install/bin
Build config: +assertions
```
Contributor guide
Research direction
Reproduce the eight combinations with the provided ftn.c, main.c, and bash commands using the reported Clang revision. Compare the generated binaries and objdump output, then trace how clang and lld select full LTO versus ThinLTO for mixed bitcode. Done means explaining the differing behavior and defining the expected handling for each combination.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, shell
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100