Synthesize -assumenosideeffects rule for MIN_SDK

Bug: 111763015
Change-Id: Ie8b11fd1d62ff76b3ed89448172d43dc39c735e7
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 624d6fe..e01b4ed 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -309,13 +309,11 @@
 
         // Add synthesized -assumevalues from min api if relevant.
         if (options.isGeneratingDex()) {
-          if (!ProguardConfigurationUtils.hasExplicitAssumeValuesRuleForMinSdk(
-              options.itemFactory,
-              options.getProguardConfiguration().getRules())) {
+          if (!ProguardConfigurationUtils.hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
+              options.itemFactory, options.getProguardConfiguration().getRules())) {
             synthesizedProguardRules.add(
-                ProguardConfigurationUtils.buildAssumeValuesForApiLevel(
-                    options.itemFactory,
-                    AndroidApiLevel.getAndroidApiLevel(options.minApiLevel)));
+                ProguardConfigurationUtils.buildAssumeNoSideEffectsRuleForApiLevel(
+                    options.itemFactory, AndroidApiLevel.getAndroidApiLevel(options.minApiLevel)));
           }
         }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index d229fa4..df73ae8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -81,15 +81,19 @@
   }
 
   private boolean mayPropagateValueFor(DexEncodedField field) {
-    return field.isProgramField(appView)
-        ? appView.appInfo().mayPropagateValueFor(field.field)
-        : appView.appInfo().assumedValues.containsKey(field.field);
+    if (field.isProgramField(appView)) {
+      return appView.appInfo().mayPropagateValueFor(field.field);
+    }
+    return appView.appInfo().assumedValues.containsKey(field.field)
+        || appView.appInfo().noSideEffects.containsKey(field.field);
   }
 
   private boolean mayPropagateValueFor(DexEncodedMethod method) {
-    return method.isProgramMethod(appView)
-        ? appView.appInfo().mayPropagateValueFor(method.method)
-        : appView.appInfo().assumedValues.containsKey(method.method);
+    if (method.isProgramMethod(appView)) {
+      return appView.appInfo().mayPropagateValueFor(method.method);
+    }
+    return appView.appInfo().assumedValues.containsKey(method.method)
+        || appView.appInfo().noSideEffects.containsKey(method.method);
   }
 
   private ProguardMemberRuleLookup lookupMemberRule(DexDefinition definition) {
@@ -319,7 +323,6 @@
     }
     ProguardMemberRuleLookup lookup = lookupMemberRule(target);
     if (lookup != null
-        && lookup.type == RuleType.ASSUME_VALUES
         && tryConstantReplacementFromProguard(
             code, affectedValues, blocks, iterator, current, lookup)) {
       return;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 39e5e9a..c5dcb0c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -85,7 +85,7 @@
     return builder.build();
   }
 
