Commit created dex items after each enqueuer wave

Bug: b/422947619
Change-Id: Ida3ab1f30c3b4cd71df6ecf4acd0aa16e2b9a86e
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index ca1c308..60cdebb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -92,12 +92,24 @@
   private final Set<DexType> possibleCompilerSynthesizedTypes = Sets.newIdentityHashSet();
 
   private Map<DexString, DexString> markers = new ConcurrentHashMap<>();
-  private Map<DexString, DexString> strings = new ConcurrentHashMap<>();
-  private Map<DexString, DexType> types = new ConcurrentHashMap<>();
-  private Map<DexField, DexField> fields = new ConcurrentHashMap<>();
-  private Map<DexProto, DexProto> protos = new ConcurrentHashMap<>();
-  private Map<DexMethod, DexMethod> methods = new ConcurrentHashMap<>();
+
   private Map<DexMethodHandle, DexMethodHandle> methodHandles = new ConcurrentHashMap<>();
+  private Map<DexMethodHandle, DexMethodHandle> committedMethodHandles = new HashMap<>();
+
+  private Map<DexString, DexString> strings = new ConcurrentHashMap<>();
+  private Map<DexString, DexString> committedStrings = new HashMap<>();
+
+  private Map<DexString, DexType> types = new ConcurrentHashMap<>();
+  private Map<DexString, DexType> committedTypes = new HashMap<>();
+
+  private Map<DexField, DexField> fields = new ConcurrentHashMap<>();
+  private Map<DexField, DexField> committedFields = new HashMap<>();
+
+  private Map<DexProto, DexProto> protos = new ConcurrentHashMap<>();
+  private Map<DexProto, DexProto> committedProtos = new HashMap<>();
+
+  private Map<DexMethod, DexMethod> methods = new ConcurrentHashMap<>();
+  private Map<DexMethod, DexMethod> committedMethods = new HashMap<>();
 
   // DexDebugEvent Canonicalization.
   private final Int2ReferenceMap<AdvanceLine> advanceLines = new Int2ReferenceOpenHashMap<>();
@@ -1887,8 +1899,12 @@
           createString("addSuppressed"), voidDescriptor, new DexString[]{throwableDescriptor});
       getSuppressed = createMethod(throwableDescriptor,
           createString("getSuppressed"), throwableArrayDescriptor, DexString.EMPTY_ARRAY);
-      initCause = createMethod(throwableDescriptor, createString("initCause"), throwableDescriptor,
-          new DexString[] { throwableDescriptor });
+      initCause =
+          createMethod(
+              throwableDescriptor,
+              createString("initCause"),
+              throwableDescriptor,
+              new DexString[] {throwableDescriptor});
       getMessage =
           createMethod(
               throwableDescriptor,
@@ -2222,14 +2238,27 @@
               getDeclaredConstructorName,
               constructorDescriptor,
               new DexString[] {classArrayDescriptor});
-      getField = createMethod(classDescriptor, getFieldName, fieldDescriptor,
-          new DexString[] {stringDescriptor});
-      getDeclaredField = createMethod(classDescriptor, getDeclaredFieldName, fieldDescriptor,
-          new DexString[] {stringDescriptor});
-      getMethod = createMethod(classDescriptor, getMethodName, methodDescriptor,
-          new DexString[] {stringDescriptor, classArrayDescriptor});
-      getDeclaredMethod = createMethod(classDescriptor, getDeclaredMethodName, methodDescriptor,
-          new DexString[] {stringDescriptor, classArrayDescriptor});
+      getField =
+          createMethod(
+              classDescriptor, getFieldName, fieldDescriptor, new DexString[] {stringDescriptor});
+      getDeclaredField =
+          createMethod(
+              classDescriptor,
+              getDeclaredFieldName,
+              fieldDescriptor,
+              new DexString[] {stringDescriptor});
+      getMethod =
+          createMethod(
+              classDescriptor,
+              getMethodName,
+              methodDescriptor,
+              new DexString[] {stringDescriptor, classArrayDescriptor});
+      getDeclaredMethod =
+          createMethod(
+              classDescriptor,
+              getDeclaredMethodName,
+              methodDescriptor,
+              new DexString[] {stringDescriptor, classArrayDescriptor});
       newInstance =
           createMethod(classDescriptor, newInstanceName, objectDescriptor, DexString.EMPTY_ARRAY);
       getMembers = ImmutableSet.of(getField, getDeclaredField, getMethod, getDeclaredMethod);
@@ -2429,6 +2458,7 @@
           && accessFlags.isFinal();
     }
   }
+
   public class NullPointerExceptionMethods {
 
     public final DexMethod init =
@@ -2454,7 +2484,7 @@
    * All boxed types (Boolean, Byte, ...) have a field named TYPE which contains the Class object
    * for the primitive type.
    *
-   * E.g. for Boolean https://docs.oracle.com/javase/8/docs/api/java/lang/Boolean.html#TYPE.
+   * <p>E.g. for Boolean https://docs.oracle.com/javase/8/docs/api/java/lang/Boolean.html#TYPE.
    */
   public class PrimitiveTypesBoxedTypeFields {
 
@@ -2808,7 +2838,7 @@
         return !strValue.getType().isNullable();
       }
 
-      assert false : "Unexpected invoke targeting `" + invokedMethod.toSourceString() +  "`";
+      assert false : "Unexpected invoke targeting `" + invokedMethod.toSourceString() + "`";
       return false;
     }
 
