OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 class SsaCodeGeneratorTask extends CompilerTask { | 5 class SsaCodeGeneratorTask extends CompilerTask { |
6 final JavaScriptBackend backend; | 6 final JavaScriptBackend backend; |
7 SsaCodeGeneratorTask(JavaScriptBackend backend) | 7 SsaCodeGeneratorTask(JavaScriptBackend backend) |
8 : this.backend = backend, | 8 : this.backend = backend, |
9 super(backend.compiler); | 9 super(backend.compiler); |
10 String get name() => 'SSA code generator'; | 10 String get name() => 'SSA code generator'; |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
134 * or possibly several comma-separated expressions. | 134 * or possibly several comma-separated expressions. |
135 * - [TYPE_DECLARATION] means that the graph can be generated as an | 135 * - [TYPE_DECLARATION] means that the graph can be generated as an |
136 * expression, and that it only generates expressions of the form | 136 * expression, and that it only generates expressions of the form |
137 * variable = expression | 137 * variable = expression |
138 * which are also valid as parts of a "var" declaration. | 138 * which are also valid as parts of a "var" declaration. |
139 */ | 139 */ |
140 static final int TYPE_STATEMENT = 0; | 140 static final int TYPE_STATEMENT = 0; |
141 static final int TYPE_EXPRESSION = 1; | 141 static final int TYPE_EXPRESSION = 1; |
142 static final int TYPE_DECLARATION = 2; | 142 static final int TYPE_DECLARATION = 2; |
143 | 143 |
144 static final String TEMPORARY_PREFIX = 't'; | |
145 | |
146 final JavaScriptBackend backend; | 144 final JavaScriptBackend backend; |
147 final WorkItem work; | 145 final WorkItem work; |
148 final StringBuffer buffer; | 146 final StringBuffer buffer; |
149 final String parameters; | 147 final String parameters; |
150 | 148 |
151 final Map<Element, String> parameterNames; | 149 final Set<String> declaredVariables; |
Lasse Reichstein Nielsen
2012/05/31 08:24:56
Can you add a doc-comment describing what this mea
ngeoffray
2012/05/31 08:48:22
Done.
| |
152 final Map<int, String> names; | |
153 final Set<String> usedNames; | |
154 final Set<HInstruction> declaredInstructions; | |
155 final Map<String, int> prefixes; | |
156 final Set<HInstruction> generateAtUseSite; | 150 final Set<HInstruction> generateAtUseSite; |
157 final Map<HPhi, String> logicalOperations; | 151 final Map<HPhi, String> logicalOperations; |
158 final Map<Element, ElementAction> breakAction; | 152 final Map<Element, ElementAction> breakAction; |
159 final Map<Element, ElementAction> continueAction; | 153 final Map<Element, ElementAction> continueAction; |
160 final Equivalence<HPhi> phiEquivalence; | 154 final List<String> delayedVariablesDeclaration; |
155 final Map<Element, String> parameterNames; | |
156 VariableNames variableNames; | |
Lasse Reichstein Nielsen
2012/05/31 08:24:56
And this one too, to explain the difference from t
ngeoffray
2012/05/31 08:48:22
Done.
| |
161 | 157 |
162 Element equalsNullElement; | 158 Element equalsNullElement; |
163 Element boolifiedEqualsNullElement; | 159 Element boolifiedEqualsNullElement; |
164 int indent = 0; | 160 int indent = 0; |
165 int expectedPrecedence = JSPrecedence.STATEMENT_PRECEDENCE; | 161 int expectedPrecedence = JSPrecedence.STATEMENT_PRECEDENCE; |
166 JSBinaryOperatorPrecedence unsignedShiftPrecedences; | 162 JSBinaryOperatorPrecedence unsignedShiftPrecedences; |
167 HGraph currentGraph; | 163 HGraph currentGraph; |
168 /** | 164 /** |
169 * Whether the code-generation should try to generate an expression | 165 * Whether the code-generation should try to generate an expression |
170 * instead of a sequence of statements. | 166 * instead of a sequence of statements. |
171 */ | 167 */ |
172 int generationState = STATE_STATEMENT; | 168 int generationState = STATE_STATEMENT; |
173 /** | 169 /** |
174 * While generating expressions, we can't insert variable declarations. | 170 * While generating expressions, we can't insert variable declarations. |
175 * Instead we declare them at the end of the function | 171 * Instead we declare them at the end of the function |
floitsch
2012/05/30 18:45:03
Move this comment too.
ngeoffray
2012/05/31 08:12:46
Done.
| |
176 */ | 172 */ |
177 Link<String> delayedVarDecl = const EmptyLink<String>(); | |
178 HBasicBlock currentBlock; | 173 HBasicBlock currentBlock; |
179 | 174 |
180 // Records a block-information that is being handled specially. | 175 // Records a block-information that is being handled specially. |
181 // Used to break bad recursion. | 176 // Used to break bad recursion. |
182 HBlockInformation currentBlockInformation; | 177 HBlockInformation currentBlockInformation; |
183 // The subgraph is used to delimit traversal for some constructions, e.g., | 178 // The subgraph is used to delimit traversal for some constructions, e.g., |
184 // if branches. | 179 // if branches. |
185 SubGraph subGraph; | 180 SubGraph subGraph; |
186 | 181 |
187 LibraryElement get currentLibrary() => work.element.getLibrary(); | 182 LibraryElement get currentLibrary() => work.element.getLibrary(); |
188 Compiler get compiler() => backend.compiler; | 183 Compiler get compiler() => backend.compiler; |
189 NativeEmitter get nativeEmitter() => backend.emitter.nativeEmitter; | 184 NativeEmitter get nativeEmitter() => backend.emitter.nativeEmitter; |
190 Enqueuer get world() => backend.compiler.enqueuer.codegen; | 185 Enqueuer get world() => backend.compiler.enqueuer.codegen; |
191 | 186 |
192 bool isGenerateAtUseSite(HInstruction instruction) { | 187 bool isGenerateAtUseSite(HInstruction instruction) { |
193 return generateAtUseSite.contains(instruction); | 188 return generateAtUseSite.contains(instruction); |
194 } | 189 } |
195 | 190 |
196 SsaCodeGenerator(this.backend, | 191 SsaCodeGenerator(this.backend, |
197 this.work, | 192 this.work, |
198 this.parameters, | 193 this.parameters, |
199 this.parameterNames) | 194 this.parameterNames) |
200 : names = new Map<int, String>(), | 195 : declaredVariables = new Set<String>(), |
201 prefixes = new Map<String, int>(), | 196 delayedVariablesDeclaration = new List<String>(), |
202 usedNames = new Set<String>(), | |
203 declaredInstructions = new Set<HInstruction>(), | |
204 buffer = new StringBuffer(), | 197 buffer = new StringBuffer(), |
205 generateAtUseSite = new Set<HInstruction>(), | 198 generateAtUseSite = new Set<HInstruction>(), |
206 logicalOperations = new Map<HPhi, String>(), | 199 logicalOperations = new Map<HPhi, String>(), |
207 breakAction = new Map<Element, ElementAction>(), | 200 breakAction = new Map<Element, ElementAction>(), |
208 continueAction = new Map<Element, ElementAction>(), | 201 continueAction = new Map<Element, ElementAction>(), |
209 phiEquivalence = new Equivalence<HPhi>(), | |
210 unsignedShiftPrecedences = JSPrecedence.binary['>>>'] { | 202 unsignedShiftPrecedences = JSPrecedence.binary['>>>'] { |
211 | 203 |
212 for (final name in parameterNames.getValues()) { | |
213 prefixes[name] = 0; | |
214 } | |
215 | |
216 // Create a namespace for temporaries. | |
217 prefixes[TEMPORARY_PREFIX] = 0; | |
218 | |
219 Interceptors interceptors = backend.builder.interceptors; | 204 Interceptors interceptors = backend.builder.interceptors; |
220 equalsNullElement = interceptors.getEqualsNullInterceptor(); | 205 equalsNullElement = interceptors.getEqualsNullInterceptor(); |
221 boolifiedEqualsNullElement = | 206 boolifiedEqualsNullElement = |
222 interceptors.getBoolifiedVersionOf(equalsNullElement); | 207 interceptors.getBoolifiedVersionOf(equalsNullElement); |
223 } | 208 } |
224 | 209 |
225 abstract visitTypeGuard(HTypeGuard node); | 210 abstract visitTypeGuard(HTypeGuard node); |
226 | 211 |
227 abstract beginGraph(HGraph graph); | 212 abstract beginGraph(HGraph graph); |
228 abstract endGraph(HGraph graph); | 213 abstract endGraph(HGraph graph); |
(...skipping 22 matching lines...) Expand all Loading... | |
251 void endExpression(int precedence) { | 236 void endExpression(int precedence) { |
252 if (precedence < expectedPrecedence) { | 237 if (precedence < expectedPrecedence) { |
253 buffer.add(')'); | 238 buffer.add(')'); |
254 } | 239 } |
255 } | 240 } |
256 | 241 |
257 void preGenerateMethod(HGraph graph) { | 242 void preGenerateMethod(HGraph graph) { |
258 new SsaInstructionMerger(generateAtUseSite).visitGraph(graph); | 243 new SsaInstructionMerger(generateAtUseSite).visitGraph(graph); |
259 new SsaConditionMerger(generateAtUseSite, | 244 new SsaConditionMerger(generateAtUseSite, |
260 logicalOperations).visitGraph(graph); | 245 logicalOperations).visitGraph(graph); |
261 new PhiEquivalator(phiEquivalence, logicalOperations).analyzeGraph(graph); | 246 SsaLiveIntervalBuilder intervalBuilder = |
floitsch
2012/05/30 18:45:03
new SsaLiveIntervalBuilder(compiler).visitGraph(gr
ngeoffray
2012/05/31 08:12:46
No, I need the object to take its liveInstructions
Lasse Reichstein Nielsen
2012/05/31 08:24:56
It's also used below.
| |
247 new SsaLiveIntervalBuilder(compiler); | |
248 intervalBuilder.visitGraph(graph); | |
249 SsaVariableAllocator allocator = new SsaVariableAllocator( | |
250 compiler, | |
251 intervalBuilder.liveInstructions, | |
252 intervalBuilder.liveIntervals, | |
253 generateAtUseSite); | |
254 allocator.visitGraph(graph); | |
255 variableNames = allocator.names; | |
262 } | 256 } |
263 | 257 |
264 visitGraph(HGraph graph) { | 258 visitGraph(HGraph graph) { |
265 preGenerateMethod(graph); | 259 preGenerateMethod(graph); |
266 currentGraph = graph; | 260 currentGraph = graph; |
267 indent++; // We are already inside a function. | 261 indent++; // We are already inside a function. |
268 subGraph = new SubGraph(graph.entry, graph.exit); | 262 subGraph = new SubGraph(graph.entry, graph.exit); |
269 beginGraph(graph); | 263 beginGraph(graph); |
270 visitBasicBlock(graph.entry); | 264 visitBasicBlock(graph.entry); |
271 if (!delayedVarDecl.isEmpty()) { | 265 if (!delayedVariablesDeclaration.isEmpty()) { |
272 addIndented("var "); | 266 addIndented("var "); |
273 while (true) { | 267 buffer.add(Strings.join(delayedVariablesDeclaration, ', ')); |
274 buffer.add(delayedVarDecl.head); | |
275 delayedVarDecl = delayedVarDecl.tail; | |
276 if (delayedVarDecl.isEmpty()) break; | |
277 buffer.add(", "); | |
278 } | |
279 buffer.add(";\n"); | 268 buffer.add(";\n"); |
280 } | 269 } |
281 endGraph(graph); | 270 endGraph(graph); |
282 } | 271 } |
283 | 272 |
284 void visitSubGraph(SubGraph newSubGraph) { | 273 void visitSubGraph(SubGraph newSubGraph) { |
285 SubGraph oldSubGraph = subGraph; | 274 SubGraph oldSubGraph = subGraph; |
286 subGraph = newSubGraph; | 275 subGraph = newSubGraph; |
287 visitBasicBlock(subGraph.start); | 276 visitBasicBlock(subGraph.start); |
288 subGraph = oldSubGraph; | 277 subGraph = oldSubGraph; |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
406 int oldState = generationState; | 395 int oldState = generationState; |
407 generationState = STATE_FIRST_DECLARATION; | 396 generationState = STATE_FIRST_DECLARATION; |
408 visitSubGraph(expressionSubGraph.subExpression); | 397 visitSubGraph(expressionSubGraph.subExpression); |
409 generationState = oldState; | 398 generationState = oldState; |
410 } | 399 } |
411 | 400 |
412 void generateCondition(HBlockInformation condition) { | 401 void generateCondition(HBlockInformation condition) { |
413 generateExpression(condition); | 402 generateExpression(condition); |
414 } | 403 } |
415 | 404 |
416 String temporary(HInstruction instruction) { | |
417 int id = instruction.id; | |
418 String name = names[id]; | |
419 if (name !== null) return name; | |
420 | |
421 String prefix = TEMPORARY_PREFIX; | |
422 if (instruction.sourceElement !== null) { | |
423 Element element = instruction.sourceElement; | |
424 if (element !== null && !element.name.isEmpty()) { | |
425 prefix = element.name.slowToString(); | |
426 // Special case the variable named [TEMPORARY_PREFIX] to allow | |
427 // keeping its name. | |
428 if (prefix == TEMPORARY_PREFIX && !usedNames.contains(prefix)) { | |
429 return newName(id, prefix); | |
430 } | |
431 // If we've never seen that prefix before, try to use it | |
432 // directly. | |
433 if (!prefixes.containsKey(prefix)) { | |
434 // Make sure the variable name does not conflict with our mangling. | |
435 while (usedNames.contains(prefix)) { | |
436 prefix = '${prefix}_'; | |
437 } | |
438 prefixes[prefix] = 0; | |
439 return newName(id, prefix); | |
440 } | |
441 } | |
442 } | |
443 | |
444 name = '${prefix}${prefixes[prefix]++}'; | |
445 while (usedNames.contains(name)) { | |
446 name = '${prefix}${prefixes[prefix]++}'; | |
447 } | |
448 return newName(id, name); | |
449 } | |
450 | |
451 // TODO(floitsch): share more code with [temporary]. | |
452 String freshTemporary() { | |
453 String prefix = TEMPORARY_PREFIX; | |
454 String name = '${prefix}${prefixes[prefix]++}'; | |
455 while (usedNames.contains(name)) { | |
456 name = '${prefix}${prefixes[prefix]++}'; | |
457 } | |
458 String result = JsNames.getValid(name); | |
459 usedNames.add(result); | |
460 return result; | |
461 } | |
462 | |
463 String newName(int id, String name) { | |
464 String result = JsNames.getValid(name); | |
465 names[id] = result; | |
466 usedNames.add(result); | |
467 return result; | |
468 } | |
469 | |
470 /** | 405 /** |
471 * Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET]. | 406 * Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET]. |
472 */ | 407 */ |
473 void visitArguments(List<HInstruction> inputs) { | 408 void visitArguments(List<HInstruction> inputs) { |
474 assert(inputs.length >= HInvoke.ARGUMENTS_OFFSET); | 409 assert(inputs.length >= HInvoke.ARGUMENTS_OFFSET); |
475 buffer.add('('); | 410 buffer.add('('); |
476 for (int i = HInvoke.ARGUMENTS_OFFSET; i < inputs.length; i++) { | 411 for (int i = HInvoke.ARGUMENTS_OFFSET; i < inputs.length; i++) { |
477 if (i != HInvoke.ARGUMENTS_OFFSET) buffer.add(', '); | 412 if (i != HInvoke.ARGUMENTS_OFFSET) buffer.add(', '); |
478 use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); | 413 use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); |
479 } | 414 } |
(...skipping 15 matching lines...) Expand all Loading... | |
495 return (generationState == STATE_DECLARATION || | 430 return (generationState == STATE_DECLARATION || |
496 generationState == STATE_FIRST_DECLARATION); | 431 generationState == STATE_FIRST_DECLARATION); |
497 } | 432 } |
498 | 433 |
499 /** | 434 /** |
500 * Called before writing an expression. | 435 * Called before writing an expression. |
501 * Ensures that expressions are comma spearated. | 436 * Ensures that expressions are comma spearated. |
502 */ | 437 */ |
503 void addExpressionSeparator() { | 438 void addExpressionSeparator() { |
504 if (generationState == STATE_FIRST_DECLARATION) { | 439 if (generationState == STATE_FIRST_DECLARATION) { |
505 buffer.add("var "); | 440 return; |
kasperl
2012/05/31 05:26:03
Maybe change the structure so that you check for S
ngeoffray
2012/05/31 08:12:46
Done.
Lasse Reichstein Nielsen
2012/05/31 08:24:56
Why this change?
Where do you change the state to
ngeoffray
2012/05/31 08:48:22
In declareVariable line 454. I need this change be
| |
506 generationState = STATE_DECLARATION; | |
507 } else if (generationState == STATE_FIRST_EXPRESSION) { | 441 } else if (generationState == STATE_FIRST_EXPRESSION) { |
508 generationState = STATE_EXPRESSION; | 442 generationState = STATE_EXPRESSION; |
509 } else { | 443 } else { |
510 buffer.add(", "); | 444 buffer.add(", "); |
511 } | 445 } |
512 } | 446 } |
513 | 447 |
448 bool isVariableDeclared(String variableName) { | |
449 return declaredVariables.contains(variableName); | |
450 } | |
451 | |
514 void declareVariable(String variableName) { | 452 void declareVariable(String variableName) { |
515 if (isGeneratingExpression()) { | 453 if (isGeneratingExpression()) { |
516 buffer.add(variableName); | 454 if (generationState == STATE_FIRST_DECLARATION) { |
517 if (!isGeneratingDeclaration()) { | 455 generationState = STATE_DECLARATION; |
518 delayedVarDecl = delayedVarDecl.prepend(variableName); | 456 if (!isVariableDeclared(variableName)) { |
457 declaredVariables.add(variableName); | |
458 buffer.add("var "); | |
floitsch
2012/05/30 18:45:03
The 'var' is emitted conditionally. This means tha
ngeoffray
2012/05/31 08:12:46
After the first declaration we must go into STATE_
| |
459 } | |
460 } else if (!isGeneratingDeclaration() | |
461 && !isVariableDeclared(variableName)) { | |
462 delayedVariablesDeclaration.add(variableName); | |
519 } | 463 } |
520 } else { | 464 } else if (!isVariableDeclared(variableName)) { |
465 declaredVariables.add(variableName); | |
521 buffer.add("var "); | 466 buffer.add("var "); |
522 buffer.add(variableName); | |
523 } | 467 } |
468 buffer.add(variableName); | |
524 } | 469 } |
525 | 470 |
526 void declareInstruction(HInstruction instruction) { | 471 void declareInstruction(HInstruction instruction) { |
527 declaredInstructions.add(instruction); | 472 declareVariable(variableNames.getName(instruction)); |
528 String name = temporary(instruction); | |
529 declareVariable(name); | |
530 } | |
531 | |
532 bool needsNewVariable(HInstruction instruction) { | |
533 bool needsVar = !instruction.usedBy.isEmpty(); | |
534 if (needsVar && instruction is HCheck) { | |
535 HCheck check = instruction; | |
536 HInstruction input = check.checkedInput; | |
537 // We only need a new var if [input] is generated at use site | |
538 // but is not a trivial code motion invariant instruction like | |
539 // for parameters or this. | |
540 // | |
541 // For example: | |
542 // Foo a = this; | |
543 // print(a); | |
544 // print(a); | |
545 // | |
546 // In checked mode no new variable is needed. | |
547 // FooTypeCheck(this); | |
548 // print(this); | |
549 // print(this); | |
550 // | |
551 // But for this example: | |
552 // Foo a = foo(); | |
553 // print(a); | |
554 // print(a); | |
555 // | |
556 // We need a new variable: | |
557 // var a = FooTypeCheck(foo()); | |
558 // print(a); | |
559 // print(a); | |
560 needsVar = isGenerateAtUseSite(input) && !input.isCodeMotionInvariant(); | |
561 } | |
562 return needsVar; | |
563 } | 473 } |
564 | 474 |
565 void define(HInstruction instruction) { | 475 void define(HInstruction instruction) { |
566 if (needsNewVariable(instruction)) { | 476 if (instruction is !HCheck && variableNames.hasName(instruction)) { |
567 declareInstruction(instruction); | 477 declareInstruction(instruction); |
568 buffer.add(" = "); | 478 buffer.add(" = "); |
569 visit(instruction, JSPrecedence.ASSIGNMENT_PRECEDENCE); | 479 visit(instruction, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
570 } else { | 480 } else { |
571 visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE); | 481 visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE); |
572 } | 482 } |
573 } | 483 } |
574 | 484 |
575 void use(HInstruction argument, int expectedPrecedenceForArgument) { | 485 void use(HInstruction argument, int expectedPrecedenceForArgument) { |
576 if (argument is HCheck) { | 486 if (isGenerateAtUseSite(argument)) { |
577 HCheck instruction = argument; | |
578 HInstruction input = instruction.checkedInput; | |
579 if (isGenerateAtUseSite(argument) && isGenerateAtUseSite(input)) { | |
580 // If both instructions can be generated at use site, we can | |
581 // just visit [argument]. | |
582 // | |
583 // For example: | |
584 // Foo a = foo(); | |
585 // print(a); | |
586 // | |
587 // In checked mode will turn into: | |
588 // print(FooTypeCheck(foo())); | |
589 visit(argument, expectedPrecedenceForArgument); | |
590 } else if (isGenerateAtUseSite(input)) { | |
591 // Get the real input of that checked instruction. | |
592 while (input is HCheck) { | |
593 HCheck check = input; | |
594 input = check.checkedInput; | |
595 } | |
596 | |
597 // If [argument] cannot be generated at use site, but [input] | |
598 // can, use the temporary of [argument]. A code motion | |
599 // invariant instruction does not have a temporary, so we just | |
600 // | |
601 // For example: | |
602 // Foo a = foo(); | |
603 // print(a); | |
604 // print(a); | |
605 // | |
606 // In checked mode will turn into: | |
607 // var a = FooTypeCheck(foo()); | |
608 // print(a); | |
609 // print(a); | |
610 // | |
611 // Note that in case the input is code motion invariant, like | |
612 // for parameters or this, we just need to visit it, since | |
613 // there is no temporary for such instruction. | |
614 if (input.isCodeMotionInvariant()) { | |
615 visit(input, expectedPrecedenceForArgument); | |
616 } else { | |
617 buffer.add(temporary(argument)); | |
618 } | |
619 } else { | |
620 // Otherwise we just use [input]. [argument] has already been | |
621 // emitted, and we just need the temporary of [input]. | |
622 // | |
623 // For example: | |
624 // var a = foo(); | |
625 // print(a); | |
626 // Foo b = a; | |
627 // print(b); | |
628 // | |
629 // In checked mode will turn into: | |
630 // var a = foo(); | |
631 // print(a); | |
632 // FooTypeCheck(a); | |
633 // print(a); | |
634 use(input, expectedPrecedenceForArgument); | |
635 } | |
636 } else if (isGenerateAtUseSite(argument)) { | |
637 visit(argument, expectedPrecedenceForArgument); | 487 visit(argument, expectedPrecedenceForArgument); |
488 } else if (argument is HCheck) { | |
489 HCheck check = argument; | |
490 use(argument.checkedInput, expectedPrecedenceForArgument); | |
638 } else { | 491 } else { |
639 buffer.add(temporary(argument)); | 492 buffer.add(variableNames.getName(argument)); |
640 } | 493 } |
641 } | 494 } |
642 | 495 |
643 visit(HInstruction node, int expectedPrecedenceForNode) { | 496 visit(HInstruction node, int expectedPrecedenceForNode) { |
644 int oldPrecedence = this.expectedPrecedence; | 497 int oldPrecedence = this.expectedPrecedence; |
645 this.expectedPrecedence = expectedPrecedenceForNode; | 498 this.expectedPrecedence = expectedPrecedenceForNode; |
646 node.accept(this); | 499 node.accept(this); |
647 this.expectedPrecedence = oldPrecedence; | 500 this.expectedPrecedence = oldPrecedence; |
648 } | 501 } |
649 | 502 |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
717 | 570 |
718 bool visitTryInfo(HTryBlockInformation info) { | 571 bool visitTryInfo(HTryBlockInformation info) { |
719 addIndented("try {\n"); | 572 addIndented("try {\n"); |
720 indent++; | 573 indent++; |
721 generateStatements(info.body); | 574 generateStatements(info.body); |
722 indent--; | 575 indent--; |
723 addIndented("}"); | 576 addIndented("}"); |
724 if (info.catchBlock !== null) { | 577 if (info.catchBlock !== null) { |
725 // Printing the catch part. | 578 // Printing the catch part. |
726 HParameterValue exception = info.catchVariable; | 579 HParameterValue exception = info.catchVariable; |
727 String name = temporary(exception); | 580 String name = variableNames.getName(exception); |
728 parameterNames[exception.element] = name; | 581 parameterNames[exception.element] = name; |
729 buffer.add(' catch ($name) {\n'); | 582 buffer.add(' catch ($name) {\n'); |
730 indent++; | 583 indent++; |
731 generateStatements(info.catchBlock); | 584 generateStatements(info.catchBlock); |
732 parameterNames.remove(exception.element); | 585 parameterNames.remove(exception.element); |
733 indent--; | 586 indent--; |
734 addIndented('}'); | 587 addIndented('}'); |
735 } | 588 } |
736 if (info.finallyBlock != null) { | 589 if (info.finallyBlock != null) { |
737 buffer.add(" finally {\n"); | 590 buffer.add(" finally {\n"); |
(...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1018 return; | 871 return; |
1019 } | 872 } |
1020 // Flow based traversal. | 873 // Flow based traversal. |
1021 if (node.isLoopHeader() && | 874 if (node.isLoopHeader() && |
1022 node.loopInformation.loopBlockInformation !== currentBlockInformation) { | 875 node.loopInformation.loopBlockInformation !== currentBlockInformation) { |
1023 beginLoop(node); | 876 beginLoop(node); |
1024 } | 877 } |
1025 iterateBasicBlock(node); | 878 iterateBasicBlock(node); |
1026 } | 879 } |
1027 | 880 |
1028 /** Generates the assignments for all phis of successors blocks. */ | 881 void emitAssignment(String source, String destination) { |
1029 void assignPhisOfAllSuccessors(HBasicBlock node) { | 882 if (isGeneratingExpression()) { |
1030 Map<HInstruction, String> temporaryNamesOfPhis = null; | 883 addExpressionSeparator(); |
884 } else { | |
885 addIndentation(); | |
886 } | |
887 declareVariable(destination); | |
888 buffer.add(' = $source'); | |
889 if (!isGeneratingExpression()) { | |
890 buffer.add(';\n'); | |
891 } | |
892 } | |
1031 | 893 |
1032 /** | 894 /** |
1033 * Generates the assignment [canonicalPhi] = [value]. | 895 * Sequentialize a list of parallel copies. |
Lasse Reichstein Nielsen
2012/05/31 08:24:56
Describe which criteria are being used to do the o
ngeoffray
2012/05/31 08:48:22
Added the 'conceptually'. As discussed, kept the '
| |
1034 * | 896 */ |
1035 * If the [canonicalPhi] has a temporary name (in [temporaryNamesOfPhis]) | 897 void sequentializeCopies(List<Copy> copies) { |
1036 * then the temporary is assigned instead of the [canonicalPhi]. However, | 898 // Map to keep track of the current location of a variable. |
1037 * when the left and right-hand side are equal ([:canonicalPhi === value:]) | 899 Map<String, String> currentLocation = new Map<String, String>(); |
Lasse Reichstein Nielsen
2012/05/31 08:24:56
What is a "location" here?
ngeoffray
2012/05/31 08:48:22
It is a variable. It really is "the variable that
| |
1038 * then the [canonicalPhi] is assigned with the temporary. | 900 |
1039 */ | 901 // Map to keep track of the initial value of a variable. |
1040 void generateAssignment(HPhi canonicalPhi, HInstruction value) { | 902 Map<String, String> initialValue = new Map<String, String>(); |
903 | |
904 // List of variables to assign a value. | |
905 List<String> todo = <String>[]; | |
Lasse Reichstein Nielsen
2012/05/31 08:24:56
"todo" -> "worklist".
ngeoffray
2012/05/31 08:48:22
Done.
| |
906 | |
907 // List of variables that we can assign a value to (ie are not | |
Lasse Reichstein Nielsen
2012/05/31 08:24:56
Describe which criteria are being used to do the o
ngeoffray
2012/05/31 08:48:22
Done.
| |
908 // being used anymore). | |
909 List<String> ready = <String>[]; | |
910 | |
911 // Prune [copies] by removing self-copies. | |
912 List<Copy> prunedCopies = <Copy>[]; | |
913 for (Copy copy in copies) { | |
914 String sourceName = variableNames.getName(copy.source); | |
915 String destinationName = variableNames.getName(copy.destination); | |
916 if (sourceName != destinationName) { | |
917 prunedCopies.add(new Copy(sourceName, destinationName)); | |
918 } | |
919 } | |
920 copies = prunedCopies; | |
921 | |
922 | |
923 // For each copy, set the current location of the source to | |
924 // itself, and the initial value of the destination to the source. | |
925 // Add the destination to the list of copies to make. | |
926 for (Copy copy in copies) { | |
927 currentLocation[copy.source] = copy.source; | |
928 initialValue[copy.destination] = copy.source; | |
929 todo.add(copy.destination); | |
930 } | |
931 | |
932 // For each copy, if the destination does not have a current | |
933 // location, then we can safely assign to it. | |
934 for (Copy copy in copies) { | |
935 if (currentLocation[copy.destination] === null) { | |
936 ready.add(copy.destination); | |
937 } | |
938 } | |
939 | |
940 while (!todo.isEmpty()) { | |
941 while (!ready.isEmpty()) { | |
942 String destination = ready.removeLast(); | |
943 String source = initialValue[destination]; | |
944 // Since [source] might have been updated, use the current | |
945 // location of [source] | |
946 String copy = currentLocation[source]; | |
947 emitAssignment(copy, destination); | |
floitsch
2012/05/30 18:45:03
I find it easier to read if the arguments are inve
ngeoffray
2012/05/31 08:12:46
Done.
| |
948 // Now [destination] is the current location of [source]. | |
949 currentLocation[source] = destination; | |
950 // If [source] hasn't been updated and needs to have a value, | |
951 // add it to the list of variables that can be updated. Copies | |
952 // of [source] will now use [destination]. | |
953 if (source == copy && initialValue[source] !== null) { | |
954 ready.add(source); | |
955 } | |
956 } | |
957 | |
958 // Check if we have a cycle. | |
959 String current = todo.removeLast(); | |
960 // If [current] is the source of a copy, and the current | |
961 // location of its initial value is not itself, then there is a | |
962 // cycle that we break by using a temporary name. | |
963 if (currentLocation[current] !== null | |
floitsch
2012/05/30 18:45:03
Is this test necessary?
Clearly: current == curre
ngeoffray
2012/05/31 08:12:46
Yes, it is. Because I don't remove a variable that
| |
964 && current != currentLocation[initialValue[current]]) { | |
965 String tempName = VariableNames.SWAP_TEMP; | |
966 emitAssignment(current, tempName); | |
967 currentLocation[current] = tempName; | |
968 // [current] can now be safely updated. Copies of [current] | |
969 // will now use [tempName]. | |
970 ready.add(current); | |
971 } | |
972 } | |
973 } | |
974 | |
975 void assignPhisOfSuccessors(HBasicBlock node) { | |
976 CopyHandler handler = variableNames.getCopies(node); | |
977 if (handler == null) return; | |
978 | |
979 sequentializeCopies(handler.copies); | |
980 | |
981 for (Copy copy in handler.assignments) { | |
1041 if (isGeneratingExpression()) { | 982 if (isGeneratingExpression()) { |
1042 addExpressionSeparator(); | 983 addExpressionSeparator(); |
1043 } else { | 984 } else { |
1044 addIndentation(); | 985 addIndentation(); |
1045 } | 986 } |
1046 if (temporaryNamesOfPhis !== null && | 987 declareVariable(variableNames.getName(copy.destination)); |
1047 canonicalPhi !== value && | 988 buffer.add(' = '); |
1048 temporaryNamesOfPhis.containsKey(canonicalPhi)) { | 989 use(copy.source, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
1049 // This is the assignment to the temporary. | |
1050 declareVariable(temporaryNamesOfPhis[canonicalPhi]); | |
1051 } else if (!declaredInstructions.contains(canonicalPhi)) { | |
1052 declareInstruction(canonicalPhi); | |
1053 } else { | |
1054 buffer.add(temporary(canonicalPhi)); | |
1055 } | |
1056 buffer.add(" = "); | |
1057 bool isLogicalOperation = logicalOperations.containsKey(canonicalPhi); | |
1058 if (isLogicalOperation) { | |
1059 emitLogicalOperation(canonicalPhi, logicalOperations[canonicalPhi]); | |
1060 } else if (canonicalPhi === value) { | |
1061 buffer.add(temporaryNamesOfPhis[value]); | |
1062 } else { | |
1063 use(value, JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
1064 } | |
1065 if (!isGeneratingExpression()) { | 990 if (!isGeneratingExpression()) { |
1066 buffer.add(';\n'); | 991 buffer.add(';\n'); |
1067 } | 992 } |
1068 } | 993 } |
1069 | |
1070 // Assignments are delayed so that we don't overwrite phis that might | |
1071 // be used as inputs. | |
1072 // TODO(floitsch): improve phi assignments. Currently we introduce | |
1073 // way too many temporary variables. | |
1074 Map<HPhi, HInstruction> phiAssignments = new Map<HPhi, HInstruction>(); | |
1075 | |
1076 for (HBasicBlock successor in node.successors) { | |
1077 int index = successor.predecessors.indexOf(node); | |
1078 successor.forEachPhi((HPhi phi) { | |
1079 bool isLogicalOperation = logicalOperations.containsKey(phi); | |
1080 // In case the phi is being generated by another | |
1081 // instruction. | |
1082 if (isLogicalOperation && isGenerateAtUseSite(phi)) return; | |
1083 HPhi canonicalPhi = phiEquivalence.getRepresentative(phi); | |
1084 assert(!isLogicalOperation || canonicalPhi === phi); | |
1085 HInstruction input = phi.inputs[index]; | |
1086 if (input is HPhi) { | |
1087 input = phiEquivalence.getRepresentative(input); | |
1088 // If we use the same variable, we don't need to create an | |
1089 // assignment. | |
1090 if (input === canonicalPhi) { | |
1091 assert(!isLogicalOperation); | |
1092 return; | |
1093 } | |
1094 } | |
1095 phiAssignments[canonicalPhi] = input; | |
1096 }); | |
1097 } | |
1098 | |
1099 Set<HPhi> inputPhis = new Set<HPhi>(); | |
1100 List<HPhi> phis = <HPhi>[]; | |
1101 | |
1102 /** | |
1103 * Transitively collects the phis that are used when emitting the [input] | |
1104 * and adds them to [inputPhis]. Does not add phis that are equal to the | |
1105 * [targetPhi] or are not in the [phiAssignments] map. | |
1106 */ | |
1107 void collectInputPhis(HInstruction input, HPhi targetPhi) { | |
1108 if (input is HPhi) { | |
1109 HPhi canonicalPhi = phiEquivalence.getRepresentative(input); | |
1110 // Self-updates are ok. | |
1111 if (canonicalPhi !== targetPhi && | |
1112 phiAssignments.containsKey(canonicalPhi)) { | |
1113 inputPhis.add(canonicalPhi); | |
1114 } | |
1115 } else if (isGenerateAtUseSite(input)) { | |
1116 for (HInstruction inputOfInput in input.inputs) { | |
1117 collectInputPhis(inputOfInput, targetPhi); | |
1118 } | |
1119 } | |
1120 } | |
1121 | |
1122 phiAssignments.forEach((HPhi targetPhi, HInstruction input) { | |
1123 phis.add(targetPhi); | |
1124 collectInputPhis(input, targetPhi); | |
1125 }); | |
1126 | |
1127 if (inputPhis.isEmpty()) { | |
1128 phiAssignments.forEach(generateAssignment); | |
1129 } else { | |
1130 // Emit the phiX=phiY assignments, taking care to break cycles. | |
1131 // Example program: | |
1132 // var x = 499; | |
1133 // var y = 99; | |
1134 // while (x != 99) { | |
1135 // var tmp = x; // <=== The tmp variable is removed in Ssa-form. | |
1136 // x = y; | |
1137 // y = tmp; | |
1138 // } | |
1139 // | |
1140 temporaryNamesOfPhis = new HashMap<HInstruction, String>(); | |
1141 // For phis that are used as inputs simply *always* allocate a | |
1142 // temporary. | |
1143 for (HPhi phi in phis) { | |
1144 if (inputPhis.contains(phi)) { | |
1145 String temporaryPhi = freshTemporary(); | |
1146 temporaryNamesOfPhis[phi] = temporaryPhi; | |
1147 } | |
1148 // The assignPhi uses temporary variables for the left-hand side if | |
1149 // they exist. | |
1150 generateAssignment(phi, phiAssignments[phi]); | |
1151 } | |
1152 // Finally assign the input phis. | |
1153 for (HPhi phi in inputPhis) { | |
1154 // The assignPhi special-cases phi assignments to itself and recognizes | |
1155 // it as assignment from the temporary variable to the actual phi. | |
1156 generateAssignment(phi, phi); | |
1157 } | |
1158 } | |
1159 } | 994 } |
1160 | 995 |
1161 void iterateBasicBlock(HBasicBlock node) { | 996 void iterateBasicBlock(HBasicBlock node) { |
1162 HInstruction instruction = node.first; | 997 HInstruction instruction = node.first; |
1163 while (instruction != null) { | 998 while (instruction != null) { |
1164 if (instruction === node.last) { | 999 if (instruction === node.last) { |
1165 assignPhisOfAllSuccessors(node); | 1000 assignPhisOfSuccessors(node); |
1166 } | 1001 } |
1167 | 1002 |
1168 if (isGenerateAtUseSite(instruction)) { | 1003 if (isGenerateAtUseSite(instruction)) { |
1169 if (instruction is HIf) { | 1004 if (instruction is HIf) { |
1170 HIf hif = instruction; | 1005 HIf hif = instruction; |
1171 // The "if" is implementing part of a logical expression. | 1006 // The "if" is implementing part of a logical expression. |
1172 // Skip directly forward to to its latest successor, since everything | 1007 // Skip directly forward to to its latest successor, since everything |
1173 // in-between must also be generateAtUseSite. | 1008 // in-between must also be generateAtUseSite. |
1174 assert(hif.trueBranch.id < hif.falseBranch.id); | 1009 assert(hif.trueBranch.id < hif.falseBranch.id); |
1175 visitBasicBlock(hif.falseBranch); | 1010 visitBasicBlock(hif.falseBranch); |
(...skipping 408 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1584 visitFieldSet(HFieldSet node) { | 1419 visitFieldSet(HFieldSet node) { |
1585 String name; | 1420 String name; |
1586 if (!node.isFromActivation()) { | 1421 if (!node.isFromActivation()) { |
1587 name = | 1422 name = |
1588 compiler.namer.instanceFieldName(currentLibrary, node.name); | 1423 compiler.namer.instanceFieldName(currentLibrary, node.name); |
1589 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); | 1424 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); |
1590 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); | 1425 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); |
1591 buffer.add('.'); | 1426 buffer.add('.'); |
1592 buffer.add(name); | 1427 buffer.add(name); |
1593 } else { | 1428 } else { |
1594 // TODO(ngeoffray): Remove the 'var' once we don't globally box | |
1595 // variables used in a try/catch. | |
1596 if (!isGeneratingExpression()) { | |
1597 buffer.add('var '); | |
1598 } | |
1599 use(node.receiver, JSPrecedence.EXPRESSION_PRECEDENCE); | 1429 use(node.receiver, JSPrecedence.EXPRESSION_PRECEDENCE); |
1600 } | 1430 } |
1601 buffer.add(' = '); | 1431 buffer.add(' = '); |
1602 use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE); | 1432 use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
1603 if (node.receiver !== null) { | 1433 if (node.receiver !== null) { |
1604 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); | 1434 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); |
1605 } | 1435 } |
1606 } | 1436 } |
1607 | 1437 |
1608 visitForeign(HForeign node) { | 1438 visitForeign(HForeign node) { |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1737 } else { | 1567 } else { |
1738 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); | 1568 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); |
1739 buffer.add('!'); | 1569 buffer.add('!'); |
1740 use(input, JSPrecedence.PREFIX_PRECEDENCE); | 1570 use(input, JSPrecedence.PREFIX_PRECEDENCE); |
1741 endExpression(JSPrecedence.PREFIX_PRECEDENCE); | 1571 endExpression(JSPrecedence.PREFIX_PRECEDENCE); |
1742 } | 1572 } |
1743 } | 1573 } |
1744 | 1574 |
1745 visitParameterValue(HParameterValue node) { | 1575 visitParameterValue(HParameterValue node) { |
1746 assert(isGenerateAtUseSite(node)); | 1576 assert(isGenerateAtUseSite(node)); |
1747 if (parameterNames[node.element] == null) { | 1577 buffer.add(variableNames.getName(node)); |
1748 buffer.add(temporary(node)); | |
1749 } else { | |
1750 buffer.add(parameterNames[node.element]); | |
1751 } | |
1752 } | 1578 } |
1753 | 1579 |
1754 visitPhi(HPhi node) { | 1580 visitPhi(HPhi node) { |
1755 String operation = logicalOperations[node]; | 1581 String operation = logicalOperations[node]; |
1756 if (operation !== null) { | 1582 if (operation !== null) { |
1757 emitLogicalOperation(node, operation); | 1583 emitLogicalOperation(node, operation); |
1758 } else { | 1584 } else { |
1759 HPhi canonicalPhi = phiEquivalence.getRepresentative(node); | 1585 buffer.add('${variableNames.getName(node)}'); |
1760 buffer.add('${temporary(canonicalPhi)}'); | |
1761 } | 1586 } |
1762 } | 1587 } |
1763 | 1588 |
1764 visitReturn(HReturn node) { | 1589 visitReturn(HReturn node) { |
1765 addIndentation(); | 1590 addIndentation(); |
1766 assert(node.inputs.length == 1); | 1591 assert(node.inputs.length == 1); |
1767 HInstruction input = node.inputs[0]; | 1592 HInstruction input = node.inputs[0]; |
1768 if (input.isConstantNull()) { | 1593 if (input.isConstantNull()) { |
1769 buffer.add('return;\n'); | 1594 buffer.add('return;\n'); |
1770 } else { | 1595 } else { |
(...skipping 702 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2473 void visitTypeGuard(HTypeGuard node) { | 2298 void visitTypeGuard(HTypeGuard node) { |
2474 indent--; | 2299 indent--; |
2475 addIndented('case ${node.state}:\n'); | 2300 addIndented('case ${node.state}:\n'); |
2476 indent++; | 2301 indent++; |
2477 addIndented('state = 0;\n'); | 2302 addIndented('state = 0;\n'); |
2478 | 2303 |
2479 setup.add(' case ${node.state}:\n'); | 2304 setup.add(' case ${node.state}:\n'); |
2480 int i = 0; | 2305 int i = 0; |
2481 for (HInstruction input in node.inputs) { | 2306 for (HInstruction input in node.inputs) { |
2482 HInstruction instruction = unwrap(input); | 2307 HInstruction instruction = unwrap(input); |
2483 setup.add(' ${temporary(instruction)} = env$i;\n'); | 2308 setup.add(' ${variableNames.getName(instruction)} = env$i;\n'); |
2484 i++; | 2309 i++; |
2485 } | 2310 } |
2486 if (i > maxBailoutParameters) maxBailoutParameters = i; | 2311 if (i > maxBailoutParameters) maxBailoutParameters = i; |
2487 setup.add(' break;\n'); | 2312 setup.add(' break;\n'); |
2488 } | 2313 } |
2489 | 2314 |
2490 void startBailoutCase(List<HTypeGuard> bailouts1, | 2315 void startBailoutCase(List<HTypeGuard> bailouts1, |
2491 List<HTypeGuard> bailouts2) { | 2316 List<HTypeGuard> bailouts2) { |
2492 indent--; | 2317 indent--; |
2493 handleBailoutCase(bailouts1); | 2318 handleBailoutCase(bailouts1); |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2632 startBailoutSwitch(); | 2457 startBailoutSwitch(); |
2633 } | 2458 } |
2634 } | 2459 } |
2635 | 2460 |
2636 void endLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { | 2461 void endLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { |
2637 if (labeledBlockInfo.body.start.hasGuards()) { | 2462 if (labeledBlockInfo.body.start.hasGuards()) { |
2638 endBailoutSwitch(); | 2463 endBailoutSwitch(); |
2639 } | 2464 } |
2640 } | 2465 } |
2641 } | 2466 } |
OLD | NEW |