Index: lld/ELF/LTO.cpp =================================================================== --- lld/ELF/LTO.cpp +++ lld/ELF/LTO.cpp @@ -187,6 +187,38 @@ return c; } +// Check whether cache pruning happens. Add a ThinLTO warning if +// cache_size_bytes or cache_size_files is too small for the current link job. +// The warning recommends the user to consider adjusting --thinlto-cache-policy. +static void +checkCacheCapacity(const std::vector> &files, + CachePruningPolicy Policy) { + // files.size() is greater the number of inputs by one. However, a timestamp + // file (named llvmcache.timestamp) is created and stored in the cache + // directory if --thinlto-cache-policy option is used. llvmcache.timestamp is + // used to prune the old cache file. The cache pruning will happen if (the + // number of inputs+1) is greater than Policy.MaxSizeFiles. Therefore, I used + // files.size() here. + if (Policy.MaxSizeFiles && (files.size() > Policy.MaxSizeFiles)) { + warn("ThinLTO cache pruning happens since the number of created files " + "exceeds the maximum number of files; consider adjusting " + "--thinlto-cache-policy"); + return; + } + + if (Policy.MaxSizeBytes > 0) { + size_t size = 0; + for (const auto &file : files) + if (file) + size += file->getBufferSize(); + + if (size > config->thinLTOCachePolicy.MaxSizeBytes) + warn("ThinLTO cache pruning happens since the total size of the cache " + "files consumed by the current link job exceeds maximum cache " + "size; consider adjusting --thinlto-cache-policy"); + } +} + BitcodeCompiler::BitcodeCompiler() { // Initialize indexFile. if (!config->thinLTOIndexOnlyArg.empty()) @@ -341,6 +373,9 @@ }, cache)); + if (!config->thinLTOCacheDir.empty()) + checkCacheCapacity(files, config->thinLTOCachePolicy); + // Emit empty index files for non-indexed files but not in single-module mode. if (config->thinLTOModulesToCompile.empty()) { for (StringRef s : thinIndices) { Index: lld/test/ELF/lto/cache-warnings.ll =================================================================== --- /dev/null +++ lld/test/ELF/lto/cache-warnings.ll @@ -0,0 +1,54 @@ +; REQUIRES: x86 +;; NetBSD: noatime mounts currently inhibit 'touch' from updating atime +; UNSUPPORTED: system-netbsd + +; RUN: opt -module-hash -module-summary %s -o %t.o +; RUN: opt -module-hash -module-summary %p/Inputs/cache.ll -o %t2.o + +;; Create two cache files in order to test the warnings. +; RUN: rm -Rf %t.cache && mkdir %t.cache +; RUN: touch %t.cache/llvmcache-foo +; RUN: touch %t.cache/llvmcache-bar + +;; Check cache policies of the number of files. +;; Case 1: A value of 0 disables the number of files based pruning. Therefore, there is no warning. +; RUN: ld.lld -v --thinlto-cache-dir=%t.cache --thinlto-cache-policy=prune_interval=0s:cache_size_files=0 %t2.o %t.o -o %t3 2>&1 | FileCheck %s --implicit-check-not=warning +;; Case 2: If the total number of the files created by the current link job is less than the maximum number of files, there is no warning. +; RUN: ld.lld -v --thinlto-cache-dir=%t.cache --thinlto-cache-policy=prune_interval=0s:cache_size_files=3 %t2.o %t.o -o %t3 2>&1 | FileCheck %s --implicit-check-not=warning +;; Case 3: If the total number of the files created by the current link job exceeds the maximum number of files, a warning is given. +; RUN: ld.lld --thinlto-cache-dir=%t.cache --thinlto-cache-policy=prune_interval=0s:cache_size_files=1 %t2.o %t.o -o %t3 2>&1 | FileCheck %s --check-prefix=WARN + +;; Check cache policies of the cache size. +;; Case 1: A value of 0 disables the absolute size-based pruning. Therefore, there is no warning. +; RUN: ld.lld -v --thinlto-cache-dir=%t.cache --thinlto-cache-policy=prune_interval=0s:cache_size_bytes=0 %t2.o %t.o -o %t3 2>&1 | FileCheck %s --implicit-check-not=warning + +;; Get the total size of created cache files. +; RUN: rm -Rf %t.cache && mkdir %t.cache +; RUN: ld.lld --thinlto-cache-dir=%t.cache --thinlto-cache-policy=prune_interval=0s:cache_size_bytes=32k %t2.o %t.o -o %t3 2>&1 +; RUN: %python -c "import os, sys; os.chdir(r\"%t.cache\"); print(sum(os.path.getsize(filename) for filename in os.listdir('.') if os.path.isfile(filename) and filename.startswith(\"llvmcache-\")))" > %t.size.txt 2>&1 + +;; Case 2: If the total size of the cache files created by the current link job is less than the maximum size for the cache directory in bytes, there is no warning. +; RUN: cat %t.size.txt \ +; RUN: | %python -c "import sys; print(int(sys.stdin.read())+int(5))" > %t.size1.txt 2>&1 +; RUN: cat %t.size1.txt \ +; RUN: | %python -c "import subprocess, sys; size = int(sys.stdin.read()); cache_dir=r'%t.cache'; subprocess.run([r'ld.lld', '-v', '--thinlto-cache-dir={}'.format(cache_dir), '--thinlto-cache-policy=prune_interval=0s:cache_size_bytes={}'.format(size), r'%t2.o', r'%t.o', '-o', r'%t3'])" | FileCheck %s --implicit-check-not=warning + +;; Case 3: If the total size of the cache files created by the current link job exceeds the maximum size for the cache directory in bytes, a warning is given. +; RUN: cat %t.size.txt \ +; RUN: | %python -c "import sys; print(int(sys.stdin.read())-int(5))" > %t.size2.txt 2>&1 +; RUN: cat %t.size2.txt \ +; RUN: | %python -c "import subprocess, sys; size = int(sys.stdin.read()); cache_dir=r'%t.cache'; subprocess.run([r'ld.lld', '-v', '--thinlto-cache-dir={}'.format(cache_dir), '--thinlto-cache-policy=prune_interval=0s:cache_size_bytes={}'.format(size), r'%t2.o', r'%t.o', '-o', r'%t3'])" | FileCheck %s --implicit-check-not=warning + +;; Check just emit a warning the first time a file is pruned due to either of the size/number limits. +; RUN: ld.lld --thinlto-cache-dir=%t.cache --thinlto-cache-policy=prune_interval=0s:cache_size_files=1:cache_size_bytes=1 %t2.o %t.o -o %t3 2>&1 | FileCheck %s --check-prefix=WARN + +; WARN: warning: ThinLTO cache pruning happens{{.*}}--thinlto-cache-policy +; WARN-NOT: warning: ThinLTO cache pruning happens{{.*}}--thinlto-cache-policy + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @globalfunc() { +entry: + ret void +}