@@ -2962,11 +2992,20 @@
         createMethod(javaUtilIteratorType, createProto(objectType), nextName);
   }
 
-  private static <T extends DexItem> T canonicalize(Map<T, T> map, T item) {
+  private static <T extends DexItem> T canonicalize(
+      Map<T, T> committedMap, Map<T, T> pendingMap, T item) {
     assert item != null;
     assert !DexItemFactory.isInternalSentinel(item);
-    T previous = map.putIfAbsent(item, item);
-    return previous == null ? item : previous;
+    // Avoid synchronization for committed items.
+    T committed = committedMap.get(item);
+    if (committed != null) {
+      return committed;
+    }
+    T previous = pendingMap.putIfAbsent(item, item);
+    if (previous != null) {
+      return previous;
+    }
+    return item;
   }
 
   public DexString createMarkerString(int size, byte[] content) {
@@ -2986,11 +3025,11 @@
   }
 
   public DexString createString(int size, byte[] content) {
-    return canonicalize(strings, new DexString(size, content));
+    return canonicalize(committedStrings, strings, new DexString(size, content));
   }
 
   public DexString createString(String source) {
-    return canonicalize(strings, new DexString(source));
+    return canonicalize(committedStrings, strings, new DexString(source));
   }
 
   public static String escapeMemberString(String str) {
@@ -3294,12 +3333,13 @@
     }
   }
 
-  public DexString lookupString(int size, byte[] content) {
-    return strings.get(new DexString(size, content));
-  }
-
   public DexString lookupString(String source) {
-    return strings.get(new DexString(source));
+    DexString key = new DexString(source);
+    DexString committed = committedStrings.get(key);
+    if (committed != null) {
+      return committed;
+    }
+    return strings.get(key);
   }
 
   // Debugging support to extract marking string.
@@ -3315,23 +3355,6 @@
     return markers;
   }
 
-  // Non-synchronized internal create.
-  private DexType internalCreateType(DexString descriptor) {
-    assert descriptor != null;
-    DexType result = types.get(descriptor);
-    if (result == null) {
-      result = new DexType(descriptor);
-      assert result.isArrayType()
-              || result.isClassType()
-              || result.isPrimitiveType()
-              || result.isVoidType()
-          : descriptor.toString();
-      assert !isInternalSentinel(result);
-      types.put(descriptor, result);
-    }
-    return result;
-  }
-
   private DexType createStaticallyKnownType(String descriptor) {
     return createStaticallyKnownType(createString(descriptor));
   }
