Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 library tree_ir.optimization.variable_merger; | |
| 2 | |
| 3 import 'optimization.dart' show Pass, PassMixin; | |
| 4 import '../tree_ir_nodes.dart'; | |
| 5 import '../../elements/elements.dart' show Local; | |
| 6 | |
| 7 /// Merges variables based on liveness and source variable information. | |
| 8 /// | |
| 9 /// This phase cleans up artifacts introduced by the translation through CPS, | |
| 10 /// where each source variable is translated into several copies. The copies | |
| 11 /// are merged again when they are not live simultaneously. | |
| 12 class VariableMerger extends RecursiveVisitor with PassMixin { | |
| 13 String get passName => 'Variable merger'; | |
| 14 | |
| 15 @override | |
| 16 void rewriteExecutableDefinition(ExecutableDefinition node) { | |
| 17 visitExecutableDefinition(node); | |
| 18 } | |
| 19 | |
| 20 /// Rewrites the given function. | |
| 21 /// This is called for the outermost function and inner functions. | |
| 22 void rewriteFunction(ExecutableDefinition node) { | |
| 23 BlockGraphBuilder builder = new BlockGraphBuilder(); | |
| 24 builder.visitExecutableDefinition(node); | |
| 25 _computeLiveness(builder.blocks); | |
| 26 Map<Variable, Variable> subst = _computeRegisterAllocation(builder.blocks); | |
| 27 new SubstVariables(subst).visitExecutableDefinition(node); | |
| 28 } | |
| 29 | |
| 30 visitFunctionDefinition(FunctionDefinition node) { | |
| 31 super.visitFunctionDefinition(node); // Recurse to visit inner functions. | |
| 32 rewriteFunction(node); | |
| 33 } | |
| 34 | |
| 35 visitFieldDefinition(FieldDefinition node) { | |
| 36 super.visitFieldDefinition(node); | |
| 37 rewriteFunction(node); | |
| 38 } | |
| 39 | |
| 40 visitConstructorDefinition(ConstructorDefinition node) { | |
| 41 super.visitConstructorDefinition(node); | |
| 42 rewriteFunction(node); | |
| 43 } | |
| 44 } | |
| 45 | |
| 46 /// Basic block in a control-flow graph. | |
| 47 /// | |
| 48 /// Each block consists of a sequence of reads or a sequence of writes. | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
Flesh out this comment a bit to say explicitly tha
asgerf
2015/03/27 15:18:26
Changed to basic blocks with interleaved operation
| |
| 49 class Block { | |
| 50 /// List of predecessors in the control-flow graph.s | |
|
Kevin Millikin (Google)
2015/03/26 14:35:53
There's an extra 's'.
asgerf
2015/03/27 15:18:27
Done.
| |
| 51 final List<Block> predecessors = <Block>[]; | |
| 52 | |
| 53 /// Entry to the catch block for the enclosing try, or `null`. | |
| 54 final Block catchBlock; | |
| 55 | |
| 56 /// List of nodes with this block as [catchBlock]. | |
| 57 final List<Block> catchPredecessors = <Block>[]; | |
| 58 | |
| 59 /// True if this is a sequence of read operations, false if write operations. | |
| 60 bool isRead = true; | |
| 61 bool get isWrite => !isRead; | |
| 62 | |
| 63 /// Variables being read or written in this block. | |
|
Kevin Millikin (Google)
2015/03/26 14:35:53
I'd mention either "Sequence of variables" or "in
asgerf
2015/03/27 15:18:27
Done.
| |
| 64 final List<Variable> variables = <Variable>[]; | |
| 65 | |
| 66 /// Auxilliary fields used by the liveness analysis. | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
One 'l' in auxiliary.
asgerf
2015/03/27 15:18:27
Done.
| |
| 67 bool inWorklist = true; | |
| 68 Set<Variable> liveBefore = new Set<Variable>(); | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
Consider liveIn and liveOut, which are fairly stan
asgerf
2015/03/27 15:18:26
Done.
| |
| 69 Set<Variable> liveAfter = new Set<Variable>(); | |
| 70 | |
| 71 Block(this.catchBlock) { | |
| 72 if (catchBlock != null) { | |
| 73 catchBlock.catchPredecessors.add(this); | |
| 74 } | |
| 75 } | |
| 76 } | |
| 77 | |
| 78 /// Builds a control-flow graph suitable for performing liveness analysis. | |
| 79 class BlockGraphBuilder extends RecursiveVisitor { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:55
The blank line after class isn't necessary.
asgerf
2015/03/27 15:18:26
Done.
| |
| 80 | |
| 81 Map<Label, Block> jumpTarget = <Label, Block>{}; | |
|
Kevin Millikin (Google)
2015/03/26 14:35:53
I'd make most of these private names, except for t
asgerf
2015/03/27 15:18:27
Done.
| |
| 82 Block currentBlock; | |
| 83 List<Block> blocks = <Block>[]; | |
| 84 | |
| 85 /// Variables with an assignment that should be treated as final. | |
| 86 /// | |
| 87 /// Such variables cannot be merged with any other variables, so we exclude | |
| 88 /// them from the control-flow graph entirely. | |
| 89 Set<Variable> finalVariables = new Set<Variable>(); | |
| 90 | |
| 91 BlockGraphBuilder() { | |
| 92 currentBlock = newBlock(); | |
| 93 } | |
| 94 | |
| 95 /// Creates a new block with the current exception handler or [catchBlock] | |
| 96 /// if provided. | |
| 97 Block newBlock({Block catchBlock}) { | |
| 98 if (catchBlock == null && currentBlock != null) { | |
| 99 catchBlock = currentBlock.catchBlock; | |
| 100 } | |
| 101 Block block = new Block(catchBlock); | |
| 102 blocks.add(block); | |
| 103 return block; | |
| 104 } | |
| 105 | |
| 106 /// Starts a new branch after the end of [block]. | |
|
Kevin Millikin (Google)
2015/03/26 14:35:53
Not necessary a branch (at least, not a non-trivia
asgerf
2015/03/27 15:18:28
Done.
| |
| 107 void branchFrom(Block block) { | |
| 108 currentBlock = newBlock()..predecessors.add(block); | |
| 109 } | |
| 110 | |
| 111 /// Called when reading from [v]. | |
| 112 /// | |
| 113 /// Appends a read operation to the current basic block, or starts a new | |
| 114 /// block if the current block is a write block. | |
| 115 void read(Variable v) { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
Go ahead and spell out 'variable', here and below.
asgerf
2015/03/27 15:18:27
Done.
| |
| 116 if (v.isCaptured) return; | |
| 117 if (finalVariables.contains(v)) return; | |
| 118 if (!currentBlock.isRead) { | |
| 119 branchFrom(currentBlock); | |
| 120 currentBlock.isRead = true; | |
|
Kevin Millikin (Google)
2015/03/26 14:35:53
true is the default and we do rely on that a bit b
asgerf
2015/03/27 15:18:26
Not relevant anymore.
| |
| 121 } | |
| 122 currentBlock.variables.add(v); | |
| 123 } | |
| 124 | |
| 125 /// Called when writing to [v]. | |
| 126 /// | |
| 127 /// Appends a write operation to the current basic block, or starts a new | |
| 128 /// block if the current block is a read block. | |
| 129 void write(Variable v) { | |
| 130 if (v.isCaptured) return; | |
| 131 if (finalVariables.contains(v)) return; | |
| 132 if (currentBlock.isRead) { | |
| 133 if (!currentBlock.variables.isEmpty) { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
This needs a short comment to explain that this co
asgerf
2015/03/27 15:18:27
Not relevant anymore.
| |
| 134 branchFrom(currentBlock); | |
| 135 } | |
| 136 currentBlock.isRead = false; | |
| 137 } | |
| 138 currentBlock.variables.add(v); | |
| 139 } | |
| 140 | |
| 141 /// Called to indicate that [v] has a final assignment, and should therefore | |
| 142 /// be ignored. Subsequent calls to [read] and [write] will ignore the it. | |
| 143 void finalWrite(Variable v) { | |
| 144 finalVariables.add(v); | |
| 145 } | |
| 146 | |
| 147 visitVariableUse(VariableUse node) { | |
| 148 read(node.variable); | |
| 149 } | |
| 150 | |
| 151 visitAssign(Assign node) { | |
| 152 visitExpression(node.value); | |
| 153 write(node.variable); | |
| 154 visitStatement(node.next); | |
| 155 } | |
| 156 | |
| 157 visitIf(If node) { | |
| 158 visitExpression(node.condition); | |
| 159 Block afterCondition = currentBlock; | |
| 160 branchFrom(afterCondition); | |
| 161 visitStatement(node.thenStatement); | |
| 162 branchFrom(afterCondition); | |
| 163 visitStatement(node.elseStatement); | |
| 164 } | |
| 165 | |
| 166 visitLabeledStatement(LabeledStatement node) { | |
| 167 Block join = jumpTarget[node.label] = newBlock(); | |
| 168 visitStatement(node.body); // visitBreak will add predecessors to join. | |
| 169 currentBlock = join; | |
| 170 visitStatement(node.next); | |
| 171 } | |
| 172 | |
| 173 visitBreak(Break node) { | |
| 174 jumpTarget[node.target].predecessors.add(currentBlock); | |
| 175 } | |
| 176 | |
| 177 visitContinue(Continue node) { | |
| 178 jumpTarget[node.target].predecessors.add(currentBlock); | |
| 179 } | |
| 180 | |
| 181 visitWhileTrue(WhileTrue node) { | |
| 182 Block join = jumpTarget[node.label] = newBlock(); | |
| 183 join.predecessors.add(currentBlock); | |
| 184 currentBlock = join; | |
| 185 visitStatement(node.body); // visitContinue will add predecessors to join. | |
| 186 } | |
| 187 | |
| 188 visitWhileCondition(WhileCondition node) { | |
| 189 Block join = jumpTarget[node.label] = newBlock(); | |
| 190 join.predecessors.add(currentBlock); | |
| 191 currentBlock = join; | |
| 192 visitExpression(node.condition); | |
| 193 Block afterCondition = currentBlock; | |
| 194 branchFrom(afterCondition); | |
| 195 visitStatement(node.body); // visitContinue will add predecessors to join. | |
| 196 branchFrom(afterCondition); | |
| 197 visitStatement(node.next); | |
| 198 } | |
| 199 | |
| 200 visitTry(Try node) { | |
| 201 Block catchBlock = newBlock(); | |
| 202 Block tryBlock = newBlock(catchBlock: catchBlock); | |
|
Kevin Millikin (Google)
2015/03/26 14:35:53
The three lines starting here are branchFrom with
asgerf
2015/03/27 15:18:27
Done.
| |
| 203 tryBlock.predecessors.add(currentBlock); | |
| 204 currentBlock = tryBlock; | |
| 205 visitStatement(node.tryBody); | |
| 206 currentBlock = catchBlock; | |
| 207 node.catchParameters.forEach(finalWrite); | |
|
Kevin Millikin (Google)
2015/03/26 14:35:53
Is this necessary or defensive? It seems like sin
asgerf
2015/03/27 15:18:27
Both. Something smarter could be done, but it's no
| |
| 208 visitStatement(node.catchBody); | |
| 209 } | |
| 210 | |
| 211 visitConditional(Conditional node) { | |
| 212 visitExpression(node.condition); | |
| 213 // TODO(asgerf): When assignment expressions are added, this is no longer | |
| 214 // sound; then we need to handle as a branch. | |
| 215 visitExpression(node.thenExpression); | |
| 216 visitExpression(node.elseExpression); | |
| 217 } | |
| 218 | |
| 219 visitLogicalOperator(LogicalOperator node) { | |
| 220 visitExpression(node.left); | |
| 221 // TODO(asgerf): When assignment expressions are added, this is no longer | |
| 222 // sound; then we need to handle as a branch. | |
| 223 visitExpression(node.right); | |
| 224 } | |
| 225 | |
| 226 visitFunctionDeclaration(FunctionDeclaration node) { | |
| 227 finalWrite(node.variable); | |
| 228 visitStatement(node.next); | |
| 229 // Do not traverse inner function. | |
| 230 } | |
| 231 | |
| 232 visitFunctionExpression(FunctionExpression node) { | |
| 233 // Do not traverse inner function. | |
| 234 } | |
| 235 | |
| 236 visitFunctionDefinition(FunctionDefinition node) { | |
| 237 // Function parameters are treated as write operations at the entry point, | |
| 238 // so they can potentially be merged with other copies of the parameter. | |
| 239 // Note that function parameters always have distinct source variables, | |
| 240 // so we don't risk accidentally merging two parameters. | |
| 241 node.parameters.forEach(write); | |
| 242 visitStatement(node.body); | |
| 243 } | |
| 244 | |
| 245 visitConstructorDefinition(ConstructorDefinition node) { | |
| 246 node.parameters.forEach(write); | |
| 247 node.initializers.forEach(visitInitializer); | |
| 248 visitStatement(node.body); | |
| 249 } | |
| 250 } | |
| 251 | |
| 252 /// Computes liveness information of the given control-flow graph. | |
| 253 /// | |
| 254 /// The results are stored in [Block.liveBefore] and [Block.liveAfter]. | |
| 255 void _computeLiveness(List<Block> blocks) { | |
| 256 List<Block> worklist = new List<Block>.from(blocks); | |
|
Kevin Millikin (Google)
2015/03/26 14:35:53
Comment that blocks are initially in AST order and
asgerf
2015/03/27 15:18:26
Done.
| |
| 257 while (!worklist.isEmpty) { | |
| 258 Block block = worklist.removeLast(); | |
| 259 block.inWorklist = false; | |
| 260 Set<Variable> live = new Set<Variable>.from(block.liveAfter); | |
|
Kevin Millikin (Google)
2015/03/26 14:35:53
This is potentially expensive, it's making an iter
asgerf
2015/03/27 15:18:27
Done.
| |
| 261 if (block.isRead) { | |
| 262 // Reading a variable makes it live before that point. | |
| 263 live.addAll(block.variables); | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
I think it is worth pointing out that our blocks a
asgerf
2015/03/27 15:18:28
Not relevant anymore.
| |
| 264 } else { | |
| 265 // Assigning to a variable makes it dead before that point. | |
| 266 // Note that when a variable is live at entry to the current catch block, | |
| 267 // it remains live before the assignment. | |
| 268 // When a variable becomes live at the catch block, it will be removed | |
| 269 // from block.variables, so here we can safely mark them all as dead. | |
| 270 live.removeAll(block.variables); | |
| 271 } | |
| 272 | |
| 273 // If anything changed, propagate liveness backwards. | |
| 274 if (block.liveBefore.length < live.length) { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
Comment that this is a monotone analysis: the live
asgerf
2015/03/27 15:18:27
I agree, but there is now an explicit changed flag
| |
| 275 block.liveBefore = live; | |
| 276 | |
| 277 // Propagate live variables to predecessors. | |
| 278 for (Block pred in block.predecessors) { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:53
Spell out predecessor.
asgerf
2015/03/27 15:18:27
Done.
| |
| 279 int size = pred.liveAfter.length; | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
size ==> length, or originalLength or the like.
asgerf
2015/03/27 15:18:28
Done.
| |
| 280 pred.liveAfter.addAll(live); | |
| 281 if (pred.liveAfter.length > size && !pred.inWorklist) { | |
| 282 worklist.add(pred); | |
| 283 pred.inWorklist = true; | |
| 284 } | |
| 285 } | |
| 286 | |
| 287 // Propagate live variables to catch predecessors. | |
| 288 for (Block pred in block.catchPredecessors) { | |
| 289 bool changed = false; | |
| 290 int size = pred.liveAfter.length; | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
Use 'length' in the name, not 'size'.
asgerf
2015/03/27 15:18:27
Done.
| |
| 291 pred.liveAfter.addAll(live); | |
| 292 if (size < pred.liveAfter.length) { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
The analogous comparison in the loop above has siz
asgerf
2015/03/31 12:14:18
Done.
| |
| 293 changed = true; | |
| 294 } | |
| 295 if (pred.isWrite) { | |
| 296 // Remove assignments to variables that are live in the catch block. | |
| 297 size = pred.variables.length; | |
| 298 pred.variables.removeWhere(block.liveBefore.contains); | |
|
Kevin Millikin (Google)
2015/03/26 14:35:55
block.liveBefore is the same as live, isn't it? I
asgerf
2015/03/27 15:18:27
Done.
| |
| 299 if (pred.variables.length < size) { | |
| 300 changed = true; | |
| 301 } | |
| 302 } | |
| 303 if (!pred.inWorklist && changed) { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
Probably cheaper to check changed before !pred.inW
asgerf
2015/03/27 15:18:28
Done.
| |
| 304 worklist.add(pred); | |
| 305 pred.inWorklist = true; | |
| 306 } | |
| 307 } | |
| 308 } | |
| 309 } | |
| 310 } | |
| 311 | |
| 312 /// Based on liveness information, computes a map of variable substitutions to | |
| 313 /// merge variables. | |
| 314 /// | |
| 315 /// Constructs a register interference graph. This is an undirected graph of | |
| 316 /// variables, with an edge between two variables if they cannot be merged | |
| 317 /// (because they are live simultaneously). | |
| 318 /// | |
| 319 /// We then compute a graph coloring, where the color of a node denotes which | |
| 320 /// variable it will be substituted by. | |
| 321 /// | |
| 322 /// We never merge variables that originated from distinct source variables, | |
| 323 /// so we build a separate register interference graph for each source variable. | |
| 324 Map<Variable, Variable> _computeRegisterAllocation(List<Block> blocks) { | |
| 325 Map<Variable, Set<Variable>> edges = new Map<Variable, Set<Variable>>(); | |
|
Kevin Millikin (Google)
2015/03/26 14:35:55
edges ==> interferences
asgerf
2015/03/27 15:18:28
Done.
| |
| 326 | |
| 327 // At the assignment to a variable x, add an edge to every variable that is | |
| 328 // live after the assignment (if it came from the same source variable). | |
| 329 for (Block block in blocks) { | |
| 330 if (block.isWrite) { | |
| 331 // Group the liveAfter set by source variable. | |
| 332 Map<Local, List<Variable>> liveAfter = <Local, List<Variable>>{}; | |
| 333 for (Variable x in block.liveAfter) { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:53
x ==> variable
asgerf
2015/03/27 15:18:27
Done.
| |
| 334 liveAfter.putIfAbsent(x.element, () => <Variable>[]).add(x); | |
| 335 edges.putIfAbsent(x, () => new Set<Variable>()); | |
| 336 } | |
| 337 // Add edges for each variable being assigned here. | |
| 338 for (Variable x in block.variables.reversed) { | |
| 339 edges.putIfAbsent(x, () => new Set<Variable>()); | |
| 340 List<Variable> live = liveAfter[x.element]; | |
| 341 if (live != null) { | |
| 342 live.remove(x); // Hide from earlier assignments in the block. | |
| 343 for (Variable y in live) { | |
| 344 edges[x].add(y); | |
| 345 edges[y].add(x); | |
| 346 } | |
| 347 } | |
| 348 } | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 // Sort the variables by descending degree. | |
| 353 // The most constrained variables will be assigned a color first. | |
| 354 List<Variable> variables = edges.keys.toList(); | |
| 355 variables.sort((x, y) => edges[y].length - edges[x].length); | |
| 356 | |
| 357 Map<Local, List<Variable>> registers = <Local, List<Variable>>{}; | |
| 358 Map<Variable, Variable> subst = <Variable, Variable>{}; | |
| 359 | |
| 360 for (Variable v1 in variables) { | |
| 361 List<Variable> register = | |
| 362 registers.putIfAbsent(v1.element, () => <Variable>[]); | |
| 363 | |
| 364 // Find an unused color. | |
| 365 Set<Variable> potential = new Set<Variable>.from(register); | |
| 366 for (Variable v2 in edges[v1]) { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
I don't have a feel for how big the sets of interf
asgerf
2015/03/27 15:18:27
When compiling swarm, it's empty about 90% of the
| |
| 367 Variable v2subst = subst[v2]; | |
| 368 if (v2subst != null) { | |
| 369 potential.remove(v2subst); | |
| 370 } | |
| 371 } | |
| 372 if (potential.isEmpty) { | |
| 373 // If no free color was found, add this variable as a new color. | |
| 374 register.add(v1); | |
| 375 subst[v1] = v1; | |
| 376 } else { | |
| 377 subst[v1] = potential.first; | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 return subst; | |
| 382 } | |
| 383 | |
| 384 /// Performs variable substitution and removes redundant assignments. | |
| 385 class SubstVariables extends RecursiveVisitor { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:53
I'm not sure what Subst is supposed to be. The ve
asgerf
2015/03/27 15:18:26
Done.
| |
| 386 | |
| 387 Map<Variable, Variable> subst; | |
| 388 | |
| 389 SubstVariables(this.subst); | |
| 390 | |
| 391 Variable replaceRead(Variable v) { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
v ==> variable, w ==> other.
asgerf
2015/03/27 15:18:27
Done.
| |
| 392 Variable w = subst[v]; | |
| 393 if (w == null) return v; | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
This is the case for final assignments, or does it
asgerf
2015/03/27 15:18:26
Done.
| |
| 394 w.readCount++; | |
| 395 v.readCount--; | |
| 396 return w; | |
| 397 } | |
| 398 | |
| 399 Variable replaceWrite(Variable v) { | |
| 400 Variable w = subst[v]; | |
| 401 if (w == null) return v; | |
| 402 w.writeCount++; | |
| 403 v.writeCount--; | |
| 404 return w; | |
| 405 } | |
| 406 | |
| 407 void replaceParameters(List<Variable> parameters) { | |
| 408 for (int i=0; i < parameters.length; i++) { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
'i = 0'.
asgerf
2015/03/27 15:18:27
Done.
| |
| 409 parameters[i] = replaceWrite(parameters[i]); | |
| 410 } | |
| 411 } | |
| 412 | |
| 413 visitVariableUse(VariableUse node) { | |
| 414 node.variable = replaceRead(node.variable); | |
| 415 } | |
| 416 | |
| 417 visitFunctionDefinition(FunctionDefinition node) { | |
| 418 replaceParameters(node.parameters); | |
| 419 node.body = visitStatement(node.body); | |
| 420 } | |
| 421 | |
| 422 visitConstructorDefinition(ConstructorDefinition node) { | |
| 423 replaceParameters(node.parameters); | |
| 424 node.initializers.forEach(visitInitializer); | |
| 425 node.body = visitStatement(node.body); | |
| 426 } | |
| 427 | |
| 428 visitFieldInitializer(FieldInitializer node) { | |
| 429 node.body = visitStatement(node.body); | |
| 430 } | |
| 431 | |
| 432 visitSuperInitializer(SuperInitializer node) { | |
| 433 for (int i=0; i<node.arguments.length; i++) { | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
'i = 0', 'i < node.arguments.length'.
And '++i' :
asgerf
2015/03/27 15:18:26
Done.
| |
| 434 node.arguments[i] = visitStatement(node.arguments[i]); | |
| 435 } | |
| 436 } | |
| 437 | |
| 438 // Statement visitors should return the transformed statement so we | |
| 439 // can remove redundant assignments. | |
| 440 Statement visitStatement(Statement node) => super.visitStatement(node); | |
| 441 | |
| 442 Statement visitAssign(Assign node) { | |
| 443 node.variable = replaceWrite(node.variable); | |
| 444 | |
| 445 visitExpression(node.value); | |
| 446 node.next = visitStatement(node.next); | |
| 447 | |
| 448 // Remove assignments of form "x := x" | |
| 449 if (node.value is VariableUse) { | |
| 450 VariableUse value = node.value; | |
| 451 if (value.variable == node.variable) { | |
| 452 value.variable.readCount--; | |
| 453 node.variable.writeCount--; | |
| 454 return node.next; | |
| 455 } | |
| 456 } | |
| 457 | |
| 458 return node; | |
| 459 } | |
| 460 | |
| 461 Statement visitLabeledStatement(LabeledStatement node) { | |
| 462 node.body = visitStatement(node.body); | |
| 463 node.next = visitStatement(node.next); | |
| 464 return node; | |
| 465 } | |
| 466 | |
| 467 Statement visitReturn(Return node) { | |
| 468 visitExpression(node.value); | |
| 469 return node; | |
| 470 } | |
| 471 | |
| 472 Statement visitBreak(Break node) => node; | |
|
Kevin Millikin (Google)
2015/03/26 14:35:54
I usually make all the 'related' method bodies hav
asgerf
2015/03/27 15:18:27
Done.
| |
| 473 | |
| 474 Statement visitContinue(Continue node) => node; | |
| 475 | |
| 476 Statement visitIf(If node) { | |
| 477 visitExpression(node.condition); | |
| 478 node.thenStatement = visitStatement(node.thenStatement); | |
| 479 node.elseStatement = visitStatement(node.elseStatement); | |
| 480 return node; | |
| 481 } | |
| 482 | |
| 483 Statement visitWhileTrue(WhileTrue node) { | |
| 484 node.body = visitStatement(node.body); | |
| 485 return node; | |
| 486 } | |
| 487 | |
| 488 Statement visitWhileCondition(WhileCondition node) { | |
| 489 visitExpression(node.condition); | |
| 490 node.body = visitStatement(node.body); | |
| 491 node.next = visitStatement(node.next); | |
| 492 return node; | |
| 493 } | |
| 494 | |
| 495 Statement visitFunctionDeclaration(FunctionDeclaration node) { | |
| 496 node.next = visitStatement(node.next); | |
| 497 return node; | |
| 498 } | |
| 499 | |
| 500 Statement visitExpressionStatement(ExpressionStatement node) { | |
| 501 visitExpression(node.expression); | |
| 502 node.next = visitStatement(node.next); | |
| 503 return node; | |
| 504 } | |
| 505 | |
| 506 Statement visitTry(Try node) { | |
| 507 node.tryBody = visitStatement(node.tryBody); | |
| 508 node.catchBody = visitStatement(node.catchBody); | |
| 509 return node; | |
| 510 } | |
| 511 | |
| 512 Statement visitSetField(SetField node) { | |
| 513 visitExpression(node.object); | |
| 514 visitExpression(node.value); | |
| 515 node.next = visitStatement(node.next); | |
| 516 return node; | |
| 517 } | |
| 518 } | |
| OLD | NEW |