[flow analysis] Don't create a new empty map for every FlowModel

Currently every FlowModel creates a new empty map that's just supposed
to be empty. When compiling `compile.dart` (from the CFE) this creates
more than 200,000 maps for seemingly no reason.

This CL removes it, thus saving the creation of (...instrumenting the
platform...) 229,472 maps when compiling `compile.dart` (from the CFE).

Thinking it was done to avoid some polymorphism I have gone over the
created flowgraphs for the file in both JIT and AOT and found only
improvements.

AOT compiled `compile.dart` then compiling itself improves by ~3.5%:

```
Difference at 95.0% confidence
        -0.1365 +/- 0.0576111
        -3.55191% +/- 1.49912%
        (Student's t, pooled s = 0.090011)
```

Change-Id: Ifdbb57e9aa3c23b2af512a2104aaf6caf72831ef
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/301061
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index 1691a69..d277196 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -1900,9 +1900,6 @@
   /// [_FlowAnalysisImpl._promotionKeyStore].
   final Map<int, VariableModel<Type> /*!*/ > variableInfo;
 
-  /// The empty map, used to [join] variables.
-  final Map<int, VariableModel<Type>> _emptyVariableMap = {};
-
   /// Creates a state object with the given [reachable] status.  All variables
   /// are assumed to be unpromoted and already assigned, so joining another
   /// state with this one will have no effect on it.
@@ -2493,7 +2490,6 @@
     TypeOperations<Type> typeOperations,
     FlowModel<Type>? first,
     FlowModel<Type>? second,
-    Map<int, VariableModel<Type>> emptyVariableMap,
   ) {
     if (first == null) return second!;
     if (second == null) return first;
@@ -2511,10 +2507,7 @@
     Reachability newReachable =
         Reachability.join(first.reachable, second.reachable);
     Map<int, VariableModel<Type>> newVariableInfo = FlowModel.joinVariableInfo(
-        typeOperations,
-        first.variableInfo,
-        second.variableInfo,
-        emptyVariableMap);
+        typeOperations, first.variableInfo, second.variableInfo);
 
     return FlowModel._identicalOrNew(
         first, second, newReachable, newVariableInfo);
@@ -2526,13 +2519,13 @@
     TypeOperations<Type> typeOperations,
     Map<int, VariableModel<Type>> first,
     Map<int, VariableModel<Type>> second,
-    Map<int, VariableModel<Type>> emptyMap,
   ) {
     if (identical(first, second)) return first;
     if (first.isEmpty || second.isEmpty) {
-      return emptyMap;
+      return const {};
     }
 
+    // TODO(jensj): How often is this empty?
     Map<int, VariableModel<Type>> result = <int, VariableModel<Type>>{};
     bool alwaysFirst = true;
     bool alwaysSecond = true;
@@ -2552,7 +2545,7 @@
 
     if (alwaysFirst) return first;
     if (alwaysSecond && result.length == second.length) return second;
-    if (result.isEmpty) return emptyMap;
+    if (result.isEmpty) return const {};
     return result;
   }
 
@@ -2562,7 +2555,6 @@
     TypeOperations<Type> typeOperations,
     FlowModel<Type>? first,
     FlowModel<Type>? second,
-    Map<int, VariableModel<Type>> emptyVariableMap,
   ) {
     if (first == null) return second!.unsplit();
     if (second == null) return first.unsplit();
@@ -2580,10 +2572,7 @@
     Reachability newReachable =
         Reachability.join(first.reachable, second.reachable).unsplit();
     Map<int, VariableModel<Type>> newVariableInfo = FlowModel.joinVariableInfo(
-        typeOperations,
-        first.variableInfo,
-        second.variableInfo,
-        emptyVariableMap);
+        typeOperations, first.variableInfo, second.variableInfo);
 
     return FlowModel._identicalOrNew(
         first, second, newReachable, newVariableInfo);
@@ -5153,7 +5142,7 @@
   }
 
   FlowModel<Type> _join(FlowModel<Type>? first, FlowModel<Type>? second) =>
-      FlowModel.join(operations, first, second, _current._emptyVariableMap);
+      FlowModel.join(operations, first, second);
 
   /// Creates a promotion key representing a temporary variable that doesn't
   /// correspond to any variable in the user's source code.  This is used by
@@ -5174,7 +5163,7 @@
   }
 
   FlowModel<Type> _merge(FlowModel<Type> first, FlowModel<Type>? second) =>
-      FlowModel.merge(operations, first, second, _current._emptyVariableMap);
+      FlowModel.merge(operations, first, second);
 
   /// Computes an updated flow model representing the result of a null check
   /// performed by a pattern.  The returned flow model represents what is known
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
index 0ea0ece..413722c 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
@@ -4613,7 +4613,6 @@
     var intType = Type('int');
     var intQType = Type('int?');
     var stringType = Type('String');
-    const emptyMap = const <int, VariableModel<Type>>{};
 
     setUp(() {
       x = h.promotionKeyStore.keyForVariable(Var('x')..type = Type('Object?'));
@@ -4641,7 +4640,7 @@
           x: model(null),
           y: model([intType])
         };
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2, emptyMap), {
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2), {
           x: _matchVariableModel(chain: null, ofInterest: ['int']),
           y: _matchVariableModel(chain: null, ofInterest: ['int'])
         });
