Fix nondeterminism in positions from inlining

Bug: b/505807928
Change-Id: Ib9c59a0c382e2579f3791f54d2bb30f1eaec4a1d
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/IRProcessingCallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/IRProcessingCallGraphBuilderBase.java
index c61cd75..40de0b5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/IRProcessingCallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/IRProcessingCallGraphBuilderBase.java
@@ -19,27 +19,37 @@
   }
 
   public CallGraph build(ExecutorService executorService, Timing timing) throws ExecutionException {
-    timing.begin("Build IR processing order constraints");
-    timing.begin("Build call graph");
-    populateGraph(executorService);
-    assert verifyNoRedundantFieldReadEdges();
-    timing.end();
-    assert verifyAllMethodsWithCodeExists();
+    try (Timing t0 = timing.begin("Build IR processing order constraints")) {
+      try (Timing t1 = timing.begin("Build call graph")) {
+        populateGraph(executorService);
+      }
+      assert verifyNoRedundantFieldReadEdges();
+      assert verifyAllMethodsWithCodeExists();
+      appView.withGeneratedMessageLiteBuilderShrinker(
+          shrinker -> shrinker.preprocessCallGraphBeforeCycleElimination(nodes));
+      runCycleEliminator(timing);
+      return new CallGraph(nodes);
+    }
+  }
 
-    appView.withGeneratedMessageLiteBuilderShrinker(
-        shrinker -> shrinker.preprocessCallGraphBeforeCycleElimination(nodes));
+  public CallGraph buildForSmallMethodInliner(ExecutorService executorService, Timing timing)
+      throws ExecutionException {
+    try (Timing t0 = timing.begin("Build call graph")) {
+      populateGraph(executorService);
+      runCycleEliminator(timing);
+      return new CallGraph(nodes);
+    }
+  }
 
+  private void runCycleEliminator(Timing timing) {
     timing.begin("Cycle elimination");
     // Sort the nodes for deterministic cycle elimination.
     Set<Node> nodesWithDeterministicOrder = Sets.newTreeSet(nodes.values());
     CycleEliminator cycleEliminator = new CycleEliminator();
     cycleEliminator.breakCycles(nodesWithDeterministicOrder);
     timing.end();
-    timing.end();
     assert cycleEliminator.breakCycles(nodesWithDeterministicOrder).numberOfRemovedCallEdges()
         == 0; // The cycles should be gone.
-
-    return new CallGraph(nodes);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/smallmethodinliner/SmallMethodInliner.java b/src/main/java/com/android/tools/r8/optimize/smallmethodinliner/SmallMethodInliner.java
index abbc1ee..a974bc6 100644
--- a/src/main/java/com/android/tools/r8/optimize/smallmethodinliner/SmallMethodInliner.java
+++ b/src/main/java/com/android/tools/r8/optimize/smallmethodinliner/SmallMethodInliner.java
@@ -31,6 +31,8 @@
 import com.android.tools.r8.ir.conversion.MethodProcessor;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
+import com.android.tools.r8.ir.conversion.callgraph.CallGraph;
+import com.android.tools.r8.ir.conversion.callgraph.PartialCallGraphBuilder;
 import com.android.tools.r8.ir.optimize.DeadCodeRemover;
 import com.android.tools.r8.ir.optimize.DefaultInliningOracle;
 import com.android.tools.r8.ir.optimize.Inliner;
@@ -236,36 +238,55 @@
       throws ExecutionException {
     try (Timing t0 = timing.begin("Process callers")) {
       MethodProcessor methodProcessor = createMethodProcessor();
+      ProgramMethodSet callGraphSeeds = ProgramMethodSet.createConcurrent();
       ThreadUtils.processItems(
           callers,
-          method -> {
-            IRCode code = method.buildIR(appView);
-            DeadCodeRemover deadCodeRemover = new DeadCodeRemover(appView);
-            Timing threadTiming = Timing.empty();
-            performInlining(
-                method,
-                code,
-                OptimizationFeedbackSimple.getInstance(),
-                methodProcessor,
-                threadTiming,
-                this,
-                this);
-
-            // Only convert IR back to LIR if any methods were inlined.
-            if (needsFinalization.remove(method)) {
-              IRFinalizer<?> finalizer =
-                  code.getConversionOptions().getFinalizer(deadCodeRemover, appView);
-              Code newCode =
-                  finalizer.finalizeCode(code, BytecodeMetadataProvider.empty(), threadTiming);
-              method.setCode(newCode, appView);
+          caller -> {
+            if (methodsToInline.contains(caller)) {
+              callGraphSeeds.add(caller);
+            } else {
+              processCaller(caller, methodProcessor);
             }
           },
           options.getThreadingModule(),
           executorService);
+      if (!callGraphSeeds.isEmpty()) {
+        CallGraph callGraph =
+            new PartialCallGraphBuilder(appView, callGraphSeeds)
+                .buildForSmallMethodInliner(executorService, timing);
+        while (!callGraph.isEmpty()) {
+          ThreadUtils.processItems(
+              callGraph.extractLeaves(),
+              caller -> processCaller(caller, methodProcessor),
+              options.getThreadingModule(),
+              executorService);
+        }
+      }
       assert needsFinalization.isEmpty();
     }
   }
 
+  private void processCaller(ProgramMethod method, MethodProcessor methodProcessor) {
+    IRCode code = method.buildIR(appView);
+    DeadCodeRemover deadCodeRemover = new DeadCodeRemover(appView);
+    Timing threadTiming = Timing.empty();
+    performInlining(
+        method,
+        code,
+        OptimizationFeedbackSimple.getInstance(),
+        methodProcessor,
+        threadTiming,
+        this,
+        this);
+
+    // Only convert IR back to LIR if any methods were inlined.
+    if (needsFinalization.remove(method)) {
+      IRFinalizer<?> finalizer = code.getConversionOptions().getFinalizer(deadCodeRemover, appView);
+      Code newCode = finalizer.finalizeCode(code, BytecodeMetadataProvider.empty(), threadTiming);
+      method.setCode(newCode, appView);
+    }
+  }
+
   private void pruneInlinedMethods(ExecutorService executorService, Timing timing)
       throws ExecutionException {
     try (Timing t0 = timing.begin("Prune inlined methods")) {