@@ -3344,7 +3367,7 @@
   }
 
   private DexType createStaticallyKnownType(DexString descriptor) {
-    DexType type = internalCreateType(descriptor);
+    DexType type = createType(descriptor);
     // Conservatively add all statically known types to "compiler synthesized types set".
     addPossiblySynthesizedType(type);
     return type;
@@ -3352,8 +3375,8 @@
 
   // Safe synchronized external create. May be used for statically known types in synthetic code.
   // See the generated BackportedMethods.java for reference.
-  public synchronized DexType createSynthesizedType(String descriptor) {
-    DexType type = internalCreateType(createString(descriptor));
+  public DexType createSynthesizedType(String descriptor) {
+    DexType type = createType(createString(descriptor));
     addPossiblySynthesizedType(type);
     return type;
   }
@@ -3381,9 +3404,13 @@
     possibleCompilerSynthesizedTypes.forEach(fn);
   }
 
-  // Safe synchronized external create. Should never be used to create a statically known type!
-  public synchronized DexType createType(DexString descriptor) {
-    return internalCreateType(descriptor);
+  public DexType createType(DexString descriptor) {
+    assert descriptor != null;
+    DexType committed = committedTypes.get(descriptor);
+    if (committed != null) {
+      return committed;
+    }
+    return types.computeIfAbsent(descriptor, DexType::new);
   }
 
   public DexType createType(String descriptor) {
@@ -3395,6 +3422,10 @@
   }
 
   public DexType lookupType(DexString descriptor) {
+    DexType committed = committedTypes.get(descriptor);
+    if (committed != null) {
+      return committed;
+    }
     return types.get(descriptor);
   }
 
@@ -3405,7 +3436,7 @@
 
   public DexField createField(DexType clazz, DexType type, DexString name) {
     DexField field = new DexField(clazz, type, name, skipNameValidationForTesting);
-    return canonicalize(fields, field);
+    return canonicalize(committedFields, fields, field);
   }
 
   public DexField createField(DexType clazz, DexType type, String name) {
@@ -3421,7 +3452,7 @@
 
   public DexProto createProto(DexType returnType, DexTypeList parameters) {
     DexProto proto = new DexProto(returnType, parameters);
-    return canonicalize(protos, proto);
+    return canonicalize(committedProtos, protos, proto);
   }
 
   public DexProto createProto(DexType returnType, DexType... parameters) {
@@ -3493,7 +3524,7 @@
 
   public DexMethod createMethod(DexType holder, DexProto proto, DexString name) {
     DexMethod method = new DexMethod(holder, proto, name, skipNameValidationForTesting);
-    return canonicalize(methods, method);
+    return canonicalize(committedMethods, methods, method);
   }
 
   public DexMethod createMethod(DexType holder, DexProto proto, String name) {
@@ -3530,7 +3561,7 @@
       DexMethod rewrittenTarget) {
     DexMethodHandle methodHandle =
         new DexMethodHandle(type, fieldOrMethod, isInterface, rewrittenTarget);
-    return canonicalize(methodHandles, methodHandle);
+    return canonicalize(committedMethodHandles, methodHandles, methodHandle);
   }
 
   public DexCallSite createCallSite(
@@ -3629,25 +3660,53 @@
 
   @Deprecated
   synchronized public void forAllTypes(Consumer<DexType> f) {
-    new ArrayList<>(types.values()).forEach(f);
+    List<DexType> allTypes = new ArrayList<>(committedTypes.values());
+    allTypes.addAll(types.values());
+    allTypes.forEach(f);
   }
 
   public void gc() {
     markers = new WeakHashMap<>(markers);
+    methodHandles = new WeakHashMap<>(methodHandles);
     strings = new WeakHashMap<>(strings);
     types = new WeakHashMap<>(types);
     fields = new WeakHashMap<>(fields);
     protos = new WeakHashMap<>(protos);
     methods = new WeakHashMap<>(methods);
-    methodHandles = new WeakHashMap<>(methodHandles);
+    committedMethodHandles = new WeakHashMap<>(committedMethodHandles);
+    committedStrings = new WeakHashMap<>(committedStrings);
+    committedTypes = new WeakHashMap<>(committedTypes);
+    committedFields = new WeakHashMap<>(committedFields);
+    committedProtos = new WeakHashMap<>(committedProtos);
+    committedMethods = new WeakHashMap<>(committedMethods);
     System.gc();
     System.gc();
     markers = new ConcurrentHashMap<>(markers);
+    methodHandles = new ConcurrentHashMap<>(methodHandles);
     strings = new ConcurrentHashMap<>(strings);
     types = new ConcurrentHashMap<>(types);
     fields = new ConcurrentHashMap<>(fields);
     protos = new ConcurrentHashMap<>(protos);
     methods = new ConcurrentHashMap<>(methods);
-    methodHandles = new ConcurrentHashMap<>(methodHandles);
+    committedMethodHandles = new HashMap<>(committedMethodHandles);
+    committedStrings = new HashMap<>(committedStrings);
+    committedTypes = new HashMap<>(committedTypes);
+    committedFields = new HashMap<>(committedFields);
+    committedProtos = new HashMap<>(committedProtos);
+    committedMethods = new HashMap<>(committedMethods);
+  }
+
+  public void commitPendingItems() {
+    commitPendingItems(committedMethodHandles, methodHandles);
+    commitPendingItems(committedStrings, strings);
+    commitPendingItems(committedTypes, types);
+    commitPendingItems(committedFields, fields);
+    commitPendingItems(committedProtos, protos);
+    commitPendingItems(committedMethods, methods);
+  }
+
+  private static <K, V> void commitPendingItems(Map<K, V> committed, Map<K, V> pending) {
+    committed.putAll(pending);
+    pending.clear();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index ba87f0c..234295e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -481,21 +481,6 @@
     return dexItemFactory.createType(newDesc);
   }
 
-  // Similar to the method above, but performs a lookup only, allowing to use
-  // this method also after strings are sorted in the ApplicationWriter.
-  public DexType lookupBaseType(DexItemFactory dexItemFactory) {
-    int leadingSquareBrackets = getNumberOfLeadingSquareBrackets();
-    if (leadingSquareBrackets == 0) {
-      return this;
-    }
-    DexString newDesc =
-        dexItemFactory.lookupString(
-            descriptor.length() - leadingSquareBrackets,
-            Arrays.copyOfRange(
-                descriptor.content, leadingSquareBrackets, descriptor.content.length));
-    return dexItemFactory.lookupType(newDesc);
-  }
-
   public DexType replaceBaseType(DexType newBase, DexItemFactory dexItemFactory) {
     assert this.isArrayType();
     assert !newBase.isArrayType();
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 85403b4..869e6ac 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -4863,6 +4863,14 @@
         timing.begin("Compute fixpoint #" + round++);
         long numberOfLiveItems = getNumberOfLiveItems();
 
+        // During the initial round of tree shaking we concurrently parse the code objects. Commit
+        // dex items before every wave to minimize synchronization overhead.
+        if (mode.isInitialTreeShaking()) {
+          timing.begin("Commit pending dex items");
+          appView.dexItemFactory().commitPendingItems();
+          timing.end();
+        }
+
         timing.begin("Process worklist");
         worklist.process(timing);
         timing.end();