From 8bc69b4012c0cc3515e4a55143710ffee34c7c37 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 4 Sep 2026 17:04:21 +0100 Subject: [PATCH 1/2] C++: Fix a bad join. Before: ``` [2026-09-04 12:36:27] Evaluated non-recursive predicate FlowSummaryImpl::Input2::hasKindAndEnclosingFunction/3#3e350d0c@603f9du6 in 374ms (size: 604170). Evaluated relational algebra for predicate FlowSummaryImpl::Input2::hasKindAndEnclosingFunction/3#3e350d0c@603f9du6 with tuple counts: 672310 ~0% {2} r1 = JOIN `cached_ResolveFunction::isFunction/1#9226b83f` WITH DataFlowPrivate::TSourceCallable#54d42094 ON FIRST 1 OUTPUT Rhs.1, Lhs.0 34670069 ~2% {2} | JOIN WITH `DataFlowUtil::Node.getEnclosingCallable/0#dispred#74002437_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 604170 ~0% {3} | JOIN WITH `DataFlowPrivate::ReturnNode.getKind/0#dispred#c7586c0b` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.0 return r1 ``` After: ``` [2026-09-04 16:46:20] Evaluated non-recursive predicate FlowSummaryImpl::Input2::hasKindAndEnclosingFunction/3#3e350d0c@1ded09ff in 1158ms (size: 604170). Evaluated relational algebra for predicate FlowSummaryImpl::Input2::hasKindAndEnclosingFunction/3#3e350d0c@1ded09ff with tuple counts: 614924 ~1% {3} r1 = JOIN `DataFlowPrivate::ReturnNode.getKind/0#dispred#c7586c0b` WITH `DataFlowUtil::Node.getEnclosingCallable/0#dispred#74002437` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.0 604170 ~5% {3} | JOIN WITH DataFlowPrivate::TSourceCallable#54d42094_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2 604170 ~5% {3} | JOIN WITH `cached_ResolveFunction::isFunction/1#9226b83f` ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2 return r1 ``` --- .../lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll index 780c802dc8ae..176b95933db8 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll @@ -256,7 +256,7 @@ private module Input2 implements Impl::Private::InputSig2 { pragma[nomagic] private predicate hasKindAndEnclosingFunction(Function f, ReturnKind rk, ReturnNode r) { r.getEnclosingCallable().asSourceCallable() = f and - r.getKind() = rk + pragma[only_bind_into](r).getKind() = rk } pragma[nomagic] From 0208f19c639b25315b61fd250a5184cb822325b7 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 4 Sep 2026 17:05:14 +0100 Subject: [PATCH 2/2] C++: Reduce tuple duplication in store step pipeline. Before (notice the large tuple duplication): ``` [2026-09-04 12:36:27] Evaluated non-recursive predicate DataFlowPrivate::storeStepImpl/4#5a9e2fd2@8e5724c9 in 1535ms (size: 189539). Evaluated relational algebra for predicate DataFlowPrivate::storeStepImpl/4#5a9e2fd2@8e5724c9 with tuple counts: 20361 ~4% {3} r1 = JOIN DataFlowNodes::TFlowSummaryNode#d5706fd6 WITH `FlowSummaryImpl::Private::Steps::summaryStoreStep/3#a7d89e4d` ON FIRST 1 OUTPUT Rhs.2, Lhs.1, Rhs.1 20361 ~0% {4} | JOIN WITH DataFlowNodes::TFlowSummaryNode#d5706fd6 ON FIRST 1 OUTPUT Lhs.1, Lhs.2, Rhs.1, _ 20361 ~0% {4} | REWRITE WITH Out.3 := true 27390490 ~0% {3} r2 = SCAN `DataFlowPrivate::nodeHasInstruction/3#f469bb06` OUTPUT In.1, In.0, In.2 1146769 ~1% {3} | JOIN WITH `Instruction::StoreInstruction.getDestinationAddressOperand/0#dispred#596a4aba` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2 1251911 ~0% {6} | JOIN WITH `DataFlowPrivate::numberOfLoadsFromOperand/4#7e555666_1023#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Rhs.3, _, Lhs.2, Rhs.2 1251911 ~3% {4} | REWRITE WITH Tmp.3 := 1, Out.3 := (Tmp.3 + In.4 + In.5) KEEPING 4 354360 ~0% {6} | JOIN WITH DataFlowNodes::PostFieldUpdateNode#ba49e082_1023#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2, Lhs.3, Rhs.2, Rhs.3 354360 ~0% {8} | JOIN WITH DataFlowNodes::TPostUpdateNodeImpl#15a1088b_21#join_rhs ON FIRST 1 OUTPUT Lhs.1, Lhs.2, Lhs.3, Lhs.0, Lhs.4, Lhs.5, Rhs.1, _ {7} | REWRITE WITH Tmp.7 := 1, TEST InOut.6 = Tmp.7 KEEPING 7 170359 ~1% {6} | SCAN OUTPUT In.3, In.4, In.5, In.0, In.1, In.2 697388 ~3% {5} | JOIN WITH DataFlowNodes::PostFieldUpdateNode#ba49e082_0231#join_rhs ON FIRST 3 OUTPUT Rhs.3, Lhs.3, Lhs.4, Lhs.5, Lhs.0 697555 ~302% {5} | JOIN WITH `DataFlowNodes::FieldAddress.getField/0#dispred#fea3b845` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2, Lhs.3, Lhs.4 895112 ~156% {5} | JOIN WITH `DataFlowUtil::FieldContent.getAField/0#dispred#ba1c91e5_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.3, Lhs.1, Lhs.2, Lhs.4 417949 ~139% {4} | JOIN WITH `DataFlowUtil::Content.getIndirectionIndex/0#dispred#c14b335b` ON FIRST 2 OUTPUT Lhs.2, Lhs.0, Lhs.4, Lhs.3 438310 ~121% {4} r3 = r1 UNION r2 return r3 ``` After: ``` [2026-09-04 16:30:27] Evaluated non-recursive predicate DataFlowPrivate::storeStepTarget/4#0e049f2e@635fa9rj in 72ms (size: 699622). Evaluated relational algebra for predicate DataFlowPrivate::storeStepTarget/4#0e049f2e@635fa9rj with tuple counts: 702928 ~0% {4} r1 = JOIN `DataFlowPrivate::hasFieldAndIndirectionIndex/3#a1231a07` WITH `DataFlowPrivate::hasFieldAddressAndField/3#35a98069` ON FIRST 1 OUTPUT Rhs.2, Lhs.1, Rhs.1, Lhs.2 return r1 [2026-09-04 16:30:38] Evaluated non-recursive predicate DataFlowPrivate::storeStepSource/4#765254a7@d27b3d3c in 163ms (size: 1251911). Evaluated relational algebra for predicate DataFlowPrivate::storeStepSource/4#765254a7@d27b3d3c with tuple counts: 863285 ~0% {2} r1 = JOIN `Instruction::StoreInstruction.getDestinationAddressOperand/0#dispred#596a4aba` WITH Instruction::StoreInstruction#ae96f30c ON FIRST 1 OUTPUT Lhs.0, Lhs.1 1146769 ~2% {3} | JOIN WITH `DataFlowPrivate::nodeHasInstruction/3#f469bb06_102#join_rhs` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Rhs.2 1251911 ~1% {6} | JOIN WITH `DataFlowPrivate::numberOfLoadsFromOperand/4#7e555666_1023#join_rhs` ON FIRST 1 OUTPUT Rhs.1, _, Lhs.1, Rhs.3, Lhs.2, Rhs.2 1251911 ~1% {4} | REWRITE WITH Tmp.1 := 1, Out.1 := (Tmp.1 + In.4 + In.5) KEEPING 4 return r1 [2026-09-04 16:30:38] Evaluated non-recursive predicate DataFlowPrivate::storeStepImpl/4#5a9e2fd2@ddd0439f in 70ms (size: 189539). Evaluated relational algebra for predicate DataFlowPrivate::storeStepImpl/4#5a9e2fd2@ddd0439f with tuple counts: 169178 ~0% {4} r1 = JOIN `DataFlowPrivate::storeStepTarget/4#0e049f2e` WITH `DataFlowPrivate::storeStepSource/4#765254a7` ON FIRST 2 OUTPUT Rhs.2, Lhs.3, Lhs.2, Rhs.3 20361 ~4% {3} r2 = JOIN DataFlowNodes::TFlowSummaryNode#d5706fd6 WITH `FlowSummaryImpl::Private::Steps::summaryStoreStep/3#a7d89e4d` ON FIRST 1 OUTPUT Rhs.2, Lhs.1, Rhs.1 20361 ~0% {4} | JOIN WITH DataFlowNodes::TFlowSummaryNode#d5706fd6 ON FIRST 1 OUTPUT Lhs.1, Lhs.2, Rhs.1, _ 20361 ~0% {4} | REWRITE WITH Out.3 := true 189539 ~0% {4} r3 = r1 UNION r2 return r3 ``` --- .../ir/dataflow/internal/DataFlowPrivate.qll | 57 ++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 3a1b42645642..551035c5589e 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -122,6 +122,47 @@ private module Cached { FlowSummaryImpl::Private::Steps::summaryJumpStep(n1, n2) } + bindingset[store] + pragma[inline_late] + private predicate nodeHasInstructionLate(Node node, StoreInstruction store, int indirectionIndex) { + nodeHasInstruction(node, store, indirectionIndex) + } + + pragma[nomagic] + private predicate storeStepSource( + Operand fieldAddress, int contentIndirectionIndex, Node node, boolean certain + ) { + exists(int indirectionIndex, int numberOfLoads, StoreInstruction store | + nodeHasInstructionLate(node, store, indirectionIndex) and + numberOfLoadsFromOperand(fieldAddress, store.getDestinationAddressOperand(), numberOfLoads, + certain) and + contentIndirectionIndex = 1 + indirectionIndex + numberOfLoads + ) + } + + pragma[nomagic] + private predicate hasFieldAddressAndField(Field f, PostFieldUpdateNode pfu, Operand fieldAddress) { + pfu.getIndirectionIndex() = 1 and + pfu.getUpdatedField() = f and + pfu.getFieldAddress() = fieldAddress + } + + pragma[nomagic] + private predicate hasFieldAndIndirectionIndex(Field f, int indirectionIndex, FieldContent fc) { + fc.getAField() = f and + fc.getIndirectionIndex() = indirectionIndex + } + + pragma[nomagic] + private predicate storeStepTarget( + Operand address, int indirectionIndex, PostFieldUpdateNode pfu, FieldContent fc + ) { + exists(Field f | + hasFieldAddressAndField(f, pfu, address) and + hasFieldAndIndirectionIndex(f, indirectionIndex, fc) + ) + } + /** * Holds if data can flow from `node1` to `node2` via an assignment to `f`. * Thus, `node2` references an object with a field `f` that contains the @@ -132,19 +173,9 @@ private module Cached { */ cached predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) { - exists( - PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads, - StoreInstruction store, FieldContent fc - | - postFieldUpdate = node2 and - fc = c and - nodeHasInstruction(node1, pragma[only_bind_into](store), - pragma[only_bind_into](indirectionIndex1)) and - postFieldUpdate.getIndirectionIndex() = 1 and - numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(), - store.getDestinationAddressOperand(), numberOfLoads, certain) and - fc.getAField() = postFieldUpdate.getUpdatedField() and - getIndirectionIndexLate(fc) = 1 + indirectionIndex1 + numberOfLoads + exists(Operand fieldAddress, int indirectionIndex | + storeStepSource(fieldAddress, indirectionIndex, node1, certain) and + storeStepTarget(fieldAddress, indirectionIndex, node2, c) ) or // models-as-data summarized flow