Python: Fix small join-order problem for call-graph

problem is:
```
           14294  ~33%    {1} r23 = r21 UNION r22
           13626   ~0%    {2}    | JOIN WITH `DataFlowPublic::Node.getEnclosingCallable/0#dispred#be95825a` ON FIRST 1 OUTPUT Rhs.1, Lhs.0
        11871493   ~2%    {2}    | JOIN WITH `DataFlowPublic::Node.getEnclosingCallable/0#dispred#be95825a_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1
         6810938   ~3%    {2}    | JOIN WITH num#DataFlowPublic::TCfgNode#2cd2fb22_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1
               0   ~0%    {4}    | JOIN WITH `DataFlowDispatch::resolveMethodCall/4#3067f1f1#reorder_0_3_1_2#prev` ON FIRST 2 OUTPUT Rhs.3, Lhs.1, Lhs.0, Rhs.2
               0   ~0%    {4}    | JOIN WITH num#DataFlowDispatch::CallTypeClassMethod#3508c3e5 ON FIRST 1 OUTPUT Lhs.3, Lhs.2, Lhs.0, Lhs.1
               0   ~0%    {4}    | JOIN WITH `DataFlowDispatch::resolveCall/3#454c02d8#reorder_1_0_2#prev` ON FIRST 3 OUTPUT Lhs.3, Lhs.1, Lhs.0, Lhs.2
               0   ~0%    {5}    | JOIN WITH num#DataFlowDispatch::TSelfArgumentPosition#de6d64b8 CARTESIAN PRODUCT OUTPUT Lhs.1, Lhs.2, Lhs.3, Lhs.0, Rhs.0
```
that is, it does cartesian product of DataFlowPublic::Node.getEnclosingCallable

After fix

```
        14294  ~33%    {1} r23 = r21 UNION r22
            0   ~0%    {4}    | JOIN WITH `DataFlowDispatch::resolveMethodCall/4#3067f1f1#reorder_3_0_1_2#prev` ON FIRST 1 OUTPUT Rhs.3, Lhs.0, Rhs.1, Rhs.2
            0   ~0%    {4}    | JOIN WITH num#DataFlowDispatch::CallTypeClassMethod#3508c3e5 ON FIRST 1 OUTPUT Lhs.3, Lhs.2, Lhs.0, Lhs.1
            0   ~0%    {4}    | JOIN WITH `DataFlowDispatch::resolveCall/3#454c02d8#reorder_1_0_2#prev` ON FIRST 3 OUTPUT Lhs.1, Lhs.3, Lhs.0, Lhs.2
            0   ~0%    {5}    | JOIN WITH num#DataFlowPublic::TCfgNode#2cd2fb22 ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.0, Lhs.2, Lhs.3
            0   ~0%    {5}    | JOIN WITH `DataFlowPublic::Node.getEnclosingCallable/0#dispred#be95825a` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.2, Lhs.3, Lhs.4
            0   ~0%    {4}    | JOIN WITH `DataFlowPublic::Node.getEnclosingCallable/0#dispred#be95825a` ON FIRST 2 OUTPUT Lhs.0, Lhs.2, Lhs.3, Lhs.4
            0   ~0%    {5}    | JOIN WITH num#DataFlowDispatch::TSelfArgumentPosition#de6d64b8 CARTESIAN PRODUCT OUTPUT Lhs.1, Lhs.2, Lhs.3, Lhs.0, Rhs.0
```

Overall stats

(old)
Pipeline standard for DataFlowDispatch::getCallArg/5#21589076@b30c7vxg was evaluated in 51 iterations totaling 54ms (delta sizes total: 38247).

==>

(new)
Pipeline standard for DataFlowDispatch::getCallArg/5#21589076@c1559vxu was evaluated in 51 iterations totaling 28ms (delta sizes total: 38247).
This commit is contained in:
Rasmus Wriedt Larsen
2024-03-21 12:31:58 +01:00
parent 2aa5ae41fb
commit cff63ad5d5

View File

@@ -1302,9 +1302,7 @@ predicate getCallArg(CallNode call, Function target, CallType type, Node arg, Ar
//
// call_func(my_obj.some_method)
// ```
exists(CfgNode cfgNode | cfgNode.getNode() = call |
cfgNode.getEnclosingCallable() = arg.getEnclosingCallable()
)
exists(CfgNode cfgNode | cfgNode.getNode() = call | sameEnclosingCallable(cfgNode, arg))
or
// cls argument for classmethod calls -- see note above about bound methods
type instanceof CallTypeClassMethod and
@@ -1312,9 +1310,7 @@ predicate getCallArg(CallNode call, Function target, CallType type, Node arg, Ar
resolveMethodCall(call, target, type, arg) and
(arg = classTracker(_) or arg = clsArgumentTracker(_)) and
// dataflow lib has requirement that arguments and calls are in same enclosing callable.
exists(CfgNode cfgNode | cfgNode.getNode() = call |
cfgNode.getEnclosingCallable() = arg.getEnclosingCallable()
)
exists(CfgNode cfgNode | cfgNode.getNode() = call | sameEnclosingCallable(cfgNode, arg))
or
// normal arguments for method calls
(
@@ -1365,6 +1361,16 @@ predicate getCallArg(CallNode call, Function target, CallType type, Node arg, Ar
)
}
/**
* join-order helper for getCallArg, since otherwise we would do cartesian product of
* the enclosing callables
*/
bindingset[node1, node2]
pragma[inline_late]
private predicate sameEnclosingCallable(Node node1, Node node2) {
node1.getEnclosingCallable() = node2.getEnclosingCallable()
}
// =============================================================================
// DataFlowCall
// =============================================================================