blob: ecd084ec80e6960f3d50532e1dc23cfc03b04a6b [file] [log] [blame]
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:typed_data';
import 'package:kernel/ast.dart' as ir;
import 'package:kernel/binary/ast_from_binary.dart' as ir;
import 'package:kernel/binary/ast_to_binary.dart' as ir;
import '../../compiler_api.dart' as api;
import '../common/codegen.dart';
import '../common/tasks.dart';
import '../deferred_load/output_unit.dart';
import '../diagnostics/diagnostic_listener.dart';
import '../dump_info.dart';
import '../elements/entities.dart';
import '../environment.dart';
import '../inferrer/abstract_value_domain.dart';
import '../inferrer/abstract_value_strategy.dart';
import '../inferrer/types.dart';
import '../io/source_information.dart';
import '../js_backend/codegen_inputs.dart';
import '../js_backend/inferred_data.dart';
import '../js_model/js_strategy.dart';
import '../js_model/js_world.dart';
import '../js_model/locals.dart';
import '../options.dart';
import 'deferrable.dart';
import 'serialization.dart';
class _StringInterner implements ir.StringInterner, StringInterner {
final Map<String, String> _map = {};
@override
String internString(String string) {
return _map[string] ??= string;
}
}
class SerializationTask extends CompilerTask {
final CompilerOptions _options;
final DiagnosticReporter _reporter;
final api.CompilerInput _provider;
final api.CompilerOutput _outputProvider;
final _stringInterner = _StringInterner();
final ValueInterner? _valueInterner;
SerializationTask(
this._options,
this._reporter,
this._provider,
this._outputProvider,
Measurer measurer,
) : _valueInterner = _options.features.internValues.isEnabled
? ValueInterner()
: null,
super(measurer);
@override
String get name => 'Serialization';
void serializeComponent(
ir.Component component, {
bool includeSourceBytes = true,
}) {
measureSubtask('serialize dill', () {
_reporter.log('Writing dill to ${_options.outputUri}');
api.BinaryOutputSink dillOutput = _outputProvider.createBinarySink(
_options.outputUri!,
);
ir.BinaryPrinter printer = ir.BinaryPrinter(
dillOutput,
includeSourceBytes: includeSourceBytes,
);
printer.writeComponentFile(component);
dillOutput.close();
});
}
Future<ir.Component> deserializeComponent() async {
return measureIoSubtask('deserialize dill', () async {
_reporter.log('Reading dill from ${_options.inputDillUri}');
final dillInput = await _provider.readFromUri(
_options.inputDillUri,
inputKind: api.InputKind.binary,
);
ir.Component component = ir.Component();
// Not using growable lists saves memory.
ir.BinaryBuilder(
dillInput.data,
useGrowableLists: false,
stringInterner: _stringInterner,
).readComponent(component);
return component;
});
}
Future<ir.Component> deserializeComponentAndUpdateOptions() async {
ir.Component component = await deserializeComponent();
return component;
}
void serializeClosedWorld(
JClosedWorld closedWorld,
SerializationIndices indices,
) {
measureSubtask('serialize closed world', () {
final outputUri = _options.dataUriForStage(CompilerStage.closedWorld);
_reporter.log('Writing closed world to $outputUri');
api.BinaryOutputSink dataOutput = _outputProvider.createBinarySink(
outputUri,
);
DataSinkWriter sink = DataSinkWriter(
BinaryDataSink(dataOutput),
_options,
indices,
);
serializeClosedWorldToSink(closedWorld, sink);
});
}
Future<JClosedWorld> deserializeClosedWorld(
AbstractValueStrategy abstractValueStrategy,
ir.Component component,
bool useDeferredSourceReads,
SerializationIndices indices,
) async {
return await measureIoSubtask('deserialize closed world', () async {
final uri = _options.dataUriForStage(CompilerStage.closedWorld);
_reporter.log('Reading data from $uri');
api.Input<Uint8List> dataInput = await _provider.readFromUri(
uri,
inputKind: api.InputKind.binary,
);
DataSourceReader source = DataSourceReader(
BinaryDataSource(dataInput.data, stringInterner: _stringInterner),
_options,
indices,
interner: _valueInterner,
useDeferredStrategy: useDeferredSourceReads,
);
var closedWorld = deserializeClosedWorldFromSource(
_options,
_reporter,
abstractValueStrategy,
component,
source,
);
return closedWorld;
});
}
void serializeGlobalTypeInference(
GlobalTypeInferenceResults results,
SerializationIndices indices,
) {
measureSubtask('serialize data', () {
final outputUri = _options.dataUriForStage(CompilerStage.globalInference);
_reporter.log('Writing data to $outputUri');
api.BinaryOutputSink dataOutput = _outputProvider.createBinarySink(
outputUri,
);
DataSinkWriter sink = DataSinkWriter(
BinaryDataSink(dataOutput),
_options,
indices,
);
serializeGlobalTypeInferenceResultsToSink(results, sink);
});
}
Future<GlobalTypeInferenceResults> deserializeGlobalTypeInferenceResults(
Environment environment,
AbstractValueStrategy abstractValueStrategy,
ir.Component component,
JClosedWorld closedWorld,
bool useDeferredSourceReads,
SerializationIndices indices,
) async {
return await measureIoSubtask('deserialize data', () async {
final uri = _options.dataUriForStage(CompilerStage.globalInference);
_reporter.log('Reading data from $uri');
api.Input<Uint8List> dataInput = await _provider.readFromUri(
uri,
inputKind: api.InputKind.binary,
);
DataSourceReader source = DataSourceReader(
BinaryDataSource(dataInput.data, stringInterner: _stringInterner),
_options,
indices,
interner: _valueInterner,
useDeferredStrategy: useDeferredSourceReads,
);
return deserializeGlobalTypeInferenceResultsFromSource(
_options,
_reporter,
environment,
abstractValueStrategy,
component,
closedWorld,
source,
);
});
}
void serializeCodegen(
JsBackendStrategy backendStrategy,
AbstractValueDomain domain,
CodegenResults codegenResults,
SerializationIndices indices,
) {
int shard = _options.codegenShard!;
int shards = _options.codegenShards!;
Map<MemberEntity, CodegenResult> results = {};
int index = 0;
final lazyMemberBodies = backendStrategy.forEachCodegenMember((
MemberEntity member,
) {
if (index % shards == shard) {
final (result: codegenResult, isGenerated: _) = codegenResults
.getCodegenResults(member);
results[member] = codegenResult;
}
index++;
});
measureSubtask('serialize codegen', () {
final outputUri = _options.dataUriForStage(CompilerStage.codegenSharded);
Uri uri = Uri.parse('$outputUri$shard');
api.BinaryOutputSink dataOutput = _outputProvider.createBinarySink(uri);
DataSinkWriter sink = DataSinkWriter(
BinaryDataSink(dataOutput),
_options,
indices,
);
_reporter.log('Writing data to $uri');
sink.writeMembers(lazyMemberBodies);
sink.registerAbstractValueDomain(domain);
sink.writeMemberMap(results, (MemberEntity member, CodegenResult result) {
sink.writeDeferrable(() => result.writeToDataSink(sink));
});
sink.close();
});
}
Future<CodegenResults> deserializeCodegen(
JsBackendStrategy backendStrategy,
JClosedWorld closedWorld,
CodegenInputs codegenInputs,
bool useDeferredSourceReads,
SourceLookup sourceLookup,
SerializationIndices indices,
) async {
int shards = _options.codegenShards!;
Map<MemberEntity, Deferrable<CodegenResult>> results = {};
for (int shard = 0; shard < shards; shard++) {
Uri uri = Uri.parse(
'${_options.dataUriForStage(CompilerStage.codegenSharded)}$shard',
);
await measureIoSubtask('deserialize codegen', () async {
_reporter.log('Reading data from $uri');
api.Input<Uint8List> dataInput = await _provider.readFromUri(
uri,
inputKind: api.InputKind.binary,
);
// TODO(36983): This code is extracted because there appeared to be a
// memory leak for large buffer held by `source`.
_deserializeCodegenInput(
backendStrategy,
closedWorld,
uri,
dataInput,
results,
useDeferredSourceReads,
sourceLookup,
indices,
);
dataInput.release();
});
}
return DeserializedCodegenResults(
codegenInputs,
DeferrableValueMap(results),
backendStrategy.functionCompiler,
);
}
void _deserializeCodegenInput(
JsBackendStrategy backendStrategy,
JClosedWorld closedWorld,
Uri uri,
api.Input<Uint8List> dataInput,
Map<MemberEntity, Deferrable<CodegenResult>> results,
bool useDeferredSourceReads,
SourceLookup sourceLookup,
SerializationIndices indices,
) {
DataSourceReader source = DataSourceReader(
BinaryDataSource(dataInput.data, stringInterner: _stringInterner),
_options,
indices,
interner: _valueInterner,
useDeferredStrategy: useDeferredSourceReads,
);
backendStrategy.prepareCodegenReader(source);
source.registerSourceLookup(sourceLookup);
final lazyMemberBodies = source.readMembers();
closedWorld.elementMap.registerLazyMemberBodies(lazyMemberBodies);
source.registerAbstractValueDomain(closedWorld.abstractValueDomain);
Map<MemberEntity, Deferrable<CodegenResult>> codegenResults = source
.readMemberMap((MemberEntity member) {
return source.readDeferrable(
CodegenResult.readFromDataSource,
cacheData: false,
);
});
_reporter.log('Read ${codegenResults.length} members from $uri');
results.addAll(codegenResults);
}
DataSinkWriter dataSinkWriterForDumpInfo(
AbstractValueDomain abstractValueDomain,
SerializationIndices indices,
) {
final outputUri = _options.dataUriForStage(CompilerStage.dumpInfo);
api.BinaryOutputSink dataOutput = _outputProvider.createBinarySink(
outputUri,
);
final sink = DataSinkWriter(BinaryDataSink(dataOutput), _options, indices);
sink.registerAbstractValueDomain(abstractValueDomain);
return sink;
}
void serializeDumpInfoProgramData(
DataSinkWriter sink,
JsBackendStrategy backendStrategy,
DumpInfoProgramData dumpInfoProgramData,
DumpInfoJsAstRegistry dumpInfoRegistry,
) {
dumpInfoProgramData.writeToDataSink(sink, dumpInfoRegistry);
sink.close();
}
Future<DumpInfoProgramData> deserializeDumpInfoProgramData(
JsBackendStrategy backendStrategy,
AbstractValueDomain abstractValueDomain,
OutputUnitData outputUnitData,
SerializationIndices indices,
) async {
final inputUri = _options.dataUriForStage(CompilerStage.dumpInfo);
final dataInput = await _provider.readFromUri(
inputUri,
inputKind: api.InputKind.binary,
);
final source = DataSourceReader(
BinaryDataSource(dataInput.data, stringInterner: _stringInterner),
_options,
indices,
// This must use a deferred strategy so that we can delay reading the
// registered impacts until we are able to read the count of them.
useDeferredStrategy: true,
);
backendStrategy.prepareCodegenReader(source);
source.registerAbstractValueDomain(abstractValueDomain);
return DumpInfoProgramData.readFromDataSource(
source,
includeCodeText: _options.dumpInfoFormat != DumpInfoFormat.binary,
);
}
}
void serializeGlobalTypeInferenceResultsToSink(
GlobalTypeInferenceResults results,
DataSinkWriter sink,
) {
final closedWorld = results.closedWorld;
GlobalLocalsMap globalLocalsMap = results.globalLocalsMap;
InferredData inferredData = results.inferredData;
globalLocalsMap.writeToDataSink(sink);
inferredData.writeToDataSink(sink);
results.writeToDataSink(sink, closedWorld.elementMap);
sink.close();
}
GlobalTypeInferenceResults deserializeGlobalTypeInferenceResultsFromSource(
CompilerOptions options,
DiagnosticReporter reporter,
Environment environment,
AbstractValueStrategy abstractValueStrategy,
ir.Component component,
JClosedWorld closedWorld,
DataSourceReader source,
) {
source.registerComponentLookup(ComponentLookup(component));
GlobalLocalsMap globalLocalsMap = GlobalLocalsMap.readFromDataSource(
closedWorld.closureDataLookup.getEnclosingMember,
source,
);
InferredData inferredData = InferredData.readFromDataSource(
source,
closedWorld,
);
return GlobalTypeInferenceResults.readFromDataSource(
source,
closedWorld.elementMap,
closedWorld,
globalLocalsMap,
inferredData,
);
}
void serializeClosedWorldToSink(JClosedWorld closedWorld, DataSinkWriter sink) {
closedWorld.writeToDataSink(sink);
sink.close();
}
JClosedWorld deserializeClosedWorldFromSource(
CompilerOptions options,
DiagnosticReporter reporter,
AbstractValueStrategy abstractValueStrategy,
ir.Component component,
DataSourceReader source,
) {
return JClosedWorld.readFromDataSource(
options,
reporter,
abstractValueStrategy,
component,
source,
);
}