Nest access control: d8 and r8 concurrent

1. d8 now processes program classes once, on-the-fly
   instead of once ahead and once to rewrite.
   This introduced concurrency issues since the
   IRConverter processes methods concurrently, hence
   the code had to be made thread-safe (Cf 2.)
2. nest processing in d8 and r8 is now thread-safe and
   concurrent, i.e., each nest is processed concurrently
   to others.

Bug:130529390
Change-Id: I4f3cd3e6f687e66218ad37db3c9b77299e51ca80
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 16b75f5..f48d075 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -426,8 +426,8 @@
       if (options.enableNestBasedAccessDesugaring) {
         timing.begin("NestBasedAccessDesugaring");
         NestBasedAccessDesugaringAnalysis analyzer =
-            new NestBasedAccessDesugaringAnalysis(appViewWithLiveness);
-        boolean changed = appView.setGraphLense(analyzer.run());
+            new NestBasedAccessDesugaringAnalysis(appViewWithLiveness, executorService);
+        boolean changed = appView.setGraphLense(analyzer.run(executorService));
         if (changed) {
           appViewWithLiveness.setAppInfo(
               appViewWithLiveness
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index a301563..bd99f55 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -832,6 +832,11 @@
     return !nestMembers.isEmpty();
   }
 
+  public DexType getNestHost() {
+    assert nestHost != null;
+    return nestHost.getNestHost();
+  }
+
   public NestHostClassAttribute getNestHostClassAttribute() {
     return nestHost;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index c6c1ba8..c307286 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -290,15 +290,10 @@
     return false;
   }
 
-  private void analyzeNests() {
+  private void desugarNestBasedAccess(Builder<?> builder, ExecutorService executorService)
+      throws ExecutionException {
     if (nestBasedAccessDesugaringRewriter != null) {
-      nestBasedAccessDesugaringRewriter.analyzeNests();
-    }
-  }
-
-  private void synthetizeNestConstructor(Builder<?> builder) {
-    if (nestBasedAccessDesugaringRewriter != null) {
-      nestBasedAccessDesugaringRewriter.synthetizeNestConstructor(builder);
+      nestBasedAccessDesugaringRewriter.desugarNestBasedAccess(builder, executorService, this);
     }
   }
 
@@ -358,7 +353,6 @@
   public DexApplication convertToDex(DexApplication application, ExecutorService executor)
       throws ExecutionException {
     removeLambdaDeserializationMethods();
-    analyzeNests();
 
     timing.begin("IR conversion");
     convertClassesToDex(application.classes(), executor);
@@ -367,7 +361,7 @@
     Builder<?> builder = application.builder();
     builder.setHighestSortingString(highestSortingString);
 
-    synthetizeNestConstructor(builder);
+    desugarNestBasedAccess(builder, executor);
     synthesizeLambdaClasses(builder, executor);
     desugarInterfaceMethods(builder, ExcludeDexResources, executor);
     synthesizeTwrCloseResourceUtilityClass(builder, executor);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index 3f6695b..d48530c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -18,15 +18,19 @@
 import com.android.tools.r8.graph.NestMemberClassAttribute;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.utils.ThreadUtils;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
 
+// NestBasedAccessDesugaring contains common code between the two subclasses
+// which are specialized for d8 and r8
 public abstract class NestBasedAccessDesugaring {
 
   // Short names to avoid creating long strings
@@ -44,51 +48,62 @@
   private static final String FULL_NEST_CONTRUCTOR_NAME = "L" + NEST_CONSTRUCTOR_NAME + ";";
 
   protected final AppView<?> appView;
-  private final HashMap<DexEncodedMethod, DexEncodedMethod> bridges = new HashMap<>();
-  private final HashMap<DexFieldWithAccess, DexEncodedMethod> fieldBridges = new HashMap<>();
-  private final HashMap<DexEncodedMethod, DexProgramClass> deferredBridgesToAdd = new HashMap<>();
+  // Following maps are there to avoid creating the bridges multiple times.
+  private final Map<DexEncodedMethod, DexEncodedMethod> bridges = new ConcurrentHashMap<>();
+  private final Map<DexEncodedField, DexEncodedMethod> getFieldBridges = new ConcurrentHashMap<>();
+  private final Map<DexEncodedField, DexEncodedMethod> putFieldBridges = new ConcurrentHashMap<>();
+  // The following map records the bridges to add in the program.
+  // It may differ from the values of the previous maps
+  // if come classes are on the classpath and not the program path.
+  final Map<DexEncodedMethod, DexProgramClass> deferredBridgesToAdd = new ConcurrentHashMap<>();
+  // Common single empty class for nest based private constructors
   private DexProgramClass nestConstructor;
 
-  public NestBasedAccessDesugaring(AppView<?> appView) {
+  NestBasedAccessDesugaring(AppView<?> appView) {
     this.appView = appView;
   }
 
-  protected DexType getConstructorType() {
-    if (nestConstructor == null) {
-      return null;
+  DexType getNestConstructorType() {
+    return nestConstructor == null ? null : nestConstructor.type;
+  }
+
+  // Extract the list of types in the programClass' nest, of host hostClass
+  List<DexType> extractNest(DexClass hostClass, DexProgramClass programClass) {
+    assert programClass != null;
+    if (hostClass == null) {
+      throw abortCompilationDueToMissingNestHost(programClass);
     }
-    return nestConstructor.type;
-  }
-
-  public void analyzeNests() {
-    // TODO(b/130529338) we don't need to compute a list with all live nests.
-    // we just need to iterate all live nests.
-    List<List<DexType>> liveNests = computeLiveNests();
-    processLiveNests(liveNests);
-    addDeferredBridges();
-  }
-
-  public void synthetizeNestConstructor(DexApplication.Builder<?> builder) {
-    if (nestConstructor != null) {
-      appView.appInfo().addSynthesizedClass(nestConstructor);
-      builder.addSynthesizedClass(nestConstructor, true);
+    List<DexType> classesInNest =
+        new ArrayList<>(hostClass.getNestMembersClassAttributes().size() + 1);
+    for (NestMemberClassAttribute nestmate : hostClass.getNestMembersClassAttributes()) {
+      classesInNest.add(nestmate.getNestMember());
     }
+    classesInNest.add(hostClass.type);
+    return classesInNest;
   }
 
-  private void addDeferredBridges() {
-    for (Map.Entry<DexEncodedMethod, DexProgramClass> entry : deferredBridgesToAdd.entrySet()) {
-      entry.getValue().addMethod(entry.getKey());
-    }
-  }
-
-  private void processLiveNests(List<List<DexType>> liveNests) {
+  void processNestsConcurrently(List<List<DexType>> liveNests, ExecutorService executorService)
+      throws ExecutionException {
+    List<Future<?>> futures = new ArrayList<>();
     for (List<DexType> nest : liveNests) {
-      for (DexType type : nest) {
-        DexClass clazz = appView.definitionFor(type);
-        if (clazz == null) {
-          // TODO(b/130529338) We could throw only a warning if a class is missing.
-          throw abortCompilationDueToIncompleteNest(nest);
-        }
+      futures.add(
+          executorService.submit(
+              () -> {
+                processNest(nest);
+                return null; // we want a Callable not a Runnable to be able to throw
+              }));
+    }
+    ThreadUtils.awaitFutures(futures);
+  }
+
+  private void processNest(List<DexType> nest) {
+    for (DexType type : nest) {
+      DexClass clazz = appView.definitionFor(type);
+      if (clazz == null) {
+        // TODO(b/130529338) We could throw only a warning if a class is missing.
+        throw abortCompilationDueToIncompleteNest(nest);
+      }
+      if (shouldProcessClassInNest(clazz, nest)) {
         NestBasedAccessDesugaringUseRegistry registry =
             new NestBasedAccessDesugaringUseRegistry(nest, clazz);
         for (DexEncodedMethod method : clazz.methods()) {
@@ -98,6 +113,14 @@
     }
   }
 
+  protected abstract boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest);
+
+  void addDeferredBridges() {
+    for (Map.Entry<DexEncodedMethod, DexProgramClass> entry : deferredBridgesToAdd.entrySet()) {
+      entry.getValue().addMethod(entry.getKey());
+    }
+  }
+
   private RuntimeException abortCompilationDueToIncompleteNest(List<DexType> nest) {
     List<String> programClassesFromNest = new ArrayList<>();
     List<String> unavailableClasses = new ArrayList<>();
@@ -128,38 +151,6 @@
     throw new CompilationError(stringBuilder.toString());
   }
 
-  private List<List<DexType>> computeLiveNests() {
-    List<List<DexType>> liveNests = new ArrayList<>();
-    // It is possible that a nest member is on the program path but its nest host
-    // is only in the class path. Nests are therefore computed the first time a
-    // nest member is met, host or not. The computedNestHosts list is there to
-    // avoid processing multiple times the same nest.
-    Set<DexType> computedNestHosts = new HashSet<>();
-    for (DexProgramClass clazz : appView.appInfo().classes()) {
-      if (clazz.isInANest()) {
-        DexType hostType =
-            clazz.isNestHost() ? clazz.type : clazz.getNestHostClassAttribute().getNestHost();
-        if (!computedNestHosts.contains(hostType)) {
-          computedNestHosts.add(hostType);
-          DexClass host =
-              clazz.isNestHost()
-                  ? clazz
-                  : appView.definitionFor(clazz.getNestHostClassAttribute().getNestHost());
-          if (host == null) {
-            throw abortCompilationDueToMissingNestHost(clazz);
-          }
-          List<DexType> classesInNest = new ArrayList<>();
-          for (NestMemberClassAttribute nestmate : host.getNestMembersClassAttributes()) {
-            classesInNest.add(nestmate.getNestMember());
-          }
-          classesInNest.add(host.type);
-          liveNests.add(classesInNest);
-        }
-      }
-    }
-    return liveNests;
-  }
-
   private RuntimeException abortCompilationDueToMissingNestHost(DexProgramClass compiledClass) {
     String nestHostName = compiledClass.getNestHostClassAttribute().getNestHost().getName();
     throw new CompilationError(
@@ -170,6 +161,16 @@
             + " to be on program or class path for compilation to succeed.");
   }
 
+  private RuntimeException abortCompilationDueToBridgeRequiredOnLibraryClass(
+      DexClass compiledClass, DexClass libraryClass) {
+    throw new CompilationError(
+        "Class "
+            + compiledClass.type.getName()
+            + " requires the insertion of a bridge on the library class "
+            + libraryClass.type.getName()
+            + " which is impossible.");
+  }
+
   protected abstract void shouldRewriteCalls(DexMethod method, DexMethod bridge);
 
   protected abstract void shouldRewriteInitializers(DexMethod method, DexMethod bridge);
@@ -182,16 +183,6 @@
 
   protected abstract void shouldRewriteInstancePutFields(DexField field, DexMethod bridge);
 
-  private RuntimeException abortCompilationDueToBridgeRequiredOnLibraryClass(
-      DexClass compiledClass, DexClass libraryClass) {
-    throw new CompilationError(
-        "Class "
-            + compiledClass.type.getName()
-            + " requires the insertion of a bridge on the library class "
-            + libraryClass.type.getName()
-            + " which is impossible.");
-  }
-
   private DexString methodBridgeName(DexEncodedMethod method) {
     String methodName = method.method.name.toString();
     String fullName;
@@ -247,7 +238,97 @@
     return nestConstructor;
   }
 
-  private class NestBasedAccessDesugaringUseRegistry extends UseRegistry {
+  void synthetizeNestConstructor(DexApplication.Builder<?> builder) {
+    if (nestConstructor != null) {
+      appView.appInfo().addSynthesizedClass(nestConstructor);
+      builder.addSynthesizedClass(nestConstructor, true);
+    }
+  }
+
+  boolean registerFieldAccess(
+      DexField field, boolean isGet, List<DexType> nest, DexClass currentClass) {
+    if (field.holder == currentClass.type || !nest.contains(field.holder)) {
+      return false;
+    }
+    DexEncodedField target = appView.definitionFor(field);
+    if (target == null || !target.accessFlags.isPrivate()) {
+      return false;
+    }
+    Map<DexEncodedField, DexEncodedMethod> fieldMap = isGet ? getFieldBridges : putFieldBridges;
+    DexEncodedMethod bridge =
+        fieldMap.computeIfAbsent(
+            target,
+            k -> {
+              DexFieldWithAccess fieldWithAccess = new DexFieldWithAccess(target, isGet);
+              DexClass holder = appView.definitionFor(field.holder);
+              DexEncodedMethod localBridge =
+                  DexEncodedMethod.createFieldAccessorBridge(
+                      fieldWithAccess, holder, appView, fieldBridgeName(fieldWithAccess));
+              // Accesses to program classes private members require bridge insertion.
+              if (holder.isProgramClass()) {
+                deferredBridgesToAdd.put(localBridge, holder.asProgramClass());
+              } else if (holder.isLibraryClass()) {
+                throw abortCompilationDueToBridgeRequiredOnLibraryClass(currentClass, holder);
+              }
+              return localBridge;
+            });
+    // In program classes, any access to nest mate private member needs to be rewritten.
+    if (currentClass.isProgramClass()) {
+      if (isGet) {
+        if (target.isStatic()) {
+          shouldRewriteStaticGetFields(field, bridge.method);
+        } else {
+          shouldRewriteInstanceGetFields(field, bridge.method);
+        }
+      } else {
+        if (target.isStatic()) {
+          shouldRewriteStaticPutFields(field, bridge.method);
+        } else {
+          shouldRewriteInstancePutFields(field, bridge.method);
+        }
+      }
+    }
+    return true;
+  }
+
+  boolean registerInvoke(DexMethod method, List<DexType> nest, DexClass currentClass) {
+    if (method.holder == currentClass.type || !nest.contains(method.holder)) {
+      return false;
+    }
+    DexEncodedMethod target = appView.definitionFor(method);
+    if (target == null || !target.accessFlags.isPrivate()) {
+      return false;
+    }
+    DexEncodedMethod bridge =
+        bridges.computeIfAbsent(
+            target,
+            k -> {
+              DexClass holder = appView.definitionFor(method.holder);
+              DexEncodedMethod localBridge =
+                  target.isInstanceInitializer()
+                      ? target.toInitializerForwardingBridge(
+                          holder, appView, ensureNestConstructorClass())
+                      : target.toStaticForwardingBridge(holder, appView, methodBridgeName(target));
+              // Accesses to program classes private members require bridge insertion.
+              if (holder.isProgramClass()) {
+                deferredBridgesToAdd.put(localBridge, holder.asProgramClass());
+              } else if (holder.isLibraryClass()) {
+                throw abortCompilationDueToBridgeRequiredOnLibraryClass(currentClass, holder);
+              }
+              return localBridge;
+            });
+    // In program classes, any access to nest mate private member needs to be rewritten.
+    if (currentClass.isProgramClass()) {
+      if (target.isInstanceInitializer()) {
+        shouldRewriteInitializers(method, bridge.method);
+      } else {
+        shouldRewriteCalls(method, bridge.method);
+      }
+    }
+    return true;
+  }
+
+  protected class NestBasedAccessDesugaringUseRegistry extends UseRegistry {
 
     private final List<DexType> nest;
     private final DexClass currentClass;
@@ -258,110 +339,28 @@
       this.currentClass = currentClass;
     }
 
-    private boolean registerInvoke(DexMethod method) {
-      if (method.holder == currentClass.type || !nest.contains(method.holder)) {
-        return false;
-      }
-      DexEncodedMethod target = appView.definitionFor(method);
-      if (target == null || !target.accessFlags.isPrivate()) {
-        return false;
-      }
-      DexEncodedMethod bridge =
-          bridges.computeIfAbsent(
-              target,
-              k -> {
-                DexClass holder = appView.definitionFor(method.holder);
-                DexEncodedMethod localBridge =
-                    target.isInstanceInitializer()
-                        ? target.toInitializerForwardingBridge(
-                            holder, appView, ensureNestConstructorClass())
-                        : target.toStaticForwardingBridge(
-                            holder, appView, methodBridgeName(target));
-                // Accesses to program classes private members require bridge insertion.
-                if (holder.isProgramClass()) {
-                  deferredBridgesToAdd.put(localBridge, holder.asProgramClass());
-                } else if (holder.isLibraryClass()) {
-                  throw abortCompilationDueToBridgeRequiredOnLibraryClass(currentClass, holder);
-                }
-                return localBridge;
-              });
-      // In program classes, any access to nest mate private member needs to be rewritten.
-      if (currentClass.isProgramClass()) {
-        if (target.isInstanceInitializer()) {
-          shouldRewriteInitializers(method, bridge.method);
-        } else {
-          shouldRewriteCalls(method, bridge.method);
-        }
-      }
-      return true;
-    }
-
-    private boolean registerFieldAccess(DexField field, boolean isGet) {
-      if (field.holder == currentClass.type || !nest.contains(field.holder)) {
-        return false;
-      }
-      DexEncodedField target = appView.definitionFor(field);
-      if (target == null || !target.accessFlags.isPrivate()) {
-        return false;
-      }
-      DexFieldWithAccess key = new DexFieldWithAccess(target, isGet);
-      DexEncodedMethod bridge =
-          fieldBridges.computeIfAbsent(
-              key,
-              k -> {
-                DexClass holder = appView.definitionFor(field.holder);
-                DexEncodedMethod localBridge =
-                    DexEncodedMethod.createFieldAccessorBridge(
-                        key, holder, appView, fieldBridgeName(key));
-                // Accesses to program classes private members require bridge insertion.
-                if (holder.isProgramClass()) {
-                  deferredBridgesToAdd.put(localBridge, holder.asProgramClass());
-                } else if (holder.isLibraryClass()) {
-                  throw abortCompilationDueToBridgeRequiredOnLibraryClass(currentClass, holder);
-                }
-                return localBridge;
-              });
-      // In program classes, any access to nest mate private member needs to be rewritten.
-      if (currentClass.isProgramClass()) {
-        if (isGet) {
-          if (target.isStatic()) {
-            shouldRewriteStaticGetFields(field, bridge.method);
-          } else {
-            shouldRewriteInstanceGetFields(field, bridge.method);
-          }
-        } else {
-          if (target.isStatic()) {
-            shouldRewriteStaticPutFields(field, bridge.method);
-          } else {
-            shouldRewriteInstancePutFields(field, bridge.method);
-          }
-        }
-      }
-      return true;
-    }
-
     @Override
     public boolean registerInvokeVirtual(DexMethod method) {
       // Calls to class nest mate private methods are targeted by invokeVirtual in jdk11.
       // The spec recommends to do so, but do not enforce it, hence invokeDirect is also registered.
-      return registerInvoke(method);
+      return registerInvoke(method, nest, currentClass);
     }
 
     @Override
     public boolean registerInvokeDirect(DexMethod method) {
-      return registerInvoke(method);
+      return registerInvoke(method, nest, currentClass);
     }
 
     @Override
     public boolean registerInvokeStatic(DexMethod method) {
-      return registerInvoke(method);
+      return registerInvoke(method, nest, currentClass);
     }
 
     @Override
     public boolean registerInvokeInterface(DexMethod method) {
       // Calls to interface nest mate private methods are targeted by invokeInterface in jdk11.
       // The spec recommends to do so, but do not enforce it, hence invokeDirect is also registered.
-      return registerInvoke(method);
+      return registerInvoke(method, nest, currentClass);
     }
 
     @Override
@@ -372,12 +371,12 @@
 
     @Override
     public boolean registerInstanceFieldWrite(DexField field) {
-      return registerFieldAccess(field, false);
+      return registerFieldAccess(field, false, nest, currentClass);
     }
 
     @Override
     public boolean registerInstanceFieldRead(DexField field) {
-      return registerFieldAccess(field, true);
+      return registerFieldAccess(field, true, nest, currentClass);
     }
 
     @Override
@@ -390,12 +389,12 @@
 
     @Override
     public boolean registerStaticFieldRead(DexField field) {
-      return registerFieldAccess(field, true);
+      return registerFieldAccess(field, true, nest, currentClass);
     }
 
     @Override
     public boolean registerStaticFieldWrite(DexField field) {
-      return registerFieldAccess(field, false);
+      return registerFieldAccess(field, false, nest, currentClass);
     }
 
     @Override
@@ -410,7 +409,7 @@
     private final DexEncodedField field;
     private final boolean isGet;
 
-    public DexFieldWithAccess(DexEncodedField field, boolean isGet) {
+    DexFieldWithAccess(DexEncodedField field, boolean isGet) {
       this.field = field;
       this.isGet = isGet;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringAnalysis.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringAnalysis.java
index de92422..e341776 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringAnalysis.java
@@ -1,54 +1,96 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
 
+// R8 specific desugaring of nest based access.
+// Summary:
+// - Computes all the live nests reachable from Program Classes (Sequential).
+// - Process all live nests finding nest based access (Nests processes concurrently).
+// - Add bridges to be processed by further passes (Sequential).
 public class NestBasedAccessDesugaringAnalysis extends NestBasedAccessDesugaring {
 
   private final NestedPrivateMethodLense.Builder builder;
 
-  public NestBasedAccessDesugaringAnalysis(AppView<?> appView) {
+  public NestBasedAccessDesugaringAnalysis(AppView<?> appView, ExecutorService executorService) {
     super(appView);
     this.builder = NestedPrivateMethodLense.builder(appView);
   }
 
-  public GraphLense run() {
+  public GraphLense run(ExecutorService executorService) throws ExecutionException {
     if (appView.options().canUseNestBasedAccess()) {
       return appView.graphLense();
     }
-    analyzeNests();
-    return builder.build(appView.graphLense(), getConstructorType());
+    List<List<DexType>> liveNests = computeLiveNests();
+    processNestsConcurrently(liveNests, executorService);
+    addDeferredBridges();
+    return builder.build(appView.graphLense(), getNestConstructorType());
+  }
+
+  private List<List<DexType>> computeLiveNests() {
+    List<List<DexType>> liveNests = new ArrayList<>();
+    // It is possible that a nest member is on the program path but its nest host
+    // is only in the class path. Nests are therefore computed the first time a
+    // nest member is met, host or not. The computedNestHosts list is there to
+    // avoid processing multiple times the same nest.
+    Set<DexType> computedNestHosts = new HashSet<>();
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
+      if (clazz.isInANest()) {
+        DexType hostType = clazz.isNestHost() ? clazz.type : clazz.getNestHost();
+        if (!computedNestHosts.contains(hostType)) {
+          computedNestHosts.add(hostType);
+          DexClass host = clazz.isNestHost() ? clazz : appView.definitionFor(clazz.getNestHost());
+          liveNests.add(extractNest(host, clazz));
+        }
+      }
+    }
+    return liveNests;
+  }
+
+  // In R8, all classes are processed ahead of time.
+  @Override
+  protected boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest) {
+    return true;
   }
 
   @Override
-  protected void shouldRewriteInitializers(DexMethod method, DexMethod bridge) {
+  protected synchronized void shouldRewriteInitializers(DexMethod method, DexMethod bridge) {
     builder.mapInitializer(method, bridge);
   }
 
   @Override
-  protected void shouldRewriteCalls(DexMethod method, DexMethod bridge) {
+  protected synchronized void shouldRewriteCalls(DexMethod method, DexMethod bridge) {
     builder.map(method, bridge);
   }
 
   @Override
-  protected void shouldRewriteStaticGetFields(DexField field, DexMethod bridge) {
+  protected synchronized void shouldRewriteStaticGetFields(DexField field, DexMethod bridge) {
     builder.mapStaticGet(field, bridge);
   }
 
   @Override
-  protected void shouldRewriteStaticPutFields(DexField field, DexMethod bridge) {
+  protected synchronized void shouldRewriteStaticPutFields(DexField field, DexMethod bridge) {
     builder.mapStaticPut(field, bridge);
   }
 
   @Override
-  protected void shouldRewriteInstanceGetFields(DexField field, DexMethod bridge) {
+  protected synchronized void shouldRewriteInstanceGetFields(DexField field, DexMethod bridge) {
     builder.mapInstanceGet(field, bridge);
   }
 
   @Override
-  protected void shouldRewriteInstancePutFields(DexField field, DexMethod bridge) {
+  protected synchronized void shouldRewriteInstancePutFields(DexField field, DexMethod bridge) {
     builder.mapInstancePut(field, bridge);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringRewriter.java
index ed4cfbe..d4e25df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringRewriter.java
@@ -1,10 +1,15 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.FieldInstruction;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -12,20 +17,33 @@
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.IRConverter;
 import java.util.ArrayList;
-import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
 
+// D8 specific nest based access desugaring.
+// Summary:
+// - Process all methods compiled rewriting nest based access (Methods processed concurrently).
+// - Process classes on class path in reachable nests to find bridges to add
+//    in Program classes (Nests processed concurrently).
+// - Add bridges and nest constructor class (Sequential).
+// - Optimize bridges (Bridges processed concurrently).
 public class NestBasedAccessDesugaringRewriter extends NestBasedAccessDesugaring {
 
-  private Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
-  private Map<DexMethod, DexMethod> initializerMap = new IdentityHashMap<>();
-  private final Map<DexField, DexMethod> staticGetToMethodMap = new IdentityHashMap<>();
-  private final Map<DexField, DexMethod> staticPutToMethodMap = new IdentityHashMap<>();
-  private final Map<DexField, DexMethod> instanceGetToMethodMap = new IdentityHashMap<>();
-  private final Map<DexField, DexMethod> instancePutToMethodMap = new IdentityHashMap<>();
+  private final Map<DexMethod, DexMethod> methodMap = new ConcurrentHashMap<>();
+  private final Map<DexMethod, DexMethod> initializerMap = new ConcurrentHashMap<>();
+  private final Map<DexField, DexMethod> staticGetToMethodMap = new ConcurrentHashMap<>();
+  private final Map<DexField, DexMethod> staticPutToMethodMap = new ConcurrentHashMap<>();
+  private final Map<DexField, DexMethod> instanceGetToMethodMap = new ConcurrentHashMap<>();
+  private final Map<DexField, DexMethod> instancePutToMethodMap = new ConcurrentHashMap<>();
+
+  // Map the nest host to its nest members, including the nest host itself.
+  private final Map<DexType, List<DexType>> metNests = new ConcurrentHashMap<>();
 
   public NestBasedAccessDesugaringRewriter(AppView<?> appView) {
     super(appView);
@@ -61,21 +79,37 @@
     instancePutToMethodMap.put(field, bridge);
   }
 
+  private List<DexType> getNestFor(DexProgramClass programClass) {
+    if (!programClass.isInANest()) {
+      return null;
+    }
+    DexType nestHost = programClass.isNestHost() ? programClass.type : programClass.getNestHost();
+    return metNests.computeIfAbsent(
+        nestHost, host -> extractNest(appView.definitionFor(nestHost), programClass));
+  }
+
   private void rewriteFieldAccess(
-      Instruction instruction,
+      FieldInstruction fieldInstruction,
       InstructionListIterator instructions,
       DexMethod method,
       Map<DexField, DexMethod> fieldToMethodMap) {
-    DexField field = instruction.asFieldInstruction().getField();
-    DexMethod newTarget = fieldToMethodMap.get(field);
+    DexMethod newTarget = fieldToMethodMap.get(fieldInstruction.getField());
     if (newTarget != null && method != newTarget) {
       instructions.replaceCurrentInstruction(
-          new InvokeStatic(newTarget, instruction.outValue(), instruction.inValues()));
+          new InvokeStatic(newTarget, fieldInstruction.outValue(), fieldInstruction.inValues()));
     }
   }
 
   public void rewriteNestBasedAccesses(
       DexEncodedMethod encodedMethod, IRCode code, AppView<?> appView) {
+    // We are compiling its code so it has to be a non-null program class.
+    DexProgramClass currentClass =
+        appView.definitionFor(encodedMethod.method.holder).asProgramClass();
+    List<DexType> nest = getNestFor(currentClass);
+    if (nest == null) {
+      return;
+    }
+
     ListIterator<BasicBlock> blocks = code.listIterator();
     while (blocks.hasNext()) {
       BasicBlock block = blocks.next();
@@ -85,6 +119,7 @@
         if (instruction.isInvokeMethod() && !instruction.isInvokeSuper()) {
           InvokeMethod invokeMethod = instruction.asInvokeMethod();
           DexMethod methodCalled = invokeMethod.getInvokedMethod();
+          registerInvoke(methodCalled, nest, currentClass);
           DexMethod newTarget = methodMap.get(methodCalled);
           if (newTarget != null && encodedMethod.method != newTarget) {
             instructions.replaceCurrentInstruction(
@@ -103,20 +138,44 @@
                   new InvokeDirect(newTarget, invokeMethod.outValue(), parameters));
             }
           }
-        } else if (instruction.isInstanceGet()) {
-          rewriteFieldAccess(
-              instruction, instructions, encodedMethod.method, instanceGetToMethodMap);
-        } else if (instruction.isInstancePut()) {
-          rewriteFieldAccess(
-              instruction, instructions, encodedMethod.method, instancePutToMethodMap);
-        } else if (instruction.isStaticGet()) {
-          rewriteFieldAccess(instruction, instructions, encodedMethod.method, staticGetToMethodMap);
-        } else if (instruction.isStaticPut()) {
-          rewriteFieldAccess(instruction, instructions, encodedMethod.method, staticPutToMethodMap);
+        } else if (instruction.isFieldInstruction()) {
+          FieldInstruction fieldInstruction = instruction.asFieldInstruction();
+          if (instruction.isInstanceGet()) {
+            registerFieldAccess(fieldInstruction.getField(), true, nest, currentClass);
+            rewriteFieldAccess(
+                fieldInstruction, instructions, encodedMethod.method, instanceGetToMethodMap);
+          } else if (instruction.isInstancePut()) {
+            registerFieldAccess(fieldInstruction.getField(), false, nest, currentClass);
+            rewriteFieldAccess(
+                fieldInstruction, instructions, encodedMethod.method, instancePutToMethodMap);
+          } else if (instruction.isStaticGet()) {
+            registerFieldAccess(fieldInstruction.getField(), true, nest, currentClass);
+            rewriteFieldAccess(
+                fieldInstruction, instructions, encodedMethod.method, staticGetToMethodMap);
+          } else if (instruction.isStaticPut()) {
+            registerFieldAccess(fieldInstruction.getField(), false, nest, currentClass);
+            rewriteFieldAccess(
+                fieldInstruction, instructions, encodedMethod.method, staticPutToMethodMap);
+          }
         }
       }
     }
   }
 
+  public void desugarNestBasedAccess(
+      DexApplication.Builder<?> builder, ExecutorService executorService, IRConverter converter)
+      throws ExecutionException {
+    List<List<DexType>> metNests = new ArrayList<>(this.metNests.values());
+    processNestsConcurrently(metNests, executorService);
+    addDeferredBridges();
+    synthetizeNestConstructor(builder);
+    converter.optimizeSynthesizedMethodsConcurrently(
+        deferredBridgesToAdd.keySet(), executorService);
+  }
 
+  // In D8, programClass are processed on the fly so they do not need to be processed again here.
+  @Override
+  protected boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest) {
+    return clazz.isNotProgramClass();
+  }
 }