OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 | |
6 /** | |
7 * If true, print a warning for each method that was resolved, but not | |
8 * compiled. | |
9 */ | |
10 final bool REPORT_EXCESS_RESOLUTION = false; | |
11 | |
5 class WorkItem { | 12 class WorkItem { |
6 final Element element; | 13 final Element element; |
7 TreeElements resolutionTree; | 14 TreeElements resolutionTree; |
8 bool allowSpeculativeOptimization = true; | 15 bool allowSpeculativeOptimization = true; |
9 List<HTypeGuard> guards = const <HTypeGuard>[]; | 16 List<HTypeGuard> guards = const <HTypeGuard>[]; |
10 | 17 |
11 WorkItem(this.element, this.resolutionTree); | 18 WorkItem(this.element, this.resolutionTree); |
12 | 19 |
13 bool isAnalyzed() => resolutionTree !== null; | 20 bool isAnalyzed() => resolutionTree !== null; |
14 | 21 |
15 String run(Compiler compiler, Enqueuer world) { | 22 String run(Compiler compiler, Enqueuer world) { |
16 String code = world.universe.generatedCode[element]; | 23 String code = world.universe.generatedCode[element]; |
17 if (code !== null) return code; | 24 if (code !== null) return code; |
18 if (!isAnalyzed()) compiler.analyze(this); | 25 resolutionTree = compiler.analyze(this, world); |
19 return compiler.codegen(this); | 26 return compiler.codegen(this, world); |
20 } | 27 } |
21 } | 28 } |
22 | 29 |
23 class Backend { | 30 class Backend { |
24 final Compiler compiler; | 31 final Compiler compiler; |
25 | 32 |
26 Backend(this.compiler); | 33 Backend(this.compiler); |
27 | 34 |
35 void enqueueAllTopLevelFunctions(LibraryElement lib, Enqueuer world) { | |
36 lib.forEachExport((Element e) { | |
37 if (e.isFunction()) world.addToWorkList(e); | |
38 }); | |
39 } | |
40 | |
41 abstract void enqueueHelpers(Enqueuer world); | |
28 abstract String codegen(WorkItem work); | 42 abstract String codegen(WorkItem work); |
29 abstract void processNativeClasses(world, libraries); | 43 abstract void processNativeClasses(world, libraries); |
30 abstract void assembleProgram(); | 44 abstract void assembleProgram(); |
31 abstract List<CompilerTask> get tasks(); | 45 abstract List<CompilerTask> get tasks(); |
32 } | 46 } |
33 | 47 |
34 class JavaScriptBackend extends Backend { | 48 class JavaScriptBackend extends Backend { |
35 SsaBuilderTask builder; | 49 SsaBuilderTask builder; |
36 SsaOptimizerTask optimizer; | 50 SsaOptimizerTask optimizer; |
37 SsaCodeGeneratorTask generator; | 51 SsaCodeGeneratorTask generator; |
38 CodeEmitterTask emitter; | 52 CodeEmitterTask emitter; |
39 | 53 |
40 List<CompilerTask> get tasks() { | 54 List<CompilerTask> get tasks() { |
41 return <CompilerTask>[builder, optimizer, generator, emitter]; | 55 return <CompilerTask>[builder, optimizer, generator, emitter]; |
42 } | 56 } |
43 | 57 |
44 JavaScriptBackend(Compiler compiler) | 58 JavaScriptBackend(Compiler compiler) |
45 : emitter = new CodeEmitterTask(compiler), | 59 : emitter = new CodeEmitterTask(compiler), |
46 super(compiler) { | 60 super(compiler) { |
47 builder = new SsaBuilderTask(this); | 61 builder = new SsaBuilderTask(this); |
48 optimizer = new SsaOptimizerTask(this); | 62 optimizer = new SsaOptimizerTask(this); |
49 generator = new SsaCodeGeneratorTask(this); | 63 generator = new SsaCodeGeneratorTask(this); |
50 } | 64 } |
51 | 65 |
66 void enqueueHelpers(Enqueuer world) { | |
67 enqueueAllTopLevelFunctions(compiler.jsHelperLibrary, world); | |
68 enqueueAllTopLevelFunctions(compiler.interceptorsLibrary, world); | |
69 for (var helper in [const SourceString('Closure'), | |
70 const SourceString('ConstantMap'), | |
71 const SourceString('ConstantProtoMap')]) { | |
72 var e = compiler.findHelper(helper); | |
73 if (e !== null) world.registerInstantiatedClass(e); | |
74 } | |
75 } | |
76 | |
52 String codegen(WorkItem work) { | 77 String codegen(WorkItem work) { |
53 HGraph graph = builder.build(work); | 78 HGraph graph = builder.build(work); |
54 optimizer.optimize(work, graph); | 79 optimizer.optimize(work, graph); |
55 if (work.allowSpeculativeOptimization | 80 if (work.allowSpeculativeOptimization |
56 && optimizer.trySpeculativeOptimizations(work, graph)) { | 81 && optimizer.trySpeculativeOptimizations(work, graph)) { |
57 String code = generator.generateBailoutMethod(work, graph); | 82 String code = generator.generateBailoutMethod(work, graph); |
58 compiler.codegenWorld.addBailoutCode(work, code); | 83 compiler.codegenWorld.addBailoutCode(work, code); |
59 optimizer.prepareForSpeculativeOptimizations(work, graph); | 84 optimizer.prepareForSpeculativeOptimizations(work, graph); |
60 optimizer.optimize(work, graph); | 85 optimizer.optimize(work, graph); |
61 } | 86 } |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
198 reportDiagnostic(spanFromElement(element), | 223 reportDiagnostic(spanFromElement(element), |
199 MessageKind.COMPILER_CRASHED.error().toString(), | 224 MessageKind.COMPILER_CRASHED.error().toString(), |
200 false); | 225 false); |
201 // TODO(ahe): Obtain the build ID. | 226 // TODO(ahe): Obtain the build ID. |
202 var buildId = 'build number could not be determined'; | 227 var buildId = 'build number could not be determined'; |
203 print(MessageKind.PLEASE_REPORT_THE_CRASH.message([buildId])); | 228 print(MessageKind.PLEASE_REPORT_THE_CRASH.message([buildId])); |
204 } | 229 } |
205 | 230 |
206 void cancel([String reason, Node node, Token token, | 231 void cancel([String reason, Node node, Token token, |
207 HInstruction instruction, Element element]) { | 232 HInstruction instruction, Element element]) { |
233 assembledCode = null; // Compilation failed. Make sure that we | |
234 // don't return a bogus result. | |
208 SourceSpan span = const SourceSpan(null, null, null); | 235 SourceSpan span = const SourceSpan(null, null, null); |
209 if (node !== null) { | 236 if (node !== null) { |
210 span = spanFromNode(node); | 237 span = spanFromNode(node); |
211 } else if (token !== null) { | 238 } else if (token !== null) { |
212 span = spanFromTokens(token, token); | 239 span = spanFromTokens(token, token); |
213 } else if (instruction !== null) { | 240 } else if (instruction !== null) { |
214 span = spanFromElement(currentElement); | 241 span = spanFromElement(currentElement); |
215 } else if (element !== null) { | 242 } else if (element !== null) { |
216 span = spanFromElement(element); | 243 span = spanFromElement(element); |
217 } else { | 244 } else { |
(...skipping 23 matching lines...) Expand all Loading... | |
241 return false; | 268 return false; |
242 } | 269 } |
243 tracer.close(); | 270 tracer.close(); |
244 log('compilation succeeded'); | 271 log('compilation succeeded'); |
245 return true; | 272 return true; |
246 } | 273 } |
247 | 274 |
248 void enableNoSuchMethod(Element element) { | 275 void enableNoSuchMethod(Element element) { |
249 // TODO(ahe): Move this method to Enqueuer. | 276 // TODO(ahe): Move this method to Enqueuer. |
250 if (enabledNoSuchMethod) return; | 277 if (enabledNoSuchMethod) return; |
251 if (element.enclosingElement == objectClass) return; | 278 if (element.enclosingElement === objectClass) { |
279 enqueuer.resolution.registerDynamicInvocationOf(element); | |
280 return; | |
281 } | |
252 enabledNoSuchMethod = true; | 282 enabledNoSuchMethod = true; |
283 enqueuer.resolution.registerInvocation(NO_SUCH_METHOD, | |
284 Selector.INVOCATION_2); | |
253 enqueuer.codegen.registerInvocation(NO_SUCH_METHOD, | 285 enqueuer.codegen.registerInvocation(NO_SUCH_METHOD, |
254 new Selector.invocation(2)); | 286 Selector.INVOCATION_2); |
255 } | 287 } |
256 | 288 |
257 void enableIsolateSupport(LibraryElement element) { | 289 void enableIsolateSupport(LibraryElement element) { |
258 // TODO(ahe): Move this method to Enqueuer. | 290 // TODO(ahe): Move this method to Enqueuer. |
259 isolateLibrary = element; | 291 isolateLibrary = element; |
292 enqueuer.resolution.addToWorkList(element.find(START_ROOT_ISOLATE)); | |
293 enqueuer.resolution.addToWorkList( | |
294 element.find(const SourceString('_currentIsolate'))); | |
295 enqueuer.resolution.addToWorkList( | |
296 element.find(const SourceString('_callInIsolate'))); | |
260 enqueuer.codegen.addToWorkList(element.find(START_ROOT_ISOLATE)); | 297 enqueuer.codegen.addToWorkList(element.find(START_ROOT_ISOLATE)); |
261 } | 298 } |
262 | 299 |
263 bool hasIsolateSupport() => isolateLibrary !== null; | 300 bool hasIsolateSupport() => isolateLibrary !== null; |
264 | 301 |
265 void onLibraryLoaded(LibraryElement library, Uri uri) { | 302 void onLibraryLoaded(LibraryElement library, Uri uri) { |
266 if (uri.toString() == 'dart:isolate') { | 303 if (uri.toString() == 'dart:isolate') { |
267 enableIsolateSupport(library); | 304 enableIsolateSupport(library); |
268 } | 305 } |
269 if (dynamicClass !== null) { | 306 if (dynamicClass !== null) { |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
359 FunctionSignature parameters = mainMethod.computeSignature(this); | 396 FunctionSignature parameters = mainMethod.computeSignature(this); |
360 parameters.forEachParameter((Element parameter) { | 397 parameters.forEachParameter((Element parameter) { |
361 reportFatalError('main cannot have parameters', parameter); | 398 reportFatalError('main cannot have parameters', parameter); |
362 }); | 399 }); |
363 } | 400 } |
364 | 401 |
365 // TODO(ahe): Remove this line. Eventually, enqueuer.resolution | 402 // TODO(ahe): Remove this line. Eventually, enqueuer.resolution |
366 // should know this. | 403 // should know this. |
367 world.populate(this, libraries.getValues()); | 404 world.populate(this, libraries.getValues()); |
368 | 405 |
369 // Not yet ready to process the enqueuer.resolution queue... | 406 log('Resolving...'); |
370 // processQueue(enqueuer.resolution); | 407 backend.enqueueHelpers(enqueuer.resolution); |
408 processQueue(enqueuer.resolution, main); | |
409 log('Resolved ${enqueuer.resolution.resolvedElements.length} elements.'); | |
410 | |
411 log('Compiling...'); | |
371 processQueue(enqueuer.codegen, main); | 412 processQueue(enqueuer.codegen, main); |
413 log('Compiled ${codegenWorld.generatedCode.length} methods.'); | |
372 | 414 |
373 backend.assembleProgram(); | 415 backend.assembleProgram(); |
374 if (!enqueuer.codegen.queue.isEmpty()) { | 416 |
375 internalErrorOnElement(enqueuer.codegen.queue.first().element, | 417 checkQueues(); |
376 "work list is not empty"); | |
377 } | |
378 } | 418 } |
379 | 419 |
380 processQueue(Enqueuer world, Element main) { | 420 processQueue(Enqueuer world, Element main) { |
381 backend.processNativeClasses(world, libraries.getValues()); | 421 backend.processNativeClasses(world, libraries.getValues()); |
382 world.addToWorkList(main); | 422 world.addToWorkList(main); |
383 codegenProgress.reset(); | 423 codegenProgress.reset(); |
384 while (!world.queue.isEmpty()) { | 424 world.forEach((WorkItem work) { |
385 WorkItem work = world.queue.removeLast(); | |
386 withCurrentElement(work.element, () => work.run(this, world)); | 425 withCurrentElement(work.element, () => work.run(this, world)); |
387 } | 426 }); |
388 world.queueIsClosed = true; | 427 world.queueIsClosed = true; |
389 assert(world.checkNoEnqueuedInvokedInstanceMethods()); | 428 assert(world.checkNoEnqueuedInvokedInstanceMethods()); |
390 world.registerFieldClosureInvocations(); | 429 world.registerFieldClosureInvocations(); |
391 } | 430 } |
392 | 431 |
432 /** | |
433 * Perform various checks of the queues. This includes checking that | |
434 * the queues are empty (nothing was added after we stopped | |
435 * processing the quese). Also compute the number of methods that | |
ngeoffray
2012/06/12 12:44:14
quese -> queues
ahe
2012/06/12 18:47:13
Done.
| |
436 * were resolved, but not compiled (aka excess resolution). | |
437 */ | |
438 checkQueues() { | |
439 for (var world in [enqueuer.resolution, enqueuer.codegen]) { | |
440 world.forEach((WorkItem work) { | |
441 internalErrorOnElement(work.element, "Work list is not empty."); | |
442 }); | |
443 } | |
444 var resolved = new Set.from(enqueuer.resolution.resolvedElements.getKeys()); | |
445 for (Element e in codegenWorld.generatedCode.getKeys()) { | |
446 resolved.remove(e); | |
447 } | |
448 for (Element e in new Set.from(resolved)) { | |
449 if (e.isClass() || | |
450 e.isField() || | |
451 e.isTypeVariable() || | |
452 e.isTypedef() || | |
453 e.kind === ElementKind.ABSTRACT_FIELD) { | |
454 resolved.remove(e); | |
455 } | |
456 if (e.kind === ElementKind.GENERATIVE_CONSTRUCTOR) { | |
457 if (e.enclosingElement.isInterface()) { | |
458 resolved.remove(e); | |
459 } | |
460 resolved.remove(e); | |
461 | |
462 } | |
463 if (e.getLibrary() === jsHelperLibrary) { | |
464 resolved.remove(e); | |
465 } | |
466 if (e.getLibrary() === interceptorsLibrary) { | |
467 resolved.remove(e); | |
468 } | |
469 } | |
470 log('Excess resolution work: ${resolved.length}.'); | |
471 if (!REPORT_EXCESS_RESOLUTION) return; | |
472 for (Element e in resolved) { | |
473 SourceSpan span = spanFromElement(e); | |
474 reportDiagnostic(span, 'Warning: $e resolved but not compiled.', false); | |
475 } | |
476 } | |
477 | |
393 TreeElements analyzeElement(Element element) { | 478 TreeElements analyzeElement(Element element) { |
479 TreeElements elements = enqueuer.resolution.getCachedElements(element); | |
480 if (elements !== null) return elements; | |
481 if (element is AbstractFieldElement) { | |
ngeoffray
2012/06/12 12:44:14
Maybe add this check together with the allowed che
ahe
2012/06/12 18:47:13
Done.
| |
482 return null; | |
483 } | |
484 final int allowed = ElementCategory.VARIABLE | ElementCategory.FUNCTION | |
485 | ElementCategory.FACTORY; | |
486 if (!element.isAccessor() && (element.kind.category & allowed) == 0) { | |
487 return null; | |
488 } | |
394 assert(parser !== null); | 489 assert(parser !== null); |
395 Node tree = parser.parse(element); | 490 Node tree = parser.parse(element); |
396 validator.validate(tree); | 491 validator.validate(tree); |
397 unparseValidator.check(element); | 492 unparseValidator.check(element); |
398 TreeElements elements = resolver.resolve(element); | 493 elements = resolver.resolve(element); |
399 checker.check(tree, elements); | 494 checker.check(tree, elements); |
400 return elements; | 495 return elements; |
401 } | 496 } |
402 | 497 |
403 TreeElements analyze(WorkItem work) { | 498 TreeElements analyze(WorkItem work, Enqueuer world) { |
404 work.resolutionTree = analyzeElement(work.element); | 499 if (work.isAnalyzed()) return work.resolutionTree; |
405 return work.resolutionTree; | 500 Element element = work.element; |
501 TreeElements result = world.getCachedElements(element); | |
502 if (result !== null) return result; | |
503 if (world !== enqueuer.resolution) { | |
504 internalErrorOnElement(element, | |
505 'Internal error: unresolved element: $element.'); | |
506 } | |
507 result = analyzeElement(element); | |
508 enqueuer.resolution.resolvedElements[element] = result; | |
509 return result; | |
406 } | 510 } |
407 | 511 |
408 String codegen(WorkItem work) { | 512 String codegen(WorkItem work, Enqueuer world) { |
513 if (world !== enqueuer.codegen) return null; | |
ngeoffray
2012/06/12 12:44:14
Can we actually prevent this situation? Or is that
ahe
2012/06/12 18:47:13
I'm not sure I understand your question.
ngeoffray
2012/06/13 07:31:57
It does not feel right that the resolver queue end
ahe
2012/06/13 08:20:41
I have some ideas for cleaning this up. If enqueue
| |
409 if (codegenProgress.elapsedInMs() > 500) { | 514 if (codegenProgress.elapsedInMs() > 500) { |
410 // TODO(ahe): Add structured diagnostics to the compiler API and | 515 // TODO(ahe): Add structured diagnostics to the compiler API and |
411 // use it to separate this from the --verbose option. | 516 // use it to separate this from the --verbose option. |
412 log('compiled ${codegenWorld.generatedCode.length} methods'); | 517 log('Compiled ${codegenWorld.generatedCode.length} methods.'); |
413 codegenProgress.reset(); | 518 codegenProgress.reset(); |
414 } | 519 } |
415 if (work.element.kind.category == ElementCategory.VARIABLE) { | 520 if (work.element.kind.category == ElementCategory.VARIABLE) { |
416 constantHandler.compileWorkItem(work); | 521 constantHandler.compileWorkItem(work); |
417 return null; | 522 return null; |
418 } else { | 523 } else { |
419 String code = backend.codegen(work); | 524 String code = backend.codegen(work); |
420 codegenWorld.addGeneratedCode(work, code); | 525 codegenWorld.addGeneratedCode(work, code); |
421 return code; | 526 return code; |
422 } | 527 } |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
472 } | 577 } |
473 | 578 |
474 reportError(Node node, var message) { | 579 reportError(Node node, var message) { |
475 SourceSpan span = spanFromNode(node); | 580 SourceSpan span = spanFromNode(node); |
476 reportDiagnostic(span, "${red('error:')} $message", true); | 581 reportDiagnostic(span, "${red('error:')} $message", true); |
477 throw new CompilerCancelledException(message.toString()); | 582 throw new CompilerCancelledException(message.toString()); |
478 } | 583 } |
479 | 584 |
480 abstract void reportDiagnostic(SourceSpan span, String message, bool fatal); | 585 abstract void reportDiagnostic(SourceSpan span, String message, bool fatal); |
481 | 586 |
482 SourceSpan spanFromTokens(Token begin, Token end) { | 587 SourceSpan spanFromTokens(Token begin, Token end, [Uri uri]) { |
483 if (begin === null || end === null) { | 588 if (begin === null || end === null) { |
484 // TODO(ahe): We can almost always do better. Often it is only | 589 // TODO(ahe): We can almost always do better. Often it is only |
485 // end that is null. Otherwise, we probably know the current | 590 // end that is null. Otherwise, we probably know the current |
486 // URI. | 591 // URI. |
487 throw 'cannot find tokens to produce error message'; | 592 throw 'Cannot find tokens to produce error message.'; |
488 } | 593 } |
489 Uri uri = currentElement.getCompilationUnit().script.uri; | 594 if (uri === null) { |
595 uri = currentElement.getCompilationUnit().script.uri; | |
596 } | |
490 return SourceSpan.withOffsets(begin, end, (beginOffset, endOffset) => | 597 return SourceSpan.withOffsets(begin, end, (beginOffset, endOffset) => |
491 new SourceSpan(uri, beginOffset, endOffset)); | 598 new SourceSpan(uri, beginOffset, endOffset)); |
492 } | 599 } |
493 | 600 |
494 SourceSpan spanFromNode(Node node) { | 601 SourceSpan spanFromNode(Node node) { |
495 return spanFromTokens(node.getBeginToken(), node.getEndToken()); | 602 return spanFromTokens(node.getBeginToken(), node.getEndToken()); |
496 } | 603 } |
497 | 604 |
498 SourceSpan spanFromElement(Element element) { | 605 SourceSpan spanFromElement(Element element) { |
499 if (element.position() === null) { | 606 if (element.position() === null) { |
500 // Sometimes, the backend fakes up elements that have no | 607 // Sometimes, the backend fakes up elements that have no |
501 // position. So we use the enclosing element instead. It is | 608 // position. So we use the enclosing element instead. It is |
502 // not a good error location, but cancel really is "internal | 609 // not a good error location, but cancel really is "internal |
503 // error" or "not implemented yet", so the vicinity is good | 610 // error" or "not implemented yet", so the vicinity is good |
504 // enough for now. | 611 // enough for now. |
505 element = element.enclosingElement; | 612 element = element.enclosingElement; |
506 // TODO(ahe): I plan to overhaul this infrastructure anyways. | 613 // TODO(ahe): I plan to overhaul this infrastructure anyways. |
507 } | 614 } |
508 if (element === null) { | 615 if (element === null) { |
509 element = currentElement; | 616 element = currentElement; |
510 } | 617 } |
511 Token position = element.position(); | 618 Token position = element.position(); |
512 if (position === null) { | 619 Uri uri = element.getCompilationUnit().script.uri; |
513 Uri uri = element.getCompilationUnit().script.uri; | 620 return (position === null) |
514 return new SourceSpan(uri, 0, 0); | 621 ? new SourceSpan(uri, 0, 0) |
515 } | 622 : spanFromTokens(position, position, uri); |
516 return spanFromTokens(position, position); | |
517 } | 623 } |
518 | 624 |
519 Script readScript(Uri uri, [ScriptTag node]) { | 625 Script readScript(Uri uri, [ScriptTag node]) { |
520 unimplemented('Compiler.readScript'); | 626 unimplemented('Compiler.readScript'); |
521 } | 627 } |
522 | 628 |
523 String get legDirectory() { | 629 String get legDirectory() { |
524 unimplemented('Compiler.legDirectory'); | 630 unimplemented('Compiler.legDirectory'); |
525 } | 631 } |
526 | 632 |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
595 // invariant that endOffset > beginOffset, but for EOF the | 701 // invariant that endOffset > beginOffset, but for EOF the |
596 // charoffset of the next token may be [beginOffset]. This can | 702 // charoffset of the next token may be [beginOffset]. This can |
597 // also happen for synthetized tokens that are produced during | 703 // also happen for synthetized tokens that are produced during |
598 // error handling. | 704 // error handling. |
599 final endOffset = | 705 final endOffset = |
600 Math.max((end.next !== null) ? end.next.charOffset : 0, beginOffset + 1); | 706 Math.max((end.next !== null) ? end.next.charOffset : 0, beginOffset + 1); |
601 assert(endOffset > beginOffset); | 707 assert(endOffset > beginOffset); |
602 return f(beginOffset, endOffset); | 708 return f(beginOffset, endOffset); |
603 } | 709 } |
604 } | 710 } |
OLD | NEW |