Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(212)

Side by Side Diff: dart/lib/compiler/implementation/compiler.dart

Issue 10542073: RFC: Resolution based tree-shaking. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: All tests pass Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698