| Index: frog/leg/ssa/nodes.dart
|
| ===================================================================
|
| --- frog/leg/ssa/nodes.dart (revision 5925)
|
| +++ frog/leg/ssa/nodes.dart (working copy)
|
| @@ -1,2083 +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 HVisitor<R> {
|
| - R visitAdd(HAdd node);
|
| - R visitBitAnd(HBitAnd node);
|
| - R visitBitNot(HBitNot node);
|
| - R visitBitOr(HBitOr node);
|
| - R visitBitXor(HBitXor node);
|
| - R visitBoolify(HBoolify node);
|
| - R visitBoundsCheck(HBoundsCheck node);
|
| - R visitBreak(HBreak node);
|
| - R visitConstant(HConstant node);
|
| - R visitContinue(HContinue node);
|
| - R visitDivide(HDivide node);
|
| - R visitEquals(HEquals node);
|
| - R visitExit(HExit node);
|
| - R visitFieldGet(HFieldGet node);
|
| - R visitFieldSet(HFieldSet node);
|
| - R visitForeign(HForeign node);
|
| - R visitForeignNew(HForeignNew node);
|
| - R visitGoto(HGoto node);
|
| - R visitGreater(HGreater node);
|
| - R visitGreaterEqual(HGreaterEqual node);
|
| - R visitIdentity(HIdentity node);
|
| - R visitIf(HIf node);
|
| - R visitIndex(HIndex node);
|
| - R visitIndexAssign(HIndexAssign node);
|
| - R visitIntegerCheck(HIntegerCheck node);
|
| - R visitInvokeClosure(HInvokeClosure node);
|
| - R visitInvokeDynamicGetter(HInvokeDynamicGetter node);
|
| - R visitInvokeDynamicMethod(HInvokeDynamicMethod node);
|
| - R visitInvokeDynamicSetter(HInvokeDynamicSetter node);
|
| - R visitInvokeInterceptor(HInvokeInterceptor node);
|
| - R visitInvokeStatic(HInvokeStatic node);
|
| - R visitInvokeSuper(HInvokeSuper node);
|
| - R visitIs(HIs node);
|
| - R visitLess(HLess node);
|
| - R visitLessEqual(HLessEqual node);
|
| - R visitLiteralList(HLiteralList node);
|
| - R visitLoopBranch(HLoopBranch node);
|
| - R visitModulo(HModulo node);
|
| - R visitMultiply(HMultiply node);
|
| - R visitNegate(HNegate node);
|
| - R visitNot(HNot node);
|
| - R visitParameterValue(HParameterValue node);
|
| - R visitPhi(HPhi node);
|
| - R visitReturn(HReturn node);
|
| - R visitShiftLeft(HShiftLeft node);
|
| - R visitShiftRight(HShiftRight node);
|
| - R visitStatic(HStatic node);
|
| - R visitStaticStore(HStaticStore node);
|
| - R visitSubtract(HSubtract node);
|
| - R visitThis(HThis node);
|
| - R visitThrow(HThrow node);
|
| - R visitTruncatingDivide(HTruncatingDivide node);
|
| - R visitTry(HTry node);
|
| - R visitTypeGuard(HTypeGuard node);
|
| -}
|
| -
|
| -class HGraphVisitor {
|
| - visitDominatorTree(HGraph graph) {
|
| - void visitBasicBlockAndSuccessors(HBasicBlock block) {
|
| - visitBasicBlock(block);
|
| - List dominated = block.dominatedBlocks;
|
| - for (int i = 0; i < dominated.length; i++) {
|
| - visitBasicBlockAndSuccessors(dominated[i]);
|
| - }
|
| - }
|
| -
|
| - visitBasicBlockAndSuccessors(graph.entry);
|
| - }
|
| -
|
| - visitPostDominatorTree(HGraph graph) {
|
| - void visitBasicBlockAndSuccessors(HBasicBlock block) {
|
| - List dominated = block.dominatedBlocks;
|
| - for (int i = dominated.length - 1; i >= 0; i--) {
|
| - visitBasicBlockAndSuccessors(dominated[i]);
|
| - }
|
| - visitBasicBlock(block);
|
| - }
|
| -
|
| - visitBasicBlockAndSuccessors(graph.entry);
|
| - }
|
| -
|
| - abstract visitBasicBlock(HBasicBlock block);
|
| -}
|
| -
|
| -class HInstructionVisitor extends HGraphVisitor {
|
| - HBasicBlock currentBlock;
|
| -
|
| - abstract visitInstruction(HInstruction node);
|
| -
|
| - visitBasicBlock(HBasicBlock node) {
|
| - void visitInstructionList(HInstructionList list) {
|
| - HInstruction instruction = list.first;
|
| - while (instruction !== null) {
|
| - visitInstruction(instruction);
|
| - instruction = instruction.next;
|
| - assert(instruction != list.first);
|
| - }
|
| - }
|
| -
|
| - currentBlock = node;
|
| - visitInstructionList(node);
|
| - }
|
| -}
|
| -
|
| -class HGraph {
|
| - HBasicBlock entry;
|
| - HBasicBlock exit;
|
| - final List<HBasicBlock> blocks;
|
| -
|
| - // We canonicalize all constants used within a graph so we do not
|
| - // have to worry about them for global value numbering.
|
| - Map<Constant, HConstant> constants;
|
| -
|
| - HGraph()
|
| - : blocks = new List<HBasicBlock>(),
|
| - constants = new Map<Constant, HConstant>() {
|
| - entry = addNewBlock();
|
| - // The exit block will be added later, so it has an id that is
|
| - // after all others in the system.
|
| - exit = new HBasicBlock();
|
| - }
|
| -
|
| - void addBlock(HBasicBlock block) {
|
| - int id = blocks.length;
|
| - block.id = id;
|
| - blocks.add(block);
|
| - assert(blocks[id] === block);
|
| - }
|
| -
|
| - HBasicBlock addNewBlock() {
|
| - HBasicBlock result = new HBasicBlock();
|
| - addBlock(result);
|
| - return result;
|
| - }
|
| -
|
| - HBasicBlock addNewLoopHeaderBlock(List<LabelElement> labels) {
|
| - HBasicBlock result = addNewBlock();
|
| - result.loopInformation = new HLoopInformation(result, labels);
|
| - return result;
|
| - }
|
| -
|
| - static HType mapConstantTypeToSsaType(Constant constant) {
|
| - if (constant.isNull()) return HType.UNKNOWN;
|
| - if (constant.isBool()) return HType.BOOLEAN;
|
| - if (constant.isInt()) return HType.INTEGER;
|
| - if (constant.isDouble()) return HType.DOUBLE;
|
| - if (constant.isString()) return HType.STRING;
|
| - if (constant.isList()) return HType.ARRAY;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - HConstant addConstant(Constant constant) {
|
| - HConstant result = constants[constant];
|
| - if (result === null) {
|
| - HType type = mapConstantTypeToSsaType(constant);
|
| - result = new HConstant.internal(constant, type);
|
| - entry.addAtExit(result);
|
| - constants[constant] = result;
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - HConstant addConstantInt(int i) {
|
| - return addConstant(new IntConstant(i));
|
| - }
|
| -
|
| - HConstant addConstantDouble(double d) {
|
| - return addConstant(new DoubleConstant(d));
|
| - }
|
| -
|
| - HConstant addConstantString(DartString str) {
|
| - return addConstant(new StringConstant(str));
|
| - }
|
| -
|
| - HConstant addConstantBool(bool value) {
|
| - return addConstant(new BoolConstant(value));
|
| - }
|
| -
|
| - HConstant addConstantNull() {
|
| - return addConstant(new NullConstant());
|
| - }
|
| -
|
| - void finalize() {
|
| - addBlock(exit);
|
| - exit.open();
|
| - exit.close(new HExit());
|
| - assignDominators();
|
| - }
|
| -
|
| - void assignDominators() {
|
| - // Run through the blocks in order of increasing ids so we are
|
| - // guaranteed that we have computed dominators for all blocks
|
| - // higher up in the dominator tree.
|
| - for (int i = 0, length = blocks.length; i < length; i++) {
|
| - HBasicBlock block = blocks[i];
|
| - List<HBasicBlock> predecessors = block.predecessors;
|
| - if (block.isLoopHeader()) {
|
| - assert(predecessors.length >= 2);
|
| - block.assignCommonDominator(predecessors[0]);
|
| - } else {
|
| - for (int j = predecessors.length - 1; j >= 0; j--) {
|
| - block.assignCommonDominator(predecessors[j]);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - bool isValid() {
|
| - HValidator validator = new HValidator();
|
| - validator.visitGraph(this);
|
| - return validator.isValid;
|
| - }
|
| -}
|
| -
|
| -class HBaseVisitor extends HGraphVisitor implements HVisitor {
|
| - HBasicBlock currentBlock;
|
| -
|
| - visitBasicBlock(HBasicBlock node) {
|
| - currentBlock = node;
|
| -
|
| - HInstruction instruction = node.first;
|
| - while (instruction !== null) {
|
| - instruction.accept(this);
|
| - instruction = instruction.next;
|
| - }
|
| - }
|
| -
|
| - visitInstruction(HInstruction instruction) {}
|
| -
|
| - visitBinaryArithmetic(HBinaryArithmetic node) => visitInvokeBinary(node);
|
| - visitBinaryBitOp(HBinaryBitOp node) => visitBinaryArithmetic(node);
|
| - visitInvoke(HInvoke node) => visitInstruction(node);
|
| - visitInvokeBinary(HInvokeBinary node) => visitInvokeStatic(node);
|
| - visitInvokeDynamic(HInvokeDynamic node) => visitInvoke(node);
|
| - visitInvokeDynamicField(HInvokeDynamicField node) => visitInvokeDynamic(node);
|
| - visitInvokeUnary(HInvokeUnary node) => visitInvokeStatic(node);
|
| - visitConditionalBranch(HConditionalBranch node) => visitControlFlow(node);
|
| - visitControlFlow(HControlFlow node) => visitInstruction(node);
|
| - visitRelational(HRelational node) => visitInvokeBinary(node);
|
| -
|
| - visitAdd(HAdd node) => visitBinaryArithmetic(node);
|
| - visitBitAnd(HBitAnd node) => visitBinaryBitOp(node);
|
| - visitBitNot(HBitNot node) => visitInvokeUnary(node);
|
| - visitBitOr(HBitOr node) => visitBinaryBitOp(node);
|
| - visitBitXor(HBitXor node) => visitBinaryBitOp(node);
|
| - visitBoolify(HBoolify node) => visitInstruction(node);
|
| - visitBoundsCheck(HBoundsCheck node) => visitCheck(node);
|
| - visitBreak(HBreak node) => visitGoto(node);
|
| - visitContinue(HContinue node) => visitGoto(node);
|
| - visitCheck(HCheck node) => visitInstruction(node);
|
| - visitConstant(HConstant node) => visitInstruction(node);
|
| - visitDivide(HDivide node) => visitBinaryArithmetic(node);
|
| - visitEquals(HEquals node) => visitRelational(node);
|
| - visitExit(HExit node) => visitControlFlow(node);
|
| - visitFieldGet(HFieldGet node) => visitInstruction(node);
|
| - visitFieldSet(HFieldSet node) => visitInstruction(node);
|
| - visitForeign(HForeign node) => visitInstruction(node);
|
| - visitForeignNew(HForeignNew node) => visitForeign(node);
|
| - visitGoto(HGoto node) => visitControlFlow(node);
|
| - visitGreater(HGreater node) => visitRelational(node);
|
| - visitGreaterEqual(HGreaterEqual node) => visitRelational(node);
|
| - visitIdentity(HIdentity node) => visitRelational(node);
|
| - visitIf(HIf node) => visitConditionalBranch(node);
|
| - visitIndex(HIndex node) => visitInvokeStatic(node);
|
| - visitIndexAssign(HIndexAssign node) => visitInvokeStatic(node);
|
| - visitIntegerCheck(HIntegerCheck node) => visitCheck(node);
|
| - visitInvokeClosure(HInvokeClosure node)
|
| - => visitInvokeDynamic(node);
|
| - visitInvokeDynamicMethod(HInvokeDynamicMethod node)
|
| - => visitInvokeDynamic(node);
|
| - visitInvokeDynamicGetter(HInvokeDynamicGetter node)
|
| - => visitInvokeDynamicField(node);
|
| - visitInvokeDynamicSetter(HInvokeDynamicSetter node)
|
| - => visitInvokeDynamicField(node);
|
| - visitInvokeInterceptor(HInvokeInterceptor node)
|
| - => visitInvokeStatic(node);
|
| - visitInvokeStatic(HInvokeStatic node) => visitInvoke(node);
|
| - visitInvokeSuper(HInvokeSuper node) => visitInvoke(node);
|
| - visitLess(HLess node) => visitRelational(node);
|
| - visitLessEqual(HLessEqual node) => visitRelational(node);
|
| - visitLiteralList(HLiteralList node) => visitInstruction(node);
|
| - visitLoopBranch(HLoopBranch node) => visitConditionalBranch(node);
|
| - visitModulo(HModulo node) => visitBinaryArithmetic(node);
|
| - visitNegate(HNegate node) => visitInvokeUnary(node);
|
| - visitNot(HNot node) => visitInstruction(node);
|
| - visitPhi(HPhi node) => visitInstruction(node);
|
| - visitMultiply(HMultiply node) => visitBinaryArithmetic(node);
|
| - visitParameterValue(HParameterValue node) => visitInstruction(node);
|
| - visitReturn(HReturn node) => visitControlFlow(node);
|
| - visitShiftRight(HShiftRight node) => visitBinaryBitOp(node);
|
| - visitShiftLeft(HShiftLeft node) => visitBinaryBitOp(node);
|
| - visitSubtract(HSubtract node) => visitBinaryArithmetic(node);
|
| - visitStatic(HStatic node) => visitInstruction(node);
|
| - visitStaticStore(HStaticStore node) => visitInstruction(node);
|
| - visitThis(HThis node) => visitParameterValue(node);
|
| - visitThrow(HThrow node) => visitControlFlow(node);
|
| - visitTry(HTry node) => visitControlFlow(node);
|
| - visitTruncatingDivide(HTruncatingDivide node) => visitBinaryArithmetic(node);
|
| - visitTypeGuard(HTypeGuard node) => visitInstruction(node);
|
| - visitIs(HIs node) => visitInstruction(node);
|
| -}
|
| -
|
| -class SubGraph {
|
| - // The first and last block of the sub-graph.
|
| - final HBasicBlock start;
|
| - final HBasicBlock end;
|
| -
|
| - const SubGraph(this.start, this.end);
|
| -
|
| - bool contains(HBasicBlock block) {
|
| - assert(start !== null);
|
| - assert(end !== null);
|
| - assert(block !== null);
|
| - return start.id <= block.id && block.id <= end.id;
|
| - }
|
| -}
|
| -
|
| -class HInstructionList {
|
| - HInstruction first = null;
|
| - HInstruction last = null;
|
| -
|
| - bool isEmpty() {
|
| - return first === null;
|
| - }
|
| -
|
| - void addAfter(HInstruction cursor, HInstruction instruction) {
|
| - if (cursor === null) {
|
| - assert(isEmpty());
|
| - first = last = instruction;
|
| - } else if (cursor === last) {
|
| - last.next = instruction;
|
| - instruction.previous = last;
|
| - last = instruction;
|
| - } else {
|
| - instruction.previous = cursor;
|
| - instruction.next = cursor.next;
|
| - cursor.next.previous = instruction;
|
| - cursor.next = instruction;
|
| - }
|
| - }
|
| -
|
| - void addBefore(HInstruction cursor, HInstruction instruction) {
|
| - if (cursor === null) {
|
| - assert(isEmpty());
|
| - first = last = instruction;
|
| - } else if (cursor === first) {
|
| - first.previous = instruction;
|
| - instruction.next = first;
|
| - first = instruction;
|
| - } else {
|
| - instruction.next = cursor;
|
| - instruction.previous = cursor.previous;
|
| - cursor.previous.next = instruction;
|
| - cursor.previous = instruction;
|
| - }
|
| - }
|
| -
|
| - void detach(HInstruction instruction) {
|
| - assert(contains(instruction));
|
| - assert(instruction.isInBasicBlock());
|
| - if (instruction.previous === null) {
|
| - first = instruction.next;
|
| - } else {
|
| - instruction.previous.next = instruction.next;
|
| - }
|
| - if (instruction.next === null) {
|
| - last = instruction.previous;
|
| - } else {
|
| - instruction.next.previous = instruction.previous;
|
| - }
|
| - instruction.previous = null;
|
| - instruction.next = null;
|
| - }
|
| -
|
| - void remove(HInstruction instruction) {
|
| - assert(instruction.usedBy.isEmpty());
|
| - detach(instruction);
|
| - }
|
| -
|
| - /** Linear search for [instruction]. */
|
| - bool contains(HInstruction instruction) {
|
| - HInstruction cursor = first;
|
| - while (cursor != null) {
|
| - if (cursor === instruction) return true;
|
| - cursor = cursor.next;
|
| - }
|
| - return false;
|
| - }
|
| -}
|
| -
|
| -class HBasicBlock extends HInstructionList implements Hashable {
|
| - // The [id] must be such that any successor's id is greater than
|
| - // this [id]. The exception are back-edges.
|
| - int id;
|
| -
|
| - static final int STATUS_NEW = 0;
|
| - static final int STATUS_OPEN = 1;
|
| - static final int STATUS_CLOSED = 2;
|
| - int status = STATUS_NEW;
|
| -
|
| - HInstructionList phis;
|
| -
|
| - HLoopInformation loopInformation = null;
|
| - HLabeledBlockInformation labeledBlockInformation = null;
|
| - HBasicBlock parentLoopHeader = null;
|
| - List<HTypeGuard> guards;
|
| -
|
| - final List<HBasicBlock> predecessors;
|
| - List<HBasicBlock> successors;
|
| -
|
| - HBasicBlock dominator = null;
|
| - final List<HBasicBlock> dominatedBlocks;
|
| -
|
| - HBasicBlock() : this.withId(null);
|
| - HBasicBlock.withId(this.id)
|
| - : phis = new HInstructionList(),
|
| - predecessors = <HBasicBlock>[],
|
| - successors = const <HBasicBlock>[],
|
| - dominatedBlocks = <HBasicBlock>[],
|
| - guards = <HTypeGuard>[];
|
| -
|
| - int hashCode() => id;
|
| -
|
| - bool isNew() => status == STATUS_NEW;
|
| - bool isOpen() => status == STATUS_OPEN;
|
| - bool isClosed() => status == STATUS_CLOSED;
|
| -
|
| - bool isLoopHeader() => loopInformation !== null;
|
| - bool hasLabeledBlockInformation() => labeledBlockInformation !== null;
|
| -
|
| - bool hasGuards() => !guards.isEmpty();
|
| -
|
| - void open() {
|
| - assert(isNew());
|
| - status = STATUS_OPEN;
|
| - }
|
| -
|
| - void close(HControlFlow end) {
|
| - assert(isOpen());
|
| - addAfter(last, end);
|
| - status = STATUS_CLOSED;
|
| - }
|
| -
|
| - // TODO(kasperl): I really don't want to pass the compiler into this
|
| - // method. Maybe we need a better logging framework.
|
| - void printToCompiler(Compiler compiler) {
|
| - HInstruction instruction = first;
|
| - while (instruction != null) {
|
| - int instructionId = instruction.id;
|
| - String inputsAsString = instruction.inputsToString();
|
| - compiler.log('$instructionId: $instruction $inputsAsString');
|
| - instruction = instruction.next;
|
| - }
|
| - }
|
| -
|
| - void addAtEntry(HInstruction instruction) {
|
| - assert(isClosed());
|
| - assert(instruction is !HPhi);
|
| - super.addBefore(first, instruction);
|
| - instruction.notifyAddedToBlock(this);
|
| - }
|
| -
|
| - void addAtExit(HInstruction instruction) {
|
| - assert(isClosed());
|
| - assert(last is HControlFlow);
|
| - assert(instruction is !HPhi);
|
| - super.addBefore(last, instruction);
|
| - instruction.notifyAddedToBlock(this);
|
| - }
|
| -
|
| - void moveAtExit(HInstruction instruction) {
|
| - assert(instruction is !HPhi);
|
| - assert(instruction.isInBasicBlock());
|
| - assert(isClosed());
|
| - assert(last is HControlFlow);
|
| - super.addBefore(last, instruction);
|
| - instruction.block = this;
|
| - assert(isValid());
|
| - }
|
| -
|
| - void add(HInstruction instruction) {
|
| - assert(instruction is !HControlFlow);
|
| - assert(instruction is !HPhi);
|
| - super.addAfter(last, instruction);
|
| - instruction.notifyAddedToBlock(this);
|
| - }
|
| -
|
| - void addPhi(HPhi phi) {
|
| - phis.addAfter(phis.last, phi);
|
| - phi.notifyAddedToBlock(this);
|
| - }
|
| -
|
| - void removePhi(HPhi phi) {
|
| - phis.remove(phi);
|
| - phi.notifyRemovedFromBlock(this);
|
| - }
|
| -
|
| - void addAfter(HInstruction cursor, HInstruction instruction) {
|
| - assert(cursor is !HPhi);
|
| - assert(instruction is !HPhi);
|
| - assert(isOpen() || isClosed());
|
| - super.addAfter(cursor, instruction);
|
| - instruction.notifyAddedToBlock(this);
|
| - }
|
| -
|
| - void addBefore(HInstruction cursor, HInstruction instruction) {
|
| - assert(cursor is !HPhi);
|
| - assert(instruction is !HPhi);
|
| - assert(isOpen() || isClosed());
|
| - super.addBefore(cursor, instruction);
|
| - instruction.notifyAddedToBlock(this);
|
| - }
|
| -
|
| - void remove(HInstruction instruction) {
|
| - assert(isOpen() || isClosed());
|
| - assert(instruction is !HPhi);
|
| - super.remove(instruction);
|
| - instruction.notifyRemovedFromBlock(this);
|
| - }
|
| -
|
| - void addSuccessor(HBasicBlock block) {
|
| - // Forward branches are only allowed to new blocks.
|
| - assert(isClosed() && (block.isNew() || block.id < id));
|
| - if (successors.isEmpty()) {
|
| - successors = [block];
|
| - } else {
|
| - successors.add(block);
|
| - }
|
| - block.predecessors.add(this);
|
| - }
|
| -
|
| - void postProcessLoopHeader() {
|
| - assert(isLoopHeader());
|
| - // Only the first entry into the loop is from outside the
|
| - // loop. All other entries must be back edges.
|
| - for (int i = 1, length = predecessors.length; i < length; i++) {
|
| - loopInformation.addBackEdge(predecessors[i]);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Rewrites all uses of the [from] instruction to using the [to]
|
| - * instruction instead.
|
| - */
|
| - void rewrite(HInstruction from, HInstruction to) {
|
| - for (HInstruction use in from.usedBy) {
|
| - rewriteInput(use, from, to);
|
| - }
|
| - to.usedBy.addAll(from.usedBy);
|
| - from.usedBy.clear();
|
| - }
|
| -
|
| - static void rewriteInput(HInstruction instruction,
|
| - HInstruction from,
|
| - HInstruction to) {
|
| - List inputs = instruction.inputs;
|
| - for (int i = 0; i < inputs.length; i++) {
|
| - if (inputs[i] === from) inputs[i] = to;
|
| - }
|
| - }
|
| -
|
| - bool isExitBlock() {
|
| - return first === last && first is HExit;
|
| - }
|
| -
|
| - void addDominatedBlock(HBasicBlock block) {
|
| - assert(isClosed());
|
| - assert(id !== null && block.id !== null);
|
| - assert(dominatedBlocks.indexOf(block) < 0);
|
| - // Keep the list of dominated blocks sorted such that if there are two
|
| - // succeeding blocks in the list, the predecessor is before the successor.
|
| - // Assume that we add the dominated blocks in the right order.
|
| - int index = dominatedBlocks.length;
|
| - while (index > 0 && dominatedBlocks[index - 1].id > block.id) {
|
| - index--;
|
| - }
|
| - if (index == dominatedBlocks.length) {
|
| - dominatedBlocks.add(block);
|
| - } else {
|
| - dominatedBlocks.insertRange(index, 1, block);
|
| - }
|
| - assert(block.dominator === null);
|
| - block.dominator = this;
|
| - }
|
| -
|
| - void removeDominatedBlock(HBasicBlock block) {
|
| - assert(isClosed());
|
| - assert(id !== null && block.id !== null);
|
| - int index = dominatedBlocks.indexOf(block);
|
| - assert(index >= 0);
|
| - if (index == dominatedBlocks.length - 1) {
|
| - dominatedBlocks.removeLast();
|
| - } else {
|
| - dominatedBlocks.removeRange(index, 1);
|
| - }
|
| - assert(block.dominator === this);
|
| - block.dominator = null;
|
| - }
|
| -
|
| - void assignCommonDominator(HBasicBlock predecessor) {
|
| - assert(isClosed());
|
| - if (dominator === null) {
|
| - // If this basic block doesn't have a dominator yet we use the
|
| - // given predecessor as the dominator.
|
| - predecessor.addDominatedBlock(this);
|
| - } else if (predecessor.dominator !== null) {
|
| - // If the predecessor has a dominator and this basic block has a
|
| - // dominator, we find a common parent in the dominator tree and
|
| - // use that as the dominator.
|
| - HBasicBlock first = dominator;
|
| - HBasicBlock second = predecessor;
|
| - while (first !== second) {
|
| - if (first.id > second.id) {
|
| - first = first.dominator;
|
| - } else {
|
| - second = second.dominator;
|
| - }
|
| - assert(first !== null && second !== null);
|
| - }
|
| - if (dominator !== first) {
|
| - dominator.removeDominatedBlock(this);
|
| - first.addDominatedBlock(this);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void forEachPhi(void f(HPhi phi)) {
|
| - HPhi current = phis.first;
|
| - while (current !== null) {
|
| - f(current);
|
| - current = current.next;
|
| - }
|
| - }
|
| -
|
| - bool isValid() {
|
| - assert(isClosed());
|
| - HValidator validator = new HValidator();
|
| - validator.visitBasicBlock(this);
|
| - return validator.isValid;
|
| - }
|
| -}
|
| -
|
| -class HLabeledBlockInformation {
|
| - final SubGraph body;
|
| - final HBasicBlock joinBlock;
|
| - final List<LabelElement> labels;
|
| - final TargetElement target;
|
| - final bool isContinue;
|
| -
|
| - HLabeledBlockInformation(this.body, this.joinBlock,
|
| - List<LabelElement> labels,
|
| - [this.isContinue = false]) :
|
| - this.labels = labels, this.target = labels[0].target;
|
| -
|
| - HLabeledBlockInformation.implicit(this.body,
|
| - this.joinBlock,
|
| - this.target,
|
| - [this.isContinue = false])
|
| - : this.labels = const<LabelElement>[];
|
| -}
|
| -
|
| -class HLoopInformation {
|
| - final HBasicBlock header;
|
| - final List<HBasicBlock> blocks;
|
| - final List<HBasicBlock> backEdges;
|
| - final List<LabelElement> labels;
|
| -
|
| - HLoopInformation(this.header, this.labels)
|
| - : blocks = new List<HBasicBlock>(),
|
| - backEdges = new List<HBasicBlock>();
|
| -
|
| - void addBackEdge(HBasicBlock predecessor) {
|
| - backEdges.add(predecessor);
|
| - addBlock(predecessor);
|
| - }
|
| -
|
| - // Adds a block and transitively all its predecessors in the loop as
|
| - // loop blocks.
|
| - void addBlock(HBasicBlock block) {
|
| - if (block === header) return;
|
| - HBasicBlock parentHeader = block.parentLoopHeader;
|
| - if (parentHeader === header) {
|
| - // Nothing to do in this case.
|
| - } else if (parentHeader !== null) {
|
| - addBlock(parentHeader);
|
| - } else {
|
| - block.parentLoopHeader = header;
|
| - blocks.add(block);
|
| - for (int i = 0, length = block.predecessors.length; i < length; i++) {
|
| - addBlock(block.predecessors[i]);
|
| - }
|
| - }
|
| - }
|
| -
|
| - HBasicBlock getLastBackEdge() {
|
| - int maxId = -1;
|
| - HBasicBlock result = null;
|
| - for (int i = 0, length = backEdges.length; i < length; i++) {
|
| - HBasicBlock current = backEdges[i];
|
| - if (current.id > maxId) {
|
| - maxId = current.id;
|
| - result = current;
|
| - }
|
| - }
|
| - return result;
|
| - }
|
| -}
|
| -
|
| -class HType {
|
| - final int flag;
|
| - const HType(int this.flag);
|
| -
|
| - static final int FLAG_CONFLICTING = 0;
|
| - static final int FLAG_UNKNOWN = 1;
|
| - static final int FLAG_BOOLEAN = FLAG_UNKNOWN << 1;
|
| - static final int FLAG_INTEGER = FLAG_BOOLEAN << 1;
|
| - static final int FLAG_STRING = FLAG_INTEGER << 1;
|
| - static final int FLAG_ARRAY = FLAG_STRING << 1;
|
| - static final int FLAG_DOUBLE = FLAG_ARRAY << 1;
|
| -
|
| - static final HType CONFLICTING = const HType(FLAG_CONFLICTING);
|
| - static final HType UNKNOWN = const HType(FLAG_UNKNOWN);
|
| - static final HType BOOLEAN = const HType(FLAG_BOOLEAN);
|
| - static final HType STRING = const HType(FLAG_STRING);
|
| - static final HType ARRAY = const HType(FLAG_ARRAY);
|
| - static final HType INTEGER = const HType(FLAG_INTEGER);
|
| - static final HType DOUBLE = const HType(FLAG_DOUBLE);
|
| - static final HType STRING_OR_ARRAY = const HType(FLAG_STRING | FLAG_ARRAY);
|
| - static final HType NUMBER = const HType(FLAG_DOUBLE | FLAG_INTEGER);
|
| -
|
| - bool isConflicting() => this === CONFLICTING;
|
| - bool isUnknown() => this === UNKNOWN;
|
| - bool isBoolean() => this === BOOLEAN;
|
| - bool isInteger() => this === INTEGER;
|
| - bool isDouble() => this === DOUBLE;
|
| - bool isString() => this === STRING;
|
| - bool isArray() => this === ARRAY;
|
| - bool isNumber() => (this.flag & (FLAG_INTEGER | FLAG_DOUBLE)) != 0;
|
| - bool isStringOrArray() => (this.flag & (FLAG_STRING | FLAG_ARRAY)) != 0;
|
| - bool isKnown() => this !== UNKNOWN && this !== CONFLICTING;
|
| -
|
| - static HType getTypeFromFlag(int flag) {
|
| - if (flag === CONFLICTING.flag) return CONFLICTING;
|
| - if (flag === UNKNOWN.flag) return UNKNOWN;
|
| - if (flag === BOOLEAN.flag) return BOOLEAN;
|
| - if (flag === INTEGER.flag) return INTEGER;
|
| - if (flag === DOUBLE.flag) return DOUBLE;
|
| - if (flag === STRING.flag) return STRING;
|
| - if (flag === ARRAY.flag) return ARRAY;
|
| - if (flag === NUMBER.flag) return NUMBER;
|
| - if (flag === STRING_OR_ARRAY.flag) return STRING_OR_ARRAY;
|
| - assert(false);
|
| - }
|
| -
|
| - HType combine(HType other) {
|
| - if (isUnknown()) return other;
|
| - if (other.isUnknown()) return this;
|
| - return getTypeFromFlag(this.flag & other.flag);
|
| - }
|
| -}
|
| -
|
| -class HInstruction implements Hashable {
|
| - final int id;
|
| - static int idCounter;
|
| -
|
| - final List<HInstruction> inputs;
|
| - final List<HInstruction> usedBy;
|
| -
|
| - HBasicBlock block;
|
| - HInstruction previous = null;
|
| - HInstruction next = null;
|
| - int flags = 0;
|
| - HType type = HType.UNKNOWN;
|
| -
|
| - // Changes flags.
|
| - static final int FLAG_CHANGES_SOMETHING = 0;
|
| - static final int FLAG_CHANGES_COUNT = FLAG_CHANGES_SOMETHING + 1;
|
| -
|
| - // Depends flags (one for each changes flag).
|
| - static final int FLAG_DEPENDS_ON_SOMETHING = FLAG_CHANGES_COUNT;
|
| -
|
| - // Other flags.
|
| - static final int FLAG_USE_GVN = FLAG_DEPENDS_ON_SOMETHING + 1;
|
| -
|
| - HInstruction(this.inputs) : id = idCounter++, usedBy = <HInstruction>[];
|
| -
|
| - int hashCode() => id;
|
| -
|
| - bool getFlag(int position) => (flags & (1 << position)) != 0;
|
| - void setFlag(int position) { flags |= (1 << position); }
|
| - void clearFlag(int position) { flags &= ~(1 << position); }
|
| -
|
| - static int computeDependsOnFlags(int flags) => flags << FLAG_CHANGES_COUNT;
|
| -
|
| - int getChangesFlags() => flags & ((1 << FLAG_CHANGES_COUNT) - 1);
|
| - bool hasSideEffects() => getChangesFlags() != 0;
|
| - void prepareGvn() { setAllSideEffects(); }
|
| -
|
| - void setAllSideEffects() { flags |= ((1 << FLAG_CHANGES_COUNT) - 1); }
|
| - void clearAllSideEffects() { flags &= ~((1 << FLAG_CHANGES_COUNT) - 1); }
|
| -
|
| - bool useGvn() => getFlag(FLAG_USE_GVN);
|
| - void setUseGvn() { setFlag(FLAG_USE_GVN); }
|
| -
|
| - bool isArray() => type.isArray();
|
| - bool isBoolean() => type.isBoolean();
|
| - bool isInteger() => type.isInteger();
|
| - bool isNumber() => type.isNumber();
|
| - bool isString() => type.isString();
|
| - bool isTypeUnknown() => type.isUnknown();
|
| - bool isStringOrArray() => type.isStringOrArray();
|
| -
|
| - // Compute the type of the instruction.
|
| - HType computeType() => HType.UNKNOWN;
|
| -
|
| - HType computeDesiredType() {
|
| - HType candidateType = HType.UNKNOWN;
|
| - for (final user in usedBy) {
|
| - HType desiredType = user.computeDesiredInputType(this);
|
| - if (candidateType.isUnknown()) {
|
| - candidateType = desiredType;
|
| - } else if (!type.isUnknown() && candidateType != desiredType) {
|
| - candidateType = candidateType.combine(desiredType);
|
| - if (!candidateType.isKnown()) {
|
| - candidateType = HType.UNKNOWN;
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - return candidateType;
|
| - }
|
| -
|
| - HType computeDesiredInputType(HInstruction input) => HType.UNKNOWN;
|
| -
|
| - // Returns whether the instruction does produce the type it claims.
|
| - // For most instructions, this returns false. A type guard will be
|
| - // inserted to make sure the users get the right type in.
|
| - bool hasExpectedType() => false;
|
| -
|
| - // Re-compute and update the type of the instruction. Returns
|
| - // whether or not the type was changed.
|
| - bool updateType() {
|
| - if (type.isConflicting()) return false;
|
| - HType newType = computeType();
|
| - HType desiredType = computeDesiredType();
|
| - HType combined = newType.combine(desiredType);
|
| - if (combined.isKnown()) newType = combined;
|
| -
|
| - bool changed = (type != newType);
|
| - if (type.isUnknown()) {
|
| - type = newType;
|
| - return changed;
|
| - } else if (changed) {
|
| - type = type.combine(newType);
|
| - return changed;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - bool isInBasicBlock() => block !== null;
|
| -
|
| - String inputsToString() {
|
| - void addAsCommaSeparated(StringBuffer buffer, List<HInstruction> list) {
|
| - for (int i = 0; i < list.length; i++) {
|
| - if (i != 0) buffer.add(', ');
|
| - buffer.add("@${list[i].id}");
|
| - }
|
| - }
|
| -
|
| - StringBuffer buffer = new StringBuffer();
|
| - buffer.add('(');
|
| - addAsCommaSeparated(buffer, inputs);
|
| - buffer.add(') - used at [');
|
| - addAsCommaSeparated(buffer, usedBy);
|
| - buffer.add(']');
|
| - return buffer.toString();
|
| - }
|
| -
|
| - bool gvnEquals(HInstruction other) {
|
| - assert(useGvn() && other.useGvn());
|
| - // Check that the type and the flags match.
|
| - bool hasSameType = typeEquals(other);
|
| - assert(hasSameType == (typeCode() == other.typeCode()));
|
| - if (!hasSameType) return false;
|
| - if (flags != other.flags) return false;
|
| - // Check that the inputs match.
|
| - final int inputsLength = inputs.length;
|
| - final List<HInstruction> otherInputs = other.inputs;
|
| - if (inputsLength != otherInputs.length) return false;
|
| - for (int i = 0; i < inputsLength; i++) {
|
| - if (inputs[i] !== otherInputs[i]) return false;
|
| - }
|
| - // Check that the data in the instruction matches.
|
| - return dataEquals(other);
|
| - }
|
| -
|
| - int gvnHashCode() {
|
| - int result = typeCode();
|
| - int length = inputs.length;
|
| - for (int i = 0; i < length; i++) {
|
| - result = (result * 19) + (inputs[i].id) + (result >> 7);
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - // These methods should be overwritten by instructions that
|
| - // participate in global value numbering.
|
| - int typeCode() => -1;
|
| - bool typeEquals(HInstruction other) => false;
|
| - bool dataEquals(HInstruction other) => false;
|
| -
|
| - abstract accept(HVisitor visitor);
|
| -
|
| - void notifyAddedToBlock(HBasicBlock block) {
|
| - assert(!isInBasicBlock());
|
| - assert(this.block === null);
|
| - // Add [this] to the inputs' uses.
|
| - for (int i = 0; i < inputs.length; i++) {
|
| - assert(inputs[i].isInBasicBlock());
|
| - inputs[i].usedBy.add(this);
|
| - }
|
| - this.block = block;
|
| - assert(isValid());
|
| - }
|
| -
|
| - void notifyRemovedFromBlock(HBasicBlock block) {
|
| - assert(isInBasicBlock());
|
| - assert(usedBy.isEmpty());
|
| - assert(this.block === block);
|
| -
|
| - // Remove [this] from the inputs' uses.
|
| - for (int i = 0; i < inputs.length; i++) {
|
| - List inputUsedBy = inputs[i].usedBy;
|
| - for (int j = 0; j < inputUsedBy.length; j++) {
|
| - if (inputUsedBy[j] === this) {
|
| - inputUsedBy[j] = inputUsedBy[inputUsedBy.length - 1];
|
| - inputUsedBy.removeLast();
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - this.block = null;
|
| - assert(isValid());
|
| - }
|
| -
|
| - bool isConstant() => false;
|
| - bool isConstantNull() => false;
|
| - bool isConstantNumber() => false;
|
| - bool isConstantString() => false;
|
| -
|
| - bool isValid() {
|
| - HValidator validator = new HValidator();
|
| - validator.currentBlock = block;
|
| - validator.visitInstruction(this);
|
| - return validator.isValid;
|
| - }
|
| -
|
| - /**
|
| - * The code for computing a bailout environment, and the code
|
| - * generation must agree on what does not need to be captured,
|
| - * so should always be generated at use site.
|
| - */
|
| - bool isCodeMotionInvariant() => false;
|
| -}
|
| -
|
| -class HBoolify extends HInstruction {
|
| - HBoolify(HInstruction value) : super(<HInstruction>[value]);
|
| - void prepareGvn() {
|
| - assert(!hasSideEffects());
|
| - setUseGvn();
|
| - }
|
| -
|
| - HType computeType() => HType.BOOLEAN;
|
| - bool hasExpectedType() => true;
|
| -
|
| - accept(HVisitor visitor) => visitor.visitBoolify(this);
|
| - int typeCode() => 0;
|
| - bool typeEquals(other) => other is HBoolify;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HCheck extends HInstruction {
|
| - HCheck(inputs) : super(inputs);
|
| -
|
| - // TODO(floitsch): make class abstract instead of adding an abstract method.
|
| - abstract accept(HVisitor visitor);
|
| -}
|
| -
|
| -class HTypeGuard extends HInstruction {
|
| - int state;
|
| - HTypeGuard(int this.state, List<HInstruction> env) : super(env);
|
| -
|
| - void prepareGvn() {
|
| - assert(!hasSideEffects());
|
| - setUseGvn();
|
| - }
|
| -
|
| - HInstruction get guarded() => inputs.last();
|
| -
|
| - HType computeType() => type;
|
| - bool hasExpectedType() => true;
|
| -
|
| - accept(HVisitor visitor) => visitor.visitTypeGuard(this);
|
| - int typeCode() => 1;
|
| - bool typeEquals(other) => other is HTypeGuard;
|
| - bool dataEquals(HTypeGuard other) => type == other.type;
|
| -}
|
| -
|
| -class HBoundsCheck extends HCheck {
|
| - HBoundsCheck(length, index) : super(<HInstruction>[length, index]) {
|
| - type = HType.INTEGER;
|
| - }
|
| -
|
| - HInstruction get length() => inputs[0];
|
| - HInstruction get index() => inputs[1];
|
| -
|
| - void prepareGvn() {
|
| - assert(!hasSideEffects());
|
| - setUseGvn();
|
| - }
|
| -
|
| - HType computeType() => HType.INTEGER;
|
| - bool hasExpectedType() => true;
|
| -
|
| - accept(HVisitor visitor) => visitor.visitBoundsCheck(this);
|
| - int typeCode() => 2;
|
| - bool typeEquals(other) => other is HBoundsCheck;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HIntegerCheck extends HCheck {
|
| - HIntegerCheck(value) : super(<HInstruction>[value]);
|
| -
|
| - HInstruction get value() => inputs[0];
|
| -
|
| - void prepareGvn() {
|
| - assert(!hasSideEffects());
|
| - setUseGvn();
|
| - }
|
| -
|
| - HType computeType() => HType.INTEGER;
|
| - bool hasExpectedType() => true;
|
| -
|
| - accept(HVisitor visitor) => visitor.visitIntegerCheck(this);
|
| - int typeCode() => 3;
|
| - bool typeEquals(other) => other is HIntegerCheck;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HConditionalBranch extends HControlFlow {
|
| - HConditionalBranch(inputs) : super(inputs);
|
| - HInstruction get condition() => inputs[0];
|
| - HBasicBlock get trueBranch() => block.successors[0];
|
| - HBasicBlock get falseBranch() => block.successors[1];
|
| - abstract toString();
|
| -}
|
| -
|
| -class HControlFlow extends HInstruction {
|
| - HControlFlow(inputs) : super(inputs);
|
| - abstract toString();
|
| -}
|
| -
|
| -class HInvoke extends HInstruction {
|
| - /**
|
| - * The first argument must be the target: either an [HStatic] node, or
|
| - * the receiver of a method-call. The remaining inputs are the arguments
|
| - * to the invocation.
|
| - */
|
| - final Selector selector;
|
| - HInvoke(Selector this.selector, List<HInstruction> inputs) : super(inputs);
|
| - static final int ARGUMENTS_OFFSET = 1;
|
| -
|
| - // TODO(floitsch): make class abstract instead of adding an abstract method.
|
| - abstract accept(HVisitor visitor);
|
| -}
|
| -
|
| -class HInvokeDynamic extends HInvoke {
|
| - SourceString name;
|
| - HInvokeDynamic(Selector selector, this.name, List<HInstruction> inputs)
|
| - : super(selector, inputs);
|
| - toString() => 'invoke dynamic: $name';
|
| - HInstruction get receiver() => inputs[0];
|
| -
|
| - // TODO(floitsch): make class abstract instead of adding an abstract method.
|
| - abstract accept(HVisitor visitor);
|
| -}
|
| -
|
| -class HInvokeClosure extends HInvokeDynamic {
|
| - Element element;
|
| - HInvokeClosure(Selector selector, List<HInstruction> inputs)
|
| - : super(selector, const SourceString('call'), inputs);
|
| - accept(HVisitor visitor) => visitor.visitInvokeClosure(this);
|
| -}
|
| -
|
| -class HInvokeDynamicMethod extends HInvokeDynamic {
|
| - HInvokeDynamicMethod(Selector selector,
|
| - SourceString methodName,
|
| - List<HInstruction> inputs)
|
| - : super(selector, methodName, inputs);
|
| - toString() => 'invoke dynamic method: $name';
|
| - accept(HVisitor visitor) => visitor.visitInvokeDynamicMethod(this);
|
| -}
|
| -
|
| -class HInvokeDynamicField extends HInvokeDynamic {
|
| - Element element;
|
| - HInvokeDynamicField(Selector selector,
|
| - Element this.element,
|
| - SourceString name,
|
| - List<HInstruction>inputs)
|
| - : super(selector, name, inputs);
|
| - toString() => 'invoke dynamic field: $name';
|
| -
|
| - // TODO(floitsch): make class abstract instead of adding an abstract method.
|
| - abstract accept(HVisitor visitor);
|
| -}
|
| -
|
| -class HInvokeDynamicGetter extends HInvokeDynamicField {
|
| - HInvokeDynamicGetter(selector, element, name, receiver)
|
| - : super(selector, element, name, [receiver]);
|
| - toString() => 'invoke dynamic getter: $name';
|
| - accept(HVisitor visitor) => visitor.visitInvokeDynamicGetter(this);
|
| -}
|
| -
|
| -class HInvokeDynamicSetter extends HInvokeDynamicField {
|
| - HInvokeDynamicSetter(selector, element, name, receiver, value)
|
| - : super(selector, element, name, [receiver, value]);
|
| - toString() => 'invoke dynamic setter: $name';
|
| - accept(HVisitor visitor) => visitor.visitInvokeDynamicSetter(this);
|
| -}
|
| -
|
| -class HInvokeStatic extends HInvoke {
|
| - /** The first input must be the target. */
|
| - HInvokeStatic(selector, inputs) : super(selector, inputs);
|
| - toString() => 'invoke static: ${element.name}';
|
| - accept(HVisitor visitor) => visitor.visitInvokeStatic(this);
|
| - Element get element() => target.element;
|
| - HStatic get target() => inputs[0];
|
| -
|
| - bool isArrayConstructor() {
|
| - // TODO(ngeoffray): This is not the right way to do the check,
|
| - // nor the right place. We need to move it to a phase.
|
| - return (element.isFactoryConstructor()
|
| - && element.enclosingElement.name.slowToString() == 'List');
|
| - }
|
| -
|
| - HType computeType() {
|
| - if (isArrayConstructor()) {
|
| - return HType.ARRAY;
|
| - }
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - bool get builtin() => isArrayConstructor();
|
| - bool hasExpectedType() => isArrayConstructor();
|
| -}
|
| -
|
| -class HInvokeSuper extends HInvokeStatic {
|
| - HInvokeSuper(selector, inputs) : super(selector, inputs);
|
| - toString() => 'invoke super: ${element.name}';
|
| - accept(HVisitor visitor) => visitor.visitInvokeSuper(this);
|
| -}
|
| -
|
| -class HInvokeInterceptor extends HInvokeStatic {
|
| - final SourceString name;
|
| - final bool getter;
|
| -
|
| - HInvokeInterceptor(Selector selector,
|
| - SourceString this.name,
|
| - bool this.getter,
|
| - List<HInstruction> inputs)
|
| - : super(selector, inputs);
|
| - toString() => 'invoke interceptor: ${element.name}';
|
| - accept(HVisitor visitor) => visitor.visitInvokeInterceptor(this);
|
| -
|
| -
|
| - String get builtinJsName() {
|
| - if (getter
|
| - && name == const SourceString('length')
|
| - && inputs[1].isStringOrArray()) {
|
| - return 'length';
|
| - } else if (name == const SourceString('add') && inputs[1].isArray()) {
|
| - return 'push';
|
| - } else if (name == const SourceString('removeLast')
|
| - && inputs[1].isArray()) {
|
| - return 'pop';
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - HType computeType() {
|
| - if (getter
|
| - && name == const SourceString('length')
|
| - && inputs[1].isStringOrArray()) {
|
| - return HType.INTEGER;
|
| - }
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - HType computeDesiredInputType(HInstruction input) {
|
| - if (input == inputs[0]) return HType.UNKNOWN;
|
| - if (input == inputs[1] && input.isStringOrArray()) {
|
| - if (name == const SourceString('add')
|
| - || name == const SourceString('removeLast')) {
|
| - return HType.ARRAY;
|
| - }
|
| - }
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - bool hasExpectedType() => builtinJsName != null;
|
| -
|
| - void prepareGvn() {
|
| - if (builtinJsName == 'length') {
|
| - clearAllSideEffects();
|
| - } else {
|
| - setAllSideEffects();
|
| - }
|
| - }
|
| -
|
| - int typeCode() => 4;
|
| - bool typeEquals(other) => other is HInvokeInterceptor;
|
| - bool dataEquals(HInvokeInterceptor other) {
|
| - return builtinJsName == other.builtinJsName && name == other.name;
|
| - }
|
| -}
|
| -
|
| -class HFieldGet extends HInstruction {
|
| - final Element element;
|
| - HFieldGet(Element this.element, HInstruction receiver)
|
| - : super(<HInstruction>[receiver]);
|
| - HFieldGet.fromActivation(Element this.element) : super(<HInstruction>[]);
|
| -
|
| - HInstruction get receiver() => inputs.length == 1 ? inputs[0] : null;
|
| - accept(HVisitor visitor) => visitor.visitFieldGet(this);
|
| -}
|
| -
|
| -class HFieldSet extends HInstruction {
|
| - final Element element;
|
| - HFieldSet(Element this.element, HInstruction receiver, HInstruction value)
|
| - : super(<HInstruction>[receiver, value]);
|
| - HFieldSet.fromActivation(Element this.element, HInstruction value)
|
| - : super(<HInstruction>[value]);
|
| -
|
| - HInstruction get receiver() => inputs.length == 2 ? inputs[0] : null;
|
| - HInstruction get value() => inputs.length == 2 ? inputs[1] : inputs[0];
|
| - accept(HVisitor visitor) => visitor.visitFieldSet(this);
|
| -
|
| - void prepareGvn() {
|
| - // TODO(ngeoffray): implement more fine grain side effects.
|
| - setAllSideEffects();
|
| - }
|
| -}
|
| -
|
| -class HForeign extends HInstruction {
|
| - final DartString code;
|
| - final DartString declaredType;
|
| - HForeign(this.code, this.declaredType, List<HInstruction> inputs)
|
| - : super(inputs);
|
| - accept(HVisitor visitor) => visitor.visitForeign(this);
|
| -
|
| - HType computeType() {
|
| - if (declaredType.slowToString() == 'bool') return HType.BOOLEAN;
|
| - if (declaredType.slowToString() == 'int') return HType.INTEGER;
|
| - if (declaredType.slowToString() == 'num') return HType.NUMBER;
|
| - if (declaredType.slowToString() == 'String') return HType.STRING;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - bool hasExpectedType() => true;
|
| -}
|
| -
|
| -class HForeignNew extends HForeign {
|
| - ClassElement element;
|
| - HForeignNew(this.element, List<HInstruction> inputs)
|
| - : super(const LiteralDartString("new"),
|
| - const LiteralDartString("Object"), inputs);
|
| - accept(HVisitor visitor) => visitor.visitForeignNew(this);
|
| -}
|
| -
|
| -class HInvokeBinary extends HInvokeStatic {
|
| - HInvokeBinary(HStatic target, HInstruction left, HInstruction right)
|
| - : super(Selector.BINARY_OPERATOR, <HInstruction>[target, left, right]);
|
| -
|
| - HInstruction get left() => inputs[1];
|
| - HInstruction get right() => inputs[2];
|
| -
|
| - HType computeInputsType() {
|
| - HType leftType = left.type;
|
| - HType rightType = right.type;
|
| - if (leftType.isUnknown() || rightType.isUnknown()) {
|
| - return HType.UNKNOWN;
|
| - }
|
| - return leftType.combine(rightType);
|
| - }
|
| -
|
| - abstract BinaryOperation get operation();
|
| -}
|
| -
|
| -class HBinaryArithmetic extends HInvokeBinary {
|
| - HBinaryArithmetic(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| -
|
| - void prepareGvn() {
|
| - // An arithmetic expression can take part in global value
|
| - // numbering and do not have any side-effects if we know that all
|
| - // inputs are numbers.
|
| - if (builtin) {
|
| - clearAllSideEffects();
|
| - setUseGvn();
|
| - } else {
|
| - setAllSideEffects();
|
| - }
|
| - }
|
| -
|
| - bool get builtin() => left.isNumber() && right.isNumber();
|
| -
|
| - HType computeType() {
|
| - HType inputsType = computeInputsType();
|
| - if (!inputsType.isUnknown()) return inputsType;
|
| - if (left.isNumber()) return HType.NUMBER;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - HType computeDesiredInputType(HInstruction input) {
|
| - // TODO(floitsch): we want the target to be a function.
|
| - if (input == target) return HType.UNKNOWN;
|
| - if (isNumber() || left.isNumber() || right.isNumber()) return HType.NUMBER;
|
| - if (type.isUnknown()) return HType.NUMBER;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - bool hasExpectedType() => left.isNumber() && right.isNumber();
|
| - // TODO(1603): The class should be marked as abstract.
|
| - abstract BinaryOperation get operation();
|
| -}
|
| -
|
| -class HAdd extends HBinaryArithmetic {
|
| - HAdd(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitAdd(this);
|
| -
|
| - bool get builtin() {
|
| - return (left.isNumber() && right.isNumber())
|
| - || (left.isString() && right.isString())
|
| - || (left.isString() && right is HConstant);
|
| - }
|
| -
|
| - HType computeType() {
|
| - HType computedType = computeInputsType();
|
| - if (computedType.isConflicting() && left.isString()) return HType.STRING;
|
| - if (!computedType.isUnknown()) return computedType;
|
| - if (left.isNumber()) return HType.NUMBER;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - bool hasExpectedType() => builtin || type.isUnknown() || left.isString();
|
| -
|
| - HType computeDesiredInputType(HInstruction input) {
|
| - // TODO(floitsch): we want the target to be a function.
|
| - if (input == target) return HType.UNKNOWN;
|
| - if (isString() || left.isString()) {
|
| - return (input == left) ? HType.STRING : HType.UNKNOWN;
|
| - }
|
| - if (right.isString()) return HType.STRING;
|
| - if (isNumber() || left.isNumber() || right.isNumber()) return HType.NUMBER;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - AddOperation get operation() => const AddOperation();
|
| -
|
| - int typeCode() => 5;
|
| - bool typeEquals(other) => other is HAdd;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HDivide extends HBinaryArithmetic {
|
| - HDivide(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitDivide(this);
|
| -
|
| - bool get builtin() => left.isNumber() && right.isNumber();
|
| -
|
| - HType computeType() {
|
| - HType inputsType = computeInputsType();
|
| - if (left.isNumber()) return HType.DOUBLE;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - DivideOperation get operation() => const DivideOperation();
|
| - int typeCode() => 6;
|
| - bool typeEquals(other) => other is HDivide;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HModulo extends HBinaryArithmetic {
|
| - HModulo(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitModulo(this);
|
| -
|
| - ModuloOperation get operation() => const ModuloOperation();
|
| - int typeCode() => 7;
|
| - bool typeEquals(other) => other is HModulo;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HMultiply extends HBinaryArithmetic {
|
| - HMultiply(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitMultiply(this);
|
| -
|
| - MultiplyOperation get operation() => const MultiplyOperation();
|
| - int typeCode() => 8;
|
| - bool typeEquals(other) => other is HMultiply;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HSubtract extends HBinaryArithmetic {
|
| - HSubtract(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitSubtract(this);
|
| -
|
| - SubtractOperation get operation() => const SubtractOperation();
|
| - int typeCode() => 9;
|
| - bool typeEquals(other) => other is HSubtract;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HTruncatingDivide extends HBinaryArithmetic {
|
| - HTruncatingDivide(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitTruncatingDivide(this);
|
| -
|
| - TruncatingDivideOperation get operation()
|
| - => const TruncatingDivideOperation();
|
| - int typeCode() => 10;
|
| - bool typeEquals(other) => other is HTruncatingDivide;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -
|
| -// TODO(floitsch): Should HBinaryArithmetic really be the super class of
|
| -// HBinaryBitOp?
|
| -class HBinaryBitOp extends HBinaryArithmetic {
|
| - HBinaryBitOp(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| -
|
| - bool get builtin() => left.isInteger() && right.isInteger();
|
| -
|
| - HType computeType() {
|
| - HType inputsType = computeInputsType();
|
| - if (!inputsType.isUnknown()) return inputsType;
|
| - if (left.isInteger()) return HType.INTEGER;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - HType computeDesiredInputType(HInstruction input) {
|
| - // TODO(floitsch): we want the target to be a function.
|
| - if (input == target) return HType.UNKNOWN;
|
| - return HType.INTEGER;
|
| - }
|
| -
|
| - // TODO(floitsch): make class abstract instead of adding an abstract method.
|
| - abstract accept(HVisitor visitor);
|
| -}
|
| -
|
| -class HShiftLeft extends HBinaryBitOp {
|
| - HShiftLeft(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitShiftLeft(this);
|
| -
|
| - ShiftLeftOperation get operation() => const ShiftLeftOperation();
|
| - int typeCode() => 11;
|
| - bool typeEquals(other) => other is HShiftLeft;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HShiftRight extends HBinaryBitOp {
|
| - HShiftRight(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitShiftRight(this);
|
| -
|
| - ShiftRightOperation get operation() => const ShiftRightOperation();
|
| - int typeCode() => 12;
|
| - bool typeEquals(other) => other is HShiftRight;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HBitOr extends HBinaryBitOp {
|
| - HBitOr(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitBitOr(this);
|
| -
|
| - BitOrOperation get operation() => const BitOrOperation();
|
| - int typeCode() => 13;
|
| - bool typeEquals(other) => other is HBitOr;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HBitAnd extends HBinaryBitOp {
|
| - HBitAnd(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitBitAnd(this);
|
| -
|
| - BitAndOperation get operation() => const BitAndOperation();
|
| - int typeCode() => 14;
|
| - bool typeEquals(other) => other is HBitAnd;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HBitXor extends HBinaryBitOp {
|
| - HBitXor(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitBitXor(this);
|
| -
|
| - BitXorOperation get operation() => const BitXorOperation();
|
| - int typeCode() => 15;
|
| - bool typeEquals(other) => other is HBitXor;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HInvokeUnary extends HInvokeStatic {
|
| - HInvokeUnary(HStatic target, HInstruction input)
|
| - : super(Selector.UNARY_OPERATOR, <HInstruction>[target, input]);
|
| -
|
| - HInstruction get operand() => inputs[1];
|
| -
|
| - void prepareGvn() {
|
| - // A unary arithmetic expression can take part in global value
|
| - // numbering and does not have any side-effects if its input is a
|
| - // number.
|
| - if (builtin) {
|
| - clearAllSideEffects();
|
| - setUseGvn();
|
| - } else {
|
| - setAllSideEffects();
|
| - }
|
| - }
|
| -
|
| - bool get builtin() => operand.isNumber();
|
| -
|
| - HType computeType() {
|
| - HType operandType = operand.type;
|
| - if (!operandType.isUnknown()) return operandType;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - HType computeDesiredInputType(HInstruction input) {
|
| - // TODO(floitsch): we want the target to be a function.
|
| - if (input == target) return HType.UNKNOWN;
|
| - if (type.isUnknown() || type.isNumber()) return HType.NUMBER;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - bool hasExpectedType() => builtin || type.isUnknown();
|
| -
|
| - abstract UnaryOperation get operation();
|
| -}
|
| -
|
| -class HNegate extends HInvokeUnary {
|
| - HNegate(HStatic target, HInstruction input) : super(target, input);
|
| - accept(HVisitor visitor) => visitor.visitNegate(this);
|
| -
|
| - NegateOperation get operation() => const NegateOperation();
|
| - int typeCode() => 16;
|
| - bool typeEquals(other) => other is HNegate;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HBitNot extends HInvokeUnary {
|
| - HBitNot(HStatic target, HInstruction input) : super(target, input);
|
| - accept(HVisitor visitor) => visitor.visitBitNot(this);
|
| -
|
| - bool get builtin() => operand.isInteger();
|
| -
|
| - HType computeType() {
|
| - HType operandType = operand.type;
|
| - if (!operandType.isUnknown()) return operandType;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - HType computeDesiredInputType(HInstruction input) {
|
| - // TODO(floitsch): we want the target to be a function.
|
| - if (input == target) return HType.UNKNOWN;
|
| - return HType.INTEGER;
|
| - }
|
| -
|
| - BitNotOperation get operation() => const BitNotOperation();
|
| - int typeCode() => 17;
|
| - bool typeEquals(other) => other is HBitNot;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HExit extends HControlFlow {
|
| - HExit() : super(const <HInstruction>[]);
|
| - toString() => 'exit';
|
| - accept(HVisitor visitor) => visitor.visitExit(this);
|
| -}
|
| -
|
| -class HGoto extends HControlFlow {
|
| - HGoto() : super(const <HInstruction>[]);
|
| - toString() => 'goto';
|
| - accept(HVisitor visitor) => visitor.visitGoto(this);
|
| -}
|
| -
|
| -class HBreak extends HGoto {
|
| - final TargetElement target;
|
| - final LabelElement label;
|
| - HBreak(this.target) : label = null;
|
| - HBreak.toLabel(LabelElement label) : label = label, target = label.target;
|
| - toString() => (label !== null) ? 'break ${label.labelName}' : 'break';
|
| - accept(HVisitor visitor) => visitor.visitBreak(this);
|
| -}
|
| -
|
| -class HContinue extends HGoto {
|
| - final TargetElement target;
|
| - final LabelElement label;
|
| - HContinue(this.target) : label = null;
|
| - HContinue.toLabel(LabelElement label) : label = label, target = label.target;
|
| - toString() => (label !== null) ? 'continue ${label.labelName}' : 'continue';
|
| - accept(HVisitor visitor) => visitor.visitContinue(this);
|
| -}
|
| -
|
| -class HTry extends HControlFlow {
|
| - HParameterValue exception;
|
| - HBasicBlock finallyBlock;
|
| - HTry() : super(const <HInstruction>[]);
|
| - toString() => 'try';
|
| - accept(HVisitor visitor) => visitor.visitTry(this);
|
| - HBasicBlock get joinBlock() => this.block.successors.last();
|
| -}
|
| -
|
| -class HIf extends HConditionalBranch {
|
| - bool hasElse;
|
| - HIfBlockInformation blockInformation = null;
|
| - HIf(HInstruction condition, this.hasElse) : super(<HInstruction>[condition]);
|
| - toString() => 'if';
|
| - accept(HVisitor visitor) => visitor.visitIf(this);
|
| -
|
| - HBasicBlock get thenBlock() {
|
| - assert(block.dominatedBlocks[0] === block.successors[0]);
|
| - return block.successors[0];
|
| - }
|
| -
|
| - HBasicBlock get elseBlock() {
|
| - if (hasElse) {
|
| - assert(block.dominatedBlocks[1] === block.successors[1]);
|
| - return block.successors[1];
|
| - } else {
|
| - return null;
|
| - }
|
| - }
|
| -
|
| - HBasicBlock get joinBlock() => blockInformation.joinBlock;
|
| -}
|
| -
|
| -class HLoopBranch extends HConditionalBranch {
|
| - static final int CONDITION_FIRST_LOOP = 0;
|
| - static final int DO_WHILE_LOOP = 1;
|
| -
|
| - final int kind;
|
| - HLoopBranch(HInstruction condition, [this.kind = CONDITION_FIRST_LOOP])
|
| - : super(<HInstruction>[condition]);
|
| - toString() => 'loop-branch';
|
| - accept(HVisitor visitor) => visitor.visitLoopBranch(this);
|
| -
|
| - bool isDoWhile() {
|
| - return kind === DO_WHILE_LOOP;
|
| - }
|
| -}
|
| -
|
| -class HConstant extends HInstruction {
|
| - final Constant constant;
|
| - HConstant.internal(this.constant, HType type) : super(<HInstruction>[]) {
|
| - this.type = type;
|
| - }
|
| -
|
| - void prepareGvn() {
|
| - assert(!hasSideEffects());
|
| - }
|
| -
|
| - toString() => 'literal: $constant';
|
| - accept(HVisitor visitor) => visitor.visitConstant(this);
|
| - HType computeType() => type;
|
| -
|
| - // Literals have the type they have. It can't be changed.
|
| - bool updateType() => false;
|
| -
|
| - bool hasExpectedType() => true;
|
| -
|
| - bool isConstant() => true;
|
| - bool isConstantBoolean() => constant.isBool();
|
| - bool isConstantNull() => constant.isNull();
|
| - bool isConstantNumber() => constant.isNum();
|
| - bool isConstantString() => constant.isString();
|
| -
|
| - // Maybe avoid this if the literal is big?
|
| - bool isCodeMotionInvariant() => true;
|
| -}
|
| -
|
| -class HNot extends HInstruction {
|
| - HNot(HInstruction value) : super(<HInstruction>[value]);
|
| - void prepareGvn() {
|
| - assert(!hasSideEffects());
|
| - setUseGvn();
|
| - }
|
| -
|
| - HType computeType() => HType.BOOLEAN;
|
| - bool hasExpectedType() => true;
|
| - HType computeDesiredInputType(HInstruction input) {
|
| - return HType.BOOLEAN;
|
| - }
|
| -
|
| - accept(HVisitor visitor) => visitor.visitNot(this);
|
| - int typeCode() => 18;
|
| - bool typeEquals(other) => other is HNot;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HParameterValue extends HInstruction {
|
| - final Element element;
|
| -
|
| - HParameterValue(this.element) : super(<HInstruction>[]);
|
| -
|
| - void prepareGvn() {
|
| - assert(!hasSideEffects());
|
| - }
|
| - toString() => 'parameter ${element.name}';
|
| - accept(HVisitor visitor) => visitor.visitParameterValue(this);
|
| - bool isCodeMotionInvariant() => true;
|
| -}
|
| -
|
| -class HThis extends HParameterValue {
|
| - HThis() : super(null);
|
| - toString() => 'this';
|
| - accept(HVisitor visitor) => visitor.visitThis(this);
|
| -}
|
| -
|
| -class HPhi extends HInstruction {
|
| - final Element element;
|
| -
|
| - static final IS_NOT_LOGICAL_OPERATOR = 0;
|
| - static final IS_AND = 1;
|
| - static final IS_OR = 2;
|
| -
|
| - int logicalOperatorType = IS_NOT_LOGICAL_OPERATOR;
|
| -
|
| - // The order of the [inputs] must correspond to the order of the
|
| - // predecessor-edges. That is if an input comes from the first predecessor
|
| - // of the surrounding block, then the input must be the first in the [HPhi].
|
| - HPhi(this.element, List<HInstruction> inputs) : super(inputs);
|
| - HPhi.noInputs(Element element) : this(element, <HInstruction>[]);
|
| - HPhi.singleInput(Element element, HInstruction input)
|
| - : this(element, <HInstruction>[input]);
|
| - HPhi.manyInputs(Element element, List<HInstruction> inputs)
|
| - : this(element, inputs);
|
| -
|
| - void addInput(HInstruction input) {
|
| - assert(isInBasicBlock());
|
| - inputs.add(input);
|
| - input.usedBy.add(this);
|
| - }
|
| -
|
| - // Compute the (shared) type of the inputs if any. If all inputs
|
| - // have the same known type return it. If any two inputs have
|
| - // different known types, we'll return a conflict -- otherwise we'll
|
| - // simply return an unknown type.
|
| - HType computeInputsType() {
|
| - bool seenUnknown = false;
|
| - HType candidateType = inputs[0].type;
|
| - for (int i = 1, length = inputs.length; i < length; i++) {
|
| - HType inputType = inputs[i].type;
|
| - if (inputType.isUnknown()) return HType.UNKNOWN;
|
| - candidateType = candidateType.combine(inputType);
|
| - if (candidateType.isConflicting()) return HType.CONFLICTING;
|
| - }
|
| - return candidateType;
|
| - }
|
| -
|
| - HType computeType() {
|
| - HType inputsType = computeInputsType();
|
| - if (!inputsType.isUnknown()) return inputsType;
|
| - return super.computeType();
|
| - }
|
| -
|
| - HType computeDesiredInputType(HInstruction input) {
|
| - if (type.isNumber()) return HType.NUMBER;
|
| - if (type.isStringOrArray()) return HType.STRING_OR_ARRAY;
|
| - return type;
|
| - }
|
| -
|
| - bool hasExpectedType() {
|
| - for (int i = 0; i < inputs.length; i++) {
|
| - if (type.combine(inputs[i].type).isConflicting()) return false;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - void setInitialTypeForLoopPhi() {
|
| - assert(block.isLoopHeader());
|
| - assert(type.isUnknown());
|
| - type = inputs[0].type;
|
| - }
|
| -
|
| - bool isLogicalOperator() => logicalOperatorType != IS_NOT_LOGICAL_OPERATOR;
|
| -
|
| - String logicalOperator() {
|
| - assert(isLogicalOperator());
|
| - if (logicalOperatorType == IS_AND) return "&&";
|
| - assert(logicalOperatorType == IS_OR);
|
| - return "||";
|
| - }
|
| -
|
| - toString() => 'phi';
|
| - accept(HVisitor visitor) => visitor.visitPhi(this);
|
| -}
|
| -
|
| -class HRelational extends HInvokeBinary {
|
| - HRelational(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right) {
|
| - type = HType.BOOLEAN;
|
| - }
|
| -
|
| - void prepareGvn() {
|
| - // Relational expressions can take part in global value numbering
|
| - // and do not have any side-effects if we know all the inputs are
|
| - // numbers. This can be improved for at least equality.
|
| - if (builtin) {
|
| - clearAllSideEffects();
|
| - setUseGvn();
|
| - } else {
|
| - setAllSideEffects();
|
| - }
|
| - }
|
| -
|
| - HType computeDesiredInputType(HInstruction input) {
|
| - // TODO(floitsch): we want the target to be a function.
|
| - if (input == target) return HType.UNKNOWN;
|
| - // For all relational operations exept HEquals, we expect to only
|
| - // get numbers.
|
| - return HType.NUMBER;
|
| - }
|
| -
|
| - bool get builtin() => left.isNumber() && right.isNumber();
|
| - HType computeType() => HType.BOOLEAN;
|
| - // A HRelational goes through the builtin operator or the top level
|
| - // element. Therefore, it always has the expected type.
|
| - bool hasExpectedType() => true;
|
| - // TODO(1603): the class should be marked as abstract.
|
| - abstract BinaryOperation get operation();
|
| -}
|
| -
|
| -class HEquals extends HRelational {
|
| - HEquals(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitEquals(this);
|
| -
|
| - bool get builtin() {
|
| - return (left.isNumber() && right.isNumber()) || left is HConstant;
|
| - }
|
| -
|
| - HType computeType() => HType.BOOLEAN;
|
| -
|
| - HType computeDesiredInputType(HInstruction input) {
|
| - // TODO(floitsch): we want the target to be a function.
|
| - if (input == target) return HType.UNKNOWN;
|
| - if (left.isNumber() || right.isNumber()) return HType.NUMBER;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - EqualsOperation get operation() => const EqualsOperation();
|
| - int typeCode() => 19;
|
| - bool typeEquals(other) => other is HEquals;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HIdentity extends HRelational {
|
| - HIdentity(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitIdentity(this);
|
| -
|
| - bool get builtin() => true;
|
| - HType computeType() => HType.BOOLEAN;
|
| - bool hasExpectedType() => true;
|
| -
|
| - HType computeDesiredInputType(HInstruction input) => HType.UNKNOWN;
|
| -
|
| - IdentityOperation get operation() => const IdentityOperation();
|
| - int typeCode() => 20;
|
| - bool typeEquals(other) => other is HIdentity;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HGreater extends HRelational {
|
| - HGreater(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitGreater(this);
|
| -
|
| - GreaterOperation get operation() => const GreaterOperation();
|
| - int typeCode() => 21;
|
| - bool typeEquals(other) => other is HGreater;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HGreaterEqual extends HRelational {
|
| - HGreaterEqual(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitGreaterEqual(this);
|
| -
|
| - GreaterEqualOperation get operation() => const GreaterEqualOperation();
|
| - int typeCode() => 22;
|
| - bool typeEquals(other) => other is HGreaterEqual;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HLess extends HRelational {
|
| - HLess(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitLess(this);
|
| -
|
| - LessOperation get operation() => const LessOperation();
|
| - int typeCode() => 23;
|
| - bool typeEquals(other) => other is HLess;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HLessEqual extends HRelational {
|
| - HLessEqual(HStatic target, HInstruction left, HInstruction right)
|
| - : super(target, left, right);
|
| - accept(HVisitor visitor) => visitor.visitLessEqual(this);
|
| -
|
| - LessEqualOperation get operation() => const LessEqualOperation();
|
| - int typeCode() => 24;
|
| - bool typeEquals(other) => other is HLessEqual;
|
| - bool dataEquals(HInstruction other) => true;
|
| -}
|
| -
|
| -class HReturn extends HControlFlow {
|
| - HReturn(value) : super(<HInstruction>[value]);
|
| - toString() => 'return';
|
| - accept(HVisitor visitor) => visitor.visitReturn(this);
|
| -}
|
| -
|
| -class HThrow extends HControlFlow {
|
| - final bool isRethrow;
|
| - HThrow(value, [this.isRethrow = false]) : super(<HInstruction>[value]);
|
| - toString() => 'throw';
|
| - accept(HVisitor visitor) => visitor.visitThrow(this);
|
| -}
|
| -
|
| -class HStatic extends HInstruction {
|
| - Element element;
|
| - HStatic(this.element) : super(<HInstruction>[]);
|
| -
|
| - void prepareGvn() {
|
| - if (!element.isAssignable()) {
|
| - clearAllSideEffects();
|
| - setUseGvn();
|
| - }
|
| - }
|
| - toString() => 'static ${element.name}';
|
| - accept(HVisitor visitor) => visitor.visitStatic(this);
|
| -
|
| - int gvnHashCode() => super.gvnHashCode() ^ element.hashCode();
|
| - int typeCode() => 25;
|
| - bool typeEquals(other) => other is HStatic;
|
| - bool dataEquals(HStatic other) => element == other.element;
|
| - bool isCodeMotionInvariant() => !element.isAssignable();
|
| -}
|
| -
|
| -class HStaticStore extends HInstruction {
|
| - Element element;
|
| - HStaticStore(this.element, HInstruction value) : super(<HInstruction>[value]);
|
| - toString() => 'static store ${element.name}';
|
| - accept(HVisitor visitor) => visitor.visitStaticStore(this);
|
| -
|
| - int typeCode() => 26;
|
| - bool typeEquals(other) => other is HStaticStore;
|
| - bool dataEquals(HStaticStore other) => element == other.element;
|
| -}
|
| -
|
| -class HLiteralList extends HInstruction {
|
| - HLiteralList(inputs, this.isConst) : super(inputs);
|
| - toString() => 'literal list';
|
| - accept(HVisitor visitor) => visitor.visitLiteralList(this);
|
| - HType computeType() => HType.ARRAY;
|
| - bool hasExpectedType() => true;
|
| - final bool isConst; // TODO(floitsch): Remove when CTC handles arrays.
|
| -
|
| - void prepareGvn() {
|
| - assert(!hasSideEffects());
|
| - }
|
| -}
|
| -
|
| -class HIndex extends HInvokeStatic {
|
| - HIndex(HStatic target, HInstruction receiver, HInstruction index)
|
| - : super(Selector.INDEX, <HInstruction>[target, receiver, index]);
|
| - toString() => 'index operator';
|
| - accept(HVisitor visitor) => visitor.visitIndex(this);
|
| -
|
| - void prepareGvn() {
|
| - if (builtin) {
|
| - clearAllSideEffects();
|
| - } else {
|
| - setAllSideEffects();
|
| - }
|
| - }
|
| -
|
| - HInstruction get receiver() => inputs[1];
|
| - HInstruction get index() => inputs[2];
|
| -
|
| - HType computeDesiredInputType(HInstruction input) {
|
| - // TODO(floitsch): we want the target to be a function.
|
| - if (input == target) return HType.UNKNOWN;
|
| - if (input == receiver) return HType.STRING_OR_ARRAY;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - bool get builtin() => receiver.isStringOrArray();
|
| - HType computeType() => HType.UNKNOWN;
|
| - bool hasExpectedType() => false;
|
| -}
|
| -
|
| -class HIndexAssign extends HInvokeStatic {
|
| - HIndexAssign(HStatic target,
|
| - HInstruction receiver,
|
| - HInstruction index,
|
| - HInstruction value)
|
| - : super(Selector.INDEX_SET,
|
| - <HInstruction>[target, receiver, index, value]);
|
| - toString() => 'index assign operator';
|
| - accept(HVisitor visitor) => visitor.visitIndexAssign(this);
|
| -
|
| - HInstruction get receiver() => inputs[1];
|
| - HInstruction get index() => inputs[2];
|
| - HInstruction get value() => inputs[3];
|
| -
|
| - HType computeDesiredInputType(HInstruction input) {
|
| - // TODO(floitsch): we want the target to be a function.
|
| - if (input == target) return HType.UNKNOWN;
|
| - if (input == receiver) return HType.ARRAY;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - bool get builtin() => receiver.isArray();
|
| - HType computeType() => value.type;
|
| - // This instruction does not yield a new value, so it always
|
| - // has the expected type (void).
|
| - bool hasExpectedType() => true;
|
| -}
|
| -
|
| -class HIs extends HInstruction {
|
| - // TODO(ahe): This should be a Type, not Element.
|
| - final Element typeExpression;
|
| - final bool nullOk;
|
| -
|
| - HIs(this.typeExpression, HInstruction expression, [nullOk = false])
|
| - : this.nullOk = nullOk, super(<HInstruction>[expression]);
|
| -
|
| - HInstruction get expression() => inputs[0];
|
| -
|
| - HType computeType() => HType.BOOLEAN;
|
| - bool hasExpectedType() => true;
|
| -
|
| - accept(HVisitor visitor) => visitor.visitIs(this);
|
| -
|
| - toString() => "$expression is $typeExpression";
|
| -}
|
| -
|
| -class HIfBlockInformation {
|
| - final HIf branch;
|
| - final SubGraph thenGraph;
|
| - final SubGraph elseGraph;
|
| - final HBasicBlock joinBlock;
|
| - HIfBlockInformation(this.branch,
|
| - this.thenGraph,
|
| - this.elseGraph,
|
| - this.joinBlock);
|
| -}
|
|
|