| Index: lib/compiler/implementation/ssa/codegen_helpers.dart
|
| ===================================================================
|
| --- lib/compiler/implementation/ssa/codegen_helpers.dart (revision 8244)
|
| +++ lib/compiler/implementation/ssa/codegen_helpers.dart (working copy)
|
| @@ -29,7 +29,7 @@
|
| if (!generateAtUseSite.contains(input)
|
| && !input.isCodeMotionInvariant()
|
| && input.usedBy.length == 1
|
| - && input is! HPhi) {
|
| + && input is !HPhi) {
|
| expectedInputs.add(input);
|
| }
|
| }
|
| @@ -133,190 +133,89 @@
|
| */
|
| class SsaConditionMerger extends HGraphVisitor {
|
| Set<HInstruction> generateAtUseSite;
|
| - Map<HPhi, String> logicalOperations;
|
| + Set<HInstruction> logicalOperations;
|
|
|
| SsaConditionMerger(this.generateAtUseSite, this.logicalOperations);
|
|
|
| void visitGraph(HGraph graph) {
|
| - visitDominatorTree(graph);
|
| + visitPostDominatorTree(graph);
|
| }
|
|
|
| /**
|
| - * Returns true if the given instruction is an expression that uses up all
|
| - * instructions up to the given [limit].
|
| - *
|
| - * That is, all instructions starting after the [limit] block (at the branch
|
| - * leading to the [instruction]) down to the given [instruction] can be
|
| - * generated at use-site.
|
| + * Check if a block has at least one statement other than
|
| + * [instruction].
|
| */
|
| - bool isExpression(HInstruction instruction, HBasicBlock limit) {
|
| - HBasicBlock block = instruction.block;
|
| - if (instruction is HPhi) {
|
| - if (!logicalOperations.containsKey(instruction)) {
|
| - return false;
|
| - }
|
| - } else {
|
| - while (instruction.previous != null) {
|
| - instruction = instruction.previous;
|
| - if (!generateAtUseSite.contains(instruction)) {
|
| - return false;
|
| - }
|
| - }
|
| - // Now [instruction] is the first instruction of the block
|
| - // (aka [block.first]). If there are also a phi, check the current
|
| - // [instruction] normally and make [instruction] be the phi.
|
| - if (!block.phis.isEmpty()) {
|
| - if (!generateAtUseSite.contains(instruction)) {
|
| - return false;
|
| - }
|
| - instruction = block.phis.last;
|
| - if (block.phis.first !== instruction) {
|
| - // If there is more than one phi, don't try to undestand it.
|
| - return false;
|
| - }
|
| - if (!logicalOperations.containsKey(instruction)) {
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| - if (instruction is HPhi) {
|
| - assert(logicalOperations.containsKey(instruction));
|
| - return isExpression(instruction.inputs[0], limit);
|
| - }
|
| - if (block.predecessors.length !== 1) {
|
| - return false;
|
| - }
|
| - HBasicBlock previousBlock = block.predecessors[0];
|
| - if (previousBlock === limit) return true;
|
| - if (previousBlock.successors.length !== 1 ||
|
| - previousBlock.last is! HGoto) {
|
| - return false;
|
| - }
|
| - return isExpression(previousBlock.last, limit);
|
| - }
|
| + bool hasStatement(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 (instruction.block !== block) return block.last !== block.first;
|
|
|
| - void replaceWithLogicalOperator(HPhi phi, String type) {
|
| - if (canGenerateAtUseSite(phi)) generateAtUseSite.add(phi);
|
| - logicalOperations[phi] = type;
|
| - // If the phi corresponds to logical control flow, mark the
|
| - // control-flow instructions as generate-at-use-site.
|
| - generateAtUseSite.add(phi.block.predecessors[0].last);
|
| - generateAtUseSite.add(phi.block.predecessors[1].last);
|
| - // If the first input is only used as branch condition and result, it too
|
| - // can be generate-at-use-site.
|
| - if (phi.inputs[0].usedBy.length == 2) {
|
| - generateAtUseSite.add(phi.inputs[0]);
|
| - }
|
| - if (phi.inputs[1].usedBy.length == 1) {
|
| - generateAtUseSite.add(phi.inputs[1]);
|
| - }
|
| - }
|
| + // If [instruction] is not the last instruction of the block
|
| + // before the control flow instruction, then we will have to emit
|
| + // a statement for that last instruction.
|
| + if (instruction !== block.last.previous) return true;
|
|
|
| - bool canGenerateAtUseSite(HPhi phi) {
|
| - if (phi.usedBy.length != 1) {
|
| - return false;
|
| + // 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;
|
| + temp !== instruction;
|
| + temp = temp.next) {
|
| + if (!generateAtUseSite.contains(temp)) return true;
|
| }
|
| - assert(phi.next == null);
|
| - HInstruction use = phi.usedBy[0];
|
|
|
| - HInstruction current = phi.block.first;
|
| - while (current != use) {
|
| - // Check that every instruction between the start of the block and the
|
| - // use of the phi (i.e., every instruction between the phi and the use)
|
| - // is itself generated at use site. That means that the phi can be
|
| - // moved to its use site without crossing any other code, because those
|
| - // instructions (if any) are moved too.
|
| - if (current is! HControlFlow && !generateAtUseSite.contains(current)) {
|
| - return false;
|
| - }
|
| - if (current.next != null) {
|
| - current = current.next;
|
| - } else if (current is HPhi) {
|
| - current = current.block.first;
|
| - } else {
|
| - assert(current is HControlFlow);
|
| - if (current is !HGoto) {
|
| - return false;
|
| - }
|
| - HBasicBlock nextBlock = current.block.successors[0];
|
| - if (!nextBlock.phis.isEmpty()) {
|
| - current = nextBlock.phis.first;
|
| - } else {
|
| - current = nextBlock.first;
|
| - }
|
| - }
|
| - }
|
| - return true;
|
| + return false;
|
| }
|
|
|
| - HInstruction previousInstruction(HInstruction instruction) {
|
| - if (instruction.previous != null) return instruction.previous;
|
| - HBasicBlock block = instruction.block;
|
| - if (instruction is! HPhi) {
|
| - if (block.phis.last != null) return block.phis.last;
|
| - }
|
| - if (block.predecessors.length == 1) {
|
| - HBasicBlock previousBlock = block.predecessors[0];
|
| - if (previousBlock.last is HGoto) {
|
| - assert(previousBlock.successors.length == 1);
|
| - assert(previousBlock.successors[0] === block);
|
| - return previousInstruction(previousBlock.last);
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| + void visitBasicBlock(HBasicBlock block) {
|
| + if (block.last is !HIf) return;
|
| + HIf startIf = block.last;
|
| + HBasicBlock end = startIf.joinBlock;
|
|
|
| - void detectLogicControlFlow(HPhi phi) {
|
| - // Check for the most common pattern for a short-circuit logic operation:
|
| - // B0 b0 = ...; if (b0) goto B1 else B2 (or: if (!b0) goto B2 else B1)
|
| - // |\
|
| - // | B1 b1 = ...; goto B2
|
| - // |/
|
| - // B2 b2 = phi(b0,b1); if(b2) ...
|
| - // TODO(lrn): Also recognize ?:-flow?
|
| - if (phi.inputs.length != 2) return;
|
| - HInstruction first = phi.inputs[0];
|
| - HBasicBlock firstBlock = phi.block.predecessors[0];
|
| - HInstruction second = phi.inputs[1];
|
| - HBasicBlock secondBlock = phi.block.predecessors[1];
|
| - // Check second input of phi being an expression followed by a goto.
|
| - if (second.usedBy.length != 1) return;
|
| - HInstruction secondNext =
|
| - (second is HPhi) ? secondBlock.first : second.next;
|
| - if (secondNext != secondBlock.last) return;
|
| - if (secondBlock.last is !HGoto) return;
|
| - if (secondBlock.successors[0] != phi.block) return;
|
| - if (!isExpression(second, firstBlock)) return;
|
| - // Check first input of phi being followed by a (possibly negated)
|
| - // conditional branch based on the same value.
|
| - if (firstBlock != phi.block.dominator) return;
|
| - if (firstBlock.last is! HIf) return;
|
| - if (firstBlock.successors[1] != phi.block) return;
|
| - HIf firstBranch = firstBlock.last;
|
| - HInstruction condition = firstBranch.inputs[0];
|
| - if (condition === first) {
|
| - replaceWithLogicalOperator(phi, "&&");
|
| - } else if (condition is HNot &&
|
| - condition.inputs[0] == first) {
|
| - replaceWithLogicalOperator(phi, "||");
|
| - // If the negation is only used by this logical operation, or only by
|
| - // logical operators in general, it won't need to be generated.
|
| - if (!generateAtUseSite.contains(condition)) {
|
| - for (HInstruction user in condition.usedBy) {
|
| - if (user is! HIf || !generateAtUseSite.contains(user)) {
|
| - return;
|
| - }
|
| - }
|
| - generateAtUseSite.add(condition);
|
| - }
|
| + // We check that the structure is the following:
|
| + // If
|
| + // / \
|
| + // / \
|
| + // 1 expr goto
|
| + // goto /
|
| + // \ /
|
| + // \ /
|
| + // phi(expr, true|false)
|
| + if (end == null) return;
|
| + if (end.phis.isEmpty()) return;
|
| + if (end.phis.first !== end.phis.last) return;
|
| + HBasicBlock thenBlock = startIf.thenBlock;
|
| + HBasicBlock elseBlock = startIf.elseBlock;
|
| + if (end.predecessors[0] !== thenBlock) return;
|
| + if (end.predecessors[1] !== elseBlock) return;
|
| + HPhi phi = end.phis.first;
|
| + if (!phi.inputs[1].isConstantBoolean()) return;
|
| +
|
| + HInstruction thenInput = phi.inputs[0];
|
| + if (hasStatement(thenBlock, thenInput)) return;
|
| + if (elseBlock.first !== elseBlock.last) return;
|
| +
|
| + // From now on, we have recognized a logical operation built from
|
| + // the builder. We don't expect the builder and the optimizers to
|
| + // generate the then and else branches with multiple successors,
|
| + // and the join branch to have more than two predecessors.
|
| +
|
| + // Mark the if instruction as a logical operation.
|
| + logicalOperations.add(startIf);
|
| +
|
| + // If the logical operation is only used by the first instruction
|
| + // of its block, it can be generated at use site.
|
| + if (phi.usedBy.length == 1 && phi.usedBy[0] === phi.block.first) {
|
| + generateAtUseSite.add(phi);
|
| }
|
| - return;
|
| - }
|
|
|
| - void visitBasicBlock(HBasicBlock block) {
|
| - if (!block.phis.isEmpty() &&
|
| - block.phis.first === block.phis.last) {
|
| - detectLogicControlFlow(block.phis.first);
|
| + // If [thenInput] is defined in [thenBlock], then it is only used
|
| + // by [phi] and can be generated at use site.
|
| + if (thenInput.block === thenBlock) {
|
| + assert(thenInput.usedBy.length == 1);
|
| + generateAtUseSite.add(thenInput);
|
| }
|
| }
|
| }
|
|
|