Model Boolean.booleanValue() and Boolean.valueOf()
Bug: 144419767, 145259212, 145253152
Change-Id: Ieb04e3b726250465c7ee965b263f7e21e2a38e7d
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 1174f5e..40c60ac 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -344,6 +344,7 @@
new StringBuildingMethods(stringBuilderType);
public final StringBuildingMethods stringBufferMethods =
new StringBuildingMethods(stringBufferType);
+ public final BooleanMembers booleanMembers = new BooleanMembers();
public final ObjectsMethods objectsMethods = new ObjectsMethods();
public final ObjectMethods objectMethods = new ObjectMethods();
public final StringMethods stringMethods = new StringMethods();
@@ -552,7 +553,7 @@
.build();
public final Set<DexType> libraryClassesWithoutStaticInitialization =
- ImmutableSet.of(enumType, objectType, stringBufferType, stringBuilderType);
+ ImmutableSet.of(boxedBooleanType, enumType, objectType, stringBufferType, stringBuilderType);
private boolean skipNameValidationForTesting = false;
@@ -568,6 +569,20 @@
return dexMethod == metafactoryMethod || dexMethod == metafactoryAltMethod;
}
+ public class BooleanMembers {
+
+ public final DexField FALSE = createField(boxedBooleanType, boxedBooleanType, "FALSE");
+ public final DexField TRUE = createField(boxedBooleanType, boxedBooleanType, "TRUE");
+ public final DexField TYPE = createField(boxedBooleanType, classType, "TYPE");
+
+ public final DexMethod booleanValue =
+ createMethod(boxedBooleanType, createProto(booleanType), "booleanValue");
+ public final DexMethod valueOf =
+ createMethod(boxedBooleanType, createProto(boxedBooleanType, booleanType), "valueOf");
+
+ private BooleanMembers() {}
+ }
+
public class LongMethods {
public final DexMethod compare;
@@ -806,7 +821,7 @@
* E.g. for Boolean https://docs.oracle.com/javase/8/docs/api/java/lang/Boolean.html#TYPE.
*/
public class PrimitiveTypesBoxedTypeFields {
- public final DexField booleanTYPE;
+
public final DexField byteTYPE;
public final DexField charTYPE;
public final DexField shortTYPE;
@@ -818,7 +833,6 @@
private final Map<DexField, DexType> boxedFieldTypeToPrimitiveType;
private PrimitiveTypesBoxedTypeFields() {
- booleanTYPE = createField(boxedBooleanType, classType, "TYPE");
byteTYPE = createField(boxedByteType, classType, "TYPE");
charTYPE = createField(boxedCharType, classType, "TYPE");
shortTYPE = createField(boxedShortType, classType, "TYPE");
@@ -829,7 +843,7 @@
boxedFieldTypeToPrimitiveType =
ImmutableMap.<DexField, DexType>builder()
- .put(booleanTYPE, booleanType)
+ .put(booleanMembers.TYPE, booleanType)
.put(byteTYPE, byteType)
.put(charTYPE, charType)
.put(shortTYPE, shortType)
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
index 28ac1a2..64401fd 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -34,6 +34,11 @@
return this;
}
+ public boolean getBooleanValue() {
+ assert value == 0 || value == 1;
+ return value != 0;
+ }
+
public long getValue() {
return value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 708e947..085c936 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -4,10 +4,12 @@
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -218,6 +220,55 @@
}
@Override
+ public void replaceCurrentInstructionWithConstInt(
+ AppView<? extends AppInfoWithSubtyping> appView, IRCode code, int value) {
+ if (current == null) {
+ throw new IllegalStateException();
+ }
+
+ assert !current.hasOutValue() || current.outValue().getTypeLattice().isInt();
+
+ // Replace the instruction by const-number.
+ ConstNumber constNumber = code.createIntConstant(value, current.getLocalInfo());
+ for (Value inValue : current.inValues()) {
+ if (inValue.hasLocalInfo()) {
+ // Add this value as a debug value to avoid changing its live range.
+ constNumber.addDebugValue(inValue);
+ }
+ }
+ replaceCurrentInstruction(constNumber);
+ }
+
+ @Override
+ public void replaceCurrentInstructionWithStaticGet(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ DexField field,
+ Set<Value> affectedValues) {
+ if (current == null) {
+ throw new IllegalStateException();
+ }
+
+ // Replace the instruction by static-get.
+ TypeLatticeElement newType = TypeLatticeElement.fromDexType(field.type, maybeNull(), appView);
+ TypeLatticeElement oldType = current.hasOutValue() ? current.outValue().getTypeLattice() : null;
+ Value value = code.createValue(newType, current.getLocalInfo());
+ StaticGet staticGet = new StaticGet(value, field);
+ for (Value inValue : current.inValues()) {
+ if (inValue.hasLocalInfo()) {
+ // Add this value as a debug value to avoid changing its live range.
+ staticGet.addDebugValue(inValue);
+ }
+ }
+ replaceCurrentInstruction(staticGet);
+
+ // Update affected values.
+ if (value.hasAnyUsers() && !newType.equals(oldType)) {
+ affectedValues.addAll(value.affectedValues());
+ }
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithSubtyping> appView,
IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index 60e06c6..fdea0f6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.InternalOptions;
import java.util.ListIterator;
@@ -37,6 +38,22 @@
}
@Override
+ public void replaceCurrentInstructionWithConstInt(
+ AppView<? extends AppInfoWithSubtyping> appView, IRCode code, int value) {
+ instructionIterator.replaceCurrentInstructionWithConstInt(appView, code, value);
+ }
+
+ @Override
+ public void replaceCurrentInstructionWithStaticGet(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ DexField field,
+ Set<Value> affectedValues) {
+ instructionIterator.replaceCurrentInstructionWithStaticGet(
+ appView, code, field, affectedValues);
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithSubtyping> appView,
IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index f4ff24a..315568e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
@@ -57,6 +58,15 @@
Value insertConstIntInstruction(IRCode code, InternalOptions options, int value);
+ void replaceCurrentInstructionWithConstInt(
+ AppView<? extends AppInfoWithSubtyping> appView, IRCode code, int value);
+
+ void replaceCurrentInstructionWithStaticGet(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ DexField field,
+ Set<Value> affectedValues);
+
/**
* Replace the current instruction with null throwing instructions.
*
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index 2c76690..3a098ca 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.InternalOptions;
import java.util.ListIterator;
@@ -50,6 +51,22 @@
}
@Override
+ public void replaceCurrentInstructionWithConstInt(
+ AppView<? extends AppInfoWithSubtyping> appView, IRCode code, int value) {
+ currentBlockIterator.replaceCurrentInstructionWithConstInt(appView, code, value);
+ }
+
+ @Override
+ public void replaceCurrentInstructionWithStaticGet(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ DexField field,
+ Set<Value> affectedValues) {
+ currentBlockIterator.replaceCurrentInstructionWithStaticGet(
+ appView, code, field, affectedValues);
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithSubtyping> appView,
IRCode code,
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 97d42fb..dca6174 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
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -12,7 +14,6 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
-import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
@@ -1126,7 +1127,7 @@
DexType type = definition.asNewInstance().clazz;
DexClass clazz = appView.definitionFor(type);
if (clazz != null && !clazz.isInterface()) {
- return ClassTypeLatticeElement.create(type, Nullability.definitelyNotNull(), appView);
+ return ClassTypeLatticeElement.create(type, definitelyNotNull(), appView);
}
return null;
}
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 fd1b111..3e9ecab 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
@@ -73,6 +73,7 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
+import com.android.tools.r8.ir.optimize.library.LibraryMethodOptimizer;
import com.android.tools.r8.ir.optimize.staticizer.ClassStaticizer;
import com.android.tools.r8.ir.optimize.string.StringBuilderOptimizer;
import com.android.tools.r8.ir.optimize.string.StringOptimizer;
@@ -126,6 +127,7 @@
private final Outliner outliner;
private final ClassInitializerDefaultsOptimization classInitializerDefaultsOptimization;
private final FieldBitAccessAnalysis fieldBitAccessAnalysis;
+ private final LibraryMethodOptimizer libraryMethodOptimizer;
private final LibraryMethodOverrideAnalysis libraryMethodOverrideAnalysis;
private final StringConcatRewriter stringConcatRewriter;
private final StringOptimizer stringOptimizer;
@@ -225,6 +227,7 @@
this.classInliner = null;
this.classStaticizer = null;
this.fieldBitAccessAnalysis = null;
+ this.libraryMethodOptimizer = null;
this.libraryMethodOverrideAnalysis = null;
this.inliner = null;
this.outliner = null;
@@ -286,6 +289,7 @@
options.enableFieldBitAccessAnalysis
? new FieldBitAccessAnalysis(appViewWithLiveness)
: null;
+ this.libraryMethodOptimizer = new LibraryMethodOptimizer(appViewWithLiveness);
this.libraryMethodOverrideAnalysis =
options.enableTreeShakingOfLibraryMethodOverrides
? new LibraryMethodOverrideAnalysis(appViewWithLiveness)
@@ -322,6 +326,7 @@
this.classStaticizer = null;
this.dynamicTypeOptimization = null;
this.fieldBitAccessAnalysis = null;
+ this.libraryMethodOptimizer = null;
this.libraryMethodOverrideAnalysis = null;
this.inliner = null;
this.lambdaMerger = null;
@@ -1185,6 +1190,9 @@
// Reflection/string optimization 3. trivial conversion/computation on const-string
stringOptimizer.computeTrivialOperationsOnConstString(code);
stringOptimizer.removeTrivialConversions(code);
+ if (libraryMethodOptimizer != null) {
+ libraryMethodOptimizer.optimize(appView, code, feedback, methodProcessor);
+ }
assert code.isConsistentSSA();
}
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 0195c84..303b4ea 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
@@ -154,7 +154,7 @@
}
}
}
- // TODO(b/119596718): Use dominant tree to extend it to non-canonicalized in values?
+ // TODO(b/145259212): Use dominant tree to extend it to non-canonicalized in values?
// For now, interested in inputs that are also canonicalized constants.
boolean invocationCanBeMovedToEntryBlock = true;
for (Value in : current.inValues()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
new file mode 100644
index 0000000..edd657b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
@@ -0,0 +1,85 @@
+// 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.library;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
+
+public class BooleanMethodOptimizer implements LibraryMethodModelCollection {
+
+ private final AppView<? extends AppInfoWithSubtyping> appView;
+ private final DexItemFactory dexItemFactory;
+
+ BooleanMethodOptimizer(AppView<? extends AppInfoWithSubtyping> appView) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
+ @Override
+ public DexType getType() {
+ return dexItemFactory.boxedBooleanType;
+ }
+
+ @Override
+ public void optimize(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexEncodedMethod singleTarget,
+ Set<Value> affectedValues) {
+ if (singleTarget.method == dexItemFactory.booleanMembers.booleanValue) {
+ optimizeBooleanValue(code, instructionIterator, invoke);
+ } else if (singleTarget.method == dexItemFactory.booleanMembers.valueOf) {
+ optimizeValueOf(code, instructionIterator, invoke, affectedValues);
+ }
+ }
+
+ private void optimizeBooleanValue(
+ IRCode code, InstructionListIterator instructionIterator, InvokeMethod invoke) {
+ Value argument = invoke.arguments().get(0).getAliasedValue();
+ if (!argument.isPhi()) {
+ Instruction definition = argument.definition;
+ if (definition.isStaticGet()) {
+ StaticGet staticGet = definition.asStaticGet();
+ DexField field = staticGet.getField();
+ if (field == dexItemFactory.booleanMembers.TRUE) {
+ instructionIterator.replaceCurrentInstructionWithConstInt(appView, code, 1);
+ } else if (field == dexItemFactory.booleanMembers.FALSE) {
+ instructionIterator.replaceCurrentInstructionWithConstInt(appView, code, 0);
+ }
+ }
+ }
+ }
+
+ private void optimizeValueOf(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ Set<Value> affectedValues) {
+ Value argument = invoke.arguments().get(0);
+ AbstractValue abstractValue = argument.getAbstractValue(appView, code.method.method.holder);
+ if (abstractValue.isSingleNumberValue()) {
+ instructionIterator.replaceCurrentInstructionWithStaticGet(
+ appView,
+ code,
+ abstractValue.asSingleNumberValue().getBooleanValue()
+ ? dexItemFactory.booleanMembers.TRUE
+ : dexItemFactory.booleanMembers.FALSE,
+ affectedValues);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
new file mode 100644
index 0000000..5608b94
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
@@ -0,0 +1,34 @@
+// 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.library;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
+
+/** Used to model the behavior of library methods for optimization purposes. */
+public interface LibraryMethodModelCollection {
+
+ /**
+ * The library class whose methods are being modeled by this collection of models. As an example,
+ * {@link BooleanMethodOptimizer} is modeling {@link Boolean}).
+ */
+ DexType getType();
+
+ /**
+ * Invoked for instructions in {@param code} that invoke a method on the class returned by {@link
+ * #getType()}. The given {@param singleTarget} is guaranteed to be non-null.
+ */
+ void optimize(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexEncodedMethod singleTarget,
+ Set<Value> affectedValues);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
new file mode 100644
index 0000000..0a92679
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
@@ -0,0 +1,75 @@
+// 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.library;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+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.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.CodeOptimization;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.google.common.collect.Sets;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class LibraryMethodOptimizer implements CodeOptimization {
+
+ private final Map<DexType, LibraryMethodModelCollection> libraryMethodModelCollections =
+ new IdentityHashMap<>();
+
+ public LibraryMethodOptimizer(AppView<? extends AppInfoWithSubtyping> appView) {
+ register(new BooleanMethodOptimizer(appView));
+ }
+
+ private void register(LibraryMethodModelCollection optimizer) {
+ LibraryMethodModelCollection existing =
+ libraryMethodModelCollections.put(optimizer.getType(), optimizer);
+ assert existing == null;
+ }
+
+ @Override
+ public void optimize(
+ AppView<?> appView,
+ IRCode code,
+ OptimizationFeedback feedback,
+ MethodProcessor methodProcessor) {
+ Set<Value> affectedValues = Sets.newIdentityHashSet();
+ InstructionListIterator instructionIterator = code.instructionListIterator();
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
+ if (instruction.isInvokeMethod()) {
+ InvokeMethod invoke = instruction.asInvokeMethod();
+ DexEncodedMethod singleTarget =
+ invoke.lookupSingleTarget(appView, code.method.method.holder);
+ if (singleTarget != null) {
+ optimizeInvoke(code, instructionIterator, invoke, singleTarget, affectedValues);
+ }
+ }
+ }
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
+ }
+
+ private void optimizeInvoke(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexEncodedMethod singleTarget,
+ Set<Value> affectedValues) {
+ LibraryMethodModelCollection optimizer =
+ libraryMethodModelCollections.getOrDefault(
+ singleTarget.method.holder, NopLibraryMethodModelCollection.getInstance());
+ optimizer.optimize(code, instructionIterator, invoke, singleTarget, affectedValues);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java
new file mode 100644
index 0000000..9a0980f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java
@@ -0,0 +1,39 @@
+// 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.library;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
+
+public class NopLibraryMethodModelCollection implements LibraryMethodModelCollection {
+
+ private static final NopLibraryMethodModelCollection INSTANCE =
+ new NopLibraryMethodModelCollection();
+
+ private NopLibraryMethodModelCollection() {}
+
+ public static NopLibraryMethodModelCollection getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public DexType getType() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void optimize(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexEncodedMethod singleTarget,
+ Set<Value> affectedValues) {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IdempotentFunctionCallCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IdempotentFunctionCallCanonicalizationTest.java
index c1b808b..d3a87fa 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IdempotentFunctionCallCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IdempotentFunctionCallCanonicalizationTest.java
@@ -68,12 +68,14 @@
{
Map<String, Boolean> map = new HashMap<>();
// After canonicalization, only one 'true' and one 'false' conversions remain.
- map.put("A", true);
- map.put("B", true);
- map.put("C", false);
- map.put("D", true);
- map.put("E", false);
- map.put("F", true);
+ boolean alwaysTrue = System.currentTimeMillis() >= 0;
+ boolean alwaysFalse = System.currentTimeMillis() < 0;
+ map.put("A", alwaysTrue);
+ map.put("B", alwaysTrue);
+ map.put("C", alwaysFalse);
+ map.put("D", alwaysTrue);
+ map.put("E", alwaysFalse);
+ map.put("F", alwaysTrue);
System.out.println(map.get("B"));
System.out.println(map.get("E"));
}
@@ -207,9 +209,12 @@
test(
result,
TOTAL_MAX_CALLS,
- EXPECTED_BOOLEAN_VALUE_OF,
- EXPECTED_INTEGER_VALUE_OF,
- EXPECTED_LONG_VALUE_OF);
+ // TODO(b/145259212): Should be `EXPECTED_BOOLEAN_VALUE_OF` (2).
+ 6,
+ // TODO(b/145253152): Should be `EXPECTED_INTEGER_VALUE_OF` (2).
+ 5,
+ // TODO(b/145253152): Should be `EXPECTED_LONG_VALUE_OF` (7).
+ 10);
result =
testForD8()
@@ -221,9 +226,12 @@
test(
result,
TOTAL_MAX_CALLS,
- EXPECTED_BOOLEAN_VALUE_OF,
- EXPECTED_INTEGER_VALUE_OF,
- EXPECTED_LONG_VALUE_OF);
+ // TODO(b/145259212): Should be `EXPECTED_BOOLEAN_VALUE_O` (2).
+ 6,
+ // TODO(b/145253152): Should be `EXPECTED_INTEGER_VALUE_OF` (2).
+ 5,
+ // TODO(b/145253152): Should be `EXPECTED_LONG_VALUE_OF` (7).
+ 10);
}
@Test
@@ -238,9 +246,12 @@
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
int expectedMaxCount = parameters.isCfRuntime() ? TOTAL_MAX_CALLS : EXPECTED_MAX_CALLS;
- int expectedBooleanValueOfCount = parameters.isCfRuntime() ? 6 : EXPECTED_BOOLEAN_VALUE_OF;
- int expectedIntValueOfCount = parameters.isCfRuntime() ? 5 : EXPECTED_INTEGER_VALUE_OF;
- int expectedLongValueOfCount = parameters.isCfRuntime() ? 10 : EXPECTED_LONG_VALUE_OF;
+ // TODO(b/145259212): Should be `EXPECTED_BOOLEAN_VALUE_OF` (2) when compiling for dex.
+ int expectedBooleanValueOfCount = 6;
+ // TODO(b/145253152): Should be `EXPECTED_INTEGER_VALUE_OF` (2) when compiling for dex.
+ int expectedIntValueOfCount = 5;
+ // TODO(b/145253152): Should be `EXPECTED_LONG_VALUE_OF` (7) when compiling for dex.
+ int expectedLongValueOfCount = 10;
test(
result,
expectedMaxCount,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/library/BooleanValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/library/BooleanValueOfTest.java
new file mode 100644
index 0000000..85eb82b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/library/BooleanValueOfTest.java
@@ -0,0 +1,179 @@
+// 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.library;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.google.common.base.Predicates.or;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+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.InstructionSubject;
+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 BooleanValueOfTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public BooleanValueOfTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(BooleanValueOfTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("true", "false", "true", "false", "true", "false");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+
+ MethodSubject testBooleanValueOfTrue =
+ testClassSubject.uniqueMethodWithName("testBooleanValueOfTrue");
+ assertThat(testBooleanValueOfTrue, isPresent());
+ assertTrue(
+ testBooleanValueOfTrue
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().name.toSourceString())
+ .noneMatch("booleanValue"::equals));
+ assertTrue(
+ testBooleanValueOfTrue
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticGet)
+ .map(staticGet -> staticGet.getField().name.toSourceString())
+ .noneMatch("TRUE"::equals));
+
+ MethodSubject testBooleanValueOfFalse =
+ testClassSubject.uniqueMethodWithName("testBooleanValueOfFalse");
+ assertThat(testBooleanValueOfFalse, isPresent());
+ assertTrue(
+ testBooleanValueOfFalse
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().name.toSourceString())
+ .noneMatch("booleanValue"::equals));
+ assertTrue(
+ testBooleanValueOfFalse
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticGet)
+ .map(staticGet -> staticGet.getField().name.toSourceString())
+ .noneMatch("FALSE"::equals));
+
+ MethodSubject testRoundTripTrueMethodSubject =
+ testClassSubject.uniqueMethodWithName("testRoundTripTrue");
+ assertThat(testRoundTripTrueMethodSubject, isPresent());
+ assertTrue(
+ testRoundTripTrueMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().name.toSourceString())
+ .noneMatch(or("booleanValue"::equals, "valueOf"::equals)));
+
+ MethodSubject testRoundTripFalseMethodSubject =
+ testClassSubject.uniqueMethodWithName("testRoundTripFalse");
+ assertThat(testRoundTripFalseMethodSubject, isPresent());
+ assertTrue(
+ testRoundTripFalseMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().name.toSourceString())
+ .noneMatch(or("booleanValue"::equals, "valueOf"::equals)));
+
+ MethodSubject testValueOfTrue = testClassSubject.uniqueMethodWithName("testValueOfTrue");
+ assertThat(testValueOfTrue, isPresent());
+ assertTrue(
+ testValueOfTrue
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().name.toSourceString())
+ .noneMatch("valueOf"::equals));
+ assertTrue(
+ testValueOfTrue
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticGet)
+ .map(staticGet -> staticGet.getField().name.toSourceString())
+ .anyMatch("TRUE"::equals));
+
+ MethodSubject testValueOfFalse = testClassSubject.uniqueMethodWithName("testValueOfFalse");
+ assertThat(testValueOfFalse, isPresent());
+ assertTrue(
+ testValueOfFalse
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().name.toSourceString())
+ .noneMatch("valueOf"::equals));
+ assertTrue(
+ testValueOfFalse
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticGet)
+ .map(staticGet -> staticGet.getField().name.toSourceString())
+ .anyMatch("FALSE"::equals));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ testBooleanValueOfTrue();
+ testBooleanValueOfFalse();
+ testRoundTripTrue();
+ testRoundTripFalse();
+ testValueOfTrue();
+ testValueOfFalse();
+ }
+
+ @NeverInline
+ static void testBooleanValueOfTrue() {
+ System.out.println(Boolean.TRUE.booleanValue());
+ }
+
+ @NeverInline
+ static void testBooleanValueOfFalse() {
+ System.out.println(Boolean.FALSE.booleanValue());
+ }
+
+ @NeverInline
+ static void testRoundTripTrue() {
+ System.out.println(Boolean.valueOf(true).booleanValue());
+ }
+
+ @NeverInline
+ static void testRoundTripFalse() {
+ System.out.println(Boolean.valueOf(false).booleanValue());
+ }
+
+ @NeverInline
+ static void testValueOfTrue() {
+ System.out.println(Boolean.valueOf(true));
+ }
+
+ @NeverInline
+ static void testValueOfFalse() {
+ System.out.println(Boolean.valueOf(false));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index e83a89f..555b94c 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -58,6 +59,21 @@
}
@Override
+ public void replaceCurrentInstructionWithConstInt(
+ AppView<? extends AppInfoWithSubtyping> appView, IRCode code, int value) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public void replaceCurrentInstructionWithStaticGet(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ DexField field,
+ Set<Value> affectedValues) {
+ throw new Unimplemented();
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithSubtyping> appView,
IRCode code,