Minor updates in preparation for constructor inlining

Change-Id: Iee0ee6fb319658a2495bc7b8f24d7b42a16901f1
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeAnalyzer.java b/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeAnalyzer.java
index be806b4..88ee7f1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeAnalyzer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeAnalyzer.java
@@ -6,14 +6,18 @@
 import static com.android.tools.r8.ir.code.Opcodes.ARGUMENT;
 import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
 import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
+import static com.android.tools.r8.ir.code.Opcodes.GOTO;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
 import static com.android.tools.r8.ir.code.Opcodes.RETURN;
 
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.CheckCast;
+import com.android.tools.r8.ir.code.Goto;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 import com.android.tools.r8.ir.code.Return;
@@ -24,17 +28,15 @@
 
   /** Returns a {@link BridgeInfo} object describing this method if it is recognized as a bridge. */
   public static BridgeInfo analyzeMethod(DexEncodedMethod method, IRCode code) {
-    if (code.blocks.size() > 1) {
-      return failure();
-    }
-
     // Scan through the instructions one-by-one. We expect a sequence of Argument instructions,
     // followed by a (possibly empty) sequence of CheckCast instructions, followed by a single
     // InvokeMethod instruction, followed by an optional CheckCast instruction, followed by a Return
     // instruction.
     InvokeMethodWithReceiver uniqueInvoke = null;
     CheckCast uniqueReturnCast = null;
-    for (Instruction instruction : code.entryBlock().getInstructions()) {
+    InstructionListIterator instructionIterator = code.entryBlock().listIterator(code);
+    while (instructionIterator.hasNext()) {
+      Instruction instruction = instructionIterator.next();
       switch (instruction.opcode()) {
         case ARGUMENT:
           break;
@@ -74,6 +76,17 @@
             break;
           }
 
+        case GOTO:
+          {
+            Goto gotoInstruction = instruction.asGoto();
+            BasicBlock targetBlock = gotoInstruction.getTarget();
+            if (targetBlock.hasCatchHandlers()) {
+              return failure();
+            }
+            instructionIterator = targetBlock.listIterator(code);
+            break;
+          }
+
         case RETURN:
           if (!analyzeReturn(instruction.asReturn(), uniqueInvoke, uniqueReturnCast)) {
             return failure();
@@ -107,8 +120,10 @@
     }
     int argumentIndex = object.definition.asArgument().getIndex();
     Value castValue = checkCast.outValue();
-    // The out value cannot have any phi users since there is only one block.
-    assert !castValue.hasPhiUsers();
+    // The out value should not have any phi users since we only allow linear control flow.
+    if (castValue.hasPhiUsers()) {
+      return false;
+    }
     // It is not allowed to have any other users than the invoke instruction.
     if (castValue.hasDebugUsers() || !castValue.hasSingleUniqueUser()) {
       return false;
@@ -142,8 +157,10 @@
     Value returnValue = invoke.outValue();
     Value uncastValue = checkCast.object().getAliasedValue();
     Value castValue = checkCast.outValue();
-    // The out value cannot have any phi users since there is only one block.
-    assert !castValue.hasPhiUsers();
+    // The out value should not have any phi users since we only allow linear control flow.
+    if (castValue.hasPhiUsers()) {
+      return false;
+    }
     // It must cast the result to the return type of the enclosing method and return the cast value.
     return uncastValue == returnValue
         && checkCast.getType() == method.returnType()
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
index d8ec5a2..44c94c4 100644
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
@@ -116,6 +116,9 @@
     if (kind == InvokeKind.STATIC) {
       return appView.appInfo().isStrictSubtypeOf(method.getHolderType(), target.holder);
     }
+    if (kind == InvokeKind.VIRTUAL) {
+      return false;
+    }
     assert false : "Unexpected invoke-kind for visibility bridge: " + kind;
     return false;
   }
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/EffectiveFinalFieldMarkedFinalTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/EffectiveFinalFieldMarkedFinalTest.java
index 83d80c8..d491189 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/EffectiveFinalFieldMarkedFinalTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/EffectiveFinalFieldMarkedFinalTest.java
@@ -10,6 +10,7 @@
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+import com.android.tools.r8.NoRedundantFieldLoadElimination;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -42,6 +43,7 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .allowAccessModification(allowAccessModification)
+        .enableNoRedundantFieldLoadEliminationAnnotations()
         .setMinApi(parameters)
         .compile()
         .inspect(
@@ -63,6 +65,7 @@
 
     static String staticField = System.currentTimeMillis() > 0 ? "Hello" : null;
 
+    @NoRedundantFieldLoadElimination
     String instanceField = System.currentTimeMillis() > 0 ? ", world!" : null;
 
     public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
index abee6a9..dff5d14 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
 import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.CoreMatchers.not;
@@ -77,7 +78,9 @@
                 assertThat(base, not(isPresent()));
                 ClassSubject sub = inspector.clazz(Sub.class);
                 assertThat(sub, isPresent());
-                assertThat(sub.uniqueInstanceInitializer(), isPresent());
+                assertThat(
+                    sub.uniqueInstanceInitializer(),
+                    isAbsentIf(parameters.canHaveNonReboundConstructorInvoke()));
                 assertEquals(1, sub.virtualMethods().size());
                 FoundMethodSubject callCallApi = sub.virtualMethods().get(0);
                 assertEquals("callCallApi", callCallApi.getOriginalName());
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
index 7bc2df5..2e368e9 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
 import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.CoreMatchers.not;
@@ -76,7 +77,9 @@
                 assertThat(base, not(isPresent()));
                 ClassSubject sub = inspector.clazz(Sub.class);
                 assertThat(sub, isPresent());
-                assertThat(sub.uniqueInstanceInitializer(), isPresent());
+                assertThat(
+                    sub.uniqueInstanceInitializer(),
+                    isAbsentIf(parameters.canHaveNonReboundConstructorInvoke()));
                 assertEquals(1, sub.virtualMethods().size());
                 FoundMethodSubject callCallApi = sub.virtualMethods().get(0);
                 assertEquals("callCallApi", callCallApi.getOriginalName());
diff --git a/src/test/java/com/android/tools/r8/classmerging/StatefulSingletonClassesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/StatefulSingletonClassesMergingTest.java
index c36c12b..3604880 100644
--- a/src/test/java/com/android/tools/r8/classmerging/StatefulSingletonClassesMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/StatefulSingletonClassesMergingTest.java
@@ -85,7 +85,7 @@
 
     static final B INSTANCE = new B("B");
 
-    @NeverPropagateValue private final String data;
+    @NeverPropagateValue private String data;
 
     // TODO(b/198758663): With argument propagation the constructors end up not being equivalent,
     //  which prevents merging in the final round of horizontal class merging.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationEnumWithSubclassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationEnumWithSubclassTest.java
index 657d32e..fc7b6d5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationEnumWithSubclassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationEnumWithSubclassTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.optimize.membervaluepropagation.fields.singleton;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -36,6 +37,10 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(SingletonFieldValuePropagationEnumWithSubclassTest.class)
         .addKeepMainRule(TestClass.class)
+        .addEnumUnboxingInspector(
+            inspector ->
+                inspector.assertUnboxedIf(
+                    parameters.canInitNewInstanceUsingSuperclassConstructor(), Characters.class))
         .setMinApi(parameters)
         .compile()
         .inspect(this::inspect)
@@ -44,10 +49,14 @@
   }
 
   private void inspect(CodeInspector inspector) {
-    ClassSubject charactersClassSubject = inspector.clazz(Characters.class);
-    assertThat(charactersClassSubject, isPresent());
-    // TODO(b/150368955): Field value propagation should cause Characters.value to become dead.
-    assertEquals(1, charactersClassSubject.allInstanceFields().size());
+    if (parameters.canInitNewInstanceUsingSuperclassConstructor()) {
+      assertThat(inspector.clazz(Characters.class), isAbsent());
+    } else {
+      ClassSubject charactersClassSubject = inspector.clazz(Characters.class);
+      assertThat(charactersClassSubject, isPresent());
+      // TODO(b/150368955): Field value propagation should cause Characters.value to become dead.
+      assertEquals(1, charactersClassSubject.allInstanceFields().size());
+    }
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
index 65d8260..a3d1fef 100644
--- a/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
@@ -48,11 +48,13 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options ->
-                options
-                    .getRedundantBridgeRemovalOptions()
-                    .setEnableRetargetingOfConstructorBridgeCalls(
-                        enableRetargetingOfConstructorBridgeCalls))
+            options -> {
+              options.inlinerOptions().setEnableConstructorInlining(false);
+              options
+                  .getRedundantBridgeRemovalOptions()
+                  .setEnableRetargetingOfConstructorBridgeCalls(
+                      enableRetargetingOfConstructorBridgeCalls);
+            })
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters)