Skip to content

Conversation

@vitalybuka
Copy link
Collaborator

Currently SpecialCaseList created at least twice,
one on by Driver, for diagnostics only, and then
the real one by the ASTContext.

Also, deppending on enabled sanitizers, not all
sections will be used.

In both cases there is unnecessary RadixTree
construction.

This patch changes GlobMatcher to do initialization
lazily only when needed.

And remove empty one from RegexMatcher.

This saves saves 0.5% of clang time building large project.

@llvmbot
Copy link
Member

llvmbot commented Nov 10, 2025

@llvm/pr-subscribers-llvm-support

Author: Vitaly Buka (vitalybuka)

Changes

Currently SpecialCaseList created at least twice,
one on by Driver, for diagnostics only, and then
the real one by the ASTContext.

Also, deppending on enabled sanitizers, not all
sections will be used.

In both cases there is unnecessary RadixTree
construction.

This patch changes GlobMatcher to do initialization
lazily only when needed.

And remove empty one from RegexMatcher.

This saves saves 0.5% of clang time building large project.


Full diff: https://github.com/llvm/llvm-project/pull/167281.diff

1 Files Affected:

  • (modified) llvm/lib/Support/SpecialCaseList.cpp (+18-27)
diff --git a/llvm/lib/Support/SpecialCaseList.cpp b/llvm/lib/Support/SpecialCaseList.cpp index 2ba53d63365d2..79032cbb07f3f 100644 --- a/llvm/lib/Support/SpecialCaseList.cpp +++ b/llvm/lib/Support/SpecialCaseList.cpp @@ -20,6 +20,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/GlobPattern.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" @@ -42,10 +43,9 @@ namespace { class RegexMatcher { public: Error insert(StringRef Pattern, unsigned LineNumber); - void preprocess(); - unsigned match(StringRef Query) const; +private: struct Reg { Reg(StringRef Name, unsigned LineNo, Regex &&Rg) : Name(Name), LineNo(LineNo), Rg(std::move(Rg)) {} @@ -60,10 +60,9 @@ class RegexMatcher { class GlobMatcher { public: Error insert(StringRef Pattern, unsigned LineNumber); - void preprocess(); - unsigned match(StringRef Query) const; +private: struct Glob { Glob(StringRef Name, unsigned LineNo, GlobPattern &&Pattern) : Name(Name), LineNo(LineNo), Pattern(std::move(Pattern)) {} @@ -72,15 +71,20 @@ class GlobMatcher { GlobPattern Pattern; }; + void LazyInit() const; + std::vector<GlobMatcher::Glob> Globs; - RadixTree<iterator_range<StringRef::const_iterator>, - RadixTree<iterator_range<StringRef::const_reverse_iterator>, - SmallVector<int, 1>>> + mutable RadixTree<iterator_range<StringRef::const_iterator>, + RadixTree<iterator_range<StringRef::const_reverse_iterator>, + SmallVector<int, 1>>> PrefixSuffixToGlob; - RadixTree<iterator_range<StringRef::const_iterator>, SmallVector<int, 1>> + mutable RadixTree<iterator_range<StringRef::const_iterator>, + SmallVector<int, 1>> SubstrToGlob; + + mutable bool Initialized = false; }; /// Represents a set of patterns and their line numbers @@ -89,7 +93,6 @@ class Matcher { Matcher(bool UseGlobs, bool RemoveDotSlash); Error insert(StringRef Pattern, unsigned LineNumber); - void preprocess(); unsigned match(StringRef Query) const; bool matchAny(StringRef Query) const { return match(Query); } @@ -122,8 +125,6 @@ Error RegexMatcher::insert(StringRef Pattern, unsigned LineNumber) { return Error::success(); } -void RegexMatcher::preprocess() {} - unsigned RegexMatcher::match(StringRef Query) const { for (const auto &R : reverse(RegExes)) if (R.Rg.match(Query)) @@ -142,7 +143,10 @@ Error GlobMatcher::insert(StringRef Pattern, unsigned LineNumber) { return Error::success(); } -void GlobMatcher::preprocess() { +void GlobMatcher::LazyInit() const { + if (LLVM_LIKELY(Initialized)) + return; + Initialized = true; for (const auto &[Idx, G] : enumerate(Globs)) { StringRef Prefix = G.Pattern.prefix(); StringRef Suffix = G.Pattern.suffix(); @@ -167,6 +171,8 @@ void GlobMatcher::preprocess() { } unsigned GlobMatcher::match(StringRef Query) const { + LazyInit(); + int Best = -1; if (!PrefixSuffixToGlob.empty()) { for (const auto &[_, SToGlob] : PrefixSuffixToGlob.find_prefixes(Query)) { @@ -224,10 +230,6 @@ Error Matcher::insert(StringRef Pattern, unsigned LineNumber) { return std::visit([&](auto &V) { return V.insert(Pattern, LineNumber); }, M); } -void Matcher::preprocess() { - return std::visit([&](auto &V) { return V.preprocess(); }, M); -} - unsigned Matcher::match(StringRef Query) const { if (RemoveDotSlash) Query = llvm::sys::path::remove_leading_dotslash(Query); @@ -237,7 +239,6 @@ unsigned Matcher::match(StringRef Query) const { class SpecialCaseList::Section::SectionImpl { friend class SpecialCaseList; - void preprocess(); const Matcher *findMatcher(StringRef Prefix, StringRef Category) const; public: @@ -410,9 +411,6 @@ bool SpecialCaseList::parse(unsigned FileIdx, const MemoryBuffer *MB, } } - for (Section &S : Sections) - S.Impl->preprocess(); - return true; } @@ -462,13 +460,6 @@ SpecialCaseList::Section::SectionImpl::findMatcher(StringRef Prefix, return &II->second; } -void SpecialCaseList::Section::SectionImpl::preprocess() { - SectionMatcher.preprocess(); - for (auto &[K1, E] : Entries) - for (auto &[K2, M] : E) - M.preprocess(); -} - unsigned SpecialCaseList::Section::getLastMatch(StringRef Prefix, StringRef Query, StringRef Category) const { 
vitalybuka added a commit to vitalybuka/llvm-project that referenced this pull request Nov 10, 2025
Currently SpecialCaseList created at least twice, one on by `Driver`, for diagnostics only, and then the real one by the `ASTContext`. Also, deppending on enabled sanitizers, not all sections will be used. In both cases there is unnecessary RadixTree construction. This patch changes `GlobMatcher` to do initialization lazily only when needed. And remove empty one from `RegexMatcher`. This saves saves 0.5% of clang time building large project. Pull Request: llvm#167281
Created using spr 1.3.7 [skip ci]
Created using spr 1.3.7
Created using spr 1.3.7 [skip ci]
Created using spr 1.3.7
vitalybuka added a commit to vitalybuka/llvm-project that referenced this pull request Nov 12, 2025
Currently SpecialCaseList created at least twice, one on by `Driver`, for diagnostics only, and then the real one by the `ASTContext`. Also, deppending on enabled sanitizers, not all sections will be used. In both cases there is unnecessary RadixTree construction. This patch changes `GlobMatcher` to do initialization lazily only when needed. And remove empty one from `RegexMatcher`. This saves saves 0.5% of clang time building large project. Pull Request: llvm#167281
alexey-bataev and others added 2 commits November 14, 2025 15:00
Created using spr 1.3.7 [skip ci]
Created using spr 1.3.7
@vitalybuka vitalybuka changed the base branch from users/vitalybuka/spr/main.nfcspecialcaselist-convert-preprocess-into-lazyinit to main November 14, 2025 23:01
@vitalybuka vitalybuka requested a review from Copilot November 14, 2025 23:01
Copilot finished reviewing on behalf of vitalybuka November 14, 2025 23:05
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR converts the preprocess() method in SpecialCaseList to use lazy initialization (LazyInit), aiming to improve performance by avoiding unnecessary RadixTree construction. The change eliminates eager preprocessing when SpecialCaseList instances are created but not fully utilized (e.g., when created by Driver for diagnostics only, or when certain sections are never queried).

