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 |