Shared: Add a shared Guards library inspired by the Java and C# versions.

This commit is contained in:
Anders Schack-Mulligen
2025-05-02 14:44:28 +02:00
parent 994c1f6427
commit 1d75008eba

View File

@@ -0,0 +1,792 @@
/**
* Provides classes and predicates for determining "guard-controls"
* relationships.
*
* In their most general form, these relate a guard expression, a value, and a
* basic block, and state that execution of the basic block implies that
* control flow must have passed through the guard in order to reach the basic
* block, and when it did, the guard evaluated to the given value.
*
* For example, in `if (x == 0) { A }`, the guard `x == 0` evaluating to `true`
* controls the basic block `A`, in this case because the true branch dominates
* `A`, but more elaborate controls-relationships may also hold.
* For example, in
* ```
* int sz = a != null ? a.length : 0;
* if (sz != 0) {
* // this block is controlled by:
* // sz != 0 evaluating to true
* // sz evaluating to not 0
* // a.length evaluating to not 0
* // a != null evaluating to true
* // a evaluating to not null
* }
* ```
*
* The implementation is nested in two parameterized modules intended to
* facilitate multiple instantiations of the nested module with different
* precision levels
*/
private import codeql.util.Boolean
private import codeql.util.Location
signature module InputSig<LocationSig Location> {
class SuccessorType {
/** Gets a textual representation of this successor type. */
string toString();
}
class ConditionalSuccessor extends SuccessorType {
/** Gets the Boolean value of this successor. */
boolean getValue();
}
class BooleanSuccessor extends ConditionalSuccessor;
class NullnessSuccessor extends ConditionalSuccessor;
/** A control flow node. */
class ControlFlowNode {
/** Gets a textual representation of this control flow node. */
string toString();
/** Gets the location of this control flow node. */
Location getLocation();
}
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*/
class BasicBlock {
/** Gets a textual representation of this basic block. */
string toString();
/** Gets the `i`th node in this basic block. */
ControlFlowNode getNode(int i);
/** Gets the last control flow node in this basic block. */
ControlFlowNode getLastNode();
/** Gets the length of this basic block. */
int length();
/** Gets the location of this basic block. */
Location getLocation();
BasicBlock getASuccessor(SuccessorType t);
predicate dominates(BasicBlock bb);
predicate strictlyDominates(BasicBlock bb);
}
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2);
class Expr {
/** Gets a textual representation of this expression. */
string toString();
/** Gets the location of this expression. */
Location getLocation();
/** Gets the associated control flow node. */
ControlFlowNode getControlFlowNode();
/** Gets the basic block containing this expression. */
BasicBlock getBasicBlock();
}
class ConstantValue {
/** Gets a textual representation of this constant value. */
string toString();
}
class ConstantExpr extends Expr {
predicate isNull();
boolean asBooleanValue();
int asIntegerValue();
ConstantValue asConstantValue();
}
class NonNullExpr extends Expr;
class Case {
/** Gets a textual representation of this switch case. */
string toString();
Location getLocation();
Expr getSwitchExpr();
predicate isDefaultCase();
ConstantExpr asConstantCase();
predicate matchEdge(BasicBlock bb1, BasicBlock bb2);
predicate nonMatchEdge(BasicBlock bb1, BasicBlock bb2);
}
class AndExpr extends Expr {
/** Gets an operand of this expression. */
Expr getAnOperand();
}
class OrExpr extends Expr {
/** Gets an operand of this expression. */
Expr getAnOperand();
}
class NotExpr extends Expr {
/** Gets the operand of this expression. */
Expr getOperand();
}
/**
* An expression that has the same value as a specific sub-expression.
*
* For example, in Java, the assignment `x = rhs` has the same value as `rhs`.
*/
class IdExpr extends Expr {
Expr getEqualChildExpr();
}
class EqualityTest extends Expr {
/** Gets an operand of this expression. */
Expr getAnOperand();
/** Gets a boolean indicating whether this test is equality (true) or inequality (false). */
boolean polarity();
}
class ConditionalExpr extends Expr {
/** Gets the condition of this expression. */
Expr getCondition();
/** Gets the true branch of this expression. */
Expr getThen();
/** Gets the false branch of this expression. */
Expr getElse();
}
}
module Make<LocationSig Location, InputSig<Location> Input> {
private import Input
private newtype TAbstractSingleValue =
TValueNull() or
TValueTrue() or
TValueInt(int i) { exists(ConstantExpr c | c.asIntegerValue() = i) or i = 0 } or
TValueConstant(ConstantValue c) { exists(ConstantExpr ce | ce.asConstantValue() = c) }
private newtype TGuardValue =
TValue(TAbstractSingleValue val, Boolean isVal) or
TCaseMatch(Case c, Boolean match)
private class AbstractSingleValue extends TAbstractSingleValue {
/** Gets a textual representation of this value. */
string toString() {
result = "null" and this instanceof TValueNull
or
result = "true" and this instanceof TValueTrue
or
exists(int i | result = i.toString() and this = TValueInt(i))
or
exists(ConstantValue c | result = c.toString() and this = TValueConstant(c))
}
}
/** An abstract value that a `Guard` may evaluate to. */
class GuardValue extends TGuardValue {
/**
* Gets the dual value. Examples of dual values include:
* - null vs. not null
* - true vs. false
* - evaluating to a specific value vs. evaluating to any other value
* - matching a specific case vs. not matching that case
*/
GuardValue getDualValue() {
exists(AbstractSingleValue val, boolean isVal |
this = TValue(val, isVal) and
result = TValue(val, isVal.booleanNot())
)
or
exists(Case c, boolean match |
this = TCaseMatch(c, match) and
result = TCaseMatch(c, match.booleanNot())
)
}
/** Holds if this value represents `null`. */
predicate isNullValue() { this = TValue(TValueNull(), true) }
/** Gets the integer that this value represents, if any. */
int asIntValue() { this = TValue(TValueInt(result), true) }
/** Gets the boolean that this value represents, if any. */
boolean asBooleanValue() { this = TValue(TValueTrue(), result) }
/** Gets the constant that this value represents, if any. */
ConstantValue asConstantValue() { this = TValue(TValueConstant(result), true) }
/** Gets a textual representation of this value. */
string toString() {
result = this.asBooleanValue().toString()
or
exists(AbstractSingleValue val | not val instanceof TValueTrue |
this = TValue(val, true) and result = val.toString()
or
this = TValue(val, false) and result = "not " + val.toString()
)
or
exists(Case c, boolean match, string s | this = TCaseMatch(c, match) |
(
exists(ConstantExpr ce | c.asConstantCase() = ce and s = ce.toString())
or
not exists(c.asConstantCase()) and s = c.toString()
) and
(
match = true and result = "match " + s
or
match = false and result = "non-match " + s
)
)
}
}
bindingset[a, b]
pragma[inline_late]
private predicate disjointValues(GuardValue a, GuardValue b) {
a = b.getDualValue()
or
exists(AbstractSingleValue a1, AbstractSingleValue b1 |
a = TValue(a1, true) and
b = TValue(b1, true) and
a1 != b1
)
}
private predicate constantHasValue(ConstantExpr c, GuardValue v) {
c.isNull() and v = TValue(TValueNull(), true)
or
v = TValue(TValueTrue(), c.asBooleanValue())
or
v = TValue(TValueInt(c.asIntegerValue()), true)
or
v = TValue(TValueConstant(c.asConstantValue()), true)
}
private predicate exprHasValue(Expr e, GuardValue v) {
constantHasValue(e, v)
or
e instanceof NonNullExpr and v = TValue(TValueNull(), false)
}
private predicate branchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue v) {
exists(ConditionalSuccessor s |
bb1.getASuccessor(s) = bb2 and
exists(AbstractSingleValue val |
s instanceof NullnessSuccessor and val = TValueNull()
or
s instanceof BooleanSuccessor and val = TValueTrue()
|
v = TValue(val, s.getValue())
)
)
or
exists(Case c |
v = TCaseMatch(c, true) and
c.matchEdge(bb1, bb2)
or
v = TCaseMatch(c, false) and
c.nonMatchEdge(bb1, bb2)
)
}
pragma[nomagic]
private predicate eqtestHasOperands(EqualityTest eqtest, Expr e1, Expr e2, boolean polarity) {
eqtest.getAnOperand() = e1 and
eqtest.getAnOperand() = e2 and
e1 != e2 and
eqtest.polarity() = polarity
}
private predicate caseGuard(PreGuard g, Case c, Expr switchExpr) {
g.hasValueBranchEdge(_, _, TCaseMatch(c, _)) and
switchExpr = c.getSwitchExpr()
}
private predicate constcaseEquality(PreGuard g, Expr e1, ConstantExpr e2, GuardValue eqval) {
exists(Case c |
caseGuard(g, c, e1) and
c.asConstantCase() = e2 and
eqval = TCaseMatch(c, true)
)
}
final private class ExprFinal = Expr;
/**
* A guard. This may be any expression whose value determines subsequent
* control flow.
*/
final class PreGuard extends ExprFinal {
/**
* Holds if this guard is the last node in `bb1` and that its successor is
* `bb2` exactly when evaluating to `v`.
*/
predicate hasValueBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue v) {
bb1.getLastNode() = this.getControlFlowNode() and
branchEdge(bb1, bb2, v)
}
/**
* Holds if this guard evaluating to `v` directly controls the basic block `bb`.
*
* That is, `bb` is dominated by the `v`-successor edge of this guard.
*/
predicate directlyValueControls(BasicBlock bb, GuardValue v) {
exists(BasicBlock guard, BasicBlock succ |
this.hasValueBranchEdge(guard, succ, v) and
dominatingEdge(guard, succ) and
succ.dominates(bb)
)
}
/**
* Holds if this guard is the last node in `bb1` and that its successor is
* `bb2` exactly when evaluating to `branch`.
*/
predicate hasBranchEdge(BasicBlock bb1, BasicBlock bb2, boolean branch) {
this.hasValueBranchEdge(bb1, bb2, TValue(TValueTrue(), branch))
}
/**
* Holds if this guard evaluating to `branch` directly controls the basic
* block `bb`.
*
* That is, `bb` is dominated by the `branch`-successor edge of this guard.
*/
predicate directlyControls(BasicBlock bb, boolean branch) {
this.directlyValueControls(bb, TValue(TValueTrue(), branch))
}
/**
* Holds if this guard tests equality between `e1` and `e2` upon evaluating
* to `eqval`.
*/
predicate isEquality(Expr e1, Expr e2, GuardValue eqval) {
eqtestHasOperands(this, e1, e2, eqval.asBooleanValue())
or
constcaseEquality(this, e1, e2, eqval)
or
constcaseEquality(this, e2, e1, eqval)
}
}
private Expr getBranchExpr(ConditionalExpr cond, boolean branch) {
branch = true and result = cond.getThen()
or
branch = false and result = cond.getElse()
}
bindingset[g1, v1]
pragma[inline_late]
private predicate unboundBaseImpliesStep(PreGuard g1, GuardValue v1, PreGuard g2, GuardValue v2) {
g1.(IdExpr).getEqualChildExpr() = g2 and v1 = v2
or
exists(ConditionalExpr cond, boolean branch, Expr e, GuardValue ev |
cond = g1 and
e = getBranchExpr(cond, branch) and
exprHasValue(e, ev) and
disjointValues(v1, ev)
|
// g1 === g2 ? e : ...;
// g1 === g2 ? ... : e;
g2 = cond.getCondition() and
v2 = TValue(TValueTrue(), branch.booleanNot())
or
// g1 === ... ? g2 : e
// g1 === ... ? e : g2
g2 = getBranchExpr(cond, branch.booleanNot()) and
v2 = v1 and
not exprHasValue(g2, v2) // disregard trivial guard
)
}
private predicate baseImpliesStep(PreGuard g1, GuardValue v1, PreGuard g2, GuardValue v2) {
g1.(AndExpr).getAnOperand() = g2 and v1 = TValue(TValueTrue(), true) and v2 = v1
or
g1.(OrExpr).getAnOperand() = g2 and v1 = TValue(TValueTrue(), false) and v2 = v1
or
g1.(NotExpr).getOperand() = g2 and v1.asBooleanValue().booleanNot() = v2.asBooleanValue()
or
exists(GuardValue eqval, ConstantExpr constant, GuardValue cv |
g1.isEquality(g2, constant, eqval) and
constantHasValue(constant, cv)
|
v1 = eqval and v2 = cv
or
v1 = eqval.getDualValue() and v2 = cv.getDualValue()
)
or
exists(NonNullExpr nonnull |
eqtestHasOperands(g1, g2, nonnull, v1.asBooleanValue()) and
v2 = TValue(TValueNull(), false)
)
or
exists(Case c1, Case c2, Expr switchExpr |
caseGuard(g1, c1, switchExpr) and
v1 = TCaseMatch(c1, true) and
c1.isDefaultCase() and
caseGuard(g2, c2, switchExpr) and
v2 = TCaseMatch(c2, false) and
c1 != c2
)
}
signature module LogicInputSig {
class SsaDefinition {
/** Gets the basic block to which this SSA definition belongs. */
BasicBlock getBasicBlock();
Expr getARead();
}
class SsaWriteDefinition extends SsaDefinition {
Expr getDefinition();
}
class SsaPhiNode extends SsaDefinition {
/** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */
predicate hasInputFromBlock(SsaDefinition inp, BasicBlock bb);
}
/**
* Holds if `guard` evaluating to `val` ensures that:
* `e <= k` when `upper = true`
* `e >= k` when `upper = false`
*/
default predicate rangeGuard(PreGuard guard, GuardValue val, Expr e, int k, boolean upper) {
none()
}
/**
* Holds if `guard` evaluating to `val` ensures that:
* `e == null` when `isNull = true`
* `e != null` when `isNull = false`
*/
default predicate additionalNullCheck(PreGuard guard, GuardValue val, Expr e, boolean isNull) {
none()
}
}
module Logic<LogicInputSig LogicInput> {
private import LogicInput
private predicate guardControlsPhiBranch(
Guard guard, GuardValue v, SsaPhiNode phi, Expr input, BasicBlock bbInput, BasicBlock bbPhi
) {
exists(SsaWriteDefinition inp |
phi.hasInputFromBlock(inp, bbInput) and
phi.getBasicBlock() = bbPhi and
inp.getDefinition() = input and
guard.directlyValueControls(bbInput, v) and
guard.getBasicBlock().strictlyDominates(bbPhi) and
not guard.directlyValueControls(bbPhi, _)
)
}
/**
* Holds if `phi` takes `input` exactly when `guard` is `v`. That is,
* `guard == v` directly controls `input` and `guard == v.getDualValue()`
* directly controls all other inputs to `phi`.
*
* This makes `phi` similar to the conditional `phi = guard==v ? input : ...`.
*/
private predicate guardDeterminesPhiInput(Guard guard, GuardValue v, SsaPhiNode phi, Expr input) {
exists(GuardValue dv, BasicBlock bbInput, BasicBlock bbPhi |
guardControlsPhiBranch(guard, v, phi, input, bbInput, bbPhi) and
dv = v.getDualValue() and
forall(BasicBlock other | phi.hasInputFromBlock(_, other) and other != bbInput |
guard.directlyValueControls(other, dv)
or
guard.hasValueBranchEdge(other, bbPhi, dv)
)
)
}
pragma[nomagic]
private predicate guardChecksEqualVars(
Guard guard, SsaDefinition v1, SsaDefinition v2, boolean branch
) {
eqtestHasOperands(guard, v1.getARead(), v2.getARead(), branch)
}
private predicate guardReadsSsaVar(Guard guard, SsaDefinition def) {
def.getARead() = guard
or
exists(Guard eqtest, SsaDefinition other, boolean branch |
guardChecksEqualVars(eqtest, def, other, branch) and
other.getARead() = guard and
eqtest.directlyControls(guard.getBasicBlock(), branch)
)
}
private predicate valueStep(Expr e1, Expr e2) {
e2.(ConditionalExpr).getThen() = e1 or
e2.(ConditionalExpr).getElse() = e1 or
e2.(IdExpr).getEqualChildExpr() = e1
}
/**
* Gets a sub-expression of `e` whose value can flow to `e` through
* `valueStep`s
*/
private Expr possibleValue(Expr e) {
result = possibleValue(any(Expr e1 | valueStep(e1, e)))
or
result = e and not valueStep(_, e)
}
/**
* Gets an ultimate definition of `v` that is not itself a phi node. The
* boolean `fromBackEdge` indicates whether the flow from `result` to `v` goes
* through a back edge.
*/
private SsaDefinition getADefinition(SsaDefinition v, boolean fromBackEdge) {
result = v and not v instanceof SsaPhiNode and fromBackEdge = false
or
exists(SsaDefinition inp, BasicBlock bb, boolean fbe |
v.(SsaPhiNode).hasInputFromBlock(inp, bb) and
result = getADefinition(inp, fbe) and
(if v.getBasicBlock().dominates(bb) then fromBackEdge = true else fromBackEdge = fbe)
)
}
/**
* Holds if `v` can have a value that is not representable as an `GuardValue`.
*/
private predicate hasPossibleUnknownValue(SsaDefinition v) {
exists(SsaDefinition def | def = getADefinition(v, _) |
not exists(def.(SsaWriteDefinition).getDefinition())
or
exists(Expr e | e = possibleValue(def.(SsaWriteDefinition).getDefinition()) |
not constantHasValue(e, _)
)
)
}
/**
* Holds if `e` equals `k` and may be assigned to `v`. The boolean
* `fromBackEdge` indicates whether the flow from `e` to `v` goes through a
* back edge.
*/
private predicate possibleValue(SsaDefinition v, boolean fromBackEdge, Expr e, GuardValue k) {
not hasPossibleUnknownValue(v) and
exists(SsaWriteDefinition def |
def = getADefinition(v, fromBackEdge) and
e = possibleValue(def.getDefinition()) and
constantHasValue(e, k)
)
}
/**
* Holds if `e` equals `k` and may be assigned to `v` without going through
* back edges, and all other possible ultimate definitions of `v` are different
* from `k`. The trivial case where `v` is an `SsaWriteDefinition` with `e` as
* the only possible value is excluded.
*/
private predicate uniqueValue(SsaDefinition v, Expr e, GuardValue k) {
possibleValue(v, false, e, k) and
forex(Expr other, GuardValue otherval | possibleValue(v, _, other, otherval) and other != e |
disjointValues(otherval, k)
)
}
/**
* Holds if `phi` has exactly two inputs, `def1` and `e2`, and that `def1`
* does not come from a back-edge into `phi`.
*/
private predicate phiWithTwoInputs(SsaPhiNode phi, SsaDefinition def1, Expr e2) {
exists(SsaWriteDefinition def2, BasicBlock bb1 |
2 = strictcount(SsaDefinition inp, BasicBlock bb | phi.hasInputFromBlock(inp, bb)) and
phi.hasInputFromBlock(def1, bb1) and
phi.hasInputFromBlock(def2, _) and
def1 != def2 and
not phi.getBasicBlock().dominates(bb1) and
def2.getDefinition() = e2
)
}
/** Holds if `e` may take the value `k` */
private predicate relevantInt(Expr e, int k) {
e.(ConstantExpr).asIntegerValue() = k
or
relevantInt(any(Expr e1 | valueStep(e1, e)), k)
or
exists(SsaDefinition def |
guardReadsSsaVar(e, def) and
relevantInt(getADefinition(def, _).(SsaWriteDefinition).getDefinition(), k)
)
}
private predicate impliesStep(Guard g1, GuardValue v1, Guard g2, GuardValue v2) {
baseImpliesStep(g1, v1, g2, v2)
or
exists(SsaDefinition def, Expr e |
// If `def = g2 ? v1 : ...` and all other assignments to `def` are different from
// `v1` then a guard proving `def == v1` ensures that `g2` evaluates to `v2`.
uniqueValue(def, e, v1) and
guardReadsSsaVar(g1, def) and
g2.directlyValueControls(e.getBasicBlock(), v2) and
not g2.directlyValueControls(g1.getBasicBlock(), v2)
)
or
exists(int k1, int k2, boolean upper |
rangeGuard(g1, v1, g2, k1, upper) and
relevantInt(g2, k2) and
v2 = TValue(TValueInt(k2), false)
|
upper = true and k1 < k2 // g2 <= k1 < k2 ==> g2 != k2
or
upper = false and k1 > k2 // g2 >= k1 > k2 ==> g2 != k2
)
or
exists(boolean isNull |
additionalNullCheck(g1, v1, g2, isNull) and
v2 = TValue(TValueNull(), isNull)
)
}
bindingset[g1, v1]
pragma[inline_late]
private predicate unboundImpliesStep(Guard g1, GuardValue v1, Guard g2, GuardValue v2) {
unboundBaseImpliesStep(g1, v1, g2, v2)
}
bindingset[def1, v1]
pragma[inline_late]
private predicate impliesStepSsaGuard(SsaDefinition def1, GuardValue v1, Guard g2, GuardValue v2) {
def1.(SsaWriteDefinition).getDefinition() = g2 and
v1 = v2 and
not exprHasValue(g2, v2) // disregard trivial guard
or
exists(Expr e, GuardValue ev |
guardDeterminesPhiInput(g2, v2.getDualValue(), def1, e) and
exprHasValue(e, ev) and
disjointValues(v1, ev)
)
}
bindingset[def1, v]
pragma[inline_late]
private predicate impliesStepSsa(SsaDefinition def1, GuardValue v, SsaDefinition def2) {
exists(Expr e, GuardValue ev |
phiWithTwoInputs(def1, def2, e) and
exprHasValue(e, ev) and
disjointValues(v, ev)
)
}
pragma[nomagic]
private predicate guardControlsBranchEdge(
Guard guard, BasicBlock bb1, BasicBlock bb2, GuardValue v
) {
guard.hasValueBranchEdge(bb1, bb2, v)
or
exists(Guard g0, GuardValue v0 |
guardControlsBranchEdge(g0, bb1, bb2, v0) and
impliesStep(g0, v0, guard, v)
)
or
exists(Guard g0, GuardValue v0 |
guardControlsBranchEdge(g0, bb1, bb2, v0) and
unboundImpliesStep(g0, v0, guard, v)
)
or
exists(SsaDefinition def0, GuardValue v0 |
ssaControlsBranchEdge(def0, bb1, bb2, v0) and
impliesStepSsaGuard(def0, v0, guard, v)
)
}
pragma[nomagic]
private predicate ssaControlsBranchEdge(
SsaDefinition def, BasicBlock bb1, BasicBlock bb2, GuardValue v
) {
exists(Guard g0 |
guardControlsBranchEdge(g0, bb1, bb2, v) and
guardReadsSsaVar(g0, def)
)
or
exists(SsaDefinition def0 |
ssaControlsBranchEdge(def0, bb1, bb2, v) and
impliesStepSsa(def0, v, def)
)
}
/**
* A guard. This may be any expression whose value determines subsequent
* control flow.
*/
final class Guard extends PreGuard {
/**
* Holds if this guard evaluating to `v` controls the control-flow branch
* edge from `bb1` to `bb2`. That is, following the edge from `bb1` to
* `bb2` implies that this guard evaluated to `v`.
*
* Note that this goes beyond mere control-flow graph dominance, as it
* also considers additional logical reasoning.
*/
predicate valueControlsBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue v) {
guardControlsBranchEdge(this, bb1, bb2, v)
}
/**
* Holds if this guard evaluating to `v` controls the basic block `bb`.
* That is, execution of `bb` implies that this guard evaluated to `v`.
*
* Note that this goes beyond mere control-flow graph dominance, as it
* also considers additional logical reasoning.
*/
predicate valueControls(BasicBlock bb, GuardValue v) {
exists(BasicBlock guard, BasicBlock succ |
this.valueControlsBranchEdge(guard, succ, v) and
dominatingEdge(guard, succ) and
succ.dominates(bb)
)
}
/**
* Holds if this guard evaluating to `branch` controls the control-flow
* branch edge from `bb1` to `bb2`. That is, following the edge from
* `bb1` to `bb2` implies that this guard evaluated to `branch`.
*
* Note that this goes beyond mere control-flow graph dominance, as it
* also considers additional logical reasoning.
*/
predicate controlsBranchEdge(BasicBlock bb1, BasicBlock bb2, boolean branch) {
this.valueControlsBranchEdge(bb1, bb2, TValue(TValueTrue(), branch))
}
/**
* Holds if this guard evaluating to `branch` controls the basic block
* `bb`. That is, execution of `bb` implies that this guard evaluated to
* `branch`.
*
* Note that this goes beyond mere control-flow graph dominance, as it
* also considers additional logical reasoning.
*/
predicate controls(BasicBlock bb, boolean branch) {
this.valueControls(bb, TValue(TValueTrue(), branch))
}
}
}
}