Key changes:

  • Removed preprocess() methods from RegexMatcher, GlobMatcher, Matcher, and Section::SectionImpl classes
  • Implemented lazy initialization in GlobMatcher via a new LazyInit() const method
  • Made RadixTree members and the Initialized flag mutable to support lazy initialization from const methods

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Created using spr 1.3.7
@vitalybuka vitalybuka merged commit 7016d43 into main Nov 15, 2025
10 checks passed
@vitalybuka vitalybuka deleted the users/vitalybuka/spr/nfcspecialcaselist-convert-preprocess-into-lazyinit branch November 15, 2025 03:22
@llvm-ci
Copy link
Collaborator

llvm-ci commented Nov 15, 2025

LLVM Buildbot has detected a new failure on builder openmp-offload-amdgpu-runtime-2 running on rocm-worker-hw-02 while building llvm at step 6 "test-openmp".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/10/builds/17365

Here is the relevant piece of the build log for the reference
Step 6 (test-openmp) failure: test (failure) ******************** TEST 'libarcher :: races/task-taskwait-nested.c' FAILED ******************** Exit Code: 1 Command Output (stdout): -- # RUN: at line 13 /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/./bin/clang -fopenmp -gdwarf-4 -O1 -fsanitize=thread -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-bins/openmp/runtime/src /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/task-taskwait-nested.c -o /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/task-taskwait-nested.c.tmp -latomic && env TSAN_OPTIONS='ignore_noninstrumented_modules=0:ignore_noninstrumented_modules=1' /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/deflake.bash /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/task-taskwait-nested.c.tmp 2>&1 | tee /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/task-taskwait-nested.c.tmp.log | /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/./bin/FileCheck /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/task-taskwait-nested.c # executed command: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/./bin/clang -fopenmp -gdwarf-4 -O1 -fsanitize=thread -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-bins/openmp/runtime/src /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/task-taskwait-nested.c -o /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/task-taskwait-nested.c.tmp -latomic # note: command had no output on stdout or stderr # executed command: env TSAN_OPTIONS=ignore_noninstrumented_modules=0:ignore_noninstrumented_modules=1 /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/deflake.bash /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/task-taskwait-nested.c.tmp # note: command had no output on stdout or stderr # executed command: tee /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/task-taskwait-nested.c.tmp.log # note: command had no output on stdout or stderr # executed command: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/./bin/FileCheck /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/task-taskwait-nested.c # .---command stderr------------ # | /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/task-taskwait-nested.c:54:16: error: CHECK-NEXT: is not on the line after the previous match # | // CHECK-NEXT: #0 {{.*}}task-taskwait-nested.c:34 # | ^ # | <stdin>:10:2: note: 'next' match was here # | #0 .omp_outlined..1 /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/task-taskwait-nested.c:34:12 (task-taskwait-nested.c.tmp+0x128f21) # | ^ # | <stdin>:3:17: note: previous match ended here # | Write of size 4 at 0x7fffffffe770 by main thread: # | ^ # | <stdin>:4:1: note: non-matching line after previous match is here # | #0 main.omp_outlined_debug__ /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/task-taskwait-nested.c:44:8 (task-taskwait-nested.c.tmp+0x128e82) # | ^ # | # | Input file: <stdin> # | Check file: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/task-taskwait-nested.c # | # | -dump-input=help explains the following input dump. # | # | Input was: # | <<<<<< # | . # | . # | . # | 5: #1 main.omp_outlined /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/task-taskwait-nested.c:24:1 (task-taskwait-nested.c.tmp+0x128e82) # | 6: #2 __kmp_invoke_microtask <null> (libomp.so+0xec8d8) # | 7: #3 main /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/task-taskwait-nested.c:24:1 (task-taskwait-nested.c.tmp+0x129067) # | 8: # | 9: Previous write of size 4 at 0x7fffffffe770 by thread T3: # | 10: #0 .omp_outlined..1 /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/task-taskwait-nested.c:34:12 (task-taskwait-nested.c.tmp+0x128f21) # | next:54 !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: match on wrong line # | 11: #1 .omp_task_entry. /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/task-taskwait-nested.c:29:1 (task-taskwait-nested.c.tmp+0x128f21) # | 12: #2 __kmp_invoke_task(int, kmp_task*, kmp_taskdata*) kmp_tasking.cpp (libomp.so+0x873ee) # | 13: # | 14: As if synchronized via sleep: # | 15: #0 usleep /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp:389:3 (task-taskwait-nested.c.tmp+0xa0cc7) ... 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

6 participants