Use nop inlining constraint for dead code elimination

Bug: 191131830
Bug: 132600418
Bug: 139276374
Change-Id: Ia5f5fc8c1541ff98210700c4d4eddefd3ed977f9
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
index e2deec9..2c78b93 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
 import com.android.tools.r8.references.FieldReference;
 
 public abstract class DexClassAndField extends DexClassAndMember<DexEncodedField, DexField> {
@@ -35,6 +36,11 @@
     return getReference().asFieldReference();
   }
 
+  @Override
+  public FieldOptimizationInfo getOptimizationInfo() {
+    return getDefinition().getOptimizationInfo();
+  }
+
   public DexType getType() {
     return getReference().getType();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
index 2ac7ced..4aebe97 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.optimize.info.MemberOptimizationInfo;
 import com.android.tools.r8.origin.Origin;
 
 public abstract class DexClassAndMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
@@ -49,6 +50,10 @@
     return definition.getReference();
   }
 
+  public MemberOptimizationInfo<?> getOptimizationInfo() {
+    return getDefinition().getOptimizationInfo();
+  }
+
   @Override
   public Origin getOrigin() {
     return holder.origin;
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index ccc7dbc..53cbfe8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
 import com.android.tools.r8.references.MethodReference;
 
 public abstract class DexClassAndMethod extends DexClassAndMember<DexEncodedMethod, DexMethod>
@@ -52,6 +53,11 @@
     return getReference().getSignature();
   }
 
+  @Override
+  public MethodOptimizationInfo getOptimizationInfo() {
+    return getDefinition().getOptimizationInfo();
+  }
+
   public DexType getParameter(int index) {
     return getReference().getParameter(index);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index 39f52cb..6a83961 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -269,6 +269,6 @@
       }
     }
 
-    return optimizationInfo.mayHaveSideEffects();
+    return optimizationInfo.mayHaveSideEffects(this, appView.options());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 1b3aef3..9313b59 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -216,11 +216,11 @@
     }
 
     // Verify that the target method does not have side-effects.
-    if (appViewWithLiveness.appInfo().noSideEffects.containsKey(singleTarget.getReference())) {
+    if (appViewWithLiveness.appInfo().isAssumeNoSideEffectsMethod(singleTarget)) {
       return false;
     }
 
-    if (singleTarget.getDefinition().getOptimizationInfo().mayHaveSideEffects()) {
+    if (singleTarget.getOptimizationInfo().mayHaveSideEffects(this, appView.options())) {
       return true;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 50443c6..b3dd6b6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -160,7 +160,7 @@
           }
 
           MethodOptimizationInfo optimizationInfo = target.getDefinition().getOptimizationInfo();
-          if (optimizationInfo.mayHaveSideEffects()
+          if (optimizationInfo.mayHaveSideEffects(invoke, appViewWithLiveness.options())
               || !optimizationInfo.returnValueOnlyDependsOnArguments()) {
             continue;
           }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 0aed133..a98bc38 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -12,11 +12,13 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableSet;
 import java.util.BitSet;
 import java.util.Set;
@@ -134,6 +136,11 @@
   }
 
   @Override
+  public SimpleInliningConstraint getNopInliningConstraint(InternalOptions options) {
+    return NeverSimpleInliningConstraint.getInstance();
+  }
+
+  @Override
   public SimpleInliningConstraint getSimpleInliningConstraint() {
     return NeverSimpleInliningConstraint.getInstance();
   }
@@ -169,6 +176,11 @@
   }
 
   @Override
+  public boolean mayHaveSideEffects(InvokeMethod invoke, InternalOptions options) {
+    return UNKNOWN_MAY_HAVE_SIDE_EFFECTS;
+  }
+
+  @Override
   public boolean returnValueOnlyDependsOnArguments() {
     return UNKNOWN_RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 748d82d..a25eabb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
@@ -78,6 +79,8 @@
 
   public abstract AbstractValue getAbstractReturnValue();
 
+  public abstract SimpleInliningConstraint getNopInliningConstraint(InternalOptions options);
+
   public abstract SimpleInliningConstraint getSimpleInliningConstraint();
 
   public abstract boolean forceInline();
@@ -90,6 +93,9 @@
 
   public abstract boolean mayHaveSideEffects();
 
+  /** Context sensitive version of {@link #mayHaveSideEffects()}. */
+  public abstract boolean mayHaveSideEffects(InvokeMethod invoke, InternalOptions options);
+
   public abstract boolean returnValueOnlyDependsOnArguments();
 
   public abstract boolean returnValueHasBeenPropagated();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 460b2a2..5b99126 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
@@ -24,6 +25,7 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import java.util.BitSet;
 import java.util.Optional;
 import java.util.Set;
@@ -322,6 +324,15 @@
   }
 
   @Override
+  public SimpleInliningConstraint getNopInliningConstraint(InternalOptions options) {
+    // We currently require that having a simple inlining constraint implies that the method becomes
+    // empty after inlining. Therefore, an invoke is a nop if the simple inlining constraint is
+    // satisfied (if the invoke does not trigger other side effects, such as class initialization).
+    assert options.simpleInliningConstraintThreshold == 0;
+    return getSimpleInliningConstraint();
+  }
+
+  @Override
   public SimpleInliningConstraint getSimpleInliningConstraint() {
     return simpleInliningConstraint;
   }
@@ -357,6 +368,17 @@
   }
 
   @Override
