diff options
| author | Katya Romanova <56653669+romanova-ekaterina@users.noreply.github.com> | 2025-11-17 04:24:26 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-17 04:24:26 -0800 |
| commit | 3ee54a6b992c6053726764905030946f8bc10cd0 (patch) | |
| tree | e9a6927520703cb41626b491d4dabfe8fb6bca00 /cross-project-tests | |
| parent | 82ba3f5d316c102aad1b0721d64c028a8724a3a4 (diff) | |
[DTLTO] [LLVM] Initial DTLTO cache implementation (#156433)
This patch implements DTLTO cache. DTLTO cache is implemented the same
way as ThinLTO cache. In fact the same class Cache is used for both of
them.
Because parameters for codegen are different for DTLTO and ThinLTO
(DTLTO codegen is done by invoking clang and its codegen parameters are
not fully synchronized with codegen parameters used by LTO backend).
The object files generated by DTLTO and ThinLTO might be different and
shouldn't be mixed. If ThinLTO and DTLTO share the same cache
directory, the cache file won't interfere with each other.
I added a couple of test files in cross-project-test/dtlto directory,
but if more tests are required for initial implementation, I could add
them.
Diffstat (limited to 'cross-project-tests')
| -rw-r--r-- | cross-project-tests/dtlto/dtlto-cache.test | 89 | ||||
| -rw-r--r-- | cross-project-tests/dtlto/dtlto-thinlto-cache.test | 70 |
2 files changed, 159 insertions, 0 deletions
diff --git a/cross-project-tests/dtlto/dtlto-cache.test b/cross-project-tests/dtlto/dtlto-cache.test new file mode 100644 index 000000000000..b98d4dbb433b --- /dev/null +++ b/cross-project-tests/dtlto/dtlto-cache.test @@ -0,0 +1,89 @@ +REQUIRES: x86-registered-target, ld.lld + +# Show that the ThinLTO cache works with DTLTO. + +RUN: rm -rf %t && split-file %s %t && cd %t + +# Compile source files into bitcode files. +RUN: %clang -O2 --target=x86_64-linux-gnu -flto=thin -c foo.c main.c + +# Execute the linker and check that the cache is populated. +RUN: %clang -O2 --target=x86_64-linux-gnu -Werror -flto=thin -fuse-ld=lld -nostdlib -e main \ +RUN: main.o foo.o -o populate1.elf \ +RUN: -Wl,--thinlto-distributor=%python \ +RUN: -Wl,--thinlto-distributor-arg=%llvm_src_root/utils/dtlto/local.py \ +RUN: -Wl,--thinlto-remote-compiler=%clang \ +RUN: -Wl,--thinlto-cache-dir=cache.dir \ +RUN: -Wl,--save-temps + +# Check that there are two backend compilation jobs occurred. +RUN: grep -wo args populate1.*.dist-file.json | wc -l | grep -qx 3 +RUN: ls cache.dir/llvmcache.timestamp +RUN: ls cache.dir | count 3 + +# Execute the linker again and check that a fully populated cache is used correctly, +# i.e., no additional cache entries are created for cache hits. +RUN: %clang -O2 --target=x86_64-linux-gnu -Werror -flto=thin -fuse-ld=lld -nostdlib -e main \ +RUN: main.o foo.o -o populate2.elf \ +RUN: -Wl,--thinlto-distributor=%python \ +RUN: -Wl,--thinlto-distributor-arg=%llvm_src_root/utils/dtlto/local.py \ +RUN: -Wl,--thinlto-remote-compiler=%clang \ +RUN: -Wl,--thinlto-cache-dir=cache.dir \ +RUN: -Wl,--save-temps + +# Check that there are no backend compilation jobs occurred. +RUN: grep -wo args populate2.*.dist-file.json | wc -l | grep -qx 1 +RUN: ls cache.dir | count 3 + +RUN: %clang -O0 --target=x86_64-linux-gnu -flto=thin -c foo.c -o foo.O0.o +RUN: %clang -O0 --target=x86_64-linux-gnu -flto=thin -c main.c -o main.O0.o + +# Execute the linker again and check that the cache is populated correctly when there +# are no cache hits but there are existing cache entries. +# As a side effect, this also verifies that the optimization level is considered when +# evaluating the cache entry key. + +RUN: %clang -O2 --target=x86_64-linux-gnu -Werror -flto=thin -fuse-ld=lld -nostdlib -e main \ +RUN: main.O0.o foo.O0.o -o populate3.elf \ +RUN: -Wl,--thinlto-distributor=%python \ +RUN: -Wl,--thinlto-distributor-arg=%llvm_src_root/utils/dtlto/local.py \ +RUN: -Wl,--thinlto-remote-compiler=%clang \ +RUN: -Wl,--thinlto-cache-dir=cache.dir \ +RUN: -Wl,--save-temps + +# Check that there are two new backend compilation jobs occurred. +RUN: grep -wo args populate3.*.dist-file.json | wc -l | grep -qx 3 +RUN: ls cache.dir | count 5 + +RUN: %clang -O2 --target=x86_64-linux-gnu -flto=thin -c main-partial.c + +# Execute the linker and check that everything works correctly with the partially populated cache; +# One more cache entry should be generated after this run. + +RUN: %clang -O2 --target=x86_64-linux-gnu -Werror -flto=thin -fuse-ld=lld -nostdlib -e main \ +RUN: main-partial.o foo.o -o main-partial.elf \ +RUN: -Wl,--thinlto-distributor=%python \ +RUN: -Wl,--thinlto-distributor-arg=%llvm_src_root/utils/dtlto/local.py \ +RUN: -Wl,--thinlto-remote-compiler=%clang \ +RUN: -Wl,--thinlto-cache-dir=cache.dir \ +RUN: -Wl,--save-temps + +# Check that there is one new backend compilation jobs occurred. +RUN: grep -wo args main-partial.*.dist-file.json | wc -l | grep -qx 2 +RUN: ls cache.dir | count 6 + +#--- foo.c +volatile int foo_int; +__attribute__((retain)) int foo(int x) { return x + foo_int; } + +#--- main.c +extern int foo(int x); +__attribute__((retain)) int main(int argc, char** argv) { + return foo(argc); +} + +#--- main-partial.c +extern int foo(int x); +__attribute__((retain)) int main(int argc, char** argv) { + return foo(argc+1); +} diff --git a/cross-project-tests/dtlto/dtlto-thinlto-cache.test b/cross-project-tests/dtlto/dtlto-thinlto-cache.test new file mode 100644 index 000000000000..c177112e2dbb --- /dev/null +++ b/cross-project-tests/dtlto/dtlto-thinlto-cache.test @@ -0,0 +1,70 @@ +REQUIRES: x86-registered-target, ld.lld + +# This test verifies that a cache populated by an in-process ThinLTO codegen is +# not reused by an out-of-process (DTLTO) codegen and vice versa. + +RUN: rm -rf %t && split-file %s %t && cd %t + +# Compile source files into bitcode files. +RUN: %clang -O2 --target=x86_64-linux-gnu -flto=thin -c foo.c main.c + +# Execute the linker and check that in-process ThinLTO cache is populated. +RUN: %clang -O2 --target=x86_64-linux-gnu -Werror -flto=thin -fuse-ld=lld -nostdlib -e main \ +RUN: main.o foo.o -o main.elf \ +RUN: -Wl,--thinlto-cache-dir=cache.dir \ +RUN: -Wl,--save-temps + +RUN: ls cache.dir/llvmcache.timestamp +RUN: ls cache.dir | count 3 + +# Execute the linker and check that out-of-process codegen (DTLTO) adds +# additional entries to the cache, implying that in-process and +# out-of-process codegens do not share cache entries. +RUN: %clang -O2 --target=x86_64-linux-gnu -Werror -flto=thin -fuse-ld=lld -nostdlib -e main \ +RUN: main.o foo.o -o populate1.elf \ +RUN: -Wl,--thinlto-distributor=%python \ +RUN: -Wl,--thinlto-distributor-arg=%llvm_src_root/utils/dtlto/local.py \ +RUN: -Wl,--thinlto-remote-compiler=%clang \ +RUN: -Wl,--thinlto-cache-dir=cache.dir \ +RUN: -Wl,--save-temps + +# Check that there are two backend compilation jobs occurred. +RUN: grep -wo args populate1.*.dist-file.json | wc -l | grep -qx 3 +RUN: ls cache.dir | count 5 + +# Clean up cache directory. +RUN: rm -rf cache.dir + +# Execute the linker and check that out-of-process (DTLTO) cache is populated. +RUN: %clang -O2 --target=x86_64-linux-gnu -Werror -flto=thin -fuse-ld=lld -nostdlib -e main \ +RUN: main.o foo.o -o populate2.elf \ +RUN: -Wl,--thinlto-distributor=%python \ +RUN: -Wl,--thinlto-distributor-arg=%llvm_src_root/utils/dtlto/local.py \ +RUN: -Wl,--thinlto-remote-compiler=%clang \ +RUN: -Wl,--thinlto-cache-dir=cache.dir \ +RUN: -Wl,--save-temps + +# Check that there are two backend compilation jobs occurred. +RUN: grep -wo args populate2.*.dist-file.json | wc -l | grep -qx 3 +RUN: ls cache.dir/llvmcache.timestamp +RUN: ls cache.dir | count 3 + +# Execute the linker and check that in-process codegen adds additional entries +# to the cache, implying that in-process and out-of-process codegens do +# not share cache entries. +RUN: %clang -O2 --target=x86_64-linux-gnu -Werror -flto=thin -fuse-ld=lld -nostdlib -e main \ +RUN: main.o foo.o -o main.elf \ +RUN: -Wl,--thinlto-cache-dir=cache.dir \ +RUN: -Wl,--save-temps + +RUN: ls cache.dir | count 5 + +#--- foo.c +volatile int foo_int; +__attribute__((retain)) int foo(int x) { return x + foo_int; } + +#--- main.c +extern int foo(int x); +__attribute__((retain)) int main(int argc, char** argv) { + return foo(argc); +} |
