Skip to content

Commit c3b992d

Browse files
jensjohacommit-bot@chromium.org
authored andcommitted
[fasta] incremental compiler can initialize from dill
As requested in #31691 the incremental compiler should be able to initialize its state from a previously generated dill file. This CL introduces that functionally. It is tested, but actual usage (e.g. proper invalidation of old files) should live outside of the front-end (i.e. it's the callers responsibility). One option would be for the caller to load the dill file and use the included sources to invalidate changed files. On my machine a from-scratch compile of dart2js takes ~5 seconds, one initialized from such an output and with a single file invalidated (though it actually hasn't changed) it takes ~1 second and the resulting dill file is bit-perfect compared to the from-scratch compiled one. Closes #31691. Change-Id: I07f5efca5f2684d73f6c252f2dbc2ad04e9b5cd0 Reviewed-on: https://dart-review.googlesource.com/37260 Commit-Queue: Jens Johansen <[email protected]> Reviewed-by: Kevin Millikin <[email protected]>
1 parent caabeaa commit c3b992d

8 files changed

+346
-88
lines changed

pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import '../fasta/incremental_compiler.dart' show IncrementalCompiler;
1515
import 'compiler_options.dart' show CompilerOptions;
1616

1717
abstract class IncrementalKernelGenerator {
18-
factory IncrementalKernelGenerator(CompilerOptions options, Uri entryPoint) {
19-
return new IncrementalCompiler(new CompilerContext(
20-
new ProcessedOptions(options, false, [entryPoint])));
18+
factory IncrementalKernelGenerator(CompilerOptions options, Uri entryPoint,
19+
[Uri bootstrapDill]) {
20+
return new IncrementalCompiler(
21+
new CompilerContext(new ProcessedOptions(options, false, [entryPoint])),
22+
bootstrapDill);
2123
}
2224

2325
/// Returns a component (nee program) whose libraries are the recompiled
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) 2018, 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+
library fasta.builder_graph;
6+
7+
import 'builder/builder.dart' show LibraryBuilder;
8+
9+
import 'export.dart' show Export;
10+
11+
import 'graph/graph.dart' show Graph;
12+
13+
import 'import.dart' show Import;
14+
15+
import 'dill/dill_library_builder.dart' show DillLibraryBuilder;
16+
17+
import 'source/source_library_builder.dart' show SourceLibraryBuilder;
18+
19+
class BuilderGraph implements Graph<Uri> {
20+
final Map<Uri, LibraryBuilder> builders;
21+
22+
BuilderGraph(this.builders);
23+
24+
Iterable<Uri> get vertices => builders.keys;
25+
26+
Iterable<Uri> neighborsOf(Uri vertex) sync* {
27+
LibraryBuilder library = builders[vertex];
28+
if (library == null) {
29+
throw "Library not found: $vertex";
30+
}
31+
if (library is SourceLibraryBuilder) {
32+
for (Import import in library.imports) {
33+
Uri uri = import.imported.uri;
34+
if (builders.containsKey(uri)) {
35+
yield uri;
36+
}
37+
}
38+
for (Export export in library.exports) {
39+
Uri uri = export.exported.uri;
40+
if (builders.containsKey(uri)) {
41+
yield uri;
42+
}
43+
}
44+
for (SourceLibraryBuilder part in library.parts) {
45+
Uri uri = part.uri;
46+
if (builders.containsKey(uri)) {
47+
yield uri;
48+
}
49+
}
50+
} else if (library is DillLibraryBuilder) {
51+
// Imports and exports
52+
for (var dependency in library.library.dependencies) {
53+
var uriString;
54+
if (dependency.importedLibraryReference.node != null) {
55+
uriString = '${dependency.targetLibrary.importUri}';
56+
} else {
57+
uriString =
58+
'${dependency.importedLibraryReference.canonicalName.name}';
59+
}
60+
Uri uri = Uri.parse(uriString);
61+
if (builders.containsKey(uri)) {
62+
yield uri;
63+
}
64+
}
65+
66+
// Parts
67+
for (var part in library.library.parts) {
68+
Uri uri = part.fileUri;
69+
if (builders.containsKey(uri)) {
70+
yield uri;
71+
}
72+
}
73+
}
74+
}
75+
}

pkg/front_end/lib/src/fasta/incremental_compiler.dart

Lines changed: 137 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,27 @@ library fasta.incremental_compiler;
77
import 'dart:async' show Future;
88

99
import 'package:kernel/kernel.dart'
10-
show Library, Program, Source, loadProgramFromBytes;
10+
show loadProgramFromBytes, Library, Procedure, Program, Source;
1111

1212
import '../api_prototype/incremental_kernel_generator.dart'
1313
show IncrementalKernelGenerator;
1414

15+
import '../api_prototype/file_system.dart' show FileSystemEntity;
16+
1517
import 'builder/builder.dart' show LibraryBuilder;
1618

19+
import 'builder_graph.dart' show BuilderGraph;
20+
21+
import 'compiler_context.dart' show CompilerContext;
22+
23+
import 'dill/dill_library_builder.dart' show DillLibraryBuilder;
24+
1725
import 'dill/dill_target.dart' show DillTarget;
1826

1927
import 'kernel/kernel_target.dart' show KernelTarget;
2028

21-
import 'source/source_graph.dart' show SourceGraph;
22-
2329
import 'source/source_library_builder.dart' show SourceLibraryBuilder;
2430

25-
import 'compiler_context.dart' show CompilerContext;
26-
2731
import 'ticker.dart' show Ticker;
2832

2933
import 'uri_translator.dart' show UriTranslator;
@@ -35,48 +39,112 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
3539

3640
List<Uri> invalidatedUris = <Uri>[];
3741

38-
DillTarget platform;
42+
DillTarget dillLoadedData;
43+
List<LibraryBuilder> platformBuilders;
44+
Map<Uri, LibraryBuilder> userBuilders;
45+
final Uri bootstrapDill;
46+
bool bootstrapSuccess = false;
3947

4048
KernelTarget userCode;
4149

42-
IncrementalCompiler(this.context) : ticker = context.options.ticker;
50+
IncrementalCompiler(this.context, [this.bootstrapDill])
51+
: ticker = context.options.ticker;
4352

4453
@override
4554
Future<Program> computeDelta({Uri entryPoint}) async {
4655
ticker.reset();
4756
entryPoint ??= context.options.inputs.single;
4857
return context.runInContext<Future<Program>>((CompilerContext c) async {
49-
if (platform == null) {
58+
bool includeUserLoadedLibraries = false;
59+
Map<Uri, Source> uriToSource = {};
60+
Map<Uri, int> importUriToOrder = {};
61+
Procedure userLoadedUriMain;
62+
bootstrapSuccess = false;
63+
if (dillLoadedData == null) {
5064
UriTranslator uriTranslator = await c.options.getUriTranslator();
5165
ticker.logMs("Read packages file");
5266

53-
platform = new DillTarget(ticker, uriTranslator, c.options.target);
54-
List<int> bytes = await c.options.loadSdkSummaryBytes();
55-
if (bytes != null) {
67+
dillLoadedData =
68+
new DillTarget(ticker, uriTranslator, c.options.target);
69+
List<int> summaryBytes = await c.options.loadSdkSummaryBytes();
70+
int bytesLength = 0;
71+
Program program;
72+
if (summaryBytes != null) {
5673
ticker.logMs("Read ${c.options.sdkSummary}");
57-
Program program = loadProgramFromBytes(bytes);
74+
program = loadProgramFromBytes(summaryBytes);
5875
ticker.logMs("Deserialized ${c.options.sdkSummary}");
59-
platform.loader.appendLibraries(program, byteCount: bytes.length);
60-
ticker.logMs("Appended libraries");
76+
bytesLength += summaryBytes.length;
77+
}
78+
79+
if (bootstrapDill != null) {
80+
FileSystemEntity entity =
81+
c.options.fileSystem.entityForUri(bootstrapDill);
82+
if (await entity.exists()) {
83+
List<int> bootstrapBytes = await entity.readAsBytes();
84+
if (bootstrapBytes != null) {
85+
Set<Uri> prevLibraryUris = new Set<Uri>.from(
86+
program.libraries.map((Library lib) => lib.importUri));
87+
ticker.logMs("Read $bootstrapDill");
88+
bool bootstrapFailed = false;
89+
try {
90+
loadProgramFromBytes(bootstrapBytes, program);
91+
} catch (e) {
92+
bootstrapFailed = true;
93+
program = loadProgramFromBytes(summaryBytes);
94+
}
95+
if (!bootstrapFailed) {
96+
bootstrapSuccess = true;
97+
bytesLength += bootstrapBytes.length;
98+
for (Library lib in program.libraries) {
99+
if (prevLibraryUris.contains(lib.importUri)) continue;
100+
importUriToOrder[lib.importUri] = importUriToOrder.length;
101+
}
102+
userLoadedUriMain = program.mainMethod;
103+
includeUserLoadedLibraries = true;
104+
uriToSource.addAll(program.uriToSource);
105+
}
106+
}
107+
}
108+
}
109+
summaryBytes = null;
110+
if (program != null) {
111+
dillLoadedData.loader
112+
.appendLibraries(program, byteCount: bytesLength);
61113
}
62-
await platform.buildOutlines();
114+
ticker.logMs("Appended libraries");
115+
await dillLoadedData.buildOutlines();
116+
userBuilders = <Uri, LibraryBuilder>{};
117+
platformBuilders = <LibraryBuilder>[];
118+
dillLoadedData.loader.builders.forEach((uri, builder) {
119+
if (builder.fileUri.scheme == "dart") {
120+
platformBuilders.add(builder);
121+
} else {
122+
userBuilders[uri] = builder;
123+
}
124+
});
125+
if (userBuilders.isEmpty) userBuilders = null;
63126
}
64127

65128
List<Uri> invalidatedUris = this.invalidatedUris.toList();
66129
this.invalidatedUris.clear();
67130

68131
List<LibraryBuilder> reusedLibraries =
69132
computeReusedLibraries(invalidatedUris);
133+
Set<Uri> reusedLibraryUris =
134+
new Set<Uri>.from(reusedLibraries.map((b) => b.uri));
135+
for (Uri uri in new Set<Uri>.from(dillLoadedData.loader.builders.keys)
136+
..removeAll(reusedLibraryUris)) {
137+
dillLoadedData.loader.builders.remove(uri);
138+
}
139+
70140
if (userCode != null) {
71141
ticker.logMs("Decided to reuse ${reusedLibraries.length}"
72142
" of ${userCode.loader.builders.length} libraries");
73143
}
74144

75-
platform.loader.builders.forEach((Uri uri, LibraryBuilder builder) {
76-
reusedLibraries.add(builder);
77-
});
145+
reusedLibraries.addAll(platformBuilders);
78146
userCode = new KernelTarget(
79-
c.fileSystem, false, platform, platform.uriTranslator,
147+
c.fileSystem, false, dillLoadedData, dillLoadedData.uriTranslator,
80148
uriToSource: c.uriToSource);
81149
for (LibraryBuilder library in reusedLibraries) {
82150
userCode.loader.builders[library.uri] = library;
@@ -94,16 +162,45 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
94162
Program programWithDill =
95163
await userCode.buildProgram(verify: c.options.verify);
96164

165+
List<Library> libraries =
166+
new List<Library>.from(userCode.loader.libraries);
167+
uriToSource.addAll(userCode.uriToSource);
168+
if (includeUserLoadedLibraries) {
169+
for (LibraryBuilder library in reusedLibraries) {
170+
if (library.fileUri.scheme == "dart") continue;
171+
assert(library is DillLibraryBuilder);
172+
libraries.add((library as DillLibraryBuilder).library);
173+
}
174+
175+
// For now ensure original order of libraries to produce bit-perfect
176+
// output.
177+
List<Library> librariesOriginalOrder =
178+
new List<Library>.filled(libraries.length, null, growable: true);
179+
int lastOpen = libraries.length;
180+
for (Library lib in libraries) {
181+
int order = importUriToOrder[lib.importUri];
182+
if (order != null) {
183+
librariesOriginalOrder[order] = lib;
184+
} else {
185+
librariesOriginalOrder[--lastOpen] = lib;
186+
}
187+
}
188+
libraries = librariesOriginalOrder;
189+
}
190+
97191
// This is the incremental program.
98-
return new Program(
99-
libraries: new List<Library>.from(userCode.loader.libraries),
100-
uriToSource: new Map<Uri, Source>.from(userCode.uriToSource))
101-
..mainMethod = programWithDill?.mainMethod;
192+
Procedure mainMethod = programWithDill == null
193+
? userLoadedUriMain
194+
: programWithDill.mainMethod;
195+
return new Program(libraries: libraries, uriToSource: uriToSource)
196+
..mainMethod = mainMethod;
102197
});
103198
}
104199

105200
List<LibraryBuilder> computeReusedLibraries(Iterable<Uri> invalidatedUris) {
106-
if (userCode == null) return <LibraryBuilder>[];
201+
if (userCode == null && userBuilders == null) {
202+
return <LibraryBuilder>[];
203+
}
107204

108205
// [invalidatedUris] converted to a set.
109206
Set<Uri> invalidatedFileUris = invalidatedUris.toSet();
@@ -116,25 +213,29 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
116213
List<Uri> invalidatedImportUris = <Uri>[];
117214

118215
// Compute [builders] and [invalidatedImportUris].
119-
addBuilderAndInvalidateUris(Uri uri, LibraryBuilder libraryBuilder) {
120-
if (libraryBuilder.loader != platform.loader) {
121-
assert(libraryBuilder is SourceLibraryBuilder);
122-
SourceLibraryBuilder library = libraryBuilder;
123-
builders[uri] = library;
124-
if (invalidatedFileUris.contains(uri) ||
125-
(uri != library.fileUri &&
126-
invalidatedFileUris.contains(library.fileUri))) {
127-
invalidatedImportUris.add(uri);
128-
}
216+
addBuilderAndInvalidateUris(Uri uri, LibraryBuilder library) {
217+
builders[uri] = library;
218+
if (invalidatedFileUris.contains(uri) ||
219+
(uri != library.fileUri &&
220+
invalidatedFileUris.contains(library.fileUri)) ||
221+
(library is DillLibraryBuilder &&
222+
uri != library.library.fileUri &&
223+
invalidatedFileUris.contains(library.library.fileUri))) {
224+
invalidatedImportUris.add(uri);
225+
}
226+
if (library is SourceLibraryBuilder) {
129227
for (var part in library.parts) {
130228
addBuilderAndInvalidateUris(part.uri, part);
131229
}
132230
}
133231
}
134232

135-
userCode.loader.builders.forEach(addBuilderAndInvalidateUris);
233+
userBuilders?.forEach(addBuilderAndInvalidateUris);
234+
if (userCode != null) {
235+
userCode.loader.builders.forEach(addBuilderAndInvalidateUris);
236+
}
136237

137-
SourceGraph graph = new SourceGraph(builders);
238+
BuilderGraph graph = new BuilderGraph(builders);
138239

139240
// Compute direct dependencies for each import URI (the reverse of the
140241
// edges returned by `graph.neighborsOf`).

pkg/front_end/lib/src/fasta/source/source_graph.dart

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)