[CFE] Cache checkInheritanceConflict calculation

When compiling a big internal app with the NNBD SDK there's a big time
regression. This is a first "poor-mans fix" that "fixes" it via caching
which probably is not the best solution, but does the trick.

Without change
==============

Without NNBD platform:

$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart --target=dart2js --platform=out/ReleaseX64/dart2js_platform.dill -v bigapp.dart
[...]
0:00:14.238352: Built class hierarchy in 1663ms.
[...]
0:00:55.871356: Wrote component to bigapp.dart.dill in 5690ms.


With NNBD platform:

$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart --target=dart2js --platform=out/ReleaseX64NNBD/dart2js_platform.dill -v bigapp.dart
[...]
0:04:41.842548: Built class hierarchy in 268716ms.
[...]
0:05:22.120455: Wrote component to bigapp.dart.dill in 5407ms.

So: Big regression; it takes almost 4.5 minutes to built the class
hierarchy and almost as long extra to compile as a whole.


With change
===========

Without NNBD platform:

$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart --target=dart2js --platform=out/ReleaseX64/dart2js_platform.dill -v bigapp.dart
[...]
0:00:15.255893: Built class hierarchy in 2175ms.
[...]
0:00:55.203753: Wrote component to bigapp.dart.dill in 5306ms.

So: Without the NNBD platform there's basically no change.


With NNBD platform:

$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart --target=dart2js --platform=out/ReleaseX64NNBD/dart2js_platform.dill -v bigapp.dart
[...]
0:00:15.864901: Built class hierarchy in 2574ms.
[...]
0:00:55.640201: Wrote component to bigapp.dart.dill in 5363ms.

So: With the NNBD platform, the speedup is significant and there's basically no
change between using the NNBD platform or not.
Change-Id: I74993710a34f08f69421b03f2682ff8418af7599
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/141982
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Jens Johansen <jensj@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
index c080b03..71e1485de 100644
--- a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
@@ -340,6 +340,9 @@
 
   Types types;
 
+  Map<ClassMember, Map<ClassMember, ClassMember>> inheritanceConflictCache =
+      new Map.identity();
+
   ClassHierarchyBuilder(this.objectClassBuilder, this.loader, this.coreTypes)
       : objectClass = objectClassBuilder.cls,
         futureClass = coreTypes.futureClass,
@@ -354,6 +357,7 @@
     substitutions.clear();
     _overrideChecks.clear();
     _delayedTypeComputations.clear();
+    inheritanceConflictCache.clear();
   }
 
   void registerDelayedTypeComputation(DelayedTypeComputation computation) {
@@ -640,12 +644,18 @@
       classBuilder.library.loader == hierarchy.loader;
 
   ClassMember checkInheritanceConflict(ClassMember a, ClassMember b) {
+    hierarchy.inheritanceConflictCache[a] ??= new Map.identity();
+    if (hierarchy.inheritanceConflictCache[a].containsKey(b)) {
+      return hierarchy.inheritanceConflictCache[a][b];
+    }
+
     if (a.hasDeclarations) {
       ClassMember result;
       for (int i = 0; i < a.declarations.length; i++) {
         ClassMember d = checkInheritanceConflict(a.declarations[i], b);
         result ??= d;
       }
+      hierarchy.inheritanceConflictCache[a][b] = result;
       return result;
     }
     if (b.hasDeclarations) {
@@ -654,12 +664,15 @@
         ClassMember d = checkInheritanceConflict(a, b.declarations[i]);
         result ??= d;
       }
+      hierarchy.inheritanceConflictCache[a][b] = result;
       return result;
     }
     if (isInheritanceConflict(a, b)) {
       reportInheritanceConflict(a, b);
+      hierarchy.inheritanceConflictCache[a][b] = a;
       return a;
     }
+    hierarchy.inheritanceConflictCache[a][b] = null;
     return null;
   }