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);