| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 class SsaCodeGeneratorTask extends CompilerTask { | |
| 6 SsaCodeGeneratorTask(Compiler compiler) : super(compiler); | |
| 7 String get name() => 'SSA code generator'; | |
| 8 | |
| 9 | |
| 10 String generateMethod(WorkItem work, HGraph graph) { | |
| 11 return measure(() { | |
| 12 compiler.tracer.traceGraph("codegen", graph); | |
| 13 Map<Element, String> parameterNames = getParameterNames(work); | |
| 14 String parameters = Strings.join(parameterNames.getValues(), ', '); | |
| 15 SsaOptimizedCodeGenerator codegen = new SsaOptimizedCodeGenerator( | |
| 16 compiler, work, parameters, parameterNames); | |
| 17 codegen.visitGraph(graph); | |
| 18 return 'function($parameters) {\n${codegen.buffer}}'; | |
| 19 }); | |
| 20 } | |
| 21 | |
| 22 String generateBailoutMethod(WorkItem work, HGraph graph) { | |
| 23 return measure(() { | |
| 24 compiler.tracer.traceGraph("codegen-bailout", graph); | |
| 25 new SsaBailoutPropagator(compiler).visitGraph(graph); | |
| 26 | |
| 27 Map<Element, String> parameterNames = getParameterNames(work); | |
| 28 String parameters = Strings.join(parameterNames.getValues(), ', '); | |
| 29 SsaUnoptimizedCodeGenerator codegen = new SsaUnoptimizedCodeGenerator( | |
| 30 compiler, work, parameters, parameterNames); | |
| 31 codegen.visitGraph(graph); | |
| 32 | |
| 33 StringBuffer newParameters = new StringBuffer(); | |
| 34 if (!parameterNames.isEmpty()) newParameters.add('$parameters, '); | |
| 35 newParameters.add('state'); | |
| 36 | |
| 37 for (int i = 0; i < codegen.maxBailoutParameters; i++) { | |
| 38 newParameters.add(', env$i'); | |
| 39 } | |
| 40 | |
| 41 return 'function($newParameters) {\n${codegen.setup}${codegen.buffer}}'; | |
| 42 }); | |
| 43 } | |
| 44 | |
| 45 Map<Element, String> getParameterNames(WorkItem work) { | |
| 46 Map<Element, String> parameterNames = new LinkedHashMap<Element, String>(); | |
| 47 FunctionElement function = work.element; | |
| 48 | |
| 49 // The dom/html libraries have inline JS code that reference | |
| 50 // parameter names directly. Long-term such code will be rejected. | |
| 51 // Now, just don't mangle the parameter name. | |
| 52 function.computeParameters(compiler).forEachParameter((Element element) { | |
| 53 parameterNames[element] = function.isNative() | |
| 54 ? element.name.slowToString() | |
| 55 : JsNames.getValid('${element.name.slowToString()}'); | |
| 56 }); | |
| 57 return parameterNames; | |
| 58 } | |
| 59 } | |
| 60 | |
| 61 typedef void ElementAction(Element element); | |
| 62 | |
| 63 class SsaCodeGenerator implements HVisitor { | |
| 64 final Compiler compiler; | |
| 65 final WorkItem work; | |
| 66 final StringBuffer buffer; | |
| 67 final String parameters; | |
| 68 | |
| 69 final Map<Element, String> parameterNames; | |
| 70 final Map<int, String> names; | |
| 71 final Map<String, int> prefixes; | |
| 72 final Set<HInstruction> generateAtUseSite; | |
| 73 final Map<HPhi, String> logicalOperations; | |
| 74 final Map<Element, ElementAction> breakAction; | |
| 75 final Map<Element, ElementAction> continueAction; | |
| 76 | |
| 77 Element equalsNullElement; | |
| 78 int indent = 0; | |
| 79 int expectedPrecedence = JSPrecedence.STATEMENT_PRECEDENCE; | |
| 80 HGraph currentGraph; | |
| 81 HBasicBlock currentBlock; | |
| 82 | |
| 83 // Records a block-information that is being handled specially. | |
| 84 // Used to break bad recursion. | |
| 85 HLabeledBlockInformation currentBlockInformation; | |
| 86 // The subgraph is used to delimit traversal for some constructions, e.g., | |
| 87 // if branches. | |
| 88 SubGraph subGraph; | |
| 89 | |
| 90 LibraryElement get currentLibrary() => work.element.getLibrary(); | |
| 91 | |
| 92 bool isGenerateAtUseSite(HInstruction instruction) { | |
| 93 return generateAtUseSite.contains(instruction); | |
| 94 } | |
| 95 | |
| 96 SsaCodeGenerator(this.compiler, | |
| 97 this.work, | |
| 98 this.parameters, | |
| 99 this.parameterNames) | |
| 100 : names = new Map<int, String>(), | |
| 101 prefixes = new Map<String, int>(), | |
| 102 buffer = new StringBuffer(), | |
| 103 generateAtUseSite = new Set<HInstruction>(), | |
| 104 logicalOperations = new Map<HPhi, String>(), | |
| 105 breakAction = new Map<Element, ElementAction>(), | |
| 106 continueAction = new Map<Element, ElementAction>() { | |
| 107 | |
| 108 for (final name in parameterNames.getValues()) { | |
| 109 prefixes[name] = 0; | |
| 110 } | |
| 111 | |
| 112 equalsNullElement = | |
| 113 compiler.builder.interceptors.getEqualsNullInterceptor(); | |
| 114 } | |
| 115 | |
| 116 abstract visitTypeGuard(HTypeGuard node); | |
| 117 | |
| 118 abstract beginGraph(HGraph graph); | |
| 119 abstract endGraph(HGraph graph); | |
| 120 | |
| 121 abstract beginLoop(HBasicBlock block); | |
| 122 abstract endLoop(HBasicBlock block); | |
| 123 abstract handleLoopCondition(HLoopBranch node); | |
| 124 | |
| 125 abstract startIf(HIf node); | |
| 126 abstract endIf(HIf node); | |
| 127 abstract startThen(HIf node); | |
| 128 abstract endThen(HIf node); | |
| 129 abstract startElse(HIf node); | |
| 130 abstract endElse(HIf node); | |
| 131 | |
| 132 void beginExpression(int precedence) { | |
| 133 if (precedence < expectedPrecedence) { | |
| 134 buffer.add('('); | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 void endExpression(int precedence) { | |
| 139 if (precedence < expectedPrecedence) { | |
| 140 buffer.add(')'); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 void preGenerateMethod(HGraph graph) { | |
| 145 new SsaInstructionMerger(generateAtUseSite).visitGraph(graph); | |
| 146 new SsaConditionMerger(generateAtUseSite, | |
| 147 logicalOperations).visitGraph(graph); | |
| 148 } | |
| 149 | |
| 150 visitGraph(HGraph graph) { | |
| 151 preGenerateMethod(graph); | |
| 152 currentGraph = graph; | |
| 153 indent++; // We are already inside a function. | |
| 154 subGraph = new SubGraph(graph.entry, graph.exit); | |
| 155 beginGraph(graph); | |
| 156 visitBasicBlock(graph.entry); | |
| 157 endGraph(graph); | |
| 158 } | |
| 159 | |
| 160 void visitSubGraph(SubGraph newSubGraph) { | |
| 161 SubGraph oldSubGraph = subGraph; | |
| 162 subGraph = newSubGraph; | |
| 163 visitBasicBlock(subGraph.start); | |
| 164 subGraph = oldSubGraph; | |
| 165 } | |
| 166 | |
| 167 String temporary(HInstruction instruction) { | |
| 168 int id = instruction.id; | |
| 169 String name = names[id]; | |
| 170 if (name !== null) return name; | |
| 171 | |
| 172 if (instruction is HPhi) { | |
| 173 HPhi phi = instruction; | |
| 174 Element element = phi.element; | |
| 175 if (element != null && element.kind == ElementKind.PARAMETER) { | |
| 176 name = parameterNames[element]; | |
| 177 names[id] = name; | |
| 178 return name; | |
| 179 } | |
| 180 | |
| 181 String prefix; | |
| 182 if (element !== null && !element.name.isEmpty()) { | |
| 183 prefix = element.name.slowToString(); | |
| 184 } else { | |
| 185 prefix = 'v'; | |
| 186 } | |
| 187 if (!prefixes.containsKey(prefix)) { | |
| 188 prefixes[prefix] = 0; | |
| 189 return newName(id, prefix); | |
| 190 } else { | |
| 191 return newName(id, '${prefix}_${prefixes[prefix]++}'); | |
| 192 } | |
| 193 } else { | |
| 194 String prefix = 't'; | |
| 195 if (!prefixes.containsKey(prefix)) prefixes[prefix] = 0; | |
| 196 return newName(id, '${prefix}${prefixes[prefix]++}'); | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 bool temporaryExists(HInstruction instruction) { | |
| 201 return names.containsKey(instruction.id); | |
| 202 } | |
| 203 | |
| 204 String newName(int id, String name) { | |
| 205 String result = JsNames.getValid(name); | |
| 206 names[id] = result; | |
| 207 return result; | |
| 208 } | |
| 209 | |
| 210 /** | |
| 211 * Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET]. | |
| 212 */ | |
| 213 void visitArguments(List<HInstruction> inputs) { | |
| 214 assert(inputs.length >= HInvoke.ARGUMENTS_OFFSET); | |
| 215 buffer.add('('); | |
| 216 for (int i = HInvoke.ARGUMENTS_OFFSET; i < inputs.length; i++) { | |
| 217 if (i != HInvoke.ARGUMENTS_OFFSET) buffer.add(', '); | |
| 218 use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 219 } | |
| 220 buffer.add(')'); | |
| 221 } | |
| 222 | |
| 223 void define(HInstruction instruction) { | |
| 224 buffer.add('var ${temporary(instruction)} = '); | |
| 225 visit(instruction, JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 226 } | |
| 227 | |
| 228 void use(HInstruction argument, int expectedPrecedence) { | |
| 229 if (isGenerateAtUseSite(argument)) { | |
| 230 visit(argument, expectedPrecedence); | |
| 231 } else if (argument is HIntegerCheck) { | |
| 232 HIntegerCheck instruction = argument; | |
| 233 use(instruction.value, expectedPrecedence); | |
| 234 } else if (argument is HBoundsCheck) { | |
| 235 HBoundsCheck instruction = argument; | |
| 236 use(instruction.index, expectedPrecedence); | |
| 237 } else if (argument is HTypeGuard) { | |
| 238 HTypeGuard instruction = argument; | |
| 239 use(instruction.guarded, expectedPrecedence); | |
| 240 } else { | |
| 241 buffer.add(temporary(argument)); | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 visit(HInstruction node, int expectedPrecedence) { | |
| 246 int oldPrecedence = this.expectedPrecedence; | |
| 247 this.expectedPrecedence = expectedPrecedence; | |
| 248 node.accept(this); | |
| 249 this.expectedPrecedence = oldPrecedence; | |
| 250 } | |
| 251 | |
| 252 void continueAsBreak(LabelElement target) { | |
| 253 addIndentation(); | |
| 254 buffer.add("break "); | |
| 255 writeContinueLabel(target); | |
| 256 buffer.add(";\n"); | |
| 257 } | |
| 258 | |
| 259 void implicitContinueAsBreak(TargetElement target) { | |
| 260 addIndentation(); | |
| 261 buffer.add("break "); | |
| 262 writeImplicitContinueLabel(target); | |
| 263 buffer.add(";\n"); | |
| 264 } | |
| 265 | |
| 266 void implicitBreakWithLabel(TargetElement target) { | |
| 267 addIndentation(); | |
| 268 buffer.add("break "); | |
| 269 writeImplicitLabel(target); | |
| 270 buffer.add(";\n"); | |
| 271 } | |
| 272 | |
| 273 void handleLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { | |
| 274 addIndentation(); | |
| 275 Link<Element> continueOverrides = const EmptyLink<Element>(); | |
| 276 // If [labeledBlockInfo.isContinue], the block is an artificial | |
| 277 // block around the body of a loop with an update block, so that | |
| 278 // continues of the loop can be written as breaks of the body | |
| 279 // block. | |
| 280 if (labeledBlockInfo.isContinue) { | |
| 281 for (LabelElement label in labeledBlockInfo.labels) { | |
| 282 if (label.isContinueTarget) { | |
| 283 writeContinueLabel(label); | |
| 284 buffer.add(':'); | |
| 285 continueAction[label] = continueAsBreak; | |
| 286 continueOverrides = continueOverrides.prepend(label); | |
| 287 } | |
| 288 } | |
| 289 // For handling unlabeled continues from the body of a loop. | |
| 290 // TODO(lrn): Consider recording whether the target is in fact | |
| 291 // a target of an unlabeled continue, and not generate this if it isn't. | |
| 292 TargetElement target = labeledBlockInfo.target; | |
| 293 writeImplicitContinueLabel(target); | |
| 294 buffer.add(':'); | |
| 295 continueAction[target] = implicitContinueAsBreak; | |
| 296 continueOverrides = continueOverrides.prepend(target); | |
| 297 } else { | |
| 298 for (LabelElement label in labeledBlockInfo.labels) { | |
| 299 if (label.isBreakTarget) { | |
| 300 writeLabel(label); | |
| 301 buffer.add(':'); | |
| 302 } | |
| 303 } | |
| 304 TargetElement target = labeledBlockInfo.target; | |
| 305 if (target.isSwitch) { | |
| 306 // This is an extra block around a switch that is generated | |
| 307 // as a nested if/else chain. We add an extra break target | |
| 308 // so that case code can break. | |
| 309 writeImplicitLabel(target); | |
| 310 buffer.add(':'); | |
| 311 breakAction[target] = implicitBreakWithLabel; | |
| 312 } | |
| 313 } | |
| 314 buffer.add('{\n'); | |
| 315 indent++; | |
| 316 | |
| 317 visitSubGraph(labeledBlockInfo.body); | |
| 318 | |
| 319 indent--; | |
| 320 addIndentation(); | |
| 321 buffer.add('}\n'); | |
| 322 | |
| 323 if (labeledBlockInfo.joinBlock !== null) { | |
| 324 visitBasicBlock(labeledBlockInfo.joinBlock); | |
| 325 } | |
| 326 if (labeledBlockInfo.isContinue) { | |
| 327 while (!continueOverrides.isEmpty()) { | |
| 328 continueAction.remove(continueOverrides.head); | |
| 329 continueOverrides = continueOverrides.tail; | |
| 330 } | |
| 331 } else { | |
| 332 breakAction.remove(labeledBlockInfo.target); | |
| 333 } | |
| 334 } | |
| 335 | |
| 336 void emitLogicalOperation(HPhi node, String operation) { | |
| 337 JSBinaryOperatorPrecedence operatorPrecedence = | |
| 338 JSPrecedence.binary[operation]; | |
| 339 beginExpression(operatorPrecedence.precedence); | |
| 340 use(node.inputs[0], operatorPrecedence.left); | |
| 341 buffer.add(" $operation "); | |
| 342 use(node.inputs[1], operatorPrecedence.right); | |
| 343 endExpression(operatorPrecedence.precedence); | |
| 344 } | |
| 345 | |
| 346 visitBasicBlock(HBasicBlock node) { | |
| 347 // Abort traversal if we are leaving the currently active sub-graph. | |
| 348 if (!subGraph.contains(node)) return; | |
| 349 | |
| 350 // If this node has special behavior attached, handle it. | |
| 351 // If we reach here again while handling the attached information, | |
| 352 // e.g., because we call visitSubGraph on a subgraph starting here, | |
| 353 // don't handle it again. | |
| 354 if (node.hasLabeledBlockInformation() && | |
| 355 node.labeledBlockInformation !== currentBlockInformation) { | |
| 356 HLabeledBlockInformation oldBlockInformation = currentBlockInformation; | |
| 357 currentBlockInformation = node.labeledBlockInformation; | |
| 358 handleLabeledBlock(currentBlockInformation); | |
| 359 currentBlockInformation = oldBlockInformation; | |
| 360 return; | |
| 361 } | |
| 362 | |
| 363 currentBlock = node; | |
| 364 | |
| 365 if (node.isLoopHeader()) { | |
| 366 // While loop will be closed by the conditional loop-branch. | |
| 367 // TODO(floitsch): HACK HACK HACK. | |
| 368 beginLoop(node); | |
| 369 } | |
| 370 | |
| 371 HInstruction instruction = node.first; | |
| 372 while (instruction != null) { | |
| 373 if (instruction === node.last) { | |
| 374 for (HBasicBlock successor in node.successors) { | |
| 375 int index = successor.predecessors.indexOf(node); | |
| 376 successor.forEachPhi((HPhi phi) { | |
| 377 bool isLogicalOperation = logicalOperations.containsKey(phi); | |
| 378 // In case the phi is being generated by another | |
| 379 // instruction. | |
| 380 if (isLogicalOperation && isGenerateAtUseSite(phi)) return; | |
| 381 addIndentation(); | |
| 382 if (!temporaryExists(phi)) buffer.add('var '); | |
| 383 buffer.add('${temporary(phi)} = '); | |
| 384 if (isLogicalOperation) { | |
| 385 emitLogicalOperation(phi, logicalOperations[phi]); | |
| 386 } else { | |
| 387 use(phi.inputs[index], JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 388 } | |
| 389 buffer.add(';\n'); | |
| 390 }); | |
| 391 } | |
| 392 } | |
| 393 | |
| 394 if (instruction is HGoto || instruction is HExit || instruction is HTry) { | |
| 395 visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE); | |
| 396 return; | |
| 397 } else if (!isGenerateAtUseSite(instruction)) { | |
| 398 if (instruction is !HIf && instruction is !HTypeGuard) { | |
| 399 addIndentation(); | |
| 400 } | |
| 401 if (instruction.usedBy.isEmpty() | |
| 402 || instruction is HTypeGuard | |
| 403 || instruction is HCheck) { | |
| 404 visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE); | |
| 405 } else { | |
| 406 define(instruction); | |
| 407 } | |
| 408 // Control flow instructions know how to handle ';'. | |
| 409 if (instruction is !HControlFlow && instruction is !HTypeGuard) { | |
| 410 buffer.add(';\n'); | |
| 411 } | |
| 412 } else if (instruction is HIf) { | |
| 413 HIf hif = instruction; | |
| 414 // The "if" is implementing part of a logical expression. | |
| 415 // Skip directly forward to to its latest successor, since everything | |
| 416 // in-between must also be generateAtUseSite. | |
| 417 assert(hif.trueBranch.id < hif.falseBranch.id); | |
| 418 visitBasicBlock(hif.falseBranch); | |
| 419 return; | |
| 420 } | |
| 421 instruction = instruction.next; | |
| 422 } | |
| 423 } | |
| 424 | |
| 425 visitInvokeBinary(HInvokeBinary node, String op) { | |
| 426 if (node.builtin) { | |
| 427 JSBinaryOperatorPrecedence operatorPrecedences = JSPrecedence.binary[op]; | |
| 428 beginExpression(operatorPrecedences.precedence); | |
| 429 use(node.left, operatorPrecedences.left); | |
| 430 buffer.add(' $op '); | |
| 431 use(node.right, operatorPrecedences.right); | |
| 432 endExpression(operatorPrecedences.precedence); | |
| 433 } else { | |
| 434 visitInvokeStatic(node); | |
| 435 } | |
| 436 } | |
| 437 | |
| 438 visitInvokeUnary(HInvokeUnary node, String op) { | |
| 439 if (node.builtin) { | |
| 440 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); | |
| 441 buffer.add('$op'); | |
| 442 use(node.operand, JSPrecedence.PREFIX_PRECEDENCE); | |
| 443 endExpression(JSPrecedence.PREFIX_PRECEDENCE); | |
| 444 } else { | |
| 445 visitInvokeStatic(node); | |
| 446 } | |
| 447 } | |
| 448 | |
| 449 visitEquals(HEquals node) { | |
| 450 if (node.builtin) { | |
| 451 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 452 use(node.left, JSPrecedence.EQUALITY_PRECEDENCE); | |
| 453 buffer.add(' === '); | |
| 454 use(node.right, JSPrecedence.RELATIONAL_PRECEDENCE); | |
| 455 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 456 } else if (node.element === equalsNullElement) { | |
| 457 beginExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 458 use(node.target, JSPrecedence.CALL_PRECEDENCE); | |
| 459 buffer.add('('); | |
| 460 use(node.left, JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 461 buffer.add(')'); | |
| 462 endExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 463 } else { | |
| 464 visitInvokeStatic(node); | |
| 465 } | |
| 466 } | |
| 467 | |
| 468 visitAdd(HAdd node) => visitInvokeBinary(node, '+'); | |
| 469 visitDivide(HDivide node) => visitInvokeBinary(node, '/'); | |
| 470 visitMultiply(HMultiply node) => visitInvokeBinary(node, '*'); | |
| 471 visitSubtract(HSubtract node) => visitInvokeBinary(node, '-'); | |
| 472 // Truncating divide does not have a JS equivalent. | |
| 473 visitTruncatingDivide(HTruncatingDivide node) => visitInvokeStatic(node); | |
| 474 // Modulo cannot be mapped to the native operator (different semantics). | |
| 475 visitModulo(HModulo node) => visitInvokeStatic(node); | |
| 476 | |
| 477 visitBitAnd(HBitAnd node) => visitInvokeBinary(node, '&'); | |
| 478 visitBitNot(HBitNot node) => visitInvokeUnary(node, '~'); | |
| 479 visitBitOr(HBitOr node) => visitInvokeBinary(node, '|'); | |
| 480 visitBitXor(HBitXor node) => visitInvokeBinary(node, '^'); | |
| 481 | |
| 482 // We need to check if the left operand is negative in order to use | |
| 483 // the native operator. | |
| 484 visitShiftRight(HShiftRight node) => visitInvokeStatic(node); | |
| 485 | |
| 486 // Shift left cannot be mapped to the native operator (different semantics). | |
| 487 visitShiftLeft(HShiftLeft node) => visitInvokeStatic(node); | |
| 488 | |
| 489 visitNegate(HNegate node) => visitInvokeUnary(node, '-'); | |
| 490 | |
| 491 visitIdentity(HIdentity node) => visitInvokeBinary(node, '==='); | |
| 492 visitLess(HLess node) => visitInvokeBinary(node, '<'); | |
| 493 visitLessEqual(HLessEqual node) => visitInvokeBinary(node, '<='); | |
| 494 visitGreater(HGreater node) => visitInvokeBinary(node, '>'); | |
| 495 visitGreaterEqual(HGreaterEqual node) => visitInvokeBinary(node, '>='); | |
| 496 | |
| 497 visitBoolify(HBoolify node) { | |
| 498 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 499 assert(node.inputs.length == 1); | |
| 500 use(node.inputs[0], JSPrecedence.EQUALITY_PRECEDENCE); | |
| 501 buffer.add(' === true'); | |
| 502 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 503 } | |
| 504 | |
| 505 visitExit(HExit node) { | |
| 506 // Don't do anything. | |
| 507 } | |
| 508 | |
| 509 visitGoto(HGoto node) { | |
| 510 assert(currentBlock.successors.length == 1); | |
| 511 List<HBasicBlock> dominated = currentBlock.dominatedBlocks; | |
| 512 // With the exception of the entry-node which dominates its successor | |
| 513 // and the exit node, no block finishing with a 'goto' can have more than | |
| 514 // one dominated block (since it has only one successor). | |
| 515 // If the successor is dominated by another block, then the other block | |
| 516 // is responsible for visiting the successor. | |
| 517 if (dominated.isEmpty()) return; | |
| 518 if (dominated.length > 2) unreachable(); | |
| 519 if (dominated.length == 2 && currentBlock !== currentGraph.entry) { | |
| 520 unreachable(); | |
| 521 } | |
| 522 assert(dominated[0] == currentBlock.successors[0]); | |
| 523 visitBasicBlock(dominated[0]); | |
| 524 } | |
| 525 | |
| 526 // Used to write the name of labels. | |
| 527 void writeLabel(LabelElement label) { | |
| 528 buffer.add('\$${label.labelName}\$${label.target.nestingLevel}'); | |
| 529 } | |
| 530 | |
| 531 void writeImplicitLabel(TargetElement target) { | |
| 532 buffer.add('\$${target.nestingLevel}'); | |
| 533 } | |
| 534 | |
| 535 // We sometimes handle continue targets differently from break targets, | |
| 536 // so we have special continue-only labels. | |
| 537 void writeContinueLabel(LabelElement label) { | |
| 538 buffer.add('c\$${label.labelName}\$${label.target.nestingLevel}'); | |
| 539 } | |
| 540 | |
| 541 void writeImplicitContinueLabel(TargetElement target) { | |
| 542 buffer.add('c\$${target.nestingLevel}'); | |
| 543 } | |
| 544 | |
| 545 /** | |
| 546 * Checks if [map] contains an [ElementAction] for [element], and | |
| 547 * if so calls that action and returns true. | |
| 548 * Otherwise returns false. | |
| 549 */ | |
| 550 bool tryCallAction(Map<Element, ElementAction> map, Element element) { | |
| 551 ElementAction action = map[element]; | |
| 552 if (action === null) return false; | |
| 553 action(element); | |
| 554 return true; | |
| 555 } | |
| 556 | |
| 557 visitBreak(HBreak node) { | |
| 558 assert(currentBlock.successors.length == 1); | |
| 559 if (node.label !== null) { | |
| 560 LabelElement label = node.label; | |
| 561 if (!tryCallAction(breakAction, label)) { | |
| 562 addIndentation(); | |
| 563 buffer.add("break "); | |
| 564 writeLabel(label); | |
| 565 buffer.add(";\n"); | |
| 566 } | |
| 567 } else { | |
| 568 TargetElement target = node.target; | |
| 569 if (!tryCallAction(breakAction, target)) { | |
| 570 addIndentation(); | |
| 571 buffer.add("break;\n"); | |
| 572 } | |
| 573 } | |
| 574 } | |
| 575 | |
| 576 visitContinue(HContinue node) { | |
| 577 assert(currentBlock.successors.length == 1); | |
| 578 if (node.label !== null) { | |
| 579 LabelElement label = node.label; | |
| 580 if (!tryCallAction(continueAction, label)) { | |
| 581 addIndentation(); | |
| 582 buffer.add("continue "); | |
| 583 writeLabel(label); | |
| 584 buffer.add(";\n"); | |
| 585 } | |
| 586 } else { | |
| 587 TargetElement target = node.target; | |
| 588 if (!tryCallAction(continueAction, target)) { | |
| 589 addIndentation(); | |
| 590 buffer.add("continue;\n"); | |
| 591 } | |
| 592 } | |
| 593 } | |
| 594 | |
| 595 visitTry(HTry node) { | |
| 596 addIndentation(); | |
| 597 buffer.add('try {\n'); | |
| 598 indent++; | |
| 599 List<HBasicBlock> successors = node.block.successors; | |
| 600 visitBasicBlock(successors[0]); | |
| 601 indent--; | |
| 602 | |
| 603 if (node.finallyBlock != successors[1]) { | |
| 604 // Printing the catch part. | |
| 605 addIndentation(); | |
| 606 String name = temporary(node.exception); | |
| 607 parameterNames[node.exception.element] = name; | |
| 608 buffer.add('} catch ($name) {\n'); | |
| 609 indent++; | |
| 610 visitBasicBlock(successors[1]); | |
| 611 parameterNames.remove(node.exception.element); | |
| 612 indent--; | |
| 613 } | |
| 614 | |
| 615 if (node.finallyBlock != null) { | |
| 616 addIndentation(); | |
| 617 buffer.add('} finally {\n'); | |
| 618 indent++; | |
| 619 visitBasicBlock(node.finallyBlock); | |
| 620 indent--; | |
| 621 } | |
| 622 addIndentation(); | |
| 623 buffer.add('}\n'); | |
| 624 | |
| 625 visitBasicBlock(node.joinBlock); | |
| 626 } | |
| 627 | |
| 628 visitIf(HIf node) { | |
| 629 List<HBasicBlock> dominated = node.block.dominatedBlocks; | |
| 630 HIfBlockInformation info = node.blockInformation; | |
| 631 startIf(node); | |
| 632 assert(!isGenerateAtUseSite(node)); | |
| 633 startThen(node); | |
| 634 assert(node.thenBlock === dominated[0]); | |
| 635 visitSubGraph(info.thenGraph); | |
| 636 int preVisitedBlocks = 1; | |
| 637 endThen(node); | |
| 638 if (node.hasElse) { | |
| 639 startElse(node); | |
| 640 assert(node.elseBlock === dominated[1]); | |
| 641 visitSubGraph(info.elseGraph); | |
| 642 preVisitedBlocks = 2; | |
| 643 endElse(node); | |
| 644 } | |
| 645 endIf(node); | |
| 646 if (info.joinBlock !== null && info.joinBlock.dominator !== node.block) { | |
| 647 // The join block is dominated by a block in one of the branches. | |
| 648 // The subgraph traversal never reached it, so we visit it here | |
| 649 // instead. | |
| 650 visitBasicBlock(info.joinBlock); | |
| 651 } | |
| 652 | |
| 653 // Visit all the dominated blocks that are not part of the then or else | |
| 654 // branches, and is not the join block. | |
| 655 // Depending on how the then/else branches terminate | |
| 656 // (e.g., return/throw/break) there can be any number of these. | |
| 657 int dominatedCount = dominated.length; | |
| 658 for (int i = preVisitedBlocks; i < dominatedCount; i++) { | |
| 659 HBasicBlock dominatedBlock = dominated[i]; | |
| 660 assert(dominatedBlock.dominator === node.block); | |
| 661 visitBasicBlock(dominatedBlock); | |
| 662 } | |
| 663 } | |
| 664 | |
| 665 visitInvokeDynamicMethod(HInvokeDynamicMethod node) { | |
| 666 beginExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 667 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); | |
| 668 buffer.add('.'); | |
| 669 // Avoid adding the generative constructor name to the list of | |
| 670 // seen selectors. | |
| 671 if (node.inputs[0] is HForeignNew) { | |
| 672 HForeignNew foreignNew = node.inputs[0]; | |
| 673 // Remove 'this' from the number of arguments. | |
| 674 int argumentCount = node.inputs.length - 1; | |
| 675 | |
| 676 // TODO(ahe): The constructor name was statically resolved in | |
| 677 // SsaBuilder.buildFactory. Is there a cleaner way to do this? | |
| 678 node.name.printOn(buffer); | |
| 679 visitArguments(node.inputs); | |
| 680 } else { | |
| 681 buffer.add(compiler.namer.instanceMethodInvocationName( | |
| 682 currentLibrary, node.name, node.selector)); | |
| 683 visitArguments(node.inputs); | |
| 684 compiler.registerDynamicInvocation(node.name, node.selector); | |
| 685 } | |
| 686 endExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 687 } | |
| 688 | |
| 689 visitInvokeDynamicSetter(HInvokeDynamicSetter node) { | |
| 690 beginExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 691 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); | |
| 692 buffer.add('.'); | |
| 693 buffer.add(compiler.namer.setterName(currentLibrary, node.name)); | |
| 694 visitArguments(node.inputs); | |
| 695 compiler.registerDynamicSetter(node.name); | |
| 696 endExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 697 } | |
| 698 | |
| 699 visitInvokeDynamicGetter(HInvokeDynamicGetter node) { | |
| 700 beginExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 701 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); | |
| 702 buffer.add('.'); | |
| 703 buffer.add(compiler.namer.getterName(currentLibrary, node.name)); | |
| 704 visitArguments(node.inputs); | |
| 705 compiler.registerDynamicGetter(node.name); | |
| 706 endExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 707 } | |
| 708 | |
| 709 visitInvokeClosure(HInvokeClosure node) { | |
| 710 beginExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 711 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); | |
| 712 buffer.add('.'); | |
| 713 buffer.add(compiler.namer.closureInvocationName(node.selector)); | |
| 714 visitArguments(node.inputs); | |
| 715 // TODO(floitsch): we should have a separate list for closure invocations. | |
| 716 compiler.registerDynamicInvocation(Namer.CLOSURE_INVOCATION_NAME, | |
| 717 node.selector); | |
| 718 endExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 719 } | |
| 720 | |
| 721 visitInvokeStatic(HInvokeStatic node) { | |
| 722 beginExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 723 use(node.target, JSPrecedence.CALL_PRECEDENCE); | |
| 724 visitArguments(node.inputs); | |
| 725 endExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 726 } | |
| 727 | |
| 728 visitInvokeSuper(HInvokeSuper node) { | |
| 729 beginExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 730 Element superMethod = node.element; | |
| 731 Element superClass = superMethod.enclosingElement; | |
| 732 // Remove the element and 'this'. | |
| 733 int argumentCount = node.inputs.length - 2; | |
| 734 String className = compiler.namer.isolatePropertyAccess(superClass); | |
| 735 String methodName; | |
| 736 if (superMethod.kind == ElementKind.FUNCTION || | |
| 737 superMethod.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | |
| 738 methodName = compiler.namer.instanceMethodName( | |
| 739 currentLibrary, superMethod.name, argumentCount); | |
| 740 } else { | |
| 741 methodName = compiler.namer.getterName(currentLibrary, superMethod.name); | |
| 742 // We need to register the name to ensure that the emitter | |
| 743 // generates the necessary getter. | |
| 744 // TODO(ahe): This is not optimal for tree-shaking, but we lack | |
| 745 // API to register the precise information. In this case, the | |
| 746 // enclosingElement of superMethod needs the getter, no other | |
| 747 // class (not even its subclasses). | |
| 748 compiler.registerDynamicGetter(superMethod.name); | |
| 749 } | |
| 750 buffer.add('$className.prototype.$methodName.call'); | |
| 751 visitArguments(node.inputs); | |
| 752 endExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 753 compiler.registerStaticUse(superMethod); | |
| 754 } | |
| 755 | |
| 756 visitFieldGet(HFieldGet node) { | |
| 757 String name = JsNames.getValid(node.element.name.slowToString()); | |
| 758 if (node.receiver !== null) { | |
| 759 beginExpression(JSPrecedence.MEMBER_PRECEDENCE); | |
| 760 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); | |
| 761 buffer.add('.'); | |
| 762 buffer.add(name); | |
| 763 beginExpression(JSPrecedence.MEMBER_PRECEDENCE); | |
| 764 } else { | |
| 765 buffer.add(name); | |
| 766 } | |
| 767 } | |
| 768 | |
| 769 visitFieldSet(HFieldSet node) { | |
| 770 if (node.receiver !== null) { | |
| 771 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 772 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); | |
| 773 buffer.add('.'); | |
| 774 } else { | |
| 775 // TODO(ngeoffray): Remove the 'var' once we don't globally box | |
| 776 // variables used in a try/catch. | |
| 777 buffer.add('var '); | |
| 778 } | |
| 779 String name = JsNames.getValid(node.element.name.slowToString()); | |
| 780 buffer.add(name); | |
| 781 buffer.add(' = '); | |
| 782 use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 783 if (node.receiver !== null) { | |
| 784 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 785 } | |
| 786 } | |
| 787 | |
| 788 visitForeign(HForeign node) { | |
| 789 String code = node.code.slowToString(); | |
| 790 List<HInstruction> inputs = node.inputs; | |
| 791 List<String> parts = code.split('#'); | |
| 792 if (parts.length != inputs.length + 1) { | |
| 793 compiler.internalError( | |
| 794 'Wrong number of arguments for JS', instruction: node); | |
| 795 } | |
| 796 beginExpression(JSPrecedence.EXPRESSION_PRECEDENCE); | |
| 797 buffer.add(parts[0]); | |
| 798 for (int i = 0; i < inputs.length; i++) { | |
| 799 use(inputs[i], JSPrecedence.EXPRESSION_PRECEDENCE); | |
| 800 buffer.add(parts[i + 1]); | |
| 801 } | |
| 802 endExpression(JSPrecedence.EXPRESSION_PRECEDENCE); | |
| 803 } | |
| 804 | |
| 805 visitForeignNew(HForeignNew node) { | |
| 806 String jsClassReference = compiler.namer.isolateAccess(node.element); | |
| 807 beginExpression(JSPrecedence.MEMBER_PRECEDENCE); | |
| 808 buffer.add('new $jsClassReference('); | |
| 809 // We can't use 'visitArguments', since our arguments start at input[0]. | |
| 810 List<HInstruction> inputs = node.inputs; | |
| 811 for (int i = 0; i < inputs.length; i++) { | |
| 812 if (i != 0) buffer.add(', '); | |
| 813 use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 814 } | |
| 815 buffer.add(')'); | |
| 816 endExpression(JSPrecedence.MEMBER_PRECEDENCE); | |
| 817 } | |
| 818 | |
| 819 visitConstant(HConstant node) { | |
| 820 assert(isGenerateAtUseSite(node)); | |
| 821 // TODO(floitsch): the compile-time constant handler and the codegen | |
| 822 // need to work together to avoid the parenthesis. See r4928 for an | |
| 823 // implementation that still dealt with precedence. | |
| 824 ConstantHandler handler = compiler.constantHandler; | |
| 825 String name = handler.getNameForConstant(node.constant); | |
| 826 if (name === null) { | |
| 827 assert(!node.constant.isObject()); | |
| 828 if (node.constant.isNum() | |
| 829 && expectedPrecedence == JSPrecedence.MEMBER_PRECEDENCE) { | |
| 830 buffer.add('('); | |
| 831 node.constant.writeJsCode(buffer, handler); | |
| 832 buffer.add(')'); | |
| 833 } else { | |
| 834 node.constant.writeJsCode(buffer, handler); | |
| 835 } | |
| 836 } else { | |
| 837 buffer.add(compiler.namer.CURRENT_ISOLATE); | |
| 838 buffer.add("."); | |
| 839 buffer.add(name); | |
| 840 } | |
| 841 } | |
| 842 | |
| 843 visitLoopBranch(HLoopBranch node) { | |
| 844 HBasicBlock branchBlock = currentBlock; | |
| 845 handleLoopCondition(node); | |
| 846 List<HBasicBlock> dominated = currentBlock.dominatedBlocks; | |
| 847 // For a do while loop, the body has already been visited. | |
| 848 if (!node.isDoWhile()) { | |
| 849 visitBasicBlock(dominated[0]); | |
| 850 } | |
| 851 endLoop(node.block); | |
| 852 visitBasicBlock(branchBlock.successors[1]); | |
| 853 // With labeled breaks we can have more dominated blocks. | |
| 854 if (dominated.length >= 3) { | |
| 855 for (int i = 2; i < dominated.length; i++) { | |
| 856 visitBasicBlock(dominated[i]); | |
| 857 } | |
| 858 } | |
| 859 } | |
| 860 | |
| 861 visitNot(HNot node) { | |
| 862 assert(node.inputs.length == 1); | |
| 863 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); | |
| 864 buffer.add('!'); | |
| 865 use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE); | |
| 866 endExpression(JSPrecedence.PREFIX_PRECEDENCE); | |
| 867 } | |
| 868 | |
| 869 visitParameterValue(HParameterValue node) { | |
| 870 assert(isGenerateAtUseSite(node)); | |
| 871 buffer.add(parameterNames[node.element]); | |
| 872 } | |
| 873 | |
| 874 visitPhi(HPhi node) { | |
| 875 String operation = logicalOperations[node]; | |
| 876 if (operation !== null) { | |
| 877 emitLogicalOperation(node, operation); | |
| 878 } else { | |
| 879 buffer.add('${temporary(node)}'); | |
| 880 } | |
| 881 } | |
| 882 | |
| 883 visitReturn(HReturn node) { | |
| 884 assert(node.inputs.length == 1); | |
| 885 HInstruction input = node.inputs[0]; | |
| 886 if (input.isConstantNull()) { | |
| 887 buffer.add('return;\n'); | |
| 888 } else { | |
| 889 buffer.add('return '); | |
| 890 use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE); | |
| 891 buffer.add(';\n'); | |
| 892 } | |
| 893 } | |
| 894 | |
| 895 visitThis(HThis node) { | |
| 896 buffer.add('this'); | |
| 897 } | |
| 898 | |
| 899 visitThrow(HThrow node) { | |
| 900 if (node.isRethrow) { | |
| 901 buffer.add('throw '); | |
| 902 use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE); | |
| 903 } else { | |
| 904 generateThrowWithHelper('captureStackTrace', node.inputs[0]); | |
| 905 } | |
| 906 buffer.add(';\n'); | |
| 907 } | |
| 908 | |
| 909 visitBoundsCheck(HBoundsCheck node) { | |
| 910 buffer.add('if ('); | |
| 911 use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE); | |
| 912 buffer.add(' < 0 || '); | |
| 913 use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE); | |
| 914 buffer.add(' >= '); | |
| 915 use(node.length, JSPrecedence.SHIFT_PRECEDENCE); | |
| 916 buffer.add(") "); | |
| 917 generateThrowWithHelper('ioore', node.index); | |
| 918 } | |
| 919 | |
| 920 visitIntegerCheck(HIntegerCheck node) { | |
| 921 buffer.add('if ('); | |
| 922 use(node.value, JSPrecedence.EQUALITY_PRECEDENCE); | |
| 923 buffer.add(' !== ('); | |
| 924 use(node.value, JSPrecedence.BITWISE_OR_PRECEDENCE); | |
| 925 buffer.add(" | 0)) "); | |
| 926 generateThrowWithHelper('iae', node.value); | |
| 927 } | |
| 928 | |
| 929 void generateThrowWithHelper(String helperName, HInstruction argument) { | |
| 930 Element helper = compiler.findHelper(new SourceString(helperName)); | |
| 931 compiler.registerStaticUse(helper); | |
| 932 buffer.add('throw '); | |
| 933 beginExpression(JSPrecedence.EXPRESSION_PRECEDENCE); | |
| 934 beginExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 935 buffer.add(compiler.namer.isolateAccess(helper)); | |
| 936 visitArguments([null, argument]); | |
| 937 endExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 938 endExpression(JSPrecedence.EXPRESSION_PRECEDENCE); | |
| 939 } | |
| 940 | |
| 941 void addIndentation() { | |
| 942 for (int i = 0; i < indent; i++) { | |
| 943 buffer.add(' '); | |
| 944 } | |
| 945 } | |
| 946 | |
| 947 void visitStatic(HStatic node) { | |
| 948 compiler.registerStaticUse(node.element); | |
| 949 buffer.add(compiler.namer.isolateAccess(node.element)); | |
| 950 } | |
| 951 | |
| 952 void visitStaticStore(HStaticStore node) { | |
| 953 compiler.registerStaticUse(node.element); | |
| 954 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 955 buffer.add(compiler.namer.isolateAccess(node.element)); | |
| 956 buffer.add(' = '); | |
| 957 use(node.inputs[0], JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 958 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 959 } | |
| 960 | |
| 961 void visitLiteralList(HLiteralList node) { | |
| 962 if (node.isConst) { | |
| 963 // TODO(floitsch): Remove this when CTC handles arrays. | |
| 964 SourceString name = new SourceString('makeLiteralListConst'); | |
| 965 Element helper = compiler.findHelper(name); | |
| 966 compiler.registerStaticUse(helper); | |
| 967 beginExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 968 buffer.add(compiler.namer.isolateAccess(helper)); | |
| 969 buffer.add('('); | |
| 970 generateArrayLiteral(node); | |
| 971 buffer.add(')'); | |
| 972 endExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 973 } else { | |
| 974 generateArrayLiteral(node); | |
| 975 } | |
| 976 } | |
| 977 | |
| 978 void generateArrayLiteral(HLiteralList node) { | |
| 979 buffer.add('['); | |
| 980 int len = node.inputs.length; | |
| 981 for (int i = 0; i < len; i++) { | |
| 982 if (i != 0) buffer.add(', '); | |
| 983 use(node.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 984 } | |
| 985 buffer.add(']'); | |
| 986 } | |
| 987 | |
| 988 void visitIndex(HIndex node) { | |
| 989 if (node.builtin) { | |
| 990 beginExpression(JSPrecedence.MEMBER_PRECEDENCE); | |
| 991 use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE); | |
| 992 buffer.add('['); | |
| 993 use(node.inputs[2], JSPrecedence.EXPRESSION_PRECEDENCE); | |
| 994 buffer.add(']'); | |
| 995 endExpression(JSPrecedence.MEMBER_PRECEDENCE); | |
| 996 } else { | |
| 997 visitInvokeStatic(node); | |
| 998 } | |
| 999 } | |
| 1000 | |
| 1001 void visitIndexAssign(HIndexAssign node) { | |
| 1002 if (node.builtin) { | |
| 1003 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 1004 use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE); | |
| 1005 buffer.add('['); | |
| 1006 use(node.inputs[2], JSPrecedence.EXPRESSION_PRECEDENCE); | |
| 1007 buffer.add('] = '); | |
| 1008 use(node.inputs[3], JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 1009 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 1010 } else { | |
| 1011 visitInvokeStatic(node); | |
| 1012 } | |
| 1013 } | |
| 1014 | |
| 1015 void visitInvokeInterceptor(HInvokeInterceptor node) { | |
| 1016 if (node.builtinJsName != null) { | |
| 1017 beginExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 1018 use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE); | |
| 1019 buffer.add('.'); | |
| 1020 buffer.add(node.builtinJsName); | |
| 1021 if (node.getter) return; | |
| 1022 buffer.add('('); | |
| 1023 for (int i = 2; i < node.inputs.length; i++) { | |
| 1024 if (i != 2) buffer.add(', '); | |
| 1025 use(node.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 1026 } | |
| 1027 buffer.add(")"); | |
| 1028 endExpression(JSPrecedence.CALL_PRECEDENCE); | |
| 1029 } else { | |
| 1030 return visitInvokeStatic(node); | |
| 1031 } | |
| 1032 } | |
| 1033 | |
| 1034 void checkInt(HInstruction input, String cmp) { | |
| 1035 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1036 use(input, JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1037 buffer.add(' $cmp ('); | |
| 1038 use(input, JSPrecedence.BITWISE_OR_PRECEDENCE); | |
| 1039 buffer.add(' | 0)'); | |
| 1040 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1041 } | |
| 1042 | |
| 1043 void checkNum(HInstruction input, String cmp) { | |
| 1044 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1045 buffer.add('typeof '); | |
| 1046 use(input, JSPrecedence.PREFIX_PRECEDENCE); | |
| 1047 buffer.add(" $cmp 'number'"); | |
| 1048 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1049 } | |
| 1050 | |
| 1051 void checkDouble(HInstruction input, String cmp) { | |
| 1052 checkNum(input, cmp); | |
| 1053 } | |
| 1054 | |
| 1055 void checkString(HInstruction input, String cmp) { | |
| 1056 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1057 buffer.add('typeof '); | |
| 1058 use(input, JSPrecedence.PREFIX_PRECEDENCE); | |
| 1059 buffer.add(" $cmp 'string'"); | |
| 1060 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1061 } | |
| 1062 | |
| 1063 void checkBool(HInstruction input, String cmp) { | |
| 1064 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1065 buffer.add('typeof '); | |
| 1066 use(input, JSPrecedence.PREFIX_PRECEDENCE); | |
| 1067 buffer.add(" $cmp 'boolean'"); | |
| 1068 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1069 } | |
| 1070 | |
| 1071 void checkObject(HInstruction input, String cmp) { | |
| 1072 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1073 buffer.add('typeof '); | |
| 1074 use(input, JSPrecedence.PREFIX_PRECEDENCE); | |
| 1075 buffer.add(" $cmp 'object'"); | |
| 1076 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1077 } | |
| 1078 | |
| 1079 void checkArray(HInstruction input, String cmp) { | |
| 1080 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1081 use(input, JSPrecedence.MEMBER_PRECEDENCE); | |
| 1082 buffer.add('.constructor $cmp Array'); | |
| 1083 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1084 } | |
| 1085 | |
| 1086 void checkNull(HInstruction input) { | |
| 1087 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1088 use(input, JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1089 buffer.add(" === (void 0)"); | |
| 1090 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1091 } | |
| 1092 | |
| 1093 void checkFunction(HInstruction input, Element element) { | |
| 1094 beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); | |
| 1095 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1096 buffer.add('typeof '); | |
| 1097 use(input, JSPrecedence.PREFIX_PRECEDENCE); | |
| 1098 buffer.add(" === 'function'"); | |
| 1099 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); | |
| 1100 buffer.add(" || "); | |
| 1101 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); | |
| 1102 checkObject(input, '==='); | |
| 1103 buffer.add(" && "); | |
| 1104 checkType(input, element); | |
| 1105 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); | |
| 1106 endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); | |
| 1107 } | |
| 1108 | |
| 1109 void checkType(HInstruction input, Element element) { | |
| 1110 bool requiresNativeIsCheck = | |
| 1111 compiler.emitter.nativeEmitter.requiresNativeIsCheck(element); | |
| 1112 if (!requiresNativeIsCheck) buffer.add('!!'); | |
| 1113 use(input, JSPrecedence.MEMBER_PRECEDENCE); | |
| 1114 buffer.add('.'); | |
| 1115 buffer.add(compiler.namer.operatorIs(element)); | |
| 1116 if (requiresNativeIsCheck) buffer.add('()'); | |
| 1117 } | |
| 1118 | |
| 1119 void handleStringSupertypeCheck(HInstruction input, Element element) { | |
| 1120 // Make sure List and String don't share supertypes, otherwise we | |
| 1121 // would need to check for List too. | |
| 1122 assert(element !== compiler.listClass | |
| 1123 && !Elements.isListSupertype(element, compiler)); | |
| 1124 beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); | |
| 1125 checkString(input, '==='); | |
| 1126 buffer.add(' || '); | |
| 1127 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); | |
| 1128 checkObject(input, '==='); | |
| 1129 buffer.add(' && '); | |
| 1130 checkType(input, element); | |
| 1131 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); | |
| 1132 endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); | |
| 1133 } | |
| 1134 | |
| 1135 void handleListOrSupertypeCheck(HInstruction input, Element element) { | |
| 1136 // Make sure List and String don't share supertypes, otherwise we | |
| 1137 // would need to check for String too. | |
| 1138 assert(element !== compiler.stringClass | |
| 1139 && !Elements.isStringSupertype(element, compiler)); | |
| 1140 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); | |
| 1141 checkObject(input, '==='); | |
| 1142 buffer.add(' && ('); | |
| 1143 beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); | |
| 1144 checkArray(input, '==='); | |
| 1145 buffer.add(' || '); | |
| 1146 checkType(input, element); | |
| 1147 buffer.add(')'); | |
| 1148 endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); | |
| 1149 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); | |
| 1150 } | |
| 1151 | |
| 1152 void visitIs(HIs node) { | |
| 1153 Element element = node.typeExpression; | |
| 1154 if (element.kind === ElementKind.TYPE_VARIABLE) { | |
| 1155 compiler.unimplemented("visitIs for type variables"); | |
| 1156 } | |
| 1157 compiler.registerIsCheck(element); | |
| 1158 LibraryElement coreLibrary = compiler.coreLibrary; | |
| 1159 ClassElement objectClass = coreLibrary.find(const SourceString('Object')); | |
| 1160 HInstruction input = node.expression; | |
| 1161 if (node.nullOk) { | |
| 1162 beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); | |
| 1163 checkNull(input); | |
| 1164 buffer.add(' || '); | |
| 1165 } | |
| 1166 | |
| 1167 if (element === objectClass || element === compiler.dynamicClass) { | |
| 1168 // The constant folder also does this optimization, but we make | |
| 1169 // it safe by assuming it may have not run. | |
| 1170 buffer.add('true'); | |
| 1171 } else if (element == compiler.stringClass) { | |
| 1172 checkString(input, '==='); | |
| 1173 } else if (element == compiler.doubleClass) { | |
| 1174 checkDouble(input, '==='); | |
| 1175 } else if (element == compiler.numClass) { | |
| 1176 checkNum(input, '==='); | |
| 1177 } else if (element == compiler.boolClass) { | |
| 1178 checkBool(input, '==='); | |
| 1179 } else if (element == compiler.functionClass) { | |
| 1180 checkFunction(input, element); | |
| 1181 } else if (element == compiler.intClass) { | |
| 1182 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); | |
| 1183 checkNum(input, '==='); | |
| 1184 buffer.add(' && '); | |
| 1185 checkInt(input, '==='); | |
| 1186 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); | |
| 1187 } else if (Elements.isStringSupertype(element, compiler)) { | |
| 1188 handleStringSupertypeCheck(input, element); | |
| 1189 } else if (element === compiler.listClass | |
| 1190 || Elements.isListSupertype(element, compiler)) { | |
| 1191 handleListOrSupertypeCheck(input, element); | |
| 1192 } else { | |
| 1193 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); | |
| 1194 checkObject(input, '==='); | |
| 1195 buffer.add(' && '); | |
| 1196 checkType(input, element); | |
| 1197 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); | |
| 1198 } | |
| 1199 | |
| 1200 if (node.nullOk) { | |
| 1201 endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); | |
| 1202 } | |
| 1203 } | |
| 1204 } | |
| 1205 | |
| 1206 class SsaOptimizedCodeGenerator extends SsaCodeGenerator { | |
| 1207 SsaOptimizedCodeGenerator(compiler, work, parameters, parameterNames) | |
| 1208 : super(compiler, work, parameters, parameterNames); | |
| 1209 | |
| 1210 void beginGraph(HGraph graph) {} | |
| 1211 void endGraph(HGraph graph) {} | |
| 1212 | |
| 1213 void bailout(HTypeGuard guard, String reason) { | |
| 1214 HInstruction input = guard.guarded; | |
| 1215 Namer namer = compiler.namer; | |
| 1216 Element element = work.element; | |
| 1217 buffer.add('return '); | |
| 1218 if (element.isInstanceMember()) { | |
| 1219 // TODO(ngeoffray): This does not work in case we come from a | |
| 1220 // super call. We must make bailout names unique. | |
| 1221 buffer.add('this.${namer.getBailoutName(element)}'); | |
| 1222 } else { | |
| 1223 buffer.add(namer.isolateBailoutAccess(element)); | |
| 1224 } | |
| 1225 int parametersCount = parameterNames.length; | |
| 1226 buffer.add('($parameters'); | |
| 1227 if (parametersCount != 0) buffer.add(', '); | |
| 1228 if (guard.guarded is !HParameterValue) { | |
| 1229 buffer.add('${guard.state}'); | |
| 1230 bool first = true; | |
| 1231 // TODO(ngeoffray): if the bailout method takes more arguments, | |
| 1232 // fill the remaining arguments with undefined. | |
| 1233 // TODO(ngeoffray): try to put a variable at a deterministic | |
| 1234 // location, so that multiple bailout calls put the variable at | |
| 1235 // the same parameter index. | |
| 1236 for (int i = 0; i < guard.inputs.length; i++) { | |
| 1237 buffer.add(', '); | |
| 1238 use(guard.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
| 1239 } | |
| 1240 } else { | |
| 1241 assert(guard.guarded is HParameterValue); | |
| 1242 buffer.add(' 0'); | |
| 1243 } | |
| 1244 buffer.add(')'); | |
| 1245 } | |
| 1246 | |
| 1247 void visitTypeGuard(HTypeGuard node) { | |
| 1248 addIndentation(); | |
| 1249 HInstruction input = node.guarded; | |
| 1250 assert(!isGenerateAtUseSite(input) || input.isCodeMotionInvariant()); | |
| 1251 if (node.isInteger()) { | |
| 1252 buffer.add('if ('); | |
| 1253 checkInt(input, '!=='); | |
| 1254 buffer.add(') '); | |
| 1255 bailout(node, 'Not an integer'); | |
| 1256 } else if (node.isNumber()) { | |
| 1257 buffer.add('if ('); | |
| 1258 checkNum(input, '!=='); | |
| 1259 buffer.add(') '); | |
| 1260 bailout(node, 'Not a number'); | |
| 1261 } else if (node.isBoolean()) { | |
| 1262 buffer.add('if ('); | |
| 1263 checkBool(input, '!=='); | |
| 1264 buffer.add(') '); | |
| 1265 bailout(node, 'Not a boolean'); | |
| 1266 } else if (node.isString()) { | |
| 1267 buffer.add('if ('); | |
| 1268 checkString(input, '!=='); | |
| 1269 buffer.add(') '); | |
| 1270 bailout(node, 'Not a string'); | |
| 1271 } else if (node.isArray()) { | |
| 1272 buffer.add('if ('); | |
| 1273 checkObject(input, '!=='); | |
| 1274 buffer.add('||'); | |
| 1275 checkArray(input, '!=='); | |
| 1276 buffer.add(') '); | |
| 1277 bailout(node, 'Not an array'); | |
| 1278 } else if (node.isStringOrArray()) { | |
| 1279 buffer.add('if ('); | |
| 1280 checkString(input, '!=='); | |
| 1281 buffer.add(' && ('); | |
| 1282 checkObject(input, '!=='); | |
| 1283 buffer.add('||'); | |
| 1284 checkArray(input, '!=='); | |
| 1285 buffer.add(')) '); | |
| 1286 bailout(node, 'Not a string or array'); | |
| 1287 } else { | |
| 1288 unreachable(); | |
| 1289 } | |
| 1290 buffer.add(';\n'); | |
| 1291 } | |
| 1292 | |
| 1293 void beginLoop(HBasicBlock block) { | |
| 1294 addIndentation(); | |
| 1295 for (LabelElement label in block.loopInformation.labels) { | |
| 1296 writeLabel(label); | |
| 1297 buffer.add(":"); | |
| 1298 } | |
| 1299 buffer.add('while (true) {\n'); | |
| 1300 indent++; | |
| 1301 } | |
| 1302 | |
| 1303 void endLoop(HBasicBlock block) { | |
| 1304 indent--; | |
| 1305 addIndentation(); | |
| 1306 buffer.add('}\n'); // Close 'while' loop. | |
| 1307 } | |
| 1308 | |
| 1309 void handleLoopCondition(HLoopBranch node) { | |
| 1310 buffer.add('if (!'); | |
| 1311 use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE); | |
| 1312 buffer.add(') break;\n'); | |
| 1313 } | |
| 1314 | |
| 1315 void startIf(HIf node) { | |
| 1316 } | |
| 1317 | |
| 1318 void endIf(HIf node) { | |
| 1319 indent--; | |
| 1320 addIndentation(); | |
| 1321 buffer.add('}\n'); | |
| 1322 } | |
| 1323 | |
| 1324 void startThen(HIf node) { | |
| 1325 addIndentation(); | |
| 1326 buffer.add('if ('); | |
| 1327 use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE); | |
| 1328 buffer.add(') {\n'); | |
| 1329 indent++; | |
| 1330 } | |
| 1331 | |
| 1332 void endThen(HIf node) { | |
| 1333 } | |
| 1334 | |
| 1335 void startElse(HIf node) { | |
| 1336 indent--; | |
| 1337 addIndentation(); | |
| 1338 buffer.add('} else {\n'); | |
| 1339 indent++; | |
| 1340 } | |
| 1341 | |
| 1342 void endElse(HIf node) { | |
| 1343 } | |
| 1344 } | |
| 1345 | |
| 1346 class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator { | |
| 1347 | |
| 1348 final StringBuffer setup; | |
| 1349 final List<String> labels; | |
| 1350 int labelId = 0; | |
| 1351 int maxBailoutParameters = 0; | |
| 1352 | |
| 1353 SsaUnoptimizedCodeGenerator(compiler, work, parameters, parameterNames) | |
| 1354 : super(compiler, work, parameters, parameterNames), | |
| 1355 setup = new StringBuffer(), | |
| 1356 labels = <String>[]; | |
| 1357 | |
| 1358 String pushLabel() { | |
| 1359 String label = 'L${labelId++}'; | |
| 1360 labels.addLast(label); | |
| 1361 return label; | |
| 1362 } | |
| 1363 | |
| 1364 String popLabel() { | |
| 1365 return labels.removeLast(); | |
| 1366 } | |
| 1367 | |
| 1368 String currentLabel() { | |
| 1369 return labels.last(); | |
| 1370 } | |
| 1371 | |
| 1372 void beginGraph(HGraph graph) { | |
| 1373 if (!graph.entry.hasGuards()) return; | |
| 1374 addIndentation(); | |
| 1375 buffer.add('switch (state) {\n'); | |
| 1376 indent++; | |
| 1377 addIndentation(); | |
| 1378 buffer.add('case 0:\n'); | |
| 1379 indent++; | |
| 1380 | |
| 1381 // The setup phase of a bailout function sets up the environment for | |
| 1382 // each bailout target. Each bailout target will populate this | |
| 1383 // setup phase. It is put at the beginning of the function. | |
| 1384 setup.add(' switch (state) {\n'); | |
| 1385 } | |
| 1386 | |
| 1387 void endGraph(HGraph graph) { | |
| 1388 if (!graph.entry.hasGuards()) return; | |
| 1389 indent--; // Close original case. | |
| 1390 indent--; | |
| 1391 addIndentation(); | |
| 1392 buffer.add('}\n'); // Close 'switch'. | |
| 1393 setup.add(' }\n'); | |
| 1394 } | |
| 1395 | |
| 1396 // For instructions that reference a guard or a check, we change that | |
| 1397 // reference to the instruction they guard against. Therefore, we must | |
| 1398 // use that instruction when restoring the environment. | |
| 1399 HInstruction unwrap(HInstruction argument) { | |
| 1400 if (argument is HIntegerCheck) { | |
| 1401 HIntegerCheck instruction = argument; | |
| 1402 return unwrap(instruction.value); | |
| 1403 } else if (argument is HBoundsCheck) { | |
| 1404 HBoundsCheck instruction = argument; | |
| 1405 return unwrap(instruction.index); | |
| 1406 } else if (argument is HTypeGuard) { | |
| 1407 HTypeGuard instruction = argument; | |
| 1408 return unwrap(instruction.guarded); | |
| 1409 } else { | |
| 1410 return argument; | |
| 1411 } | |
| 1412 } | |
| 1413 | |
| 1414 void visitTypeGuard(HTypeGuard node) { | |
| 1415 indent--; | |
| 1416 addIndentation(); | |
| 1417 buffer.add('case ${node.state}:\n'); | |
| 1418 indent++; | |
| 1419 addIndentation(); | |
| 1420 buffer.add('state = 0;\n'); | |
| 1421 | |
| 1422 setup.add(' case ${node.state}:\n'); | |
| 1423 int i = 0; | |
| 1424 for (HInstruction input in node.inputs) { | |
| 1425 HInstruction instruction = unwrap(input); | |
| 1426 setup.add(' ${temporary(instruction)} = env$i;\n'); | |
| 1427 i++; | |
| 1428 } | |
| 1429 if (i > maxBailoutParameters) maxBailoutParameters = i; | |
| 1430 setup.add(' break;\n'); | |
| 1431 } | |
| 1432 | |
| 1433 void startBailoutCase(List<HTypeGuard> bailouts1, | |
| 1434 List<HTypeGuard> bailouts2) { | |
| 1435 indent--; | |
| 1436 handleBailoutCase(bailouts1); | |
| 1437 handleBailoutCase(bailouts2); | |
| 1438 indent++; | |
| 1439 } | |
| 1440 | |
| 1441 void handleBailoutCase(List<HTypeGuard> guards) { | |
| 1442 for (int i = 0, len = guards.length; i < len; i++) { | |
| 1443 addIndentation(); | |
| 1444 buffer.add('case ${guards[i].state}:\n'); | |
| 1445 } | |
| 1446 } | |
| 1447 | |
| 1448 void startBailoutSwitch() { | |
| 1449 addIndentation(); | |
| 1450 buffer.add('switch (state) {\n'); | |
| 1451 indent++; | |
| 1452 addIndentation(); | |
| 1453 buffer.add('case 0:\n'); | |
| 1454 indent++; | |
| 1455 } | |
| 1456 | |
| 1457 void endBailoutSwitch() { | |
| 1458 indent--; // Close 'case'. | |
| 1459 indent--; | |
| 1460 addIndentation(); | |
| 1461 buffer.add('}\n'); // Close 'switch'. | |
| 1462 } | |
| 1463 | |
| 1464 | |
| 1465 void beginLoop(HBasicBlock block) { | |
| 1466 // TODO(ngeoffray): Don't put labels on loops that don't bailout. | |
| 1467 String newLabel = pushLabel(); | |
| 1468 if (block.hasGuards()) { | |
| 1469 startBailoutCase(block.guards, const <HTypeGuard>[]); | |
| 1470 } | |
| 1471 | |
| 1472 addIndentation(); | |
| 1473 for (SourceString label in block.loopInformation.labels) { | |
| 1474 writeLabel(label); | |
| 1475 buffer.add(":"); | |
| 1476 } | |
| 1477 buffer.add('$newLabel: while (true) {\n'); | |
| 1478 indent++; | |
| 1479 | |
| 1480 if (block.hasGuards()) { | |
| 1481 startBailoutSwitch(); | |
| 1482 } | |
| 1483 } | |
| 1484 | |
| 1485 void endLoop(HBasicBlock block) { | |
| 1486 popLabel(); | |
| 1487 HBasicBlock header = block.isLoopHeader() ? block : block.parentLoopHeader; | |
| 1488 if (header.hasGuards()) { | |
| 1489 endBailoutSwitch(); | |
| 1490 } | |
| 1491 indent--; | |
| 1492 addIndentation(); | |
| 1493 buffer.add('}\n'); // Close 'while'. | |
| 1494 } | |
| 1495 | |
| 1496 void handleLoopCondition(HLoopBranch node) { | |
| 1497 buffer.add('if (!'); | |
| 1498 use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE); | |
| 1499 buffer.add(') break ${currentLabel()};\n'); | |
| 1500 } | |
| 1501 | |
| 1502 void startIf(HIf node) { | |
| 1503 bool hasGuards = node.thenBlock.hasGuards() | |
| 1504 || (node.hasElse && node.elseBlock.hasGuards()); | |
| 1505 if (hasGuards) { | |
| 1506 startBailoutCase(node.thenBlock.guards, | |
| 1507 node.hasElse ? node.elseBlock.guards : const <HTypeGuard>[]); | |
| 1508 } | |
| 1509 } | |
| 1510 | |
| 1511 void endIf(HIf node) { | |
| 1512 indent--; | |
| 1513 addIndentation(); | |
| 1514 buffer.add('}\n'); | |
| 1515 } | |
| 1516 | |
| 1517 void startThen(HIf node) { | |
| 1518 addIndentation(); | |
| 1519 bool hasGuards = node.thenBlock.hasGuards() | |
| 1520 || (node.hasElse && node.elseBlock.hasGuards()); | |
| 1521 buffer.add('if ('); | |
| 1522 int precedence = JSPrecedence.EXPRESSION_PRECEDENCE; | |
| 1523 if (hasGuards) { | |
| 1524 // TODO(ngeoffray): Put the condition initialization in the | |
| 1525 // [setup] buffer. | |
| 1526 List<HTypeGuard> guards = node.thenBlock.guards; | |
| 1527 for (int i = 0, len = guards.length; i < len; i++) { | |
| 1528 buffer.add('state == ${guards[i].state} || '); | |
| 1529 } | |
| 1530 buffer.add('(state == 0 && '); | |
| 1531 precedence = JSPrecedence.BITWISE_OR_PRECEDENCE; | |
| 1532 } | |
| 1533 use(node.inputs[0], precedence); | |
| 1534 if (hasGuards) { | |
| 1535 buffer.add(')'); | |
| 1536 } | |
| 1537 buffer.add(') {\n'); | |
| 1538 indent++; | |
| 1539 if (node.thenBlock.hasGuards()) { | |
| 1540 startBailoutSwitch(); | |
| 1541 } | |
| 1542 } | |
| 1543 | |
| 1544 void endThen(HIf node) { | |
| 1545 if (node.thenBlock.hasGuards()) { | |
| 1546 endBailoutSwitch(); | |
| 1547 } | |
| 1548 } | |
| 1549 | |
| 1550 void startElse(HIf node) { | |
| 1551 indent--; | |
| 1552 addIndentation(); | |
| 1553 buffer.add('} else {\n'); | |
| 1554 indent++; | |
| 1555 if (node.elseBlock.hasGuards()) { | |
| 1556 startBailoutSwitch(); | |
| 1557 } | |
| 1558 } | |
| 1559 | |
| 1560 void endElse(HIf node) { | |
| 1561 if (node.elseBlock.hasGuards()) { | |
| 1562 endBailoutSwitch(); | |
| 1563 } | |
| 1564 } | |
| 1565 } | |
| OLD | NEW |