mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 15:50:01 +00:00
[vm, bytecode] Generate bytecode per package in fronend_server incremental compiles.
flutter/examples/flutter_gallery$ flutter test --local-engine host_debug -v | grep took First run -> Second run (initialize from dill file) AST 11539ms -> 6837ms Bytecode before 32790ms -> 26665ms Bytecode after 15435ms -> 11111ms Change-Id: If1088f86a583170c9f3690778a5b8e2fd4b48ace Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/118341 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
48f7636798
commit
4a69ac1344
|
@ -33,10 +33,12 @@ import 'package:vm/kernel_front_end.dart'
|
|||
show
|
||||
asFileUri,
|
||||
compileToKernel,
|
||||
parseCommandLineDefines,
|
||||
convertFileOrUriArgumentToUri,
|
||||
createFrontEndTarget,
|
||||
createFrontEndFileSystem,
|
||||
createFrontEndTarget,
|
||||
forEachPackage,
|
||||
packageFor,
|
||||
parseCommandLineDefines,
|
||||
runWithFrontEndCompilerContext,
|
||||
setVMEnvironmentDefines,
|
||||
writeDepfile;
|
||||
|
@ -341,6 +343,7 @@ class FrontendCompiler implements CompilerInterface {
|
|||
compilerOptions.bytecode = options['gen-bytecode'];
|
||||
final BytecodeOptions bytecodeOptions = new BytecodeOptions(
|
||||
enableAsserts: options['enable-asserts'],
|
||||
emitSourceFiles: options['embed-source-text'],
|
||||
environmentDefines: environmentDefines)
|
||||
..parseCommandLineFlags(options['bytecode-options']);
|
||||
|
||||
|
@ -549,6 +552,59 @@ class FrontendCompiler implements CompilerInterface {
|
|||
}
|
||||
}
|
||||
|
||||
bool _elementsIdentical(List a, List b) {
|
||||
if (a.length != b.length) return false;
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
if (!identical(a[i], b[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
final _packageLibraries = new Expando();
|
||||
final _packageBytes = new Expando();
|
||||
|
||||
void _writePackage(Component component, String package,
|
||||
List<Library> libraries, IOSink sink) {
|
||||
final canCache = libraries.isNotEmpty &&
|
||||
_compilerOptions.bytecode &&
|
||||
errors.isEmpty &&
|
||||
package != "main";
|
||||
|
||||
if (canCache) {
|
||||
var cachedLibraries = _packageLibraries[libraries.first];
|
||||
if ((cachedLibraries != null) &&
|
||||
_elementsIdentical(cachedLibraries, libraries)) {
|
||||
sink.add(_packageBytes[libraries.first]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Component partComponent = component;
|
||||
if (_compilerOptions.bytecode && errors.isEmpty) {
|
||||
generateBytecode(partComponent,
|
||||
options: _bytecodeOptions,
|
||||
libraries: libraries,
|
||||
coreTypes: _generator.getCoreTypes(),
|
||||
hierarchy: _generator.getClassHierarchy());
|
||||
|
||||
if (_options['drop-ast']) {
|
||||
partComponent = createFreshComponentWithBytecode(partComponent);
|
||||
}
|
||||
}
|
||||
|
||||
final byteSink = new ByteSink();
|
||||
final BinaryPrinter printer = new LimitedBinaryPrinter(byteSink,
|
||||
(lib) => packageFor(lib) == package, false /* excludeUriToSource */);
|
||||
printer.writeComponentFile(partComponent);
|
||||
|
||||
final bytes = byteSink.builder.takeBytes();
|
||||
sink.add(bytes);
|
||||
if (canCache) {
|
||||
_packageLibraries[libraries.first] = libraries;
|
||||
_packageBytes[libraries.first] = bytes;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Null> recompileDelta({String entryPoint}) async {
|
||||
final String boundaryKey = new Uuid().generateV4();
|
||||
|
@ -564,8 +620,21 @@ class FrontendCompiler implements CompilerInterface {
|
|||
transformer.transform(deltaProgram);
|
||||
}
|
||||
final compiledSources = deltaProgram.uriToSource.keys;
|
||||
deltaProgram = await _generateBytecodeIfNeeded(deltaProgram);
|
||||
await writeDillFile(deltaProgram, _kernelBinaryFilename);
|
||||
|
||||
if (_compilerOptions.bytecode) {
|
||||
final IOSink sink = new File(_kernelBinaryFilename).openWrite();
|
||||
await runWithFrontEndCompilerContext(
|
||||
_mainSource, _compilerOptions, deltaProgram, () async {
|
||||
await forEachPackage(deltaProgram,
|
||||
(String package, List<Library> libraries) async {
|
||||
_writePackage(deltaProgram, package, libraries, sink);
|
||||
});
|
||||
});
|
||||
await sink.close();
|
||||
} else {
|
||||
await writeDillFile(deltaProgram, _kernelBinaryFilename);
|
||||
}
|
||||
|
||||
_outputStream.writeln(boundaryKey);
|
||||
await _outputDependenciesDelta(compiledSources);
|
||||
_outputStream
|
||||
|
|
|
@ -595,28 +595,6 @@ Future writeOutputSplitByPackages(
|
|||
BytecodeOptions bytecodeOptions,
|
||||
bool dropAST: false,
|
||||
}) async {
|
||||
// Package sharing: make the encoding not depend on the order in which parts
|
||||
// of a package are loaded.
|
||||
component.libraries.sort((Library a, Library b) {
|
||||
return a.importUri.toString().compareTo(b.importUri.toString());
|
||||
});
|
||||
component.computeCanonicalNames();
|
||||
for (Library lib in component.libraries) {
|
||||
lib.additionalExports.sort((Reference a, Reference b) {
|
||||
return a.canonicalName.toString().compareTo(b.canonicalName.toString());
|
||||
});
|
||||
}
|
||||
|
||||
final packagesSet = new Set<String>();
|
||||
for (Library lib in component.libraries) {
|
||||
packagesSet.add(packageFor(lib));
|
||||
}
|
||||
packagesSet.remove('main');
|
||||
packagesSet.remove(null);
|
||||
|
||||
final List<String> packages = packagesSet.toList();
|
||||
packages.add('main'); // Make sure main package is last.
|
||||
|
||||
if (bytecodeOptions.showBytecodeSizeStatistics) {
|
||||
BytecodeSizeStatistics.reset();
|
||||
}
|
||||
|
@ -629,31 +607,24 @@ Future writeOutputSplitByPackages(
|
|||
new ClassHierarchy(component, onAmbiguousSupertypes: (cls, a, b) {});
|
||||
}
|
||||
|
||||
final packages = new List<String>();
|
||||
await runWithFrontEndCompilerContext(source, compilerOptions, component,
|
||||
() async {
|
||||
for (String package in packages) {
|
||||
await forEachPackage(component,
|
||||
(String package, List<Library> libraries) async {
|
||||
packages.add(package);
|
||||
final String filename = '$outputFileName-$package.dilp';
|
||||
final IOSink sink = new File(filename).openWrite();
|
||||
|
||||
final main = component.mainMethod;
|
||||
final problems = component.problemsAsJson;
|
||||
if (package != 'main') {
|
||||
component.mainMethod = null;
|
||||
component.problemsAsJson = null;
|
||||
}
|
||||
|
||||
Component partComponent = component;
|
||||
if (genBytecode) {
|
||||
final List<Library> libraries = component.libraries
|
||||
.where((lib) => packageFor(lib) == package)
|
||||
.toList();
|
||||
generateBytecode(component,
|
||||
generateBytecode(partComponent,
|
||||
options: bytecodeOptions,
|
||||
libraries: libraries,
|
||||
hierarchy: hierarchy);
|
||||
|
||||
if (dropAST) {
|
||||
partComponent = createFreshComponentWithBytecode(component);
|
||||
partComponent = createFreshComponentWithBytecode(partComponent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -661,11 +632,8 @@ Future writeOutputSplitByPackages(
|
|||
(lib) => packageFor(lib) == package, false /* excludeUriToSource */);
|
||||
printer.writeComponentFile(partComponent);
|
||||
|
||||
component.mainMethod = main;
|
||||
component.problemsAsJson = problems;
|
||||
|
||||
await sink.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (bytecodeOptions.showBytecodeSizeStatistics) {
|
||||
|
@ -692,6 +660,47 @@ String packageFor(Library lib) {
|
|||
return 'main';
|
||||
}
|
||||
|
||||
Future<Null> forEachPackage<T>(Component component,
|
||||
T action(String package, List<Library> libraries)) async {
|
||||
// Package sharing: make the encoding not depend on the order in which parts
|
||||
// of a package are loaded.
|
||||
component.libraries.sort((Library a, Library b) {
|
||||
return a.importUri.toString().compareTo(b.importUri.toString());
|
||||
});
|
||||
component.computeCanonicalNames();
|
||||
for (Library lib in component.libraries) {
|
||||
lib.additionalExports.sort((Reference a, Reference b) {
|
||||
return a.canonicalName.toString().compareTo(b.canonicalName.toString());
|
||||
});
|
||||
}
|
||||
|
||||
final packages = new Map<String, List<Library>>();
|
||||
for (Library lib in component.libraries) {
|
||||
packages.putIfAbsent(packageFor(lib), () => new List<Library>()).add(lib);
|
||||
}
|
||||
if (packages.containsKey(null)) {
|
||||
packages.remove(null);
|
||||
}
|
||||
if (packages.containsKey('main')) {
|
||||
// Make sure main package is last.
|
||||
packages['main'] = packages.remove('main');
|
||||
}
|
||||
|
||||
for (String package in packages.keys) {
|
||||
final main = component.mainMethod;
|
||||
final problems = component.problemsAsJson;
|
||||
if (package != 'main') {
|
||||
component.mainMethod = null;
|
||||
component.problemsAsJson = null;
|
||||
}
|
||||
|
||||
await action(package, packages[package]);
|
||||
|
||||
component.mainMethod = main;
|
||||
component.problemsAsJson = problems;
|
||||
}
|
||||
}
|
||||
|
||||
String _escapePath(String path) {
|
||||
return path.replaceAll('\\', '\\\\').replaceAll(' ', '\\ ');
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue