| Index: frog/leg/ssa/codegen.dart
|
| ===================================================================
|
| --- frog/leg/ssa/codegen.dart (revision 5925)
|
| +++ frog/leg/ssa/codegen.dart (working copy)
|
| @@ -1,1565 +0,0 @@
|
| -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -class SsaCodeGeneratorTask extends CompilerTask {
|
| - SsaCodeGeneratorTask(Compiler compiler) : super(compiler);
|
| - String get name() => 'SSA code generator';
|
| -
|
| -
|
| - String generateMethod(WorkItem work, HGraph graph) {
|
| - return measure(() {
|
| - compiler.tracer.traceGraph("codegen", graph);
|
| - Map<Element, String> parameterNames = getParameterNames(work);
|
| - String parameters = Strings.join(parameterNames.getValues(), ', ');
|
| - SsaOptimizedCodeGenerator codegen = new SsaOptimizedCodeGenerator(
|
| - compiler, work, parameters, parameterNames);
|
| - codegen.visitGraph(graph);
|
| - return 'function($parameters) {\n${codegen.buffer}}';
|
| - });
|
| - }
|
| -
|
| - String generateBailoutMethod(WorkItem work, HGraph graph) {
|
| - return measure(() {
|
| - compiler.tracer.traceGraph("codegen-bailout", graph);
|
| - new SsaBailoutPropagator(compiler).visitGraph(graph);
|
| -
|
| - Map<Element, String> parameterNames = getParameterNames(work);
|
| - String parameters = Strings.join(parameterNames.getValues(), ', ');
|
| - SsaUnoptimizedCodeGenerator codegen = new SsaUnoptimizedCodeGenerator(
|
| - compiler, work, parameters, parameterNames);
|
| - codegen.visitGraph(graph);
|
| -
|
| - StringBuffer newParameters = new StringBuffer();
|
| - if (!parameterNames.isEmpty()) newParameters.add('$parameters, ');
|
| - newParameters.add('state');
|
| -
|
| - for (int i = 0; i < codegen.maxBailoutParameters; i++) {
|
| - newParameters.add(', env$i');
|
| - }
|
| -
|
| - return 'function($newParameters) {\n${codegen.setup}${codegen.buffer}}';
|
| - });
|
| - }
|
| -
|
| - Map<Element, String> getParameterNames(WorkItem work) {
|
| - Map<Element, String> parameterNames = new LinkedHashMap<Element, String>();
|
| - FunctionElement function = work.element;
|
| -
|
| - // The dom/html libraries have inline JS code that reference
|
| - // parameter names directly. Long-term such code will be rejected.
|
| - // Now, just don't mangle the parameter name.
|
| - function.computeParameters(compiler).forEachParameter((Element element) {
|
| - parameterNames[element] = function.isNative()
|
| - ? element.name.slowToString()
|
| - : JsNames.getValid('${element.name.slowToString()}');
|
| - });
|
| - return parameterNames;
|
| - }
|
| -}
|
| -
|
| -typedef void ElementAction(Element element);
|
| -
|
| -class SsaCodeGenerator implements HVisitor {
|
| - final Compiler compiler;
|
| - final WorkItem work;
|
| - final StringBuffer buffer;
|
| - final String parameters;
|
| -
|
| - final Map<Element, String> parameterNames;
|
| - final Map<int, String> names;
|
| - final Map<String, int> prefixes;
|
| - final Set<HInstruction> generateAtUseSite;
|
| - final Map<HPhi, String> logicalOperations;
|
| - final Map<Element, ElementAction> breakAction;
|
| - final Map<Element, ElementAction> continueAction;
|
| -
|
| - Element equalsNullElement;
|
| - int indent = 0;
|
| - int expectedPrecedence = JSPrecedence.STATEMENT_PRECEDENCE;
|
| - HGraph currentGraph;
|
| - HBasicBlock currentBlock;
|
| -
|
| - // Records a block-information that is being handled specially.
|
| - // Used to break bad recursion.
|
| - HLabeledBlockInformation currentBlockInformation;
|
| - // The subgraph is used to delimit traversal for some constructions, e.g.,
|
| - // if branches.
|
| - SubGraph subGraph;
|
| -
|
| - LibraryElement get currentLibrary() => work.element.getLibrary();
|
| -
|
| - bool isGenerateAtUseSite(HInstruction instruction) {
|
| - return generateAtUseSite.contains(instruction);
|
| - }
|
| -
|
| - SsaCodeGenerator(this.compiler,
|
| - this.work,
|
| - this.parameters,
|
| - this.parameterNames)
|
| - : names = new Map<int, String>(),
|
| - prefixes = new Map<String, int>(),
|
| - buffer = new StringBuffer(),
|
| - generateAtUseSite = new Set<HInstruction>(),
|
| - logicalOperations = new Map<HPhi, String>(),
|
| - breakAction = new Map<Element, ElementAction>(),
|
| - continueAction = new Map<Element, ElementAction>() {
|
| -
|
| - for (final name in parameterNames.getValues()) {
|
| - prefixes[name] = 0;
|
| - }
|
| -
|
| - equalsNullElement =
|
| - compiler.builder.interceptors.getEqualsNullInterceptor();
|
| - }
|
| -
|
| - abstract visitTypeGuard(HTypeGuard node);
|
| -
|
| - abstract beginGraph(HGraph graph);
|
| - abstract endGraph(HGraph graph);
|
| -
|
| - abstract beginLoop(HBasicBlock block);
|
| - abstract endLoop(HBasicBlock block);
|
| - abstract handleLoopCondition(HLoopBranch node);
|
| -
|
| - abstract startIf(HIf node);
|
| - abstract endIf(HIf node);
|
| - abstract startThen(HIf node);
|
| - abstract endThen(HIf node);
|
| - abstract startElse(HIf node);
|
| - abstract endElse(HIf node);
|
| -
|
| - void beginExpression(int precedence) {
|
| - if (precedence < expectedPrecedence) {
|
| - buffer.add('(');
|
| - }
|
| - }
|
| -
|
| - void endExpression(int precedence) {
|
| - if (precedence < expectedPrecedence) {
|
| - buffer.add(')');
|
| - }
|
| - }
|
| -
|
| - void preGenerateMethod(HGraph graph) {
|
| - new SsaInstructionMerger(generateAtUseSite).visitGraph(graph);
|
| - new SsaConditionMerger(generateAtUseSite,
|
| - logicalOperations).visitGraph(graph);
|
| - }
|
| -
|
| - visitGraph(HGraph graph) {
|
| - preGenerateMethod(graph);
|
| - currentGraph = graph;
|
| - indent++; // We are already inside a function.
|
| - subGraph = new SubGraph(graph.entry, graph.exit);
|
| - beginGraph(graph);
|
| - visitBasicBlock(graph.entry);
|
| - endGraph(graph);
|
| - }
|
| -
|
| - void visitSubGraph(SubGraph newSubGraph) {
|
| - SubGraph oldSubGraph = subGraph;
|
| - subGraph = newSubGraph;
|
| - visitBasicBlock(subGraph.start);
|
| - subGraph = oldSubGraph;
|
| - }
|
| -
|
| - String temporary(HInstruction instruction) {
|
| - int id = instruction.id;
|
| - String name = names[id];
|
| - if (name !== null) return name;
|
| -
|
| - if (instruction is HPhi) {
|
| - HPhi phi = instruction;
|
| - Element element = phi.element;
|
| - if (element != null && element.kind == ElementKind.PARAMETER) {
|
| - name = parameterNames[element];
|
| - names[id] = name;
|
| - return name;
|
| - }
|
| -
|
| - String prefix;
|
| - if (element !== null && !element.name.isEmpty()) {
|
| - prefix = element.name.slowToString();
|
| - } else {
|
| - prefix = 'v';
|
| - }
|
| - if (!prefixes.containsKey(prefix)) {
|
| - prefixes[prefix] = 0;
|
| - return newName(id, prefix);
|
| - } else {
|
| - return newName(id, '${prefix}_${prefixes[prefix]++}');
|
| - }
|
| - } else {
|
| - String prefix = 't';
|
| - if (!prefixes.containsKey(prefix)) prefixes[prefix] = 0;
|
| - return newName(id, '${prefix}${prefixes[prefix]++}');
|
| - }
|
| - }
|
| -
|
| - bool temporaryExists(HInstruction instruction) {
|
| - return names.containsKey(instruction.id);
|
| - }
|
| -
|
| - String newName(int id, String name) {
|
| - String result = JsNames.getValid(name);
|
| - names[id] = result;
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET].
|
| - */
|
| - void visitArguments(List<HInstruction> inputs) {
|
| - assert(inputs.length >= HInvoke.ARGUMENTS_OFFSET);
|
| - buffer.add('(');
|
| - for (int i = HInvoke.ARGUMENTS_OFFSET; i < inputs.length; i++) {
|
| - if (i != HInvoke.ARGUMENTS_OFFSET) buffer.add(', ');
|
| - use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - }
|
| - buffer.add(')');
|
| - }
|
| -
|
| - void define(HInstruction instruction) {
|
| - buffer.add('var ${temporary(instruction)} = ');
|
| - visit(instruction, JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - }
|
| -
|
| - void use(HInstruction argument, int expectedPrecedence) {
|
| - if (isGenerateAtUseSite(argument)) {
|
| - visit(argument, expectedPrecedence);
|
| - } else if (argument is HIntegerCheck) {
|
| - HIntegerCheck instruction = argument;
|
| - use(instruction.value, expectedPrecedence);
|
| - } else if (argument is HBoundsCheck) {
|
| - HBoundsCheck instruction = argument;
|
| - use(instruction.index, expectedPrecedence);
|
| - } else if (argument is HTypeGuard) {
|
| - HTypeGuard instruction = argument;
|
| - use(instruction.guarded, expectedPrecedence);
|
| - } else {
|
| - buffer.add(temporary(argument));
|
| - }
|
| - }
|
| -
|
| - visit(HInstruction node, int expectedPrecedence) {
|
| - int oldPrecedence = this.expectedPrecedence;
|
| - this.expectedPrecedence = expectedPrecedence;
|
| - node.accept(this);
|
| - this.expectedPrecedence = oldPrecedence;
|
| - }
|
| -
|
| - void continueAsBreak(LabelElement target) {
|
| - addIndentation();
|
| - buffer.add("break ");
|
| - writeContinueLabel(target);
|
| - buffer.add(";\n");
|
| - }
|
| -
|
| - void implicitContinueAsBreak(TargetElement target) {
|
| - addIndentation();
|
| - buffer.add("break ");
|
| - writeImplicitContinueLabel(target);
|
| - buffer.add(";\n");
|
| - }
|
| -
|
| - void implicitBreakWithLabel(TargetElement target) {
|
| - addIndentation();
|
| - buffer.add("break ");
|
| - writeImplicitLabel(target);
|
| - buffer.add(";\n");
|
| - }
|
| -
|
| - void handleLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
|
| - addIndentation();
|
| - Link<Element> continueOverrides = const EmptyLink<Element>();
|
| - // If [labeledBlockInfo.isContinue], the block is an artificial
|
| - // block around the body of a loop with an update block, so that
|
| - // continues of the loop can be written as breaks of the body
|
| - // block.
|
| - if (labeledBlockInfo.isContinue) {
|
| - for (LabelElement label in labeledBlockInfo.labels) {
|
| - if (label.isContinueTarget) {
|
| - writeContinueLabel(label);
|
| - buffer.add(':');
|
| - continueAction[label] = continueAsBreak;
|
| - continueOverrides = continueOverrides.prepend(label);
|
| - }
|
| - }
|
| - // For handling unlabeled continues from the body of a loop.
|
| - // TODO(lrn): Consider recording whether the target is in fact
|
| - // a target of an unlabeled continue, and not generate this if it isn't.
|
| - TargetElement target = labeledBlockInfo.target;
|
| - writeImplicitContinueLabel(target);
|
| - buffer.add(':');
|
| - continueAction[target] = implicitContinueAsBreak;
|
| - continueOverrides = continueOverrides.prepend(target);
|
| - } else {
|
| - for (LabelElement label in labeledBlockInfo.labels) {
|
| - if (label.isBreakTarget) {
|
| - writeLabel(label);
|
| - buffer.add(':');
|
| - }
|
| - }
|
| - TargetElement target = labeledBlockInfo.target;
|
| - if (target.isSwitch) {
|
| - // This is an extra block around a switch that is generated
|
| - // as a nested if/else chain. We add an extra break target
|
| - // so that case code can break.
|
| - writeImplicitLabel(target);
|
| - buffer.add(':');
|
| - breakAction[target] = implicitBreakWithLabel;
|
| - }
|
| - }
|
| - buffer.add('{\n');
|
| - indent++;
|
| -
|
| - visitSubGraph(labeledBlockInfo.body);
|
| -
|
| - indent--;
|
| - addIndentation();
|
| - buffer.add('}\n');
|
| -
|
| - if (labeledBlockInfo.joinBlock !== null) {
|
| - visitBasicBlock(labeledBlockInfo.joinBlock);
|
| - }
|
| - if (labeledBlockInfo.isContinue) {
|
| - while (!continueOverrides.isEmpty()) {
|
| - continueAction.remove(continueOverrides.head);
|
| - continueOverrides = continueOverrides.tail;
|
| - }
|
| - } else {
|
| - breakAction.remove(labeledBlockInfo.target);
|
| - }
|
| - }
|
| -
|
| - void emitLogicalOperation(HPhi node, String operation) {
|
| - JSBinaryOperatorPrecedence operatorPrecedence =
|
| - JSPrecedence.binary[operation];
|
| - beginExpression(operatorPrecedence.precedence);
|
| - use(node.inputs[0], operatorPrecedence.left);
|
| - buffer.add(" $operation ");
|
| - use(node.inputs[1], operatorPrecedence.right);
|
| - endExpression(operatorPrecedence.precedence);
|
| - }
|
| -
|
| - visitBasicBlock(HBasicBlock node) {
|
| - // Abort traversal if we are leaving the currently active sub-graph.
|
| - if (!subGraph.contains(node)) return;
|
| -
|
| - // If this node has special behavior attached, handle it.
|
| - // If we reach here again while handling the attached information,
|
| - // e.g., because we call visitSubGraph on a subgraph starting here,
|
| - // don't handle it again.
|
| - if (node.hasLabeledBlockInformation() &&
|
| - node.labeledBlockInformation !== currentBlockInformation) {
|
| - HLabeledBlockInformation oldBlockInformation = currentBlockInformation;
|
| - currentBlockInformation = node.labeledBlockInformation;
|
| - handleLabeledBlock(currentBlockInformation);
|
| - currentBlockInformation = oldBlockInformation;
|
| - return;
|
| - }
|
| -
|
| - currentBlock = node;
|
| -
|
| - if (node.isLoopHeader()) {
|
| - // While loop will be closed by the conditional loop-branch.
|
| - // TODO(floitsch): HACK HACK HACK.
|
| - beginLoop(node);
|
| - }
|
| -
|
| - HInstruction instruction = node.first;
|
| - while (instruction != null) {
|
| - if (instruction === node.last) {
|
| - for (HBasicBlock successor in node.successors) {
|
| - int index = successor.predecessors.indexOf(node);
|
| - successor.forEachPhi((HPhi phi) {
|
| - bool isLogicalOperation = logicalOperations.containsKey(phi);
|
| - // In case the phi is being generated by another
|
| - // instruction.
|
| - if (isLogicalOperation && isGenerateAtUseSite(phi)) return;
|
| - addIndentation();
|
| - if (!temporaryExists(phi)) buffer.add('var ');
|
| - buffer.add('${temporary(phi)} = ');
|
| - if (isLogicalOperation) {
|
| - emitLogicalOperation(phi, logicalOperations[phi]);
|
| - } else {
|
| - use(phi.inputs[index], JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - }
|
| - buffer.add(';\n');
|
| - });
|
| - }
|
| - }
|
| -
|
| - if (instruction is HGoto || instruction is HExit || instruction is HTry) {
|
| - visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE);
|
| - return;
|
| - } else if (!isGenerateAtUseSite(instruction)) {
|
| - if (instruction is !HIf && instruction is !HTypeGuard) {
|
| - addIndentation();
|
| - }
|
| - if (instruction.usedBy.isEmpty()
|
| - || instruction is HTypeGuard
|
| - || instruction is HCheck) {
|
| - visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE);
|
| - } else {
|
| - define(instruction);
|
| - }
|
| - // Control flow instructions know how to handle ';'.
|
| - if (instruction is !HControlFlow && instruction is !HTypeGuard) {
|
| - buffer.add(';\n');
|
| - }
|
| - } else if (instruction is HIf) {
|
| - HIf hif = instruction;
|
| - // The "if" is implementing part of a logical expression.
|
| - // Skip directly forward to to its latest successor, since everything
|
| - // in-between must also be generateAtUseSite.
|
| - assert(hif.trueBranch.id < hif.falseBranch.id);
|
| - visitBasicBlock(hif.falseBranch);
|
| - return;
|
| - }
|
| - instruction = instruction.next;
|
| - }
|
| - }
|
| -
|
| - visitInvokeBinary(HInvokeBinary node, String op) {
|
| - if (node.builtin) {
|
| - JSBinaryOperatorPrecedence operatorPrecedences = JSPrecedence.binary[op];
|
| - beginExpression(operatorPrecedences.precedence);
|
| - use(node.left, operatorPrecedences.left);
|
| - buffer.add(' $op ');
|
| - use(node.right, operatorPrecedences.right);
|
| - endExpression(operatorPrecedences.precedence);
|
| - } else {
|
| - visitInvokeStatic(node);
|
| - }
|
| - }
|
| -
|
| - visitInvokeUnary(HInvokeUnary node, String op) {
|
| - if (node.builtin) {
|
| - beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
|
| - buffer.add('$op');
|
| - use(node.operand, JSPrecedence.PREFIX_PRECEDENCE);
|
| - endExpression(JSPrecedence.PREFIX_PRECEDENCE);
|
| - } else {
|
| - visitInvokeStatic(node);
|
| - }
|
| - }
|
| -
|
| - visitEquals(HEquals node) {
|
| - if (node.builtin) {
|
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - use(node.left, JSPrecedence.EQUALITY_PRECEDENCE);
|
| - buffer.add(' === ');
|
| - use(node.right, JSPrecedence.RELATIONAL_PRECEDENCE);
|
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - } else if (node.element === equalsNullElement) {
|
| - beginExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - use(node.target, JSPrecedence.CALL_PRECEDENCE);
|
| - buffer.add('(');
|
| - use(node.left, JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - buffer.add(')');
|
| - endExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - } else {
|
| - visitInvokeStatic(node);
|
| - }
|
| - }
|
| -
|
| - visitAdd(HAdd node) => visitInvokeBinary(node, '+');
|
| - visitDivide(HDivide node) => visitInvokeBinary(node, '/');
|
| - visitMultiply(HMultiply node) => visitInvokeBinary(node, '*');
|
| - visitSubtract(HSubtract node) => visitInvokeBinary(node, '-');
|
| - // Truncating divide does not have a JS equivalent.
|
| - visitTruncatingDivide(HTruncatingDivide node) => visitInvokeStatic(node);
|
| - // Modulo cannot be mapped to the native operator (different semantics).
|
| - visitModulo(HModulo node) => visitInvokeStatic(node);
|
| -
|
| - visitBitAnd(HBitAnd node) => visitInvokeBinary(node, '&');
|
| - visitBitNot(HBitNot node) => visitInvokeUnary(node, '~');
|
| - visitBitOr(HBitOr node) => visitInvokeBinary(node, '|');
|
| - visitBitXor(HBitXor node) => visitInvokeBinary(node, '^');
|
| -
|
| - // We need to check if the left operand is negative in order to use
|
| - // the native operator.
|
| - visitShiftRight(HShiftRight node) => visitInvokeStatic(node);
|
| -
|
| - // Shift left cannot be mapped to the native operator (different semantics).
|
| - visitShiftLeft(HShiftLeft node) => visitInvokeStatic(node);
|
| -
|
| - visitNegate(HNegate node) => visitInvokeUnary(node, '-');
|
| -
|
| - visitIdentity(HIdentity node) => visitInvokeBinary(node, '===');
|
| - visitLess(HLess node) => visitInvokeBinary(node, '<');
|
| - visitLessEqual(HLessEqual node) => visitInvokeBinary(node, '<=');
|
| - visitGreater(HGreater node) => visitInvokeBinary(node, '>');
|
| - visitGreaterEqual(HGreaterEqual node) => visitInvokeBinary(node, '>=');
|
| -
|
| - visitBoolify(HBoolify node) {
|
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - assert(node.inputs.length == 1);
|
| - use(node.inputs[0], JSPrecedence.EQUALITY_PRECEDENCE);
|
| - buffer.add(' === true');
|
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - }
|
| -
|
| - visitExit(HExit node) {
|
| - // Don't do anything.
|
| - }
|
| -
|
| - visitGoto(HGoto node) {
|
| - assert(currentBlock.successors.length == 1);
|
| - List<HBasicBlock> dominated = currentBlock.dominatedBlocks;
|
| - // With the exception of the entry-node which dominates its successor
|
| - // and the exit node, no block finishing with a 'goto' can have more than
|
| - // one dominated block (since it has only one successor).
|
| - // If the successor is dominated by another block, then the other block
|
| - // is responsible for visiting the successor.
|
| - if (dominated.isEmpty()) return;
|
| - if (dominated.length > 2) unreachable();
|
| - if (dominated.length == 2 && currentBlock !== currentGraph.entry) {
|
| - unreachable();
|
| - }
|
| - assert(dominated[0] == currentBlock.successors[0]);
|
| - visitBasicBlock(dominated[0]);
|
| - }
|
| -
|
| - // Used to write the name of labels.
|
| - void writeLabel(LabelElement label) {
|
| - buffer.add('\$${label.labelName}\$${label.target.nestingLevel}');
|
| - }
|
| -
|
| - void writeImplicitLabel(TargetElement target) {
|
| - buffer.add('\$${target.nestingLevel}');
|
| - }
|
| -
|
| - // We sometimes handle continue targets differently from break targets,
|
| - // so we have special continue-only labels.
|
| - void writeContinueLabel(LabelElement label) {
|
| - buffer.add('c\$${label.labelName}\$${label.target.nestingLevel}');
|
| - }
|
| -
|
| - void writeImplicitContinueLabel(TargetElement target) {
|
| - buffer.add('c\$${target.nestingLevel}');
|
| - }
|
| -
|
| - /**
|
| - * Checks if [map] contains an [ElementAction] for [element], and
|
| - * if so calls that action and returns true.
|
| - * Otherwise returns false.
|
| - */
|
| - bool tryCallAction(Map<Element, ElementAction> map, Element element) {
|
| - ElementAction action = map[element];
|
| - if (action === null) return false;
|
| - action(element);
|
| - return true;
|
| - }
|
| -
|
| - visitBreak(HBreak node) {
|
| - assert(currentBlock.successors.length == 1);
|
| - if (node.label !== null) {
|
| - LabelElement label = node.label;
|
| - if (!tryCallAction(breakAction, label)) {
|
| - addIndentation();
|
| - buffer.add("break ");
|
| - writeLabel(label);
|
| - buffer.add(";\n");
|
| - }
|
| - } else {
|
| - TargetElement target = node.target;
|
| - if (!tryCallAction(breakAction, target)) {
|
| - addIndentation();
|
| - buffer.add("break;\n");
|
| - }
|
| - }
|
| - }
|
| -
|
| - visitContinue(HContinue node) {
|
| - assert(currentBlock.successors.length == 1);
|
| - if (node.label !== null) {
|
| - LabelElement label = node.label;
|
| - if (!tryCallAction(continueAction, label)) {
|
| - addIndentation();
|
| - buffer.add("continue ");
|
| - writeLabel(label);
|
| - buffer.add(";\n");
|
| - }
|
| - } else {
|
| - TargetElement target = node.target;
|
| - if (!tryCallAction(continueAction, target)) {
|
| - addIndentation();
|
| - buffer.add("continue;\n");
|
| - }
|
| - }
|
| - }
|
| -
|
| - visitTry(HTry node) {
|
| - addIndentation();
|
| - buffer.add('try {\n');
|
| - indent++;
|
| - List<HBasicBlock> successors = node.block.successors;
|
| - visitBasicBlock(successors[0]);
|
| - indent--;
|
| -
|
| - if (node.finallyBlock != successors[1]) {
|
| - // Printing the catch part.
|
| - addIndentation();
|
| - String name = temporary(node.exception);
|
| - parameterNames[node.exception.element] = name;
|
| - buffer.add('} catch ($name) {\n');
|
| - indent++;
|
| - visitBasicBlock(successors[1]);
|
| - parameterNames.remove(node.exception.element);
|
| - indent--;
|
| - }
|
| -
|
| - if (node.finallyBlock != null) {
|
| - addIndentation();
|
| - buffer.add('} finally {\n');
|
| - indent++;
|
| - visitBasicBlock(node.finallyBlock);
|
| - indent--;
|
| - }
|
| - addIndentation();
|
| - buffer.add('}\n');
|
| -
|
| - visitBasicBlock(node.joinBlock);
|
| - }
|
| -
|
| - visitIf(HIf node) {
|
| - List<HBasicBlock> dominated = node.block.dominatedBlocks;
|
| - HIfBlockInformation info = node.blockInformation;
|
| - startIf(node);
|
| - assert(!isGenerateAtUseSite(node));
|
| - startThen(node);
|
| - assert(node.thenBlock === dominated[0]);
|
| - visitSubGraph(info.thenGraph);
|
| - int preVisitedBlocks = 1;
|
| - endThen(node);
|
| - if (node.hasElse) {
|
| - startElse(node);
|
| - assert(node.elseBlock === dominated[1]);
|
| - visitSubGraph(info.elseGraph);
|
| - preVisitedBlocks = 2;
|
| - endElse(node);
|
| - }
|
| - endIf(node);
|
| - if (info.joinBlock !== null && info.joinBlock.dominator !== node.block) {
|
| - // The join block is dominated by a block in one of the branches.
|
| - // The subgraph traversal never reached it, so we visit it here
|
| - // instead.
|
| - visitBasicBlock(info.joinBlock);
|
| - }
|
| -
|
| - // Visit all the dominated blocks that are not part of the then or else
|
| - // branches, and is not the join block.
|
| - // Depending on how the then/else branches terminate
|
| - // (e.g., return/throw/break) there can be any number of these.
|
| - int dominatedCount = dominated.length;
|
| - for (int i = preVisitedBlocks; i < dominatedCount; i++) {
|
| - HBasicBlock dominatedBlock = dominated[i];
|
| - assert(dominatedBlock.dominator === node.block);
|
| - visitBasicBlock(dominatedBlock);
|
| - }
|
| - }
|
| -
|
| - visitInvokeDynamicMethod(HInvokeDynamicMethod node) {
|
| - beginExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
|
| - buffer.add('.');
|
| - // Avoid adding the generative constructor name to the list of
|
| - // seen selectors.
|
| - if (node.inputs[0] is HForeignNew) {
|
| - HForeignNew foreignNew = node.inputs[0];
|
| - // Remove 'this' from the number of arguments.
|
| - int argumentCount = node.inputs.length - 1;
|
| -
|
| - // TODO(ahe): The constructor name was statically resolved in
|
| - // SsaBuilder.buildFactory. Is there a cleaner way to do this?
|
| - node.name.printOn(buffer);
|
| - visitArguments(node.inputs);
|
| - } else {
|
| - buffer.add(compiler.namer.instanceMethodInvocationName(
|
| - currentLibrary, node.name, node.selector));
|
| - visitArguments(node.inputs);
|
| - compiler.registerDynamicInvocation(node.name, node.selector);
|
| - }
|
| - endExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - }
|
| -
|
| - visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
|
| - beginExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
|
| - buffer.add('.');
|
| - buffer.add(compiler.namer.setterName(currentLibrary, node.name));
|
| - visitArguments(node.inputs);
|
| - compiler.registerDynamicSetter(node.name);
|
| - endExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - }
|
| -
|
| - visitInvokeDynamicGetter(HInvokeDynamicGetter node) {
|
| - beginExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
|
| - buffer.add('.');
|
| - buffer.add(compiler.namer.getterName(currentLibrary, node.name));
|
| - visitArguments(node.inputs);
|
| - compiler.registerDynamicGetter(node.name);
|
| - endExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - }
|
| -
|
| - visitInvokeClosure(HInvokeClosure node) {
|
| - beginExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
|
| - buffer.add('.');
|
| - buffer.add(compiler.namer.closureInvocationName(node.selector));
|
| - visitArguments(node.inputs);
|
| - // TODO(floitsch): we should have a separate list for closure invocations.
|
| - compiler.registerDynamicInvocation(Namer.CLOSURE_INVOCATION_NAME,
|
| - node.selector);
|
| - endExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - }
|
| -
|
| - visitInvokeStatic(HInvokeStatic node) {
|
| - beginExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - use(node.target, JSPrecedence.CALL_PRECEDENCE);
|
| - visitArguments(node.inputs);
|
| - endExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - }
|
| -
|
| - visitInvokeSuper(HInvokeSuper node) {
|
| - beginExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - Element superMethod = node.element;
|
| - Element superClass = superMethod.enclosingElement;
|
| - // Remove the element and 'this'.
|
| - int argumentCount = node.inputs.length - 2;
|
| - String className = compiler.namer.isolatePropertyAccess(superClass);
|
| - String methodName;
|
| - if (superMethod.kind == ElementKind.FUNCTION ||
|
| - superMethod.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
|
| - methodName = compiler.namer.instanceMethodName(
|
| - currentLibrary, superMethod.name, argumentCount);
|
| - } else {
|
| - methodName = compiler.namer.getterName(currentLibrary, superMethod.name);
|
| - // We need to register the name to ensure that the emitter
|
| - // generates the necessary getter.
|
| - // TODO(ahe): This is not optimal for tree-shaking, but we lack
|
| - // API to register the precise information. In this case, the
|
| - // enclosingElement of superMethod needs the getter, no other
|
| - // class (not even its subclasses).
|
| - compiler.registerDynamicGetter(superMethod.name);
|
| - }
|
| - buffer.add('$className.prototype.$methodName.call');
|
| - visitArguments(node.inputs);
|
| - endExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - compiler.registerStaticUse(superMethod);
|
| - }
|
| -
|
| - visitFieldGet(HFieldGet node) {
|
| - String name = JsNames.getValid(node.element.name.slowToString());
|
| - if (node.receiver !== null) {
|
| - beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
|
| - use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
|
| - buffer.add('.');
|
| - buffer.add(name);
|
| - beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
|
| - } else {
|
| - buffer.add(name);
|
| - }
|
| - }
|
| -
|
| - visitFieldSet(HFieldSet node) {
|
| - if (node.receiver !== null) {
|
| - beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
|
| - buffer.add('.');
|
| - } else {
|
| - // TODO(ngeoffray): Remove the 'var' once we don't globally box
|
| - // variables used in a try/catch.
|
| - buffer.add('var ');
|
| - }
|
| - String name = JsNames.getValid(node.element.name.slowToString());
|
| - buffer.add(name);
|
| - buffer.add(' = ');
|
| - use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - if (node.receiver !== null) {
|
| - endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - }
|
| - }
|
| -
|
| - visitForeign(HForeign node) {
|
| - String code = node.code.slowToString();
|
| - List<HInstruction> inputs = node.inputs;
|
| - List<String> parts = code.split('#');
|
| - if (parts.length != inputs.length + 1) {
|
| - compiler.internalError(
|
| - 'Wrong number of arguments for JS', instruction: node);
|
| - }
|
| - beginExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
|
| - buffer.add(parts[0]);
|
| - for (int i = 0; i < inputs.length; i++) {
|
| - use(inputs[i], JSPrecedence.EXPRESSION_PRECEDENCE);
|
| - buffer.add(parts[i + 1]);
|
| - }
|
| - endExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
|
| - }
|
| -
|
| - visitForeignNew(HForeignNew node) {
|
| - String jsClassReference = compiler.namer.isolateAccess(node.element);
|
| - beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
|
| - buffer.add('new $jsClassReference(');
|
| - // We can't use 'visitArguments', since our arguments start at input[0].
|
| - List<HInstruction> inputs = node.inputs;
|
| - for (int i = 0; i < inputs.length; i++) {
|
| - if (i != 0) buffer.add(', ');
|
| - use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - }
|
| - buffer.add(')');
|
| - endExpression(JSPrecedence.MEMBER_PRECEDENCE);
|
| - }
|
| -
|
| - visitConstant(HConstant node) {
|
| - assert(isGenerateAtUseSite(node));
|
| - // TODO(floitsch): the compile-time constant handler and the codegen
|
| - // need to work together to avoid the parenthesis. See r4928 for an
|
| - // implementation that still dealt with precedence.
|
| - ConstantHandler handler = compiler.constantHandler;
|
| - String name = handler.getNameForConstant(node.constant);
|
| - if (name === null) {
|
| - assert(!node.constant.isObject());
|
| - if (node.constant.isNum()
|
| - && expectedPrecedence == JSPrecedence.MEMBER_PRECEDENCE) {
|
| - buffer.add('(');
|
| - node.constant.writeJsCode(buffer, handler);
|
| - buffer.add(')');
|
| - } else {
|
| - node.constant.writeJsCode(buffer, handler);
|
| - }
|
| - } else {
|
| - buffer.add(compiler.namer.CURRENT_ISOLATE);
|
| - buffer.add(".");
|
| - buffer.add(name);
|
| - }
|
| - }
|
| -
|
| - visitLoopBranch(HLoopBranch node) {
|
| - HBasicBlock branchBlock = currentBlock;
|
| - handleLoopCondition(node);
|
| - List<HBasicBlock> dominated = currentBlock.dominatedBlocks;
|
| - // For a do while loop, the body has already been visited.
|
| - if (!node.isDoWhile()) {
|
| - visitBasicBlock(dominated[0]);
|
| - }
|
| - endLoop(node.block);
|
| - visitBasicBlock(branchBlock.successors[1]);
|
| - // With labeled breaks we can have more dominated blocks.
|
| - if (dominated.length >= 3) {
|
| - for (int i = 2; i < dominated.length; i++) {
|
| - visitBasicBlock(dominated[i]);
|
| - }
|
| - }
|
| - }
|
| -
|
| - visitNot(HNot node) {
|
| - assert(node.inputs.length == 1);
|
| - beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
|
| - buffer.add('!');
|
| - use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE);
|
| - endExpression(JSPrecedence.PREFIX_PRECEDENCE);
|
| - }
|
| -
|
| - visitParameterValue(HParameterValue node) {
|
| - assert(isGenerateAtUseSite(node));
|
| - buffer.add(parameterNames[node.element]);
|
| - }
|
| -
|
| - visitPhi(HPhi node) {
|
| - String operation = logicalOperations[node];
|
| - if (operation !== null) {
|
| - emitLogicalOperation(node, operation);
|
| - } else {
|
| - buffer.add('${temporary(node)}');
|
| - }
|
| - }
|
| -
|
| - visitReturn(HReturn node) {
|
| - assert(node.inputs.length == 1);
|
| - HInstruction input = node.inputs[0];
|
| - if (input.isConstantNull()) {
|
| - buffer.add('return;\n');
|
| - } else {
|
| - buffer.add('return ');
|
| - use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
|
| - buffer.add(';\n');
|
| - }
|
| - }
|
| -
|
| - visitThis(HThis node) {
|
| - buffer.add('this');
|
| - }
|
| -
|
| - visitThrow(HThrow node) {
|
| - if (node.isRethrow) {
|
| - buffer.add('throw ');
|
| - use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
|
| - } else {
|
| - generateThrowWithHelper('captureStackTrace', node.inputs[0]);
|
| - }
|
| - buffer.add(';\n');
|
| - }
|
| -
|
| - visitBoundsCheck(HBoundsCheck node) {
|
| - buffer.add('if (');
|
| - use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE);
|
| - buffer.add(' < 0 || ');
|
| - use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE);
|
| - buffer.add(' >= ');
|
| - use(node.length, JSPrecedence.SHIFT_PRECEDENCE);
|
| - buffer.add(") ");
|
| - generateThrowWithHelper('ioore', node.index);
|
| - }
|
| -
|
| - visitIntegerCheck(HIntegerCheck node) {
|
| - buffer.add('if (');
|
| - use(node.value, JSPrecedence.EQUALITY_PRECEDENCE);
|
| - buffer.add(' !== (');
|
| - use(node.value, JSPrecedence.BITWISE_OR_PRECEDENCE);
|
| - buffer.add(" | 0)) ");
|
| - generateThrowWithHelper('iae', node.value);
|
| - }
|
| -
|
| - void generateThrowWithHelper(String helperName, HInstruction argument) {
|
| - Element helper = compiler.findHelper(new SourceString(helperName));
|
| - compiler.registerStaticUse(helper);
|
| - buffer.add('throw ');
|
| - beginExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
|
| - beginExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - buffer.add(compiler.namer.isolateAccess(helper));
|
| - visitArguments([null, argument]);
|
| - endExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - endExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
|
| - }
|
| -
|
| - void addIndentation() {
|
| - for (int i = 0; i < indent; i++) {
|
| - buffer.add(' ');
|
| - }
|
| - }
|
| -
|
| - void visitStatic(HStatic node) {
|
| - compiler.registerStaticUse(node.element);
|
| - buffer.add(compiler.namer.isolateAccess(node.element));
|
| - }
|
| -
|
| - void visitStaticStore(HStaticStore node) {
|
| - compiler.registerStaticUse(node.element);
|
| - beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - buffer.add(compiler.namer.isolateAccess(node.element));
|
| - buffer.add(' = ');
|
| - use(node.inputs[0], JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - }
|
| -
|
| - void visitLiteralList(HLiteralList node) {
|
| - if (node.isConst) {
|
| - // TODO(floitsch): Remove this when CTC handles arrays.
|
| - SourceString name = new SourceString('makeLiteralListConst');
|
| - Element helper = compiler.findHelper(name);
|
| - compiler.registerStaticUse(helper);
|
| - beginExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - buffer.add(compiler.namer.isolateAccess(helper));
|
| - buffer.add('(');
|
| - generateArrayLiteral(node);
|
| - buffer.add(')');
|
| - endExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - } else {
|
| - generateArrayLiteral(node);
|
| - }
|
| - }
|
| -
|
| - void generateArrayLiteral(HLiteralList node) {
|
| - buffer.add('[');
|
| - int len = node.inputs.length;
|
| - for (int i = 0; i < len; i++) {
|
| - if (i != 0) buffer.add(', ');
|
| - use(node.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - }
|
| - buffer.add(']');
|
| - }
|
| -
|
| - void visitIndex(HIndex node) {
|
| - if (node.builtin) {
|
| - beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
|
| - use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE);
|
| - buffer.add('[');
|
| - use(node.inputs[2], JSPrecedence.EXPRESSION_PRECEDENCE);
|
| - buffer.add(']');
|
| - endExpression(JSPrecedence.MEMBER_PRECEDENCE);
|
| - } else {
|
| - visitInvokeStatic(node);
|
| - }
|
| - }
|
| -
|
| - void visitIndexAssign(HIndexAssign node) {
|
| - if (node.builtin) {
|
| - beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE);
|
| - buffer.add('[');
|
| - use(node.inputs[2], JSPrecedence.EXPRESSION_PRECEDENCE);
|
| - buffer.add('] = ');
|
| - use(node.inputs[3], JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - } else {
|
| - visitInvokeStatic(node);
|
| - }
|
| - }
|
| -
|
| - void visitInvokeInterceptor(HInvokeInterceptor node) {
|
| - if (node.builtinJsName != null) {
|
| - beginExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE);
|
| - buffer.add('.');
|
| - buffer.add(node.builtinJsName);
|
| - if (node.getter) return;
|
| - buffer.add('(');
|
| - for (int i = 2; i < node.inputs.length; i++) {
|
| - if (i != 2) buffer.add(', ');
|
| - use(node.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - }
|
| - buffer.add(")");
|
| - endExpression(JSPrecedence.CALL_PRECEDENCE);
|
| - } else {
|
| - return visitInvokeStatic(node);
|
| - }
|
| - }
|
| -
|
| - void checkInt(HInstruction input, String cmp) {
|
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - use(input, JSPrecedence.EQUALITY_PRECEDENCE);
|
| - buffer.add(' $cmp (');
|
| - use(input, JSPrecedence.BITWISE_OR_PRECEDENCE);
|
| - buffer.add(' | 0)');
|
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - }
|
| -
|
| - void checkNum(HInstruction input, String cmp) {
|
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - buffer.add('typeof ');
|
| - use(input, JSPrecedence.PREFIX_PRECEDENCE);
|
| - buffer.add(" $cmp 'number'");
|
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - }
|
| -
|
| - void checkDouble(HInstruction input, String cmp) {
|
| - checkNum(input, cmp);
|
| - }
|
| -
|
| - void checkString(HInstruction input, String cmp) {
|
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - buffer.add('typeof ');
|
| - use(input, JSPrecedence.PREFIX_PRECEDENCE);
|
| - buffer.add(" $cmp 'string'");
|
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - }
|
| -
|
| - void checkBool(HInstruction input, String cmp) {
|
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - buffer.add('typeof ');
|
| - use(input, JSPrecedence.PREFIX_PRECEDENCE);
|
| - buffer.add(" $cmp 'boolean'");
|
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - }
|
| -
|
| - void checkObject(HInstruction input, String cmp) {
|
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - buffer.add('typeof ');
|
| - use(input, JSPrecedence.PREFIX_PRECEDENCE);
|
| - buffer.add(" $cmp 'object'");
|
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - }
|
| -
|
| - void checkArray(HInstruction input, String cmp) {
|
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - use(input, JSPrecedence.MEMBER_PRECEDENCE);
|
| - buffer.add('.constructor $cmp Array');
|
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - }
|
| -
|
| - void checkNull(HInstruction input) {
|
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - use(input, JSPrecedence.EQUALITY_PRECEDENCE);
|
| - buffer.add(" === (void 0)");
|
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - }
|
| -
|
| - void checkFunction(HInstruction input, Element element) {
|
| - beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
|
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - buffer.add('typeof ');
|
| - use(input, JSPrecedence.PREFIX_PRECEDENCE);
|
| - buffer.add(" === 'function'");
|
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
|
| - buffer.add(" || ");
|
| - beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
|
| - checkObject(input, '===');
|
| - buffer.add(" && ");
|
| - checkType(input, element);
|
| - endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
|
| - endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
|
| - }
|
| -
|
| - void checkType(HInstruction input, Element element) {
|
| - bool requiresNativeIsCheck =
|
| - compiler.emitter.nativeEmitter.requiresNativeIsCheck(element);
|
| - if (!requiresNativeIsCheck) buffer.add('!!');
|
| - use(input, JSPrecedence.MEMBER_PRECEDENCE);
|
| - buffer.add('.');
|
| - buffer.add(compiler.namer.operatorIs(element));
|
| - if (requiresNativeIsCheck) buffer.add('()');
|
| - }
|
| -
|
| - void handleStringSupertypeCheck(HInstruction input, Element element) {
|
| - // Make sure List and String don't share supertypes, otherwise we
|
| - // would need to check for List too.
|
| - assert(element !== compiler.listClass
|
| - && !Elements.isListSupertype(element, compiler));
|
| - beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
|
| - checkString(input, '===');
|
| - buffer.add(' || ');
|
| - beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
|
| - checkObject(input, '===');
|
| - buffer.add(' && ');
|
| - checkType(input, element);
|
| - endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
|
| - endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
|
| - }
|
| -
|
| - void handleListOrSupertypeCheck(HInstruction input, Element element) {
|
| - // Make sure List and String don't share supertypes, otherwise we
|
| - // would need to check for String too.
|
| - assert(element !== compiler.stringClass
|
| - && !Elements.isStringSupertype(element, compiler));
|
| - beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
|
| - checkObject(input, '===');
|
| - buffer.add(' && (');
|
| - beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
|
| - checkArray(input, '===');
|
| - buffer.add(' || ');
|
| - checkType(input, element);
|
| - buffer.add(')');
|
| - endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
|
| - endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
|
| - }
|
| -
|
| - void visitIs(HIs node) {
|
| - Element element = node.typeExpression;
|
| - if (element.kind === ElementKind.TYPE_VARIABLE) {
|
| - compiler.unimplemented("visitIs for type variables");
|
| - }
|
| - compiler.registerIsCheck(element);
|
| - LibraryElement coreLibrary = compiler.coreLibrary;
|
| - ClassElement objectClass = coreLibrary.find(const SourceString('Object'));
|
| - HInstruction input = node.expression;
|
| - if (node.nullOk) {
|
| - beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
|
| - checkNull(input);
|
| - buffer.add(' || ');
|
| - }
|
| -
|
| - if (element === objectClass || element === compiler.dynamicClass) {
|
| - // The constant folder also does this optimization, but we make
|
| - // it safe by assuming it may have not run.
|
| - buffer.add('true');
|
| - } else if (element == compiler.stringClass) {
|
| - checkString(input, '===');
|
| - } else if (element == compiler.doubleClass) {
|
| - checkDouble(input, '===');
|
| - } else if (element == compiler.numClass) {
|
| - checkNum(input, '===');
|
| - } else if (element == compiler.boolClass) {
|
| - checkBool(input, '===');
|
| - } else if (element == compiler.functionClass) {
|
| - checkFunction(input, element);
|
| - } else if (element == compiler.intClass) {
|
| - beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
|
| - checkNum(input, '===');
|
| - buffer.add(' && ');
|
| - checkInt(input, '===');
|
| - endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
|
| - } else if (Elements.isStringSupertype(element, compiler)) {
|
| - handleStringSupertypeCheck(input, element);
|
| - } else if (element === compiler.listClass
|
| - || Elements.isListSupertype(element, compiler)) {
|
| - handleListOrSupertypeCheck(input, element);
|
| - } else {
|
| - beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
|
| - checkObject(input, '===');
|
| - buffer.add(' && ');
|
| - checkType(input, element);
|
| - endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
|
| - }
|
| -
|
| - if (node.nullOk) {
|
| - endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
|
| - }
|
| - }
|
| -}
|
| -
|
| -class SsaOptimizedCodeGenerator extends SsaCodeGenerator {
|
| - SsaOptimizedCodeGenerator(compiler, work, parameters, parameterNames)
|
| - : super(compiler, work, parameters, parameterNames);
|
| -
|
| - void beginGraph(HGraph graph) {}
|
| - void endGraph(HGraph graph) {}
|
| -
|
| - void bailout(HTypeGuard guard, String reason) {
|
| - HInstruction input = guard.guarded;
|
| - Namer namer = compiler.namer;
|
| - Element element = work.element;
|
| - buffer.add('return ');
|
| - if (element.isInstanceMember()) {
|
| - // TODO(ngeoffray): This does not work in case we come from a
|
| - // super call. We must make bailout names unique.
|
| - buffer.add('this.${namer.getBailoutName(element)}');
|
| - } else {
|
| - buffer.add(namer.isolateBailoutAccess(element));
|
| - }
|
| - int parametersCount = parameterNames.length;
|
| - buffer.add('($parameters');
|
| - if (parametersCount != 0) buffer.add(', ');
|
| - if (guard.guarded is !HParameterValue) {
|
| - buffer.add('${guard.state}');
|
| - bool first = true;
|
| - // TODO(ngeoffray): if the bailout method takes more arguments,
|
| - // fill the remaining arguments with undefined.
|
| - // TODO(ngeoffray): try to put a variable at a deterministic
|
| - // location, so that multiple bailout calls put the variable at
|
| - // the same parameter index.
|
| - for (int i = 0; i < guard.inputs.length; i++) {
|
| - buffer.add(', ');
|
| - use(guard.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
|
| - }
|
| - } else {
|
| - assert(guard.guarded is HParameterValue);
|
| - buffer.add(' 0');
|
| - }
|
| - buffer.add(')');
|
| - }
|
| -
|
| - void visitTypeGuard(HTypeGuard node) {
|
| - addIndentation();
|
| - HInstruction input = node.guarded;
|
| - assert(!isGenerateAtUseSite(input) || input.isCodeMotionInvariant());
|
| - if (node.isInteger()) {
|
| - buffer.add('if (');
|
| - checkInt(input, '!==');
|
| - buffer.add(') ');
|
| - bailout(node, 'Not an integer');
|
| - } else if (node.isNumber()) {
|
| - buffer.add('if (');
|
| - checkNum(input, '!==');
|
| - buffer.add(') ');
|
| - bailout(node, 'Not a number');
|
| - } else if (node.isBoolean()) {
|
| - buffer.add('if (');
|
| - checkBool(input, '!==');
|
| - buffer.add(') ');
|
| - bailout(node, 'Not a boolean');
|
| - } else if (node.isString()) {
|
| - buffer.add('if (');
|
| - checkString(input, '!==');
|
| - buffer.add(') ');
|
| - bailout(node, 'Not a string');
|
| - } else if (node.isArray()) {
|
| - buffer.add('if (');
|
| - checkObject(input, '!==');
|
| - buffer.add('||');
|
| - checkArray(input, '!==');
|
| - buffer.add(') ');
|
| - bailout(node, 'Not an array');
|
| - } else if (node.isStringOrArray()) {
|
| - buffer.add('if (');
|
| - checkString(input, '!==');
|
| - buffer.add(' && (');
|
| - checkObject(input, '!==');
|
| - buffer.add('||');
|
| - checkArray(input, '!==');
|
| - buffer.add(')) ');
|
| - bailout(node, 'Not a string or array');
|
| - } else {
|
| - unreachable();
|
| - }
|
| - buffer.add(';\n');
|
| - }
|
| -
|
| - void beginLoop(HBasicBlock block) {
|
| - addIndentation();
|
| - for (LabelElement label in block.loopInformation.labels) {
|
| - writeLabel(label);
|
| - buffer.add(":");
|
| - }
|
| - buffer.add('while (true) {\n');
|
| - indent++;
|
| - }
|
| -
|
| - void endLoop(HBasicBlock block) {
|
| - indent--;
|
| - addIndentation();
|
| - buffer.add('}\n'); // Close 'while' loop.
|
| - }
|
| -
|
| - void handleLoopCondition(HLoopBranch node) {
|
| - buffer.add('if (!');
|
| - use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE);
|
| - buffer.add(') break;\n');
|
| - }
|
| -
|
| - void startIf(HIf node) {
|
| - }
|
| -
|
| - void endIf(HIf node) {
|
| - indent--;
|
| - addIndentation();
|
| - buffer.add('}\n');
|
| - }
|
| -
|
| - void startThen(HIf node) {
|
| - addIndentation();
|
| - buffer.add('if (');
|
| - use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
|
| - buffer.add(') {\n');
|
| - indent++;
|
| - }
|
| -
|
| - void endThen(HIf node) {
|
| - }
|
| -
|
| - void startElse(HIf node) {
|
| - indent--;
|
| - addIndentation();
|
| - buffer.add('} else {\n');
|
| - indent++;
|
| - }
|
| -
|
| - void endElse(HIf node) {
|
| - }
|
| -}
|
| -
|
| -class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
|
| -
|
| - final StringBuffer setup;
|
| - final List<String> labels;
|
| - int labelId = 0;
|
| - int maxBailoutParameters = 0;
|
| -
|
| - SsaUnoptimizedCodeGenerator(compiler, work, parameters, parameterNames)
|
| - : super(compiler, work, parameters, parameterNames),
|
| - setup = new StringBuffer(),
|
| - labels = <String>[];
|
| -
|
| - String pushLabel() {
|
| - String label = 'L${labelId++}';
|
| - labels.addLast(label);
|
| - return label;
|
| - }
|
| -
|
| - String popLabel() {
|
| - return labels.removeLast();
|
| - }
|
| -
|
| - String currentLabel() {
|
| - return labels.last();
|
| - }
|
| -
|
| - void beginGraph(HGraph graph) {
|
| - if (!graph.entry.hasGuards()) return;
|
| - addIndentation();
|
| - buffer.add('switch (state) {\n');
|
| - indent++;
|
| - addIndentation();
|
| - buffer.add('case 0:\n');
|
| - indent++;
|
| -
|
| - // The setup phase of a bailout function sets up the environment for
|
| - // each bailout target. Each bailout target will populate this
|
| - // setup phase. It is put at the beginning of the function.
|
| - setup.add(' switch (state) {\n');
|
| - }
|
| -
|
| - void endGraph(HGraph graph) {
|
| - if (!graph.entry.hasGuards()) return;
|
| - indent--; // Close original case.
|
| - indent--;
|
| - addIndentation();
|
| - buffer.add('}\n'); // Close 'switch'.
|
| - setup.add(' }\n');
|
| - }
|
| -
|
| - // For instructions that reference a guard or a check, we change that
|
| - // reference to the instruction they guard against. Therefore, we must
|
| - // use that instruction when restoring the environment.
|
| - HInstruction unwrap(HInstruction argument) {
|
| - if (argument is HIntegerCheck) {
|
| - HIntegerCheck instruction = argument;
|
| - return unwrap(instruction.value);
|
| - } else if (argument is HBoundsCheck) {
|
| - HBoundsCheck instruction = argument;
|
| - return unwrap(instruction.index);
|
| - } else if (argument is HTypeGuard) {
|
| - HTypeGuard instruction = argument;
|
| - return unwrap(instruction.guarded);
|
| - } else {
|
| - return argument;
|
| - }
|
| - }
|
| -
|
| - void visitTypeGuard(HTypeGuard node) {
|
| - indent--;
|
| - addIndentation();
|
| - buffer.add('case ${node.state}:\n');
|
| - indent++;
|
| - addIndentation();
|
| - buffer.add('state = 0;\n');
|
| -
|
| - setup.add(' case ${node.state}:\n');
|
| - int i = 0;
|
| - for (HInstruction input in node.inputs) {
|
| - HInstruction instruction = unwrap(input);
|
| - setup.add(' ${temporary(instruction)} = env$i;\n');
|
| - i++;
|
| - }
|
| - if (i > maxBailoutParameters) maxBailoutParameters = i;
|
| - setup.add(' break;\n');
|
| - }
|
| -
|
| - void startBailoutCase(List<HTypeGuard> bailouts1,
|
| - List<HTypeGuard> bailouts2) {
|
| - indent--;
|
| - handleBailoutCase(bailouts1);
|
| - handleBailoutCase(bailouts2);
|
| - indent++;
|
| - }
|
| -
|
| - void handleBailoutCase(List<HTypeGuard> guards) {
|
| - for (int i = 0, len = guards.length; i < len; i++) {
|
| - addIndentation();
|
| - buffer.add('case ${guards[i].state}:\n');
|
| - }
|
| - }
|
| -
|
| - void startBailoutSwitch() {
|
| - addIndentation();
|
| - buffer.add('switch (state) {\n');
|
| - indent++;
|
| - addIndentation();
|
| - buffer.add('case 0:\n');
|
| - indent++;
|
| - }
|
| -
|
| - void endBailoutSwitch() {
|
| - indent--; // Close 'case'.
|
| - indent--;
|
| - addIndentation();
|
| - buffer.add('}\n'); // Close 'switch'.
|
| - }
|
| -
|
| -
|
| - void beginLoop(HBasicBlock block) {
|
| - // TODO(ngeoffray): Don't put labels on loops that don't bailout.
|
| - String newLabel = pushLabel();
|
| - if (block.hasGuards()) {
|
| - startBailoutCase(block.guards, const <HTypeGuard>[]);
|
| - }
|
| -
|
| - addIndentation();
|
| - for (SourceString label in block.loopInformation.labels) {
|
| - writeLabel(label);
|
| - buffer.add(":");
|
| - }
|
| - buffer.add('$newLabel: while (true) {\n');
|
| - indent++;
|
| -
|
| - if (block.hasGuards()) {
|
| - startBailoutSwitch();
|
| - }
|
| - }
|
| -
|
| - void endLoop(HBasicBlock block) {
|
| - popLabel();
|
| - HBasicBlock header = block.isLoopHeader() ? block : block.parentLoopHeader;
|
| - if (header.hasGuards()) {
|
| - endBailoutSwitch();
|
| - }
|
| - indent--;
|
| - addIndentation();
|
| - buffer.add('}\n'); // Close 'while'.
|
| - }
|
| -
|
| - void handleLoopCondition(HLoopBranch node) {
|
| - buffer.add('if (!');
|
| - use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE);
|
| - buffer.add(') break ${currentLabel()};\n');
|
| - }
|
| -
|
| - void startIf(HIf node) {
|
| - bool hasGuards = node.thenBlock.hasGuards()
|
| - || (node.hasElse && node.elseBlock.hasGuards());
|
| - if (hasGuards) {
|
| - startBailoutCase(node.thenBlock.guards,
|
| - node.hasElse ? node.elseBlock.guards : const <HTypeGuard>[]);
|
| - }
|
| - }
|
| -
|
| - void endIf(HIf node) {
|
| - indent--;
|
| - addIndentation();
|
| - buffer.add('}\n');
|
| - }
|
| -
|
| - void startThen(HIf node) {
|
| - addIndentation();
|
| - bool hasGuards = node.thenBlock.hasGuards()
|
| - || (node.hasElse && node.elseBlock.hasGuards());
|
| - buffer.add('if (');
|
| - int precedence = JSPrecedence.EXPRESSION_PRECEDENCE;
|
| - if (hasGuards) {
|
| - // TODO(ngeoffray): Put the condition initialization in the
|
| - // [setup] buffer.
|
| - List<HTypeGuard> guards = node.thenBlock.guards;
|
| - for (int i = 0, len = guards.length; i < len; i++) {
|
| - buffer.add('state == ${guards[i].state} || ');
|
| - }
|
| - buffer.add('(state == 0 && ');
|
| - precedence = JSPrecedence.BITWISE_OR_PRECEDENCE;
|
| - }
|
| - use(node.inputs[0], precedence);
|
| - if (hasGuards) {
|
| - buffer.add(')');
|
| - }
|
| - buffer.add(') {\n');
|
| - indent++;
|
| - if (node.thenBlock.hasGuards()) {
|
| - startBailoutSwitch();
|
| - }
|
| - }
|
| -
|
| - void endThen(HIf node) {
|
| - if (node.thenBlock.hasGuards()) {
|
| - endBailoutSwitch();
|
| - }
|
| - }
|
| -
|
| - void startElse(HIf node) {
|
| - indent--;
|
| - addIndentation();
|
| - buffer.add('} else {\n');
|
| - indent++;
|
| - if (node.elseBlock.hasGuards()) {
|
| - startBailoutSwitch();
|
| - }
|
| - }
|
| -
|
| - void endElse(HIf node) {
|
| - if (node.elseBlock.hasGuards()) {
|
| - endBailoutSwitch();
|
| - }
|
| - }
|
| -}
|
|
|