Skip to content

Commit db6a807

Browse files
committed
C++: Move same-stage predicates into cached module
This change only moves code around -- there are no changes to predicate bodies or signatures. The predicates that go in `ConstantExprs.Cached` after this change were already cached in the same stage or, in the case of the `aborting*` predicates, did not need to be cached. This is a fortunate consequence of how the mutual recursion between the predicates happens to work, and it's not going to be the case after the next commit.
1 parent 0096024 commit db6a807

File tree

2 files changed

+118
-104
lines changed

2 files changed

+118
-104
lines changed

cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll

Lines changed: 1 addition & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -75,85 +75,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
7575
}
7676
}
7777

78-
import Cached
79-
private cached module Cached {
80-
// The base case of `reachable` is factored out for performance. If it's
81-
// written in-line in `reachable`, the compiler inserts a `n instanceof
82-
// ControlFlowNode` check because the `not ... and not ...` case doesn't
83-
// otherwise bind `n`. The problem is that this check is inserted at the
84-
// outermost level of this predicate, so it covers all cases including the
85-
// recursive case. The optimizer doesn't eliminate the check even though it's
86-
// redundant, and having the check leads to needless extra computation and a
87-
// risk of bad join orders.
88-
private predicate reachableBaseCase(ControlFlowNode n) {
89-
exists(Function f | f.getEntryPoint() = n)
90-
or
91-
// Okay to use successors_extended directly here
92-
(not successors_extended(_,n) and not successors_extended(n,_))
93-
or
94-
n instanceof Handler
95-
}
96-
97-
/**
98-
* Holds if the control-flow node `n` is reachable, meaning that either
99-
* it is an entry point, or there exists a path in the control-flow
100-
* graph of its function that connects an entry point to it.
101-
* Compile-time constant conditions are taken into account, so that
102-
* the call to `f` is not reachable in `if (0) f();` even if the
103-
* `if` statement as a whole is reachable.
104-
*/
105-
cached
106-
predicate reachable(ControlFlowNode n)
107-
{
108-
reachableBaseCase(n)
109-
or
110-
reachable(n.getAPredecessor())
111-
}
112-
113-
/** Holds if `condition` always evaluates to a nonzero value. */
114-
cached
115-
predicate conditionAlwaysTrue(Expr condition) {
116-
conditionAlways(condition, true)
117-
}
118-
119-
/** Holds if `condition` always evaluates to zero. */
120-
cached
121-
predicate conditionAlwaysFalse(Expr condition) {
122-
conditionAlways(condition, false)
123-
or
124-
// If a loop condition evaluates to false upon entry, it will always
125-
// be false
126-
loopConditionAlwaysUponEntry(_, condition, false)
127-
}
128-
129-
/**
130-
* The condition `condition` for the loop `loop` is provably `true` upon entry.
131-
* That is, at least one iteration of the loop is guaranteed.
132-
*/
133-
cached
134-
predicate loopConditionAlwaysTrueUponEntry(ControlFlowNode loop, Expr condition) {
135-
loopConditionAlwaysUponEntry(loop, condition, true)
136-
}
137-
}
138-
139-
private predicate conditionAlways(Expr condition, boolean b) {
140-
exists(ConditionEvaluator x, int val |
141-
val = x.getValue(condition) |
142-
val != 0 and b = true
143-
or
144-
val = 0 and b = false
145-
)
146-
}
147-
148-
private predicate loopConditionAlwaysUponEntry(ControlFlowNode loop, Expr condition, boolean b) {
149-
exists(LoopEntryConditionEvaluator x, int val |
150-
x.isLoopEntry(condition, loop) and
151-
val = x.getValue(condition) |
152-
val != 0 and b = true
153-
or
154-
val = 0 and b = false
155-
)
156-
}
78+
import ControlFlowGraphPublic
15779

