Revert "Extend the range of assume instructions, part 5."
This reverts commit 49009e86fe3ee33e0ac088765c41ed3a06beaa86.
Revert "Extend the range of assume instructions, part 4."
This reverts commit aa48c9f1a855f089e8a5dce06bfd7f8130c41fd1.
Revert "Reland "Extend the range of assume instructions, part 3.""
This reverts commit f51913e75bb5e78bb2fcb60bdd535a50deff84a3.
Bug: 141667708, 141654799
Change-Id: Ibbba3050f0edccbe153bc12f664b4280edff3c4f
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index 18ba962..c4dfbc4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -153,10 +153,6 @@
return self;
}
- public boolean mayAffectStaticType() {
- return isAssumeNonNull();
- }
-
@Override
public boolean couldIntroduceAnAlias(AppView<?> appView, Value root) {
assert root != null && root.getTypeLattice().isReference();
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index cb32015..fa3074e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -1008,22 +1008,14 @@
}
public void removeAllTrivialPhis() {
- removeAllTrivialPhis(null, null);
+ removeAllTrivialPhis(null);
}
public void removeAllTrivialPhis(IRBuilder builder) {
- removeAllTrivialPhis(builder, null);
- }
-
- public void removeAllTrivialPhis(Set<Value> affectedValues) {
- removeAllTrivialPhis(null, affectedValues);
- }
-
- public void removeAllTrivialPhis(IRBuilder builder, Set<Value> affectedValues) {
for (BasicBlock block : blocks) {
List<Phi> phis = new ArrayList<>(block.getPhis());
for (Phi phi : phis) {
- phi.removeTrivialPhi(builder, affectedValues);
+ phi.removeTrivialPhi(builder);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 7cd8ca2..e2f9cf3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -123,7 +123,7 @@
builder.constrainType(operand, readConstraint);
appendOperand(operand);
}
- removeTrivialPhi(builder, null);
+ removeTrivialPhi(builder);
}
public void addOperands(List<Value> operands) {
@@ -224,10 +224,10 @@
}
public void removeTrivialPhi() {
- removeTrivialPhi(null, null);
+ removeTrivialPhi(null);
}
- void removeTrivialPhi(IRBuilder builder, Set<Value> affectedValues) {
+ public void removeTrivialPhi(IRBuilder builder) {
Value same = null;
for (Value op : operands) {
if (op == same || op == this) {
@@ -252,9 +252,6 @@
if (builder != null && typeLattice.isPreciseType() && !typeLattice.isBottom()) {
builder.constrainType(same, ValueTypeConstraint.fromTypeLattice(typeLattice));
}
- if (affectedValues != null) {
- affectedValues.addAll(this.affectedValues());
- }
// Removing this phi, so get rid of it as a phi user from all of the operands to avoid
// recursively getting back here with the same phi. If the phi has itself as an operand
// that also removes the self-reference.
@@ -280,7 +277,7 @@
replaceUsers(same);
// Try to simplify phi users that might now have become trivial.
for (Phi user : phiUsersToSimplify) {
- user.removeTrivialPhi(builder, affectedValues);
+ user.removeTrivialPhi(builder);
}
}
// Get rid of the phi itself.
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 6648777..2881de7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.utils.LongInterval;
import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
@@ -425,22 +424,6 @@
return users.getFirst();
}
- public Set<Instruction> aliasedUsers() {
- Set<Instruction> users = SetUtils.newIdentityHashSet(uniqueUsers());
- collectAliasedUsersViaAssume(uniqueUsers(), users);
- return users;
- }
-
- private static void collectAliasedUsersViaAssume(
- Set<Instruction> usersToTest, Set<Instruction> collectedUsers) {
- for (Instruction user : usersToTest) {
- if (user.isAssume()) {
- collectedUsers.addAll(user.outValue().uniqueUsers());
- collectAliasedUsersViaAssume(user.outValue().uniqueUsers(), collectedUsers);
- }
- }
- }
-
public Phi firstPhiUser() {
assert !phiUsers.isEmpty();
return phiUsers.getFirst();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 46f8bf4..dc2516c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1249,6 +1249,26 @@
assert code.verifyTypes(appView);
+ if (nonNullTracker != null) {
+ // TODO(b/139246447): Once we extend this optimization to, e.g., constants of primitive args,
+ // this may not be the right place to collect call site optimization info.
+ // Collecting call-site optimization info depends on the existence of non-null IRs.
+ // Arguments can be changed during the debug mode.
+ if (!isDebugMode && appView.callSiteOptimizationInfoPropagator() != null) {
+ appView.callSiteOptimizationInfoPropagator().collectCallSiteOptimizationInfo(code);
+ }
+ // Computation of non-null parameters on normal exits rely on the existence of non-null IRs.
+ nonNullTracker.computeNonNullParamOnNormalExits(feedback, code);
+ }
+ if (aliasIntroducer != null || nonNullTracker != null || dynamicTypeOptimization != null) {
+ codeRewriter.removeAssumeInstructions(code);
+ assert code.isConsistentSSA();
+ }
+ // Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
+ assert code.verifyNoNullabilityBottomTypes();
+
+ assert code.verifyTypes(appView);
+
previous = printMethod(code, "IR before class inlining (SSA)", previous);
if (classInliner != null) {
@@ -1310,6 +1330,26 @@
assert code.isConsistentSSA();
}
+ if (nonNullTracker != null) {
+ // TODO(b/139246447): Once we extend this optimization to, e.g., constants of primitive args,
+ // this may not be the right place to collect call site optimization info.
+ // Collecting call-site optimization info depends on the existence of non-null IRs.
+ // Arguments can be changed during the debug mode.
+ if (!isDebugMode && appView.callSiteOptimizationInfoPropagator() != null) {
+ appView.callSiteOptimizationInfoPropagator().collectCallSiteOptimizationInfo(code);
+ }
+ // Computation of non-null parameters on normal exits rely on the existence of non-null IRs.
+ nonNullTracker.computeNonNullParamOnNormalExits(feedback, code);
+ }
+ if (aliasIntroducer != null || nonNullTracker != null || dynamicTypeOptimization != null) {
+ codeRewriter.removeAssumeInstructions(code);
+ assert code.isConsistentSSA();
+ }
+ // Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
+ assert code.verifyNoNullabilityBottomTypes();
+
+ assert code.verifyTypes(appView);
+
previous = printMethod(code, "IR after lambda merger (SSA)", previous);
if (options.outline.enabled) {
@@ -1342,26 +1382,6 @@
classStaticizer.examineMethodCode(method, code);
}
- if (nonNullTracker != null) {
- // TODO(b/139246447): Once we extend this optimization to, e.g., constants of primitive args,
- // this may not be the right place to collect call site optimization info.
- // Collecting call-site optimization info depends on the existence of non-null IRs.
- // Arguments can be changed during the debug mode.
- if (!isDebugMode && appView.callSiteOptimizationInfoPropagator() != null) {
- appView.callSiteOptimizationInfoPropagator().collectCallSiteOptimizationInfo(code);
- }
- // Computation of non-null parameters on normal exits rely on the existence of non-null IRs.
- nonNullTracker.computeNonNullParamOnNormalExits(feedback, code);
- }
- if (aliasIntroducer != null || nonNullTracker != null || dynamicTypeOptimization != null) {
- codeRewriter.removeAssumeInstructions(code);
- assert code.isConsistentSSA();
- }
- // Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
- assert code.verifyNoNullabilityBottomTypes();
-
- assert code.verifyTypes(appView);
-
if (appView.enableWholeProgramOptimizations()) {
if (libraryMethodOverrideAnalysis != null) {
libraryMethodOverrideAnalysis.analyze(code);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 1f36d25..fba5ae7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -218,7 +218,7 @@
// Therefore, Assume elimination may result in a trivial phi:
// z <- phi(x, x)
if (needToCheckTrivialPhis) {
- code.removeAllTrivialPhis(valuesThatRequireWidening);
+ code.removeAllTrivialPhis();
}
if (!valuesThatRequireWidening.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 1845186..4837545 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -556,10 +556,8 @@
templateInstructions.add(OutlineInstruction.fromInstruction(current));
} else if (current.isConstInstruction()) {
// Don't include const instructions in the template.
- } else if (current.isAssume()) {
- // Don't include assume instructions in the template.
} else {
- assert false : "Unexpected type of instruction in outlining template:" + current;
+ assert false : "Unexpected type of instruction in outlining template.";
}
}
}
@@ -790,12 +788,6 @@
include = true;
instructionIncrement = 0;
}
- } else if (instruction.isAssume()) {
- // Technically, assume instructions will be removed, and thus it should not be included.
- // However, we should keep searching, so here we pretend to include it with 0 increment.
- // When adding instruction into the outline candidate, we filter out assume instructions.
- include = true;
- instructionIncrement = 0;
} else {
include = canIncludeInstruction(instruction);
}
@@ -994,16 +986,12 @@
// Add the current instruction to the outline.
private void includeInstruction(Instruction instruction) {
- if (instruction.isAssume()) {
- return;
- }
-
List<Value> inValues = orderedInValues(instruction, returnValue);
Value prevReturnValue = returnValue;
if (returnValue != null) {
for (Value value : inValues) {
- if (value.getAliasedValue() == returnValue) {
+ if (value == returnValue) {
assert returnValueUsersLeft > 0;
returnValueUsersLeft--;
}
@@ -1025,7 +1013,7 @@
|| instruction.isArithmeticBinop();
if (inValues.size() > 0) {
for (int i = 0; i < inValues.size(); i++) {
- Value value = inValues.get(i).getAliasedValue();
+ Value value = inValues.get(i);
if (value == prevReturnValue) {
argumentsMap.add(OutlineInstruction.OUTLINE_TEMP);
continue;
@@ -1079,6 +1067,7 @@
}
}
+
protected abstract void handle(int start, int end, Outline outline);
private void candidate(int start, int index) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 7df3542..b656678 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -8,11 +8,9 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionOrPhi;
-import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.Inliner;
@@ -20,11 +18,9 @@
import com.android.tools.r8.ir.optimize.string.StringOptimizer;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import java.util.Iterator;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -38,14 +34,8 @@
NON_CLASS_TYPE,
UNKNOWN_TYPE,
- // Used by isClassEligible
- NON_PROGRAM_CLASS,
- ABSTRACT_OR_INTERFACE,
- NEVER_CLASS_INLINE,
- HAS_FINALIZER,
- TRIGGER_CLINIT,
-
// Used by InlineCandidateProcessor#isClassAndUsageEligible
+ INELIGIBLE_CLASS,
HAS_CLINIT,
HAS_INSTANCE_FIELDS,
NON_FINAL_TYPE,
@@ -56,8 +46,7 @@
}
private final LambdaRewriter lambdaRewriter;
- private final ConcurrentHashMap<DexClass, EligibilityStatus> knownClasses =
- new ConcurrentHashMap<>();
+ private final ConcurrentHashMap<DexClass, Boolean> knownClasses = new ConcurrentHashMap<>();
public ClassInliner(LambdaRewriter lambdaRewriter) {
this.lambdaRewriter = lambdaRewriter;
@@ -67,15 +56,7 @@
DexEncodedMethod context, Instruction root, EligibilityStatus status) {
if (Log.ENABLED && Log.isLoggingEnabledFor(ClassInliner.class)) {
Log.info(getClass(), "At %s,", context.toSourceString());
- Log.info(getClass(), "ClassInlining eligibility of `%s`: %s.", root, status);
- }
- }
-
- private void logIneligibleUser(
- DexEncodedMethod context, Instruction root, InstructionOrPhi ineligibleUser) {
- if (Log.ENABLED && Log.isLoggingEnabledFor(ClassInliner.class)) {
- Log.info(getClass(), "At %s,", context.toSourceString());
- Log.info(getClass(), "Ineligible user of `%s`: `%s`.", root, ineligibleUser);
+ Log.info(getClass(), "ClassInlining eligibility of %s: %s,", root, status);
}
}
@@ -152,7 +133,7 @@
// return 1;
// }
// static int method3() {
- // return 123;
+ // return "F::getX";
// }
// }
//
@@ -214,7 +195,6 @@
InstructionOrPhi ineligibleUser = processor.areInstanceUsersEligible(defaultOracle);
if (ineligibleUser != null) {
// This root may succeed if users change in future.
- logIneligibleUser(code.method, root, ineligibleUser);
continue;
}
@@ -228,11 +208,7 @@
anyInlinedMethods |= processor.processInlining(code, defaultOracle);
// Restore normality.
- Set<Value> affectedValues = Sets.newIdentityHashSet();
- code.removeAllTrivialPhis(affectedValues);
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ code.removeAllTrivialPhis();
assert code.isConsistentSSA();
rootsIterator.remove();
repeat = true;
@@ -257,11 +233,11 @@
}
}
- private EligibilityStatus isClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
- EligibilityStatus eligible = knownClasses.get(clazz);
+ private boolean isClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
+ Boolean eligible = knownClasses.get(clazz);
if (eligible == null) {
- EligibilityStatus computed = computeClassEligible(appView, clazz);
- EligibilityStatus existing = knownClasses.putIfAbsent(clazz, computed);
+ boolean computed = computeClassEligible(appView, clazz);
+ Boolean existing = knownClasses.putIfAbsent(clazz, computed);
assert existing == null || existing == computed;
eligible = existing == null ? computed : existing;
}
@@ -272,19 +248,13 @@
// - is not an abstract class or interface
// - does not declare finalizer
// - does not trigger any static initializers except for its own
- private EligibilityStatus computeClassEligible(
- AppView<AppInfoWithLiveness> appView, DexClass clazz) {
- if (clazz == null) {
- return EligibilityStatus.UNKNOWN_TYPE;
- }
- if (clazz.isNotProgramClass()) {
- return EligibilityStatus.NON_PROGRAM_CLASS;
- }
- if (clazz.isAbstract() || clazz.isInterface()) {
- return EligibilityStatus.ABSTRACT_OR_INTERFACE;
- }
- if (appView.appInfo().neverClassInline.contains(clazz.type)) {
- return EligibilityStatus.NEVER_CLASS_INLINE;
+ private boolean computeClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
+ if (clazz == null
+ || clazz.isNotProgramClass()
+ || clazz.accessFlags.isAbstract()
+ || clazz.accessFlags.isInterface()
+ || appView.appInfo().neverClassInline.contains(clazz.type)) {
+ return false;
}
// Class must not define finalizer.
@@ -292,14 +262,11 @@
for (DexEncodedMethod method : clazz.virtualMethods()) {
if (method.method.name == dexItemFactory.finalizeMethodName
&& method.method.proto == dexItemFactory.objectMethods.finalize.proto) {
- return EligibilityStatus.HAS_FINALIZER;
+ return false;
}
}
// Check for static initializers in this class or any of interfaces it implements.
- if (clazz.initializationOfParentTypesMayHaveSideEffects(appView)) {
- return EligibilityStatus.TRIGGER_CLINIT;
- }
- return EligibilityStatus.ELIGIBLE;
+ return !clazz.initializationOfParentTypesMayHaveSideEffects(appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
index d06b3f9..a16e9a3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize.classinliner;
-import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import com.android.tools.r8.graph.AppView;
@@ -15,7 +14,6 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
-import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Phi.RegisterReadType;
import com.android.tools.r8.ir.code.Value;
@@ -41,9 +39,6 @@
this.code = code;
this.root = root;
this.appView = appView;
- // Verify that `root` is not aliased.
- assert root.hasOutValue();
- assert root.outValue() == root.outValue().getAliasedValue();
}
void replaceValue(Value oldValue, Value newValue) {
@@ -127,10 +122,10 @@
Instruction instruction = iterator.previous();
assert instruction != null;
- if (instruction == root
- || (instruction.isInstancePut()
- && instruction.asInstancePut().getField() == field
- && instruction.asInstancePut().object().getAliasedValue() == root.outValue())) {
+ if (instruction == root ||
+ (instruction.isInstancePut() &&
+ instruction.asInstancePut().getField() == field &&
+ instruction.asInstancePut().object() == root.outValue())) {
valueProducingInsn = instruction;
break;
}
@@ -145,17 +140,12 @@
assert root == valueProducingInsn;
if (defaultValue == null) {
- InstructionListIterator it = block.listIterator(code, root);
// If we met newInstance it means that default value is supposed to be used.
- if (field.type.isPrimitiveType()) {
- defaultValue = code.createValue(
- TypeLatticeElement.fromDexType(field.type, definitelyNotNull(), appView));
- ConstNumber defaultValueInsn = new ConstNumber(defaultValue, 0);
- defaultValueInsn.setPosition(root.getPosition());
- it.add(defaultValueInsn);
- } else {
- defaultValue = it.insertConstNullInstruction(code, appView.options());
- }
+ defaultValue =
+ code.createValue(TypeLatticeElement.fromDexType(field.type, maybeNull(), appView));
+ ConstNumber defaultValueInsn = new ConstNumber(defaultValue, 0);
+ defaultValueInsn.setPosition(root.getPosition());
+ block.listIterator(code, root).add(defaultValueInsn);
}
return defaultValue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 1318135..a2e9358 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -17,7 +17,6 @@
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
-import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
@@ -41,18 +40,16 @@
import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.kotlin.KotlinInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Pair;
-import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -63,7 +60,7 @@
private final AppView<AppInfoWithLiveness> appView;
private final LambdaRewriter lambdaRewriter;
private final Inliner inliner;
- private final Function<DexClass, EligibilityStatus> isClassEligible;
+ private final Predicate<DexClass> isClassEligible;
private final Predicate<DexEncodedMethod> isProcessedConcurrently;
private final DexEncodedMethod method;
private final Instruction root;
@@ -86,7 +83,7 @@
AppView<AppInfoWithLiveness> appView,
LambdaRewriter lambdaRewriter,
Inliner inliner,
- Function<DexClass, EligibilityStatus> isClassEligible,
+ Predicate<DexClass> isClassEligible,
Predicate<DexEncodedMethod> isProcessedConcurrently,
DexEncodedMethod method,
Instruction root) {
@@ -143,9 +140,8 @@
// * class has class initializer marked as TrivialClassInitializer, and
// class initializer initializes the field we are reading here.
EligibilityStatus isClassAndUsageEligible() {
- EligibilityStatus status = isClassEligible.apply(eligibleClassDefinition);
- if (status != EligibilityStatus.ELIGIBLE) {
- return status;
+ if (!isClassEligible.test(eligibleClassDefinition)) {
+ return EligibilityStatus.INELIGIBLE_CLASS;
}
if (root.isNewInstance()) {
@@ -255,7 +251,7 @@
*
* @return null if all users are eligible, or the first ineligible user.
*/
- InstructionOrPhi areInstanceUsersEligible(Supplier<InliningOracle> defaultOracle) {
+ protected InstructionOrPhi areInstanceUsersEligible(Supplier<InliningOracle> defaultOracle) {
// No Phi users.
if (eligibleInstance.numberOfPhiUsers() > 0) {
return eligibleInstance.firstPhiUser(); // Not eligible.
@@ -263,19 +259,11 @@
Set<Instruction> currentUsers = eligibleInstance.uniqueUsers();
while (!currentUsers.isEmpty()) {
- Set<Instruction> indirectUsers = Sets.newIdentityHashSet();
+ Set<Instruction> indirectUsers = new HashSet<>();
for (Instruction user : currentUsers) {
- if (user.isAssume()) {
- if (user.outValue().numberOfPhiUsers() > 0) {
- return user.outValue().firstPhiUser(); // Not eligible.
- }
- indirectUsers.addAll(user.outValue().uniqueUsers());
- continue;
- }
// Field read/write.
if (user.isInstanceGet()
- || (user.isInstancePut()
- && user.asInstancePut().value().getAliasedValue() != eligibleInstance)) {
+ || (user.isInstancePut() && user.asInstancePut().value() != eligibleInstance)) {
DexField field = user.asFieldInstruction().getField();
if (field.holder == eligibleClass
&& eligibleClassDefinition.lookupInstanceField(field) != null) {
@@ -300,7 +288,7 @@
boolean isCorrespondingConstructorCall =
root.isNewInstance()
&& !invoke.inValues().isEmpty()
- && root.outValue() == invoke.getReceiver();
+ && root.outValue() == invoke.inValues().get(0);
if (isCorrespondingConstructorCall) {
InliningInfo inliningInfo =
isEligibleConstructorCall(user.asInvokeDirect(), singleTarget, defaultOracle);
@@ -355,7 +343,6 @@
// Process inlining, includes the following steps:
//
- // * remove linked assume instructions if any so that users of the eligible field are up-to-date.
// * replace unused instance usages as arguments which are never used
// * inline extra methods if any, collect new direct method calls
// * inline direct methods if any
@@ -365,9 +352,6 @@
//
// Returns `true` if at least one method was inlined.
boolean processInlining(IRCode code, Supplier<InliningOracle> defaultOracle) {
- // Verify that `eligibleInstance` is not aliased.
- assert eligibleInstance == eligibleInstance.getAliasedValue();
-
replaceUsagesAsUnusedArgument(code);
boolean anyInlinedMethods = forceInlineExtraMethodInvocations(code);
@@ -390,14 +374,11 @@
// methods that need to be inlined anyway.
return true;
}
- assert extraMethodCalls.isEmpty()
- : "Remaining extra method calls: " + StringUtils.join(extraMethodCalls.entrySet(), ", ");
- assert unusedArguments.isEmpty()
- : "Remaining unused arg: " + StringUtils.join(unusedArguments, ", ");
+ assert extraMethodCalls.isEmpty();
+ assert unusedArguments.isEmpty();
}
anyInlinedMethods |= forceInlineDirectMethodInvocations(code);
- removeAssumeInstructionsLinkedToEligibleInstance();
removeMiscUsages(code);
removeFieldReads(code);
removeFieldWrites();
@@ -438,22 +419,6 @@
return true;
}
- private void removeAssumeInstructionsLinkedToEligibleInstance() {
- for (Instruction user : eligibleInstance.aliasedUsers()) {
- if (!user.isAssume()) {
- continue;
- }
- Assume<?> assumeInstruction = user.asAssume();
- Value src = assumeInstruction.src();
- Value dest = assumeInstruction.outValue();
- assert dest.numberOfPhiUsers() == 0;
- dest.replaceUsers(src);
- removeInstruction(user);
- }
- // Verify that no more assume instructions are left as users.
- assert eligibleInstance.aliasedUsers().stream().noneMatch(Instruction::isAssume);
- }
-
// Remove miscellaneous users before handling field reads.
private void removeMiscUsages(IRCode code) {
boolean needToRemoveUnreachableBlocks = false;
@@ -530,8 +495,8 @@
}
}
- private void replaceFieldRead(
- IRCode code, InstanceGet fieldRead, Map<DexField, FieldValueHelper> fieldHelpers) {
+ private void replaceFieldRead(IRCode code,
+ InstanceGet fieldRead, Map<DexField, FieldValueHelper> fieldHelpers) {
Value value = fieldRead.outValue();
if (value != null) {
FieldValueHelper helper =
@@ -543,10 +508,7 @@
fieldValueHelper.replaceValue(value, newValue);
}
assert value.numberOfAllUsers() == 0;
- // `newValue` could be a phi introduced by FieldValueHelper. Its initial type is set as the
- // type of read field, but it could be more precise than that due to (multiple) inlining.
- // Instead of values affected by `newValue`, it's necessary to begin with `newValue` itself.
- new TypeAnalysis(appView).narrowing(ImmutableSet.of(newValue));
+ new TypeAnalysis(appView).narrowing(newValue.affectedValues());
}
removeInstruction(fieldRead);
}
@@ -577,8 +539,7 @@
assert isEligibleSingleTarget(singleTarget);
// Must be a constructor called on the receiver.
- if (ListUtils.lastIndexMatching(
- invoke.inValues(), v -> v.getAliasedValue() == eligibleInstance) != 0) {
+ if (invoke.inValues().lastIndexOf(eligibleInstance) != 0) {
return null;
}
@@ -633,7 +594,7 @@
: null;
}
- // An invoke is eligible for inlining in the following cases:
+ // An invoke is eligible for inlinining in the following cases:
//
// - if it does not return the receiver
// - if there are no uses of the out value
@@ -684,8 +645,7 @@
DexEncodedMethod singleTarget,
Set<Instruction> indirectUsers) {
assert isEligibleSingleTarget(singleTarget);
- if (ListUtils.lastIndexMatching(
- invoke.inValues(), v -> v.getAliasedValue() == eligibleInstance) > 0) {
+ if (invoke.inValues().lastIndexOf(eligibleInstance) > 0) {
return null; // Instance passed as an argument.
}
return isEligibleVirtualMethodCall(
@@ -754,8 +714,7 @@
return false;
}
if (invoke.isInvokeMethodWithReceiver()
- && invoke.asInvokeMethodWithReceiver().getReceiver().getAliasedValue()
- == eligibleInstance) {
+ && invoke.asInvokeMethodWithReceiver().getReceiver() == eligibleInstance) {
return false;
}
if (invoke.isInvokeSuper()) {
@@ -796,7 +755,7 @@
// If we got here with invocation on receiver the user is ineligible.
if (invoke.isInvokeMethodWithReceiver()) {
- if (arguments.get(0).getAliasedValue() == eligibleInstance) {
+ if (arguments.get(0) == eligibleInstance) {
return false;
}
@@ -816,7 +775,7 @@
}
for (int argIndex = 0; argIndex < arguments.size(); argIndex++) {
- Value argument = arguments.get(argIndex).getAliasedValue();
+ Value argument = arguments.get(argIndex);
if (argument == eligibleInstance && optimizationInfo.getParameterUsages(argIndex).notUsed()) {
// Reference can be removed since it's not used.
unusedArguments.add(new Pair<>(invoke, argIndex));
@@ -837,7 +796,7 @@
Supplier<InliningOracle> defaultOracle) {
// Go through all arguments, see if all usages of eligibleInstance are good.
for (int argIndex = 0; argIndex < arguments.size(); argIndex++) {
- Value argument = arguments.get(argIndex).getAliasedValue();
+ Value argument = arguments.get(argIndex);
if (argument != eligibleInstance) {
continue; // Nothing to worry about.
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 1e7d21a..88a20da 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -28,7 +28,6 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.Arrays;
@@ -44,7 +43,6 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
-import java.util.function.Predicate;
public final class ClassStaticizer {
@@ -232,8 +230,8 @@
// We are inside an instance method of candidate class (not an instance initializer
// which we will check later), check if all the references to 'this' are valid
// (the call will invalidate the candidate if some of them are not valid).
- analyzeAllValueUsers(
- receiverClassCandidateInfo, receiverValue, factory.isConstructor(method.method));
+ analyzeAllValueUsers(receiverClassCandidateInfo,
+ receiverValue, factory.isConstructor(method.method));
// If the candidate is still valid, ignore all instructions
// we treat as valid usages on receiver.
@@ -291,7 +289,7 @@
// If the candidate still valid, ignore all usages in further analysis.
Value value = instruction.outValue();
if (value != null) {
- alreadyProcessed.addAll(value.aliasedUsers());
+ alreadyProcessed.addAll(value.uniqueUsers());
}
}
continue;
@@ -435,16 +433,14 @@
appView.appInfo().lookupDirectTarget(invoke.getInvokedMethod());
List<Value> values = invoke.inValues();
- if (ListUtils.lastIndexMatching(values, v -> v.getAliasedValue() == candidateValue) != 0
- || methodInvoked == null
- || methodInvoked.method.holder != candidateType) {
+ if (values.lastIndexOf(candidateValue) != 0 ||
+ methodInvoked == null || methodInvoked.method.holder != candidateType) {
return false;
}
// Check arguments.
for (int i = 1; i < values.size(); i++) {
- Value arg = values.get(i).getAliasedValue();
- if (arg.isPhi() || !arg.definition.isConstInstruction()) {
+ if (!values.get(i).definition.isConstInstruction()) {
return false;
}
}
@@ -488,54 +484,40 @@
private CandidateInfo analyzeAllValueUsers(
CandidateInfo candidateInfo, Value value, boolean ignoreSuperClassInitInvoke) {
- assert value != null && value == value.getAliasedValue();
+ assert value != null;
if (value.numberOfPhiUsers() > 0) {
return candidateInfo.invalidate();
}
- Set<Instruction> currentUsers = value.uniqueUsers();
- while (!currentUsers.isEmpty()) {
- Set<Instruction> indirectUsers = Sets.newIdentityHashSet();
- for (Instruction user : currentUsers) {
- if (user.isAssume()) {
- if (user.outValue().numberOfPhiUsers() > 0) {
- return candidateInfo.invalidate();
- }
- indirectUsers.addAll(user.outValue().uniqueUsers());
- continue;
- }
- if (user.isInvokeVirtual() || user.isInvokeDirect() /* private methods */) {
- InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver();
- Predicate<Value> isAliasedValue = v -> v.getAliasedValue() == value;
- DexMethod methodReferenced = invoke.getInvokedMethod();
- if (factory.isConstructor(methodReferenced)) {
- assert user.isInvokeDirect();
- if (ignoreSuperClassInitInvoke
- && ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0
- && methodReferenced == factory.objectMethods.constructor) {
- // If we are inside candidate constructor and analyzing usages
- // of the receiver, we want to ignore invocations of superclass
- // constructor which will be removed after staticizing.
- continue;
- }
- return candidateInfo.invalidate();
- }
- AppInfo appInfo = appView.appInfo();
- DexEncodedMethod methodInvoked = user.isInvokeDirect()
- ? appInfo.lookupDirectTarget(methodReferenced)
- : appInfo.lookupVirtualTarget(methodReferenced.holder, methodReferenced);
- if (ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0
- && methodInvoked != null
- && methodInvoked.method.holder == candidateInfo.candidate.type) {
+ for (Instruction user : value.uniqueUsers()) {
+ if (user.isInvokeVirtual() || user.isInvokeDirect() /* private methods */) {
+ InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver();
+ DexMethod methodReferenced = invoke.getInvokedMethod();
+ if (factory.isConstructor(methodReferenced)) {
+ assert user.isInvokeDirect();
+ if (ignoreSuperClassInitInvoke &&
+ invoke.inValues().lastIndexOf(value) == 0 &&
+ methodReferenced == factory.objectMethods.constructor) {
+ // If we are inside candidate constructor and analyzing usages
+ // of the receiver, we want to ignore invocations of superclass
+ // constructor which will be removed after staticizing.
continue;
}
+ return candidateInfo.invalidate();
}
-
- // All other users are not allowed.
- return candidateInfo.invalidate();
+ AppInfo appInfo = appView.appInfo();
+ DexEncodedMethod methodInvoked = user.isInvokeDirect()
+ ? appInfo.lookupDirectTarget(methodReferenced)
+ : appInfo.lookupVirtualTarget(methodReferenced.holder, methodReferenced);
+ if (invoke.inValues().lastIndexOf(value) == 0 &&
+ methodInvoked != null && methodInvoked.method.holder == candidateInfo.candidate.type) {
+ continue;
+ }
}
- currentUsers = indirectUsers;
+
+ // All other users are not allowed.
+ return candidateInfo.invalidate();
}
return candidateInfo;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 3956609..1ab806d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -340,11 +340,10 @@
}
Set<Phi> chainedPhis = Sets.newIdentityHashSet();
for (Value operand : phi.getOperands()) {
- Value v = operand.getAliasedValue();
- if (v.isPhi()) {
+ if (operand.isPhi()) {
chainedPhis.add(operand.asPhi());
} else {
- if (v != thisValue) {
+ if (operand != thisValue) {
return false;
}
}
@@ -361,7 +360,7 @@
// Fixup `this` usages: rewrites all method calls so that they point to static methods.
private void fixupStaticizedThisUsers(IRCode code, Value thisValue) {
- assert thisValue != null && thisValue == thisValue.getAliasedValue();
+ assert thisValue != null;
// Depending on other optimizations, e.g., inlining, `this` can be flown to phis.
Set<Phi> trivialPhis = Sets.newIdentityHashSet();
boolean onlyHasTrivialPhis = testAndCollectPhisComposedOfThis(
@@ -369,10 +368,10 @@
assert thisValue.numberOfPhiUsers() == 0 || onlyHasTrivialPhis;
assert trivialPhis.isEmpty() || onlyHasTrivialPhis;
- Set<Instruction> users = SetUtils.newIdentityHashSet(thisValue.aliasedUsers());
+ Set<Instruction> users = SetUtils.newIdentityHashSet(thisValue.uniqueUsers());
// If that is the case, method calls we want to fix up include users of those phis.
for (Phi phi : trivialPhis) {
- users.addAll(phi.aliasedUsers());
+ users.addAll(phi.uniqueUsers());
}
fixupStaticizedValueUsers(code, users);
@@ -426,14 +425,13 @@
}
Set<Phi> chainedPhis = Sets.newIdentityHashSet();
for (Value operand : phi.getOperands()) {
- Value v = operand.getAliasedValue();
- if (v.isPhi()) {
+ if (operand.isPhi()) {
chainedPhis.add(operand.asPhi());
} else {
- if (!v.definition.isStaticGet()) {
+ if (!operand.definition.isStaticGet()) {
return false;
}
- if (v.definition.asStaticGet().getField() != field) {
+ if (operand.definition.asStaticGet().getField() != field) {
return false;
}
}
@@ -460,10 +458,10 @@
assert dest.numberOfPhiUsers() == 0 || onlyHasTrivialPhis;
assert trivialPhis.isEmpty() || onlyHasTrivialPhis;
- Set<Instruction> users = SetUtils.newIdentityHashSet(dest.aliasedUsers());
+ Set<Instruction> users = SetUtils.newIdentityHashSet(dest.uniqueUsers());
// If that is the case, method calls we want to fix up include users of those phis.
for (Phi phi : trivialPhis) {
- users.addAll(phi.aliasedUsers());
+ users.addAll(phi.uniqueUsers());
}
fixupStaticizedValueUsers(code, users);
@@ -477,9 +475,6 @@
private void fixupStaticizedValueUsers(IRCode code, Set<Instruction> users) {
for (Instruction user : users) {
- if (user.isAssume()) {
- continue;
- }
assert user.isInvokeVirtual() || user.isInvokeDirect();
InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver();
Value newValue = null;
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 370d5e2..a9c3f98 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -8,19 +8,9 @@
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
-import java.util.function.Predicate;
public class ListUtils {
- public static <T> int lastIndexMatching(List<T> list, Predicate<T> tester) {
- for (int i = list.size() - 1; i >= 0; i--) {
- if (tester.test(list.get(i))) {
- return i;
- }
- }
- return -1;
- }
-
public static <S, T> List<T> map(Collection<S> list, Function<S, T> fn) {
List<T> result = new ArrayList<>(list.size());
for (S element : list) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/WithStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/WithStaticizerTest.java
index d55484d..79ea92e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/WithStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/WithStaticizerTest.java
@@ -44,6 +44,7 @@
.addKeepMainRule(MAIN)
.enableInliningAnnotations()
.enableClassInliningAnnotations()
+ .noMinification()
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("Input")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java
deleted file mode 100644
index eabd3cc..0000000
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java
+++ /dev/null
@@ -1,186 +0,0 @@
-// 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.outliner;
-
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.CodeMatchers;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class OutlinesWithNonNullTest extends TestBase {
- private static final String JVM_OUTPUT = StringUtils.lines(
- "42",
- "arg",
- "42",
- "arg"
- );
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
- }
-
- public OutlinesWithNonNullTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testNonNullOnOneSide() throws Exception {
- testForR8(parameters.getBackend())
- .enableClassInliningAnnotations()
- .enableInliningAnnotations()
- .addProgramClasses(TestArg.class, TestClassWithNonNullOnOneSide.class)
- .addKeepMainRule(TestClassWithNonNullOnOneSide.class)
- .setMinApi(parameters.getRuntime())
- .allowAccessModification()
- .noMinification()
- .addOptionsModification(
- options -> {
- options.outline.threshold = 2;
- options.outline.minSize = 2;
- })
- .compile()
- .inspect(inspector -> validateOutlining(inspector, TestClassWithNonNullOnOneSide.class))
- .run(parameters.getRuntime(), TestClassWithNonNullOnOneSide.class)
- .assertSuccessWithOutput(JVM_OUTPUT);
- }
-
- @Test
- public void testNonNullOnBothSides() throws Exception {
- testForR8(parameters.getBackend())
- .enableClassInliningAnnotations()
- .enableInliningAnnotations()
- .addProgramClasses(TestArg.class, TestClassWithNonNullOnBothSides.class)
- .addKeepMainRule(TestClassWithNonNullOnBothSides.class)
- .setMinApi(parameters.getRuntime())
- .allowAccessModification()
- .noMinification()
- .addOptionsModification(
- options -> {
- options.outline.threshold = 2;
- options.outline.minSize = 2;
- })
- .compile()
- .inspect(inspector -> validateOutlining(inspector, TestClassWithNonNullOnBothSides.class))
- .run(parameters.getRuntime(), TestClassWithNonNullOnBothSides.class)
- .assertSuccessWithOutput(JVM_OUTPUT);
- }
-
- private void validateOutlining(CodeInspector inspector, Class<?> main) {
- ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
- assertThat(outlineClass, isPresent());
- MethodSubject outlineMethod = outlineClass.uniqueMethodWithName("outline0");
- assertThat(outlineMethod, isPresent());
-
- ClassSubject argClass = inspector.clazz(TestArg.class);
- assertThat(argClass, isPresent());
- MethodSubject printHash = argClass.uniqueMethodWithName("printHash");
- assertThat(printHash, isPresent());
- MethodSubject printArg= argClass.uniqueMethodWithName("printArg");
- assertThat(printArg, isPresent());
-
- ClassSubject classSubject = inspector.clazz(main);
- assertThat(classSubject, isPresent());
- MethodSubject method1 = classSubject.uniqueMethodWithName("method1");
- assertThat(method1, isPresent());
- assertThat(method1, CodeMatchers.invokesMethod(outlineMethod));
- assertThat(method1, not(CodeMatchers.invokesMethod(printHash)));
- assertThat(method1, not(CodeMatchers.invokesMethod(printArg)));
- MethodSubject method2 = classSubject.uniqueMethodWithName("method2");
- assertThat(method2, isPresent());
- assertThat(method2, CodeMatchers.invokesMethod(outlineMethod));
- assertThat(method2, not(CodeMatchers.invokesMethod(printHash)));
- assertThat(method2, not(CodeMatchers.invokesMethod(printArg)));
- }
-
- @NeverClassInline
- public static class TestArg {
- @Override
- public int hashCode() {
- return 42;
- }
-
- @Override
- public String toString() {
- return "arg";
- }
-
- @NeverInline
- static void printHash(Object arg) {
- if (arg == null) {
- throw new NullPointerException();
- }
- System.out.println(arg.hashCode());
- // This method guarantees that, at the normal exit, argument is not null.
- }
-
- @NeverInline
- static void printArg(Object arg) {
- System.out.println(arg);
- }
- }
-
- static class TestClassWithNonNullOnOneSide {
- @NeverInline
- static void method1(Object arg) {
- TestArg.printHash(arg);
- // We will have non-null aliasing here.
- TestArg.printArg(arg);
- }
-
- @NeverInline
- static void method2(Object arg) {
- if (arg != null) {
- // We will have non-null aliasing here.
- TestArg.printHash(arg);
- TestArg.printArg(arg);
- }
- }
-
- public static void main(String... args) {
- TestArg arg = new TestArg();
- method1(arg);
- method2(arg);
- }
- }
-
- static class TestClassWithNonNullOnBothSides {
- @NeverInline
- static void method1(Object arg) {
- TestArg.printHash(arg);
- // We will have non-null aliasing here.
- TestArg.printArg(arg);
- }
-
- @NeverInline
- static void method2(Object arg) {
- TestArg.printHash(arg);
- // We will have non-null aliasing here.
- TestArg.printArg(arg);
- }
-
- public static void main(String... args) {
- TestArg arg = new TestArg();
- method1(arg);
- method2(arg);
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionAsArgumentTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionAsArgumentTest.java
deleted file mode 100644
index a93e72b..0000000
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionAsArgumentTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-// 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.staticizer;
-
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-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 org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class CompanionAsArgumentTest extends TestBase {
- private static final Class<?> MAIN = Main.class;
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- // TODO(b/112831361): support for class staticizer in CF backend.
- return getTestParameters().withDexRuntimes().build();
- }
-
- private final TestParameters parameters;
-
- public CompanionAsArgumentTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(CompanionAsArgumentTest.class)
- .addKeepMainRule(MAIN)
- .enableInliningAnnotations()
- .enableClassInliningAnnotations()
- .setMinApi(parameters.getRuntime())
- .run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutputLines("Companion#foo(true)")
- .inspect(this::inspect);
- }
-
- private void inspect(CodeInspector inspector) {
- // Check if the candidate is not staticized.
- ClassSubject companion = inspector.clazz(Host.Companion.class);
- assertThat(companion, isPresent());
- MethodSubject foo = companion.uniqueMethodWithName("foo");
- assertThat(foo, isPresent());
- assertTrue(foo.streamInstructions().anyMatch(
- i -> i.isInvokeVirtual()
- && i.getMethod().toSourceString().contains("PrintStream.println")));
-
- // Nothing migrated from Companion to Host.
- ClassSubject host = inspector.clazz(Host.class);
- assertThat(host, isPresent());
- MethodSubject migrated_foo = host.uniqueMethodWithName("foo");
- assertThat(migrated_foo, not(isPresent()));
- }
-
- @NeverClassInline
- static class Host {
- private static final Companion companion = new Companion();
-
- static class Companion {
- @NeverInline
- public void foo(Object arg) {
- System.out.println("Companion#foo(" + (arg != null) + ")");
- }
- }
-
- @NeverInline
- static void bar() {
- // The target singleton is used as not only a receiver but also an argument.
- companion.foo(companion);
- }
- }
-
- static class Main {
- public static void main(String[] args) {
- Host.bar();
- }
- }
-}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
index f28f242..fa76296 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
@@ -3,14 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.examples;
-import static org.junit.Assert.assertFalse;
-
import com.android.tools.r8.shaking.TreeShakingTest;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -44,7 +43,8 @@
}
private static void unusedRemoved(CodeInspector inspector) {
- assertFalse(
+ // TODO(b/80455722): Change to assertFalse when tree-shaking detects this case.
+ Assert.assertTrue(
"DerivedUnused should be removed", inspector.clazz("shaking18.DerivedUnused").isPresent());
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/ListUtilsTest.java b/src/test/java/com/android/tools/r8/utils/ListUtilsTest.java
deleted file mode 100644
index 3a63555..0000000
--- a/src/test/java/com/android/tools/r8/utils/ListUtilsTest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// 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.utils;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Predicate;
-import org.junit.Test;
-
-public class ListUtilsTest {
-
- private List<Integer> createInputData(int size) {
- List<Integer> input = new ArrayList<>(size);
- for (int i = 0; i < size; i++) {
- input.add(i);
- }
- return input;
- }
-
- @Test
- public void lastIndexOf_outOfRange() {
- List<Integer> input = createInputData(3);
- Predicate<Integer> tester = x -> x * x == -1;
- assertEquals(-1, ListUtils.lastIndexMatching(input, tester));
- }
-
- @Test
- public void lastIndexOf_first() {
- List<Integer> input = createInputData(3);
- Predicate<Integer> tester = x -> x * x == 0;
- assertEquals(0, ListUtils.lastIndexMatching(input, tester));
- }
-
- @Test
- public void lastIndexOf_middle() {
- List<Integer> input = createInputData(4);
- Predicate<Integer> tester = x -> x * x == 4;
- assertEquals(2, ListUtils.lastIndexMatching(input, tester));
- }
-
- @Test
- public void lastIndexOf_last() {
- List<Integer> input = createInputData(2);
- Predicate<Integer> tester = x -> x * x == 1;
- assertEquals(1, ListUtils.lastIndexMatching(input, tester));
- }
-}