+  public boolean mayHaveSideEffects(InvokeMethod invoke, InternalOptions options) {
+    if (!mayHaveSideEffects()) {
+      return false;
+    }
+    if (getNopInliningConstraint(options).isSatisfied(invoke)) {
+      return false;
+    }
+    return true;
+  }
+
+  @Override
   public boolean returnValueOnlyDependsOnArguments() {
     return isFlagSet(RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG);
   }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index b75ac5a..0a1fcc7 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -263,7 +263,7 @@
   public boolean enableInliningOfInvokesWithNullableReceivers = true;
   public boolean disableInliningOfLibraryMethodOverrides = true;
   public boolean enableSimpleInliningConstraints = true;
-  public int simpleInliningConstraintThreshold = 0;
+  public final int simpleInliningConstraintThreshold = 0;
   public boolean enableClassInlining = true;
   public boolean enableClassStaticizer = true;
   public boolean enableInitializedClassesAnalysis = true;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/NopInliningConstraintTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/NopInliningConstraintTest.java
index c6cbbe4..3295203 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/NopInliningConstraintTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/NopInliningConstraintTest.java
@@ -55,10 +55,10 @@
                   mainClassSubject.uniqueMethodWithName("checkNotNull");
               assertThat(checkNotNullMethodSubject, isPresent());
 
-              // There are two calls to checkNotNull() in main().
-              // TODO(b/191131830): Should only be one.
+              // There is a single call to checkNotNull() in main(), as checkNotNull(newObject())
+              // is dead code eliminated.
               assertEquals(
-                  2,
+                  1,
                   mainClassSubject
                       .mainMethod()
                       .streamInstructions()
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
index 872b5f0..f6cc385 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
@@ -130,10 +130,10 @@
               MethodSubject method = main.uniqueMethodWithName(methodName);
               assertThat(method, isPresent());
               int arity = method.getMethod().getReference().getArity();
-              // One from the method's own argument, if any, and
-              // Two from Array utils, `contains` and `indexOf`, if inlined with access relaxation.
+              // One for each of the method's own arguments, unless building with
+              // -allowaccessmodification.
               assertEquals(
-                  allowAccessModification ? 0 : arity + 2,
+                  allowAccessModification ? 0 : arity,
                   countCall(method, "checkParameterIsNotNull"));
             });
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index 4769427..e83626b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -91,7 +91,7 @@
               // ?: in aOrDefault
               // TODO(b/179951729): Not the same amount of ifz on CF and DEX.
               assertEquals(testParameters.isCfRuntime() ? 0 : 1, ifzCount);
-              assertEquals(allowAccessModification ? 0 : 1, paramNullCheckCount);
+              assertEquals(0, paramNullCheckCount);
             });
   }