Optimize AArch64 code with LLVM link-time optimization and profile-guided optimization
Introduction
Understand PGO and LTO for AArch64 code
Prepare your AArch64 environment and verify LLVM tool availability
Build AArch64 code with LTO
Optimize AArch64 code with S-PGO
Optimize AArch64 code with FE-PGO
Optimize AArch64 code with IR-PGO
Optimize AArch64 code with CSIR-PGO
Next Steps
Optimize AArch64 code with LLVM link-time optimization and profile-guided optimization
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.