Python: Change perf fix PoorMansFunctionResolution

Thanks @yoff, this leaves us with the following evaluation, which looks
very close to the one in the other fix (but with cleaner implementation)
-- both at 688k max tuples (although numbers are not exactly the same).

```
[2021-11-24 13:48:40] (14s) Tuple counts for PoorMansFunctionResolution::getSimpleMethodReferenceWithinClass#ff/2@e5f05asv after 74ms:
                      47493  ~3%     {3} r1 = JOIN Class::Class::getAMethod_dispred#ff WITH py_Classes ON FIRST 1 OUTPUT Lhs.1, 0, Lhs.0
                      47335  ~0%     {2} r2 = JOIN r1 WITH AstGenerated::Function_::getArg_dispred#fff ON FIRST 2 OUTPUT Rhs.2, Lhs.2
                      46683  ~0%     {2} r3 = JOIN r2 WITH DataFlowPublic::ParameterNode::getParameter_dispred#fb_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1
                      259968 ~4%     {2} r4 = JOIN r3 WITH LocalSources::Cached::hasLocalSource#ff_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1
                      161985 ~0%     {3} r5 = JOIN r4 WITH Attributes::AttrRef::accesses_dispred#bff_102#join_rhs ON FIRST 1 OUTPUT Rhs.1 'result', Lhs.1, Rhs.2
                      161985 ~2%     {3} r6 = JOIN r5 WITH Attributes::AttrRead#class#f ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Lhs.0 'result'
                      688766 ~0%     {3} r7 = JOIN r6 WITH Function::Function::getName_dispred#ff_10#join_rhs ON FIRST 1 OUTPUT Lhs.1, Rhs.1 'func', Lhs.2 'result'
                      20928  ~0%     {2} r8 = JOIN r7 WITH Class::Class::getAMethod_dispred#ff ON FIRST 2 OUTPUT Lhs.1 'func', Lhs.2 'result'
                                     return r8
```
This commit is contained in:
Rasmus Wriedt Larsen
2021-11-24 13:49:48 +01:00
parent 47448d9efc
commit e2652591a5

View File

@@ -62,49 +62,19 @@ private DataFlow::TypeTrackingNode poorMansFunctionTracker(DataFlow::TypeTracker
exists(DataFlow::TypeTracker t2 | result = poorMansFunctionTracker(t2, func).track(t2, t))
}
/** Helper predicate to avoid bad join order. */
pragma[noinline]
private predicate getSimpleMethodReferenceWithinClass_helper(
Function func, Class cls, DataFlow::AttrRead read
) {
DataFlow::parameterNode(func.getArg(0)).flowsTo(read.getObject()) and
cls.getAMethod() = func
}
/**
* Helper predicate to avoid bad join order, which looked like:
*
* (8s) Tuple counts for PoorMansFunctionResolution::getSimpleMethodReferenceWithinClass#ff/2@cbddf257 after 8.6s:
* 387565 ~0% {3} r1 = JOIN Attributes::AttrRead#class#f WITH Attributes::AttrRef::accesses_dispred#bff ON FIRST 1 OUTPUT Rhs.2, Lhs.0 'result', Rhs.1
* 6548632 ~0% {3} r2 = JOIN r1 WITH Function::Function::getName_dispred#ff_10#join_rhs ON FIRST 1 OUTPUT Rhs.1 'func', Lhs.1 'result', Lhs.2
* 5640480 ~0% {4} r3 = JOIN r2 WITH Class::Class::getAMethod_dispred#ff_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1 'result', Lhs.2, Lhs.0 'func'
* 55660458 ~0% {5} r4 = JOIN r3 WITH Class::Class::getAMethod_dispred#ff ON FIRST 1 OUTPUT Rhs.1, 0, Lhs.1 'result', Lhs.2, Lhs.3 'func'
* 55621412 ~0% {4} r5 = JOIN r4 WITH AstGenerated::Function_::getArg_dispred#fff ON FIRST 2 OUTPUT Rhs.2, Lhs.2 'result', Lhs.3, Lhs.4 'func'
* 54467144 ~0% {4} r6 = JOIN r5 WITH DataFlowPublic::ParameterNode::getParameter_dispred#fb_10#join_rhs ON FIRST 1 OUTPUT Lhs.2, Rhs.1, Lhs.1 'result', Lhs.3 'func'
* 20928 ~0% {2} r7 = JOIN r6 WITH LocalSources::Cached::hasLocalSource#ff ON FIRST 2 OUTPUT Lhs.3 'func', Lhs.2 'result'
* return r7
*/
pragma[noinline]
private predicate getSimpleMethodReferenceWithinClass_helper2(
Function func, Class cls, DataFlow::AttrRead read, Function readFunction
) {
getSimpleMethodReferenceWithinClass_helper(pragma[only_bind_into](func),
pragma[only_bind_into](cls), pragma[only_bind_into](read)) and
read.getAttributeName() = readFunction.getName()
}
/**
* Gets a reference to `func`. `func` must be defined inside a class, and the reference
* will be inside a different method of the same class.
*/
private DataFlow::Node getSimpleMethodReferenceWithinClass(Function func) {
// TODO: Should take MRO into account
exists(Class cls, Function otherFunc |
cls.getAMethod() = func and
cls.getAMethod() = otherFunc
exists(Class cls, Function otherFunc, DataFlow::Node selfRefOtherFunc |
pragma[only_bind_into](cls).getAMethod() = func and
pragma[only_bind_into](cls).getAMethod() = otherFunc
|
getSimpleMethodReferenceWithinClass_helper2(pragma[only_bind_into](otherFunc),
pragma[only_bind_into](cls), pragma[only_bind_into](result), pragma[only_bind_into](func))
selfRefOtherFunc.getALocalSource().(DataFlow::ParameterNode).getParameter() =
otherFunc.getArg(0) and
result.(DataFlow::AttrRead).accesses(selfRefOtherFunc, func.getName())
)
}