Extend startup outlining to nowinandroid

Bug: b/275292237
Change-Id: Ib353e80a8bae66f10bfa2f70df8f53ce5d5bc0b2
diff --git a/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java
index fd7a46d..4363bdf 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java
@@ -27,8 +27,8 @@
  * <p>Subclasses can override the lookup methods.
  *
  * <p>For method mapping where invocation type can change just override {@link
- * #mapInvocationType(DexMethod, DexMethod, InvokeType)} if the default name mapping applies, and
- * only invocation type might need to change.
+ * #mapInvocationType(DexMethod, DexMethod, DexMethod, InvokeType)} if the default name mapping
+ * applies, and only invocation type might need to change.
  */
 public class NestedGraphLens extends DefaultNonIdentityGraphLens {
 
@@ -199,7 +199,10 @@
                   rewrittenReboundReference))
           .setType(
               mapInvocationType(
-                  rewrittenReboundReference, previous.getReference(), previous.getType()))
+                  rewrittenReference,
+                  rewrittenReboundReference,
+                  previous.getReference(),
+                  previous.getType()))
           .build();
     } else {
       // TODO(b/168282032): We should always have the rebound reference, so this should become
@@ -220,7 +223,8 @@
       return MethodLookupResult.builder(this, codeLens)
           .setReference(newMethod)
           .setPrototypeChanges(newPrototypeChanges)
-          .setType(mapInvocationType(newMethod, previous.getReference(), previous.getType()))
+          .setType(
+              mapInvocationType(newMethod, newMethod, previous.getReference(), previous.getType()))
           .build();
     }
   }
