| Index: frog/leg/ssa/optimize.dart
|
| ===================================================================
|
| --- frog/leg/ssa/optimize.dart (revision 5925)
|
| +++ frog/leg/ssa/optimize.dart (working copy)
|
| @@ -1,709 +0,0 @@
|
| -// Copyright (c) 2011, 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.
|
| -
|
| -interface OptimizationPhase {
|
| - String get name();
|
| - void visitGraph(HGraph graph);
|
| -}
|
| -
|
| -class SsaOptimizerTask extends CompilerTask {
|
| - SsaOptimizerTask(Compiler compiler) : super(compiler);
|
| - String get name() => 'SSA optimizer';
|
| -
|
| - void runPhases(HGraph graph, List<OptimizationPhase> phases) {
|
| - for (OptimizationPhase phase in phases) {
|
| - phase.visitGraph(graph);
|
| - compiler.tracer.traceGraph(phase.name, graph);
|
| - }
|
| - }
|
| -
|
| - void optimize(WorkItem work, HGraph graph) {
|
| - measure(() {
|
| - List<OptimizationPhase> phases = <OptimizationPhase>[
|
| - new SsaConstantFolder(compiler),
|
| - new SsaRedundantPhiEliminator(),
|
| - new SsaDeadPhiEliminator(),
|
| - new SsaGlobalValueNumberer(compiler),
|
| - new SsaCodeMotion(),
|
| - new SsaDeadCodeEliminator()];
|
| - runPhases(graph, phases);
|
| - });
|
| - }
|
| -
|
| - bool trySpeculativeOptimizations(WorkItem work, HGraph graph) {
|
| - return measure(() {
|
| - // Run the phases that will generate type guards. We must also run
|
| - // [SsaCheckInserter] because the type propagator also propagates
|
| - // types non-speculatively. For example, it propagates the type
|
| - // array for a call to the List constructor.
|
| - List<OptimizationPhase> phases = <OptimizationPhase>[
|
| - new SsaTypePropagator(compiler),
|
| - new SsaTypeGuardBuilder(compiler, work),
|
| - new SsaCheckInserter(compiler)];
|
| - runPhases(graph, phases);
|
| - return !work.guards.isEmpty();
|
| - });
|
| - }
|
| -
|
| - void prepareForSpeculativeOptimizations(WorkItem work, HGraph graph) {
|
| - measure(() {
|
| - // In order to generate correct code for the bailout version, we did not
|
| - // propagate types from the instruction to the type guard. We do it
|
| - // now to be able to optimize further.
|
| - work.guards.forEach((HTypeGuard guard) {
|
| - guard.type = guard.guarded.type;
|
| - guard.guarded.type = HType.UNKNOWN;
|
| - });
|
| - // We also need to insert range and integer checks for the type guards,
|
| - // now that they know their type. We did not need to do that
|
| - // before because instructions that reference a guard would
|
| - // have not tried to use, e.g. native array access, since the
|
| - // guard was not typed.
|
| - runPhases(graph, <OptimizationPhase>[new SsaCheckInserter(compiler)]);
|
| - });
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * If both inputs to known operations are available execute the operation at
|
| - * compile-time.
|
| - */
|
| -class SsaConstantFolder extends HBaseVisitor implements OptimizationPhase {
|
| - final String name = "SsaConstantFolder";
|
| - final Compiler compiler;
|
| - HGraph graph;
|
| -
|
| - SsaConstantFolder(this.compiler);
|
| -
|
| - 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) {
|
| - 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);
|
| - }
|
| - block.rewrite(instruction, replacement);
|
| - block.remove(instruction);
|
| - // Because the constant folder runs after type propagation, we
|
| - // must update the type of this instruction manually. Later
|
| - // phases can then optimize this instruction based on its
|
| - // type.
|
| - replacement.updateType();
|
| - }
|
| - instruction = next;
|
| - }
|
| - }
|
| -
|
| - HInstruction visitInstruction(HInstruction node) {
|
| - return node;
|
| - }
|
| -
|
| - HInstruction visitBoolify(HBoolify node) {
|
| - List<HInstruction> inputs = node.inputs;
|
| - assert(inputs.length == 1);
|
| - HInstruction input = inputs[0];
|
| - if (input.isBoolean()) return input;
|
| - // All values !== true are boolified to false.
|
| - if (input.type.isKnown()) {
|
| - return graph.addConstantBool(false);
|
| - }
|
| - return node;
|
| - }
|
| -
|
| - HInstruction visitNot(HNot node) {
|
| - List<HInstruction> inputs = node.inputs;
|
| - assert(inputs.length == 1);
|
| - HInstruction input = inputs[0];
|
| - if (input is HConstant) {
|
| - HConstant constant = input;
|
| - bool isTrue = constant.constant.isTrue();
|
| - return graph.addConstantBool(!isTrue);
|
| - }
|
| - return node;
|
| - }
|
| -
|
| - HInstruction visitInvokeUnary(HInvokeUnary node) {
|
| - HInstruction operand = node.operand;
|
| - if (operand is HConstant) {
|
| - UnaryOperation operation = node.operation;
|
| - HConstant receiver = operand;
|
| - Constant folded = operation.fold(receiver.constant);
|
| - if (folded !== null) return graph.addConstant(folded);
|
| - }
|
| - return node;
|
| - }
|
| -
|
| - HInstruction visitInvokeInterceptor(HInvokeInterceptor node) {
|
| - if (node.name == const SourceString('length') &&
|
| - node.inputs[1].isConstantString()) {
|
| - HConstant input = node.inputs[1];
|
| - StringConstant constant = input.constant;
|
| - DartString string = constant.value;
|
| - return graph.addConstantInt(string.length);
|
| - }
|
| - return node;
|
| - }
|
| -
|
| - HInstruction visitInvokeBinary(HInvokeBinary node) {
|
| - HInstruction left = node.left;
|
| - HInstruction right = node.right;
|
| - if (left is HConstant && right is HConstant) {
|
| - BinaryOperation operation = node.operation;
|
| - HConstant op1 = left;
|
| - HConstant op2 = right;
|
| - Constant folded = operation.fold(op1.constant, op2.constant);
|
| - if (folded !== null) return graph.addConstant(folded);
|
| - }
|
| - return node;
|
| - }
|
| -
|
| - HInstruction visitEquals(HEquals node) {
|
| - HInstruction left = node.left;
|
| - HInstruction right = node.right;
|
| - if (!left.isConstant() && right.isConstantNull()) {
|
| - // TODO(floitsch): cache interceptors.
|
| - HStatic target = new HStatic(
|
| - compiler.builder.interceptors.getEqualsNullInterceptor());
|
| - node.block.addBefore(node,target);
|
| - return new HEquals(target, node.left, node.right);
|
| - }
|
| - // All other cases are dealt with by the [visitInvokeBinary].
|
| - return visitInvokeBinary(node);
|
| - }
|
| -
|
| - HInstruction visitTypeGuard(HTypeGuard node) {
|
| - HInstruction value = node.guarded;
|
| - return (value.type.combine(node.type) == value.type) ? value : node;
|
| - }
|
| -
|
| - HInstruction visitIntegerCheck(HIntegerCheck node) {
|
| - HInstruction value = node.value;
|
| - return value.isInteger() ? value : node;
|
| - }
|
| -
|
| - HInstruction visitIs(HIs node) {
|
| - Element element = node.typeExpression;
|
| - if (element.kind === ElementKind.TYPE_VARIABLE) {
|
| - compiler.unimplemented("visitIs for type variables");
|
| - }
|
| -
|
| - HType expressionType = node.expression.type;
|
| - if (element === compiler.objectClass
|
| - || element === compiler.dynamicClass) {
|
| - return graph.addConstantBool(true);
|
| - } else if (expressionType.isInteger()) {
|
| - if (element === compiler.intClass || element === compiler.numClass) {
|
| - return graph.addConstantBool(true);
|
| - } else if (element === compiler.doubleClass) {
|
| - // We let the JS semantics decide for that check. Currently
|
| - // the code we emit will always return true.
|
| - return node;
|
| - } else {
|
| - return graph.addConstantBool(false);
|
| - }
|
| - } else if (expressionType.isDouble()) {
|
| - if (element === compiler.doubleClass || element === compiler.numClass) {
|
| - return graph.addConstantBool(true);
|
| - } else if (element === compiler.intClass) {
|
| - // We let the JS semantics decide for that check. Currently
|
| - // the code we emit will return true for a double that can be
|
| - // represented as a 31-bit integer.
|
| - return node;
|
| - } else {
|
| - return graph.addConstantBool(false);
|
| - }
|
| - } else if (expressionType.isNumber()) {
|
| - if (element === compiler.numClass) {
|
| - return graph.addConstantBool(true);
|
| - }
|
| - // We cannot just return false, because the expression may be of
|
| - // type int or double.
|
| - } else if (expressionType.isString()) {
|
| - if (element === compiler.stringClass
|
| - || Elements.isStringSupertype(element, compiler)) {
|
| - return graph.addConstantBool(true);
|
| - } else {
|
| - return graph.addConstantBool(false);
|
| - }
|
| - } else if (expressionType.isArray()) {
|
| - if (element === compiler.listClass
|
| - || Elements.isListSupertype(element, compiler)) {
|
| - return graph.addConstantBool(true);
|
| - } else {
|
| - return graph.addConstantBool(false);
|
| - }
|
| - }
|
| - return node;
|
| - }
|
| -}
|
| -
|
| -class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase {
|
| - final String name = "SsaCheckInserter";
|
| - Element lengthInterceptor;
|
| -
|
| - SsaCheckInserter(Compiler compiler) {
|
| - SourceString lengthString = const SourceString('length');
|
| - lengthInterceptor =
|
| - compiler.builder.interceptors.getStaticGetInterceptor(lengthString);
|
| - }
|
| -
|
| - void visitGraph(HGraph graph) {
|
| - visitDominatorTree(graph);
|
| - }
|
| -
|
| - void visitBasicBlock(HBasicBlock block) {
|
| - HInstruction instruction = block.first;
|
| - while (instruction !== null) {
|
| - HInstruction next = instruction.next;
|
| - instruction = instruction.accept(this);
|
| - instruction = next;
|
| - }
|
| - }
|
| -
|
| - HBoundsCheck insertBoundsCheck(HInstruction node,
|
| - HInstruction receiver,
|
| - HInstruction index) {
|
| - HStatic interceptor = new HStatic(lengthInterceptor);
|
| - node.block.addBefore(node, interceptor);
|
| - HInvokeInterceptor length = new HInvokeInterceptor(
|
| - Selector.INVOCATION_0,
|
| - const SourceString("length"),
|
| - true,
|
| - <HInstruction>[interceptor, receiver]);
|
| - length.type = HType.NUMBER;
|
| - node.block.addBefore(node, length);
|
| -
|
| - HBoundsCheck check = new HBoundsCheck(length, index);
|
| - node.block.addBefore(node, check);
|
| - return check;
|
| - }
|
| -
|
| - HIntegerCheck insertIntegerCheck(HInstruction node, HInstruction value) {
|
| - HIntegerCheck check = new HIntegerCheck(value);
|
| - node.block.addBefore(node, check);
|
| - return check;
|
| - }
|
| -
|
| - void visitIndex(HIndex node) {
|
| - if (!node.builtin) return;
|
| - HInstruction index = insertIntegerCheck(node, node.index);
|
| - index = insertBoundsCheck(node, node.receiver, index);
|
| - HIndex newInstruction = new HIndex(node.target, node.receiver, index);
|
| - node.block.addBefore(node, newInstruction);
|
| - node.block.rewrite(node, newInstruction);
|
| - node.block.remove(node);
|
| - }
|
| -
|
| - void visitIndexAssign(HIndexAssign node) {
|
| - if (!node.builtin) return;
|
| - HInstruction index = insertIntegerCheck(node, node.index);
|
| - index = insertBoundsCheck(node, node.receiver, index);
|
| - HIndexAssign newInstruction =
|
| - new HIndexAssign(node.target, node.receiver, index, node.value);
|
| - node.block.addBefore(node, newInstruction);
|
| - node.block.rewrite(node, newInstruction);
|
| - node.block.remove(node);
|
| - }
|
| -}
|
| -
|
| -class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
|
| - final String name = "SsaDeadCodeEliminator";
|
| -
|
| - static bool isDeadCode(HInstruction instruction) {
|
| - // TODO(ngeoffray): the way we handle side effects is not right
|
| - // (e.g. branching instructions have side effects).
|
| - return !instruction.hasSideEffects()
|
| - && instruction.usedBy.isEmpty()
|
| - && instruction is !HCheck
|
| - && instruction is !HTypeGuard;
|
| - }
|
| -
|
| - void visitGraph(HGraph graph) {
|
| - visitPostDominatorTree(graph);
|
| - }
|
| -
|
| - void visitBasicBlock(HBasicBlock block) {
|
| - HInstruction instruction = block.last;
|
| - while (instruction !== null) {
|
| - var previous = instruction.previous;
|
| - if (isDeadCode(instruction)) block.remove(instruction);
|
| - instruction = previous;
|
| - }
|
| - }
|
| -}
|
| -
|
| -class SsaDeadPhiEliminator implements OptimizationPhase {
|
| - final String name = "SsaDeadPhiEliminator";
|
| -
|
| - void visitGraph(HGraph graph) {
|
| - final List<HPhi> worklist = <HPhi>[];
|
| - // A set to keep track of the live phis that we found.
|
| - final Set<HPhi> livePhis = new Set<HPhi>();
|
| -
|
| - // Add to the worklist all live phis: phis referenced by non-phi
|
| - // instructions.
|
| - for (final block in graph.blocks) {
|
| - block.forEachPhi((HPhi phi) {
|
| - for (final user in phi.usedBy) {
|
| - if (user is !HPhi) {
|
| - worklist.add(phi);
|
| - livePhis.add(phi);
|
| - break;
|
| - }
|
| - }
|
| - });
|
| - }
|
| -
|
| - // Process the worklist by propagating liveness to phi inputs.
|
| - while (!worklist.isEmpty()) {
|
| - HPhi phi = worklist.removeLast();
|
| - for (final input in phi.inputs) {
|
| - if (input is HPhi && !livePhis.contains(input)) {
|
| - worklist.add(input);
|
| - livePhis.add(input);
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Remove phis that are not live.
|
| - // Traverse in reverse order to remove phis with no uses before the
|
| - // phis that they might use.
|
| - // NOTICE: Doesn't handle circular references, but we don't currently
|
| - // create any.
|
| - List<HBasicBlock> blocks = graph.blocks;
|
| - for (int i = blocks.length - 1; i >= 0; i--) {
|
| - HBasicBlock block = blocks[i];
|
| - HPhi current = block.phis.first;
|
| - HPhi next = null;
|
| - while (current != null) {
|
| - next = current.next;
|
| - if (!livePhis.contains(current)
|
| - // TODO(ahe): Not sure the following is correct.
|
| - && current.usedBy.isEmpty()) {
|
| - block.removePhi(current);
|
| - }
|
| - current = next;
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -class SsaRedundantPhiEliminator implements OptimizationPhase {
|
| - final String name = "SsaRedundantPhiEliminator";
|
| -
|
| - void visitGraph(HGraph graph) {
|
| - final List<HPhi> worklist = <HPhi>[];
|
| -
|
| - // Add all phis in the worklist.
|
| - for (final block in graph.blocks) {
|
| - block.forEachPhi((HPhi phi) => worklist.add(phi));
|
| - }
|
| -
|
| - while (!worklist.isEmpty()) {
|
| - HPhi phi = worklist.removeLast();
|
| -
|
| - // If the phi has already been processed, continue.
|
| - if (!phi.isInBasicBlock()) continue;
|
| -
|
| - // Find if the inputs of the phi are the same instruction.
|
| - // The builder ensures that phi.inputs[0] cannot be the phi
|
| - // itself.
|
| - assert(phi.inputs[0] !== phi);
|
| - HInstruction candidate = phi.inputs[0];
|
| - for (int i = 1; i < phi.inputs.length; i++) {
|
| - HInstruction input = phi.inputs[i];
|
| - // If the input is the phi, the phi is still candidate for
|
| - // elimination.
|
| - if (input !== candidate && input !== phi) {
|
| - candidate = null;
|
| - break;
|
| - }
|
| - }
|
| -
|
| - // If the inputs are not the same, continue.
|
| - if (candidate == null) continue;
|
| -
|
| - // Because we're updating the users of this phi, we may have new
|
| - // phis candidate for elimination. Add phis that used this phi
|
| - // to the worklist.
|
| - for (final user in phi.usedBy) {
|
| - if (user is HPhi) worklist.add(user);
|
| - }
|
| - phi.block.rewrite(phi, candidate);
|
| - phi.block.removePhi(phi);
|
| - }
|
| - }
|
| -}
|
| -
|
| -class SsaGlobalValueNumberer implements OptimizationPhase {
|
| - final String name = "SsaGlobalValueNumberer";
|
| - final Compiler compiler;
|
| - final Set<int> visited;
|
| -
|
| - List<int> blockChangesFlags;
|
| - List<int> loopChangesFlags;
|
| -
|
| - SsaGlobalValueNumberer(this.compiler) : visited = new Set<int>();
|
| -
|
| - void visitGraph(HGraph graph) {
|
| - computeChangesFlags(graph);
|
| - moveLoopInvariantCode(graph);
|
| - visitBasicBlock(graph.entry, new ValueSet());
|
| - }
|
| -
|
| - void moveLoopInvariantCode(HGraph graph) {
|
| - for (int i = graph.blocks.length - 1; i >= 0; i--) {
|
| - HBasicBlock block = graph.blocks[i];
|
| - if (block.isLoopHeader()) {
|
| - int changesFlags = loopChangesFlags[block.id];
|
| - HBasicBlock last = block.loopInformation.getLastBackEdge();
|
| - for (int j = block.id; j <= last.id; j++) {
|
| - moveLoopInvariantCodeFromBlock(graph.blocks[j], block, changesFlags);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - void moveLoopInvariantCodeFromBlock(HBasicBlock block,
|
| - HBasicBlock loopHeader,
|
| - int changesFlags) {
|
| - HBasicBlock preheader = loopHeader.predecessors[0];
|
| - int dependsFlags = HInstruction.computeDependsOnFlags(changesFlags);
|
| - HInstruction instruction = block.first;
|
| - while (instruction != null) {
|
| - HInstruction next = instruction.next;
|
| - if (instruction.useGvn()
|
| - && (instruction is !HCheck)
|
| - && (instruction.flags & dependsFlags) == 0) {
|
| - bool loopInvariantInputs = true;
|
| - List<HInstruction> inputs = instruction.inputs;
|
| - for (int i = 0, length = inputs.length; i < length; i++) {
|
| - if (isInputDefinedAfterDominator(inputs[i], preheader)) {
|
| - loopInvariantInputs = false;
|
| - break;
|
| - }
|
| - }
|
| -
|
| - // If the inputs are loop invariant, we can move the
|
| - // instruction from the current block to the pre-header block.
|
| - if (loopInvariantInputs) {
|
| - block.detach(instruction);
|
| - preheader.moveAtExit(instruction);
|
| - }
|
| - }
|
| - instruction = next;
|
| - }
|
| - }
|
| -
|
| - bool isInputDefinedAfterDominator(HInstruction input,
|
| - HBasicBlock dominator) {
|
| - return input.block.id > dominator.id;
|
| - }
|
| -
|
| - void visitBasicBlock(HBasicBlock block, ValueSet values) {
|
| - HInstruction instruction = block.first;
|
| - while (instruction !== null) {
|
| - HInstruction next = instruction.next;
|
| - int flags = instruction.getChangesFlags();
|
| - if (flags != 0) {
|
| - assert(!instruction.useGvn());
|
| - values.kill(flags);
|
| - } else if (instruction.useGvn()) {
|
| - HInstruction other = values.lookup(instruction);
|
| - if (other !== null) {
|
| - assert(other.gvnEquals(instruction) && instruction.gvnEquals(other));
|
| - block.rewrite(instruction, other);
|
| - block.remove(instruction);
|
| - } else {
|
| - values.add(instruction);
|
| - }
|
| - }
|
| - instruction = next;
|
| - }
|
| -
|
| - List<HBasicBlock> dominatedBlocks = block.dominatedBlocks;
|
| - for (int i = 0, length = dominatedBlocks.length; i < length; i++) {
|
| - HBasicBlock dominated = dominatedBlocks[i];
|
| - // No need to copy the value set for the last child.
|
| - ValueSet successorValues = (i == length - 1) ? values : values.copy();
|
| - // If we have no values in our set, we do not have to kill
|
| - // anything. Also, if the range of block ids from the current
|
| - // block to the dominated block is empty, there is no blocks on
|
| - // any path from the current block to the dominated block so we
|
| - // don't have to do anything either.
|
| - assert(block.id < dominated.id);
|
| - if (!successorValues.isEmpty() && block.id + 1 < dominated.id) {
|
| - visited.clear();
|
| - int changesFlags = getChangesFlagsForDominatedBlock(block, dominated);
|
| - successorValues.kill(changesFlags);
|
| - }
|
| - visitBasicBlock(dominated, successorValues);
|
| - }
|
| - }
|
| -
|
| - void computeChangesFlags(HGraph graph) {
|
| - // Create the changes flags lists. Make sure to initialize the
|
| - // loop changes flags list to zero so we can use bitwise or when
|
| - // propagating loop changes upwards.
|
| - final int length = graph.blocks.length;
|
| - blockChangesFlags = new List<int>(length);
|
| - loopChangesFlags = new List<int>(length);
|
| - for (int i = 0; i < length; i++) loopChangesFlags[i] = 0;
|
| -
|
| - // Run through all the basic blocks in the graph and fill in the
|
| - // changes flags lists.
|
| - for (int i = length - 1; i >= 0; i--) {
|
| - final HBasicBlock block = graph.blocks[i];
|
| - final int id = block.id;
|
| -
|
| - // Compute block changes flags for the block.
|
| - int changesFlags = 0;
|
| - HInstruction instruction = block.first;
|
| - while (instruction !== null) {
|
| - instruction.prepareGvn();
|
| - changesFlags |= instruction.getChangesFlags();
|
| - instruction = instruction.next;
|
| - }
|
| - assert(blockChangesFlags[id] === null);
|
| - blockChangesFlags[id] = changesFlags;
|
| -
|
| - // Loop headers are part of their loop, so update the loop
|
| - // changes flags accordingly.
|
| - if (block.isLoopHeader()) {
|
| - loopChangesFlags[id] |= changesFlags;
|
| - }
|
| -
|
| - // Propagate loop changes flags upwards.
|
| - HBasicBlock parentLoopHeader = block.parentLoopHeader;
|
| - if (parentLoopHeader !== null) {
|
| - loopChangesFlags[parentLoopHeader.id] |= (block.isLoopHeader())
|
| - ? loopChangesFlags[id]
|
| - : changesFlags;
|
| - }
|
| - }
|
| - }
|
| -
|
| - int getChangesFlagsForDominatedBlock(HBasicBlock dominator,
|
| - HBasicBlock dominated) {
|
| - int changesFlags = 0;
|
| - List<HBasicBlock> predecessors = dominated.predecessors;
|
| - for (int i = 0, length = predecessors.length; i < length; i++) {
|
| - HBasicBlock block = predecessors[i];
|
| - int id = block.id;
|
| - // If the current predecessor block is on the path from the
|
| - // dominator to the dominated, it must have an id that is in the
|
| - // range from the dominator to the dominated.
|
| - if (dominator.id < id && id < dominated.id && !visited.contains(id)) {
|
| - visited.add(id);
|
| - changesFlags |= blockChangesFlags[id];
|
| - changesFlags |= getChangesFlagsForDominatedBlock(dominator, block);
|
| - }
|
| - }
|
| - return changesFlags;
|
| - }
|
| -}
|
| -
|
| -// This phase merges equivalent instructions on different paths into
|
| -// one instruction in a dominator block. It runs through the graph
|
| -// post dominator order and computes a ValueSet for each block of
|
| -// instructions that can be moved to a dominator block. These
|
| -// instructions are the ones that:
|
| -// 1) can be used for GVN, and
|
| -// 2) do not use definitions of their own block.
|
| -//
|
| -// A basic block looks at its sucessors and finds the intersection of
|
| -// these computed ValueSet. It moves all instructions of the
|
| -// intersection into its own list of instructions.
|
| -class SsaCodeMotion extends HBaseVisitor implements OptimizationPhase {
|
| - final String name = "SsaCodeMotion";
|
| -
|
| - List<ValueSet> values;
|
| -
|
| - void visitGraph(HGraph graph) {
|
| - values = new List<ValueSet>(graph.blocks.length);
|
| - for (int i = 0; i < graph.blocks.length; i++) {
|
| - values[graph.blocks[i].id] = new ValueSet();
|
| - }
|
| - visitPostDominatorTree(graph);
|
| - }
|
| -
|
| - void visitBasicBlock(HBasicBlock block) {
|
| - List<HBasicBlock> successors = block.successors;
|
| -
|
| - // Phase 1: get the ValueSet of all successors, compute the
|
| - // intersection and move the instructions of the intersection into
|
| - // this block.
|
| - if (successors.length != 0) {
|
| - ValueSet instructions = values[successors[0].id];
|
| - for (int i = 1; i < successors.length; i++) {
|
| - ValueSet other = values[successors[i].id];
|
| - instructions = instructions.intersection(other);
|
| - }
|
| -
|
| - if (!instructions.isEmpty()) {
|
| - List<HInstruction> list = instructions.toList();
|
| - for (HInstruction instruction in list) {
|
| - // Move the instruction to the current block.
|
| - instruction.block.detach(instruction);
|
| - block.moveAtExit(instruction);
|
| - // Go through all successors and rewrite their instruction
|
| - // to the shared one.
|
| - for (final successor in successors) {
|
| - HInstruction toRewrite = values[successor.id].lookup(instruction);
|
| - if (toRewrite != instruction) {
|
| - successor.rewrite(toRewrite, instruction);
|
| - successor.remove(toRewrite);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Don't try to merge instructions to a dominator if we have
|
| - // multiple predecessors.
|
| - if (block.predecessors.length != 1) return;
|
| -
|
| - // Phase 2: Go through all instructions of this block and find
|
| - // which instructions can be moved to a dominator block.
|
| - ValueSet set_ = values[block.id];
|
| - HInstruction instruction = block.first;
|
| - int flags = 0;
|
| - while (instruction !== null) {
|
| - int dependsFlags = HInstruction.computeDependsOnFlags(flags);
|
| - flags |= instruction.getChangesFlags();
|
| -
|
| - HInstruction current = instruction;
|
| - instruction = instruction.next;
|
| -
|
| - // TODO(ngeoffray): this check is needed because we currently do
|
| - // not have flags to express 'Gvn'able', but not movable.
|
| - if (current is HCheck) continue;
|
| - if (!current.useGvn()) continue;
|
| - if ((current.flags & dependsFlags) != 0) continue;
|
| -
|
| - bool canBeMoved = true;
|
| - for (final HInstruction input in current.inputs) {
|
| - if (input.block == block) {
|
| - canBeMoved = false;
|
| - break;
|
| - }
|
| - }
|
| - if (!canBeMoved) continue;
|
| -
|
| - // This is safe because we are running after GVN.
|
| - // TODO(ngeoffray): ensure GVN has been run.
|
| - set_.add(current);
|
| - }
|
| - }
|
| -}
|
|
|