Use cached serialization of source-information

This reduces the size of the serialized codegen data by 20-25%

Change-Id: I85424576d68b72a56c3c924fe704a086000bc926
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103534
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/common/codegen.dart b/pkg/compiler/lib/src/common/codegen.dart
index 928e70d..f7c4050 100644
--- a/pkg/compiler/lib/src/common/codegen.dart
+++ b/pkg/compiler/lib/src/common/codegen.dart
@@ -1096,7 +1096,10 @@
   }
 
   void _writeInfo(js.Node node) {
-    SourceInformation.writeToDataSink(sink, node.sourceInformation);
+    sink.writeCached<SourceInformation>(node.sourceInformation,
+        (SourceInformation sourceInformation) {
+      SourceInformation.writeToDataSink(sink, sourceInformation);
+    });
   }
 
   @override
@@ -2091,7 +2094,9 @@
         break;
     }
     SourceInformation sourceInformation =
-        SourceInformation.readFromDataSource(source);
+        source.readCached<SourceInformation>(() {
+      return SourceInformation.readFromDataSource(source);
+    });
     if (sourceInformation != null) {
       node = node.withSourceInformation(sourceInformation);
     }
diff --git a/pkg/compiler/lib/src/io/position_information.dart b/pkg/compiler/lib/src/io/position_information.dart
index a92ab7e..cb1c2a4 100644
--- a/pkg/compiler/lib/src/io/position_information.dart
+++ b/pkg/compiler/lib/src/io/position_information.dart
@@ -12,6 +12,7 @@
 import '../js/js_debug.dart';
 import '../js/js_source_mapping.dart';
 import '../serialization/serialization.dart';
+import '../util/util.dart';
 import 'code_output.dart' show BufferedCodeOutput;
 import 'source_information.dart';
 
