mirror of
https://github.com/github/codeql.git
synced 2026-05-04 21:25:44 +02:00
Merge pull request #995 from hvitved/csharp/split-guards-performance
C#: Speedup guards predicates
This commit is contained in:
@@ -42,41 +42,81 @@ class Assertion extends MethodCall {
|
||||
/** Gets the expression that this assertion pertains to. */
|
||||
Expr getExpr() { result = this.getArgumentForParameter(target.getAssertedParameter()) }
|
||||
|
||||
/**
|
||||
* Holds if basic block `succ` is immediately dominated by this assertion.
|
||||
* That is, `succ` can only be reached from the callable entry point by
|
||||
* going via *some* basic block `pred` containing this assertion, and `pred`
|
||||
* is an immediate predecessor of `succ`.
|
||||
*
|
||||
* Moreover, this assertion corresponds to multiple control flow nodes,
|
||||
* which is why
|
||||
*
|
||||
* ```
|
||||
* exists(BasicBlock bb |
|
||||
* bb.getANode() = this.getAControlFlowNode() |
|
||||
* bb.immediatelyDominates(succ)
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* does not work.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private JoinBlockPredecessor getAPossiblyDominatedPredecessor(JoinBlock jb) {
|
||||
private predicate immediatelyDominatesBlockSplit(BasicBlock succ) {
|
||||
// Only calculate dominance by explicit recursion for split nodes;
|
||||
// all other nodes can use regular CFG dominance
|
||||
this instanceof ControlFlow::Internal::SplitControlFlowElement and
|
||||
exists(BasicBlock bb | bb = this.getAControlFlowNode().getBasicBlock() |
|
||||
result = bb.getASuccessor*()
|
||||
) and
|
||||
result.getASuccessor() = jb and
|
||||
not jb.dominates(result)
|
||||
exists(BasicBlock bb | bb.getANode() = this.getAControlFlowNode() |
|
||||
succ = bb.getASuccessor() and
|
||||
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != bb |
|
||||
succ.dominates(pred)
|
||||
or
|
||||
// `pred` might be another split of this element
|
||||
pred.getANode().getElement() = this
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isPossiblyDominatedJoinBlock(JoinBlock jb) {
|
||||
exists(this.getAPossiblyDominatedPredecessor(jb)) and
|
||||
forall(BasicBlock pred | pred = jb.getAPredecessor() |
|
||||
pred = this.getAPossiblyDominatedPredecessor(jb)
|
||||
pragma[noinline]
|
||||
private predicate strictlyDominatesJoinBlockPredecessor(JoinBlock jb, int i) {
|
||||
this.strictlyDominatesSplit(jb.getJoinBlockPredecessor(i))
|
||||
}
|
||||
|
||||
private predicate strictlyDominatesJoinBlockSplit(JoinBlock jb, int i) {
|
||||
i = -1 and
|
||||
this.strictlyDominatesJoinBlockPredecessor(jb, _)
|
||||
or
|
||||
this.strictlyDominatesJoinBlockSplit(jb, i - 1) and
|
||||
(
|
||||
this.strictlyDominatesJoinBlockPredecessor(jb, i)
|
||||
or
|
||||
jb.dominates(pred)
|
||||
this.getAControlFlowNode().getBasicBlock() = jb.getJoinBlockPredecessor(i)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate strictlyDominatesSplit(BasicBlock bb) {
|
||||
this.getAControlFlowNode().getBasicBlock().immediatelyDominates(bb)
|
||||
this.immediatelyDominatesBlockSplit(bb)
|
||||
or
|
||||
if bb instanceof JoinBlock
|
||||
then
|
||||
this.isPossiblyDominatedJoinBlock(bb) and
|
||||
forall(BasicBlock pred | pred = this.getAPossiblyDominatedPredecessor(bb) |
|
||||
this.strictlyDominatesSplit(pred)
|
||||
or
|
||||
this.getAControlFlowNode().getBasicBlock() = pred
|
||||
)
|
||||
else this.strictlyDominatesSplit(bb.getAPredecessor())
|
||||
// Equivalent with
|
||||
//
|
||||
// ```
|
||||
// exists(JoinBlockPredecessor pred | pred = bb.getAPredecessor() |
|
||||
// this.strictlyDominatesSplit(pred)
|
||||
// ) and
|
||||
// forall(JoinBlockPredecessor pred | pred = bb.getAPredecessor() |
|
||||
// this.strictlyDominatesSplit(pred)
|
||||
// or
|
||||
// this.getAControlFlowNode().getBasicBlock() = pred
|
||||
// )
|
||||
// ```
|
||||
//
|
||||
// but uses no universal recursion for better performance.
|
||||
exists(int last | last = max(int i | exists(bb.(JoinBlock).getJoinBlockPredecessor(i))) |
|
||||
this.strictlyDominatesJoinBlockSplit(bb, last)
|
||||
)
|
||||
or
|
||||
not bb instanceof JoinBlock and
|
||||
this.strictlyDominatesSplit(bb.getAPredecessor())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -407,9 +407,47 @@ class ExitBasicBlock extends BasicBlock {
|
||||
/** Holds if `bb` is an exit basic block. */
|
||||
private predicate exitBB(BasicBlock bb) { bb.getLastNode() instanceof ControlFlow::Nodes::ExitNode }
|
||||
|
||||
private module JoinBlockPredecessors {
|
||||
private import ControlFlow::Nodes
|
||||
|
||||
private class CallableOrCFE extends Element {
|
||||
CallableOrCFE() { this instanceof Callable or this instanceof ControlFlowElement }
|
||||
}
|
||||
|
||||
private predicate id(CallableOrCFE x, CallableOrCFE y) { x = y }
|
||||
|
||||
private predicate idOf(CallableOrCFE x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
|
||||
int getId(JoinBlockPredecessor jbp) {
|
||||
idOf(jbp.getFirstNode().(ElementNode).getElement(), result)
|
||||
or
|
||||
idOf(jbp.(EntryBasicBlock).getCallable(), result)
|
||||
}
|
||||
|
||||
string getSplitString(JoinBlockPredecessor jbp) {
|
||||
result = jbp.getFirstNode().(ElementNode).getSplitsString()
|
||||
or
|
||||
not exists(jbp.getFirstNode().(ElementNode).getSplitsString()) and
|
||||
result = ""
|
||||
}
|
||||
}
|
||||
|
||||
/** A basic block with more than one predecessor. */
|
||||
class JoinBlock extends BasicBlock {
|
||||
JoinBlock() { getFirstNode().isJoin() }
|
||||
|
||||
/**
|
||||
* Gets the `i`th predecessor of this join block, with respect to some
|
||||
* arbitrary order.
|
||||
*/
|
||||
cached
|
||||
JoinBlockPredecessor getJoinBlockPredecessor(int i) {
|
||||
result = rank[i + 1](JoinBlockPredecessor jbp |
|
||||
jbp = this.getAPredecessor()
|
||||
|
|
||||
jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A basic block that is an immediate predecessor of a join block. */
|
||||
|
||||
@@ -101,32 +101,27 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
|
|
||||
succ.dominates(pred)
|
||||
or
|
||||
// `pred` might be another split of `cfe`
|
||||
// `pred` might be another split of this element
|
||||
pred.getLastNode().getElement() = this and
|
||||
pred.getASuccessorByType(t) = succ and
|
||||
t = s
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private JoinBlockPredecessor getAPossiblyControlledPredecessor(
|
||||
JoinBlock controlled, ConditionalSuccessor s
|
||||
) {
|
||||
exists(BasicBlock mid | this.immediatelyControlsBlockSplit(mid, s) |
|
||||
result = mid.getASuccessor*()
|
||||
) and
|
||||
result.getASuccessor() = controlled and
|
||||
not controlled.dominates(result)
|
||||
pragma[noinline]
|
||||
private predicate controlsJoinBlockPredecessor(JoinBlock controlled, ConditionalSuccessor s, int i) {
|
||||
this.controlsBlockSplit(controlled.getJoinBlockPredecessor(i), s)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isPossiblyControlledJoinBlock(JoinBlock controlled, ConditionalSuccessor s) {
|
||||
exists(this.getAPossiblyControlledPredecessor(controlled, s)) and
|
||||
forall(BasicBlock pred | pred = controlled.getAPredecessor() |
|
||||
pred = this.getAPossiblyControlledPredecessor(controlled, s)
|
||||
private predicate controlsJoinBlockSplit(JoinBlock controlled, ConditionalSuccessor s, int i) {
|
||||
i = -1 and
|
||||
this.controlsJoinBlockPredecessor(controlled, s, _)
|
||||
or
|
||||
this.controlsJoinBlockSplit(controlled, s, i - 1) and
|
||||
(
|
||||
this.controlsJoinBlockPredecessor(controlled, s, i)
|
||||
or
|
||||
controlled.dominates(pred)
|
||||
controlled.dominates(controlled.getJoinBlockPredecessor(i))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -134,13 +129,28 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
private predicate controlsBlockSplit(BasicBlock controlled, ConditionalSuccessor s) {
|
||||
this.immediatelyControlsBlockSplit(controlled, s)
|
||||
or
|
||||
if controlled instanceof JoinBlock
|
||||
then
|
||||
this.isPossiblyControlledJoinBlock(controlled, s) and
|
||||
forall(BasicBlock pred | pred = this.getAPossiblyControlledPredecessor(controlled, s) |
|
||||
this.controlsBlock(pred, s)
|
||||
)
|
||||
else this.controlsBlockSplit(controlled.getAPredecessor(), s)
|
||||
// Equivalent with
|
||||
//
|
||||
// ```
|
||||
// exists(JoinBlockPredecessor pred | pred = controlled.getAPredecessor() |
|
||||
// this.controlsBlockSplit(pred, s)
|
||||
// ) and
|
||||
// forall(JoinBlockPredecessor pred | pred = controlled.getAPredecessor() |
|
||||
// this.controlsBlockSplit(pred, s)
|
||||
// or
|
||||
// controlled.dominates(pred)
|
||||
// )
|
||||
// ```
|
||||
//
|
||||
// but uses no universal recursion for better performance.
|
||||
exists(int last |
|
||||
last = max(int i | exists(controlled.(JoinBlock).getJoinBlockPredecessor(i)))
|
||||
|
|
||||
this.controlsJoinBlockSplit(controlled, s, last)
|
||||
)
|
||||
or
|
||||
not controlled instanceof JoinBlock and
|
||||
this.controlsBlockSplit(controlled.getAPredecessor(), s)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1271,6 +1271,14 @@ module Internal {
|
||||
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded.getElement()))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isGuardedByNode2(ControlFlow::Nodes::ElementNode guarded, Ssa::Definition def) {
|
||||
isGuardedByNode1(guarded, _, _, _) and
|
||||
exists(BasicBlock bb | bb = guarded.getBasicBlock() |
|
||||
def = guarded.getElement().(AccessOrCallExpr).getAnSsaQualifier(bb.getANode())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate isGuardedByNode(
|
||||
ControlFlow::Nodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, AbstractValue v
|
||||
@@ -1278,11 +1286,7 @@ module Internal {
|
||||
isGuardedByNode1(guarded, g, sub, v) and
|
||||
sub = g.getAChildExpr*() and
|
||||
forall(Ssa::Definition def | def = sub.getAnSsaQualifier(_) |
|
||||
exists(ControlFlow::Node cfn |
|
||||
def = guarded.getElement().(AccessOrCallExpr).getAnSsaQualifier(cfn)
|
||||
|
|
||||
cfn.getBasicBlock() = guarded.getBasicBlock()
|
||||
)
|
||||
isGuardedByNode2(guarded, def)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
| comments2.cs:118:5:118:21 | // ... | comments2.cs:119:11:119:25 | GenericClass<> | NestedType |
|
||||
| comments2.cs:118:5:118:21 | // ... | comments2.cs:119:11:119:25 | GenericClass<> | UnboundGenericClass |
|
||||
| comments2.cs:124:5:124:16 | // ... | comments2.cs:125:9:125:20 | GenericFn | CallableOrCFE |
|
||||
| comments2.cs:124:5:124:16 | // ... | comments2.cs:125:9:125:20 | GenericFn | InstanceCallable |
|
||||
| comments2.cs:124:5:124:16 | // ... | comments2.cs:125:9:125:20 | GenericFn | UnboundGenericMethod |
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
| VisualStudio.cs:9:11:9:21 | MyTestSuite | TestClass | LeafType |
|
||||
| VisualStudio.cs:9:11:9:21 | MyTestSuite | TestClass | VSTestClass |
|
||||
| VisualStudio.cs:12:21:12:25 | Test1 | TestMethod | CallableOrCFE |
|
||||
| VisualStudio.cs:12:21:12:25 | Test1 | TestMethod | InstanceCallable |
|
||||
| VisualStudio.cs:12:21:12:25 | Test1 | TestMethod | VSTestMethod |
|
||||
| VisualStudio.cs:17:21:17:25 | Test2 | TestMethod | CallableOrCFE |
|
||||
| VisualStudio.cs:17:21:17:25 | Test2 | TestMethod | InstanceCallable |
|
||||
| VisualStudio.cs:17:21:17:25 | Test2 | TestMethod | VSTestMethod |
|
||||
| XUnit.cs:22:11:22:21 | MyTestSuite | TestClass | LeafType |
|
||||
| XUnit.cs:22:11:22:21 | MyTestSuite | TestClass | XUnitTestClass |
|
||||
| XUnit.cs:25:21:25:25 | Test1 | TestMethod | CallableOrCFE |
|
||||
| XUnit.cs:25:21:25:25 | Test1 | TestMethod | InstanceCallable |
|
||||
| XUnit.cs:25:21:25:25 | Test1 | TestMethod | XUnitTestMethod |
|
||||
| XUnit.cs:30:21:30:25 | Test2 | TestMethod | CallableOrCFE |
|
||||
| XUnit.cs:30:21:30:25 | Test2 | TestMethod | InstanceCallable |
|
||||
| XUnit.cs:30:21:30:25 | Test2 | TestMethod | XUnitTestMethod |
|
||||
| nunit.cs:42:11:42:21 | MyTestSuite | TestClass | LeafType |
|
||||
| nunit.cs:42:11:42:21 | MyTestSuite | TestClass | NUnitFixture |
|
||||
| nunit.cs:52:21:52:25 | Test1 | TestMethod | CallableOrCFE |
|
||||
| nunit.cs:52:21:52:25 | Test1 | TestMethod | InstanceCallable |
|
||||
| nunit.cs:52:21:52:25 | Test1 | TestMethod | NUnitTestMethod |
|
||||
| nunit.cs:57:21:57:25 | Test2 | TestMethod | CallableOrCFE |
|
||||
| nunit.cs:57:21:57:25 | Test2 | TestMethod | InstanceCallable |
|
||||
| nunit.cs:57:21:57:25 | Test2 | TestMethod | NUnitTestMethod |
|
||||
| nunit.cs:62:21:62:25 | Test3 | TestMethod | CallableOrCFE |
|
||||
| nunit.cs:62:21:62:25 | Test3 | TestMethod | InstanceCallable |
|
||||
| nunit.cs:62:21:62:25 | Test3 | TestMethod | NUnitTestMethod |
|
||||
| nunit.cs:67:21:67:25 | Test4 | TestMethod | CallableOrCFE |
|
||||
| nunit.cs:67:21:67:25 | Test4 | TestMethod | InstanceCallable |
|
||||
| nunit.cs:67:21:67:25 | Test4 | TestMethod | NUnitTestMethod |
|
||||
| nunit.cs:72:21:72:25 | Test5 | TestMethod | CallableOrCFE |
|
||||
| nunit.cs:72:21:72:25 | Test5 | TestMethod | InstanceCallable |
|
||||
| nunit.cs:72:21:72:25 | Test5 | TestMethod | NUnitTestMethod |
|
||||
|
||||
Reference in New Issue
Block a user