Add a @NeverSingleCallerInline annotation for testing
Fixes: 176066007
Change-Id: Ifa750f8b3dd558e6428af39f19f92cd704b2b3db
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
index cc2a612..e6b5afe 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
@@ -51,11 +51,8 @@
// program.
return Reason.SIMPLE;
}
- if (callSiteInformation.hasSingleCallSite(target)) {
- if (appView.options().testing.validInliningReasons == null
- || appView.options().testing.validInliningReasons.contains(Reason.SINGLE_CALLER)) {
- return Reason.SINGLE_CALLER;
- }
+ if (isSingleCallerInliningTarget(target)) {
+ return Reason.SINGLE_CALLER;
}
if (isDoubleInliningTarget(target)) {
return Reason.DUAL_CALLER;
@@ -63,6 +60,20 @@
return Reason.SIMPLE;
}
+ private boolean isSingleCallerInliningTarget(ProgramMethod method) {
+ if (!callSiteInformation.hasSingleCallSite(method)) {
+ return false;
+ }
+ if (appView.appInfo().isNeverInlineDueToSingleCallerMethod(method)) {
+ return false;
+ }
+ if (appView.options().testing.validInliningReasons != null
+ && !appView.options().testing.validInliningReasons.contains(Reason.SINGLE_CALLER)) {
+ return false;
+ }
+ return true;
+ }
+
private boolean isDoubleInliningTarget(ProgramMethod candidate) {
// 10 is found from measuring.
if (callSiteInformation.hasDoubleCallSite(candidate)
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 6fe65b8..49cff50 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -134,6 +134,11 @@
private final Set<DexMethod> forceInline;
/** All methods that *must* never be inlined due to a configuration directive (testing only). */
private final Set<DexMethod> neverInline;
+ /**
+ * All methods that *must* never be inlined as a result of having a single caller due to a
+ * configuration directive (testing only).
+ */
+ private final Set<DexMethod> neverInlineDueToSingleCaller;
/** Items for which to print inlining decisions for (testing only). */
private final Set<DexMethod> whyAreYouNotInlining;
/** All methods that may not have any parameters with a constant value removed. */
@@ -207,6 +212,7 @@
Set<DexMethod> alwaysInline,
Set<DexMethod> forceInline,
Set<DexMethod> neverInline,
+ Set<DexMethod> neverInlineDueToSingleCaller,
Set<DexMethod> whyAreYouNotInlining,
Set<DexMethod> keepConstantArguments,
Set<DexMethod> keepUnusedArguments,
@@ -244,6 +250,7 @@
this.alwaysInline = alwaysInline;
this.forceInline = forceInline;
this.neverInline = neverInline;
+ this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
this.whyAreYouNotInlining = whyAreYouNotInlining;
this.keepConstantArguments = keepConstantArguments;
this.keepUnusedArguments = keepUnusedArguments;
@@ -289,6 +296,7 @@
previous.alwaysInline,
previous.forceInline,
previous.neverInline,
+ previous.neverInlineDueToSingleCaller,
previous.whyAreYouNotInlining,
previous.keepConstantArguments,
previous.keepUnusedArguments,
@@ -335,6 +343,7 @@
previous.alwaysInline,
previous.forceInline,
previous.neverInline,
+ previous.neverInlineDueToSingleCaller,
previous.whyAreYouNotInlining,
previous.keepConstantArguments,
previous.keepUnusedArguments,
@@ -426,6 +435,7 @@
this.alwaysInline = previous.alwaysInline;
this.forceInline = previous.forceInline;
this.neverInline = previous.neverInline;
+ this.neverInlineDueToSingleCaller = previous.neverInlineDueToSingleCaller;
this.whyAreYouNotInlining = previous.whyAreYouNotInlining;
this.keepConstantArguments = previous.keepConstantArguments;
this.keepUnusedArguments = previous.keepUnusedArguments;
@@ -563,6 +573,10 @@
return neverInline.contains(method);
}
+ public boolean isNeverInlineDueToSingleCallerMethod(ProgramMethod method) {
+ return neverInlineDueToSingleCaller.contains(method.getReference());
+ }
+
public boolean isWhyAreYouNotInliningMethod(DexMethod method) {
return whyAreYouNotInlining.contains(method);
}
@@ -978,6 +992,7 @@
lens.rewriteMethods(alwaysInline),
lens.rewriteMethods(forceInline),
lens.rewriteMethods(neverInline),
+ lens.rewriteMethods(neverInlineDueToSingleCaller),
lens.rewriteMethods(whyAreYouNotInlining),
lens.rewriteMethods(keepConstantArguments),
lens.rewriteMethods(keepUnusedArguments),
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 21d1d50..826740f 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3329,6 +3329,7 @@
rootSet.alwaysInline,
rootSet.forceInline,
rootSet.neverInline,
+ rootSet.neverInlineDueToSingleCaller,
rootSet.whyAreYouNotInlining,
rootSet.keepConstantArguments,
rootSet.keepUnusedArguments,
diff --git a/src/main/java/com/android/tools/r8/shaking/InlineRule.java b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
index 41b6b33..32412d7 100644
--- a/src/main/java/com/android/tools/r8/shaking/InlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
@@ -18,7 +18,10 @@
};
public enum Type {
- ALWAYS, FORCE, NEVER
+ ALWAYS,
+ FORCE,
+ NEVER,
+ NEVER_SINGLE_CALLER
}
public static class Builder extends ProguardConfigurationRule.Builder<InlineRule, Builder> {
@@ -127,6 +130,8 @@
return "forceinline";
case NEVER:
return "neverinline";
+ case NEVER_SINGLE_CALLER:
+ return "neversinglecaller";
}
throw new Unreachable("Unknown inline type " + type);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index f71d027..8294be0 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -469,6 +469,11 @@
configurationBuilder.addRule(rule);
return true;
}
+ if (acceptString("neversinglecallerinline")) {
+ InlineRule rule = parseInlineRule(InlineRule.Type.NEVER_SINGLE_CALLER, optionStart);
+ configurationBuilder.addRule(rule);
+ return true;
+ }
if (acceptString(NoUnusedInterfaceRemovalRule.RULE_NAME)) {
ProguardConfigurationRule rule = parseNoUnusedInterfaceRemovalRule(optionStart);
configurationBuilder.addRule(rule);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index be431d3..bbcf912 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -91,6 +91,7 @@
private final Set<DexMethod> alwaysInline = Sets.newIdentityHashSet();
private final Set<DexMethod> forceInline = Sets.newIdentityHashSet();
private final Set<DexMethod> neverInline = Sets.newIdentityHashSet();
+ private final Set<DexMethod> neverInlineDueToSingleCaller = Sets.newIdentityHashSet();
private final Set<DexMethod> bypassClinitforInlining = Sets.newIdentityHashSet();
private final Set<DexMethod> whyAreYouNotInlining = Sets.newIdentityHashSet();
private final Set<DexMethod> keepParametersWithConstantValue = Sets.newIdentityHashSet();
@@ -347,6 +348,7 @@
alwaysInline,
forceInline,
neverInline,
+ neverInlineDueToSingleCaller,
bypassClinitforInlining,
whyAreYouNotInlining,
keepParametersWithConstantValue,
@@ -431,6 +433,7 @@
ConsequentRootSet buildConsequentRootSet() {
return new ConsequentRootSet(
neverInline,
+ neverInlineDueToSingleCaller,
neverClassInline,
noShrinking,
softPinned,
@@ -1198,15 +1201,19 @@
context.markAsUsed();
} else if (context instanceof InlineRule) {
if (item.isDexEncodedMethod()) {
+ DexMethod reference = item.asDexEncodedMethod().getReference();
switch (((InlineRule) context).getType()) {
case ALWAYS:
- alwaysInline.add(item.asDexEncodedMethod().method);
+ alwaysInline.add(reference);
break;
case FORCE:
- forceInline.add(item.asDexEncodedMethod().method);
+ forceInline.add(reference);
break;
case NEVER:
- neverInline.add(item.asDexEncodedMethod().method);
+ neverInline.add(reference);
+ break;
+ case NEVER_SINGLE_CALLER:
+ neverInlineDueToSingleCaller.add(reference);
break;
default:
throw new Unreachable();
@@ -1331,6 +1338,7 @@
abstract static class RootSetBase {
final Set<DexMethod> neverInline;
+ final Set<DexMethod> neverInlineDueToSingleCaller;
final Set<DexType> neverClassInline;
final MutableItemsWithRules noShrinking;
final MutableItemsWithRules softPinned;
@@ -1342,6 +1350,7 @@
RootSetBase(
Set<DexMethod> neverInline,
+ Set<DexMethod> neverInlineDueToSingleCaller,
Set<DexType> neverClassInline,
MutableItemsWithRules noShrinking,
MutableItemsWithRules softPinned,
@@ -1351,6 +1360,7 @@
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
this.neverInline = neverInline;
+ this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
this.neverClassInline = neverClassInline;
this.noShrinking = noShrinking;
this.softPinned = softPinned;
@@ -1777,6 +1787,7 @@
Set<DexMethod> alwaysInline,
Set<DexMethod> forceInline,
Set<DexMethod> neverInline,
+ Set<DexMethod> neverInlineDueToSingleCaller,
Set<DexMethod> bypassClinitForInlining,
Set<DexMethod> whyAreYouNotInlining,
Set<DexMethod> keepConstantArguments,
@@ -1801,6 +1812,7 @@
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
super(
neverInline,
+ neverInlineDueToSingleCaller,
neverClassInline,
noShrinking,
softPinned,
@@ -1850,6 +1862,7 @@
void addConsequentRootSet(ConsequentRootSet consequentRootSet, boolean addNoShrinking) {
neverInline.addAll(consequentRootSet.neverInline);
+ neverInlineDueToSingleCaller.addAll(consequentRootSet.neverInlineDueToSingleCaller);
neverClassInline.addAll(consequentRootSet.neverClassInline);
noObfuscation.addAll(consequentRootSet.noObfuscation);
if (addNoShrinking) {
@@ -2107,6 +2120,7 @@
ConsequentRootSet(
Set<DexMethod> neverInline,
+ Set<DexMethod> neverInlineDueToSingleCaller,
Set<DexType> neverClassInline,
MutableItemsWithRules noShrinking,
MutableItemsWithRules softPinned,
@@ -2117,6 +2131,7 @@
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
super(
neverInline,
+ neverInlineDueToSingleCaller,
neverClassInline,
noShrinking,
softPinned,
diff --git a/src/test/java/com/android/tools/r8/NeverSingleCallerInline.java b/src/test/java/com/android/tools/r8/NeverSingleCallerInline.java
new file mode 100644
index 0000000..8c710fb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NeverSingleCallerInline.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
+public @interface NeverSingleCallerInline {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index ebae827..e831d72 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -367,6 +367,14 @@
"-forceinline class * { @" + annotationPackageName + ".ForceInline *; }");
}
+ public T enableNeverSingleCallerInlineAnnotations() {
+ return addNeverSingleCallerInlineAnnotations()
+ .addInternalKeepRules(
+ "-neversinglecallerinline class * {",
+ " @com.android.tools.r8.NeverSingleCallerInline <methods>;",
+ "}");
+ }
+
public T enableNeverClassInliningAnnotations() {
return addNeverClassInliningAnnotations()
.addInternalKeepRules("-neverclassinline @com.android.tools.r8.NeverClassInline class *");
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 37673c2..1bcbc55 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -343,6 +343,10 @@
return addTestingAnnotation(NeverReprocessMethod.class);
}
+ public final T addNeverSingleCallerInlineAnnotations() {
+ return addTestingAnnotation(NeverSingleCallerInline.class);
+ }
+
public final T addNoHorizontalClassMergingAnnotations() {
return addTestingAnnotation(NoHorizontalClassMerging.class);
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningTestBase.java
index 2aef42a..61c9edc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningTestBase.java
@@ -36,18 +36,10 @@
public void configure(R8FullTestBuilder testBuilder) {
testBuilder
.addOptionsModification(this::enableSimpleInliningConstraints)
- .addOptionsModification(this::disableSingleCallerInlining)
.setMinApi(parameters.getApiLevel());
}
private void enableSimpleInliningConstraints(InternalOptions options) {
options.enableSimpleInliningConstraints = enableSimpleInliningConstraints;
}
-
- // TODO(b/176066007): Introduce a @NeverSingleCallerInline instead.
- private void disableSingleCallerInlining(InternalOptions options) {
- assert options.testing.validInliningReasons == null;
- options.testing.validInliningReasons = SetUtils.newIdentityHashSet(Inliner.Reason.values());
- options.testing.validInliningReasons.remove(Inliner.Reason.SINGLE_CALLER);
- }
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfNullOrNotNullInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfNullOrNotNullInliningTest.java
index 2f605ac..c5073c5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfNullOrNotNullInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfNullOrNotNullInliningTest.java
@@ -10,6 +10,7 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NeverSingleCallerInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -50,6 +51,7 @@
.addProgramClasses(mainClass, TestMethods.class)
.addKeepMainRule(mainClass)
.apply(this::configure)
+ .enableNeverSingleCallerInlineAnnotations()
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), mainClass)
@@ -114,6 +116,7 @@
static class TestMethods {
+ @NeverSingleCallerInline
static void simpleIfNullTest(Object o) {
if (o == null) {
return;
@@ -132,6 +135,7 @@
System.out.println("!");
}
+ @NeverSingleCallerInline
static void simpleIfBothNullTest(Object o1, Object o2) {
if (o1 == null && o2 == null) {
return;
@@ -150,6 +154,7 @@
System.out.println("!");
}
+ @NeverSingleCallerInline
static void simpleIfNotNullTest(Object o) {
if (o != null) {
return;
@@ -168,6 +173,7 @@
System.out.println("!");
}
+ @NeverSingleCallerInline
static void simpleIfBothNotNullTest(Object o1, Object o2) {
if (o1 != null && o2 != null) {
return;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfTrueOrFalseInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfTrueOrFalseInliningTest.java
index 6c0debe..b7a7186 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfTrueOrFalseInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfTrueOrFalseInliningTest.java
@@ -10,6 +10,7 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NeverSingleCallerInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -50,6 +51,7 @@
.addProgramClasses(mainClass, TestMethods.class)
.addKeepMainRule(mainClass)
.apply(this::configure)
+ .enableNeverSingleCallerInlineAnnotations()
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), mainClass)
@@ -112,6 +114,7 @@
static class TestMethods {
+ @NeverSingleCallerInline
static void simpleIfTrueTest(boolean b) {
if (b) {
return;
@@ -130,6 +133,7 @@
System.out.println("!");
}
+ @NeverSingleCallerInline
static void simpleIfBothTrueTest(boolean b1, boolean b2) {
if (b1 && b2) {
return;
@@ -148,6 +152,7 @@
System.out.println("!");
}
+ @NeverSingleCallerInline
static void simpleIfFalseTest(boolean b) {
if (!b) {
return;
@@ -166,6 +171,7 @@
System.out.println("!");
}
+ @NeverSingleCallerInline
static void simpleIfBothFalseTest(boolean b1, boolean b2) {
if (!b1 && !b2) {
return;