Extend remaining outliner tests to R8

Change-Id: I0c2d9733352b65ee200153986c2b3744463dbe44
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
index 8d98060..65a5c1a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
@@ -17,7 +17,6 @@
 import static com.android.tools.r8.ir.code.Opcodes.RETURN;
 import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
 
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
@@ -71,24 +70,15 @@
    * value must have in order for the program to type check.
    */
   public static TypeElement computeUseType(AppView<?> appView, DexType returnType, Value value) {
-    if (appView.hasClassHierarchy()) {
-      return computeUseType(appView, returnType, value, (s, t) -> s.join(t, appView));
-    } else {
-      AppView<AppInfo> appViewWithoutClassHierarchy = appView.withoutClassHierarchy();
-      AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
-      return computeUseType(
-          appView,
-          returnType,
-          value,
-          (s, t) -> joinWithoutClassHierarchy(s, t, appViewWithoutClassHierarchy, appInfo));
-    }
+    AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
+    return computeUseType(appView, returnType, value, (s, t) -> meet(s, t, appView, appInfo));
   }
 
   private static TypeElement computeUseType(
       AppView<?> appView,
       DexType returnType,
       Value value,
-      BiFunction<TypeElement, TypeElement, TypeElement> joiner) {
+      BiFunction<TypeElement, TypeElement, TypeElement> meet) {
     TypeElement staticType = value.getType();
     TypeElement useType = TypeElement.getBottom();
     WorkList<UserAndValuePair> users = WorkList.newEqualityWorkList();
@@ -101,9 +91,8 @@
       } else {
         Instruction instruction = user.asInstruction();
         TypeElement instructionUseType =
-            computeUseTypeForInstruction(
-                appView, returnType, instruction, item.value, joiner, users);
-        useType = joiner.apply(useType, instructionUseType);
+            computeUseTypeForInstruction(appView, returnType, instruction, item.value, meet, users);
+        useType = meet.apply(useType, instructionUseType);
         if (useType.isTop() || useType.equalUpToNullability(staticType)) {
           // Bail-out.
           return staticType;
@@ -127,7 +116,7 @@
       DexType returnType,
       Instruction instruction,
       Value value,
-      BiFunction<TypeElement, TypeElement, TypeElement> joiner,
+      BiFunction<TypeElement, TypeElement, TypeElement> meet,
       WorkList<UserAndValuePair> users) {
     switch (instruction.opcode()) {
       case ASSUME:
@@ -138,13 +127,13 @@
       case INSTANCE_GET:
         return computeUseTypeForInstanceGet(appView, instruction.asInstanceGet());
       case INSTANCE_PUT:
-        return computeUseTypeForInstancePut(appView, instruction.asInstancePut(), value, joiner);
+        return computeUseTypeForInstancePut(appView, instruction.asInstancePut(), value, meet);
       case INVOKE_DIRECT:
       case INVOKE_INTERFACE:
       case INVOKE_STATIC:
       case INVOKE_SUPER:
       case INVOKE_VIRTUAL:
-        return computeUseTypeForInvoke(appView, instruction.asInvokeMethod(), value, joiner);
+        return computeUseTypeForInvoke(appView, instruction.asInvokeMethod(), value, meet);
       case RETURN:
         return computeUseTypeForReturn(appView, returnType);
       case STATIC_PUT:
@@ -170,14 +159,14 @@
       AppView<?> appView,
       InstancePut instancePut,
       Value value,
-      BiFunction<TypeElement, TypeElement, TypeElement> joiner) {
+      BiFunction<TypeElement, TypeElement, TypeElement> meet) {
     DexField field = instancePut.getField();
     TypeElement useType = TypeElement.getBottom();
     if (instancePut.object() == value) {
-      useType = joiner.apply(useType, field.getHolderType().toTypeElement(appView));
+      useType = meet.apply(useType, field.getHolderType().toTypeElement(appView));
     }
     if (instancePut.value() == value) {
-      useType = joiner.apply(useType, field.getType().toTypeElement(appView));
+      useType = meet.apply(useType, field.getType().toTypeElement(appView));
     }
     return useType;
   }
@@ -186,7 +175,7 @@
       AppView<?> appView,
       InvokeMethod invoke,
       Value value,
-      BiFunction<TypeElement, TypeElement, TypeElement> joiner) {
+      BiFunction<TypeElement, TypeElement, TypeElement> meet) {
     TypeElement useType = TypeElement.getBottom();
     for (int argumentIndex = 0; argumentIndex < invoke.arguments().size(); argumentIndex++) {
       Value argument = invoke.getArgument(argumentIndex);
@@ -198,7 +187,7 @@
               .getInvokedMethod()
               .getArgumentType(argumentIndex, invoke.isInvokeStatic())
               .toTypeElement(appView);
-      useType = joiner.apply(useType, useTypeForArgument);
+      useType = meet.apply(useType, useTypeForArgument);
     }
     assert !useType.isBottom();
     return useType;
@@ -212,19 +201,24 @@
     return staticPut.getField().getType().toTypeElement(appView);
   }
 
-  private static TypeElement joinWithoutClassHierarchy(
-      TypeElement type,
-      TypeElement other,
-      AppView<AppInfo> appView,
-      AppInfoWithClassHierarchy appInfo) {
-    assert !other.isBottom();
+  private static TypeElement meet(
+      TypeElement type, TypeElement other, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
+    if (other.isBottom()) {
+      return type;
+    }
     if (type.isBottom()) {
       return other;
     }
     if (type.isTop() || other.isTop()) {
       return TypeElement.getTop();
     }
-    if (type.equals(other)) {
+    if (type.equalUpToNullability(other)) {
+      if (type.isReferenceType()) {
+        if (!type.nullability().equals(other.nullability())) {
+          return type.asReferenceType()
+              .getOrCreateVariant(type.nullability().meet(other.nullability()));
+        }
+      }
       return type;
     }
     if (type.isPrimitiveType()) {
@@ -242,11 +236,16 @@
     DexType classType = type.asClassType().getClassType();
     DexType otherClassType = other.asClassType().getClassType();
     if (appInfo.isSubtype(classType, otherClassType)) {
-      return ClassTypeElement.createForD8(classType, type.nullability().join(other.nullability()));
+      if (type.nullability().equals(other.nullability())) {
+        return type;
+      }
+      return type.asClassType().getOrCreateVariant(type.nullability().meet(other.nullability()));
     }
     if (appInfo.isSubtype(otherClassType, classType)) {
-      return ClassTypeElement.createForD8(
-          otherClassType, type.nullability().join(other.nullability()));
+      if (type.nullability().equals(other.nullability())) {
+        return other;
+      }
+      return other.asClassType().getOrCreateVariant(type.nullability().meet(other.nullability()));
     }
     return TypeElement.getTop();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlineMarkerRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlineMarkerRewriter.java
index 004e148..e9de503 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlineMarkerRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlineMarkerRewriter.java
@@ -75,7 +75,9 @@
         appView,
         callerPosition,
         outline.getProtoChanges(),
-        MethodConversionOptions.forD8(appView, method));
+        appView.enableWholeProgramOptimizations()
+            ? MethodConversionOptions.forLirPhase(appView)
+            : MethodConversionOptions.forD8(appView, method));
   }
 
   private void finalizeCode(ProgramMethod method, IRCode code) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerArrayUseTypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerArrayUseTypeTest.java
index 0ee7d9b..d07c1e6 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerArrayUseTypeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerArrayUseTypeTest.java
@@ -3,22 +3,46 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize.outliner.exceptions;
 
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.KeepUnusedArguments;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import java.util.Collection;
 import org.junit.Test;
 
 public class ThrowBlockOutlinerArrayUseTypeTest extends ThrowBlockOutlinerTestBase {
 
   @Test
-  public void test() throws Exception {
-    testForD8(parameters)
-        .addInnerClasses(getClass())
-        .apply(this::configure)
-        .compile()
+  public void testD8() throws Exception {
+    runTest(testForD8(parameters));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeRelease();
+    runTest(
+        testForR8(parameters)
+            .addKeepMainRule(Main.class)
+            .enableInliningAnnotations()
+            .enableUnusedArgumentAnnotations());
+  }
+
+  private void runTest(
+      TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> testBuilder)
+      throws Exception {
+    TestCompileResult<?, ?> compileResult =
+        testBuilder.addInnerClasses(getClass()).apply(this::configure).compile();
+
+    ClassSubject exceptionClassSubject = compileResult.inspector().clazz(MyException.class);
+    compileResult
         .run(parameters.getRuntime(), Main.class)
-        .assertFailureWithErrorThatThrows(MyException.class);
+        .assertFailureWithErrorThatMatches(containsString(exceptionClassSubject.getFinalName()));
   }
 
   @Override
@@ -46,6 +70,8 @@
 
   static class MyException extends RuntimeException {
 
+    @KeepUnusedArguments
+    @NeverInline
     MyException(String msg, Main[] main) {}
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerConstArgumentTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerConstArgumentTest.java
index cd6c533..2badee6 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerConstArgumentTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerConstArgumentTest.java
@@ -10,8 +10,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.SingleTestRunResult;
 import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.optimize.outliner.exceptions.ThrowBlockOutlinerArrayUseTypeTest.Main;
 import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -24,9 +27,21 @@
 public class ThrowBlockOutlinerConstArgumentTest extends ThrowBlockOutlinerTestBase {
 
   @Test
-  public void test() throws Exception {
+  public void testD8() throws Exception {
+    runTest(testForD8(parameters));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeRelease();
+    runTest(testForR8(parameters).addKeepMainRule(Main.class));
+  }
+
+  private void runTest(
+      TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> testBuilder)
+      throws Exception {
     TestCompileResult<?, ?> compileResult =
-        testForD8(parameters)
+        testBuilder
             .addInnerClasses(getClass())
             .apply(this::configure)
             .compile()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerInterfaceMethodDesugaringTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerInterfaceMethodDesugaringTest.java
index 3b9a6e4..d0c21e3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerInterfaceMethodDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerInterfaceMethodDesugaringTest.java
@@ -6,8 +6,11 @@
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.SingleTestRunResult;
 import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.optimize.outliner.exceptions.ThrowBlockOutlinerArrayUseTypeTest.Main;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.util.Collection;
 import org.junit.Test;
@@ -15,9 +18,21 @@
 public class ThrowBlockOutlinerInterfaceMethodDesugaringTest extends ThrowBlockOutlinerTestBase {
 
   @Test
-  public void test() throws Exception {
+  public void testD8() throws Exception {
+    runTest(testForD8(parameters));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeRelease();
+    runTest(testForR8(parameters).addKeepMainRule(Main.class));
+  }
+
+  private void runTest(
+      TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> testBuilder)
+      throws Exception {
     TestCompileResult<?, ?> compileResult =
-        testForD8(parameters)
+        testBuilder
             .addProgramClasses(Main.class, A.class)
             .addProgramClassFileData(
                 transformer(I.class)
@@ -25,7 +40,7 @@
                     .transform())
             .apply(this::configure)
             .compile()
-            .inspect(this::inspectOutput);
+            .inspect(inspector -> inspectOutput(inspector, testBuilder));
     compileResult
         .run(parameters.getRuntime(), Main.class, "default")
         .assertFailureWithErrorThatThrows(RuntimeException.class)
@@ -49,7 +64,12 @@
     assertEquals(3, outline.getNumberOfUsers());
   }
 
-  private void inspectOutput(CodeInspector inspector) {
+  private void inspectOutput(
+      CodeInspector inspector,
+      TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> testBuilder) {
+    if (testBuilder.isR8TestBuilder()) {
+      return;
+    }
     // Main, I, I$-CC, A and the synthetic outline class.
     assertEquals(
         parameters.canUseDefaultAndStaticInterfaceMethods() ? 4 : 5, inspector.allClasses().size());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerNoArgumentsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerNoArgumentsTest.java
index 174b194..cfb6c30 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerNoArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerNoArgumentsTest.java
@@ -47,6 +47,7 @@
 
   @Test
   public void testR8() throws Exception {
+    assumeRelease();
     runTest(testForR8(parameters).addKeepMainRule(Main.class));
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerSharedStringBuilderTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerSharedStringBuilderTest.java
index bebc31b..51d44e7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerSharedStringBuilderTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerSharedStringBuilderTest.java
@@ -10,9 +10,12 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.SingleTestRunResult;
 import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.optimize.outliner.exceptions.ThrowBlockOutlinerArrayUseTypeTest.Main;
 import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -25,9 +28,21 @@
 public class ThrowBlockOutlinerSharedStringBuilderTest extends ThrowBlockOutlinerTestBase {
 
   @Test
-  public void test() throws Exception {
+  public void testD8() throws Exception {
+    runTest(testForD8(parameters));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeRelease();
+    runTest(testForR8(parameters).addKeepMainRule(Main.class));
+  }
+
+  private void runTest(
+      TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> testBuilder)
+      throws Exception {
     TestCompileResult<?, ?> compileResult =
-        testForD8(parameters)
+        testBuilder
             .addInnerClasses(getClass())
             .apply(this::configure)
             .compile()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerTestBase.java
index f507e28..a3edcff 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerTestBase.java
@@ -5,6 +5,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.SingleTestRunResult;
@@ -49,6 +50,13 @@
     assertTrue(receivedCallback.isTrue());
   }
 
+  public void assumeRelease() {
+    if (mode.isDebug()) {
+      receivedCallback.set();
+    }
+    assumeTrue(mode.isRelease());
+  }
+
   public void configure(
       TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> testBuilder) {
     testBuilder
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerUseTypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerUseTypeTest.java
index d48861d..5a3fcdb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerUseTypeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerUseTypeTest.java
@@ -3,22 +3,46 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize.outliner.exceptions;
 
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.KeepUnusedArguments;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import java.util.Collection;
 import org.junit.Test;
 
 public class ThrowBlockOutlinerUseTypeTest extends ThrowBlockOutlinerTestBase {
 
   @Test
-  public void test() throws Exception {
-    testForD8(parameters)
-        .addInnerClasses(getClass())
-        .apply(this::configure)
-        .compile()
+  public void testD8() throws Exception {
+    runTest(testForD8(parameters));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeRelease();
+    runTest(
+        testForR8(parameters)
+            .addKeepMainRule(Main.class)
+            .enableInliningAnnotations()
+            .enableUnusedArgumentAnnotations());
+  }
+
+  private void runTest(
+      TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> testBuilder)
+      throws Exception {
+    TestCompileResult<?, ?> compileResult =
+        testBuilder.addInnerClasses(getClass()).apply(this::configure).compile();
+
+    ClassSubject exceptionClassSubject = compileResult.inspector().clazz(MyException.class);
+    compileResult
         .run(parameters.getRuntime(), Main.class)
-        .assertFailureWithErrorThatThrows(MyException.class);
+        .assertFailureWithErrorThatMatches(containsString(exceptionClassSubject.getFinalName()));
   }
 
   @Override
@@ -46,6 +70,8 @@
 
   static class MyException extends RuntimeException {
 
+    @KeepUnusedArguments
+    @NeverInline
     MyException(String msg, Main main) {}
   }
 }