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 final bool REPORT_EXCESS_RESOLUTION = false; | |
ahe
2012/06/12 06:22:49
Document this.
ahe
2012/06/12 10:56:11
Done.
| |
6 | |
5 class WorkItem { | 7 class WorkItem { |
6 final Element element; | 8 final Element element; |
7 TreeElements resolutionTree; | 9 TreeElements resolutionTree; |
8 bool allowSpeculativeOptimization = true; | 10 bool allowSpeculativeOptimization = true; |
9 List<HTypeGuard> guards = const <HTypeGuard>[]; | 11 List<HTypeGuard> guards = const <HTypeGuard>[]; |
10 | 12 |
11 WorkItem(this.element, this.resolutionTree); | 13 WorkItem(this.element, this.resolutionTree); |
12 | 14 |
13 bool isAnalyzed() => resolutionTree !== null; | 15 bool isAnalyzed() => resolutionTree !== null; |
14 | 16 |
15 String run(Compiler compiler, Enqueuer world) { | 17 String run(Compiler compiler, Enqueuer world) { |
16 String code = world.universe.generatedCode[element]; | 18 String code = world.universe.generatedCode[element]; |
17 if (code !== null) return code; | 19 if (code !== null) return code; |
18 if (!isAnalyzed()) compiler.analyze(this); | 20 resolutionTree = compiler.analyze(this, world); |
19 return compiler.codegen(this); | 21 return compiler.codegen(this, world); |
20 } | 22 } |
21 } | 23 } |
22 | 24 |
23 class Backend { | 25 class Backend { |
24 final Compiler compiler; | 26 final Compiler compiler; |
25 | 27 |
26 Backend(this.compiler); | 28 Backend(this.compiler); |
27 | 29 |
30 abstract void enqueueHelpers(Enqueuer world); | |
ahe
2012/06/12 06:22:49
Implement in dart backend.
ahe
2012/06/12 10:56:11
Done.
| |
28 abstract String codegen(WorkItem work); | 31 abstract String codegen(WorkItem work); |
29 abstract void processNativeClasses(world, libraries); | 32 abstract void processNativeClasses(world, libraries); |
30 abstract void assembleProgram(); | 33 abstract void assembleProgram(); |
31 abstract List<CompilerTask> get tasks(); | 34 abstract List<CompilerTask> get tasks(); |
32 } | 35 } |
33 | 36 |
34 class JavaScriptBackend extends Backend { | 37 class JavaScriptBackend extends Backend { |
35 SsaBuilderTask builder; | 38 SsaBuilderTask builder; |
36 SsaOptimizerTask optimizer; | 39 SsaOptimizerTask optimizer; |
37 SsaCodeGeneratorTask generator; | 40 SsaCodeGeneratorTask generator; |
38 CodeEmitterTask emitter; | 41 CodeEmitterTask emitter; |
39 | 42 |
40 List<CompilerTask> get tasks() { | 43 List<CompilerTask> get tasks() { |
41 return <CompilerTask>[builder, optimizer, generator, emitter]; | 44 return <CompilerTask>[builder, optimizer, generator, emitter]; |
42 } | 45 } |
43 | 46 |
44 JavaScriptBackend(Compiler compiler) | 47 JavaScriptBackend(Compiler compiler) |
45 : emitter = new CodeEmitterTask(compiler), | 48 : emitter = new CodeEmitterTask(compiler), |
46 super(compiler) { | 49 super(compiler) { |
47 builder = new SsaBuilderTask(this); | 50 builder = new SsaBuilderTask(this); |
48 optimizer = new SsaOptimizerTask(this); | 51 optimizer = new SsaOptimizerTask(this); |
49 generator = new SsaCodeGeneratorTask(this); | 52 generator = new SsaCodeGeneratorTask(this); |
50 } | 53 } |
51 | 54 |
55 void enqueueHelpers(Enqueuer world) { | |
56 for (var lib in [compiler.jsHelperLibrary, compiler.interceptorsLibrary]) { | |
kasperl
2012/06/12 05:50:12
Add a comment that explains that this enqueues all
ahe
2012/06/12 10:56:11
Done.
| |
57 for (Element e in lib.elements.getValues()) { | |
58 if (e.isFunction()) { | |
59 world.addToWorkList(e); | |
60 } | |
61 } | |
62 } | |
63 for (var helper in [const SourceString('Closure'), | |
64 const SourceString('ConstantMap'), | |
65 const SourceString('ConstantProtoMap')]) { | |
66 world.registerInstantiatedClass(compiler.findHelper(helper)); | |
67 } | |
68 } | |
69 | |
52 String codegen(WorkItem work) { | 70 String codegen(WorkItem work) { |
53 HGraph graph = builder.build(work); | 71 HGraph graph = builder.build(work); |
54 optimizer.optimize(work, graph); | 72 optimizer.optimize(work, graph); |
55 if (work.allowSpeculativeOptimization | 73 if (work.allowSpeculativeOptimization |
56 && optimizer.trySpeculativeOptimizations(work, graph)) { | 74 && optimizer.trySpeculativeOptimizations(work, graph)) { |
57 String code = generator.generateBailoutMethod(work, graph); | 75 String code = generator.generateBailoutMethod(work, graph); |
58 compiler.codegenWorld.addBailoutCode(work, code); | 76 compiler.codegenWorld.addBailoutCode(work, code); |
59 optimizer.prepareForSpeculativeOptimizations(work, graph); | 77 optimizer.prepareForSpeculativeOptimizations(work, graph); |
60 optimizer.optimize(work, graph); | 78 optimizer.optimize(work, graph); |
61 } | 79 } |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
241 return false; | 259 return false; |
242 } | 260 } |
243 tracer.close(); | 261 tracer.close(); |
244 log('compilation succeeded'); | 262 log('compilation succeeded'); |
245 return true; | 263 return true; |
246 } | 264 } |
247 | 265 |
248 void enableNoSuchMethod(Element element) { | 266 void enableNoSuchMethod(Element element) { |
249 // TODO(ahe): Move this method to Enqueuer. | 267 // TODO(ahe): Move this method to Enqueuer. |
250 if (enabledNoSuchMethod) return; | 268 if (enabledNoSuchMethod) return; |
251 if (element.enclosingElement == objectClass) return; | 269 if (element.enclosingElement === objectClass) { |
270 enqueuer.resolution.registerDynamicInvocationOf(element); | |
271 return; | |
272 } | |
252 enabledNoSuchMethod = true; | 273 enabledNoSuchMethod = true; |
274 enqueuer.resolution.registerInvocation(NO_SUCH_METHOD, | |
275 Selector.INVOCATION_2); | |
253 enqueuer.codegen.registerInvocation(NO_SUCH_METHOD, | 276 enqueuer.codegen.registerInvocation(NO_SUCH_METHOD, |
254 new Selector.invocation(2)); | 277 Selector.INVOCATION_2); |
255 } | 278 } |
256 | 279 |
257 void enableIsolateSupport(LibraryElement element) { | 280 void enableIsolateSupport(LibraryElement element) { |
258 // TODO(ahe): Move this method to Enqueuer. | 281 // TODO(ahe): Move this method to Enqueuer. |
259 isolateLibrary = element; | 282 isolateLibrary = element; |
283 enqueuer.resolution.addToWorkList(element.find(START_ROOT_ISOLATE)); | |
284 enqueuer.resolution.addToWorkList( | |
285 element.find(const SourceString('_currentIsolate'))); | |
286 enqueuer.resolution.addToWorkList( | |
287 element.find(const SourceString('_callInIsolate'))); | |
260 enqueuer.codegen.addToWorkList(element.find(START_ROOT_ISOLATE)); | 288 enqueuer.codegen.addToWorkList(element.find(START_ROOT_ISOLATE)); |
261 } | 289 } |
262 | 290 |
263 bool hasIsolateSupport() => isolateLibrary !== null; | 291 bool hasIsolateSupport() => isolateLibrary !== null; |
264 | 292 |
265 void onLibraryLoaded(LibraryElement library, Uri uri) { | 293 void onLibraryLoaded(LibraryElement library, Uri uri) { |
266 if (uri.toString() == 'dart:isolate') { | 294 if (uri.toString() == 'dart:isolate') { |
267 enableIsolateSupport(library); | 295 enableIsolateSupport(library); |
268 } | 296 } |
269 if (dynamicClass !== null) { | 297 if (dynamicClass !== null) { |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
359 FunctionSignature parameters = mainMethod.computeSignature(this); | 387 FunctionSignature parameters = mainMethod.computeSignature(this); |
360 parameters.forEachParameter((Element parameter) { | 388 parameters.forEachParameter((Element parameter) { |
361 reportFatalError('main cannot have parameters', parameter); | 389 reportFatalError('main cannot have parameters', parameter); |
362 }); | 390 }); |
363 } | 391 } |
364 | 392 |
365 // TODO(ahe): Remove this line. Eventually, enqueuer.resolution | 393 // TODO(ahe): Remove this line. Eventually, enqueuer.resolution |
366 // should know this. | 394 // should know this. |
367 world.populate(this, libraries.getValues()); | 395 world.populate(this, libraries.getValues()); |
368 | 396 |
369 // Not yet ready to process the enqueuer.resolution queue... | 397 log('Resolving...'); |
370 // processQueue(enqueuer.resolution); | 398 backend.enqueueHelpers(enqueuer.resolution); |
399 processQueue(enqueuer.resolution, main); | |
400 log('Resolved ${enqueuer.resolution.resolvedElements.length} elements.'); | |
401 | |
402 log('Compiling...'); | |
371 processQueue(enqueuer.codegen, main); | 403 processQueue(enqueuer.codegen, main); |
404 log('Compiled ${codegenWorld.generatedCode.length} methods.'); | |
372 | 405 |
373 backend.assembleProgram(); | 406 backend.assembleProgram(); |
374 if (!enqueuer.codegen.queue.isEmpty()) { | 407 |
375 internalErrorOnElement(enqueuer.codegen.queue.first().element, | 408 checkQueues(); |
376 "work list is not empty"); | |
377 } | |
378 } | 409 } |
379 | 410 |
380 processQueue(Enqueuer world, Element main) { | 411 processQueue(Enqueuer world, Element main) { |
381 backend.processNativeClasses(world, libraries.getValues()); | 412 backend.processNativeClasses(world, libraries.getValues()); |
382 world.addToWorkList(main); | 413 world.addToWorkList(main); |
383 codegenProgress.reset(); | 414 codegenProgress.reset(); |
384 while (!world.queue.isEmpty()) { | 415 world.forEach((WorkItem work) { |
385 WorkItem work = world.queue.removeLast(); | |
386 withCurrentElement(work.element, () => work.run(this, world)); | 416 withCurrentElement(work.element, () => work.run(this, world)); |
387 } | 417 }); |
388 world.queueIsClosed = true; | 418 world.queueIsClosed = true; |
389 assert(world.checkNoEnqueuedInvokedInstanceMethods()); | 419 assert(world.checkNoEnqueuedInvokedInstanceMethods()); |
390 world.registerFieldClosureInvocations(); | 420 world.registerFieldClosureInvocations(); |
391 } | 421 } |
392 | 422 |
423 checkQueues() { | |
kasperl
2012/06/12 05:50:12
Add a high level comment that explains the purpose
ahe
2012/06/12 10:56:11
Done.
| |
424 for (var world in [enqueuer.resolution, enqueuer.codegen]) { | |
425 world.forEach((WorkItem work) { | |
426 internalErrorOnElement(work.element, "Work list is not empty."); | |
427 }); | |
428 } | |
429 var resolved = new Set.from(enqueuer.resolution.resolvedElements.getKeys()); | |
430 for (Element e in codegenWorld.generatedCode.getKeys()) { | |
431 resolved.remove(e); | |
432 } | |
433 for (Element e in resolved) { | |
434 if (e.isClass() || | |
435 e.isField() || | |
436 e.isTypeVariable() || | |
437 e.isTypedef() || | |
438 e.kind === ElementKind.ABSTRACT_FIELD) { | |
439 resolved.remove(e); | |
sra1
2012/06/12 02:25:20
I'm not sure it is safe to remove elements from a
ahe
2012/06/12 06:22:49
I thought about that when writing the code. Nothin
ahe
2012/06/12 10:56:11
Done.
| |
440 } | |
441 if (e.kind === ElementKind.GENERATIVE_CONSTRUCTOR) { | |
442 if (e.enclosingElement.isInterface()) { | |
443 resolved.remove(e); | |
444 } | |
445 resolved.remove(e); | |
446 | |
447 } | |
448 if (e.getLibrary() === jsHelperLibrary) { | |
449 resolved.remove(e); | |
450 } | |
451 if (e.getLibrary() === interceptorsLibrary) { | |
452 resolved.remove(e); | |
453 } | |
454 } | |
455 log('Excess resolution work: ${resolved.length}.'); | |
456 if (!REPORT_EXCESS_RESOLUTION) return; | |
457 for (Element e in resolved) { | |
458 SourceSpan span = spanFromElement(e); | |
459 reportDiagnostic(span, 'Warning: $e resolved but not compiled.', false); | |
460 } | |
461 } | |
462 | |
393 TreeElements analyzeElement(Element element) { | 463 TreeElements analyzeElement(Element element) { |
464 TreeElements elements = enqueuer.resolution.getCachedElements(element); | |
465 if (elements !== null) return elements; | |
466 if (element is AbstractFieldElement) { | |
ahe
2012/06/12 06:22:49
Clean up these and the following special cases.
ahe
2012/06/12 10:56:11
That doesn't work :-(
| |
467 return null; | |
468 } | |
469 final int allowed = ElementCategory.VARIABLE | ElementCategory.FUNCTION | |
470 | ElementCategory.FACTORY; | |
471 if (!element.isAccessor() && (element.kind.category & allowed) == 0) { | |
472 return null; | |
473 } | |
394 assert(parser !== null); | 474 assert(parser !== null); |
395 Node tree = parser.parse(element); | 475 Node tree = parser.parse(element); |
396 validator.validate(tree); | 476 validator.validate(tree); |
397 unparseValidator.check(element); | 477 unparseValidator.check(element); |
398 TreeElements elements = resolver.resolve(element); | 478 elements = resolver.resolve(element); |
kasperl
2012/06/12 05:50:12
I think I would use two separate variables for thi
ahe
2012/06/12 10:56:11
I don't see the benefit.
| |
399 checker.check(tree, elements); | 479 checker.check(tree, elements); |
400 return elements; | 480 return elements; |
401 } | 481 } |
402 | 482 |
403 TreeElements analyze(WorkItem work) { | 483 TreeElements analyze(WorkItem work, Enqueuer world) { |
404 work.resolutionTree = analyzeElement(work.element); | 484 if (work.isAnalyzed()) return work.resolutionTree; |
405 return work.resolutionTree; | 485 Element element = work.element; |
486 TreeElements result = world.getCachedElements(element); | |
kasperl
2012/06/12 05:50:12
Consider a cached/result split here too.
ahe
2012/06/12 10:56:11
Done. Well, I considered it :-)
| |
487 if (result !== null) return result; | |
488 if (world !== enqueuer.resolution) { | |
489 internalErrorOnElement(element, | |
490 'Internal error: unresolved element: $element.'); | |
491 } | |
492 result = analyzeElement(element); | |
493 enqueuer.resolution.resolvedElements[element] = result; | |
494 return result; | |
406 } | 495 } |
407 | 496 |
408 String codegen(WorkItem work) { | 497 String codegen(WorkItem work, Enqueuer world) { |
498 if (world !== enqueuer.codegen) return null; | |
409 if (codegenProgress.elapsedInMs() > 500) { | 499 if (codegenProgress.elapsedInMs() > 500) { |
410 // TODO(ahe): Add structured diagnostics to the compiler API and | 500 // TODO(ahe): Add structured diagnostics to the compiler API and |
411 // use it to separate this from the --verbose option. | 501 // use it to separate this from the --verbose option. |
412 log('compiled ${codegenWorld.generatedCode.length} methods'); | 502 log('Compiled ${codegenWorld.generatedCode.length} methods.'); |
413 codegenProgress.reset(); | 503 codegenProgress.reset(); |
414 } | 504 } |
415 if (work.element.kind.category == ElementCategory.VARIABLE) { | 505 if (work.element.kind.category == ElementCategory.VARIABLE) { |
416 constantHandler.compileWorkItem(work); | 506 constantHandler.compileWorkItem(work); |
417 return null; | 507 return null; |
418 } else { | 508 } else { |
419 String code = backend.codegen(work); | 509 String code = backend.codegen(work); |
420 codegenWorld.addGeneratedCode(work, code); | 510 codegenWorld.addGeneratedCode(work, code); |
421 return code; | 511 return code; |
422 } | 512 } |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
502 // not a good error location, but cancel really is "internal | 592 // not a good error location, but cancel really is "internal |
503 // error" or "not implemented yet", so the vicinity is good | 593 // error" or "not implemented yet", so the vicinity is good |
504 // enough for now. | 594 // enough for now. |
505 element = element.enclosingElement; | 595 element = element.enclosingElement; |
506 // TODO(ahe): I plan to overhaul this infrastructure anyways. | 596 // TODO(ahe): I plan to overhaul this infrastructure anyways. |
507 } | 597 } |
508 if (element === null) { | 598 if (element === null) { |
509 element = currentElement; | 599 element = currentElement; |
510 } | 600 } |
511 Token position = element.position(); | 601 Token position = element.position(); |
602 Uri uri = element.getCompilationUnit().script.uri; | |
512 if (position === null) { | 603 if (position === null) { |
513 Uri uri = element.getCompilationUnit().script.uri; | |
514 return new SourceSpan(uri, 0, 0); | 604 return new SourceSpan(uri, 0, 0); |
605 } else { | |
606 return SourceSpan.withOffsets( | |
ahe
2012/06/12 06:22:49
This inlines part of spanFromTokens to define the
ahe
2012/06/12 10:56:11
Done.
| |
607 position, position, | |
608 (beginOffset, endOffset) { | |
kasperl
2012/06/12 05:50:12
Maybe pull out this closure into a named function
ahe
2012/06/12 10:56:11
Done.
| |
609 return new SourceSpan(uri, beginOffset, endOffset); | |
610 }); | |
515 } | 611 } |
516 return spanFromTokens(position, position); | |
517 } | 612 } |
518 | 613 |
519 Script readScript(Uri uri, [ScriptTag node]) { | 614 Script readScript(Uri uri, [ScriptTag node]) { |
520 unimplemented('Compiler.readScript'); | 615 unimplemented('Compiler.readScript'); |
521 } | 616 } |
522 | 617 |
523 String get legDirectory() { | 618 String get legDirectory() { |
524 unimplemented('Compiler.legDirectory'); | 619 unimplemented('Compiler.legDirectory'); |
525 } | 620 } |
526 | 621 |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
595 // invariant that endOffset > beginOffset, but for EOF the | 690 // invariant that endOffset > beginOffset, but for EOF the |
596 // charoffset of the next token may be [beginOffset]. This can | 691 // charoffset of the next token may be [beginOffset]. This can |
597 // also happen for synthetized tokens that are produced during | 692 // also happen for synthetized tokens that are produced during |
598 // error handling. | 693 // error handling. |
599 final endOffset = | 694 final endOffset = |
600 Math.max((end.next !== null) ? end.next.charOffset : 0, beginOffset + 1); | 695 Math.max((end.next !== null) ? end.next.charOffset : 0, beginOffset + 1); |
601 assert(endOffset > beginOffset); | 696 assert(endOffset > beginOffset); |
602 return f(beginOffset, endOffset); | 697 return f(beginOffset, endOffset); |
603 } | 698 } |
604 } | 699 } |
OLD | NEW |