| Index: sdk/lib/_internal/compiler/implementation/ssa/codegen_helpers.dart
|
| diff --git a/sdk/lib/_internal/compiler/implementation/ssa/codegen_helpers.dart b/sdk/lib/_internal/compiler/implementation/ssa/codegen_helpers.dart
|
| deleted file mode 100644
|
| index abc6c8658399fa7253d83202f82b0d3e298e106e..0000000000000000000000000000000000000000
|
| --- a/sdk/lib/_internal/compiler/implementation/ssa/codegen_helpers.dart
|
| +++ /dev/null
|
| @@ -1,703 +0,0 @@
|
| -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -part of ssa;
|
| -
|
| -/**
|
| - * Replaces some instructions with specialized versions to make codegen easier.
|
| - * Caches codegen information on nodes.
|
| - */
|
| -class SsaInstructionSelection extends HBaseVisitor {
|
| - final Compiler compiler;
|
| - HGraph graph;
|
| -
|
| - SsaInstructionSelection(this.compiler);
|
| -
|
| - JavaScriptBackend get backend => compiler.backend;
|
| -
|
| - void visitGraph(HGraph graph) {
|
| - this.graph = graph;
|
| - visitDominatorTree(graph);
|
| - }
|
| -
|
| - visitBasicBlock(HBasicBlock block) {
|
| - HInstruction instruction = block.first;
|
| - while (instruction != null) {
|
| - HInstruction next = instruction.next;
|
| - HInstruction replacement = instruction.accept(this);
|
| - if (replacement != instruction && replacement != null) {
|
| - block.rewrite(instruction, replacement);
|
| -
|
| - // If the replacement instruction does not know its source element, use
|
| - // the source element of the instruction.
|
| - if (replacement.sourceElement == null) {
|
| - replacement.sourceElement = instruction.sourceElement;
|
| - }
|
| - if (replacement.sourcePosition == null) {
|
| - replacement.sourcePosition = instruction.sourcePosition;
|
| - }
|
| - if (!replacement.isInBasicBlock()) {
|
| - // The constant folding can return an instruction that is already
|
| - // part of the graph (like an input), so we only add the replacement
|
| - // if necessary.
|
| - block.addAfter(instruction, replacement);
|
| - // Visit the replacement as the next instruction in case it can also
|
| - // be constant folded away.
|
| - next = replacement;
|
| - }
|
| - block.remove(instruction);
|
| - }
|
| - instruction = next;
|
| - }
|
| - }
|
| -
|
| - HInstruction visitInstruction(HInstruction node) {
|
| - return node;
|
| - }
|
| -
|
| - HInstruction visitIs(HIs node) {
|
| - if (node.kind == HIs.RAW_CHECK) {
|
| - HInstruction interceptor = node.interceptor;
|
| - if (interceptor != null) {
|
| - return new HIsViaInterceptor(node.typeExpression, interceptor,
|
| - backend.boolType);
|
| - }
|
| - }
|
| - return node;
|
| - }
|
| -
|
| - HInstruction visitIdentity(HIdentity node) {
|
| - node.singleComparisonOp = simpleOp(node.left, node.right);
|
| - return node;
|
| - }
|
| -
|
| - String simpleOp(HInstruction left, HInstruction right) {
|
| - // Returns the single identity comparison (== or ===) or null if a more
|
| - // complex expression is required.
|
| - TypeMask leftType = left.instructionType;
|
| - TypeMask rightType = right.instructionType;
|
| - if (leftType.isNullable && rightType.isNullable) {
|
| - if (left.isConstantNull() ||
|
| - right.isConstantNull() ||
|
| - (left.isPrimitive(compiler) &&
|
| - leftType == rightType)) {
|
| - return '==';
|
| - }
|
| - return null;
|
| - }
|
| - return '===';
|
| - }
|
| -
|
| - HInstruction visitInvokeDynamic(HInvokeDynamic node) {
|
| - if (node.isInterceptedCall) {
|
| - // Calls of the form
|
| - //
|
| - // a.foo$1(a, x)
|
| - //
|
| - // where the interceptor calling convention is used come from recognizing
|
| - // that 'a' is a 'self-interceptor'. If the selector matches only methods
|
| - // that ignore the explicit receiver parameter, replace occurences of the
|
| - // receiver argument with a dummy receiver '0':
|
| - //
|
| - // a.foo$1(a, x) ---> a.foo$1(0, x)
|
| - //
|
| - // This often reduces the number of references to 'a' to one, allowing 'a'
|
| - // to be generated at use to avoid a temporary, e.g.
|
| - //
|
| - // t1 = b.get$thing();
|
| - // t1.foo$1(t1, x)
|
| - // --->
|
| - // b.get$thing().foo$1(0, x)
|
| - //
|
| - Selector selector = node.selector;
|
| - if (backend.isInterceptedSelector(selector) &&
|
| - !backend.isInterceptedMixinSelector(selector)) {
|
| - HInstruction interceptor = node.inputs[0];
|
| - HInstruction receiverArgument = node.inputs[1];
|
| -
|
| - if (interceptor.nonCheck() == receiverArgument.nonCheck()) {
|
| - // TODO(15933): Make automatically generated property extraction
|
| - // closures work with the dummy receiver optimization.
|
| - if (!selector.isGetter) {
|
| - ConstantValue constant = new DummyConstantValue(
|
| - receiverArgument.instructionType);
|
| - HConstant dummy = graph.addConstant(constant, compiler);
|
| - receiverArgument.usedBy.remove(node);
|
| - node.inputs[1] = dummy;
|
| - dummy.usedBy.add(node);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - return node;
|
| - }
|
| -
|
| - HInstruction visitFieldSet(HFieldSet setter) {
|
| - // Pattern match
|
| - // t1 = x.f; t2 = t1 + 1; x.f = t2; use(t2) --> ++x.f
|
| - // t1 = x.f; t2 = t1 op y; x.f = t2; use(t2) --> x.f op= y
|
| - // t1 = x.f; t2 = t1 + 1; x.f = t2; use(t1) --> x.f++
|
| - HBasicBlock block = setter.block;
|
| - HInstruction op = setter.value;
|
| - HInstruction receiver = setter.receiver;
|
| -
|
| - bool isMatchingRead(HInstruction candidate) {
|
| - if (candidate is HFieldGet) {
|
| - if (candidate.element != setter.element) return false;
|
| - if (candidate.receiver != setter.receiver) return false;
|
| - // Recognize only three instructions in sequence in the same block. This
|
| - // could be broadened to allow non-interfering interleaved instructions.
|
| - if (op.block != block) return false;
|
| - if (candidate.block != block) return false;
|
| - if (setter.previous != op) return false;
|
| - if (op.previous != candidate) return false;
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - HInstruction noMatchingRead() {
|
| - // If we have other HFieldSet optimizations, they go here.
|
| - return null;
|
| - }
|
| -
|
| - HInstruction replaceOp(HInstruction replacement, HInstruction getter) {
|
| - block.addBefore(setter, replacement);
|
| - block.remove(setter);
|
| - block.rewrite(op, replacement);
|
| - block.remove(op);
|
| - block.remove(getter);
|
| - return null;
|
| - }
|
| -
|
| - HInstruction plusOrMinus(String assignOp, String incrementOp) {
|
| - HInvokeBinary binary = op;
|
| - HInstruction left = binary.left;
|
| - HInstruction right = binary.right;
|
| - if (isMatchingRead(left)) {
|
| - if (left.usedBy.length == 1) {
|
| - if (right is HConstant && right.constant.isOne) {
|
| - HInstruction rmw = new HReadModifyWrite.preOp(
|
| - setter.element, incrementOp, receiver, op.instructionType);
|
| - return replaceOp(rmw, left);
|
| - } else {
|
| - HInstruction rmw = new HReadModifyWrite.assignOp(
|
| - setter.element,
|
| - assignOp,
|
| - receiver, right, op.instructionType);
|
| - return replaceOp(rmw, left);
|
| - }
|
| - } else if (op.usedBy.length == 1 &&
|
| - right is HConstant &&
|
| - right.constant.isOne) {
|
| - HInstruction rmw = new HReadModifyWrite.postOp(
|
| - setter.element, incrementOp, receiver, op.instructionType);
|
| - block.addAfter(left, rmw);
|
| - block.remove(setter);
|
| - block.remove(op);
|
| - block.rewrite(left, rmw);
|
| - block.remove(left);
|
| - return null;
|
| - }
|
| - }
|
| - return noMatchingRead();
|
| - }
|
| -
|
| - HInstruction simple(String assignOp,
|
| - HInstruction left, HInstruction right) {
|
| - if (isMatchingRead(left)) {
|
| - if (left.usedBy.length == 1) {
|
| - HInstruction rmw = new HReadModifyWrite.assignOp(
|
| - setter.element,
|
| - assignOp,
|
| - receiver, right, op.instructionType);
|
| - return replaceOp(rmw, left);
|
| - }
|
| - }
|
| - return noMatchingRead();
|
| - }
|
| -
|
| - HInstruction simpleBinary(String assignOp) {
|
| - HInvokeBinary binary = op;
|
| - return simple(assignOp, binary.left, binary.right);
|
| - }
|
| -
|
| - HInstruction bitop(String assignOp) {
|
| - // HBitAnd, HBitOr etc. are more difficult because HBitAnd(a.x, y)
|
| - // sometimes needs to be forced to unsigned: a.x = (a.x & y) >>> 0.
|
| - if (op.isUInt31(compiler)) return simpleBinary(assignOp);
|
| - return noMatchingRead();
|
| - }
|
| -
|
| - if (op is HAdd) return plusOrMinus('+', '++');
|
| - if (op is HSubtract) return plusOrMinus('-', '--');
|
| -
|
| - if (op is HStringConcat) return simple('+', op.left, op.right);
|
| -
|
| - if (op is HMultiply) return simpleBinary('*');
|
| - if (op is HDivide) return simpleBinary('/');
|
| -
|
| - if (op is HBitAnd) return bitop('&');
|
| - if (op is HBitOr) return bitop('|');
|
| - if (op is HBitXor) return bitop('^');
|
| -
|
| - return noMatchingRead();
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Remove [HTypeKnown] instructions from the graph, to make codegen
|
| - * analysis easier.
|
| - */
|
| -class SsaTypeKnownRemover extends HBaseVisitor {
|
| -
|
| - void visitGraph(HGraph graph) {
|
| - visitDominatorTree(graph);
|
| - }
|
| -
|
| - void visitBasicBlock(HBasicBlock block) {
|
| - HInstruction instruction = block.first;
|
| - while (instruction != null) {
|
| - HInstruction next = instruction.next;
|
| - instruction.accept(this);
|
| - instruction = next;
|
| - }
|
| - }
|
| -
|
| - void visitTypeKnown(HTypeKnown instruction) {
|
| - instruction.block.rewrite(instruction, instruction.checkedInput);
|
| - instruction.block.remove(instruction);
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Instead of emitting each SSA instruction with a temporary variable
|
| - * mark instructions that can be emitted at their use-site.
|
| - * For example, in:
|
| - * t0 = 4;
|
| - * t1 = 3;
|
| - * t2 = add(t0, t1);
|
| - * t0 and t1 would be marked and the resulting code would then be:
|
| - * t2 = add(4, 3);
|
| - */
|
| -class SsaInstructionMerger extends HBaseVisitor {
|
| - final Compiler compiler;
|
| - /**
|
| - * List of [HInstruction] that the instruction merger expects in
|
| - * order when visiting the inputs of an instruction.
|
| - */
|
| - List<HInstruction> expectedInputs;
|
| - /**
|
| - * Set of pure [HInstruction] that the instruction merger expects to
|
| - * find. The order of pure instructions do not matter, as they will
|
| - * not be affected by side effects.
|
| - */
|
| - Set<HInstruction> pureInputs;
|
| - Set<HInstruction> generateAtUseSite;
|
| -
|
| - void markAsGenerateAtUseSite(HInstruction instruction) {
|
| - assert(!instruction.isJsStatement());
|
| - generateAtUseSite.add(instruction);
|
| - }
|
| -
|
| - SsaInstructionMerger(this.generateAtUseSite, this.compiler);
|
| -
|
| - void visitGraph(HGraph graph) {
|
| - visitDominatorTree(graph);
|
| - }
|
| -
|
| - void analyzeInputs(HInstruction user, int start) {
|
| - List<HInstruction> inputs = user.inputs;
|
| - for (int i = start; i < inputs.length; i++) {
|
| - HInstruction input = inputs[i];
|
| - if (!generateAtUseSite.contains(input)
|
| - && !input.isCodeMotionInvariant()
|
| - && input.usedBy.length == 1
|
| - && input is !HPhi
|
| - && input is !HLocalValue
|
| - && !input.isJsStatement()) {
|
| - if (input.isPure()) {
|
| - // Only consider a pure input if it is in the same loop.
|
| - // Otherwise, we might move GVN'ed instruction back into the
|
| - // loop.
|
| - if (user.hasSameLoopHeaderAs(input)) {
|
| - // Move it closer to [user], so that instructions in
|
| - // between do not prevent making it generate at use site.
|
| - input.moveBefore(user);
|
| - pureInputs.add(input);
|
| - // Previous computations done on [input] are now invalid
|
| - // because we moved [input] to another place. So all
|
| - // non code motion invariant instructions need
|
| - // to be removed from the [generateAtUseSite] set.
|
| - input.inputs.forEach((instruction) {
|
| - if (!instruction.isCodeMotionInvariant()) {
|
| - generateAtUseSite.remove(instruction);
|
| - }
|
| - });
|
| - // Visit the pure input now so that the expected inputs
|
| - // are after the expected inputs of [user].
|
| - input.accept(this);
|
| - }
|
| - } else {
|
| - expectedInputs.add(input);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - void visitInstruction(HInstruction instruction) {
|
| - // A code motion invariant instruction is dealt before visiting it.
|
| - assert(!instruction.isCodeMotionInvariant());
|
| - analyzeInputs(instruction, 0);
|
| - }
|
| -
|
| - // The codegen might use the input multiple times, so it must not be
|
| - // set generate at use site.
|
| - void visitIs(HIs instruction) {}
|
| -
|
| - // A bounds check method must not have its first input generated at use site,
|
| - // because it's using it twice.
|
| - void visitBoundsCheck(HBoundsCheck instruction) {
|
| - analyzeInputs(instruction, 1);
|
| - }
|
| -
|
| - // An identity operation must only have its inputs generated at use site if
|
| - // does not require an expression with multiple uses (because of null /
|
| - // undefined).
|
| - void visitIdentity(HIdentity instruction) {
|
| - if (instruction.singleComparisonOp != null) {
|
| - super.visitIdentity(instruction);
|
| - }
|
| - // Do nothing.
|
| - }
|
| -
|
| - void visitTypeConversion(HTypeConversion instruction) {
|
| - if (!instruction.isArgumentTypeCheck
|
| - && !instruction.isReceiverTypeCheck) {
|
| - assert(instruction.isCheckedModeCheck || instruction.isCastTypeCheck);
|
| - // Checked mode checks and cast checks compile to code that
|
| - // only use their input once, so we can safely visit them
|
| - // and try to merge the input.
|
| - visitInstruction(instruction);
|
| - }
|
| - }
|
| -
|
| - void visitTypeKnown(HTypeKnown instruction) {
|
| - // [HTypeKnown] instructions are removed before code generation.
|
| - assert(false);
|
| - }
|
| -
|
| - void tryGenerateAtUseSite(HInstruction instruction) {
|
| - if (instruction.isControlFlow()) return;
|
| - markAsGenerateAtUseSite(instruction);
|
| - }
|
| -
|
| - bool isBlockSinglePredecessor(HBasicBlock block) {
|
| - return block.successors.length == 1
|
| - && block.successors[0].predecessors.length == 1;
|
| - }
|
| -
|
| - void visitBasicBlock(HBasicBlock block) {
|
| - // Compensate from not merging blocks: if the block is the
|
| - // single predecessor of its single successor, let the successor
|
| - // visit it.
|
| - if (isBlockSinglePredecessor(block)) return;
|
| -
|
| - tryMergingExpressions(block);
|
| - }
|
| -
|
| - void tryMergingExpressions(HBasicBlock block) {
|
| - // Visit each instruction of the basic block in last-to-first order.
|
| - // Keep a list of expected inputs of the current "expression" being
|
| - // merged. If instructions occur in the expected order, they are
|
| - // included in the expression.
|
| -
|
| - // The expectedInputs list holds non-trivial instructions that may
|
| - // be generated at their use site, if they occur in the correct order.
|
| - if (expectedInputs == null) expectedInputs = new List<HInstruction>();
|
| - if (pureInputs == null) pureInputs = new Set<HInstruction>();
|
| -
|
| - // Pop instructions from expectedInputs until instruction is found.
|
| - // Return true if it is found, or false if not.
|
| - bool findInInputsAndPopNonMatching(HInstruction instruction) {
|
| - assert(!instruction.isPure());
|
| - while (!expectedInputs.isEmpty) {
|
| - HInstruction nextInput = expectedInputs.removeLast();
|
| - assert(!generateAtUseSite.contains(nextInput));
|
| - assert(nextInput.usedBy.length == 1);
|
| - if (identical(nextInput, instruction)) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - block.last.accept(this);
|
| - for (HInstruction instruction = block.last.previous;
|
| - instruction != null;
|
| - instruction = instruction.previous) {
|
| - if (generateAtUseSite.contains(instruction)) {
|
| - continue;
|
| - }
|
| - if (instruction.isCodeMotionInvariant()) {
|
| - markAsGenerateAtUseSite(instruction);
|
| - continue;
|
| - }
|
| - if (instruction.isPure()) {
|
| - if (pureInputs.contains(instruction)) {
|
| - tryGenerateAtUseSite(instruction);
|
| - } else {
|
| - // If the input is not in the [pureInputs] set, it has not
|
| - // been visited or should not be generated at use-site. The most
|
| - // likely reason for the latter, is that the instruction is used
|
| - // in more than one location.
|
| - // We must either clear the expectedInputs, or move the pure
|
| - // instruction's inputs in front of the existing ones.
|
| - // Example:
|
| - // t1 = foo(); // side-effect.
|
| - // t2 = bar(); // side-effect.
|
| - // t3 = pure(t2); // used more than once.
|
| - // f(t1, t3); // expected inputs of 'f': t1.
|
| - // use(t3);
|
| - //
|
| - // If we don't clear the expected inputs we end up in a situation
|
| - // where pure pushes "t2" on top of "t1" leading to:
|
| - // t3 = pure(bar());
|
| - // f(foo(), t3);
|
| - // use(t3);
|
| - //
|
| - // If we clear the expected-inputs list we have the correct
|
| - // output:
|
| - // t1 = foo();
|
| - // t3 = pure(bar());
|
| - // f(t1, t3);
|
| - // use(t3);
|
| - //
|
| - // Clearing is, however, not optimal.
|
| - // Example:
|
| - // t1 = foo(); // t1 is now used by `pure`.
|
| - // t2 = bar(); // t2 is now used by `f`.
|
| - // t3 = pure(t1);
|
| - // f(t2, t3);
|
| - // use(t3);
|
| - //
|
| - // If we clear the expected-inputs we can't generate-at-use any of
|
| - // the instructions.
|
| - //
|
| - // The optimal solution is to move the inputs of 'pure' in
|
| - // front of the expectedInputs list. This makes sense, since we
|
| - // push expected-inputs from left-to right, and the `pure` function
|
| - // invocation is "more left" (i.e. before) the first argument of `f`.
|
| - // With that approach we end up with:
|
| - // t3 = pure(foo();
|
| - // f(bar(), t3);
|
| - // use(t3);
|
| - //
|
| - int oldLength = expectedInputs.length;
|
| - instruction.accept(this);
|
| - if (oldLength != 0 && oldLength != expectedInputs.length) {
|
| - // Move the pure instruction's inputs to the front.
|
| - List<HInstruction> newInputs = expectedInputs.sublist(oldLength);
|
| - int newCount = newInputs.length;
|
| - expectedInputs.setRange(
|
| - newCount, newCount + oldLength, expectedInputs);
|
| - expectedInputs.setRange(0, newCount, newInputs);
|
| - }
|
| - }
|
| - } else {
|
| - if (findInInputsAndPopNonMatching(instruction)) {
|
| - // The current instruction is the next non-trivial
|
| - // expected input.
|
| - tryGenerateAtUseSite(instruction);
|
| - } else {
|
| - assert(expectedInputs.isEmpty);
|
| - }
|
| - instruction.accept(this);
|
| - }
|
| - }
|
| -
|
| - if (block.predecessors.length == 1
|
| - && isBlockSinglePredecessor(block.predecessors[0])) {
|
| - assert(block.phis.isEmpty);
|
| - tryMergingExpressions(block.predecessors[0]);
|
| - } else {
|
| - expectedInputs = null;
|
| - pureInputs = null;
|
| - }
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Detect control flow arising from short-circuit logical and
|
| - * conditional operators, and prepare the program to be generated
|
| - * using these operators instead of nested ifs and boolean variables.
|
| - */
|
| -class SsaConditionMerger extends HGraphVisitor {
|
| - Set<HInstruction> generateAtUseSite;
|
| - Set<HInstruction> controlFlowOperators;
|
| -
|
| - void markAsGenerateAtUseSite(HInstruction instruction) {
|
| - assert(!instruction.isJsStatement());
|
| - generateAtUseSite.add(instruction);
|
| - }
|
| -
|
| - SsaConditionMerger(this.generateAtUseSite, this.controlFlowOperators);
|
| -
|
| - void visitGraph(HGraph graph) {
|
| - visitPostDominatorTree(graph);
|
| - }
|
| -
|
| - /**
|
| - * Check if a block has at least one statement other than
|
| - * [instruction].
|
| - */
|
| - bool hasAnyStatement(HBasicBlock block, HInstruction instruction) {
|
| - // If [instruction] is not in [block], then if the block is not
|
| - // empty, we know there will be a statement to emit.
|
| - if (!identical(instruction.block, block)) return !identical(block.last, block.first);
|
| -
|
| - // If [instruction] is not the last instruction of the block
|
| - // before the control flow instruction, or the last instruction,
|
| - // then we will have to emit a statement for that last instruction.
|
| - if (instruction != block.last
|
| - && !identical(instruction, block.last.previous)) return true;
|
| -
|
| - // If one of the instructions in the block until [instruction] is
|
| - // not generated at use site, then we will have to emit a
|
| - // statement for it.
|
| - // TODO(ngeoffray): we could generate a comma separated
|
| - // list of expressions.
|
| - for (HInstruction temp = block.first;
|
| - !identical(temp, instruction);
|
| - temp = temp.next) {
|
| - if (!generateAtUseSite.contains(temp)) return true;
|
| - }
|
| -
|
| - return false;
|
| - }
|
| -
|
| - bool isSafeToGenerateAtUseSite(HInstruction user, HInstruction input) {
|
| - // A [HForeign] instruction uses operators and if we generate
|
| - // [input] at use site, the precedence might be wrong.
|
| - if (user is HForeign) return false;
|
| - // A [HCheck] instruction with control flow uses its input
|
| - // multiple times, so we avoid generating it at use site.
|
| - if (user is HCheck && user.isControlFlow()) return false;
|
| - // A [HIs] instruction uses its input multiple times, so we
|
| - // avoid generating it at use site.
|
| - if (user is HIs) return false;
|
| - return true;
|
| - }
|
| -
|
| - void visitBasicBlock(HBasicBlock block) {
|
| - if (block.last is !HIf) return;
|
| - HIf startIf = block.last;
|
| - HBasicBlock end = startIf.joinBlock;
|
| -
|
| - // We check that the structure is the following:
|
| - // If
|
| - // / \
|
| - // / \
|
| - // 1 expr goto
|
| - // goto /
|
| - // \ /
|
| - // \ /
|
| - // phi(expr, true|false)
|
| - //
|
| - // and the same for nested nodes:
|
| - //
|
| - // If
|
| - // / \
|
| - // / \
|
| - // 1 expr1 \
|
| - // If \
|
| - // / \ \
|
| - // / \ goto
|
| - // 1 expr2 |
|
| - // goto goto |
|
| - // \ / |
|
| - // \ / |
|
| - // phi1(expr2, true|false)
|
| - // \ |
|
| - // \ |
|
| - // phi(phi1, true|false)
|
| -
|
| - if (end == null) return;
|
| - if (end.phis.isEmpty) return;
|
| - if (!identical(end.phis.first, end.phis.last)) return;
|
| - HBasicBlock elseBlock = startIf.elseBlock;
|
| -
|
| - if (!identical(end.predecessors[1], elseBlock)) return;
|
| - HPhi phi = end.phis.first;
|
| - HInstruction thenInput = phi.inputs[0];
|
| - HInstruction elseInput = phi.inputs[1];
|
| - if (thenInput.isJsStatement() || elseInput.isJsStatement()) return;
|
| -
|
| - if (hasAnyStatement(elseBlock, elseInput)) return;
|
| - assert(elseBlock.successors.length == 1);
|
| - assert(end.predecessors.length == 2);
|
| -
|
| - HBasicBlock thenBlock = startIf.thenBlock;
|
| - // Skip trivial goto blocks.
|
| - while (thenBlock.successors[0] != end && thenBlock.first is HGoto) {
|
| - thenBlock = thenBlock.successors[0];
|
| - }
|
| -
|
| - // If the [thenBlock] is already a control flow operation, and does not
|
| - // have any statement and its join block is [end], we can emit a
|
| - // sequence of control flow operation.
|
| - if (controlFlowOperators.contains(thenBlock.last)) {
|
| - HIf otherIf = thenBlock.last;
|
| - if (!identical(otherIf.joinBlock, end)) {
|
| - // This could be a join block that just feeds into our join block.
|
| - HBasicBlock otherJoin = otherIf.joinBlock;
|
| - if (otherJoin.first != otherJoin.last) return;
|
| - if (otherJoin.successors.length != 1) return;
|
| - if (otherJoin.successors[0] != end) return;
|
| - if (otherJoin.phis.isEmpty) return;
|
| - if (!identical(otherJoin.phis.first, otherJoin.phis.last)) return;
|
| - HPhi otherPhi = otherJoin.phis.first;
|
| - if (thenInput != otherPhi) return;
|
| - if (elseInput != otherPhi.inputs[1]) return;
|
| - }
|
| - if (hasAnyStatement(thenBlock, otherIf)) return;
|
| - } else {
|
| - if (!identical(end.predecessors[0], thenBlock)) return;
|
| - if (hasAnyStatement(thenBlock, thenInput)) return;
|
| - assert(thenBlock.successors.length == 1);
|
| - }
|
| -
|
| - // From now on, we have recognized a control flow operation built from
|
| - // the builder. Mark the if instruction as such.
|
| - controlFlowOperators.add(startIf);
|
| -
|
| - // Find the next non-HGoto instruction following the phi.
|
| - HInstruction nextInstruction = phi.block.first;
|
| - while (nextInstruction is HGoto) {
|
| - nextInstruction = nextInstruction.block.successors[0].first;
|
| - }
|
| -
|
| - // If the operation is only used by the first instruction
|
| - // of its block and is safe to be generated at use site, mark it
|
| - // so.
|
| - if (phi.usedBy.length == 1
|
| - && phi.usedBy[0] == nextInstruction
|
| - && isSafeToGenerateAtUseSite(phi.usedBy[0], phi)) {
|
| - markAsGenerateAtUseSite(phi);
|
| - }
|
| -
|
| - if (identical(elseInput.block, elseBlock)) {
|
| - assert(elseInput.usedBy.length == 1);
|
| - markAsGenerateAtUseSite(elseInput);
|
| - }
|
| -
|
| - // If [thenInput] is defined in the first predecessor, then it is only used
|
| - // by [phi] and can be generated at use site.
|
| - if (identical(thenInput.block, end.predecessors[0])) {
|
| - assert(thenInput.usedBy.length == 1);
|
| - markAsGenerateAtUseSite(thenInput);
|
| - }
|
| - }
|
| -}
|
|
|