-  public static ProguardAssumeValuesRule buildAssumeValuesForApiLevel(
+  public static ProguardAssumeNoSideEffectRule buildAssumeNoSideEffectsRuleForApiLevel(
       DexItemFactory factory, AndroidApiLevel apiLevel) {
     Origin synthesizedFromApiLevel =
         new Origin(Origin.root()) {
@@ -100,36 +100,35 @@
     publicStaticFinalFlags.setStatic();
     publicStaticFinalFlags.setFinal();
 
-    return ProguardAssumeValuesRule
-        .builder()
+    return ProguardAssumeNoSideEffectRule.builder()
         .setOrigin(synthesizedFromApiLevel)
         .setClassType(ProguardClassType.CLASS)
         .setClassNames(
             ProguardClassNameList.singletonList(
                 ProguardTypeMatcher.create(factory.createType("Landroid/os/Build$VERSION;"))))
-        .setMemberRules(ImmutableList.of(
-            ProguardMemberRule.builder()
-                .setAccessFlags(publicStaticFinalFlags)
-                .setRuleType(ProguardMemberType.FIELD)
-                .setTypeMatcher(ProguardTypeMatcher.create(factory.intType))
-                .setName(IdentifierPatternWithWildcards.withoutWildcards("SDK_INT"))
-                .setReturnValue(
-                    new ProguardMemberRuleReturnValue(
-                        new LongInterval(apiLevel.getLevel(), Integer.MAX_VALUE)))
-                .build()
-        ))
+        .setMemberRules(
+            ImmutableList.of(
+                ProguardMemberRule.builder()
+                    .setAccessFlags(publicStaticFinalFlags)
+                    .setRuleType(ProguardMemberType.FIELD)
+                    .setTypeMatcher(ProguardTypeMatcher.create(factory.intType))
+                    .setName(IdentifierPatternWithWildcards.withoutWildcards("SDK_INT"))
+                    .setReturnValue(
+                        new ProguardMemberRuleReturnValue(
+                            new LongInterval(apiLevel.getLevel(), Integer.MAX_VALUE)))
+                    .build()))
         .build();
   }
 
   /**
-   * Check if an explicit rule matching the field
-   *   public static final int android.os.Build$VERSION.SDK_INT
-   * is present.
+   * Check if an explicit rule matching the field public static final int
+   * android.os.Build$VERSION.SDK_INT is present.
    */
-  public static boolean hasExplicitAssumeValuesRuleForMinSdk(
+  public static boolean hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
       DexItemFactory factory, List<ProguardConfigurationRule> rules) {
     for (ProguardConfigurationRule rule : rules) {
-      if (!(rule instanceof ProguardAssumeValuesRule)) {
+      if (!(rule instanceof ProguardAssumeValuesRule
+          || rule instanceof ProguardAssumeNoSideEffectRule)) {
         continue;
       }
       if (rule.getClassType() != ProguardClassType.CLASS) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MinSdkMemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MinSdkMemberValuePropagationTest.java
new file mode 100644
index 0000000..caad10b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MinSdkMemberValuePropagationTest.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MinSdkMemberValuePropagationTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final String rule;
+
+  @Parameterized.Parameters(name = "{0}, rule: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().build(),
+        ImmutableList.of("assumenosideeffects", "assumevalues"));
+  }
+
+  public MinSdkMemberValuePropagationTest(TestParameters parameters, String rule) {
+    this.parameters = parameters;
+    this.rule = rule;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(TestClass.class)
+        .addKeepMainRule(TestClass.class)
+        .addKeepRules(
+            "-" + rule + " class " + Library.class.getTypeName() + " {",
+            "  static int MIN_SDK return 42;",
+            "}")
+        .addLibraryClasses(Library.class)
+        .addLibraryFiles(runtimeJar(parameters))
+        .setMinApi(parameters.getRuntime())
+        .compile()
+        .inspect(this::verifyOutput)
+        .addRunClasspathFiles(
+            testForR8(parameters.getBackend())
+                .addProgramClasses(Library.class)
+                .addKeepAllClassesRule()
+                .setMinApi(parameters.getRuntime())
+                .compile()
+                .writeToZip())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void verifyOutput(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(TestClass.class);
+    assertThat(classSubject, isPresent());
+
+    MethodSubject mainSubject = classSubject.mainMethod();
+    assertThat(mainSubject, isPresent());
+
+    boolean readsMinSdkField =
+        mainSubject
+            .streamInstructions()
+            .anyMatch(x -> x.isStaticGet() && x.getField().name.toString().equals("MIN_SDK"));
+    assertEquals(rule.equals("assumevalues"), readsMinSdkField);
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      if (Library.MIN_SDK == 42) {
+        System.out.println("Hello world!");
+      }
+    }
+  }
+
+  static class Library {
+
+    static int MIN_SDK = -1;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
index 610f63b..66e49e6 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
@@ -7,7 +7,6 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
@@ -19,8 +18,7 @@
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
-import com.android.tools.r8.shaking.ProguardAssumeValuesRule;
-import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardAssumeNoSideEffectRule;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -138,7 +136,7 @@
   private void checkSynthesizedRuleExpectation(
       List<ProguardConfigurationRule> synthesizedRules, SynthesizedRule expected) {
     for (ProguardConfigurationRule rule : synthesizedRules) {
-      if (rule instanceof ProguardAssumeValuesRule
+      if (rule instanceof ProguardAssumeNoSideEffectRule
           && rule.getOrigin().part().contains("SYNTHESIZED_FROM_API_LEVEL")) {
         assertEquals(expected, SynthesizedRule.PRESENT);
         return;
@@ -147,12 +145,8 @@
     assertEquals(expected, SynthesizedRule.NOT_PRESENT);
   }
 
-  private void noSynthesizedRules(ProguardConfiguration proguardConfiguration) {
-    for (ProguardConfigurationRule rule : proguardConfiguration.getRules()) {
-      if (rule instanceof ProguardAssumeValuesRule) {
-        assertFalse(rule.getOrigin().part().contains("SYNTHESIZED_FROM_API_LEVEL"));
-      }
-    }
+  private void noSynthesizedRules(List<ProguardConfigurationRule> synthesizedRules) {
+    assertTrue(synthesizedRules.isEmpty());
   }
 
   private void runTest(
@@ -187,7 +181,7 @@
           .addKeepMainRule(mainClassName)
           .addKeepRules(additionalKeepRules)
           .compile()
-          .inspectProguardConfiguration(this::noSynthesizedRules)
+          .inspectSyntheticProguardRules(this::noSynthesizedRules)
           .addRunClasspathFiles(
               ImmutableList.of(mockAndroidRuntimeLibrary(AndroidApiLevel.D.getLevel())))
           .run(mainClassName)