[all-commits] [llvm/llvm-project] ad3f09: [ADT] Add single-pass merge for ImmutableSet/Immut...
Gábor Horváth via All-commits
all-commits at lists.llvm.org
Tue Jul 21 04:33:50 PDT 2026
Branch: refs/heads/main
Home: https://github.com/llvm/llvm-project
Commit: ad3f09c7de3da1f023bc1134eaa33be6d39cc9ca
https://github.com/llvm/llvm-project/commit/ad3f09c7de3da1f023bc1134eaa33be6d39cc9ca
Author: Gábor Horváth <xazax.hun at gmail.com>
Date: 2026-07-21 (Tue, 21 Jul 2026)
Changed paths:
M clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
M clang/lib/Analysis/LiveVariables.cpp
M llvm/benchmarks/CMakeLists.txt
A llvm/benchmarks/ImmutableMergeBM.cpp
M llvm/include/llvm/ADT/ImmutableMap.h
M llvm/include/llvm/ADT/ImmutableSet.h
M llvm/unittests/ADT/ImmutableMapTest.cpp
M llvm/unittests/ADT/ImmutableSetTest.cpp
Log Message:
-----------
[ADT] Add single-pass merge for ImmutableSet/ImmutableMap (#209807)
The immutable-set/map dataflow joins merged two containers by inserting
the
elements of one into the other one at a time, costing O(|B| * log|A|)
and
re-copying shared spine nodes on every insert.
Add a single-pass, structure-sharing tree merge on ImutAVLFactory
(mergeTrees), exposed as ImmutableSet::Factory::unionSets and
ImmutableMap::Factory::mergeWith. It recurses over the larger operand
and
splits the smaller at each key, returning non-overlapping subtrees
unchanged,
so each spine node is copied at most once: O(|B| * log(|A|/|B| + 1)).
Two flags tune it for the different joins:
* KeepUnmatched - keep keys unique to one side (set union / a lattice
join
with an identity) vs. pass them through the combiner (a symmetric join,
e.g. liveness Must->Maybe).
* SkipShared - return a pointer-identical subtree unchanged in O(1).
Valid
for an idempotent merge (Combine(a, a) == a), which holds for set union
and
every lattice join. Since dataflow states are path-copied from one
another,
a join's operands usually share most subtrees by pointer, so a join that
touches only a few keys becomes nearly O(diff).
Wire it into the Clang lifetime-safety analysis (set and map joins) and
the
LiveVariables merge, and add ImmutableMergeBM to track it.
Speedups on the lifetime-safety dataflow benchmark (LoanPropagation
phase):
pointer cycle in a loop ~2.6x (1.59 s -> 0.61 s)
CFG merges ~190x (1.83 s -> 9.5 ms)
switch fan-out ~1.9x (LiveOrigins; disjoint states, no skip)
LiveVariables is ~2-3x faster on liveness-heavy inputs and neutral on
typical
code (the join is a small fraction of real-code analysis time).
ImmutableMergeBM confirms the merge beats the add loop for small and
near-identical operands, except for independently-built sets differing
by
exactly one element; when one operand is derived from the other -- the
real
dataflow case -- the pointer-skip makes even that a large win. New
randomized
ADT stress tests check the merge against std::set/std::map oracles for
both
canonicalizing and non-canonicalizing factories.
Assisted by: Claude Opus 4.8
Co-authored-by: Gabor Horvath <gaborh at apple.com>
To unsubscribe from these emails, change your notification settings at https://github.com/llvm/llvm-project/settings/notifications
More information about the All-commits
mailing list