| 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 useCount = 0; | 76 int useCount = 0; |
| 77 | 77 |
| 78 /// The [LabeledStatement] or [WhileTrue] binding this label. | 78 /// The [LabeledStatement] or [WhileTrue] binding this label. |
| 79 JumpTarget binding; | 79 JumpTarget 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(ExpressionVisitor visitor) => visitor.visitVariable(this); | 94 accept(ExpressionVisitor 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 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 312 | 305 |
| 313 WhileCondition(this.label, this.condition, this.body, | 306 WhileCondition(this.label, this.condition, this.body, |
| 314 this.next, this.updates) { | 307 this.next, this.updates) { |
| 315 assert(label.binding == null); | 308 assert(label.binding == null); |
| 316 label.binding = this; | 309 label.binding = this; |
| 317 } | 310 } |
| 318 | 311 |
| 319 accept(StatementVisitor visitor) => visitor.visitWhileCondition(this); | 312 accept(StatementVisitor visitor) => visitor.visitWhileCondition(this); |
| 320 } | 313 } |
| 321 | 314 |
| 322 | |
| 323 /// A [Break] or [Continue] statement. | 315 /// A [Break] or [Continue] statement. |
| 324 abstract class Jump extends Statement { | 316 abstract class Jump extends Statement { |
| 325 Label get target; | 317 Label get target; |
| 326 } | 318 } |
| 327 | 319 |
| 328 /** | 320 /** |
| 329 * A break from an enclosing [LabeledStatement]. The break targets the | 321 * A break from an enclosing [LabeledStatement]. The break targets the |
| 330 * labeled statement's successor statement. | 322 * labeled statement's successor statement. |
| 331 */ | 323 */ |
| 332 class Break extends Jump { | 324 class Break extends Jump { |
| 333 final Label target; | 325 final Label target; |
| 334 | 326 |
| 335 Statement get next => null; | 327 Statement get next => null; |
| 336 void set next(Statement s) => throw 'UNREACHABLE'; | 328 void set next(Statement s) => throw 'UNREACHABLE'; |
| 337 | 329 |
| 338 Break(this.target) { | 330 Break(this.target) { |
| 339 ++target.useCount; | 331 ++target.useCount; |
| 340 } | 332 } |
| 341 | 333 |
| 342 accept(StatementVisitor visitor) => visitor.visitBreak(this); | 334 accept(StatementVisitor visitor) => visitor.visitBreak(this); |
| 343 } | 335 } |
| 344 | 336 |
| 345 /** | 337 /** |
| 346 * A continue to an enclosing [WhileTrue] loop. The continue targets the | 338 * A continue to an enclosing [WhileTrue] or [WhileCondition] loop. |
| 347 * loop's body. | 339 * The continue targets the loop's body. |
| 348 */ | 340 */ |
| 349 class Continue extends Jump { | 341 class Continue extends Jump { |
| 350 final Label target; | 342 final Label target; |
| 351 | 343 |
| 352 Statement get next => null; | 344 Statement get next => null; |
| 353 void set next(Statement s) => throw 'UNREACHABLE'; | 345 void set next(Statement s) => throw 'UNREACHABLE'; |
| 354 | 346 |
| 355 Continue(this.target) { | 347 Continue(this.target) { |
| 356 ++target.useCount; | 348 ++target.useCount; |
| 357 } | 349 } |
| 358 | 350 |
| 359 accept(StatementVisitor visitor) => visitor.visitContinue(this); | 351 accept(StatementVisitor visitor) => visitor.visitContinue(this); |
| 360 } | 352 } |
| 361 | 353 |
| 362 /** | 354 /** |
| 363 * An assignments of an [Expression] to a [Variable]. | 355 * An assignments of an [Expression] to a [Variable]. |
| 364 * | 356 * |
| 365 * In contrast to the CPS-based IR, non-primitive expressions can be assigned | 357 * In contrast to the CPS-based IR, non-primitive expressions can be assigned |
| 366 * to variables. | 358 * to variables. |
| 367 */ | 359 */ |
| 368 class Assign extends Statement { | 360 class Assign extends Statement { |
| 369 Statement next; | 361 Statement next; |
| 370 final Variable variable; | 362 final Variable variable; |
| 371 Expression definition; | 363 Expression definition; |
| 372 final bool hasExactlyOneUse; | |
| 373 | 364 |
| 374 Assign(this.variable, this.definition, this.next, this.hasExactlyOneUse); | 365 Assign(this.variable, this.definition, this.next); |
| 366 |
| 367 bool get hasExactlyOneUse => variable.readCount == 1; |
| 375 | 368 |
| 376 accept(StatementVisitor visitor) => visitor.visitAssign(this); | 369 accept(StatementVisitor visitor) => visitor.visitAssign(this); |
| 377 } | 370 } |
| 378 | 371 |
| 379 | |
| 380 /** | 372 /** |
| 381 * A return exit from the function. | 373 * A return exit from the function. |
| 382 * | 374 * |
| 383 * In contrast to the CPS-based IR, the return value is an arbitrary | 375 * In contrast to the CPS-based IR, the return value is an arbitrary |
| 384 * expression. | 376 * expression. |
| 385 */ | 377 */ |
| 386 class Return extends Statement { | 378 class Return extends Statement { |
| 387 /// Should not be null. Use [Constant] with [NullConstant] for void returns. | 379 /// Should not be null. Use [Constant] with [NullConstant] for void returns. |
| 388 Expression value; | 380 Expression value; |
| 389 | 381 |
| 390 Statement get next => null; | 382 Statement get next => null; |
| 391 void set next(Statement s) => throw 'UNREACHABLE'; | 383 void set next(Statement s) => throw 'UNREACHABLE'; |
| 392 | 384 |
| 393 Return(this.value); | 385 Return(this.value); |
| 394 | 386 |
| 395 accept(StatementVisitor visitor) => visitor.visitReturn(this); | 387 accept(StatementVisitor visitor) => visitor.visitReturn(this); |
| 396 } | 388 } |
| 397 | 389 |
| 398 | |
| 399 | |
| 400 | |
| 401 /** | 390 /** |
| 402 * A conditional branch based on the true value of an [Expression]. | 391 * A conditional branch based on the true value of an [Expression]. |
| 403 */ | 392 */ |
| 404 class If extends Statement { | 393 class If extends Statement { |
| 405 Expression condition; | 394 Expression condition; |
| 406 Statement thenStatement; | 395 Statement thenStatement; |
| 407 Statement elseStatement; | 396 Statement elseStatement; |
| 408 | 397 |
| 409 Statement get next => null; | 398 Statement get next => null; |
| 410 void set next(Statement s) => throw 'UNREACHABLE'; | 399 void set next(Statement s) => throw 'UNREACHABLE'; |
| 411 | 400 |
| 412 If(this.condition, this.thenStatement, this.elseStatement); | 401 If(this.condition, this.thenStatement, this.elseStatement); |
| 413 | 402 |
| 414 accept(StatementVisitor visitor) => visitor.visitIf(this); | 403 accept(StatementVisitor visitor) => visitor.visitIf(this); |
| 415 } | 404 } |
| 416 | 405 |
| 417 | |
| 418 class ExpressionStatement extends Statement { | 406 class ExpressionStatement extends Statement { |
| 419 Statement next; | 407 Statement next; |
| 420 Expression expression; | 408 Expression expression; |
| 421 | 409 |
| 422 ExpressionStatement(this.expression, this.next); | 410 ExpressionStatement(this.expression, this.next); |
| 423 | 411 |
| 424 accept(StatementVisitor visitor) => visitor.visitExpressionStatement(this); | 412 accept(StatementVisitor visitor) => visitor.visitExpressionStatement(this); |
| 425 } | 413 } |
| 426 | 414 |
| 427 class FunctionDefinition extends Node { | 415 class FunctionDefinition extends Node { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 494 * translation out of SSA. Jumps are eliminated during the Tree-to-Tree | 482 * translation out of SSA. Jumps are eliminated during the Tree-to-Tree |
| 495 * control-flow recognition. | 483 * control-flow recognition. |
| 496 * | 484 * |
| 497 * Otherwise, the output of Builder looks very much like the input. In | 485 * Otherwise, the output of Builder looks very much like the input. In |
| 498 * particular, intermediate values and blocks used for local control flow are | 486 * particular, intermediate values and blocks used for local control flow are |
| 499 * still all named. | 487 * still all named. |
| 500 */ | 488 */ |
| 501 class Builder extends ir.Visitor<Node> { | 489 class Builder extends ir.Visitor<Node> { |
| 502 final dart2js.Compiler compiler; | 490 final dart2js.Compiler compiler; |
| 503 | 491 |
| 504 // Uses of IR primitives are replaced with Tree variables. This is the | 492 /// Maps variable/parameter elements to the Tree variables that represent it. |
| 505 // mapping from primitives to variables. | 493 final Map<Element, List<Variable>> element2variables = |
| 506 final Map<ir.Primitive, Variable> variables = <ir.Primitive, Variable>{}; | 494 <Element,List<Variable>>{}; |
| 507 | 495 |
| 508 // Continuations with more than one use are replaced with Tree labels. This | 496 // Continuations with more than one use are replaced with Tree labels. This |
| 509 // is the mapping from continuations to labels. | 497 // is the mapping from continuations to labels. |
| 510 final Map<ir.Continuation, Label> labels = <ir.Continuation, Label>{}; | 498 final Map<ir.Continuation, Label> labels = <ir.Continuation, Label>{}; |
| 511 | 499 |
| 512 FunctionDefinition function; | 500 FunctionDefinition function; |
| 513 ir.Continuation returnContinuation; | 501 ir.Continuation returnContinuation; |
| 514 | 502 |
| 503 /// Variable used in [buildPhiAssignments] as a temporary when swapping |
| 504 /// variables. |
| 505 final Variable tempVar = new Variable(null); |
| 506 |
| 515 Builder(this.compiler); | 507 Builder(this.compiler); |
| 516 | 508 |
| 509 /// Obtains the variable representing the given primitive. Returns null for |
| 510 /// primitives that have no reference and do not need a variable. |
| 511 Variable getVariable(ir.Primitive primitive) { |
| 512 if (primitive.registerIndex == null) { |
| 513 return null; // variable is unused |
| 514 } |
| 515 List<Variable> variables = element2variables[primitive.element]; |
| 516 if (variables == null) { |
| 517 variables = <Variable>[]; |
| 518 element2variables[primitive.element] = variables; |
| 519 } |
| 520 while (variables.length <= primitive.registerIndex) { |
| 521 variables.add(new Variable(primitive.element)); |
| 522 } |
| 523 return variables[primitive.registerIndex]; |
| 524 } |
| 525 |
| 526 /// Obtains a reference to the tree Variable corresponding to the IR primitive |
| 527 /// referred to by [reference]. |
| 528 /// This increments the reference count for the given variable, so the |
| 529 /// returned expression must be used in the tree. |
| 530 Expression getVariableReference(ir.Reference reference) { |
| 531 Variable variable = getVariable(reference.definition); |
| 532 if (variable == null) { |
| 533 compiler.internalError( |
| 534 compiler.currentElement, |
| 535 "Reference to ${reference.definition} has no register"); |
| 536 } |
| 537 ++variable.readCount; |
| 538 return variable; |
| 539 } |
| 540 |
| 517 FunctionDefinition build(ir.FunctionDefinition node) { | 541 FunctionDefinition build(ir.FunctionDefinition node) { |
| 542 new ir.RegisterAllocator().visit(node); |
| 518 visit(node); | 543 visit(node); |
| 519 return function; | 544 return function; |
| 520 } | 545 } |
| 521 | 546 |
| 522 List<Expression> translateArguments(List<ir.Reference> args) { | 547 List<Expression> translateArguments(List<ir.Reference> args) { |
| 523 return new List<Expression>.generate(args.length, | 548 return new List<Expression>.generate(args.length, |
| 524 (int index) => variables[args[index].definition]); | 549 (int index) => getVariableReference(args[index])); |
| 525 } | 550 } |
| 526 | 551 |
| 527 Statement buildParameterAssignments( | 552 List<Variable> translatePhiArguments(List<ir.Reference> args) { |
| 553 return new List<Variable>.generate(args.length, |
| 554 (int index) => getVariableReference(args[index])); |
| 555 } |
| 556 |
| 557 Statement buildContinuationAssignment( |
| 558 ir.Parameter parameter, |
| 559 Expression argument, |
| 560 Statement buildRest()) { |
| 561 Variable variable = getVariable(parameter); |
| 562 Statement assignment; |
| 563 if (variable == null) { |
| 564 assignment = new ExpressionStatement(argument, null); |
| 565 } else { |
| 566 assignment = new Assign(variable, argument, null); |
| 567 } |
| 568 assignment.next = buildRest(); |
| 569 return assignment; |
| 570 } |
| 571 |
| 572 /// Simultaneously assigns each argument to the corresponding parameter, |
| 573 /// then continues at the statement created by [buildRest]. |
| 574 Statement buildPhiAssignments( |
| 528 List<ir.Parameter> parameters, | 575 List<ir.Parameter> parameters, |
| 529 List<Expression> arguments, | 576 List<Variable> arguments, |
| 530 Statement buildRest()) { | 577 Statement buildRest()) { |
| 531 assert(parameters.length == arguments.length); | 578 assert(parameters.length == arguments.length); |
| 579 // We want a parallel assignment to all parameters simultaneously. |
| 580 // Since we do not have parallel assignments in dart_tree, we must linearize |
| 581 // the assignments without attempting to read a previously-overwritten |
| 582 // value. For example {x,y = y,x} cannot be linearized to {x = y; y = x}, |
| 583 // for this we must introduce a temporary variable: {t = x; x = y; y = t}. |
| 584 |
| 585 // [rightHand] is the inverse of [arguments], that is, it maps variables |
| 586 // to the assignments on which is occurs as the right-hand side. |
| 587 Map<Variable, List<int>> rightHand = <Variable, List<int>>{}; |
| 588 for (int i = 0; i < parameters.length; i++) { |
| 589 Variable param = getVariable(parameters[i]); |
| 590 Variable arg = arguments[i]; |
| 591 if (param == null || param == arg) { |
| 592 continue; // No assignment necessary. |
| 593 } |
| 594 List<int> list = rightHand[arg]; |
| 595 if (list == null) { |
| 596 rightHand[arg] = list = <int>[]; |
| 597 } |
| 598 list.add(i); |
| 599 } |
| 600 |
| 532 Statement first, current; | 601 Statement first, current; |
| 533 for (int i = 0; i < parameters.length; ++i) { | 602 void addAssignment(Variable dst, Variable src) { |
| 534 ir.Parameter parameter = parameters[i]; | 603 if (first == null) { |
| 535 Statement assignment; | 604 first = current = new Assign(dst, src, null); |
| 536 if (parameter.hasAtLeastOneUse) { | |
| 537 assignment = new Assign(variables[parameter], arguments[i], null, | |
| 538 parameter.hasExactlyOneUse); | |
| 539 } else { | 605 } else { |
| 540 assignment = new ExpressionStatement(arguments[i], null); | 606 current = current.next = new Assign(dst, src, null); |
| 541 } | |
| 542 | |
| 543 if (first == null) { | |
| 544 current = first = assignment; | |
| 545 } else { | |
| 546 current = current.next = assignment; | |
| 547 } | 607 } |
| 548 } | 608 } |
| 549 | 609 |
| 610 List<Variable> assignmentSrc = new List<Variable>(parameters.length); |
| 611 List<bool> done = new List<bool>(parameters.length); |
| 612 void visitAssignment(int i) { |
| 613 if (done[i] == true) { |
| 614 return; |
| 615 } |
| 616 Variable param = getVariable(parameters[i]); |
| 617 Variable arg = arguments[i]; |
| 618 if (param == null || param == arg) { |
| 619 return; // No assignment necessary. |
| 620 } |
| 621 if (assignmentSrc[i] != null) { |
| 622 // Cycle found; store argument in a temporary variable. |
| 623 // The temporary will then be used as right-hand side when the |
| 624 // assignment gets added. |
| 625 if (assignmentSrc[i] != tempVar) { // Only move to temporary once. |
| 626 assignmentSrc[i] = tempVar; |
| 627 addAssignment(tempVar, arg); |
| 628 } |
| 629 return; |
| 630 } |
| 631 assignmentSrc[i] = arg; |
| 632 List<int> paramUses = rightHand[param]; |
| 633 if (paramUses != null) { |
| 634 for (int useIndex in paramUses) { |
| 635 visitAssignment(useIndex); |
| 636 } |
| 637 } |
| 638 addAssignment(param, assignmentSrc[i]); |
| 639 done[i] = true; |
| 640 } |
| 641 |
| 642 for (int i = 0; i < parameters.length; i++) { |
| 643 if (done[i] == null) { |
| 644 visitAssignment(i); |
| 645 } |
| 646 } |
| 647 |
| 550 if (first == null) { | 648 if (first == null) { |
| 551 first = buildRest(); | 649 first = buildRest(); |
| 552 } else { | 650 } else { |
| 553 current.next = buildRest(); | 651 current.next = buildRest(); |
| 554 } | 652 } |
| 555 return first; | 653 return first; |
| 556 } | 654 } |
| 557 | 655 |
| 558 Expression visitFunctionDefinition(ir.FunctionDefinition node) { | 656 Expression visitFunctionDefinition(ir.FunctionDefinition node) { |
| 559 returnContinuation = node.returnContinuation; | 657 returnContinuation = node.returnContinuation; |
| 560 List<Variable> parameters = <Variable>[]; | 658 List<Variable> parameters = <Variable>[]; |
| 561 for (ir.Parameter p in node.parameters) { | 659 for (ir.Parameter p in node.parameters) { |
| 562 Variable parameter = new Variable(p.element); | 660 Variable parameter = getVariable(p); |
| 661 assert(parameter != null); |
| 563 parameters.add(parameter); | 662 parameters.add(parameter); |
| 564 variables[p] = parameter; | |
| 565 } | 663 } |
| 566 function = new FunctionDefinition(parameters, visit(node.body)); | 664 function = new FunctionDefinition(parameters, visit(node.body)); |
| 567 return null; | 665 return null; |
| 568 } | 666 } |
| 569 | 667 |
| 570 Statement visitLetPrim(ir.LetPrim node) { | 668 Statement visitLetPrim(ir.LetPrim node) { |
| 571 // LetPrim is translated to LetVal. | 669 // LetPrim is translated to Assign. |
| 572 Expression definition = visit(node.primitive); | 670 Expression definition = visit(node.primitive); |
| 573 if (node.primitive.hasAtLeastOneUse) { | 671 Variable variable = getVariable(node.primitive); |
| 574 Variable variable = new Variable(null); | 672 if (variable != null) { // Variable is null if primitive is unused. |
| 575 variables[node.primitive] = variable; | 673 return new Assign(variable, definition, visit(node.body)); |
| 576 return new Assign(variable, definition, visit(node.body), | |
| 577 node.primitive.hasExactlyOneUse); | |
| 578 } else if (node.primitive is ir.Constant) { | 674 } else if (node.primitive is ir.Constant) { |
| 579 // TODO(kmillikin): Implement more systematic treatment of pure CPS | 675 // TODO(kmillikin): Implement more systematic treatment of pure CPS |
| 580 // values (e.g., as part of a shrinking reductions pass). | 676 // values (e.g., as part of a shrinking reductions pass). |
| 581 return visit(node.body); | 677 return visit(node.body); |
| 582 } else { | 678 } else { |
| 583 return new ExpressionStatement(definition, visit(node.body)); | 679 return new ExpressionStatement(definition, visit(node.body)); |
| 584 } | 680 } |
| 585 } | 681 } |
| 586 | 682 |
| 587 Statement visitLetCont(ir.LetCont node) { | 683 Statement visitLetCont(ir.LetCont node) { |
| 588 Label label; | 684 Label label; |
| 589 if (node.continuation.hasMultipleUses) { | 685 if (node.continuation.hasMultipleUses) { |
| 590 label = new Label(); | 686 label = new Label(); |
| 591 labels[node.continuation] = label; | 687 labels[node.continuation] = label; |
| 592 } | 688 } |
| 593 node.continuation.parameters.forEach((p) { | |
| 594 if (p.hasAtLeastOneUse) variables[p] = new Variable(null); | |
| 595 }); | |
| 596 Statement body = visit(node.body); | 689 Statement body = visit(node.body); |
| 597 // The continuation's body is not always translated directly here because | 690 // The continuation's body is not always translated directly here because |
| 598 // it may have been already translated: | 691 // it may have been already translated: |
| 599 // * For singly-used continuations, the continuation's body is | 692 // * For singly-used continuations, the continuation's body is |
| 600 // translated at the site of the continuation invocation. | 693 // translated at the site of the continuation invocation. |
| 601 // * For recursive continuations, there is a single non-recursive | 694 // * For recursive continuations, there is a single non-recursive |
| 602 // invocation. The continuation's body is translated at the site | 695 // invocation. The continuation's body is translated at the site |
| 603 // of the non-recursive continuation invocation. | 696 // of the non-recursive continuation invocation. |
| 604 // See visitInvokeContinuation for the implementation. | 697 // See visitInvokeContinuation for the implementation. |
| 605 if (label == null || node.continuation.isRecursive) return body; | 698 if (label == null || node.continuation.isRecursive) return body; |
| 606 return new LabeledStatement(label, body, visit(node.continuation.body)); | 699 return new LabeledStatement(label, body, visit(node.continuation.body)); |
| 607 } | 700 } |
| 608 | 701 |
| 609 Statement visitInvokeStatic(ir.InvokeStatic node) { | 702 Statement visitInvokeStatic(ir.InvokeStatic node) { |
| 610 // Calls are translated to direct style. | 703 // Calls are translated to direct style. |
| 611 List<Expression> arguments = translateArguments(node.arguments); | 704 List<Expression> arguments = translateArguments(node.arguments); |
| 612 Expression invoke = new InvokeStatic(node.target, node.selector, arguments); | 705 Expression invoke = new InvokeStatic(node.target, node.selector, arguments); |
| 613 ir.Continuation cont = node.continuation.definition; | 706 ir.Continuation cont = node.continuation.definition; |
| 614 if (cont == returnContinuation) { | 707 if (cont == returnContinuation) { |
| 615 return new Return(invoke); | 708 return new Return(invoke); |
| 616 } else { | 709 } else { |
| 617 assert(cont.hasExactlyOneUse); | 710 assert(cont.hasExactlyOneUse); |
| 618 assert(cont.parameters.length == 1); | 711 assert(cont.parameters.length == 1); |
| 619 return buildParameterAssignments(cont.parameters, [invoke], | 712 return buildContinuationAssignment(cont.parameters.single, invoke, |
| 620 () => visit(cont.body)); | 713 () => visit(cont.body)); |
| 621 } | 714 } |
| 622 } | 715 } |
| 623 | 716 |
| 624 Statement visitInvokeMethod(ir.InvokeMethod node) { | 717 Statement visitInvokeMethod(ir.InvokeMethod node) { |
| 625 Variable receiver = variables[node.receiver.definition]; | 718 Expression receiver = getVariableReference(node.receiver); |
| 626 List<Expression> arguments = translateArguments(node.arguments); | 719 List<Expression> arguments = translateArguments(node.arguments); |
| 627 Expression invoke = new InvokeMethod(receiver, node.selector, arguments); | 720 Expression invoke = new InvokeMethod(receiver, node.selector, arguments); |
| 628 ir.Continuation cont = node.continuation.definition; | 721 ir.Continuation cont = node.continuation.definition; |
| 629 if (cont == returnContinuation) { | 722 if (cont == returnContinuation) { |
| 630 return new Return(invoke); | 723 return new Return(invoke); |
| 631 } else { | 724 } else { |
| 632 assert(cont.hasExactlyOneUse); | 725 assert(cont.hasExactlyOneUse); |
| 633 assert(cont.parameters.length == 1); | 726 assert(cont.parameters.length == 1); |
| 634 return buildParameterAssignments(cont.parameters, [invoke], | 727 return buildContinuationAssignment(cont.parameters.single, invoke, |
| 635 () => visit(cont.body)); | 728 () => visit(cont.body)); |
| 636 } | 729 } |
| 637 } | 730 } |
| 638 | 731 |
| 639 Statement visitConcatenateStrings(ir.ConcatenateStrings node) { | 732 Statement visitConcatenateStrings(ir.ConcatenateStrings node) { |
| 640 List<Expression> arguments = translateArguments(node.arguments); | 733 List<Expression> arguments = translateArguments(node.arguments); |
| 641 Expression concat = new ConcatenateStrings(arguments); | 734 Expression concat = new ConcatenateStrings(arguments); |
| 642 ir.Continuation cont = node.continuation.definition; | 735 ir.Continuation cont = node.continuation.definition; |
| 643 if (cont == returnContinuation) { | 736 if (cont == returnContinuation) { |
| 644 return new Return(concat); | 737 return new Return(concat); |
| 645 } else { | 738 } else { |
| 646 assert(cont.hasExactlyOneUse); | 739 assert(cont.hasExactlyOneUse); |
| 647 assert(cont.parameters.length == 1); | 740 assert(cont.parameters.length == 1); |
| 648 return buildParameterAssignments(cont.parameters, [concat], | 741 return buildContinuationAssignment(cont.parameters.single, concat, |
| 649 () => visit(cont.body)); | 742 () => visit(cont.body)); |
| 650 } | 743 } |
| 651 } | 744 } |
| 652 | 745 |
| 653 Statement visitInvokeConstructor(ir.InvokeConstructor node) { | 746 Statement visitInvokeConstructor(ir.InvokeConstructor node) { |
| 654 List<Expression> arguments = translateArguments(node.arguments); | 747 List<Expression> arguments = translateArguments(node.arguments); |
| 655 Expression invoke = | 748 Expression invoke = |
| 656 new InvokeConstructor(node.type, node.target, node.selector, arguments); | 749 new InvokeConstructor(node.type, node.target, node.selector, arguments); |
| 657 ir.Continuation cont = node.continuation.definition; | 750 ir.Continuation cont = node.continuation.definition; |
| 658 if (cont == returnContinuation) { | 751 if (cont == returnContinuation) { |
| 659 return new Return(invoke); | 752 return new Return(invoke); |
| 660 } else { | 753 } else { |
| 661 assert(cont.hasExactlyOneUse); | 754 assert(cont.hasExactlyOneUse); |
| 662 assert(cont.parameters.length == 1); | 755 assert(cont.parameters.length == 1); |
| 663 return buildParameterAssignments(cont.parameters, [invoke], | 756 return buildContinuationAssignment(cont.parameters.single, invoke, |
| 664 () => visit(cont.body)); | 757 () => visit(cont.body)); |
| 665 } | 758 } |
| 666 } | 759 } |
| 667 | 760 |
| 668 Statement visitInvokeContinuation(ir.InvokeContinuation node) { | 761 Statement visitInvokeContinuation(ir.InvokeContinuation node) { |
| 669 // Invocations of the return continuation are translated to returns. | 762 // Invocations of the return continuation are translated to returns. |
| 670 // Other continuation invocations are replaced with assignments of the | 763 // Other continuation invocations are replaced with assignments of the |
| 671 // arguments to formal parameter variables, followed by the body if | 764 // arguments to formal parameter variables, followed by the body if |
| 672 // the continuation is singly reference or a break if it is multiply | 765 // the continuation is singly reference or a break if it is multiply |
| 673 // referenced. | 766 // referenced. |
| 674 ir.Continuation cont = node.continuation.definition; | 767 ir.Continuation cont = node.continuation.definition; |
| 675 if (cont == returnContinuation) { | 768 if (cont == returnContinuation) { |
| 676 assert(node.arguments.length == 1); | 769 assert(node.arguments.length == 1); |
| 677 return new Return(variables[node.arguments[0].definition]); | 770 return new Return(getVariableReference(node.arguments.single)); |
| 678 } else { | 771 } else { |
| 679 List<Expression> arguments = translateArguments(node.arguments); | 772 List<Expression> arguments = translatePhiArguments(node.arguments); |
| 680 return buildParameterAssignments(cont.parameters, arguments, | 773 return buildPhiAssignments(cont.parameters, arguments, |
| 681 () { | 774 () { |
| 682 // Translate invocations of recursive and non-recursive | 775 // Translate invocations of recursive and non-recursive |
| 683 // continuations differently. | 776 // continuations differently. |
| 684 // * Non-recursive continuations | 777 // * Non-recursive continuations |
| 685 // - If there is one use, translate the continuation body | 778 // - If there is one use, translate the continuation body |
| 686 // inline at the invocation site. | 779 // inline at the invocation site. |
| 687 // - If there are multiple uses, translate to Break. | 780 // - If there are multiple uses, translate to Break. |
| 688 // * Recursive continuations | 781 // * Recursive continuations |
| 689 // - There is a single non-recursive invocation. Translate | 782 // - There is a single non-recursive invocation. Translate |
| 690 // the continuation body inline as a labeled loop at the | 783 // the continuation body inline as a labeled loop at the |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 744 } | 837 } |
| 745 | 838 |
| 746 Expression visitContinuation(ir.Continuation node) { | 839 Expression visitContinuation(ir.Continuation node) { |
| 747 // Until continuations with multiple uses are supported, they are not | 840 // Until continuations with multiple uses are supported, they are not |
| 748 // visited. | 841 // visited. |
| 749 compiler.internalError(compiler.currentElement, 'Unexpected IR node.'); | 842 compiler.internalError(compiler.currentElement, 'Unexpected IR node.'); |
| 750 return null; | 843 return null; |
| 751 } | 844 } |
| 752 | 845 |
| 753 Expression visitIsTrue(ir.IsTrue node) { | 846 Expression visitIsTrue(ir.IsTrue node) { |
| 754 return variables[node.value.definition]; | 847 return getVariableReference(node.value); |
| 755 } | 848 } |
| 756 } | 849 } |
| 757 | 850 |
| 758 /** | 851 /** |
| 759 * Performs the following transformations on the tree: | 852 * Performs the following transformations on the tree: |
| 760 * - Assignment propagation | 853 * - Assignment propagation |
| 761 * - If-to-conditional conversion | 854 * - If-to-conditional conversion |
| 762 * - Flatten nested ifs | 855 * - Flatten nested ifs |
| 763 * - Break inlining | 856 * - Break inlining |
| 764 * - Redirect breaks | 857 * - Redirect breaks |
| (...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1005 return result; | 1098 return result; |
| 1006 } | 1099 } |
| 1007 | 1100 |
| 1008 node.body = visitStatement(node.body); | 1101 node.body = visitStatement(node.body); |
| 1009 | 1102 |
| 1010 if (node.label.useCount == 0) { | 1103 if (node.label.useCount == 0) { |
| 1011 // Eliminate the label if next was inlined at a break | 1104 // Eliminate the label if next was inlined at a break |
| 1012 return node.body; | 1105 return node.body; |
| 1013 } | 1106 } |
| 1014 | 1107 |
| 1108 // Do not propagate assignments into the successor statements, since they |
| 1109 // may be overwritten by assignments in the body. |
| 1110 List<Assign> savedEnvironment = environment; |
| 1111 environment = <Assign>[]; |
| 1015 node.next = visitStatement(node.next); | 1112 node.next = visitStatement(node.next); |
| 1113 environment = savedEnvironment; |
| 1114 |
| 1016 return node; | 1115 return node; |
| 1017 } | 1116 } |
| 1018 | 1117 |
| 1019 Statement visitIf(If node) { | 1118 Statement visitIf(If node) { |
| 1020 node.condition = visitExpression(node.condition); | 1119 node.condition = visitExpression(node.condition); |
| 1021 | 1120 |
| 1022 // Do not propagate assignments into branches. Doing so will lead to code | 1121 // Do not propagate assignments into branches. Doing so will lead to code |
| 1023 // duplication. | 1122 // duplication. |
| 1024 // TODO(kmillikin): Rethink this. Propagating some assignments (e.g., | 1123 // TODO(kmillikin): Rethink this. Propagating some assignments (e.g., |
| 1025 // constants or variables) is benign. If they can occur here, they should | 1124 // constants or variables) is benign. If they can occur here, they should |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1123 Statement t, | 1222 Statement t, |
| 1124 Expression combine(Expression s, Expression t)) { | 1223 Expression combine(Expression s, Expression t)) { |
| 1125 if (s is Return && t is Return) { | 1224 if (s is Return && t is Return) { |
| 1126 return new Return(combine(s.value, t.value)); | 1225 return new Return(combine(s.value, t.value)); |
| 1127 } | 1226 } |
| 1128 if (s is Assign && t is Assign && s.variable == t.variable) { | 1227 if (s is Assign && t is Assign && s.variable == t.variable) { |
| 1129 Statement next = combineStatements(s.next, t.next); | 1228 Statement next = combineStatements(s.next, t.next); |
| 1130 if (next != null) { | 1229 if (next != null) { |
| 1131 return new Assign(s.variable, | 1230 return new Assign(s.variable, |
| 1132 combine(s.definition, t.definition), | 1231 combine(s.definition, t.definition), |
| 1133 next, | 1232 next); |
| 1134 s.hasExactlyOneUse); | |
| 1135 } | 1233 } |
| 1136 } | 1234 } |
| 1137 if (s is ExpressionStatement && t is ExpressionStatement) { | 1235 if (s is ExpressionStatement && t is ExpressionStatement) { |
| 1138 Statement next = combineStatements(s.next, t.next); | 1236 Statement next = combineStatements(s.next, t.next); |
| 1139 if (next != null) { | 1237 if (next != null) { |
| 1140 return new ExpressionStatement(combine(s.expression, t.expression), | 1238 return new ExpressionStatement(combine(s.expression, t.expression), |
| 1141 next); | 1239 next); |
| 1142 } | 1240 } |
| 1143 } | 1241 } |
| 1144 return null; | 1242 return null; |
| 1145 } | 1243 } |
| 1146 | 1244 |
| 1147 /// Returns a statement equivalent to both [s] and [t], or null if [s] and | 1245 /// Returns a statement equivalent to both [s] and [t], or null if [s] and |
| 1148 /// [t] are incompatible. | 1246 /// [t] are incompatible. |
| 1149 /// If non-null is returned, the caller MUST discard [s] and [t] and use | 1247 /// If non-null is returned, the caller MUST discard [s] and [t] and use |
| 1150 /// the returned statement instead. | 1248 /// the returned statement instead. |
| 1151 /// If two breaks are combined, the label's break counter will be decremented. | 1249 /// If two breaks are combined, the label's break counter will be decremented. |
| 1152 static Statement combineStatements(Statement s, Statement t) { | 1250 static Statement combineStatements(Statement s, Statement t) { |
| 1153 if (s is Break && t is Break && s.target == t.target) { | 1251 if (s is Break && t is Break && s.target == t.target) { |
| 1154 --t.target.useCount; // Two breaks become one. | 1252 --t.target.useCount; // Two breaks become one. |
| 1155 return s; | 1253 return s; |
| 1156 } | 1254 } |
| 1157 if (s is Continue && t is Continue && s.target == t.target) { | 1255 if (s is Continue && t is Continue && s.target == t.target) { |
| 1158 --t.target.useCount; // Two continues become one. | 1256 --t.target.useCount; // Two continues become one. |
| 1159 return s; | 1257 return s; |
| 1160 } | 1258 } |
| 1161 if (s is Return && t is Return && equivalentExpressions(s.value, t.value)) { | 1259 if (s is Return && t is Return) { |
| 1162 return s; | 1260 Expression e = combineExpressions(s.value, t.value); |
| 1261 if (e != null) { |
| 1262 return new Return(e); |
| 1263 } |
| 1163 } | 1264 } |
| 1164 return null; | 1265 return null; |
| 1165 } | 1266 } |
| 1166 | 1267 |
| 1167 /// True if the two expressions both syntactically and semantically | 1268 /// Returns an expression equivalent to both [e1] and [e2]. |
| 1168 /// equivalent. | 1269 /// If non-null is returned, the caller must discard [e1] and [e2] and use |
| 1169 static bool equivalentExpressions(Expression e1, Expression e2) { | 1270 /// the resulting expression in the tree. |
| 1170 if (e1 == e2) { // Detect same variable reference | 1271 static Expression combineExpressions(Expression e1, Expression e2) { |
| 1171 // TODO(asgerf): This might turn the variable into a single-use, | 1272 if (e1 is Variable && e1 == e2) { |
| 1172 // but we currently don't discover this. | 1273 --e1.readCount; // Two references become one. |
| 1173 return true; | 1274 return e1; |
| 1174 } | 1275 } |
| 1175 if (e1 is Constant && e2 is Constant) { | 1276 if (e1 is Constant && e2 is Constant && e1.value == e2.value) { |
| 1176 return e1.value == e2.value; | 1277 return e1; |
| 1177 } | 1278 } |
| 1178 return false; | 1279 return null; |
| 1179 } | 1280 } |
| 1180 | 1281 |
| 1181 /// Try to collapse nested ifs using && and || expressions. | 1282 /// Try to collapse nested ifs using && and || expressions. |
| 1182 /// For example: | 1283 /// For example: |
| 1183 /// | 1284 /// |
| 1184 /// if (E1) { if (E2) S else break L } else break L | 1285 /// if (E1) { if (E2) S else break L } else break L |
| 1185 /// ==> | 1286 /// ==> |
| 1186 /// if (E1 && E2) S else break L | 1287 /// if (E1 && E2) S else break L |
| 1187 /// | 1288 /// |
| 1188 /// [branch1] and [branch2] control the position of the S statement. | 1289 /// [branch1] and [branch2] control the position of the S statement. |
| (...skipping 605 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1794 } | 1895 } |
| 1795 } | 1896 } |
| 1796 | 1897 |
| 1797 /// Destructively updates each entry of [l] with the result of visiting it. | 1898 /// Destructively updates each entry of [l] with the result of visiting it. |
| 1798 void _rewriteList(List<Expression> l) { | 1899 void _rewriteList(List<Expression> l) { |
| 1799 for (int i = 0; i < l.length; i++) { | 1900 for (int i = 0; i < l.length; i++) { |
| 1800 l[i] = visitExpression(l[i]); | 1901 l[i] = visitExpression(l[i]); |
| 1801 } | 1902 } |
| 1802 } | 1903 } |
| 1803 } | 1904 } |
| OLD | NEW |