Skip to content

C++: Fix two more joins - #22513

Open
MathiasVP wants to merge 2 commits into
github:mainfrom
MathiasVP:fix-two-more-joins
Open

C++: Fix two more joins#22513
MathiasVP wants to merge 2 commits into
github:mainfrom
MathiasVP:fix-two-more-joins

Conversation

@MathiasVP

@MathiasVP MathiasVP commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

This PR fixes two more bad joins.

The first one is pretty obvious: we should start by reducing the set of tuples to ReturnNodes before joining to grab all the enclosing callable.

The next one is a minor performance improvement. It doesn't fix a tuple count explosion from a bad join, but rather reduces the tuple duplication throughout pipeline. This may not look very bad, but I was seeing way larger numbers on an internal Microsoft repo which suggests that the rewrite is worth it:

[2026-09-03 16:51:21] Evaluated non-recursive predicate DataFlowPrivate::storeStepImpl/4#5a9e2fd2@d68f5cme in 220359ms (size: 752669).
Evaluated relational algebra for predicate DataFlowPrivate::storeStepImpl/4#5a9e2fd2@d68f5cme with tuple counts:
              8817       ~0%    {3} r1 = JOIN `FlowSummaryImpl::Private::Steps::summaryStoreStep/3#a7d89e4d` WITH DataFlowNodes::TFlowSummaryNode#d5706fd6 ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Rhs.1
              8817       ~0%    {4}    | JOIN WITH DataFlowNodes::TFlowSummaryNode#d5706fd6 ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Rhs.1, _
              8817       ~0%    {4}    | REWRITE WITH Out.3 := true

         202895630       ~0%    {3} r2 = SCAN `DataFlowPrivate::nodeHasInstruction/3#f469bb06` OUTPUT In.1, In.0, In.2
           8657916       ~3%    {3}    | JOIN WITH `Instruction::StoreInstruction.getDestinationAddressOperand/0#dispred#596a4aba` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2
          12715059       ~7%    {6}    | JOIN WITH `DataFlowPrivate::numberOfLoadsFromOperand/4#7e555666_1023#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Rhs.3, _, Lhs.2, Rhs.2
          12715059       ~0%    {4}    | REWRITE WITH Tmp.3 := 1, Out.3 := (Tmp.3 + In.4 + In.5) KEEPING 4
           1472551       ~4%    {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
           1472551       ~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
            734797       ~6%    {6}    | SCAN OUTPUT In.3, In.4, In.5, In.0, In.1, In.2
        1621127648       ~0%    {5}    | JOIN WITH DataFlowNodes::PostFieldUpdateNode#ba49e082_0231#join_rhs ON FIRST 3 OUTPUT Rhs.3, Lhs.3, Lhs.4, Lhs.5, Lhs.0
        1621127650  ~223033%    {5}    | JOIN WITH `DataFlowNodes::FieldAddress.getField/0#dispred#fea3b845` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2, Lhs.3, Lhs.4
        4183728692  ~244195%    {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
        1387996922  ~202996%    {4}    | JOIN WITH `DataFlowUtil::Content.getIndirectionIndex/0#dispred#c14b335b` ON FIRST 2 OUTPUT Lhs.2, Lhs.0, Lhs.4, Lhs.3

        1388005739  ~199712%    {4} r3 = r1 UNION r2
                                return r3

Notice that the true number of tuples is ~750K, but we end up operating on a pipeline with ~4B tuples.

Copilot AI balanced review requested due to automatic review settings September 4, 2026 16:11
@MathiasVP
MathiasVP requested a review from a team as a code owner September 4, 2026 16:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

The rewrites preserve existing predicate semantics while directly addressing the documented performance problems.

Review tier: Balanced
Findings: None

What changed in this PR

Optimizes two C++ data-flow joins while preserving their semantics.

Changes:

  • Restricts return-node tuples before joining return kinds.
  • Splits store-step matching into smaller source and target joins.
File Description
DataFlowPrivate.qll Restructures store-step joins to reduce tuple duplication.
FlowSummaryImpl.qll Delays return-kind lookup until the return node is bound.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@MathiasVP MathiasVP added the no-change-note-required This PR does not need a change note label Sep 4, 2026

@paldepind paldepind left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me. It seems like you forgot the summary line in the commit messages though, they only have a long description which makes them look wonky.

@MathiasVP

MathiasVP commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Fair enough 😂 Do you want me to force-push a pretty one-line summary in the commits? Fixed!

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
```
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
```

@paldepind paldepind left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for fixing the commit messages. LGTM assuming DCA comes back without complaints :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C++ no-change-note-required This PR does not need a change note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants