Leverage that most type elements have default interfaces in subtyping checks

Bug: b/422947619
Bug: b/424006396
Change-Id: If87cf909c530f3797d862ff23d4bf9d698cf60e1
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index f2240aa..d567fb6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -3552,6 +3552,11 @@
         .getOrCreateVariant(nullability);
   }
 
+  public InterfaceCollection getLeastUpperBoundOfImplementedInterfacesOrDefault(
+      DexType type, InterfaceCollection defaultValue) {
+    return classTypeInterfaces.getOrDefault(type, defaultValue);
+  }
+
   public InterfaceCollection getOrComputeLeastUpperBoundOfImplementedInterfaces(
       DexType type, AppView<? extends AppInfoWithClassHierarchy> appView) {
     return classTypeInterfaces.computeIfAbsent(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index 287f94e..a9ee774 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.ir.analysis.type.InterfaceCollection.Builder;
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.ObjectUtils;
 import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.SetUtils;
@@ -105,6 +106,22 @@
     return lazyInterfaces;
   }
 
+  public boolean hasDefaultImplementedInterfaces() {
+    assert appView != null : "Unexpected use of hasDefaultImplementedInterfaces() in D8";
+    if (lazyInterfaces == null) {
+      return true;
+    }
+    InterfaceCollection defaultImplementedInterfaces =
+        appView
+            .dexItemFactory()
+            .getLeastUpperBoundOfImplementedInterfacesOrDefault(getClassType(), null);
+    if (ObjectUtils.identical(lazyInterfaces, defaultImplementedInterfaces)) {
+      return true;
+    }
+    assert !lazyInterfaces.equals(defaultImplementedInterfaces);
+    return false;
+  }
+
   private ClassTypeElement createVariant(
       Nullability nullability, NullabilityVariants<ClassTypeElement> variants) {
     assert this.nullability != nullability;
@@ -161,7 +178,7 @@
       ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
     DexType thisClassType = getClassType();
     DexType otherClassType = otherClass.getType();
-    if (getInterfaces().isEmpty()) {
+    if (getInterfaces().isEmpty() || hasDefaultImplementedInterfaces()) {
       // <= can be implemented by a single class hierarchy check.
       if (thisClassType.isIdenticalTo(otherClassType)) {
         return true;
@@ -188,7 +205,7 @@
       ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
     DexType thisClassType = getClassType();
     DexType otherClassType = otherClass.getType();
-    if (getInterfaces().isEmpty()) {
+    if (getInterfaces().isEmpty() || hasDefaultImplementedInterfaces()) {
       // >= can be implemented by a single class hierarchy check.
       if (thisClassType.isIdenticalTo(otherClassType)) {
         return true;