@@ -271,7 +275,7 @@
    * method or {@link #lookupMethod(DexMethod, DexMethod, InvokeType)}
    */
   protected InvokeType mapInvocationType(
-      DexMethod newMethod, DexMethod originalMethod, InvokeType type) {
+      DexMethod newMethod, DexMethod newReboundMethod, DexMethod originalMethod, InvokeType type) {
     return type;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index 5d60881..ca1d499 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -196,7 +196,7 @@
         .setPrototypeChanges(
             internalDescribePrototypeChanges(
                 previous.getPrototypeChanges(), previous.getReference(), result))
-        .setType(mapInvocationType(result, previous.getReference(), previous.getType()))
+        .setType(mapInvocationType(result, result, previous.getReference(), previous.getType()))
         .build();
   }
 
@@ -244,7 +244,7 @@
   @Override
   @SuppressWarnings("ReferenceEquality")
   protected InvokeType mapInvocationType(
-      DexMethod newMethod, DexMethod originalMethod, InvokeType type) {
+      DexMethod newMethod, DexMethod newReboundMethod, DexMethod originalMethod, InvokeType type) {
     if (typeMap.containsKey(originalMethod.getHolderType())) {
       // Methods moved from unboxed enums to the utility class are either static or statified.
       assert newMethod != originalMethod;
diff --git a/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java b/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
index d14a8bd..0b52815 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
@@ -138,7 +138,7 @@
               || (type.isDirect()
                   && (newType.isInterface() || newType.isStatic() || newType.isVirtual()))
               || (type.isInterface() && (newType.isStatic() || newType.isVirtual()))
-              || (type.isSuper() && newType.isVirtual())
+              || (type.isSuper() && (newType.isStatic() || newType.isVirtual()))
               || (type.isVirtual() && (newType.isInterface() || newType.isStatic()))
           : type + " -> " + newType;
       numberOfInvokeOpcodeChanges++;
@@ -301,6 +301,9 @@
       if (LirOpcodeUtils.isInvokeInterface(opcode)) {
         return InvokeType.INTERFACE;
       }
+      if (LirOpcodeUtils.isInvokeSuper(opcode)) {
+        return InvokeType.SUPER;
+      }
       if (LirOpcodeUtils.isInvokeVirtual(opcode)) {
         return InvokeType.VIRTUAL;
       }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirOpcodeUtils.java b/src/main/java/com/android/tools/r8/lightir/LirOpcodeUtils.java
index ec788d1..d9efe9b 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirOpcodeUtils.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirOpcodeUtils.java
@@ -78,6 +78,10 @@
     }
   }
 
+  public static boolean isInvokeSuper(int opcode) {
+    return opcode == INVOKESUPER || opcode == INVOKESUPER_ITF;
+  }
+
   public static boolean isInvokeVirtual(int opcode) {
     return opcode == INVOKEVIRTUAL;
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index a5a4a61..87a811b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -89,13 +89,13 @@
 
   @Override
   protected InvokeType mapInvocationType(
-      DexMethod newMethod, DexMethod originalMethod, InvokeType type) {
-    return hasPrototypeChanges(newMethod)
-            && getPrototypeChanges(newMethod)
+      DexMethod newMethod, DexMethod newReboundMethod, DexMethod originalMethod, InvokeType type) {
+    return hasPrototypeChanges(newReboundMethod)
+            && getPrototypeChanges(newReboundMethod)
                 .getArgumentInfoCollection()
                 .isConvertedToStaticMethod()
         ? InvokeType.STATIC
-        : super.mapInvocationType(newMethod, originalMethod, type);
+        : super.mapInvocationType(newMethod, newReboundMethod, originalMethod, type);
   }
 
   public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index e524b2a..990c260 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -440,13 +440,16 @@
             assert !info.isPinned(options)
                 || info.isMinificationAllowed(options)
                 || newMethod.name.isIdenticalTo(method.name);
-            assert !info.isPinned(options) || newMethod.getArity() == method.getArity();
+            assert !info.isPinned(options)
+                || newMethod.getArity() == method.getArity()
+                || (info.isShrinkingAllowed(options) && lens.isNonStartupInStartupOutlinerLens());
             assert !info.isPinned(options)
                 || Streams.zip(
                         newMethod.getParameters().stream(),
                         method.getParameters().stream().map(lens::lookupType),
                         Object::equals)
-                    .allMatch(x -> x);
+                    .allMatch(x -> x)
+                || (info.isShrinkingAllowed(options) && lens.isNonStartupInStartupOutlinerLens());
             assert !info.isPinned(options)
                 || newMethod.getReturnType().isIdenticalTo(lens.lookupType(method.getReturnType()));
             KeepMethodInfo previous = newMethodInfo.put(newMethod, info);
diff --git a/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutliner.java b/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutliner.java
index 627c46b..5e5f3bd 100644
--- a/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutliner.java
+++ b/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutliner.java
@@ -41,6 +41,8 @@
 import java.util.concurrent.ExecutorService;
 import java.util.function.Consumer;
 
+// TODO(b/275292237): Preserve class initialization side effects using InitClass.
+// TODO(b/275292237): Preserve receiver NPE semantics by inserting null checks.
 public class NonStartupInStartupOutliner {
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
@@ -103,7 +105,7 @@
         method -> {
           if (!startupProfile.containsMethodRule(method.getReference())
               && !method.getDefinition().isInitializer()
-              && !hasPrivateOrSuperAccess(method)) {
+              && canCodeBeMoved(method)) {
             fn.accept(method);
           }
         });
@@ -111,20 +113,16 @@
 
   // TODO(b/275292237): Extend to cover all possible accesses to private items (e.g., consider
   //  method handles).
-  private boolean hasPrivateOrSuperAccess(ProgramMethod method) {
+  private boolean canCodeBeMoved(ProgramMethod method) {
     return method.registerCodeReferencesWithResult(
-        new DefaultUseRegistryWithResult<>(appView, method, false) {
+        new DefaultUseRegistryWithResult<>(appView, method, true) {
 
           private AppInfoWithClassHierarchy appInfo() {
             return NonStartupInStartupOutliner.this.appView.appInfo();
           }
 
-          private void setHasPrivateAccess() {
-            setResult(true);
-          }
-
-          private void setHasSuperAccess() {
-            setResult(true);
+          private void setCodeCannotBeMoved() {
+            setResult(false);
           }
 
           // Field accesses.
@@ -152,8 +150,14 @@
           private void registerFieldAccess(DexField field) {
             DexClassAndField resolvedField =
                 appInfo().resolveField(field, getContext()).getResolutionPair();
-            if (resolvedField != null && resolvedField.getAccessFlags().isPrivate()) {
-              setHasPrivateAccess();
+            if (resolvedField == null) {
+              return;
+            }
+            if (resolvedField.getAccessFlags().isPrivate()) {
+              setCodeCannotBeMoved();
+            } else if (resolvedField.getAccessFlags().isProtected()
+                && !resolvedField.isSamePackage(getContext())) {
+              setCodeCannotBeMoved();
             }
           }
 
@@ -176,7 +180,7 @@
 
           @Override
           public void registerInvokeSuper(DexMethod method) {
-            setHasSuperAccess();
+            setCodeCannotBeMoved();
           }
 
           @Override
@@ -186,8 +190,14 @@
 
           private void registerInvokeMethod(MethodResolutionResult resolutionResult) {
             DexClassAndMethod resolvedMethod = resolutionResult.getResolutionPair();
-            if (resolvedMethod != null && resolvedMethod.getAccessFlags().isPrivate()) {
-              setHasPrivateAccess();
+            if (resolvedMethod == null) {
+              return;
+            }
+            if (resolvedMethod.getAccessFlags().isPrivate()) {
+              setCodeCannotBeMoved();
+            } else if (resolvedMethod.getAccessFlags().isProtected()
+                && !resolvedMethod.isSamePackage(getContext())) {
+              setCodeCannotBeMoved();
             }
           }
         });
@@ -267,7 +277,8 @@
     }
     // Virtual methods can only be staticized and moved if they are monomorphic.
     assert method.getAccessFlags().belongsToVirtualPool();
-    return monomorphicVirtualMethods.contains(method);
+    return method.getDefinition().isLibraryMethodOverride().isFalse()
+        && monomorphicVirtualMethods.contains(method);
   }
 
   private ProgramMethod performMove(
@@ -324,16 +335,23 @@
                             method.getReference(), !method.getAccessFlags().isStatic()))
                     .setCode(
                         syntheticMethod -> {
-                          Code code =
-                              method
-                                  .getDefinition()
-                                  .getCode()
-                                  .getCodeAsInlining(
-                                      syntheticMethod,
-                                      true,
-                                      method.getReference(),
-                                      method.getDefinition().isD8R8Synthesized(),
-                                      factory);
+                          Code code;
+                          if (method.getDefinition().getCode().supportsPendingInlineFrame()) {
+                            code = method.getDefinition().getCode();
+                            builder.setInlineFrame(
+                                method.getReference(), method.getDefinition().isD8R8Synthesized());
+                          } else {
+                            code =
+                                method
+                                    .getDefinition()
+                                    .getCode()
+                                    .getCodeAsInlining(
+                                        syntheticMethod,
+                                        true,
+                                        method.getReference(),
+                                        method.getDefinition().isD8R8Synthesized(),
+                                        factory);
+                          }
                           if (!method.getAccessFlags().isStatic()) {
                             DexEncodedMethod.setDebugInfoWithFakeThisParameter(
                                 code, syntheticMethod.getArity(), appView);
diff --git a/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutlinerLens.java b/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutlinerLens.java
index 086eab6..61ee4ec 100644
--- a/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutlinerLens.java
+++ b/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutlinerLens.java
@@ -31,11 +31,8 @@
 
   @Override
   protected InvokeType mapInvocationType(
-      DexMethod newMethod, DexMethod previousMethod, InvokeType type) {
-    if (newMethod.isIdenticalTo(previousMethod)) {
-      return type;
-    }
-    return InvokeType.STATIC;
+      DexMethod newMethod, DexMethod newReboundMethod, DexMethod previousMethod, InvokeType type) {
+    return newMethod.isIdenticalTo(previousMethod) ? type : InvokeType.STATIC;
   }
 
   public static class Builder {
@@ -50,7 +47,6 @@
     public synchronized void recordMove(ProgramMethod from, ProgramMethod to) {
       newMethodSignatures.put(from.getReference(), to.getReference());
     }
-
     public NonStartupInStartupOutlinerLens build(
         AppView<? extends AppInfoWithClassHierarchy> appView) {
       return new NonStartupInStartupOutlinerLens(appView, newMethodSignatures);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index b758859..27ae067 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -35,6 +35,7 @@
   private DexProto proto = null;
   private CfVersion classFileVersion;
   private SyntheticCodeGenerator codeGenerator = null;
+  private DexMethod pendingInlineFrame = null;
   private MethodAccessFlags accessFlags = null;
   private MethodTypeSignature genericSignature = MethodTypeSignature.noSignature();
   private DexAnnotationSet annotations = DexAnnotationSet.empty();
@@ -95,6 +96,14 @@
     return this;
   }
 
+  public SyntheticMethodBuilder setInlineFrame(DexMethod callee, boolean calleeIsD8R8Synthesized) {
+    if (!calleeIsD8R8Synthesized) {
+      // // No need to record a compiler synthesized inline frame.
+      this.pendingInlineFrame = callee;
+    }
+    return this;
+  }
+
   public SyntheticMethodBuilder setAccessFlags(MethodAccessFlags accessFlags) {
     this.accessFlags = accessFlags;
     return this;
@@ -144,6 +153,7 @@
             .setAnnotations(annotations)
             .setParameterAnnotations(parameterAnnotationsList)
             .setCode(code)
+            .applyIf(pendingInlineFrame != null, b -> b.setInlineFrame(pendingInlineFrame, false))
             .setClassFileVersion(classFileVersion)
             .setApiLevelForDefinition(apiLevelForDefinition)
             .setApiLevelForCode(apiLevelForCode)
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
index 7f78da8..516241e 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
@@ -462,7 +462,11 @@
       assert residualIsD8R8Synthesized
           || originalMethod.isIdenticalTo(lensOriginalMethod)
           // TODO(b/326562454): In some case the lens is mapping two methods to a common original.
-          || originalMethod.getHolderType().isIdenticalTo(lensOriginalMethod.getHolderType());
+          || originalMethod.getHolderType().isIdenticalTo(lensOriginalMethod.getHolderType())
+          || appView
+              .getSyntheticItems()
+              .isSyntheticOfKind(
+                  method.getHolderType(), kinds -> kinds.NON_STARTUP_IN_STARTUP_OUTLINE);
       return true;
     }
 
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
index f6583d2..7860fed 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
@@ -222,7 +222,12 @@
           MethodLookupResult.builder(this, codeLens)
               .setReboundReference(newReboundReference)
               .setReference(newReference)
-              .setType(mapInvocationType(newReference, previous.getReference(), previous.getType()))
+              .setType(
+                  mapInvocationType(
+                      newReference,
+                      newReboundReference,
+                      previous.getReference(),
+                      previous.getType()))
               .setPrototypeChanges(
                   internalDescribePrototypeChanges(
                       previous.getPrototypeChanges(),
@@ -287,8 +292,8 @@
 
   @Override
   protected InvokeType mapInvocationType(
-      DexMethod newMethod, DexMethod previousMethod, InvokeType type) {
-    if (isStaticized(newMethod)) {
+      DexMethod newMethod, DexMethod newReboundMethod, DexMethod previousMethod, InvokeType type) {
+    if (isStaticized(newReboundMethod)) {
       return InvokeType.STATIC;
     }
     if (type.isInterface()