|
1 | | -/** |
2 | | - * Provides classes for working with basic blocks, and predicates for computing |
3 | | - * liveness information for local variables. |
4 | | - */ |
5 | | - |
6 | | -import javascript |
7 | | -private import internal.StmtContainers |
8 | | -private import semmle.javascript.internal.CachedStages |
9 | | - |
10 | | -module BasicBlockInternal { |
11 | | - // TODO: Expose these as public predicate in a private module instead of this hack. |
12 | | - predicate getImmediateBasicBlockDominator = immediateDominator/1; |
13 | | -} |
14 | | - |
15 | | -/** |
16 | | - * Holds if `nd` starts a new basic block. |
17 | | - */ |
18 | | -private predicate startsBB(ControlFlowNode nd) { |
19 | | - not exists(nd.getAPredecessor()) and exists(nd.getASuccessor()) |
20 | | - or |
21 | | - nd.isJoin() |
22 | | - or |
23 | | - nd.getAPredecessor().isBranch() |
24 | | -} |
25 | | - |
26 | | -/** |
27 | | - * Holds if the first node of basic block `succ` is a control flow |
28 | | - * successor of the last node of basic block `bb`. |
29 | | - */ |
30 | | -private predicate succBB(BasicBlock bb, BasicBlock succ) { succ = bb.getLastNode().getASuccessor() } |
31 | | - |
32 | | -/** |
33 | | - * Holds if the first node of basic block `bb` is a control flow |
34 | | - * successor of the last node of basic block `pre`. |
35 | | - */ |
36 | | -private predicate predBB(BasicBlock bb, BasicBlock pre) { succBB(pre, bb) } |
37 | | - |
38 | | -/** Holds if `bb` is an entry basic block. */ |
39 | | -private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof ControlFlowEntryNode } |
40 | | - |
41 | | -/** Holds if `bb` is an exit basic block. */ |
42 | | -private predicate exitBB(BasicBlock bb) { bb.getLastNode() instanceof ControlFlowExitNode } |
43 | | - |
44 | | -cached |
45 | | -private module Internal { |
46 | | - /** |
47 | | - * Holds if `succ` is a control flow successor of `nd` within the same basic block. |
48 | | - */ |
49 | | - private predicate intraBBSucc(ControlFlowNode nd, ControlFlowNode succ) { |
50 | | - succ = nd.getASuccessor() and |
51 | | - not succ instanceof BasicBlock |
52 | | - } |
53 | | - |
54 | | - /** |
55 | | - * Holds if `nd` is the `i`th node in basic block `bb`. |
56 | | - * |
57 | | - * In other words, `i` is the shortest distance from a node `bb` |
58 | | - * that starts a basic block to `nd` along the `intraBBSucc` relation. |
59 | | - */ |
60 | | - cached |
61 | | - predicate bbIndex(BasicBlock bb, ControlFlowNode nd, int i) = |
62 | | - shortestDistances(startsBB/1, intraBBSucc/2)(bb, nd, i) |
63 | | - |
64 | | - cached |
65 | | - int bbLength(BasicBlock bb) { result = strictcount(ControlFlowNode nd | bbIndex(bb, nd, _)) } |
66 | | - |
67 | | - cached |
68 | | - predicate useAt(BasicBlock bb, int i, Variable v, VarUse u) { |
69 | | - Stages::BasicBlocks::ref() and |
70 | | - v = u.getVariable() and |
71 | | - bbIndex(bb, u, i) |
72 | | - } |
73 | | - |
74 | | - cached |
75 | | - predicate defAt(BasicBlock bb, int i, Variable v, VarDef d) { |
76 | | - exists(VarRef lhs | |
77 | | - lhs = d.getTarget().(BindingPattern).getABindingVarRef() and |
78 | | - v = lhs.getVariable() |
79 | | - | |
80 | | - lhs = d.getTarget() and |
81 | | - bbIndex(bb, d, i) |
82 | | - or |
83 | | - exists(PropertyPattern pp | |
84 | | - lhs = pp.getValuePattern() and |
85 | | - bbIndex(bb, pp, i) |
86 | | - ) |
87 | | - or |
88 | | - exists(ObjectPattern op | |
89 | | - lhs = op.getRest() and |
90 | | - bbIndex(bb, lhs, i) |
91 | | - ) |
92 | | - or |
93 | | - exists(ArrayPattern ap | |
94 | | - lhs = ap.getAnElement() and |
95 | | - bbIndex(bb, lhs, i) |
96 | | - ) |
97 | | - ) |
98 | | - } |
99 | | - |
100 | | - cached |
101 | | - predicate reachableBB(BasicBlock bb) { |
102 | | - entryBB(bb) |
103 | | - or |
104 | | - exists(BasicBlock predBB | succBB(predBB, bb) | reachableBB(predBB)) |
105 | | - } |
106 | | -} |
107 | | - |
108 | | -private import Internal |
109 | | - |
110 | | -/** Gets the immediate dominator of `bb`. */ |
111 | | -cached |
112 | | -private BasicBlock immediateDominator(BasicBlock bb) = |
113 | | - idominance(entryBB/1, succBB/2)(_, result, bb) |
114 | | - |
115 | | -/** Gets the immediate post-dominator of `bb`. */ |
116 | | -cached |
117 | | -private BasicBlock immediatePostDominator(BasicBlock bb) = |
118 | | - idominance(exitBB/1, predBB/2)(_, result, bb) |
119 | | - |
120 | | -/** |
121 | | - * A basic block, that is, a maximal straight-line sequence of control flow nodes |
122 | | - * without branches or joins. |
123 | | - * |
124 | | - * At the database level, a basic block is represented by its first control flow node. |
125 | | - */ |
126 | | -class BasicBlock extends @cfg_node, NodeInStmtContainer { |
127 | | - cached |
128 | | - BasicBlock() { Stages::BasicBlocks::ref() and startsBB(this) } |
129 | | - |
130 | | - /** Gets a basic block succeeding this one. */ |
131 | | - BasicBlock getASuccessor() { succBB(this, result) } |
132 | | - |
133 | | - /** Gets a basic block preceding this one. */ |
134 | | - BasicBlock getAPredecessor() { result.getASuccessor() = this } |
135 | | - |
136 | | - /** Gets a node in this block. */ |
137 | | - ControlFlowNode getANode() { result = this.getNode(_) } |
138 | | - |
139 | | - /** Gets the node at the given position in this block. */ |
140 | | - ControlFlowNode getNode(int pos) { bbIndex(this, result, pos) } |
141 | | - |
142 | | - /** Gets the first node in this block. */ |
143 | | - ControlFlowNode getFirstNode() { result = this } |
144 | | - |
145 | | - /** Gets the last node in this block. */ |
146 | | - ControlFlowNode getLastNode() { result = this.getNode(this.length() - 1) } |
147 | | - |
148 | | - /** Gets the length of this block. */ |
149 | | - int length() { result = bbLength(this) } |
150 | | - |
151 | | - /** Holds if this basic block uses variable `v` in its `i`th node `u`. */ |
152 | | - predicate useAt(int i, Variable v, VarUse u) { useAt(this, i, v, u) } |
153 | | - |
154 | | - /** Holds if this basic block defines variable `v` in its `i`th node `d`. */ |
155 | | - predicate defAt(int i, Variable v, VarDef d) { defAt(this, i, v, d) } |
156 | | - |
157 | | - /** |
158 | | - * Holds if `v` is live at entry to this basic block and `u` is a use of `v` |
159 | | - * witnessing the liveness. |
160 | | - * |
161 | | - * In other words, `u` is a use of `v` that is reachable from the |
162 | | - * entry node of this basic block without going through a redefinition |
163 | | - * of `v`. The use `u` may either be in this basic block, or in another |
164 | | - * basic block reachable from this one. |
165 | | - */ |
166 | | - predicate isLiveAtEntry(Variable v, VarUse u) { |
167 | | - // restrict `u` to be reachable from this basic block |
168 | | - u = this.getASuccessor*().getANode() and |
169 | | - ( |
170 | | - // shortcut: if `v` is never defined, then it must be live |
171 | | - this.isDefinedInSameContainer(v) |
172 | | - implies |
173 | | - // otherwise, do full liveness computation |
174 | | - this.isLiveAtEntryImpl(v, u) |
175 | | - ) |
176 | | - } |
177 | | - |
178 | | - /** |
179 | | - * Holds if `v` is live at entry to this basic block and `u` is a use of `v` |
180 | | - * witnessing the liveness, where `v` is defined at least once in the enclosing |
181 | | - * function or script. |
182 | | - */ |
183 | | - private predicate isLiveAtEntryImpl(Variable v, VarUse u) { |
184 | | - this.isLocallyLiveAtEntry(v, u) |
185 | | - or |
186 | | - this.isDefinedInSameContainer(v) and |
187 | | - not this.defAt(_, v, _) and |
188 | | - this.getASuccessor().isLiveAtEntryImpl(v, u) |
189 | | - } |
190 | | - |
191 | | - /** |
192 | | - * Holds if `v` is defined at least once in the function or script to which |
193 | | - * this basic block belongs. |
194 | | - */ |
195 | | - private predicate isDefinedInSameContainer(Variable v) { |
196 | | - exists(VarDef def | def.getAVariable() = v and def.getContainer() = this.getContainer()) |
197 | | - } |
198 | | - |
199 | | - /** |
200 | | - * Holds if `v` is a variable that is live at entry to this basic block. |
201 | | - * |
202 | | - * Note that this is equivalent to `bb.isLiveAtEntry(v, _)`, but may |
203 | | - * be more efficient on large databases. |
204 | | - */ |
205 | | - predicate isLiveAtEntry(Variable v) { |
206 | | - this.isLocallyLiveAtEntry(v, _) |
207 | | - or |
208 | | - not this.defAt(_, v, _) and this.getASuccessor().isLiveAtEntry(v) |
209 | | - } |
210 | | - |
211 | | - /** |
212 | | - * Holds if local variable `v` is live at entry to this basic block and |
213 | | - * `u` is a use of `v` witnessing the liveness. |
214 | | - */ |
215 | | - predicate localIsLiveAtEntry(LocalVariable v, VarUse u) { |
216 | | - this.isLocallyLiveAtEntry(v, u) |
217 | | - or |
218 | | - not this.defAt(_, v, _) and this.getASuccessor().localIsLiveAtEntry(v, u) |
219 | | - } |
220 | | - |
221 | | - /** |
222 | | - * Holds if local variable `v` is live at entry to this basic block. |
223 | | - */ |
224 | | - predicate localIsLiveAtEntry(LocalVariable v) { |
225 | | - this.isLocallyLiveAtEntry(v, _) |
226 | | - or |
227 | | - not this.defAt(_, v, _) and this.getASuccessor().localIsLiveAtEntry(v) |
228 | | - } |
229 | | - |
230 | | - /** |
231 | | - * Holds if `d` is a definition of `v` that is reachable from the beginning of |
232 | | - * this basic block without going through a redefinition of `v`. |
233 | | - */ |
234 | | - predicate localMayBeOverwritten(LocalVariable v, VarDef d) { |
235 | | - this.isLocallyOverwritten(v, d) |
236 | | - or |
237 | | - not this.defAt(_, v, _) and this.getASuccessor().localMayBeOverwritten(v, d) |
238 | | - } |
239 | | - |
240 | | - /** |
241 | | - * Gets the next index after `i` in this basic block at which `v` is |
242 | | - * defined or used, provided that `d` is a definition of `v` at index `i`. |
243 | | - * If there are no further uses or definitions of `v` after `i`, the |
244 | | - * result is the length of this basic block. |
245 | | - */ |
246 | | - private int nextDefOrUseAfter(PurelyLocalVariable v, int i, VarDef d) { |
247 | | - this.defAt(i, v, d) and |
248 | | - result = |
249 | | - min(int j | |
250 | | - (this.defAt(j, v, _) or this.useAt(j, v, _) or j = this.length()) and |
251 | | - j > i |
252 | | - ) |
253 | | - } |
254 | | - |
255 | | - /** |
256 | | - * Holds if `d` defines variable `v` at the `i`th node of this basic block, and |
257 | | - * the definition is live, that is, the variable may be read after this |
258 | | - * definition and before a re-definition. |
259 | | - */ |
260 | | - predicate localLiveDefAt(PurelyLocalVariable v, int i, VarDef d) { |
261 | | - exists(int j | j = this.nextDefOrUseAfter(v, i, d) | |
262 | | - this.useAt(j, v, _) |
263 | | - or |
264 | | - j = this.length() and this.getASuccessor().localIsLiveAtEntry(v) |
265 | | - ) |
266 | | - } |
267 | | - |
268 | | - /** |
269 | | - * Holds if `u` is a use of `v` in this basic block, and there are |
270 | | - * no definitions of `v` before it. |
271 | | - */ |
272 | | - private predicate isLocallyLiveAtEntry(Variable v, VarUse u) { |
273 | | - exists(int n | this.useAt(n, v, u) | not exists(int m | m < n | this.defAt(m, v, _))) |
274 | | - } |
275 | | - |
276 | | - /** |
277 | | - * Holds if `d` is a definition of `v` in this basic block, and there are |
278 | | - * no other definitions of `v` before it. |
279 | | - */ |
280 | | - private predicate isLocallyOverwritten(Variable v, VarDef d) { |
281 | | - exists(int n | this.defAt(n, v, d) | not exists(int m | m < n | this.defAt(m, v, _))) |
282 | | - } |
283 | | - |
284 | | - /** |
285 | | - * Gets the basic block that immediately dominates this basic block. |
286 | | - */ |
287 | | - ReachableBasicBlock getImmediateDominator() { result = immediateDominator(this) } |
288 | | - |
289 | | - /** |
290 | | - * Holds if this if a basic block whose last node is an exit node. |
291 | | - */ |
292 | | - predicate isExitBlock() { exitBB(this) } |
293 | | -} |
294 | | - |
295 | | -/** |
296 | | - * An unreachable basic block, that is, a basic block |
297 | | - * whose first node is unreachable. |
298 | | - */ |
299 | | -class UnreachableBlock extends BasicBlock { |
300 | | - UnreachableBlock() { this.getFirstNode().isUnreachable() } |
301 | | -} |
302 | | - |
303 | | -/** |
304 | | - * An entry basic block, that is, a basic block |
305 | | - * whose first node is the entry node of a statement container. |
306 | | - */ |
307 | | -class EntryBasicBlock extends BasicBlock { |
308 | | - EntryBasicBlock() { entryBB(this) } |
309 | | -} |
310 | | - |
311 | | -/** |
312 | | - * A basic block that is reachable from an entry basic block. |
313 | | - */ |
314 | | -class ReachableBasicBlock extends BasicBlock { |
315 | | - ReachableBasicBlock() { reachableBB(this) } |
316 | | - |
317 | | - /** |
318 | | - * Holds if this basic block strictly dominates `bb`. |
319 | | - */ |
320 | | - pragma[inline] |
321 | | - predicate strictlyDominates(ReachableBasicBlock bb) { this = immediateDominator+(bb) } |
322 | | - |
323 | | - /** |
324 | | - * Holds if this basic block dominates `bb`. |
325 | | - * |
326 | | - * This predicate is reflexive: each reachable basic block dominates itself. |
327 | | - */ |
328 | | - pragma[inline] |
329 | | - predicate dominates(ReachableBasicBlock bb) { this = immediateDominator*(bb) } |
330 | | - |
331 | | - /** |
332 | | - * Holds if this basic block strictly post-dominates `bb`. |
333 | | - */ |
334 | | - pragma[inline] |
335 | | - predicate strictlyPostDominates(ReachableBasicBlock bb) { this = immediatePostDominator+(bb) } |
336 | | - |
337 | | - /** |
338 | | - * Holds if this basic block post-dominates `bb`. |
339 | | - * |
340 | | - * This predicate is reflexive: each reachable basic block post-dominates itself. |
341 | | - */ |
342 | | - pragma[inline] |
343 | | - predicate postDominates(ReachableBasicBlock bb) { this = immediatePostDominator*(bb) } |
344 | | -} |
345 | | - |
346 | | -/** |
347 | | - * A reachable basic block with more than one predecessor. |
348 | | - */ |
349 | | -class ReachableJoinBlock extends ReachableBasicBlock { |
350 | | - ReachableJoinBlock() { this.getFirstNode().isJoin() } |
351 | | - |
352 | | - /** |
353 | | - * Holds if this basic block belongs to the dominance frontier of `b`, that is |
354 | | - * `b` dominates a predecessor of this block, but not this block itself. |
355 | | - * |
356 | | - * Algorithm from Cooper et al., "A Simple, Fast Dominance Algorithm" (Figure 5), |
357 | | - * who in turn attribute it to Ferrante et al., "The program dependence graph and |
358 | | - * its use in optimization". |
359 | | - */ |
360 | | - predicate inDominanceFrontierOf(ReachableBasicBlock b) { |
361 | | - b = this.getAPredecessor() and not b = this.getImmediateDominator() |
362 | | - or |
363 | | - exists(ReachableBasicBlock prev | this.inDominanceFrontierOf(prev) | |
364 | | - b = prev.getImmediateDominator() and |
365 | | - not b = this.getImmediateDominator() |
366 | | - ) |
367 | | - } |
368 | | -} |
| 1 | +import internal.BasicBlockInternal |
0 commit comments