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

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: Minor tweaks for the unit tests 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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698