34

I'm trying to use link-time optimizations with the -flto flag of GCC (6.1.1).

While it works fine with my code, it doesn't link with a static linked library I'm also building and linking with my project (which is Engine and the library is glsl-optimizer, just for reference).

Here is the output:

...
/usr/bin/ranlib: ir_expression_flattening.cpp.o: plugin needed to handle lto object
/usr/bin/ranlib: opt_function_inlining.cpp.o: plugin needed to handle lto object
/usr/bin/ranlib: opt_copy_propagation_elements.cpp.o: plugin needed to handle lto object
...

And after that, of course, I get several "undefined references" to some functions.

I did some research and found out that it might be because of ar, and I should try to use gcc-ar, but I'm not sure how I might do that.

Also, I'm using CMake that does not support lto (except on Intel's compiler on some platforms, so I read...). Even though, I tried using:

set_property(TARGET glsl_optimizer PROPERTY INTERPROCEDURAL_OPTIMIZATION True)

Which didn't work.

Also, I tried GCC's -fuse-linker-plugin flag which didn't work.

I guess I'll have to do it manually the old way directly using gcc-ar, or maybe there's some other method?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
CpCd0y
  • 640
  • 1
  • 8
  • 17
  • Did you try to replace `ar` with `gcc-ar` in `CMAKE_AR` cached variable in `CMakeCache.txt` or via CMake's GUI (under advanced options)? That `INTERPROCEDURAL_OPTIMIZATION` is not working for GCC is an [open issue](https://gitlab.kitware.com/cmake/cmake/issues/15939) at CMake's GitLab page. – Florian Aug 31 '16 at 11:42
  • @Florian: I just tried and only setting `CMAKE_AR` doesn't solve the problem. You also need `CMAKE_CXX_ARCHIVE_CREATE` and `CMAKE_CXX_ARCHIVE_FINISH` (cf. @Mike Kinghan's answer) – CpCd0y Aug 31 '16 at 19:25
  • [This quick fix](https://stackoverflow.com/a/32461766) worked like a charm for me! – Gumby The Green May 04 '19 at 11:28

2 Answers2

30

Here is an MCVE CMake project that reproduces the problem:

$ ls -R hellow
hellow:
CMakeLists.txt  hello.c  libhello.c

$ cat hellow/CMakeLists.txt 
cmake_minimum_required (VERSION 2.6)
project (hellow)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto")
#SET(CMAKE_AR  "gcc-ar")
#SET(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>")
#SET(CMAKE_C_ARCHIVE_FINISH   true)
add_library(hello STATIC libhello.c) 
add_executable(hellow hello.c)
target_link_libraries(hellow hello)
add_dependencies(hellow hello)


$ cat hellow/hello.c 
extern void hello(void);

int main(void)
{
    hello();
    return 0;
}

$ cat hellow/libhello.c 
#include <stdio.h>

void hello(void)
{
    puts("Hello");
}

Configuration is good:

$ mkdir build_hellow
$ cd build_hellow/
$ cmake ../hellow
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/imk/dev/so/build_hellow

Build fails as per problem:

$ make
Scanning dependencies of target hello
[ 25%] Building C object CMakeFiles/hello.dir/libhello.c.o
[ 50%] Linking C static library libhello.a
/usr/bin/ar: CMakeFiles/hello.dir/libhello.c.o: plugin needed to handle lto object
/usr/bin/ranlib: libhello.c.o: plugin needed to handle lto object
[ 50%] Built target hello
Scanning dependencies of target hellow
[ 75%] Building C object CMakeFiles/hellow.dir/hello.c.o
[100%] Linking C executable hellow
/tmp/ccV0lG36.ltrans0.ltrans.o: In function `main':
<artificial>:(.text+0x5): undefined reference to `hello'
collect2: error: ld returned 1 exit status
CMakeFiles/hellow.dir/build.make:95: recipe for target 'hellow' failed
make[2]: *** [hellow] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/hellow.dir/all' failed
make[1]: *** [CMakeFiles/hellow.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2

There is more than one solution. One is to uncomment the 3 commented lines in CMakeLists.txt above. Then:

$ cmake ../hellow/
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/imk/dev/so/build_hellow

$ make
Scanning dependencies of target hello
[ 25%] Building C object CMakeFiles/hello.dir/libhello.c.o
[ 50%] Linking C static library libhello.a
[ 50%] Built target hello
Scanning dependencies of target hellow
[ 75%] Building C object CMakeFiles/hellow.dir/hello.c.o
[100%] Linking C executable hellow
[100%] Built target hellow

$ ./hellow 
Hello

This fix makes use of the following facts.

The build-breaking problem:

/usr/bin/ar: CMakeFiles/hello.dir/libhello.c.o: plugin needed to handle lto object
...
/usr/bin/ranlib: libhello.c.o: plugin needed to handle lto object

can solved by giving ar and ranlib the option:

--plugin=$(gcc --print-file-name=liblto_plugin.so)

However, GNU ranlib is merely a synonym for ar -s, and gcc-ar is a wrapper for ar that supplies that plugin.

CMake's build template for a C static library is:

CMAKE_C_ARCHIVE_CREATE ( = <CMAKE_AR> qc <TARGET> <LINK_FLAGS> <OBJECTS>)
CMAKE_C_ARCHIVE_FINISH ( = <CMAKE_RANLIB> <TARGET>)

which for GNU ar is equivalent to:

CMAKE_C_ARCHIVE_CREATE ( = <CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>)
CMAKE_C_ARCHIVE_FINISH ( = true) # Or any other no-op command

So with these settings plus:

SET(CMAKE_AR  "gcc-ar")

we're good.

For a C++ project, of course, set CMAKE_CXX_ARCHIVE_CREATE and CMAKE_CXX_ARCHIVE_FINISH

Community
  • 1
  • 1
Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
  • Thanks for your answer. It worked perfectly. You get the tick :) – CpCd0y Aug 31 '16 at 19:24
  • 1
    I tried these steps with GCC5.4, while it "compiles with LTO" you don't see any of the optimisations take place that you would expect. – BlamKiwi Jan 07 '17 at 21:08
  • 1
    @BlamKiwi In [this archived message](https://lists.launchpad.net/kicad-developers/msg17690.html), the modify `CMAKE_AR`, `CMAKE_NM`, and `CMAKE_RANLIB`. Is this may be the reason? If that is the case, can somebody update the answer (my technical kwowledge is not enough to comprehand and explain it properly). – Halil Sen Feb 03 '17 at 10:57
  • @HalilŞEN I never really quite managed to figure out whats going on. Although I noticed that Clang respects always_inline when LTOing – BlamKiwi Feb 03 '17 at 23:51
  • @Mike Kingham Can you elaborate on what exactly `liblto_plugin.so` does and what is the difference between your solution and using `-ffat-lto-objects` which also works? I asked another SO [question](https://stackoverflow.com/questions/46934111/which-is-the-correct-way-to-build-a-static-library-with-link-time-code-generatio/) for this questions. – bobeff Oct 25 '17 at 15:19
  • @bobeff `liblto_plugin.so` is the shared library plugin that supported LTO for tools that are concerned with it. I hadn't heard of `-ffat-lto-objects` till now. It was introduced in GCC 7, which had not been released when I wrote my answer. Its documentation is [here](https://gcc.gnu.org/onlinedocs/gcc-7.2.0/gcc/Optimize-Options.html#Optimize-Options) – Mike Kinghan Oct 25 '17 at 18:46
  • 2
    `SET(CMAKE_AR "gcc-ar")` helped - thank you! Can you move it to the top? – RushPL Apr 28 '18 at 04:39
  • Thanks. I was trying to cross compile for armr5. Changing `"ar"` to `"gcc-ar"` helped. – sergej Aug 22 '18 at 08:22
  • Changing `CMAKE_AR` inside the cache doesn't seem to do anything (with Ninja generator), changing the variable inside CMakeLists has an effect, however. Any idea why this is? – Andreas Duering Jul 16 '19 at 11:19
  • The solution with `CMAKE_AR` having the "ar" replaced with "gcc-ar" fixes the problem, but ICEs on GCC 8.2 and some other search results also show similar problems with LTO with GCC 8.0, 8.1 and earlier versions. – Xeverous Feb 06 '20 at 22:38
  • The LTO crash happens only when a non-LTO code is linked with LTO code. Having everything or none compiled with LTO does not crash. – Xeverous Feb 08 '20 at 08:44
  • In other words, here is an answer that is 5 pages long.... good luck!! – C.J. Aug 02 '20 at 00:26
1

I had a similar issue after porting to g++-11. It was probably caused by a change of defaults - using "slim" objects instead of "fat" - and adding -ffat-lto-objects solved it.

See also this SO answer and the comments there.

Elazar
  • 20,415
  • 4
  • 46
  • 67