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

Unnecessarily relink when Link-Time Optimization is on #2535

Closed
gyatpear opened this issue Nov 27, 2024 · 6 comments
Closed

Unnecessarily relink when Link-Time Optimization is on #2535

gyatpear opened this issue Nov 27, 2024 · 6 comments

Comments

@gyatpear
Copy link

Ninja version: 1.12.1

Ninja seems tracking some link-time temporary files, maybe for link-time optimization, and finds them missing at next build time.

Project to reproduce:

CMakeLists.txt:

cmake_minimum_required(VERSION 3.20)
project(test_proj)
add_executable(main main.c)

main.c:

#include <stdio.h>
int main() {
  printf("Hello world!\n");
  return 0;
}

Case 1: Release build w/o Link-Time Optimization

$ mkdir -p build
$ cd build
$ cmake -S .. -G Ninja -D CMAKE_BUILD_TYPE=Release --fresh
-- The C compiler identification is GNU 14.2.1
-- The CXX compiler identification is GNU 14.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.7s)
-- Generating done (0.0s)
-- Build files have been written to: /tmp/tmp.J0m6gKPUMX/build
$ ninja                                                   
[2/2] Linking C executable main
$ ninja -d explain
ninja: no work to do.

Case 2: Release build w/ Link-Time Optimization

$ mkdir -p build
$ cd build
$ cmake -S .. -G Ninja -D CMAKE_BUILD_TYPE=Release -D CMAKE_INTERPROCEDURAL_OPTIMIZATION=ON --fresh
-- The C compiler identification is GNU 14.2.1
-- The CXX compiler identification is GNU 14.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.7s)
-- Generating done (0.0s)
-- Build files have been written to: /tmp/tmp.J0m6gKPUMX/build
$ ninja                                                                                            
[2/2] Linking C executable main
$ ninja -d explain
ninja explain: /tmp/ccafrVpr.ltrans0.ltrans.o has no in-edge and is missing
ninja explain: /tmp/ccafrVpr.ltrans0.ltrans.o is dirty
ninja explain: main is dirty
[1/1] Linking C executable main
$ ninja -d explain
ninja explain: /tmp/cckpFqL6.ltrans0.ltrans.o has no in-edge and is missing
ninja explain: /tmp/cckpFqL6.ltrans0.ltrans.o is dirty
ninja explain: main is dirty
[1/1] Linking C executable main
@digit-google
Copy link
Contributor

It looks like the compiler or linker command is generating incorrect depfile information that includes temporary files that are removed after the compile/link step.

It's either a bug in the toolchain, or the CMake-generated Ninja build plan triggered by CMAKE_INTERPROCEDURAL_OPTIMIZATION=ON, not a problem with Ninja itself.

I recommend you file a bug in the CMake bug tracker for this issue instead.

@gyatpear
Copy link
Author

gyatpear commented Nov 27, 2024

Addendum:

When LTO is on:
GCC+ld: rebuild
GCC+lld: cannot build, however ninja has recorded the /tmp/lto-llvm-XXXXXX.o file
Clang+ld: rebuild
Clang+lld: not rebuild

@gyatpear
Copy link
Author

gyatpear commented Nov 27, 2024

It looks like the compiler or linker command is generating incorrect depfile information that includes temporary files that are removed after the compile/link step.

It's either a bug in the toolchain, or the CMake-generated Ninja build plan triggered by CMAKE_INTERPROCEDURAL_OPTIMIZATION=ON, not a problem with Ninja itself.

I recommend you file a bug in the CMake bug tracker for this issue instead.

I don't think it is CMake and I have examined the build.ninja and rules.ninja, nothing special. Also, when I changed to Clang, it was so clean that Ninja was not relinking.

I think the /tmp/ccXXXXXX.ltrans0.ltrans.o files are produced by ld (or collect2) and ld cleans them before its every exit.

But how can I make Ninja to omit the files?

For GCC+ld:

$ ninja -t deps
main: #deps 4, deps mtime 1732706691602482002 (VALID)
    /usr/lib/libgcc_s.so
    /usr/lib/libc.so
    /tmp/ccGTaMBy.ltrans0.ltrans.o
    /usr/lib/ld-linux-x86-64.so.2

For Clang+lld

$ ninja -t deps
main: #deps 13, deps mtime 1732706774280601562 (VALID)
    /usr/lib64/Scrt1.o
    /usr/lib64/crti.o
    /usr/lib64/gcc/x86_64-pc-linux-gnu/14.2.1/crtbeginS.o
    CMakeFiles/main.dir/main.c.o
    /usr/lib64/gcc/x86_64-pc-linux-gnu/14.2.1/libgcc.a
    /usr/lib64/libgcc_s.so
    /usr/lib64/libgcc_s.so.1
    /usr/lib64/libc.so
    /usr/lib/libc.so.6
    /usr/lib/libc_nonshared.a
    /usr/lib/ld-linux-x86-64.so.2
    /usr/lib64/gcc/x86_64-pc-linux-gnu/14.2.1/crtendS.o
    /usr/lib64/crtn.o

@digit-google
Copy link
Contributor

digit-google commented Nov 27, 2024

Thanks for the extra information.

Entries like /tmp/ccGTaMBy.ltrans0.ltrans.o in the linker command's depfile are the problems. They should not appear there as they are temporary files that are no real inputs, and no longer exist after it completes.

I.e. there is really no practical reason to list them in the .d file generated by the link command.

So this definitely looks like a linker bug, not CMake or Ninja related per se. You would probably have the same issue with the Make backend if it reuses the same files.

A temporary work-around would use a wrapper script around the linker command to process its generated .d file and remove the problematic entries before Ninja consumes them :-/

@ilyapopov
Copy link

ilyapopov commented Nov 27, 2024

I tried the code you shown and can not reproduce.

$ cmake -S ../ -G Ninja -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_BUILD_TYPE=Release --fresh
-- The C compiler identification is GNU 14.2.0
-- The CXX compiler identification is GNU 14.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.2s)
-- Generating done (0.0s)
-- Build files have been written to: /home/ilya/devel/tmp/lto/build
$ ninja 
[2/2] Linking C executable main
$ ninja 
ninja: no work to do.
$ ninja 
ninja: no work to do.
$ gcc --version
gcc (Ubuntu 14.2.0-4ubuntu2) 14.2.0
$ ld --version
GNU ld (GNU Binutils for Ubuntu) 2.43.1
$ cmake --version
cmake version 3.30.3

Looks like there is something with your linker? Or how your toolchain is configured.

Interestingly, for me

ninja -t deps

does not show anything for main at all, only for main.c.o.

@gyatpear
Copy link
Author

Thanks for the extra information.

Entries like /tmp/ccGTaMBy.ltrans0.ltrans.o in the linker command's depfile are the problems. They should not appear there as they are temporary files that are no real inputs, and no longer exist after it completes.

I.e. there is really no practical reason to list them in the .d file generated by the link command.

So this definitely looks like a linker bug, not CMake or Ninja related per se. You would probably have the same issue with the Make backend if it reuses the same files.

A temporary work-around would use a wrapper script around the linker command to process its generated .d file and remove the problematic entries before Ninja consumes them :-/

Finally I found the problem from binutils (2.43+r4+g7999dae6961-1) from Arch Linux. On Ubuntu, binutils 2.42 generate depfile more like lld.

I will close the issue since resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants