| Index: pkg/compiler/lib/src/ssa/builder.dart
|
| diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
|
| index c9ef7eeb36a8334be1988372ff04a6645a278c72..1f5d56b36da1d0db797b4bfae3cd61574ca2875f 100644
|
| --- a/pkg/compiler/lib/src/ssa/builder.dart
|
| +++ b/pkg/compiler/lib/src/ssa/builder.dart
|
| @@ -38,10 +38,12 @@ import '../universe/side_effects.dart' show SideEffects;
|
| import '../universe/use.dart' show DynamicUse, StaticUse, TypeUse;
|
| import '../util/util.dart';
|
| import '../world.dart' show ClassWorld;
|
| +
|
| import 'graph_builder.dart';
|
| import 'locals_handler.dart';
|
| import 'nodes.dart';
|
| import 'optimize.dart';
|
| +import 'ssa_branch_builder.dart';
|
| import 'types.dart';
|
|
|
| /// A synthetic local variable only used with the SSA graph.
|
| @@ -411,8 +413,6 @@ class SsaBuilder extends ast.Visitor
|
| */
|
| final List<Element> sourceElementStack = <Element>[];
|
|
|
| - LocalsHandler localsHandler;
|
| -
|
| HInstruction rethrowableException;
|
|
|
| Map<JumpTarget, JumpHandler> jumpTargets = <JumpTarget, JumpHandler>{};
|
| @@ -1907,6 +1907,7 @@ class SsaBuilder extends ast.Visitor
|
| push(attachPosition(instruction, node));
|
| }
|
|
|
| + @override
|
| HInstruction popBoolified() {
|
| HInstruction value = pop();
|
| if (_checkOrTrustTypes) {
|
| @@ -2537,31 +2538,69 @@ class SsaBuilder extends ast.Visitor
|
| void visitThen(),
|
| void visitElse(),
|
| SourceInformation sourceInformation}) {
|
| - SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, diagnosticNode);
|
| + SsaBranchBuilder branchBuilder =
|
| + new SsaBranchBuilder(this, compiler, diagnosticNode);
|
| branchBuilder.handleIf(visitCondition, visitThen, visitElse,
|
| sourceInformation: sourceInformation);
|
| }
|
|
|
| @override
|
| void visitIfNull(ast.Send node, ast.Node left, ast.Node right, _) {
|
| - SsaBranchBuilder brancher = new SsaBranchBuilder(this, node);
|
| + SsaBranchBuilder brancher = new SsaBranchBuilder(this, compiler, node);
|
| brancher.handleIfNull(() => visit(left), () => visit(right));
|
| }
|
|
|
| + /// Optimizes logical binary where the left is also a logical binary.
|
| + ///
|
| + /// This method transforms the operator by optimizing the case where [left] is
|
| + /// a logical "and" or logical "or". Then it uses [branchBuilder] to build the
|
| + /// graph for the optimized expression.
|
| + ///
|
| + /// For example, `(x && y) && z` is transformed into `x && (y && z)`:
|
| + ///
|
| + /// t0 = boolify(x);
|
| + /// if (t0) {
|
| + /// t1 = boolify(y);
|
| + /// if (t1) {
|
| + /// t2 = boolify(z);
|
| + /// }
|
| + /// t3 = phi(t2, false);
|
| + /// }
|
| + /// result = phi(t3, false);
|
| + void handleLogicalBinaryWithLeftNode(
|
| + ast.Node left, void visitRight(), SsaBranchBuilder branchBuilder,
|
| + {bool isAnd}) {
|
| + ast.Send send = left.asSend();
|
| + if (send != null && (isAnd ? send.isLogicalAnd : send.isLogicalOr)) {
|
| + ast.Node newLeft = send.receiver;
|
| + Link<ast.Node> link = send.argumentsNode.nodes;
|
| + assert(link.tail.isEmpty);
|
| + ast.Node middle = link.head;
|
| + handleLogicalBinaryWithLeftNode(
|
| + newLeft,
|
| + () => handleLogicalBinaryWithLeftNode(
|
| + middle, visitRight, branchBuilder,
|
| + isAnd: isAnd),
|
| + branchBuilder,
|
| + isAnd: isAnd);
|
| + } else {
|
| + branchBuilder.handleLogicalBinary(() => visit(left), visitRight,
|
| + isAnd: isAnd);
|
| + }
|
| + }
|
| +
|
| @override
|
| void visitLogicalAnd(ast.Send node, ast.Node left, ast.Node right, _) {
|
| - SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, node);
|
| - branchBuilder.handleLogicalAndOrWithLeftNode(left, () {
|
| - visit(right);
|
| - }, isAnd: true);
|
| + SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, compiler, node);
|
| + handleLogicalBinaryWithLeftNode(left, () => visit(right), branchBuilder,
|
| + isAnd: true);
|
| }
|
|
|
| @override
|
| void visitLogicalOr(ast.Send node, ast.Node left, ast.Node right, _) {
|
| - SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, node);
|
| - branchBuilder.handleLogicalAndOrWithLeftNode(left, () {
|
| - visit(right);
|
| - }, isAnd: false);
|
| + SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, compiler, node);
|
| + handleLogicalBinaryWithLeftNode(left, () => visit(right), branchBuilder,
|
| + isAnd: false);
|
| }
|
|
|
| @override
|
| @@ -2819,7 +2858,7 @@ class SsaBuilder extends ast.Visitor
|
| // we will be able to later compress it as:
|
| // t1 || t1.x
|
| HInstruction expression;
|
| - SsaBranchBuilder brancher = new SsaBranchBuilder(this, node);
|
| + SsaBranchBuilder brancher = new SsaBranchBuilder(this, compiler, node);
|
| brancher.handleConditional(
|
| () {
|
| expression = visitAndPop(receiver);
|
| @@ -2835,7 +2874,7 @@ class SsaBuilder extends ast.Visitor
|
| });
|
| }
|
|
|
| - /// Pushes a boolean checking [expression] against null.
|
| + @override
|
| pushCheckNull(HInstruction expression) {
|
| push(new HIdentity(
|
| expression, graph.addConstantNull(compiler), null, backend.boolType));
|
| @@ -3202,7 +3241,7 @@ class SsaBuilder extends ast.Visitor
|
| ast.NodeList arguments, Selector selector, _) {
|
| /// Desugar `exp?.m()` to `(t1 = exp) == null ? t1 : t1.m()`
|
| HInstruction receiver;
|
| - SsaBranchBuilder brancher = new SsaBranchBuilder(this, node);
|
| + SsaBranchBuilder brancher = new SsaBranchBuilder(this, compiler, node);
|
| brancher.handleConditional(() {
|
| receiver = generateInstanceSendReceiver(node);
|
| pushCheckNull(receiver);
|
| @@ -5008,7 +5047,7 @@ class SsaBuilder extends ast.Visitor
|
| }
|
|
|
| if (node.isIfNullAssignment) {
|
| - SsaBranchBuilder brancher = new SsaBranchBuilder(this, node);
|
| + SsaBranchBuilder brancher = new SsaBranchBuilder(this, compiler, node);
|
| brancher.handleIfNull(() => stack.add(getterInstruction), () {
|
| addDynamicSendArgumentsToList(node, setterInputs);
|
| generateSuperSendSet();
|
| @@ -5328,7 +5367,7 @@ class SsaBuilder extends ast.Visitor
|
| // if (t1 == null)
|
| // t1 = x[i] = e;
|
| // result = t1
|
| - SsaBranchBuilder brancher = new SsaBranchBuilder(this, node);
|
| + SsaBranchBuilder brancher = new SsaBranchBuilder(this, compiler, node);
|
| brancher.handleIfNull(() => stack.add(getterInstruction), () {
|
| visit(arguments.head);
|
| HInstruction value = pop();
|
| @@ -5376,7 +5415,7 @@ class SsaBuilder extends ast.Visitor
|
| // else
|
| // result = e.x = e2
|
| HInstruction receiverInstruction;
|
| - SsaBranchBuilder brancher = new SsaBranchBuilder(this, node);
|
| + SsaBranchBuilder brancher = new SsaBranchBuilder(this, compiler, node);
|
| brancher.handleConditional(
|
| () {
|
| receiverInstruction = generateInstanceSendReceiver(node);
|
| @@ -5549,7 +5588,8 @@ class SsaBuilder extends ast.Visitor
|
| receiver);
|
| HInstruction getterInstruction = pop();
|
| if (node.isIfNullAssignment) {
|
| - SsaBranchBuilder brancher = new SsaBranchBuilder(this, node);
|
| + SsaBranchBuilder brancher =
|
| + new SsaBranchBuilder(this, compiler, node);
|
| brancher.handleIfNull(() => stack.add(getterInstruction), () {
|
| visit(node.arguments.head);
|
| generateInstanceSetterWithCompiledReceiver(node, receiver, pop());
|
| @@ -5570,7 +5610,7 @@ class SsaBuilder extends ast.Visitor
|
| // t1 = e
|
| // t1 == null ? t1 : (t1.x = t1.x op e2);
|
| HInstruction receiver;
|
| - SsaBranchBuilder brancher = new SsaBranchBuilder(this, node);
|
| + SsaBranchBuilder brancher = new SsaBranchBuilder(this, compiler, node);
|
| brancher.handleConditional(() {
|
| receiver = generateInstanceSendReceiver(node);
|
| pushCheckNull(receiver);
|
| @@ -5596,7 +5636,7 @@ class SsaBuilder extends ast.Visitor
|
| }
|
| HInstruction getterInstruction = pop();
|
| if (node.isIfNullAssignment) {
|
| - SsaBranchBuilder brancher = new SsaBranchBuilder(this, node);
|
| + SsaBranchBuilder brancher = new SsaBranchBuilder(this, compiler, node);
|
| brancher.handleIfNull(() => stack.add(getterInstruction), () {
|
| visit(node.arguments.head);
|
| generateNonInstanceSetter(node, element, pop());
|
| @@ -6006,7 +6046,7 @@ class SsaBuilder extends ast.Visitor
|
| }
|
|
|
| visitConditional(ast.Conditional node) {
|
| - SsaBranchBuilder brancher = new SsaBranchBuilder(this, node);
|
| + SsaBranchBuilder brancher = new SsaBranchBuilder(this, compiler, node);
|
| brancher.handleConditional(() => visit(node.condition),
|
| () => visit(node.thenExpression), () => visit(node.elseExpression));
|
| }
|
| @@ -7478,252 +7518,6 @@ class AstInliningState extends InliningState {
|
| : super(function);
|
| }
|
|
|
| -class SsaBranch {
|
| - final SsaBranchBuilder branchBuilder;
|
| - final HBasicBlock block;
|
| - LocalsHandler startLocals;
|
| - LocalsHandler exitLocals;
|
| - SubGraph graph;
|
| -
|
| - SsaBranch(this.branchBuilder) : block = new HBasicBlock();
|
| -}
|
| -
|
| -class SsaBranchBuilder {
|
| - final SsaBuilder builder;
|
| - final ast.Node diagnosticNode;
|
| -
|
| - SsaBranchBuilder(this.builder, [this.diagnosticNode]);
|
| -
|
| - Compiler get compiler => builder.compiler;
|
| -
|
| - void checkNotAborted() {
|
| - if (builder.isAborted()) {
|
| - compiler.unimplemented(diagnosticNode, "aborted control flow");
|
| - }
|
| - }
|
| -
|
| - void buildCondition(
|
| - void visitCondition(),
|
| - SsaBranch conditionBranch,
|
| - SsaBranch thenBranch,
|
| - SsaBranch elseBranch,
|
| - SourceInformation sourceInformation) {
|
| - startBranch(conditionBranch);
|
| - visitCondition();
|
| - checkNotAborted();
|
| - assert(identical(builder.current, builder.lastOpenedBlock));
|
| - HInstruction conditionValue = builder.popBoolified();
|
| - HIf branch = new HIf(conditionValue)..sourceInformation = sourceInformation;
|
| - HBasicBlock conditionExitBlock = builder.current;
|
| - builder.close(branch);
|
| - conditionBranch.exitLocals = builder.localsHandler;
|
| - conditionExitBlock.addSuccessor(thenBranch.block);
|
| - conditionExitBlock.addSuccessor(elseBranch.block);
|
| - bool conditionBranchLocalsCanBeReused =
|
| - mergeLocals(conditionBranch, thenBranch, mayReuseFromLocals: true);
|
| - mergeLocals(conditionBranch, elseBranch,
|
| - mayReuseFromLocals: conditionBranchLocalsCanBeReused);
|
| -
|
| - conditionBranch.graph =
|
| - new SubExpression(conditionBranch.block, conditionExitBlock);
|
| - }
|
| -
|
| - /**
|
| - * Returns true if the locals of the [fromBranch] may be reused. A [:true:]
|
| - * return value implies that [mayReuseFromLocals] was set to [:true:].
|
| - */
|
| - bool mergeLocals(SsaBranch fromBranch, SsaBranch toBranch,
|
| - {bool mayReuseFromLocals}) {
|
| - LocalsHandler fromLocals = fromBranch.exitLocals;
|
| - if (toBranch.startLocals == null) {
|
| - if (mayReuseFromLocals) {
|
| - toBranch.startLocals = fromLocals;
|
| - return false;
|
| - } else {
|
| - toBranch.startLocals = new LocalsHandler.from(fromLocals);
|
| - return true;
|
| - }
|
| - } else {
|
| - toBranch.startLocals.mergeWith(fromLocals, toBranch.block);
|
| - return true;
|
| - }
|
| - }
|
| -
|
| - void startBranch(SsaBranch branch) {
|
| - builder.graph.addBlock(branch.block);
|
| - builder.localsHandler = branch.startLocals;
|
| - builder.open(branch.block);
|
| - }
|
| -
|
| - HInstruction buildBranch(SsaBranch branch, void visitBranch(),
|
| - SsaBranch joinBranch, bool isExpression) {
|
| - startBranch(branch);
|
| - visitBranch();
|
| - branch.graph = new SubGraph(branch.block, builder.lastOpenedBlock);
|
| - branch.exitLocals = builder.localsHandler;
|
| - if (!builder.isAborted()) {
|
| - builder.goto(builder.current, joinBranch.block);
|
| - mergeLocals(branch, joinBranch, mayReuseFromLocals: true);
|
| - }
|
| - if (isExpression) {
|
| - checkNotAborted();
|
| - return builder.pop();
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - handleIf(void visitCondition(), void visitThen(), void visitElse(),
|
| - {SourceInformation sourceInformation}) {
|
| - if (visitElse == null) {
|
| - // Make sure to have an else part to avoid a critical edge. A
|
| - // critical edge is an edge that connects a block with multiple
|
| - // successors to a block with multiple predecessors. We avoid
|
| - // such edges because they prevent inserting copies during code
|
| - // generation of phi instructions.
|
| - visitElse = () {};
|
| - }
|
| -
|
| - _handleDiamondBranch(visitCondition, visitThen, visitElse,
|
| - isExpression: false, sourceInformation: sourceInformation);
|
| - }
|
| -
|
| - handleConditional(void visitCondition(), void visitThen(), void visitElse()) {
|
| - assert(visitElse != null);
|
| - _handleDiamondBranch(visitCondition, visitThen, visitElse,
|
| - isExpression: true);
|
| - }
|
| -
|
| - handleIfNull(void left(), void right()) {
|
| - // x ?? y is transformed into: x == null ? y : x
|
| - HInstruction leftExpression;
|
| - handleConditional(() {
|
| - left();
|
| - leftExpression = builder.pop();
|
| - builder.pushCheckNull(leftExpression);
|
| - }, right, () => builder.stack.add(leftExpression));
|
| - }
|
| -
|
| - void handleLogicalAndOr(void left(), void right(), {bool isAnd}) {
|
| - // x && y is transformed into:
|
| - // t0 = boolify(x);
|
| - // if (t0) {
|
| - // t1 = boolify(y);
|
| - // }
|
| - // result = phi(t1, false);
|
| - //
|
| - // x || y is transformed into:
|
| - // t0 = boolify(x);
|
| - // if (not(t0)) {
|
| - // t1 = boolify(y);
|
| - // }
|
| - // result = phi(t1, true);
|
| - HInstruction boolifiedLeft;
|
| - HInstruction boolifiedRight;
|
| -
|
| - void visitCondition() {
|
| - left();
|
| - boolifiedLeft = builder.popBoolified();
|
| - builder.stack.add(boolifiedLeft);
|
| - if (!isAnd) {
|
| - builder.push(new HNot(builder.pop(), builder.backend.boolType));
|
| - }
|
| - }
|
| -
|
| - void visitThen() {
|
| - right();
|
| - boolifiedRight = builder.popBoolified();
|
| - }
|
| -
|
| - handleIf(visitCondition, visitThen, null);
|
| - HConstant notIsAnd =
|
| - builder.graph.addConstantBool(!isAnd, builder.compiler);
|
| - JavaScriptBackend backend = builder.backend;
|
| - HPhi result = new HPhi.manyInputs(
|
| - null, <HInstruction>[boolifiedRight, notIsAnd], backend.dynamicType);
|
| - builder.current.addPhi(result);
|
| - builder.stack.add(result);
|
| - }
|
| -
|
| - void handleLogicalAndOrWithLeftNode(ast.Node left, void visitRight(),
|
| - {bool isAnd}) {
|
| - // This method is similar to [handleLogicalAndOr] but optimizes the case
|
| - // where left is a logical "and" or logical "or".
|
| - //
|
| - // For example (x && y) && z is transformed into x && (y && z):
|
| - // t0 = boolify(x);
|
| - // if (t0) {
|
| - // t1 = boolify(y);
|
| - // if (t1) {
|
| - // t2 = boolify(z);
|
| - // }
|
| - // t3 = phi(t2, false);
|
| - // }
|
| - // result = phi(t3, false);
|
| -
|
| - ast.Send send = left.asSend();
|
| - if (send != null && (isAnd ? send.isLogicalAnd : send.isLogicalOr)) {
|
| - ast.Node newLeft = send.receiver;
|
| - Link<ast.Node> link = send.argumentsNode.nodes;
|
| - assert(link.tail.isEmpty);
|
| - ast.Node middle = link.head;
|
| - handleLogicalAndOrWithLeftNode(
|
| - newLeft,
|
| - () =>
|
| - handleLogicalAndOrWithLeftNode(middle, visitRight, isAnd: isAnd),
|
| - isAnd: isAnd);
|
| - } else {
|
| - handleLogicalAndOr(() => builder.visit(left), visitRight, isAnd: isAnd);
|
| - }
|
| - }
|
| -
|
| - void _handleDiamondBranch(
|
| - void visitCondition(), void visitThen(), void visitElse(),
|
| - {bool isExpression, SourceInformation sourceInformation}) {
|
| - SsaBranch conditionBranch = new SsaBranch(this);
|
| - SsaBranch thenBranch = new SsaBranch(this);
|
| - SsaBranch elseBranch = new SsaBranch(this);
|
| - SsaBranch joinBranch = new SsaBranch(this);
|
| -
|
| - conditionBranch.startLocals = builder.localsHandler;
|
| - builder.goto(builder.current, conditionBranch.block);
|
| -
|
| - buildCondition(visitCondition, conditionBranch, thenBranch, elseBranch,
|
| - sourceInformation);
|
| - HInstruction thenValue =
|
| - buildBranch(thenBranch, visitThen, joinBranch, isExpression);
|
| - HInstruction elseValue =
|
| - buildBranch(elseBranch, visitElse, joinBranch, isExpression);
|
| -
|
| - if (isExpression) {
|
| - assert(thenValue != null && elseValue != null);
|
| - JavaScriptBackend backend = builder.backend;
|
| - HPhi phi = new HPhi.manyInputs(
|
| - null, <HInstruction>[thenValue, elseValue], backend.dynamicType);
|
| - joinBranch.block.addPhi(phi);
|
| - builder.stack.add(phi);
|
| - }
|
| -
|
| - HBasicBlock joinBlock;
|
| - // If at least one branch did not abort, open the joinBranch.
|
| - if (!joinBranch.block.predecessors.isEmpty) {
|
| - startBranch(joinBranch);
|
| - joinBlock = joinBranch.block;
|
| - }
|
| -
|
| - HIfBlockInformation info = new HIfBlockInformation(
|
| - new HSubExpressionBlockInformation(conditionBranch.graph),
|
| - new HSubGraphBlockInformation(thenBranch.graph),
|
| - new HSubGraphBlockInformation(elseBranch.graph));
|
| -
|
| - HBasicBlock conditionStartBlock = conditionBranch.block;
|
| - conditionStartBlock.setBlockFlow(info, joinBlock);
|
| - SubGraph conditionGraph = conditionBranch.graph;
|
| - HIf branch = conditionGraph.end.last;
|
| - assert(branch is HIf);
|
| - branch.blockInformation = conditionStartBlock.blockFlow;
|
| - }
|
| -}
|
| -
|
| class TypeBuilder implements DartTypeVisitor<dynamic, SsaBuilder> {
|
| final ClassWorld classWorld;
|
|
|
|
|