Version 1.4.79

Cherry-pick:
Remove unused import.
CL: https://r8-review.googlesource.com/c/r8/+/35300

Cherry-pick:
Process double inlining in parallel.
CL: https://r8-review.googlesource.com/c/r8/+/35201

Cherry-pick:
Reproduce b/128987064: illegal invoke-super during double-inlining processing.
CL: https://r8-review.googlesource.com/c/r8/+/36220

Bug: 121235635
Bug: 128987064
Change-Id: Ic70e70461f5c3f2d09987143380b50063d0166bb
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 11d013c..d7a4efa 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.4.78";
+  public static final String LABEL = "1.4.79";
 
   private Version() {
   }
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 edd33c8..32fc687 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
@@ -559,7 +559,7 @@
     if (inliner != null) {
       printPhase("Double caller inlining");
       assert graphLenseForIR == graphLense();
-      inliner.processDoubleInlineCallers(this, feedback);
+      inliner.processDoubleInlineCallers(this, executorService, feedback);
       feedback.updateVisibleOptimizationInfo();
       assert graphLenseForIR == graphLense();
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 2b0d831..30fb191 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -32,6 +32,7 @@
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.MainDexClasses;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -39,8 +40,10 @@
 import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
 import java.util.function.Predicate;
-import java.util.stream.Collectors;
 
 public class Inliner {
 
@@ -160,24 +163,29 @@
     return target;
   }
 
-  public synchronized void processDoubleInlineCallers(
-      IRConverter converter, OptimizationFeedback feedback) {
-    if (doubleInlineCallers.size() > 0) {
-      applyDoubleInlining = true;
-      List<DexEncodedMethod> methods = doubleInlineCallers
-          .stream()
-          .sorted(DexEncodedMethod::slowCompare)
-          .collect(Collectors.toList());
-      for (DexEncodedMethod method : methods) {
-        converter.processMethod(
-            method,
-            feedback,
-            x -> false,
-            CallSiteInformation.empty(),
-            Outliner::noProcessing);
-        assert method.isProcessed();
-      }
+  public void processDoubleInlineCallers(
+      IRConverter converter, ExecutorService executorService, OptimizationFeedback feedback)
+      throws ExecutionException {
+    if (doubleInlineCallers.isEmpty()) {
+      return;
     }
+    applyDoubleInlining = true;
+    List<Future<?>> futures = new ArrayList<>();
+    for (DexEncodedMethod method : doubleInlineCallers) {
+      futures.add(
+          executorService.submit(
+              () -> {
+                converter.processMethod(
+                    method,
+                    feedback,
+                    doubleInlineCallers::contains,
+                    CallSiteInformation.empty(),
+                    Outliner::noProcessing);
+                assert method.isProcessed();
+                return null;
+              }));
+    }
+    ThreadUtils.awaitFutures(futures);
   }
 
   /**
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 236cb92..f53c120 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -94,6 +94,7 @@
     String javaOutput = runOnJava(main);
     TestRunResult result = testForR8(backend)
         .addProgramClasses(classes)
+        .enableInliningAnnotations()
         .addKeepMainRule(main)
         .addKeepRules(
             "-dontobfuscate", "-allowaccessmodification", "-keepattributes LineNumberTable")
@@ -172,7 +173,6 @@
     String javaOutput = runOnJava(main);
     TestRunResult result = testForR8(backend)
         .addProgramClasses(classes)
-        .enableProguardTestOptions()
         .enableInliningAnnotations()
         .addKeepMainRule(main)
         .addKeepRules(
@@ -277,6 +277,7 @@
     String javaOutput = runOnJava(main);
     TestRunResult result = testForR8(backend)
         .addProgramClasses(classes)
+        .enableInliningAnnotations()
         .addKeepMainRule(main)
         .addKeepRules(
             "-dontobfuscate", "-allowaccessmodification", "-keepattributes LineNumberTable")
@@ -317,6 +318,8 @@
     String javaOutput = runOnJava(main);
     TestRunResult result = testForR8(backend)
         .addProgramClasses(classes)
+        .enableProguardTestOptions()
+        .enableInliningAnnotations()
         .addKeepMainRule(main)
         .addKeepRules(
             "-dontobfuscate", "-allowaccessmodification", "-keepattributes LineNumberTable")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/code/C.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/code/C.java
index 38e75c4..dcfb5eb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/code/C.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/code/C.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.ir.optimize.classinliner.code;
 
+import com.android.tools.r8.NeverInline;
+
 public class C {
   public static class L {
     public final int x;
@@ -25,15 +27,18 @@
     }
   }
 
-  public synchronized static int method1() {
+  @NeverInline
+  public static int method1() {
     return new L(1).x;
   }
 
-  public synchronized static int method2() {
+  @NeverInline
+  public static int method2() {
     return new L(1).getX();
   }
 
-  public synchronized static int method3() {
+  @NeverInline
+  public static int method3() {
     return F.I.getX();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java
index 26dc8cc..8bba53e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.ir.optimize.classinliner.invalidroot;
 
+import com.android.tools.r8.NeverInline;
+
 public class InvalidRootsTestClass {
   private static int ID = 0;
 
@@ -19,7 +21,8 @@
     test.testRootInvalidatesAfterInlining();
   }
 
-  private synchronized void testExtraNeverReturnsNormally() {
+  @NeverInline
+  private void testExtraNeverReturnsNormally() {
     testExtraNeverReturnsNormallyA();
     testExtraNeverReturnsNormallyB();
 
@@ -31,7 +34,8 @@
     }
   }
 
-  private synchronized void testExtraNeverReturnsNormallyA() {
+  @NeverInline
+  private void testExtraNeverReturnsNormallyA() {
     try {
       neverReturnsNormallyExtra(next(), null);
     } catch (RuntimeException re) {
@@ -39,7 +43,8 @@
     }
   }
 
-  private synchronized void testExtraNeverReturnsNormallyB() {
+  @NeverInline
+  private void testExtraNeverReturnsNormallyB() {
     try {
       neverReturnsNormallyExtra(next(), null);
     } catch (RuntimeException re) {
@@ -47,7 +52,8 @@
     }
   }
 
-  private synchronized void testDirectNeverReturnsNormally() {
+  @NeverInline
+  private void testDirectNeverReturnsNormally() {
     try {
       NeverReturnsNormally a = new NeverReturnsNormally();
       System.out.println(a.foo());
@@ -56,7 +62,8 @@
     }
   }
 
-  private synchronized void testInitNeverReturnsNormally() {
+  @NeverInline
+  private void testInitNeverReturnsNormally() {
     try {
       new InitNeverReturnsNormally();
     } catch (RuntimeException re) {
@@ -85,7 +92,8 @@
     }
   }
 
-  private synchronized void testRootInvalidatesAfterInlining() {
+  @NeverInline
+  private void testRootInvalidatesAfterInlining() {
     A a = new A();
     try {
       notInlinedExtraMethod(next(), a);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java
index 7e3cba2..a2576bf 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.ir.optimize.classinliner.trivial;
 
+import com.android.tools.r8.NeverInline;
+
 public class TrivialTestClass {
   private static int ID = 0;
 
@@ -25,35 +27,42 @@
     test.testCycles();
   }
 
-  private synchronized void testInner() {
+  @NeverInline
+  private void testInner() {
     Inner inner = new Inner("inner{", 123, next() + "}");
     System.out.println(inner.toString() + " " + inner.getPrefix() + " = " + inner.prefix);
   }
 
-  private synchronized void testConstructorMapping1() {
+  @NeverInline
+  private void testConstructorMapping1() {
     ReferencedFields o = new ReferencedFields(next());
     System.out.println(o.getA());
   }
 
-  private synchronized void testConstructorMapping2() {
+  @NeverInline
+  private void testConstructorMapping2() {
     ReferencedFields o = new ReferencedFields(next());
     System.out.println(o.getB());
   }
 
-  private synchronized void testConstructorMapping3() {
+  @NeverInline
+  private void testConstructorMapping3() {
     ReferencedFields o = new ReferencedFields(next(), next());
     System.out.println(o.getA() + o.getB() + o.getConcat());
   }
 
-  private synchronized void testEmptyClass() {
+  @NeverInline
+  private void testEmptyClass() {
     new EmptyClass();
   }
 
-  private synchronized void testEmptyClassWithInitializer() {
+  @NeverInline
+  private void testEmptyClassWithInitializer() {
     new EmptyClassWithInitializer();
   }
 
-  private synchronized void testClassWithFinalizer() {
+  @NeverInline
+  private void testClassWithFinalizer() {
     new ClassWithFinal();
   }
 
@@ -61,7 +70,8 @@
     iface.foo();
   }
 
-  private synchronized void testCallOnIface1() {
+  @NeverInline
+  private void testCallOnIface1() {
     callOnIface1(new Iface1Impl(next()));
   }
 
@@ -69,14 +79,18 @@
     iface.foo();
   }
 
-  private synchronized void testCallOnIface2() {
+  @NeverInline
+  private void testCallOnIface2() {
     callOnIface2(new Iface2Impl(next()));
     System.out.println(Iface2Impl.CONSTANT); // Keep constant referenced
   }
 
-  private synchronized void testCycles() {
+  @NeverInline
+  private void testCycles() {
     new CycleReferenceAB("first").foo(3);
     new CycleReferenceBA("second").foo(4);
+    new CycleReferenceAB("third").foo(5);
+    new CycleReferenceBA("fourth").foo(6);
   }
 
   public class Inner {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoubleInliningInvokeSuperTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoubleInliningInvokeSuperTest.java
new file mode 100644
index 0000000..5d8b1f0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoubleInliningInvokeSuperTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.inliner;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import org.junit.Test;
+
+// Regression test for b/128987064
+public class DoubleInliningInvokeSuperTest extends TestBase {
+
+  @Test
+  public void test() throws Exception {
+    testForR8(Backend.DEX)
+        .addInnerClasses(DoubleInliningInvokeSuperTest.class)
+        .addKeepMainRule(TestClass.class)
+        .addKeepRules("-keepclassmembers class * { void fooCaller(...); }")
+        .enableClassInliningAnnotations()
+        .enableInliningAnnotations()
+        .enableMergeAnnotations()
+        .run(TestClass.class)
+        .assertSuccess();
+  }
+
+  @NeverClassInline
+  @NeverMerge
+  static class A {
+    int x;
+    @NeverInline
+    A foo(int x) {
+      this.x = x;
+      return this;
+    }
+  }
+
+  @NeverClassInline
+  @NeverMerge
+  static class B extends A {
+    // B#foo is invoked twice by other wrappers in the same class.
+    @Override
+    B foo(int x) {
+      // this invoke-super should not be inlined to the class outside of the class hierarchy.
+      super.foo(x);
+      return this;
+    }
+
+    // place-holder to make B#foo to be a double-inline selected target.
+    B fooWrapper(Integer x) {
+      int y = System.currentTimeMillis() > 0 ? x * 2 : x;
+      return foo(y);
+    }
+
+    // Another B#foo caller.
+    B anotherWrapper(Integer x) {
+      int y = System.currentTimeMillis() > 0 ? x * 2 : x;
+      // invoke-super in B#foo is inlined here during double-inlining.
+      return foo(y);
+    }
+  }
+
+  static class TestClass {
+    // place-holder to make B#fooWrapper live. This one is force kept.
+    static void fooCaller(B b) {
+      System.out.println(b.fooWrapper(8).x);
+    }
+
+    public static void main(String[] args) {
+      B instance = new B();
+      // By invoking B#anotherWrapper twice, `main` is regarded as double-inline caller.
+      // Due to the name order, B#fooWrapper is processed first, and invoke-super is moved.
+      // Its inlining constraints aren't updated yet since inliner does not finish processing all
+      // the methods in the double-inline pool. Now, when processing `main`, invoke-super in
+      // B#anotherWrapper (which is inlined from B#foo) is flown to here, resulting in illegal
+      // invoke-super at the 2nd tree shaking phase.
+      System.out.println(instance.anotherWrapper(8).x);
+      System.out.println(instance.anotherWrapper(8).x);
+    }
+  }
+
+}