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