@@ -34,8 +35,10 @@
 
   factory PositionSourceInformation.readFromDataSource(DataSource source) {
     source.begin(tag);
-    SourceLocation startPosition = SourceLocation.readFromDataSource(source);
-    SourceLocation innerPosition = SourceLocation.readFromDataSource(source);
+    SourceLocation startPosition = source.readCached<SourceLocation>(
+        () => SourceLocation.readFromDataSource(source));
+    SourceLocation innerPosition = source.readCached<SourceLocation>(
+        () => SourceLocation.readFromDataSource(source));
     List<FrameContext> inliningContext = source.readList(
         () => FrameContext.readFromDataSource(source),
         emptyAsNull: true);
@@ -46,8 +49,14 @@
 
   void writeToDataSinkInternal(DataSink sink) {
     sink.begin(tag);
-    SourceLocation.writeToDataSink(sink, startPosition);
-    SourceLocation.writeToDataSink(sink, innerPosition);
+    sink.writeCached(
+        startPosition,
+        (SourceLocation sourceLocation) =>
+            SourceLocation.writeToDataSink(sink, sourceLocation));
+    sink.writeCached(
+        innerPosition,
+        (SourceLocation sourceLocation) =>
+            SourceLocation.writeToDataSink(sink, sourceLocation));
     sink.writeList(inliningContext,
         (FrameContext context) => context.writeToDataSink(sink),
         allowNull: true);
@@ -77,16 +86,17 @@
 
   @override
   int get hashCode {
-    return 0x7FFFFFFF &
-        (startPosition.hashCode * 17 + innerPosition.hashCode * 19);
+    return Hashing.listHash(
+        inliningContext, Hashing.objectsHash(startPosition, innerPosition));
   }
 
   @override
   bool operator ==(other) {
     if (identical(this, other)) return true;
-    if (other is! PositionSourceInformation) return false;
-    return startPosition == other.startPosition &&
-        innerPosition == other.innerPosition;
+    return other is PositionSourceInformation &&
+        startPosition == other.startPosition &&
+        innerPosition == other.innerPosition &&
+        equalElements(inliningContext, other.inliningContext);
   }
 
   /// Create a textual representation of the source information using [uriText]
diff --git a/pkg/compiler/lib/src/io/source_information.dart b/pkg/compiler/lib/src/io/source_information.dart
index bc8f6e7..0034fb5 100644
--- a/pkg/compiler/lib/src/io/source_information.dart
+++ b/pkg/compiler/lib/src/io/source_information.dart
@@ -84,8 +84,8 @@
 
   factory FrameContext.readFromDataSource(DataSource source) {
     source.begin(tag);
-    SourceInformation callInformation =
-        SourceInformation.readFromDataSource(source);
+    SourceInformation callInformation = source.readCached<SourceInformation>(
+        () => SourceInformation.readFromDataSource(source));
     String inlinedMethodName = source.readString();
     source.end(tag);
     return new FrameContext(callInformation, inlinedMethodName);
@@ -93,7 +93,10 @@
 
   void writeToDataSink(DataSink sink) {
     sink.begin(tag);
-    SourceInformation.writeToDataSink(sink, callInformation);
+    sink.writeCached<SourceInformation>(
+        callInformation,
+        (SourceInformation sourceInformation) =>
+            SourceInformation.writeToDataSink(sink, sourceInformation));
     sink.writeString(inlinedMethodName);
     sink.end(tag);
   }
@@ -317,8 +320,8 @@
   @override
   bool operator ==(other) {
     if (identical(this, other)) return true;
-    if (other is! SourceLocation) return false;
-    return sourceUri == other.sourceUri &&
+    return other is SourceLocation &&
+        sourceUri == other.sourceUri &&
         offset == other.offset &&
         sourceName == other.sourceName;
   }
diff --git a/pkg/compiler/lib/src/serialization/abstract_sink.dart b/pkg/compiler/lib/src/serialization/abstract_sink.dart
index 2015a3a..a1af8e8 100644
--- a/pkg/compiler/lib/src/serialization/abstract_sink.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_sink.dart
@@ -37,7 +37,9 @@
   EntityWriter _entityWriter = const EntityWriter();
   CodegenWriter _codegenWriter;
 
-  AbstractDataSink({this.useDataKinds: false}) {
+  final Map<String, int> tagFrequencyMap;
+
+  AbstractDataSink({this.useDataKinds: false, this.tagFrequencyMap}) {
     _dartTypeWriter = new DartTypeWriter(this);
     _dartTypeNodeWriter = new DartTypeNodeWriter(this);
     _stringIndex = new IndexedSink<String>(this);
@@ -48,6 +50,10 @@
 
   @override
   void begin(String tag) {
+    if (tagFrequencyMap != null) {
+      tagFrequencyMap[tag] ??= 0;
+      tagFrequencyMap[tag]++;
+    }
     if (useDataKinds) {
       _tags ??= <String>[];
       _tags.add(tag);
diff --git a/pkg/compiler/lib/src/serialization/binary_sink.dart b/pkg/compiler/lib/src/serialization/binary_sink.dart
index 3f771b4..6d46fb4 100644
--- a/pkg/compiler/lib/src/serialization/binary_sink.dart
+++ b/pkg/compiler/lib/src/serialization/binary_sink.dart
@@ -12,9 +12,10 @@
   BufferedSink _bufferedSink;
   int _length = 0;
 
-  BinarySink(this.sink, {bool useDataKinds: false})
+  BinarySink(this.sink,
+      {bool useDataKinds: false, Map<String, int> tagFrequencyMap})
       : _bufferedSink = new BufferedSink(sink),
-        super(useDataKinds: useDataKinds);
+        super(useDataKinds: useDataKinds, tagFrequencyMap: tagFrequencyMap);
 
   @override
   void _begin(String tag) {
diff --git a/pkg/compiler/lib/src/serialization/helpers.dart b/pkg/compiler/lib/src/serialization/helpers.dart
index c0c8c29..b73a8c9 100644
--- a/pkg/compiler/lib/src/serialization/helpers.dart
+++ b/pkg/compiler/lib/src/serialization/helpers.dart
@@ -336,6 +336,7 @@
 class IndexedSink<E> {
   final AbstractDataSink _sink;
   final Map<E, int> _cache = {};
+  Set<E> _current = {};
 
   IndexedSink(this._sink);
 
@@ -346,10 +347,14 @@
   void write(E value, void writeValue(E value)) {
     int index = _cache[value];
     if (index == null) {
+      if (!_current.add(value)) {
+        throw new ArgumentError("Cyclic dependency on cached value: $value");
+      }
       index = _cache.length;
-      _cache[value] = index;
       _sink._writeIntInternal(index);
       writeValue(value);
+      _cache[value] = index;
+      _current.remove(value);
     } else {
       _sink._writeIntInternal(index);
     }
diff --git a/pkg/compiler/lib/src/serialization/object_sink.dart b/pkg/compiler/lib/src/serialization/object_sink.dart
index 31376e3..82c8885 100644
--- a/pkg/compiler/lib/src/serialization/object_sink.dart
+++ b/pkg/compiler/lib/src/serialization/object_sink.dart
@@ -11,8 +11,8 @@
 class ObjectSink extends AbstractDataSink {
   List<dynamic> _data;
 
-  ObjectSink(this._data, {bool useDataKinds})
-      : super(useDataKinds: useDataKinds);
+  ObjectSink(this._data, {bool useDataKinds, Map<String, int> tagFrequencyMap})
+      : super(useDataKinds: useDataKinds, tagFrequencyMap: tagFrequencyMap);
 
   @override
   void _begin(String tag) {
diff --git a/pkg/compiler/tool/modular_dart2js.dart b/pkg/compiler/tool/modular_dart2js.dart
index e313a9d..f8e15e3 100644
--- a/pkg/compiler/tool/modular_dart2js.dart
+++ b/pkg/compiler/tool/modular_dart2js.dart
@@ -9,19 +9,25 @@
 main(List<String> args) async {
   Stopwatch stopwatch = new Stopwatch();
   String input;
+  String serializedInput;
   String output = 'out.js';
   List<String> arguments = [];
   int start = 0;
+  int stop = 3;
   int shards;
   bool enableAssertions = false;
   for (String arg in args) {
     if (arg.startsWith('-')) {
       if (arg.startsWith('--start=')) {
         start = int.parse(arg.substring('--start='.length));
+      } else if (arg.startsWith('--stop=')) {
+        stop = int.parse(arg.substring('--stop='.length));
       } else if (arg.startsWith('--shards=')) {
         shards = int.parse(arg.substring('--shards='.length));
       } else if (arg == '-ea' || arg == '--enable_asserts') {
         enableAssertions = true;
+      } else if (arg.startsWith('--in=')) {
+        serializedInput = arg.substring('--in='.length);
       } else if (arg.startsWith('-o')) {
         output = arg.substring('-o'.length);
       } else if (arg.startsWith('--out=')) {
@@ -43,6 +49,12 @@
     exit(-1);
   }
 
+  serializedInput ??= output;
+
+  String inputPrefix = serializedInput;
+  if (serializedInput.endsWith('.js')) {
+    inputPrefix = output.substring(0, output.length - '.js'.length);
+  }
   String outputPrefix = output;
   if (output.endsWith('.js')) {
     outputPrefix = output.substring(0, output.length - '.js'.length);
@@ -55,18 +67,18 @@
   baseOptions.add('package:compiler/src/dart2js.dart');
   baseOptions.addAll(arguments);
 
-  String cfeOutput = '${outputPrefix}0.dill';
-  String dillOutput = '${outputPrefix}.dill';
-  String dataOutput = '${outputPrefix}.dill.data';
+  String cfeOutput = '${inputPrefix}0.dill';
+  String dillOutput = '${inputPrefix}.dill';
+  String dataOutput = '${inputPrefix}.dill.data';
   String codeOutput = '${outputPrefix}.code';
   shards ??= 2;
 
   stopwatch.start();
-  if (start <= 0) {
+  if (start <= 0 && stop >= 0) {
     await subProcess(
         baseOptions, [input, Flags.cfeOnly, '--out=$cfeOutput'], '0:\t');
   }
-  if (start <= 1) {
+  if (start <= 1 && stop >= 1) {
     await subProcess(
         baseOptions,
         [cfeOutput, '--out=$dillOutput', '${Flags.writeData}=${dataOutput}'],
@@ -78,7 +90,7 @@
         [dillOutput, '${Flags.readData}=${dataOutput}', '--out=${output}'],
         '3:\t');
   } else {
-    if (start <= 2) {
+    if (start <= 2 && stop >= 2) {
       List<List<String>> additionalArguments = [];
       List<String> outputPrefixes = [];
       for (int shard = 0; shard < shards; shard++) {
@@ -101,7 +113,7 @@
       subwatch.stop();
       print('2:\tTotal time: ${_formatMs(subwatch.elapsedMilliseconds)}');
     }
-    if (start <= 3) {
+    if (start <= 3 && stop >= 3) {
       await subProcess(
           baseOptions,
           [