Skip to content

Commit

Permalink
Support post-inference serialization/deserialization
Browse files Browse the repository at this point in the history
Currently handling Swarm in-memory serialization/deserialization.

Change-Id: I4b01f70d1e42d7462ed079ece227d6c2ed4de597
Reviewed-on: https://dart-review.googlesource.com/c/80445
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Auto-Submit: Johnni Winther <johnniwinther@google.com>
  • Loading branch information
johnniwinther authored and commit-bot@chromium.org committed Oct 22, 2018
1 parent 9c3834d commit 8b6f896
Show file tree
Hide file tree
Showing 57 changed files with 6,421 additions and 73 deletions.
8 changes: 5 additions & 3 deletions pkg/compiler/lib/src/backend_strategy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import 'io/source_information.dart';
import 'js_backend/inferred_data.dart';
import 'js_backend/js_backend.dart';
import 'js_backend/native_data.dart';
import 'js_emitter/sorter.dart';
import 'ssa/ssa.dart';
import 'types/types.dart';
import 'universe/world_builder.dart';
Expand All @@ -26,8 +25,11 @@ abstract class BackendStrategy {
JClosedWorld createJClosedWorld(
KClosedWorld closedWorld, OutputUnitData outputUnitData);

/// The [Sorter] used for sorting elements in the generated code.
Sorter get sorter;
/// Registers [closedWorld] as the current closed world used by this backend
/// strategy.
///
/// This is used to support serialization after type inference.
void registerJClosedWorld(JClosedWorld closedWorld);

/// Creates the [CodegenWorldBuilder] used by the codegen enqueuer.
CodegenWorldBuilder createCodegenWorldBuilder(
Expand Down
84 changes: 84 additions & 0 deletions pkg/compiler/lib/src/closure.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import 'common/tasks.dart' show CompilerTask, Measurer;
import 'common.dart';
import 'elements/entities.dart';
import 'elements/types.dart';
import 'js_model/closure.dart';
import 'js_model/element_map.dart';
import 'serialization/serialization.dart';

abstract class ClosureConversionTask extends CompilerTask {
ClosureConversionTask(Measurer measurer) : super(measurer);
Expand All @@ -17,6 +20,14 @@ abstract class ClosureConversionTask extends CompilerTask {
/// node to look up, it returns a information about the internal representation
/// of how closure conversion is implemented. T is an ir.Node or Node.
abstract class ClosureData {
/// Deserializes a [ClosureData] object from [source].
factory ClosureData.readFromDataSource(
JsToElementMap elementMap, DataSource source) =
ClosureDataImpl.readFromDataSource;

/// Serializes this [ClosureData] to [sink].
void writeToDataSink(DataSink sink);

/// Look up information about the variables that have been mutated and are
/// used inside the scope of [node].
ScopeInfo getScopeInfo(MemberEntity member);
Expand All @@ -32,6 +43,14 @@ abstract class ClosureData {
CapturedScope getCapturedScope(MemberEntity entity);
}

/// Enum used for identifying [ScopeInfo] subclasses in serialization.
enum ScopeInfoKind {
scopeInfo,
capturedScope,
capturedLoopScope,
closureRepresentationInfo,
}

/// Class that represents one level of scoping information, whether this scope
/// is a closure or not. This is specifically used to store information
/// about the usage of variables in try or sync blocks, because they need to be
Expand All @@ -44,6 +63,27 @@ abstract class ClosureData {
class ScopeInfo {
const ScopeInfo();

/// Deserializes a [ScopeInfo] object from [source].
factory ScopeInfo.readFromDataSource(DataSource source) {
ScopeInfoKind kind = source.readEnum(ScopeInfoKind.values);
switch (kind) {
case ScopeInfoKind.scopeInfo:
return new JsScopeInfo.readFromDataSource(source);
case ScopeInfoKind.capturedScope:
return new JsCapturedScope.readFromDataSource(source);
case ScopeInfoKind.capturedLoopScope:
return new JsCapturedLoopScope.readFromDataSource(source);
case ScopeInfoKind.closureRepresentationInfo:
return new KernelClosureClassInfo.readFromDataSource(source);
}
throw new UnsupportedError('Unexpected ScopeInfoKind $kind');
}

/// Serializes this [ScopeInfo] to [sink].
void writeToDataSink(DataSink sink) {
throw new UnsupportedError('${runtimeType}.writeToDataSink');
}

/// Convenience reference pointer to the element representing `this`.
/// If this scope is not in an instance member, it will be null.
Local get thisLocal => null;
Expand Down Expand Up @@ -81,6 +121,21 @@ class ScopeInfo {
class CapturedScope extends ScopeInfo {
const CapturedScope();

/// Deserializes a [CapturedScope] object from [source].
factory CapturedScope.readFromDataSource(DataSource source) {
ScopeInfoKind kind = source.readEnum(ScopeInfoKind.values);
switch (kind) {
case ScopeInfoKind.scopeInfo:
case ScopeInfoKind.closureRepresentationInfo:
throw new UnsupportedError('Unexpected CapturedScope kind $kind');
case ScopeInfoKind.capturedScope:
return new JsCapturedScope.readFromDataSource(source);
case ScopeInfoKind.capturedLoopScope:
return new JsCapturedLoopScope.readFromDataSource(source);
}
throw new UnsupportedError('Unexpected ScopeInfoKind $kind');
}

/// If true, this closure accesses a variable that was defined in an outside
/// scope and this variable gets modified at some point (sometimes we say that
/// variable has been "captured"). In this situation, access to this variable
Expand Down Expand Up @@ -113,6 +168,20 @@ class CapturedScope extends ScopeInfo {
class CapturedLoopScope extends CapturedScope {
const CapturedLoopScope();

/// Deserializes a [CapturedLoopScope] object from [source].
factory CapturedLoopScope.readFromDataSource(DataSource source) {
ScopeInfoKind kind = source.readEnum(ScopeInfoKind.values);
switch (kind) {
case ScopeInfoKind.scopeInfo:
case ScopeInfoKind.closureRepresentationInfo:
case ScopeInfoKind.capturedScope:
throw new UnsupportedError('Unexpected CapturedLoopScope kind $kind');
case ScopeInfoKind.capturedLoopScope:
return new JsCapturedLoopScope.readFromDataSource(source);
}
throw new UnsupportedError('Unexpected ScopeInfoKind $kind');
}

/// True if this loop scope declares in the first part of the loop
/// `for (<here>;...;...)` any variables that need to be boxed.
bool get hasBoxedLoopVariables => false;
Expand Down Expand Up @@ -166,6 +235,21 @@ class CapturedLoopScope extends CapturedScope {
class ClosureRepresentationInfo extends ScopeInfo {
const ClosureRepresentationInfo();

/// Deserializes a [ClosureRepresentationInfo] object from [source].
factory ClosureRepresentationInfo.readFromDataSource(DataSource source) {
ScopeInfoKind kind = source.readEnum(ScopeInfoKind.values);
switch (kind) {
case ScopeInfoKind.scopeInfo:
case ScopeInfoKind.capturedScope:
case ScopeInfoKind.capturedLoopScope:
throw new UnsupportedError(
'Unexpected ClosureRepresentationInfo kind $kind');
case ScopeInfoKind.closureRepresentationInfo:
return new KernelClosureClassInfo.readFromDataSource(source);
}
throw new UnsupportedError('Unexpected ScopeInfoKind $kind');
}

/// The original local function before any translation.
///
/// Will be null for methods.
Expand Down
3 changes: 2 additions & 1 deletion pkg/compiler/lib/src/compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ abstract class Compiler {
void generateJavaScriptCode(
GlobalTypeInferenceResults globalInferenceResults) {
JClosedWorld closedWorld = globalInferenceResults.closedWorld;
backendStrategy.registerJClosedWorld(closedWorld);
FunctionEntity mainFunction = closedWorld.elementEnvironment.mainFunction;
reporter.log('Compiling...');
phase = PHASE_COMPILING;
Expand Down Expand Up @@ -402,7 +403,7 @@ abstract class Compiler {
enqueuer.createCodegenEnqueuer(closedWorld, globalInferenceResults);
_codegenWorldBuilder = codegenEnqueuer.worldBuilder;
codegenEnqueuer.applyImpact(backend.onCodegenStart(
closedWorld, _codegenWorldBuilder, backendStrategy.sorter));
closedWorld, _codegenWorldBuilder, closedWorld.sorter));
return codegenEnqueuer;
}

Expand Down
87 changes: 87 additions & 0 deletions pkg/compiler/lib/src/deferred_load.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import 'elements/types.dart';
import 'elements/entities.dart';
import 'kernel/kelements.dart' show KLocalFunction;
import 'library_loader.dart';
import 'serialization/serialization.dart';
import 'universe/use.dart';
import 'universe/world_impact.dart'
show ImpactUseCase, WorldImpact, WorldImpactVisitorImpl;
Expand Down Expand Up @@ -1271,6 +1272,10 @@ class ConstantWorkItem extends WorkItem {
// TODO(sigmund): consider moving here every piece of data used as a result of
// deferred loading (including hunksToLoad, etc).
class OutputUnitData {
/// Tag used for identifying serialized [OutputUnitData] objects in a
/// debugging data stream.
static const String tag = 'output-unit-data';

final bool isProgramSplit;
final OutputUnit mainOutputUnit;
final Map<ClassEntity, OutputUnit> _classToUnit;
Expand All @@ -1288,6 +1293,7 @@ class OutputUnitData {
this._constantToUnit,
this.outputUnits);

// Creates J-world data from the K-world data.
OutputUnitData.from(
OutputUnitData other,
Map<ClassEntity, OutputUnit> Function(
Expand All @@ -1305,9 +1311,90 @@ class OutputUnitData {
convertMemberMap(other._memberToUnit, other._localFunctionToUnit),
_classToUnit =
convertClassMap(other._classToUnit, other._localFunctionToUnit),
// Local functions only make sense in the K-world model.
_localFunctionToUnit = const <Local, OutputUnit>{},
_constantToUnit = convertConstantMap(other._constantToUnit);

/// Deserializes an [OutputUnitData] object from [source].
factory OutputUnitData.readFromDataSource(DataSource source) {
source.begin(tag);
bool isProgramSplit = source.readBool();
List<ImportEntity> imports = source.readList(() {
String name = source.readString();
Uri uri = source.readUri();
Uri enclosingLibraryUri = source.readUri();
bool isDeferred = source.readBool();
return new ImportEntity(isDeferred, name, uri, enclosingLibraryUri);
});
List<OutputUnit> outputUnits = source.readList(() {
bool isMainOutput = source.readBool();
String name = source.readString();
Set<ImportEntity> importSet = source.readList(() {
return imports[source.readInt()];
}).toSet();
return new OutputUnit(isMainOutput, name, importSet);
});
OutputUnit mainOutputUnit = outputUnits[source.readInt()];

Map<ClassEntity, OutputUnit> classToUnit = source.readClassMap(() {
return outputUnits[source.readInt()];
});
Map<MemberEntity, OutputUnit> memberToUnit = source.readMemberMap(() {
return outputUnits[source.readInt()];
});
Map<ConstantValue, OutputUnit> constantToUnit = source.readConstantMap(() {
return outputUnits[source.readInt()];
});
source.end(tag);
return new OutputUnitData(
isProgramSplit,
mainOutputUnit,
classToUnit,
memberToUnit,
// Local functions only make sense in the K-world model.
const <Local, OutputUnit>{},
constantToUnit,
outputUnits);
}

/// Serializes this [OutputUnitData] to [sink].
void writeToDataSink(DataSink sink) {
sink.begin(tag);
sink.writeBool(isProgramSplit);
Map<ImportEntity, int> importIndex = {};
for (OutputUnit outputUnit in outputUnits) {
for (ImportEntity import in outputUnit._imports) {
importIndex[import] ??= importIndex.length;
}
}
sink.writeList(importIndex.keys, (ImportEntity import) {
sink.writeString(import.name);
sink.writeUri(import.uri);
sink.writeUri(import.enclosingLibraryUri);
sink.writeBool(import.isDeferred);
});
Map<OutputUnit, int> outputUnitIndices = {};
sink.writeList(outputUnits, (OutputUnit outputUnit) {
outputUnitIndices[outputUnit] = outputUnitIndices.length;
sink.writeBool(outputUnit.isMainOutput);
sink.writeString(outputUnit.name);
sink.writeList(outputUnit._imports, (ImportEntity import) {
sink.writeInt(importIndex[import]);
});
});
sink.writeInt(outputUnitIndices[mainOutputUnit]);
sink.writeClassMap(_classToUnit, (OutputUnit outputUnit) {
sink.writeInt(outputUnitIndices[outputUnit]);
});
sink.writeMemberMap(_memberToUnit, (OutputUnit outputUnit) {
sink.writeInt(outputUnitIndices[outputUnit]);
});
sink.writeConstantMap(_constantToUnit, (OutputUnit outputUnit) {
sink.writeInt(outputUnitIndices[outputUnit]);
});
sink.end(tag);
}

/// Returns the [OutputUnit] where [cls] belongs.
OutputUnit outputUnitForClass(ClassEntity cls) {
if (!isProgramSplit) return mainOutputUnit;
Expand Down
27 changes: 27 additions & 0 deletions pkg/compiler/lib/src/elements/entities.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ library entities;
import 'package:front_end/src/api_unstable/dart2js.dart' show AsyncModifier;

import '../common.dart';
import '../serialization/serialization.dart';
import '../universe/call_structure.dart' show CallStructure;
import '../util/util.dart';
import 'names.dart';
Expand Down Expand Up @@ -258,6 +259,10 @@ abstract class Local extends Entity {}

/// The structure of function parameters.
class ParameterStructure {
/// Tag used for identifying serialized [ParameterStructure] objects in a
/// debugging data stream.
static const String tag = 'parameter-structure';

/// The number of required (positional) parameters.
final int requiredParameters;

Expand Down Expand Up @@ -285,6 +290,28 @@ class ParameterStructure {
type.typeVariables.length);
}

/// Deserializes a [ParameterStructure] object from [source].
factory ParameterStructure.readFromDataSource(DataSource source) {
source.begin(tag);
int requiredParameters = source.readInt();
int positionalParameters = source.readInt();
List<String> namedParameters = source.readStrings();
int typeParameters = source.readInt();
source.end(tag);
return new ParameterStructure(requiredParameters, positionalParameters,
namedParameters, typeParameters);
}

/// Serializes this [ParameterStructure] to [sink].
void writeToDataSink(DataSink sink) {
sink.begin(tag);
sink.writeInt(requiredParameters);
sink.writeInt(positionalParameters);
sink.writeStrings(namedParameters);
sink.writeInt(typeParameters);
sink.end(tag);
}

/// The number of optional parameters (positional or named).
int get optionalParameters =>
positionalParameters - requiredParameters + namedParameters.length;
Expand Down
Loading

0 comments on commit 8b6f896

Please sign in to comment.