What LTO is

Link-time optimization (LTO) enables optimization across source file boundaries during the link stage. Without LTO, the compiler optimizes each source file independently before the linker combines the resulting object files. As a result, the optimizer can’t make optimization decisions based on the whole program.

For more information, see the LLVM Link Time Optimization design documentation.

Compare Full-LTO and Thin-LTO

LLVM supports two main LTO modes: Full-LTO and Thin-LTO.

Both modes make Clang emit LLVM bitcode during compilation. The difference is how LLVM performs optimization during the link stage.

Full-LTO merges all input bitcode into a single LLVM module. LLVM then optimizes the program as one unit before generating native code. This gives the optimizer a complete view of the program, but it can increase link time and memory usage.

Thin-LTO keeps the build more scalable. Each compiled module includes a compact summary. During the link stage, LLVM combines these summaries into a global index and determines which functions to import across module boundaries. LLVM then optimizes each module in parallel. Thin-LTO also supports incremental builds by caching compilation results and rebuilding only the modules whose generated code changes.

Build with LTO

LTO is disabled by default. Use -flto=full to enable Full-LTO or -flto=thin to enable Thin-LTO. If you specify -flto without a value, Clang uses Full-LTO.

Build the example in each mode. Pass the same LTO option during compilation and linking:

    

        
        

clang++ -O3 -flto=full -c bsort.cpp -o out/bsort.lto.full.o
clang++ -O3 -flto=full -fuse-ld=lld out/bsort.lto.full.o -o out/bsort.lto.full
  

    
    

        
        

clang++ -O3 -flto=thin -c bsort.cpp -o out/bsort.lto.thin.o
clang++ -O3 -flto=thin -fuse-ld=lld out/bsort.lto.thin.o -o out/bsort.lto.thin
  

    

The -fuse-ld=lld option selects the LLVM linker.

Verify the LTO object files

Use llvm-bcanalyzer on each object file to verify that it contains LLVM bitcode and identify its LTO mode. Thin-LTO bitcode contains a GLOBALVAL_SUMMARY_BLOCK. Full-LTO bitcode contains a FULL_LTO_GLOBALVAL_SUMMARY_BLOCK.

    

        
        
llvm-bcanalyzer -dump out/bsort.lto.full.o | grep 'SUMMARY_BLOCK'
__output__  <FULL_LTO_GLOBALVAL_SUMMARY_BLOCK NumWords=56 BlockCodeSize=4>
__output__  </FULL_LTO_GLOBALVAL_SUMMARY_BLOCK>
__output__  Block ID #24 (FULL_LTO_GLOBALVAL_SUMMARY_BLOCK):
  

        
    
    

        
        
llvm-bcanalyzer -dump out/bsort.lto.thin.o | grep 'SUMMARY_BLOCK'
__output__  <GLOBALVAL_SUMMARY_BLOCK NumWords=56 BlockCodeSize=4>
__output__  </GLOBALVAL_SUMMARY_BLOCK>
__output__  Block ID #20 (GLOBALVAL_SUMMARY_BLOCK):
  

        
    

The block names confirm that Clang created the expected type of LTO bitcode. Run both linked binaries to check that they complete successfully:

    

        
        
./out/bsort.lto.full
./out/bsort.lto.thin

    

What you’ve accomplished and what’s next

You’ve now built and run the example with Full-LTO and Thin-LTO. You’ve also inspected the object files to verify each LTO mode.

Next, you’ll collect sampled profile data and use it to build the application with S-PGO and Thin-LTO.

Back
Next