From 2c980d76628e10e99bc106f4f203cf7a18d11d45 Mon Sep 17 00:00:00 2001 From: yoff Date: Tue, 25 Aug 2026 12:36:31 +0200 Subject: [PATCH 1/2] Python: test exposed shared SSA adjacency relations Exercise the three public AdjacentUses relations and compare them with their internal projection or recursive expansion contracts. The cache-placement defect changes evaluator specialization and work rather than semantic results, so a semantic contract snapshot is the stable red-state equivalent; wall-time assertions would be machine-dependent and flaky. This intentionally records no MISSING or SPURIOUS rows: the expected invariant is exact relation equality before and after cache placement changes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 21ab8585-861f-42c9-a834-451604646c6b --- .../AdjacentUsesContract.expected | 12 ++++ .../dataflow-new-ssa/AdjacentUsesContract.ql | 72 +++++++++++++++++++ .../library-tests/dataflow-new-ssa/test.py | 5 ++ 3 files changed, 89 insertions(+) create mode 100644 python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.expected create mode 100644 python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.ql diff --git a/python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.expected b/python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.expected new file mode 100644 index 000000000000..f93ea8a137b2 --- /dev/null +++ b/python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.expected @@ -0,0 +1,12 @@ +exposed_first_use_count +| 9 | +exposed_adjacent_use_count +| 1 | +exposed_use_of_def_count +| 10 | +first_use_projection_mismatch_count +| 0 | +adjacent_use_projection_mismatch_count +| 0 | +use_of_def_expansion_mismatch_count +| 0 | diff --git a/python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.ql b/python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.ql new file mode 100644 index 000000000000..1b6a8790dd28 --- /dev/null +++ b/python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.ql @@ -0,0 +1,72 @@ +import python +private import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl +private import semmle.python.controlflow.internal.Cfg as Cfg +private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl + +private predicate projectedFirstUse(SsaImpl::Definition def, Cfg::NameNode use) { + exists(CfgImpl::BasicBlock bb, int i | + SsaImpl::Impl::firstUse(def, bb, i, _) and + use = bb.getNode(i) + ) +} + +private predicate projectedAdjacentUse(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo) { + exists(CfgImpl::BasicBlock bb1, int i1, CfgImpl::BasicBlock bb2, int i2 | + SsaImpl::Impl::adjacentUseUse(bb1, i1, bb2, i2, _, _) and + nodeFrom = bb1.getNode(i1) and + nodeTo = bb2.getNode(i2) + ) +} + +private predicate expandedUseOfDef(SsaImpl::Definition def, Cfg::NameNode use) { + exists(Cfg::NameNode first | + SsaImpl::AdjacentUses::firstUse(def, first) and + SsaImpl::AdjacentUses::adjacentUseUse*(first, use) + ) +} + +query int exposed_first_use_count() { + result = + count(SsaImpl::Definition def, Cfg::NameNode use | SsaImpl::AdjacentUses::firstUse(def, use)) +} + +query int exposed_adjacent_use_count() { + result = + count(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo | + SsaImpl::AdjacentUses::adjacentUseUse(nodeFrom, nodeTo) + ) +} + +query int exposed_use_of_def_count() { + result = + count(SsaImpl::Definition def, Cfg::NameNode use | SsaImpl::AdjacentUses::useOfDef(def, use)) +} + +query int first_use_projection_mismatch_count() { + result = + count(SsaImpl::Definition def, Cfg::NameNode use | + SsaImpl::AdjacentUses::firstUse(def, use) and not projectedFirstUse(def, use) + or + projectedFirstUse(def, use) and not SsaImpl::AdjacentUses::firstUse(def, use) + ) +} + +query int adjacent_use_projection_mismatch_count() { + result = + count(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo | + SsaImpl::AdjacentUses::adjacentUseUse(nodeFrom, nodeTo) and + not projectedAdjacentUse(nodeFrom, nodeTo) + or + projectedAdjacentUse(nodeFrom, nodeTo) and + not SsaImpl::AdjacentUses::adjacentUseUse(nodeFrom, nodeTo) + ) +} + +query int use_of_def_expansion_mismatch_count() { + result = + count(SsaImpl::Definition def, Cfg::NameNode use | + SsaImpl::AdjacentUses::useOfDef(def, use) and not expandedUseOfDef(def, use) + or + expandedUseOfDef(def, use) and not SsaImpl::AdjacentUses::useOfDef(def, use) + ) +} diff --git a/python/ql/test/library-tests/dataflow-new-ssa/test.py b/python/ql/test/library-tests/dataflow-new-ssa/test.py index a05c871e04df..ad5f47c3852c 100644 --- a/python/ql/test/library-tests/dataflow-new-ssa/test.py +++ b/python/ql/test/library-tests/dataflow-new-ssa/test.py @@ -31,6 +31,11 @@ def basic_assign(): # $ def=basic_assign return y # $ use=y +def repeated_use(x): # $ def=repeated_use def=x + first = x # $ def=first use=x + return x + first # $ use=x use=first + + def reassignment(): # $ def=reassignment x = 1 x = 2 # $ def=x From 0933871654a8e70c6b4de8c46fd38f917755036f Mon Sep 17 00:00:00 2001 From: yoff Date: Tue, 25 Aug 2026 13:19:31 +0200 Subject: [PATCH 2/2] Python: cache exposed shared SSA adjacency relations The shared SSA module requires language adapters to cache predicates that they expose. The Python adapter exposed firstUse, adjacentUseUse, and useOfDef without restoring that cache boundary, unlike the legacy AdjacentUses implementation. On exact historical Salt, the missing boundary caused the same 6,313,793-row liveAtExit fixed point to be evaluated twice. The equivalent plans received distinct RA hashes (c6bc8xgji0uv6seurbhesjqd315 versus fabf1xs3jb6t67a2buq2iv8iof4 for unsafe deserialization, and c6bc8xgji0uv6seurbhesjqd315 versus 8270excv27ldlfrtk19ou81d206 for modification-of-default-value) because one inherited an unrelated cached-empty sentinel while the other used a literal empty base. Cache the three Python adapter relations rather than generic liveness. This restores the documented shared-SSA contract at the narrow language boundary and avoids imposing a 6.31M-row generic cache on every language instantiation. On current head 1a8e317b with exact saltstack/salt@d036b117, three matched prewarmed -j1 repeats reduced median evaluator time from 51.294s to 45.103s for unsafe deserialization and from 42.377s to 34.238s for modification-of-default-value. Median paired reductions were 6.428s and 8.247s. Joined tuples fell by 58,255,670 and 85,664,313; recursive pipeline runs fell by 1,999 and 3,015. Both queries retained the identical empty endpoint hash 2a514e093aae140a14f6bf77beebe1ad in every repeat. Historical exact controls also retained 483,922 definitions, 169,921 phi inputs, 390,548 first uses, 475,226 adjacent uses, and 123,231 semantic call edges with zero left-only or right-only rows. Historical Salt evaluator recovery was 16.5% and 19.9%. Cold prewarm evaluator time was neutral (106.609s to 106.620s), so this is a warm-query optimization rather than a claimed cold-cache speedup. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 21ab8585-861f-42c9-a834-451604646c6b --- python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll index 6b756d67bc6a..ec14749bece7 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll @@ -564,8 +564,10 @@ class EssaVariable extends Ssa::SsaDefinition { * library. Provides the same interface as legacy * `semmle.python.essa.SsaCompute::AdjacentUses`. */ +cached module AdjacentUses { /** Holds if `nodeFrom` and `nodeTo` are adjacent uses of the same SSA variable. */ + cached predicate adjacentUseUse(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo) { exists(CfgImpl::BasicBlock bb1, int i1, CfgImpl::BasicBlock bb2, int i2 | Impl::adjacentUseUse(bb1, i1, bb2, i2, _, _) and @@ -575,6 +577,7 @@ module AdjacentUses { } /** Holds if `use` is a first use of definition `def`. */ + cached predicate firstUse(Ssa::SsaDefinition def, Cfg::NameNode use) { exists(CfgImpl::BasicBlock bb, int i | Impl::firstUse(def, bb, i, _) and @@ -586,6 +589,7 @@ module AdjacentUses { * Holds if `use` is any reachable use of definition `def`. Combines * `firstUse` with transitive use-use adjacency. */ + cached predicate useOfDef(Ssa::SsaDefinition def, Cfg::NameNode use) { firstUse(def, use) or