Interface Method desugaring class processing concurrent

- ClassProcessor now works concurrently to the others.
- Merge all interface processing in a single loop, which
  can be executed concurrently.

Bug: 183998768
Change-Id: Ic896fe814c0dd5b95f46159e9846e98bc683060e
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 41853d5..956cd2c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -43,7 +43,9 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import org.objectweb.asm.Opcodes;
 
 /**
@@ -340,17 +342,17 @@
   private final boolean needsLibraryInfo;
 
   // Mapping from program and classpath classes to their information summary.
-  private final Map<DexClass, ClassInfo> classInfo = new IdentityHashMap<>();
+  private final Map<DexClass, ClassInfo> classInfo = new ConcurrentHashMap<>();
 
   // Mapping from library classes to their information summary.
-  private final Map<DexLibraryClass, SignaturesInfo> libraryClassInfo = new IdentityHashMap<>();
+  private final Map<DexLibraryClass, SignaturesInfo> libraryClassInfo = new ConcurrentHashMap<>();
 
   // Mapping from arbitrary interfaces to an information summary.
-  private final Map<DexClass, SignaturesInfo> interfaceInfo = new IdentityHashMap<>();
+  private final Map<DexClass, SignaturesInfo> interfaceInfo = new ConcurrentHashMap<>();
 
   // Mapping from actual program classes to the synthesized forwarding methods to be created.
   private final Map<DexProgramClass, ProgramMethodSet> newSyntheticMethods =
-      new IdentityHashMap<>();
+      new ConcurrentHashMap<>();
 
   ClassProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
     this.appView = appView;
@@ -813,6 +815,26 @@
     return clazz;
   }
 
+  // We cannot use ConcurrentHashMap computeIfAbsent because the calls are recursive ending in
+  // concurrent modification of the ConcurrentHashMap while performing computeIfAbsent.
+  private <C extends DexClass, I> I reentrantComputeIfAbsent(
+      Map<C, I> infoMap, C clazz, Function<C, I> supplier) {
+    // Try to avoid the synchronization when possible.
+    I computedInfo = infoMap.get(clazz);
+    if (computedInfo != null) {
+      return computedInfo;
+    }
+    synchronized (clazz) {
+      computedInfo = infoMap.get(clazz);
+      if (computedInfo != null) {
+        return computedInfo;
+      }
+      computedInfo = supplier.apply(clazz);
+      infoMap.put(clazz, computedInfo);
+      return computedInfo;
+    }
+  }
+
   private ClassInfo visitClassInfo(DexType type, ReportingContext context) {
     DexClass clazz = definitionOrNull(type, context);
     return clazz == null ? ClassInfo.EMPTY : visitClassInfo(clazz, context);
@@ -823,7 +845,7 @@
     if (clazz.isLibraryClass()) {
       return ClassInfo.EMPTY;
     }
-    return classInfo.computeIfAbsent(clazz, key -> visitClassInfoRaw(key, context));
+    return reentrantComputeIfAbsent(classInfo, clazz, key -> visitClassInfoRaw(key, context));
   }
 
   private ClassInfo visitClassInfoRaw(DexClass clazz, ReportingContext context) {
@@ -854,7 +876,8 @@
   private SignaturesInfo visitLibraryClassInfo(DexClass clazz) {
     assert !clazz.isInterface();
     return clazz.isLibraryClass()
-        ? libraryClassInfo.computeIfAbsent(clazz.asLibraryClass(), this::visitLibraryClassInfoRaw)
+        ? reentrantComputeIfAbsent(
+            libraryClassInfo, clazz.asLibraryClass(), this::visitLibraryClassInfoRaw)
         : SignaturesInfo.EMPTY;
   }
 
@@ -876,7 +899,8 @@
     if (iface.isLibraryClass() && ignoreLibraryInfo()) {
       return SignaturesInfo.EMPTY;
     }
-    return interfaceInfo.computeIfAbsent(iface, key -> visitInterfaceInfoRaw(key, context));
+    return reentrantComputeIfAbsent(
+        interfaceInfo, iface, key -> visitInterfaceInfoRaw(key, context));
   }
 
   private SignaturesInfo visitInterfaceInfoRaw(DexClass iface, ReportingContext context) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 7fad101..0661c1d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -981,19 +981,14 @@
     // classes if needed.
     InterfaceProcessor interfaceProcessor = new InterfaceProcessor(appView, this);
 
-    // TODO(b/183998768): Merge the following loops into a single one.
-    process(classProcessor, builder, flavour);
-
     // The interface processors must be ordered so that finalization of the processing is performed
     // in that order. The emulatedInterfaceProcessor has to be last at this point to avoid renaming
     // emulated interfaces before the other processing.
     ImmutableList<InterfaceDesugaringProcessor> orderedInterfaceDesugaringProcessors =
-        ImmutableList.of(interfaceProcessor, emulatedInterfaceProcessor);
+        ImmutableList.of(classProcessor, interfaceProcessor, emulatedInterfaceProcessor);
     processClassesConcurrently(
         orderedInterfaceDesugaringProcessors, builder, flavour, executorService);
 
-    classProcessor.finalizeProcessing(builder, synthesizedMethods);
-
     SortedProgramMethodSet sortedSynthesizedMethods = SortedProgramMethodSet.create();
     sortedSynthesizedMethods.addAll(synthesizedMethods);
     converter.processMethodsConcurrently(sortedSynthesizedMethods, executorService);