Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library dart_tree; | 5 library dart_tree; |
| 6 | 6 |
| 7 import '../dart2jslib.dart' as dart2js; | 7 import '../dart2jslib.dart' as dart2js; |
| 8 import '../elements/elements.dart' | 8 import '../elements/elements.dart' |
| 9 show Element, FunctionElement, FunctionSignature, ParameterElement, | 9 show Element, FunctionElement, FunctionSignature, ParameterElement, |
| 10 ClassElement; | 10 ClassElement; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 76 int breakCount = 0; | 76 int breakCount = 0; |
| 77 | 77 |
| 78 /// The [LabeledStatement] binding this label. | 78 /// The [LabeledStatement] binding this label. |
| 79 LabeledStatement binding; | 79 LabeledStatement binding; |
| 80 } | 80 } |
| 81 | 81 |
| 82 /** | 82 /** |
| 83 * Variables are [Expression]s. | 83 * Variables are [Expression]s. |
| 84 */ | 84 */ |
| 85 class Variable extends Expression { | 85 class Variable extends Expression { |
| 86 // A counter used to generate names. The counter is reset to 0 for each | 86 /// Element used for synthesizing a name for the variable. |
| 87 // function emitted. | 87 /// Different variables may have the same element. May be null. |
| 88 static int counter = 0; | 88 Element element; |
| 89 static String _newName() => 'v${counter++}'; | |
| 90 | 89 |
| 91 Element element; | 90 int readCount = 0; |
| 92 String cachedName; | |
| 93 | |
| 94 String get name { | |
| 95 if (cachedName != null) return cachedName; | |
| 96 return cachedName = ((element == null) ? _newName() : element.name); | |
| 97 } | |
| 98 | 91 |
| 99 Variable(this.element); | 92 Variable(this.element); |
| 100 | 93 |
| 101 accept(Visitor visitor) => visitor.visitVariable(this); | 94 accept(Visitor visitor) => visitor.visitVariable(this); |
| 102 } | 95 } |
| 103 | 96 |
| 104 /** | 97 /** |
| 105 * Common interface for invocations with arguments. | 98 * Common interface for invocations with arguments. |
| 106 */ | 99 */ |
| 107 abstract class Invoke { | 100 abstract class Invoke { |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 264 /** | 257 /** |
| 265 * An assignments of an [Expression] to a [Variable]. | 258 * An assignments of an [Expression] to a [Variable]. |
| 266 * | 259 * |
| 267 * In contrast to the CPS-based IR, non-primitive expressions can be assigned | 260 * In contrast to the CPS-based IR, non-primitive expressions can be assigned |
| 268 * to variables. | 261 * to variables. |
| 269 */ | 262 */ |
| 270 class Assign extends Statement { | 263 class Assign extends Statement { |
| 271 Statement next; | 264 Statement next; |
| 272 final Variable variable; | 265 final Variable variable; |
| 273 Expression definition; | 266 Expression definition; |
| 274 final bool hasExactlyOneUse; | |
| 275 | 267 |
| 276 Assign(this.variable, this.definition, this.next, this.hasExactlyOneUse); | 268 Assign(this.variable, this.definition, this.next); |
| 269 | |
| 270 bool get hasExactlyOneUse => variable.readCount == 1; | |
| 277 | 271 |
| 278 accept(Visitor visitor) => visitor.visitAssign(this); | 272 accept(Visitor visitor) => visitor.visitAssign(this); |
| 279 } | 273 } |
| 280 | 274 |
| 281 /** | 275 /** |
| 282 * A return exit from the function. | 276 * A return exit from the function. |
| 283 * | 277 * |
| 284 * In contrast to the CPS-based IR, the return value is an arbitrary | 278 * In contrast to the CPS-based IR, the return value is an arbitrary |
| 285 * expression. | 279 * expression. |
| 286 */ | 280 */ |
| 287 class Return extends Statement { | 281 class Return extends Statement { |
| 288 /// Should not be null. Use [Constant] with [NullConstant] for void returns. | 282 /// Should not be null. Use [Constant] with [NullConstant] for void returns. |
| 289 Expression value; | 283 Expression value; |
| 290 | 284 |
| 291 Statement get next => null; | 285 Statement get next => null; |
| 292 void set next(Statement s) => throw 'UNREACHABLE'; | 286 void set next(Statement s) => throw 'UNREACHABLE'; |
| 293 | 287 |
| 294 Return(this.value); | 288 Return(this.value); |
| 295 | 289 |
| 296 accept(Visitor visitor) => visitor.visitReturn(this); | 290 accept(Visitor visitor) => visitor.visitReturn(this); |
| 297 } | 291 } |
| 298 | 292 |
| 299 /** | 293 /** |
| 300 * A break from an enclosing [LabeledStatement]. The break targets the | 294 * A break from an enclosing [LabeledStatement]. The break targets the |
| 301 * labeled statement's successor statement. | 295 * labeled statement's successor statement. |
| 302 */ | 296 */ |
| 303 class Break extends Statement { | 297 class Break extends Statement { |
| 304 Label _target; | 298 Label target; |
| 305 | |
| 306 Label get target => _target; | |
| 307 void set target(Label newTarget) { | |
| 308 ++newTarget.breakCount; | |
| 309 --_target.breakCount; | |
| 310 _target = newTarget; | |
| 311 } | |
| 312 | 299 |
| 313 Statement get next => null; | 300 Statement get next => null; |
| 314 void set next(Statement s) => throw 'UNREACHABLE'; | 301 void set next(Statement s) => throw 'UNREACHABLE'; |
| 315 | 302 |
| 316 Break(this._target) { | 303 Break(this.target) { |
| 317 ++target.breakCount; | 304 ++target.breakCount; |
| 318 } | 305 } |
| 319 | 306 |
| 320 accept(Visitor visitor) => visitor.visitBreak(this); | 307 accept(Visitor visitor) => visitor.visitBreak(this); |
| 321 } | 308 } |
| 322 | 309 |
| 323 /** | 310 /** |
| 324 * A continue to an enclosing [While] loop. The continue targets the | 311 * A continue to an enclosing [While] loop. The continue targets the |
| 325 * loop's body. | 312 * loop's body. |
| 326 */ | 313 */ |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 437 * translation out of SSA. Jumps are eliminated during the Tree-to-Tree | 424 * translation out of SSA. Jumps are eliminated during the Tree-to-Tree |
| 438 * control-flow recognition. | 425 * control-flow recognition. |
| 439 * | 426 * |
| 440 * Otherwise, the output of Builder looks very much like the input. In | 427 * Otherwise, the output of Builder looks very much like the input. In |
| 441 * particular, intermediate values and blocks used for local control flow are | 428 * particular, intermediate values and blocks used for local control flow are |
| 442 * still all named. | 429 * still all named. |
| 443 */ | 430 */ |
| 444 class Builder extends ir.Visitor<Node> { | 431 class Builder extends ir.Visitor<Node> { |
| 445 final dart2js.Compiler compiler; | 432 final dart2js.Compiler compiler; |
| 446 | 433 |
| 447 // Uses of IR primitives are replaced with Tree variables. This is the | 434 /// Maps variable/parameter elements to the Tree variables that represent it. |
| 448 // mapping from primitives to variables. | 435 final Map<Element, List<Variable>> element2variables = |
| 449 final Map<ir.Primitive, Variable> variables = <ir.Primitive, Variable>{}; | 436 <Element,List<Variable>>{}; |
| 450 | 437 |
| 451 // Continuations with more than one use are replaced with Tree labels. This | 438 // Continuations with more than one use are replaced with Tree labels. This |
| 452 // is the mapping from continuations to labels. | 439 // is the mapping from continuations to labels. |
| 453 final Map<ir.Continuation, Label> labels = <ir.Continuation, Label>{}; | 440 final Map<ir.Continuation, Label> labels = <ir.Continuation, Label>{}; |
| 454 | 441 |
| 455 FunctionDefinition function; | 442 FunctionDefinition function; |
| 456 ir.Continuation returnContinuation; | 443 ir.Continuation returnContinuation; |
| 457 | 444 |
| 458 Builder(this.compiler); | 445 Builder(this.compiler); |
| 459 | 446 |
| 447 /// Obtains the variable representing the given primitive. Returns null for | |
| 448 /// primitives that have no reference and do not need a variable. | |
| 449 Variable getVariable(ir.Primitive primitive) { | |
| 450 if (primitive.registerIndex == null) { | |
| 451 return null; // variable is unused | |
| 452 } | |
| 453 List<Variable> variables = element2variables[primitive.element]; | |
| 454 if (variables == null) { | |
| 455 variables = <Variable>[]; | |
| 456 element2variables[primitive.element] = variables; | |
| 457 } | |
| 458 while (variables.length <= primitive.registerIndex) { | |
| 459 variables.add(new Variable(primitive.element)); | |
| 460 } | |
| 461 return variables[primitive.registerIndex]; | |
| 462 } | |
| 463 | |
| 464 /// Obtains a reference to the tree Variable corresponding to the IR primitive | |
| 465 /// referred to by [reference]. | |
| 466 /// This increments the reference count for the given variable, so the | |
| 467 /// returned expression must be used in the tree. | |
| 468 Expression getVariableReference(ir.Reference reference) { | |
| 469 Variable variable = getVariable(reference.definition); | |
| 470 if (variable == null) { | |
| 471 compiler.internalError( | |
| 472 compiler.currentElement, | |
| 473 "Reference to ${reference.definition} has no register"); | |
| 474 } | |
| 475 ++variable.readCount; | |
| 476 return variable; | |
| 477 } | |
| 478 | |
| 460 FunctionDefinition build(ir.FunctionDefinition node) { | 479 FunctionDefinition build(ir.FunctionDefinition node) { |
| 480 new ir.RegisterAllocator().visit(node); | |
| 461 visit(node); | 481 visit(node); |
| 462 return function; | 482 return function; |
| 463 } | 483 } |
| 464 | 484 |
| 465 List<Expression> translateArguments(List<ir.Reference> args) { | 485 List<Expression> translateArguments(List<ir.Reference> args) { |
| 466 return new List<Expression>.generate(args.length, | 486 return new List<Expression>.generate(args.length, |
| 467 (int index) => variables[args[index].definition]); | 487 (int index) => getVariableReference(args[index])); |
| 468 } | 488 } |
| 469 | 489 |
| 470 Statement buildParameterAssignments( | 490 List<Variable> translatePhiArguments(List<ir.Reference> args) { |
| 491 return new List<Variable>.generate(args.length, | |
| 492 (int index) => getVariableReference(args[index])); | |
| 493 } | |
| 494 | |
| 495 Statement buildContinuationAssignment( | |
| 496 ir.Parameter parameter, | |
| 497 Expression argument, | |
| 498 Statement buildRest()) { | |
| 499 Variable variable = getVariable(parameter); | |
| 500 Statement assignment; | |
| 501 if (variable == null) { | |
| 502 assignment = new ExpressionStatement(argument, null); | |
| 503 } else { | |
| 504 assignment = new Assign(variable, argument, null); | |
| 505 } | |
| 506 assignment.next = buildRest(); | |
| 507 return assignment; | |
| 508 } | |
| 509 | |
| 510 /// Simultaneously assigns each argument to the corresponding parameter, | |
| 511 /// then continues at the statement created by [buildRest]. | |
| 512 Statement buildPhiAssignments( | |
| 471 List<ir.Parameter> parameters, | 513 List<ir.Parameter> parameters, |
| 472 List<Expression> arguments, | 514 List<Variable> arguments, |
| 473 Statement buildRest()) { | 515 Statement buildRest()) { |
| 474 assert(parameters.length == arguments.length); | 516 assert(parameters.length == arguments.length); |
| 517 // We want a parallel assignment to all parameters simultaneously. | |
| 518 // Since we do not have parallel assignments in dart_tree, we must linearize | |
| 519 // the assignments without attempting to read a previously-overwritten | |
| 520 // value. For example {x,y = y,x} cannot be linearized to {x = y; y = x}, | |
| 521 // for this we must introduce a temporary variable: {t = x; x = y; y = t}. | |
| 522 | |
| 523 // [rightHand] is the inverse of [arguments], that is, it maps variables | |
| 524 // to the assignments on which is occurs as the right-hand side. | |
| 525 Map<Variable, List<int>> rightHand = <Variable, List<int>>{}; | |
| 526 for (int i = 0; i < parameters.length; i++) { | |
| 527 Variable param = getVariable(parameters[i]); | |
| 528 Variable arg = arguments[i]; | |
| 529 if (param == null || param == arg) | |
| 530 continue; // No assignment necessary. | |
| 531 List<int> list = rightHand[arg]; | |
| 532 if (list == null) { | |
| 533 rightHand[arg] = list = <int>[]; | |
| 534 } | |
| 535 list.add(i); | |
| 536 } | |
| 537 | |
| 475 Statement first, current; | 538 Statement first, current; |
| 476 for (int i = 0; i < parameters.length; ++i) { | 539 void addAssignment(Variable dst, Variable src) { |
| 477 ir.Parameter parameter = parameters[i]; | 540 if (first == null) { |
| 478 Statement assignment; | 541 first = current = new Assign(dst, src, null); |
| 479 if (parameter.hasAtLeastOneUse) { | |
| 480 assignment = new Assign(variables[parameter], arguments[i], null, | |
| 481 parameter.hasExactlyOneUse); | |
| 482 } else { | 542 } else { |
| 483 assignment = new ExpressionStatement(arguments[i], null); | 543 current = current.next = new Assign(dst, src, null); |
| 484 } | |
| 485 | |
| 486 if (first == null) { | |
| 487 current = first = assignment; | |
| 488 } else { | |
| 489 current = current.next = assignment; | |
| 490 } | 544 } |
| 491 } | 545 } |
| 492 | 546 |
| 547 Variable temp = new Variable(null); | |
| 548 List<Variable> assignmentSrc = new List<Variable>(parameters.length); | |
| 549 List<bool> done = new List<bool>(parameters.length); | |
| 550 void visitAssignment(int i) { | |
| 551 if (done[i] == true) | |
| 552 return; | |
|
sigurdm
2014/06/12 14:12:19
Can be on one line
asgerf
2014/06/12 15:24:11
Thanks.
| |
| 553 Variable param = getVariable(parameters[i]); | |
| 554 Variable arg = arguments[i]; | |
| 555 if (param == null || param == arg) | |
| 556 return; // No assignment necessary. | |
| 557 if (assignmentSrc[i] != null) { | |
| 558 // Cycle found; store argument in a temporary variable. | |
| 559 // The temporary will then be used as right-hand side when the | |
| 560 // assignment gets added. | |
| 561 if (assignmentSrc[i] != temp) { // Only move to temporary once. | |
| 562 assignmentSrc[i] = temp; | |
| 563 addAssignment(temp, arg); | |
| 564 } | |
| 565 return; | |
| 566 } | |
| 567 assignmentSrc[i] = arg; | |
| 568 List<int> paramUses = rightHand[param]; | |
| 569 if (paramUses != null) { | |
| 570 for (int useIndex in paramUses) { | |
| 571 visitAssignment(useIndex); | |
| 572 } | |
| 573 } | |
| 574 addAssignment(param, assignmentSrc[i]); | |
| 575 done[i] = true; | |
| 576 } | |
| 577 | |
| 578 for (int i = 0; i < parameters.length; i++) { | |
| 579 if (done[i] == null) { | |
| 580 visitAssignment(i); | |
| 581 } | |
| 582 } | |
| 583 | |
| 493 if (first == null) { | 584 if (first == null) { |
| 494 first = buildRest(); | 585 first = buildRest(); |
| 495 } else { | 586 } else { |
| 496 current.next = buildRest(); | 587 current.next = buildRest(); |
| 497 } | 588 } |
| 498 return first; | 589 return first; |
| 499 } | 590 } |
| 500 | 591 |
| 501 Expression visitFunctionDefinition(ir.FunctionDefinition node) { | 592 Expression visitFunctionDefinition(ir.FunctionDefinition node) { |
| 502 returnContinuation = node.returnContinuation; | 593 returnContinuation = node.returnContinuation; |
| 503 List<Variable> parameters = <Variable>[]; | 594 List<Variable> parameters = <Variable>[]; |
| 504 for (ir.Parameter p in node.parameters) { | 595 for (ir.Parameter p in node.parameters) { |
| 505 Variable parameter = new Variable(p.element); | 596 Variable parameter = getVariable(p); |
| 597 assert(parameter != null); | |
| 506 parameters.add(parameter); | 598 parameters.add(parameter); |
| 507 variables[p] = parameter; | |
| 508 } | 599 } |
| 509 function = new FunctionDefinition(parameters, visit(node.body)); | 600 function = new FunctionDefinition(parameters, visit(node.body)); |
| 510 return null; | 601 return null; |
| 511 } | 602 } |
| 512 | 603 |
| 513 Statement visitLetPrim(ir.LetPrim node) { | 604 Statement visitLetPrim(ir.LetPrim node) { |
| 514 // LetPrim is translated to LetVal. | 605 // LetPrim is translated to Assign. |
| 515 Expression definition = visit(node.primitive); | 606 Expression definition = visit(node.primitive); |
| 516 if (node.primitive.hasAtLeastOneUse) { | 607 Variable variable = getVariable(node.primitive); |
| 517 Variable variable = new Variable(null); | 608 if (variable != null) { // Variable is null if primitive is unused. |
| 518 variables[node.primitive] = variable; | 609 return new Assign(variable, definition, visit(node.body)); |
| 519 return new Assign(variable, definition, visit(node.body), | |
| 520 node.primitive.hasExactlyOneUse); | |
| 521 } else if (node.primitive is ir.Constant) { | 610 } else if (node.primitive is ir.Constant) { |
| 522 // TODO(kmillikin): Implement more systematic treatment of pure CPS | 611 // TODO(kmillikin): Implement more systematic treatment of pure CPS |
| 523 // values (e.g., as part of a shrinking reductions pass). | 612 // values (e.g., as part of a shrinking reductions pass). |
| 524 return visit(node.body); | 613 return visit(node.body); |
| 525 } else { | 614 } else { |
| 526 return new ExpressionStatement(definition, visit(node.body)); | 615 return new ExpressionStatement(definition, visit(node.body)); |
| 527 } | 616 } |
| 528 } | 617 } |
| 529 | 618 |
| 530 Statement visitLetCont(ir.LetCont node) { | 619 Statement visitLetCont(ir.LetCont node) { |
| 531 Label label; | 620 Label label; |
| 532 if (node.continuation.hasMultipleUses) { | 621 if (node.continuation.hasMultipleUses) { |
| 533 label = new Label(); | 622 label = new Label(); |
| 534 labels[node.continuation] = label; | 623 labels[node.continuation] = label; |
| 535 } | 624 } |
| 536 node.continuation.parameters.forEach((p) { | |
| 537 if (p.hasAtLeastOneUse) variables[p] = new Variable(null); | |
| 538 }); | |
| 539 Statement body = visit(node.body); | 625 Statement body = visit(node.body); |
| 540 // The continuation's body is not always translated directly here because | 626 // The continuation's body is not always translated directly here because |
| 541 // it may have been already translated: | 627 // it may have been already translated: |
| 542 // * For singly-used continuations, the continuation's body is | 628 // * For singly-used continuations, the continuation's body is |
| 543 // translated at the site of the continuation invocation. | 629 // translated at the site of the continuation invocation. |
| 544 // * For recursive continuations, there is a single non-recursive | 630 // * For recursive continuations, there is a single non-recursive |
| 545 // invocation. The continuation's body is translated at the site | 631 // invocation. The continuation's body is translated at the site |
| 546 // of the non-recursive continuation invocation. | 632 // of the non-recursive continuation invocation. |
| 547 // See visitInvokeContinuation for the implementation. | 633 // See visitInvokeContinuation for the implementation. |
| 548 if (label == null || node.continuation.isRecursive) return body; | 634 if (label == null || node.continuation.isRecursive) return body; |
| 549 return new LabeledStatement(label, body, visit(node.continuation.body)); | 635 return new LabeledStatement(label, body, visit(node.continuation.body)); |
| 550 } | 636 } |
| 551 | 637 |
| 552 Statement visitInvokeStatic(ir.InvokeStatic node) { | 638 Statement visitInvokeStatic(ir.InvokeStatic node) { |
| 553 // Calls are translated to direct style. | 639 // Calls are translated to direct style. |
| 554 List<Expression> arguments = translateArguments(node.arguments); | 640 List<Expression> arguments = translateArguments(node.arguments); |
| 555 Expression invoke = new InvokeStatic(node.target, node.selector, arguments); | 641 Expression invoke = new InvokeStatic(node.target, node.selector, arguments); |
| 556 ir.Continuation cont = node.continuation.definition; | 642 ir.Continuation cont = node.continuation.definition; |
| 557 if (cont == returnContinuation) { | 643 if (cont == returnContinuation) { |
| 558 return new Return(invoke); | 644 return new Return(invoke); |
| 559 } else { | 645 } else { |
| 560 assert(cont.hasExactlyOneUse); | 646 assert(cont.hasExactlyOneUse); |
| 561 assert(cont.parameters.length == 1); | 647 assert(cont.parameters.length == 1); |
| 562 return buildParameterAssignments(cont.parameters, [invoke], | 648 return buildContinuationAssignment(cont.parameters[0], invoke, |
|
sigurdm
2014/06/12 14:12:19
You can use cont.parameters.single, it also assert
asgerf
2014/06/12 15:24:15
single is only defined for Link. cont.parameters i
sigurdm
2014/06/13 07:40:07
No - single is defined on Iterable which List impl
| |
| 563 () => visit(cont.body)); | 649 () => visit(cont.body)); |
| 564 } | 650 } |
| 565 } | 651 } |
| 566 | 652 |
| 567 Statement visitInvokeMethod(ir.InvokeMethod node) { | 653 Statement visitInvokeMethod(ir.InvokeMethod node) { |
| 568 Variable receiver = variables[node.receiver.definition]; | 654 Expression receiver = getVariableReference(node.receiver); |
| 569 List<Expression> arguments = translateArguments(node.arguments); | 655 List<Expression> arguments = translateArguments(node.arguments); |
| 570 Expression invoke = new InvokeMethod(receiver, node.selector, arguments); | 656 Expression invoke = new InvokeMethod(receiver, node.selector, arguments); |
| 571 ir.Continuation cont = node.continuation.definition; | 657 ir.Continuation cont = node.continuation.definition; |
| 572 if (cont == returnContinuation) { | 658 if (cont == returnContinuation) { |
| 573 return new Return(invoke); | 659 return new Return(invoke); |
| 574 } else { | 660 } else { |
| 575 assert(cont.hasExactlyOneUse); | 661 assert(cont.hasExactlyOneUse); |
| 576 assert(cont.parameters.length == 1); | 662 assert(cont.parameters.length == 1); |
| 577 return buildParameterAssignments(cont.parameters, [invoke], | 663 return buildContinuationAssignment(cont.parameters[0], invoke, |
| 578 () => visit(cont.body)); | 664 () => visit(cont.body)); |
| 579 } | 665 } |
| 580 } | 666 } |
| 581 | 667 |
| 582 Statement visitConcatenateStrings(ir.ConcatenateStrings node) { | 668 Statement visitConcatenateStrings(ir.ConcatenateStrings node) { |
| 583 List<Expression> arguments = translateArguments(node.arguments); | 669 List<Expression> arguments = translateArguments(node.arguments); |
| 584 Expression concat = new ConcatenateStrings(arguments); | 670 Expression concat = new ConcatenateStrings(arguments); |
| 585 ir.Continuation cont = node.continuation.definition; | 671 ir.Continuation cont = node.continuation.definition; |
| 586 if (cont == returnContinuation) { | 672 if (cont == returnContinuation) { |
| 587 return new Return(concat); | 673 return new Return(concat); |
| 588 } else { | 674 } else { |
| 589 assert(cont.hasExactlyOneUse); | 675 assert(cont.hasExactlyOneUse); |
| 590 assert(cont.parameters.length == 1); | 676 assert(cont.parameters.length == 1); |
| 591 return buildParameterAssignments(cont.parameters, [concat], | 677 return buildContinuationAssignment(cont.parameters[0], concat, |
| 592 () => visit(cont.body)); | 678 () => visit(cont.body)); |
| 593 } | 679 } |
| 594 } | 680 } |
| 595 | 681 |
| 596 Statement visitInvokeConstructor(ir.InvokeConstructor node) { | 682 Statement visitInvokeConstructor(ir.InvokeConstructor node) { |
| 597 List<Expression> arguments = translateArguments(node.arguments); | 683 List<Expression> arguments = translateArguments(node.arguments); |
| 598 Expression invoke = | 684 Expression invoke = |
| 599 new InvokeConstructor(node.type, node.target, node.selector, arguments); | 685 new InvokeConstructor(node.type, node.target, node.selector, arguments); |
| 600 ir.Continuation cont = node.continuation.definition; | 686 ir.Continuation cont = node.continuation.definition; |
| 601 if (cont == returnContinuation) { | 687 if (cont == returnContinuation) { |
| 602 return new Return(invoke); | 688 return new Return(invoke); |
| 603 } else { | 689 } else { |
| 604 assert(cont.hasExactlyOneUse); | 690 assert(cont.hasExactlyOneUse); |
| 605 assert(cont.parameters.length == 1); | 691 assert(cont.parameters.length == 1); |
| 606 return buildParameterAssignments(cont.parameters, [invoke], | 692 return buildContinuationAssignment(cont.parameters[0], invoke, |
| 607 () => visit(cont.body)); | 693 () => visit(cont.body)); |
| 608 } | 694 } |
| 609 } | 695 } |
| 610 | 696 |
| 611 Statement visitInvokeContinuation(ir.InvokeContinuation node) { | 697 Statement visitInvokeContinuation(ir.InvokeContinuation node) { |
| 612 // Invocations of the return continuation are translated to returns. | 698 // Invocations of the return continuation are translated to returns. |
| 613 // Other continuation invocations are replaced with assignments of the | 699 // Other continuation invocations are replaced with assignments of the |
| 614 // arguments to formal parameter variables, followed by the body if | 700 // arguments to formal parameter variables, followed by the body if |
| 615 // the continuation is singly reference or a break if it is multiply | 701 // the continuation is singly reference or a break if it is multiply |
| 616 // referenced. | 702 // referenced. |
| 617 ir.Continuation cont = node.continuation.definition; | 703 ir.Continuation cont = node.continuation.definition; |
| 618 if (cont == returnContinuation) { | 704 if (cont == returnContinuation) { |
| 619 assert(node.arguments.length == 1); | 705 assert(node.arguments.length == 1); |
| 620 return new Return(variables[node.arguments[0].definition]); | 706 return new Return(getVariableReference(node.arguments[0])); |
| 621 } else { | 707 } else { |
| 622 List<Expression> arguments = translateArguments(node.arguments); | 708 List<Expression> arguments = translatePhiArguments(node.arguments); |
| 623 return buildParameterAssignments(cont.parameters, arguments, | 709 return buildPhiAssignments(cont.parameters, arguments, |
| 624 () { | 710 () { |
| 625 // Translate invocations of recursive and non-recursive | 711 // Translate invocations of recursive and non-recursive |
| 626 // continuations differently. | 712 // continuations differently. |
| 627 // * Non-recursive continuations | 713 // * Non-recursive continuations |
| 628 // - If there is one use, translate the continuation body | 714 // - If there is one use, translate the continuation body |
| 629 // inline at the invocation site. | 715 // inline at the invocation site. |
| 630 // - If there are multiple uses, translate to Break. | 716 // - If there are multiple uses, translate to Break. |
| 631 // * Recursive continuations | 717 // * Recursive continuations |
| 632 // - There is a single non-recursive invocation. Translate | 718 // - There is a single non-recursive invocation. Translate |
| 633 // the continuation body inline as a labeled loop at the | 719 // the continuation body inline as a labeled loop at the |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 687 } | 773 } |
| 688 | 774 |
| 689 Expression visitContinuation(ir.Continuation node) { | 775 Expression visitContinuation(ir.Continuation node) { |
| 690 // Until continuations with multiple uses are supported, they are not | 776 // Until continuations with multiple uses are supported, they are not |
| 691 // visited. | 777 // visited. |
| 692 compiler.internalError(compiler.currentElement, 'Unexpected IR node.'); | 778 compiler.internalError(compiler.currentElement, 'Unexpected IR node.'); |
| 693 return null; | 779 return null; |
| 694 } | 780 } |
| 695 | 781 |
| 696 Expression visitIsTrue(ir.IsTrue node) { | 782 Expression visitIsTrue(ir.IsTrue node) { |
| 697 return variables[node.value.definition]; | 783 return getVariableReference(node.value); |
| 698 } | 784 } |
| 699 } | 785 } |
| 700 | 786 |
| 701 /** | 787 /** |
| 702 * Performs the following transformations on the tree: | 788 * Performs the following transformations on the tree: |
| 703 * - Assignment propagation | 789 * - Assignment propagation |
| 704 * - If-to-conditional conversion | 790 * - If-to-conditional conversion |
| 705 * - Flatten nested ifs | 791 * - Flatten nested ifs |
| 706 * - Break inlining | 792 * - Break inlining |
| 707 * - Redirect breaks | 793 * - Redirect breaks |
| (...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 933 | 1019 |
| 934 Statement visitLabeledStatement(LabeledStatement node) { | 1020 Statement visitLabeledStatement(LabeledStatement node) { |
| 935 if (node.next is Break) { | 1021 if (node.next is Break) { |
| 936 // Eliminate label if next is just a break statement | 1022 // Eliminate label if next is just a break statement |
| 937 // Breaks to this label are redirected to the outer label. | 1023 // Breaks to this label are redirected to the outer label. |
| 938 // Note that breakCount for the two labels is updated proactively here | 1024 // Note that breakCount for the two labels is updated proactively here |
| 939 // so breaks can reliably tell if they should inline their target. | 1025 // so breaks can reliably tell if they should inline their target. |
| 940 Break next = node.next; | 1026 Break next = node.next; |
| 941 Label newTarget = redirect(next.target); | 1027 Label newTarget = redirect(next.target); |
| 942 labelRedirects[node.label] = newTarget; | 1028 labelRedirects[node.label] = newTarget; |
| 943 newTarget.breakCount += node.label.breakCount; | 1029 newTarget.breakCount += node.label.breakCount - 1; |
| 944 node.label.breakCount = 0; | 1030 node.label.breakCount = 0; |
| 945 Statement result = visitStatement(node.body); | 1031 Statement result = visitStatement(node.body); |
| 946 labelRedirects.remove(node.label); // Save some space. | 1032 labelRedirects.remove(node.label); // Save some space. |
| 947 return result; | 1033 return result; |
| 948 } | 1034 } |
| 949 | 1035 |
| 950 node.body = visitStatement(node.body); | 1036 node.body = visitStatement(node.body); |
| 951 | 1037 |
| 952 if (node.label.breakCount == 0) { | 1038 if (node.label.breakCount == 0) { |
| 953 // Eliminate the label if next was inlined at a break | 1039 // Eliminate the label if next was inlined at a break |
| 954 return node.body; | 1040 return node.body; |
| 955 } | 1041 } |
| 956 | 1042 |
| 1043 // Do not propagate assignments into the successor statements, since they | |
| 1044 // may be overwritten by assignments in the body. | |
| 1045 List<Assign> savedEnvironment = environment; | |
| 1046 environment = <Assign>[]; | |
| 957 node.next = visitStatement(node.next); | 1047 node.next = visitStatement(node.next); |
| 1048 environment = savedEnvironment; | |
| 1049 | |
| 958 return node; | 1050 return node; |
| 959 } | 1051 } |
| 960 | 1052 |
| 961 Statement visitIf(If node) { | 1053 Statement visitIf(If node) { |
| 962 node.condition = visitExpression(node.condition); | 1054 node.condition = visitExpression(node.condition); |
| 963 | 1055 |
| 964 // Do not propagate assignments into branches. Doing so will lead to code | 1056 // Do not propagate assignments into branches. Doing so will lead to code |
| 965 // duplication. | 1057 // duplication. |
| 966 // TODO(kmillikin): Rethink this. Propagating some assignments (e.g., | 1058 // TODO(kmillikin): Rethink this. Propagating some assignments (e.g., |
| 967 // constants or variables) is benign. If they can occur here, they should | 1059 // constants or variables) is benign. If they can occur here, they should |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1060 Statement t, | 1152 Statement t, |
| 1061 Expression combine(Expression s, Expression t)) { | 1153 Expression combine(Expression s, Expression t)) { |
| 1062 if (s is Return && t is Return) { | 1154 if (s is Return && t is Return) { |
| 1063 return new Return(combine(s.value, t.value)); | 1155 return new Return(combine(s.value, t.value)); |
| 1064 } | 1156 } |
| 1065 if (s is Assign && t is Assign && s.variable == t.variable) { | 1157 if (s is Assign && t is Assign && s.variable == t.variable) { |
| 1066 Statement next = combineStatements(s.next, t.next); | 1158 Statement next = combineStatements(s.next, t.next); |
| 1067 if (next != null) { | 1159 if (next != null) { |
| 1068 return new Assign(s.variable, | 1160 return new Assign(s.variable, |
| 1069 combine(s.definition, t.definition), | 1161 combine(s.definition, t.definition), |
| 1070 next, | 1162 next); |
| 1071 s.hasExactlyOneUse); | |
| 1072 } | 1163 } |
| 1073 } | 1164 } |
| 1074 if (s is ExpressionStatement && t is ExpressionStatement) { | 1165 if (s is ExpressionStatement && t is ExpressionStatement) { |
| 1075 Statement next = combineStatements(s.next, t.next); | 1166 Statement next = combineStatements(s.next, t.next); |
| 1076 if (next != null) { | 1167 if (next != null) { |
| 1077 return new ExpressionStatement(combine(s.expression, t.expression), | 1168 return new ExpressionStatement(combine(s.expression, t.expression), |
| 1078 next); | 1169 next); |
| 1079 } | 1170 } |
| 1080 } | 1171 } |
| 1081 return null; | 1172 return null; |
| 1082 } | 1173 } |
| 1083 | 1174 |
| 1084 /// Returns a statement equivalent to both [s] and [t], or null if [s] and | 1175 /// Returns a statement equivalent to both [s] and [t], or null if [s] and |
| 1085 /// [t] are incompatible. | 1176 /// [t] are incompatible. |
| 1086 /// If non-null is returned, the caller MUST discard [s] and [t] and use | 1177 /// If non-null is returned, the caller MUST discard [s] and [t] and use |
| 1087 /// the returned statement instead. | 1178 /// the returned statement instead. |
| 1088 /// If two breaks are combined, the label's break counter will be decremented. | 1179 /// If two breaks are combined, the label's break counter will be decremented. |
| 1089 static Statement combineStatements(Statement s, Statement t) { | 1180 static Statement combineStatements(Statement s, Statement t) { |
| 1090 if (s is Break && t is Break && s.target == t.target) { | 1181 if (s is Break && t is Break && s.target == t.target) { |
| 1091 --t.target.breakCount; // Two breaks become one. | 1182 --t.target.breakCount; // Two breaks become one. |
| 1092 return s; | 1183 return s; |
| 1093 } | 1184 } |
| 1094 if (s is Return && t is Return && equivalentExpressions(s.value, t.value)) { | 1185 if (s is Return && t is Return) { |
| 1095 return s; | 1186 Expression e = combineExpressions(s.value, t.value); |
| 1187 if (e != null) { | |
| 1188 return new Return(e); | |
| 1189 } | |
| 1096 } | 1190 } |
| 1097 return null; | 1191 return null; |
| 1098 } | 1192 } |
| 1099 | 1193 |
| 1100 /// True if the two expressions both syntactically and semantically | 1194 /// Returns an expression equivalent to both [e1] and [e2]. |
| 1101 /// equivalent. | 1195 /// If non-null is returned, the caller must discard [e1] and [e2] and use |
| 1102 static bool equivalentExpressions(Expression e1, Expression e2) { | 1196 /// the resulting expression in the tree. |
| 1103 if (e1 == e2) { // Detect same variable reference | 1197 static Expression combineExpressions(Expression e1, Expression e2) { |
| 1104 // TODO(asgerf): This might turn the variable into a single-use, | 1198 if (e1 is Variable && e1 == e2) { |
| 1105 // but we currently don't discover this. | 1199 --e1.readCount; // Two references become one. |
| 1106 return true; | 1200 return e1; |
| 1107 } | 1201 } |
| 1108 if (e1 is Constant && e2 is Constant) { | 1202 if (e1 is Constant && e2 is Constant && e1.value == e2.value) { |
| 1109 return e1.value == e2.value; | 1203 return e1; |
| 1110 } | 1204 } |
| 1111 return false; | 1205 return null; |
| 1112 } | 1206 } |
| 1113 | 1207 |
| 1114 /// Try to collapse nested ifs using && and || expressions. | 1208 /// Try to collapse nested ifs using && and || expressions. |
| 1115 /// For example: | 1209 /// For example: |
| 1116 /// | 1210 /// |
| 1117 /// if (E1) { if (E2) S else break L } else break L | 1211 /// if (E1) { if (E2) S else break L } else break L |
| 1118 /// ==> | 1212 /// ==> |
| 1119 /// if (E1 && E2) S else break L | 1213 /// if (E1 && E2) S else break L |
| 1120 /// | 1214 /// |
| 1121 /// [branch1] and [branch2] control the position of the S statement. | 1215 /// [branch1] and [branch2] control the position of the S statement. |
| (...skipping 480 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1602 } | 1696 } |
| 1603 } | 1697 } |
| 1604 | 1698 |
| 1605 /// Destructively updates each entry of [l] with the result of visiting it. | 1699 /// Destructively updates each entry of [l] with the result of visiting it. |
| 1606 void _rewriteList(List<Expression> l) { | 1700 void _rewriteList(List<Expression> l) { |
| 1607 for (int i = 0; i < l.length; i++) { | 1701 for (int i = 0; i < l.length; i++) { |
| 1608 l[i] = visitExpression(l[i]); | 1702 l[i] = visitExpression(l[i]); |
| 1609 } | 1703 } |
| 1610 } | 1704 } |
| 1611 } | 1705 } |
| OLD | NEW |