@@ -4653,8 +4652,7 @@
           x: model([intType]),
           y: model([stringType])
         };
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p, p, emptyMap),
-            same(p));
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p, p), same(p));
       });
 
       test('one input empty', () {
@@ -4663,10 +4661,11 @@
           y: model([stringType])
         };
         var p2 = <int, VariableModel<Type>>{};
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2, emptyMap),
-            same(emptyMap));
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1, emptyMap),
-            same(emptyMap));
+        const expected = const <int, VariableModel<Never>>{};
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2),
+            same(expected));
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1),
+            same(expected));
       });
 
       test('promoted with unpromoted', () {
@@ -4677,10 +4676,8 @@
         var expected = {
           x: _matchVariableModel(chain: null, ofInterest: ['int'])
         };
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2, emptyMap),
-            expected);
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1, emptyMap),
-            expected);
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2), expected);
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1), expected);
       });
 
       test('related type chains', () {
@@ -4693,10 +4690,8 @@
         var expected = {
           x: _matchVariableModel(chain: ['int?'], ofInterest: ['int?', 'int'])
         };
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2, emptyMap),
-            expected);
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1, emptyMap),
-            expected);
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2), expected);
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1), expected);
       });
 
       test('unrelated type chains', () {
@@ -4709,10 +4704,8 @@
         var expected = {
           x: _matchVariableModel(chain: null, ofInterest: ['String', 'int'])
         };
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2, emptyMap),
-            expected);
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1, emptyMap),
-            expected);
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2), expected);
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1), expected);
       });
 
       test('sub-map', () {
@@ -4722,10 +4715,8 @@
           y: model([stringType])
         };
         var p2 = {x: xModel};
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2, emptyMap),
-            same(p2));
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1, emptyMap),
-            same(p2));
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2), same(p2));
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1), same(p2));
       });
 
       test('sub-map with matched subtype', () {
@@ -4739,10 +4730,8 @@
         var expected = {
           x: _matchVariableModel(chain: ['int?'], ofInterest: ['int?', 'int'])
         };
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2, emptyMap),
-            expected);
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1, emptyMap),
-            expected);
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2), expected);
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1), expected);
       });
 
       test('sub-map with mismatched subtype', () {
@@ -4756,10 +4745,8 @@
         var expected = {
           x: _matchVariableModel(chain: ['int?'], ofInterest: ['int?', 'int'])
         };
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2, emptyMap),
-            expected);
-        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1, emptyMap),
-            expected);
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p1, p2), expected);
+        expect(FlowModel.joinVariableInfo(h.typeOperations, p2, p1), expected);
       });
 
       test('assigned', () {
@@ -4767,8 +4754,7 @@
         var assigned = model(null, assigned: true);
         var p1 = {x: assigned, y: assigned, z: unassigned, w: unassigned};
         var p2 = {x: assigned, y: unassigned, z: assigned, w: unassigned};
-        var joined =
-            FlowModel.joinVariableInfo(h.typeOperations, p1, p2, emptyMap);
+        var joined = FlowModel.joinVariableInfo(h.typeOperations, p1, p2);
         expect(joined, {
           x: same(assigned),
           y: _matchVariableModel(
@@ -4794,8 +4780,7 @@
           z: writeCapturedModel,
           w: intQModel
         };
-        var joined =
-            FlowModel.joinVariableInfo(h.typeOperations, p1, p2, emptyMap);
+        var joined = FlowModel.joinVariableInfo(h.typeOperations, p1, p2);
         expect(joined, {
           x: same(writeCapturedModel),
           y: same(writeCapturedModel),
@@ -4827,7 +4812,7 @@
 
     test('first is null', () {
       var s1 = FlowModel.withInfo(Reachability.initial.split(), emptyMap);
-      var result = FlowModel.merge(h.typeOperations, null, s1, emptyMap);
+      var result = FlowModel.merge(h.typeOperations, null, s1);
       expect(result.reachable, same(Reachability.initial));
     });
 
@@ -4835,7 +4820,7 @@
       var splitPoint = Reachability.initial.split();
       var afterSplit = splitPoint.split();
       var s1 = FlowModel.withInfo(afterSplit, emptyMap);
-      var result = FlowModel.merge(h.typeOperations, s1, null, emptyMap);
+      var result = FlowModel.merge(h.typeOperations, s1, null);
       expect(result.reachable, same(splitPoint));
     });
 
@@ -4848,7 +4833,7 @@
       var s2 = FlowModel.withInfo(afterSplit, {
         x: varModel([stringType])
       });
-      var result = FlowModel.merge(h.typeOperations, s1, s2, emptyMap);
+      var result = FlowModel.merge(h.typeOperations, s1, s2);
       expect(result.reachable, same(splitPoint));
       expect(result.variableInfo[x]!.promotedTypes, isNull);
     });
@@ -4862,7 +4847,7 @@
       var s2 = FlowModel.withInfo(afterSplit, {
         x: varModel([stringType])
       });
-      var result = FlowModel.merge(h.typeOperations, s1, s2, emptyMap);
+      var result = FlowModel.merge(h.typeOperations, s1, s2);
       expect(result.reachable, same(splitPoint));
       expect(result.variableInfo, same(s2.variableInfo));
     });
@@ -4876,7 +4861,7 @@
       var s2 = FlowModel.withInfo(afterSplit.setUnreachable(), {
         x: varModel([stringType])
       });
-      var result = FlowModel.merge(h.typeOperations, s1, s2, emptyMap);
+      var result = FlowModel.merge(h.typeOperations, s1, s2);
       expect(result.reachable, same(splitPoint));
       expect(result.variableInfo, same(s1.variableInfo));
     });
@@ -4890,7 +4875,7 @@
       var s2 = FlowModel.withInfo(afterSplit.setUnreachable(), {
         x: varModel([stringType])
       });
-      var result = FlowModel.merge(h.typeOperations, s1, s2, emptyMap);
+      var result = FlowModel.merge(h.typeOperations, s1, s2);
       expect(result.reachable.locallyReachable, false);
       expect(result.reachable.parent, same(splitPoint.parent));
       expect(result.variableInfo[x]!.promotedTypes, isNull);