15880
/**
15981
* An element that is convertible to `ControlFlowNode`. This class is similar

cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll

Lines changed: 117 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,126 @@
11
import cpp
22
private import PrimitiveBasicBlocks
33
private class Node = ControlFlowNodeBase;
4+
import Cached
5+
6+
cached
7+
private module Cached {
8+
/** A call to a function known not to return. */
9+
cached
10+
predicate aborting(FunctionCall c) {
11+
not potentiallyReturningFunctionCall(c)
12+
}
13+
14+
/**
15+
* Functions that are known not to return. This is normally because the function
16+
* exits the program or longjmps to another location.
17+
*/
18+
cached
19+
predicate abortingFunction(Function f) {
20+
not potentiallyReturningFunction(f)
21+
}
22+
23+
/**
24+
* An adapted version of the `successors_extended` relation that excludes
25+
* impossible control-flow edges - flow will never occur along these
26+
* edges, so it is safe (and indeed sensible) to remove them.
27+
*/
28+
cached predicate successors_adapted(Node pred, Node succ) {
29+
successors_extended(pred, succ)
30+
and possiblePredecessor(pred)
31+
and not impossibleFalseEdge(pred, succ)
32+
and not impossibleTrueEdge(pred, succ)
33+
and not impossibleSwitchEdge(pred, succ)
34+
and not impossibleDefaultSwitchEdge(pred, succ)
35+
and not impossibleFunctionReturn(pred, succ)
36+
and not getOptions().exprExits(pred)
37+
}
38+
39+
/**
40+
* Provides predicates that should be exported as if they were top-level
41+
* predicates in `ControlFlowGraph.qll`. They have to be defined in this file
42+
* in order to be grouped in a `cached module` with other predicates that
43+
* must go in the same cached stage.
44+
*/
45+
cached
46+
module ControlFlowGraphPublic {
47+
// The base case of `reachable` is factored out for performance. If it's
48+
// written in-line in `reachable`, the compiler inserts a `n instanceof
49+
// ControlFlowNode` check because the `not ... and not ...` case doesn't
50+
// otherwise bind `n`. The problem is that this check is inserted at the
51+
// outermost level of this predicate, so it covers all cases including the
52+
// recursive case. The optimizer doesn't eliminate the check even though it's
53+
// redundant, and having the check leads to needless extra computation and a
54+
// risk of bad join orders.
55+
private predicate reachableBaseCase(ControlFlowNode n) {
56+
exists(Function f | f.getEntryPoint() = n)
57+
or
58+
// Okay to use successors_extended directly here
59+
(not successors_extended(_,n) and not successors_extended(n,_))
60+
or
61+
n instanceof Handler
62+
}
63+
64+
/**
65+
* Holds if the control-flow node `n` is reachable, meaning that either
66+
* it is an entry point, or there exists a path in the control-flow
67+
* graph of its function that connects an entry point to it.
68+
* Compile-time constant conditions are taken into account, so that
69+
* the call to `f` is not reachable in `if (0) f();` even if the
70+
* `if` statement as a whole is reachable.
71+
*/
72+
cached
73+
predicate reachable(ControlFlowNode n)
74+
{
75+
reachableBaseCase(n)
76+
or
77+
reachable(n.getAPredecessor())
78+
}
79+
80+
/** Holds if `condition` always evaluates to a nonzero value. */
81+
cached
82+
predicate conditionAlwaysTrue(Expr condition) {
83+
conditionAlways(condition, true)
84+
}
85+
86+
/** Holds if `condition` always evaluates to zero. */
87+
cached
88+
predicate conditionAlwaysFalse(Expr condition) {
89+
conditionAlways(condition, false)
90+
or
91+
// If a loop condition evaluates to false upon entry, it will always
92+
// be false
93+
loopConditionAlwaysUponEntry(_, condition, false)
94+
}
95+
96+
/**
97+
* The condition `condition` for the loop `loop` is provably `true` upon entry.
98+
* That is, at least one iteration of the loop is guaranteed.
99+
*/
100+
cached
101+
predicate loopConditionAlwaysTrueUponEntry(ControlFlowNode loop, Expr condition) {
102+
loopConditionAlwaysUponEntry(loop, condition, true)
103+
}
104+
}
105+
}
4106

5-
/** A call to a function known not to return. */
6-
predicate aborting(FunctionCall c) {
7-
not potentiallyReturningFunctionCall(c)
107+
private predicate conditionAlways(Expr condition, boolean b) {
108+
exists(ConditionEvaluator x, int val |
109+
val = x.getValue(condition) |
110+
val != 0 and b = true
111+
or
112+
val = 0 and b = false
113+
)
8114
}
9115

10-
/**
11-
* Functions that are known not to return. This is normally because the function
12-
* exits the program or longjmps to another location.
13-
*/
14-
predicate abortingFunction(Function f) {
15-
not potentiallyReturningFunction(f)
116+
private predicate loopConditionAlwaysUponEntry(ControlFlowNode loop, Expr condition, boolean b) {
117+
exists(LoopEntryConditionEvaluator x, int val |
118+
x.isLoopEntry(condition, loop) and
119+
val = x.getValue(condition) |
120+
val != 0 and b = true
121+
or
122+
val = 0 and b = false
123+
)
16124
}
17125

18126
/**
@@ -201,22 +309,6 @@ private predicate possiblePredecessor(Node pred) {
201309
potentiallyReturningFunctionCall(pred)
202310
}
203311

204-
/**
205-
* An adapted version of the `successors_extended` relation that excludes
206-
* impossible control-flow edges - flow will never occur along these
207-
* edges, so it is safe (and indeed sensible) to remove them.
208-
*/
209-
cached predicate successors_adapted(Node pred, Node succ) {
210-
successors_extended(pred, succ)
211-
and possiblePredecessor(pred)
212-
and not impossibleFalseEdge(pred, succ)
213-
and not impossibleTrueEdge(pred, succ)
214-
and not impossibleSwitchEdge(pred, succ)
215-
and not impossibleDefaultSwitchEdge(pred, succ)
216-
and not impossibleFunctionReturn(pred, succ)
217-
and not getOptions().exprExits(pred)
218-
}
219-
220312
private predicate compileTimeConstantInt(Expr e, int val) {
221313
val = e.getFullyConverted().getValue().toInt() and
222314
not e instanceof StringLiteral and

0 commit comments

Comments
 (0)