mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Python: Use aggressive dead-code elimination when pruning.
This commit is contained in:
@@ -34,7 +34,7 @@ private AstNode toAst(ControlFlowNode n) {
|
||||
class ControlFlowNode extends @py_flow_node {
|
||||
|
||||
cached ControlFlowNode() {
|
||||
not Pruner::unreachable(this)
|
||||
Pruner::reachable(this)
|
||||
}
|
||||
|
||||
/** Whether this control flow node is a load (including those in augmented assignments) */
|
||||
@@ -175,12 +175,13 @@ class ControlFlowNode extends @py_flow_node {
|
||||
|
||||
/** Gets a predecessor of this flow node */
|
||||
ControlFlowNode getAPredecessor() {
|
||||
py_successors(result, this)
|
||||
this = result.getASuccessor()
|
||||
}
|
||||
|
||||
/** Gets a successor of this flow node */
|
||||
ControlFlowNode getASuccessor() {
|
||||
py_successors(this, result)
|
||||
py_successors(this, result) and
|
||||
not Pruner::unreachableEdge(this, result)
|
||||
}
|
||||
|
||||
/** Gets the immediate dominator of this flow node */
|
||||
@@ -291,22 +292,25 @@ class ControlFlowNode extends @py_flow_node {
|
||||
|
||||
/** Gets a successor for this node if the relevant condition is True. */
|
||||
ControlFlowNode getATrueSuccessor() {
|
||||
result = this.getASuccessor() and
|
||||
py_true_successors(this, result)
|
||||
}
|
||||
|
||||
/** Gets a successor for this node if the relevant condition is False. */
|
||||
ControlFlowNode getAFalseSuccessor() {
|
||||
result = this.getASuccessor() and
|
||||
py_false_successors(this, result)
|
||||
}
|
||||
|
||||
/** Gets a successor for this node if an exception is raised. */
|
||||
ControlFlowNode getAnExceptionalSuccessor() {
|
||||
result = this.getASuccessor() and
|
||||
py_exception_successors(this, result)
|
||||
}
|
||||
|
||||
/** Gets a successor for this node if no exception is raised. */
|
||||
ControlFlowNode getANormalSuccessor() {
|
||||
py_successors(this, result) and not
|
||||
result = this.getASuccessor() and not
|
||||
py_exception_successors(this, result)
|
||||
}
|
||||
|
||||
@@ -942,21 +946,91 @@ predicate defined_by(NameNode def, Variable v) {
|
||||
exists(NameNode p | defined_by(p, v) and p.getASuccessor() = def and not p.defines(v))
|
||||
}
|
||||
|
||||
/** A basic block (ignoring exceptional flow edges to scope exit) */
|
||||
class BasicBlock extends @py_flow_node {
|
||||
/* Combine extractor-generated basic block after pruning */
|
||||
|
||||
BasicBlock() {
|
||||
py_flow_bb_node(_, _, this, _)
|
||||
private class BasicBlockPart extends @py_flow_node {
|
||||
|
||||
string toString() { result = "Basic block part" }
|
||||
|
||||
BasicBlockPart() {
|
||||
py_flow_bb_node(_, _, this, _) and
|
||||
Pruner::reachable(this)
|
||||
}
|
||||
|
||||
predicate isHead() {
|
||||
count(this.(ControlFlowNode).getAPredecessor()) != 1
|
||||
or
|
||||
exists(ControlFlowNode pred | pred = this.(ControlFlowNode).getAPredecessor() | strictcount(pred.getASuccessor()) > 1)
|
||||
}
|
||||
|
||||
private BasicBlockPart previous() {
|
||||
not this.isHead() and
|
||||
py_flow_bb_node(this.(ControlFlowNode).getAPredecessor(), _, result, _)
|
||||
}
|
||||
|
||||
BasicBlockPart getHead() {
|
||||
this.isHead() and result = this
|
||||
or
|
||||
result = this.previous().getHead()
|
||||
}
|
||||
|
||||
predicate isLast() {
|
||||
not exists(BasicBlockPart part | part.previous() = this)
|
||||
}
|
||||
|
||||
int length() {
|
||||
result = max(int j | py_flow_bb_node(_, _, this, j)) + 1
|
||||
}
|
||||
|
||||
int startIndex() {
|
||||
this.isHead() and result = 0
|
||||
or
|
||||
exists(BasicBlockPart prev |
|
||||
prev = this.previous() and
|
||||
result = prev.startIndex() + prev.length()
|
||||
)
|
||||
}
|
||||
|
||||
/** Whether this basic block contains the specified node */
|
||||
predicate contains(ControlFlowNode node) {
|
||||
py_flow_bb_node(node, _, this, _)
|
||||
}
|
||||
|
||||
int indexOf(ControlFlowNode node) {
|
||||
py_flow_bb_node(node, _, this, result)
|
||||
}
|
||||
|
||||
ControlFlowNode lastNode() {
|
||||
this.indexOf(result) = max(this.indexOf(_))
|
||||
}
|
||||
|
||||
BasicBlockPart getImmediateDominator() {
|
||||
result.contains(this.(ControlFlowNode).getImmediateDominator())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A basic block (ignoring exceptional flow edges to scope exit) */
|
||||
class BasicBlock extends @py_flow_node {
|
||||
|
||||
BasicBlock() {
|
||||
this.(BasicBlockPart).isHead()
|
||||
}
|
||||
|
||||
private BasicBlockPart getAPart() {
|
||||
result.getHead() = this
|
||||
}
|
||||
|
||||
/** Whether this basic block contains the specified node */
|
||||
predicate contains(ControlFlowNode node) {
|
||||
this.getAPart().contains(node)
|
||||
}
|
||||
|
||||
/** Gets the nth node in this basic block */
|
||||
ControlFlowNode getNode(int n) {
|
||||
py_flow_bb_node(result, _, this, n)
|
||||
exists(BasicBlockPart part |
|
||||
part = this.getAPart() and
|
||||
n = part.startIndex() + part.indexOf(result)
|
||||
)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
@@ -976,7 +1050,7 @@ class BasicBlock extends @py_flow_node {
|
||||
}
|
||||
|
||||
BasicBlock getImmediateDominator() {
|
||||
this.firstNode().getImmediateDominator().getBasicBlock() = result
|
||||
this.getAPart().getImmediateDominator() = result.getAPart()
|
||||
}
|
||||
|
||||
/** Dominance frontier of a node x is the set of all nodes `other` such that `this` dominates a predecessor
|
||||
@@ -991,9 +1065,10 @@ class BasicBlock extends @py_flow_node {
|
||||
|
||||
/** Gets the last node in this basic block */
|
||||
ControlFlowNode getLastNode() {
|
||||
exists(int i |
|
||||
this.getNode(i) = result and
|
||||
i = max(int j | py_flow_bb_node(_, _, this, j))
|
||||
exists(BasicBlockPart part |
|
||||
part = this.getAPart() and
|
||||
part.isLast() and
|
||||
result = part.lastNode()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -396,16 +396,25 @@ module Pruner {
|
||||
)
|
||||
}
|
||||
|
||||
predicate reachable(UnprunedCfgNode n) {
|
||||
exists(UnprunedBasicBlock bb |
|
||||
reachableBB(bb) and bb.contains(n)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the basic block `bb` is unreachable due to
|
||||
* one or more constraints.
|
||||
*/
|
||||
predicate unreachableBB(UnprunedBasicBlock bb) {
|
||||
not bb.isEntry() and
|
||||
forall(UnprunedBasicBlock pred |
|
||||
pred.getASuccessor() = bb
|
||||
|
|
||||
unreachableEdge(pred, bb)
|
||||
)
|
||||
not reachableBB(bb)
|
||||
}
|
||||
|
||||
/** Holds if the basic block `bb` is reachable despite
|
||||
* constraints
|
||||
*/
|
||||
predicate reachableBB(UnprunedBasicBlock bb) {
|
||||
bb.isEntry() or
|
||||
reachableEdge(_, bb)
|
||||
}
|
||||
|
||||
Constraint constraintFromTest(SsaVariable var, UnprunedCfgNode node) {
|
||||
@@ -533,18 +542,30 @@ module Pruner {
|
||||
}
|
||||
|
||||
/** Holds if the edge `pred` -> `succ` should be pruned as it cannot be reached */
|
||||
predicate unreachableEdge(UnprunedBasicBlock pred, UnprunedBasicBlock succ) {
|
||||
predicate unreachableEdge(UnprunedCfgNode pred, UnprunedCfgNode succ) {
|
||||
exists(UnprunedBasicBlock predBB, UnprunedBasicBlock succBB |
|
||||
succBB = predBB.getASuccessor() and
|
||||
not reachableEdge(predBB, succBB) and
|
||||
pred = predBB.last() and succ = succBB.first()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the edge `pred` -> `succ` is reachable as a result of
|
||||
* `pred` being reachable and this edge not being pruned. */
|
||||
predicate reachableEdge(UnprunedBasicBlock pred, UnprunedBasicBlock succ) {
|
||||
reachableBB(pred) and succ = pred.getASuccessor() and
|
||||
not contradictoryEdge(pred, succ) and
|
||||
not simplyDead(pred, succ)
|
||||
}
|
||||
|
||||
predicate contradictoryEdge(UnprunedBasicBlock pred, UnprunedBasicBlock succ) {
|
||||
exists(Constraint pre, Constraint cond |
|
||||
controllingConditions(pred, succ, pre, cond) and
|
||||
contradicts(pre, cond)
|
||||
)
|
||||
or
|
||||
unreachableBB(pred) and succ = pred.getASuccessor()
|
||||
or
|
||||
simply_dead(pred, succ)
|
||||
}
|
||||
|
||||
/* Helper for `unreachableEdge`, deal with inequalities here to avoid blow up */
|
||||
/* Helper for `contradictoryEdge`, deal with inequalities here to avoid blow up */
|
||||
pragma [inline]
|
||||
private predicate contradicts(Constraint a, Constraint b) {
|
||||
a = TIsNone(true) and b.cannotBeNone()
|
||||
@@ -567,13 +588,13 @@ module Pruner {
|
||||
}
|
||||
|
||||
/** Holds if edge is simply dead. Stuff like `if False: ...` */
|
||||
predicate simply_dead(UnprunedBasicBlock pred, UnprunedBasicBlock succ) {
|
||||
predicate simplyDead(UnprunedBasicBlock pred, UnprunedBasicBlock succ) {
|
||||
constTest(pred.last()) = true and pred.getAFalseSuccessor() = succ
|
||||
or
|
||||
constTest(pred.last()) = false and pred.getATrueSuccessor() = succ
|
||||
}
|
||||
|
||||
/* Helper for simply_dead */
|
||||
/* Helper for simplyDead */
|
||||
private boolean constTest(UnprunedCfgNode node) {
|
||||
exists(ImmutableLiteral lit |
|
||||
result = lit.booleanValue() and lit = node.getNode()
|
||||
|
||||
Reference in New Issue
Block a user