Remove successive boxing/unboxing operations
Change-Id: Ia7c11d612565a414188d996b313969568738df64
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/BooleanMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/BooleanMethodOptimizer.java
index 35915cb..100b7b4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/BooleanMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/BooleanMethodOptimizer.java
@@ -6,11 +6,9 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.SingleBoxedBooleanValue;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.ConstString;
@@ -19,18 +17,28 @@
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.optimize.library.StatelessLibraryMethodModelCollection;
import com.android.tools.r8.utils.StringUtils;
import java.util.Set;
-public class BooleanMethodOptimizer extends StatelessLibraryMethodModelCollection {
-
- private final AppView<?> appView;
- private final DexItemFactory dexItemFactory;
+public class BooleanMethodOptimizer extends PrimitiveMethodOptimizer {
BooleanMethodOptimizer(AppView<?> appView) {
- this.appView = appView;
- this.dexItemFactory = appView.dexItemFactory();
+ super(appView);
+ }
+
+ @Override
+ DexMethod getBoxMethod() {
+ return dexItemFactory.booleanMembers.valueOf;
+ }
+
+ @Override
+ DexMethod getUnboxMethod() {
+ return dexItemFactory.booleanMembers.booleanValue;
+ }
+
+ @Override
+ boolean isMatchingSingleBoxedPrimitive(AbstractValue abstractValue) {
+ return abstractValue.isSingleBoxedBoolean();
}
@Override
@@ -47,31 +55,10 @@
DexClassAndMethod singleTarget,
Set<Value> affectedValues,
Set<BasicBlock> blocksToRemove) {
- DexMethod singleTargetReference = singleTarget.getReference();
- if (singleTargetReference.isIdenticalTo(dexItemFactory.booleanMembers.booleanValue)) {
- optimizeBooleanValue(code, instructionIterator, invoke);
- } else if (singleTargetReference.isIdenticalTo(dexItemFactory.booleanMembers.parseBoolean)) {
+ if (singleTarget.getReference().isIdenticalTo(dexItemFactory.booleanMembers.parseBoolean)) {
optimizeParseBoolean(code, instructionIterator, invoke);
- } else if (singleTargetReference.isIdenticalTo(dexItemFactory.booleanMembers.valueOf)) {
- optimizeValueOf(code, instructionIterator, invoke, affectedValues);
- }
- }
-
- private void optimizeBooleanValue(
- IRCode code, InstructionListIterator instructionIterator, InvokeMethod booleanValueInvoke) {
- // Optimize Boolean.valueOf(b).booleanValue() into b.
- AbstractValue abstractValue =
- booleanValueInvoke.getFirstArgument().getAbstractValue(appView, code.context());
- if (abstractValue.isSingleBoxedBoolean()) {
- if (booleanValueInvoke.hasOutValue()) {
- SingleBoxedBooleanValue singleBoxedBoolean = abstractValue.asSingleBoxedBoolean();
- instructionIterator.replaceCurrentInstruction(
- singleBoxedBoolean
- .toPrimitive(appView.abstractValueFactory())
- .createMaterializingInstruction(appView, code, booleanValueInvoke));
- } else {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- }
+ } else {
+ optimizeBoxingMethods(code, instructionIterator, invoke, singleTarget, affectedValues);
}
}
@@ -91,13 +78,14 @@
}
}
- private void optimizeValueOf(
+ @Override
+ void optimizeBoxMethod(
IRCode code,
InstructionListIterator instructionIterator,
- InvokeMethod invoke,
+ InvokeMethod boxInvoke,
Set<Value> affectedValues) {
// Optimize Boolean.valueOf(b) into Boolean.FALSE or Boolean.TRUE.
- Value argument = invoke.getFirstOperand();
+ Value argument = boxInvoke.getFirstOperand();
AbstractValue abstractValue = argument.getAbstractValue(appView, code.context());
if (abstractValue.isSingleNumberValue()) {
instructionIterator.replaceCurrentInstructionWithStaticGet(
@@ -107,6 +95,8 @@
? dexItemFactory.booleanMembers.TRUE
: dexItemFactory.booleanMembers.FALSE,
affectedValues);
+ return;
}
+ super.optimizeBoxMethod(code, instructionIterator, boxInvoke, affectedValues);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/ByteMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/ByteMethodOptimizer.java
index 0d47dd0..8f97ed0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/ByteMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/ByteMethodOptimizer.java
@@ -5,64 +5,33 @@
package com.android.tools.r8.ir.optimize.library.primitive;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.SingleBoxedByteValue;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlockIterator;
-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 com.android.tools.r8.ir.optimize.library.StatelessLibraryMethodModelCollection;
-import java.util.Set;
-public class ByteMethodOptimizer extends StatelessLibraryMethodModelCollection {
-
- private final AppView<?> appView;
- private final DexItemFactory dexItemFactory;
+public class ByteMethodOptimizer extends PrimitiveMethodOptimizer {
ByteMethodOptimizer(AppView<?> appView) {
- this.appView = appView;
- this.dexItemFactory = appView.dexItemFactory();
+ super(appView);
+ }
+
+ @Override
+ DexMethod getBoxMethod() {
+ return dexItemFactory.byteMembers.valueOf;
+ }
+
+ @Override
+ DexMethod getUnboxMethod() {
+ return dexItemFactory.byteMembers.byteValue;
+ }
+
+ @Override
+ boolean isMatchingSingleBoxedPrimitive(AbstractValue abstractValue) {
+ return abstractValue.isSingleBoxedByte();
}
@Override
public DexType getType() {
return dexItemFactory.boxedByteType;
}
-
- @Override
- public void optimize(
- IRCode code,
- BasicBlockIterator blockIterator,
- InstructionListIterator instructionIterator,
- InvokeMethod invoke,
- DexClassAndMethod singleTarget,
- Set<Value> affectedValues,
- Set<BasicBlock> blocksToRemove) {
- if (singleTarget.getReference().isIdenticalTo(dexItemFactory.byteMembers.byteValue)) {
- optimizeByteValue(code, instructionIterator, invoke);
- }
- }
-
- private void optimizeByteValue(
- IRCode code, InstructionListIterator instructionIterator, InvokeMethod byteValueInvoke) {
- // Optimize Byte.valueOf(b).byteValue() into b.
- AbstractValue abstractValue =
- byteValueInvoke.getFirstArgument().getAbstractValue(appView, code.context());
- if (abstractValue.isSingleBoxedByte()) {
- if (byteValueInvoke.hasOutValue()) {
- SingleBoxedByteValue singleBoxedByte = abstractValue.asSingleBoxedByte();
- instructionIterator.replaceCurrentInstruction(
- singleBoxedByte
- .toPrimitive(appView.abstractValueFactory())
- .createMaterializingInstruction(appView, code, byteValueInvoke));
- } else {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- }
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/CharacterMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/CharacterMethodOptimizer.java
index c443041..689155d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/CharacterMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/CharacterMethodOptimizer.java
@@ -5,64 +5,33 @@
package com.android.tools.r8.ir.optimize.library.primitive;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.SingleBoxedCharValue;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlockIterator;
-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 com.android.tools.r8.ir.optimize.library.StatelessLibraryMethodModelCollection;
-import java.util.Set;
-public class CharacterMethodOptimizer extends StatelessLibraryMethodModelCollection {
-
- private final AppView<?> appView;
- private final DexItemFactory dexItemFactory;
+public class CharacterMethodOptimizer extends PrimitiveMethodOptimizer {
CharacterMethodOptimizer(AppView<?> appView) {
- this.appView = appView;
- this.dexItemFactory = appView.dexItemFactory();
+ super(appView);
+ }
+
+ @Override
+ DexMethod getBoxMethod() {
+ return dexItemFactory.charMembers.valueOf;
+ }
+
+ @Override
+ DexMethod getUnboxMethod() {
+ return dexItemFactory.charMembers.charValue;
+ }
+
+ @Override
+ boolean isMatchingSingleBoxedPrimitive(AbstractValue abstractValue) {
+ return abstractValue.isSingleBoxedChar();
}
@Override
public DexType getType() {
return dexItemFactory.boxedCharType;
}
-
- @Override
- public void optimize(
- IRCode code,
- BasicBlockIterator blockIterator,
- InstructionListIterator instructionIterator,
- InvokeMethod invoke,
- DexClassAndMethod singleTarget,
- Set<Value> affectedValues,
- Set<BasicBlock> blocksToRemove) {
- if (singleTarget.getReference().isIdenticalTo(dexItemFactory.charMembers.charValue)) {
- optimizeCharValue(code, instructionIterator, invoke);
- }
- }
-
- private void optimizeCharValue(
- IRCode code, InstructionListIterator instructionIterator, InvokeMethod charValueInvoke) {
- // Optimize Char.valueOf(c).charValue() into c.
- AbstractValue abstractValue =
- charValueInvoke.getFirstArgument().getAbstractValue(appView, code.context());
- if (abstractValue.isSingleBoxedChar()) {
- if (charValueInvoke.hasOutValue()) {
- SingleBoxedCharValue singleBoxedChar = abstractValue.asSingleBoxedChar();
- instructionIterator.replaceCurrentInstruction(
- singleBoxedChar
- .toPrimitive(appView.abstractValueFactory())
- .createMaterializingInstruction(appView, code, charValueInvoke));
- } else {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- }
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/DoubleMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/DoubleMethodOptimizer.java
index 7c95d1c..780cc81 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/DoubleMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/DoubleMethodOptimizer.java
@@ -5,64 +5,33 @@
package com.android.tools.r8.ir.optimize.library.primitive;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.SingleBoxedDoubleValue;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlockIterator;
-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 com.android.tools.r8.ir.optimize.library.StatelessLibraryMethodModelCollection;
-import java.util.Set;
-public class DoubleMethodOptimizer extends StatelessLibraryMethodModelCollection {
-
- private final AppView<?> appView;
- private final DexItemFactory dexItemFactory;
+public class DoubleMethodOptimizer extends PrimitiveMethodOptimizer {
DoubleMethodOptimizer(AppView<?> appView) {
- this.appView = appView;
- this.dexItemFactory = appView.dexItemFactory();
+ super(appView);
+ }
+
+ @Override
+ DexMethod getBoxMethod() {
+ return dexItemFactory.doubleMembers.valueOf;
+ }
+
+ @Override
+ DexMethod getUnboxMethod() {
+ return dexItemFactory.doubleMembers.doubleValue;
+ }
+
+ @Override
+ boolean isMatchingSingleBoxedPrimitive(AbstractValue abstractValue) {
+ return abstractValue.isSingleBoxedDouble();
}
@Override
public DexType getType() {
return dexItemFactory.boxedDoubleType;
}
-
- @Override
- public void optimize(
- IRCode code,
- BasicBlockIterator blockIterator,
- InstructionListIterator instructionIterator,
- InvokeMethod invoke,
- DexClassAndMethod singleTarget,
- Set<Value> affectedValues,
- Set<BasicBlock> blocksToRemove) {
- if (singleTarget.getReference().isIdenticalTo(dexItemFactory.doubleMembers.doubleValue)) {
- optimizeDoubleValue(code, instructionIterator, invoke);
- }
- }
-
- private void optimizeDoubleValue(
- IRCode code, InstructionListIterator instructionIterator, InvokeMethod doubleValueInvoke) {
- // Optimize Double.valueOf(d).doubleValue() into d.
- AbstractValue abstractValue =
- doubleValueInvoke.getFirstArgument().getAbstractValue(appView, code.context());
- if (abstractValue.isSingleBoxedDouble()) {
- if (doubleValueInvoke.hasOutValue()) {
- SingleBoxedDoubleValue singleBoxedDouble = abstractValue.asSingleBoxedDouble();
- instructionIterator.replaceCurrentInstruction(
- singleBoxedDouble
- .toPrimitive(appView.abstractValueFactory())
- .createMaterializingInstruction(appView, code, doubleValueInvoke));
- } else {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- }
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/FloatMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/FloatMethodOptimizer.java
index 5af9174..c3206b4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/FloatMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/FloatMethodOptimizer.java
@@ -5,64 +5,33 @@
package com.android.tools.r8.ir.optimize.library.primitive;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.SingleBoxedFloatValue;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlockIterator;
-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 com.android.tools.r8.ir.optimize.library.StatelessLibraryMethodModelCollection;
-import java.util.Set;
-public class FloatMethodOptimizer extends StatelessLibraryMethodModelCollection {
-
- private final AppView<?> appView;
- private final DexItemFactory dexItemFactory;
+public class FloatMethodOptimizer extends PrimitiveMethodOptimizer {
FloatMethodOptimizer(AppView<?> appView) {
- this.appView = appView;
- this.dexItemFactory = appView.dexItemFactory();
+ super(appView);
+ }
+
+ @Override
+ DexMethod getBoxMethod() {
+ return dexItemFactory.floatMembers.valueOf;
+ }
+
+ @Override
+ DexMethod getUnboxMethod() {
+ return dexItemFactory.floatMembers.floatValue;
+ }
+
+ @Override
+ boolean isMatchingSingleBoxedPrimitive(AbstractValue abstractValue) {
+ return abstractValue.isSingleBoxedFloat();
}
@Override
public DexType getType() {
return dexItemFactory.boxedFloatType;
}
-
- @Override
- public void optimize(
- IRCode code,
- BasicBlockIterator blockIterator,
- InstructionListIterator instructionIterator,
- InvokeMethod invoke,
- DexClassAndMethod singleTarget,
- Set<Value> affectedValues,
- Set<BasicBlock> blocksToRemove) {
- if (singleTarget.getReference().isIdenticalTo(dexItemFactory.floatMembers.floatValue)) {
- optimizeFloatValue(code, instructionIterator, invoke);
- }
- }
-
- private void optimizeFloatValue(
- IRCode code, InstructionListIterator instructionIterator, InvokeMethod floatValueInvoke) {
- // Optimize Float.valueOf(f).floatValue() into f.
- AbstractValue abstractValue =
- floatValueInvoke.getFirstArgument().getAbstractValue(appView, code.context());
- if (abstractValue.isSingleBoxedFloat()) {
- if (floatValueInvoke.hasOutValue()) {
- SingleBoxedFloatValue singleBoxedFloat = abstractValue.asSingleBoxedFloat();
- instructionIterator.replaceCurrentInstruction(
- singleBoxedFloat
- .toPrimitive(appView.abstractValueFactory())
- .createMaterializingInstruction(appView, code, floatValueInvoke));
- } else {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- }
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/IntegerMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/IntegerMethodOptimizer.java
index 2b16ec1..dba75d4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/IntegerMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/IntegerMethodOptimizer.java
@@ -5,64 +5,33 @@
package com.android.tools.r8.ir.optimize.library.primitive;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.SingleBoxedIntegerValue;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlockIterator;
-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 com.android.tools.r8.ir.optimize.library.StatelessLibraryMethodModelCollection;
-import java.util.Set;
-public class IntegerMethodOptimizer extends StatelessLibraryMethodModelCollection {
-
- private final AppView<?> appView;
- private final DexItemFactory dexItemFactory;
+public class IntegerMethodOptimizer extends PrimitiveMethodOptimizer {
IntegerMethodOptimizer(AppView<?> appView) {
- this.appView = appView;
- this.dexItemFactory = appView.dexItemFactory();
+ super(appView);
+ }
+
+ @Override
+ DexMethod getBoxMethod() {
+ return dexItemFactory.integerMembers.valueOf;
+ }
+
+ @Override
+ DexMethod getUnboxMethod() {
+ return dexItemFactory.integerMembers.intValue;
+ }
+
+ @Override
+ boolean isMatchingSingleBoxedPrimitive(AbstractValue abstractValue) {
+ return abstractValue.isSingleBoxedInteger();
}
@Override
public DexType getType() {
return dexItemFactory.boxedIntType;
}
-
- @Override
- public void optimize(
- IRCode code,
- BasicBlockIterator blockIterator,
- InstructionListIterator instructionIterator,
- InvokeMethod invoke,
- DexClassAndMethod singleTarget,
- Set<Value> affectedValues,
- Set<BasicBlock> blocksToRemove) {
- if (singleTarget.getReference().isIdenticalTo(dexItemFactory.integerMembers.intValue)) {
- optimizeIntegerValue(code, instructionIterator, invoke);
- }
- }
-
- private void optimizeIntegerValue(
- IRCode code, InstructionListIterator instructionIterator, InvokeMethod intValueInvoke) {
- // Optimize Integer.valueOf(i).intValue() into i.
- AbstractValue abstractValue =
- intValueInvoke.getFirstArgument().getAbstractValue(appView, code.context());
- if (abstractValue.isSingleBoxedInteger()) {
- if (intValueInvoke.hasOutValue()) {
- SingleBoxedIntegerValue singleBoxedInteger = abstractValue.asSingleBoxedInteger();
- instructionIterator.replaceCurrentInstruction(
- singleBoxedInteger
- .toPrimitive(appView.abstractValueFactory())
- .createMaterializingInstruction(appView, code, intValueInvoke));
- } else {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- }
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/LongMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/LongMethodOptimizer.java
index 375428a..960baaa 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/LongMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/LongMethodOptimizer.java
@@ -5,64 +5,33 @@
package com.android.tools.r8.ir.optimize.library.primitive;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.SingleBoxedLongValue;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlockIterator;
-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 com.android.tools.r8.ir.optimize.library.StatelessLibraryMethodModelCollection;
-import java.util.Set;
-public class LongMethodOptimizer extends StatelessLibraryMethodModelCollection {
-
- private final AppView<?> appView;
- private final DexItemFactory dexItemFactory;
+public class LongMethodOptimizer extends PrimitiveMethodOptimizer {
LongMethodOptimizer(AppView<?> appView) {
- this.appView = appView;
- this.dexItemFactory = appView.dexItemFactory();
+ super(appView);
+ }
+
+ @Override
+ DexMethod getBoxMethod() {
+ return dexItemFactory.longMembers.valueOf;
+ }
+
+ @Override
+ DexMethod getUnboxMethod() {
+ return dexItemFactory.longMembers.longValue;
+ }
+
+ @Override
+ boolean isMatchingSingleBoxedPrimitive(AbstractValue abstractValue) {
+ return abstractValue.isSingleBoxedLong();
}
@Override
public DexType getType() {
return dexItemFactory.boxedLongType;
}
-
- @Override
- public void optimize(
- IRCode code,
- BasicBlockIterator blockIterator,
- InstructionListIterator instructionIterator,
- InvokeMethod invoke,
- DexClassAndMethod singleTarget,
- Set<Value> affectedValues,
- Set<BasicBlock> blocksToRemove) {
- if (singleTarget.getReference().isIdenticalTo(dexItemFactory.longMembers.longValue)) {
- optimizeLongValue(code, instructionIterator, invoke);
- }
- }
-
- private void optimizeLongValue(
- IRCode code, InstructionListIterator instructionIterator, InvokeMethod longValueInvoke) {
- // Optimize Long.valueOf(l).longValue() into l.
- AbstractValue abstractValue =
- longValueInvoke.getFirstArgument().getAbstractValue(appView, code.context());
- if (abstractValue.isSingleBoxedLong()) {
- if (longValueInvoke.hasOutValue()) {
- SingleBoxedLongValue singleBoxedLong = abstractValue.asSingleBoxedLong();
- instructionIterator.replaceCurrentInstruction(
- singleBoxedLong
- .toPrimitive(appView.abstractValueFactory())
- .createMaterializingInstruction(appView, code, longValueInvoke));
- } else {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- }
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/PrimitiveMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/PrimitiveMethodOptimizer.java
index 6d49a6c..c121c66 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/PrimitiveMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/PrimitiveMethodOptimizer.java
@@ -5,13 +5,33 @@
package com.android.tools.r8.ir.optimize.library.primitive;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.SingleBoxedPrimitiveValue;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+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 com.android.tools.r8.ir.optimize.library.StatelessLibraryMethodModelCollection;
+import java.util.Set;
import java.util.function.Consumer;
public abstract class PrimitiveMethodOptimizer extends StatelessLibraryMethodModelCollection {
+ final AppView<?> appView;
+ final DexItemFactory dexItemFactory;
+
+ PrimitiveMethodOptimizer(AppView<?> appView) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
public static void forEachPrimitiveOptimizer(
- AppView<?> appView, Consumer<StatelessLibraryMethodModelCollection> register) {
+ AppView<?> appView, Consumer<PrimitiveMethodOptimizer> register) {
register.accept(new BooleanMethodOptimizer(appView));
register.accept(new ByteMethodOptimizer(appView));
register.accept(new CharacterMethodOptimizer(appView));
@@ -21,4 +41,83 @@
register.accept(new LongMethodOptimizer(appView));
register.accept(new ShortMethodOptimizer(appView));
}
+
+ abstract DexMethod getBoxMethod();
+
+ abstract DexMethod getUnboxMethod();
+
+ abstract boolean isMatchingSingleBoxedPrimitive(AbstractValue abstractValue);
+
+ @Override
+ public void optimize(
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexClassAndMethod singleTarget,
+ Set<Value> affectedValues,
+ Set<BasicBlock> blocksToRemove) {
+ optimizeBoxingMethods(code, instructionIterator, invoke, singleTarget, affectedValues);
+ }
+
+ void optimizeBoxingMethods(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexClassAndMethod singleTarget,
+ Set<Value> affectedValues) {
+ if (singleTarget.getReference().isIdenticalTo(getUnboxMethod())) {
+ optimizeUnboxMethod(code, instructionIterator, invoke);
+ } else if (singleTarget.getReference().isIdenticalTo(getBoxMethod())) {
+ optimizeBoxMethod(code, instructionIterator, invoke, affectedValues);
+ }
+ }
+
+ void optimizeBoxMethod(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod boxInvoke,
+ Set<Value> affectedValues) {
+ Value firstArg = boxInvoke.getFirstArgument();
+ if (firstArg
+ .getAliasedValue()
+ .isDefinedByInstructionSatisfying(i -> i.isInvokeMethod(getUnboxMethod()))) {
+ // Optimize Primitive.box(boxed.unbox()) into boxed.
+ InvokeMethod unboxInvoke = firstArg.getAliasedValue().getDefinition().asInvokeMethod();
+ assert unboxInvoke.isInvokeVirtual();
+ Value src = boxInvoke.outValue();
+ Value replacement = unboxInvoke.getFirstArgument();
+ // We need to update affected values if the nullability is different.
+ src.replaceUsers(replacement, affectedValues);
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
+ }
+
+ void optimizeUnboxMethod(
+ IRCode code, InstructionListIterator instructionIterator, InvokeMethod unboxInvoke) {
+ Value firstArg = unboxInvoke.getFirstArgument();
+ AbstractValue abstractValue = firstArg.getAbstractValue(appView, code.context());
+ if (isMatchingSingleBoxedPrimitive(abstractValue)) {
+ // Optimize Primitive.box(cst).unbox() into cst, possibly inter-procedurally.
+ if (unboxInvoke.hasOutValue()) {
+ SingleBoxedPrimitiveValue singleBoxedNumber = abstractValue.asSingleBoxedPrimitive();
+ instructionIterator.replaceCurrentInstruction(
+ singleBoxedNumber
+ .toPrimitive(appView.abstractValueFactory())
+ .createMaterializingInstruction(appView, code, unboxInvoke));
+ } else {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
+ return;
+ }
+ if (firstArg
+ .getAliasedValue()
+ .isDefinedByInstructionSatisfying(i -> i.isInvokeMethod(getBoxMethod()))) {
+ // Optimize Primitive.box(unboxed).unbox() into unboxed.
+ InvokeMethod boxInvoke = firstArg.getAliasedValue().getDefinition().asInvokeMethod();
+ assert boxInvoke.isInvokeStatic();
+ unboxInvoke.outValue().replaceUsers(boxInvoke.getFirstArgument());
+ instructionIterator.replaceCurrentInstructionByNullCheckIfPossible(appView, code.context());
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/ShortMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/ShortMethodOptimizer.java
index df1f82e..8bae65e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/ShortMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/primitive/ShortMethodOptimizer.java
@@ -3,66 +3,34 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize.library.primitive;
-
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.SingleBoxedShortValue;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlockIterator;
-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 com.android.tools.r8.ir.optimize.library.StatelessLibraryMethodModelCollection;
-import java.util.Set;
-public class ShortMethodOptimizer extends StatelessLibraryMethodModelCollection {
-
- private final AppView<?> appView;
- private final DexItemFactory dexItemFactory;
+public class ShortMethodOptimizer extends PrimitiveMethodOptimizer {
ShortMethodOptimizer(AppView<?> appView) {
- this.appView = appView;
- this.dexItemFactory = appView.dexItemFactory();
+ super(appView);
+ }
+
+ @Override
+ DexMethod getBoxMethod() {
+ return dexItemFactory.shortMembers.valueOf;
+ }
+
+ @Override
+ DexMethod getUnboxMethod() {
+ return dexItemFactory.shortMembers.shortValue;
+ }
+
+ @Override
+ boolean isMatchingSingleBoxedPrimitive(AbstractValue abstractValue) {
+ return abstractValue.isSingleBoxedShort();
}
@Override
public DexType getType() {
return dexItemFactory.boxedShortType;
}
-
- @Override
- public void optimize(
- IRCode code,
- BasicBlockIterator blockIterator,
- InstructionListIterator instructionIterator,
- InvokeMethod invoke,
- DexClassAndMethod singleTarget,
- Set<Value> affectedValues,
- Set<BasicBlock> blocksToRemove) {
- if (singleTarget.getReference().isIdenticalTo(dexItemFactory.shortMembers.shortValue)) {
- optimizeShortValue(code, instructionIterator, invoke);
- }
- }
-
- private void optimizeShortValue(
- IRCode code, InstructionListIterator instructionIterator, InvokeMethod shortValueInvoke) {
- // Optimize Short.valueOf(s).shortValue() into s.
- AbstractValue abstractValue =
- shortValueInvoke.getFirstArgument().getAbstractValue(appView, code.context());
- if (abstractValue.isSingleBoxedShort()) {
- if (shortValueInvoke.hasOutValue()) {
- SingleBoxedShortValue singleBoxedShort = abstractValue.asSingleBoxedShort();
- instructionIterator.replaceCurrentInstruction(
- singleBoxedShort
- .toPrimitive(appView.abstractValueFactory())
- .createMaterializingInstruction(appView, code, shortValueInvoke));
- } else {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- }
- }
- }
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/boxedprimitives/ReverseBoxingOperationsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/boxedprimitives/ReverseBoxingOperationsTest.java
new file mode 100644
index 0000000..552abdc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/boxedprimitives/ReverseBoxingOperationsTest.java
@@ -0,0 +1,345 @@
+// Copyright (c) 2023, 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.boxedprimitives;
+
+import static org.junit.Assert.assertEquals;
+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.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ReverseBoxingOperationsTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testUnboxingRemoved() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters)
+ .compile()
+ .inspect(this::assertBoxOperationsRemoved)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(getExpectedResult());
+ }
+
+ private void assertBoxOperationsRemoved(CodeInspector codeInspector) {
+ DexItemFactory factory = codeInspector.getFactory();
+
+ Set<DexMethod> unboxMethods = Sets.newIdentityHashSet();
+ Set<DexMethod> boxMethods = Sets.newIdentityHashSet();
+ for (DexType primitiveType : factory.primitiveToBoxed.keySet()) {
+ unboxMethods.add(factory.getUnboxPrimitiveMethod(primitiveType));
+ boxMethods.add(factory.getBoxPrimitiveMethod(primitiveType));
+ }
+
+ // All box operations should have been removed, except for the unbox methods that act as null
+ // checks, which may or may not be replaced by null-checks depending if they are reprocessed.
+ assertEquals(
+ 24,
+ codeInspector
+ .clazz(Main.class)
+ .allMethods(m -> !m.getOriginalName().equals("main"))
+ .size());
+ codeInspector
+ .clazz(Main.class)
+ .allMethods(m -> !m.getOriginalName().equals("main"))
+ .forEach(
+ m ->
+ assertTrue(
+ m.streamInstructions()
+ .noneMatch(i -> i.isInvoke() && boxMethods.contains(i.getMethod()))));
+ codeInspector
+ .clazz(Main.class)
+ .allMethods(
+ m ->
+ !m.getOriginalName().equals("main")
+ && (m.getOriginalName().contains("Unbox")
+ || m.getOriginalName().contains("NonNull")))
+ .forEach(
+ m ->
+ assertTrue(
+ m.streamInstructions()
+ .noneMatch(i -> i.isInvoke() && unboxMethods.contains(i.getMethod()))));
+ }
+
+ private String getExpectedResult() {
+ String[] resultItems = {"1", "1", "1.0", "1.0", "1", "1", "c", "true"};
+ String[] resultItems2 = {"2", "2", "2.0", "2.0", "2", "2", "e", "false"};
+ List<String> result = new ArrayList<>();
+ for (int i = 0; i < resultItems.length; i++) {
+ String item = resultItems[i];
+ String item2 = resultItems2[i];
+ result.add(">" + item);
+ result.add(">" + item);
+ result.add(">npe failure");
+ result.add(">" + item);
+ result.add(">" + item2);
+ }
+ return StringUtils.lines(result);
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ int i = System.currentTimeMillis() > 0 ? 1 : 0;
+ System.out.println(intUnboxTest(i));
+ System.out.println(intTest(i));
+ try {
+ System.out.println(intTest(null));
+ } catch (NullPointerException npe7) {
+ System.out.println("npe failure");
+ }
+ System.out.println(intTestNonNull(i));
+ System.out.println(intTestNonNull(i + 1));
+
+ long l = System.currentTimeMillis() > 0 ? 1L : 0L;
+ System.out.println(longUnboxTest(l));
+ System.out.println(longTest(l));
+ try {
+ System.out.println(longTest(null));
+ } catch (NullPointerException npe6) {
+ System.out.println("npe failure");
+ }
+ System.out.println(longTestNonNull(l));
+ System.out.println(longTestNonNull(l + 1));
+
+ double d = System.currentTimeMillis() > 0 ? 1.0 : 0.0;
+ System.out.println(doubleUnboxTest(d));
+ System.out.println(doubleTest(d));
+ try {
+ System.out.println(doubleTest(null));
+ } catch (NullPointerException npe5) {
+ System.out.println("npe failure");
+ }
+ System.out.println(doubleTestNonNull(d));
+ System.out.println(doubleTestNonNull(d + 1));
+
+ float f = System.currentTimeMillis() > 0 ? 1.0f : 0.0f;
+ System.out.println(floatUnboxTest(f));
+ System.out.println(floatTest(f));
+ try {
+ System.out.println(floatTest(null));
+ } catch (NullPointerException npe4) {
+ System.out.println("npe failure");
+ }
+ System.out.println(floatTestNonNull(f));
+ System.out.println(floatTestNonNull(f + 1));
+
+ byte b = (byte) (System.currentTimeMillis() > 0 ? 1 : 0);
+ System.out.println(byteUnboxTest(b));
+ System.out.println(byteTest(b));
+ try {
+ System.out.println(byteTest(null));
+ } catch (NullPointerException npe3) {
+ System.out.println("npe failure");
+ }
+ System.out.println(byteTestNonNull(b));
+ System.out.println(byteTestNonNull((byte) (b + 1)));
+
+ short s = (short) (System.currentTimeMillis() > 0 ? 1 : 0);
+ System.out.println(shortUnboxTest(s));
+ System.out.println(shortTest(s));
+ try {
+ System.out.println(shortTest(null));
+ } catch (NullPointerException npe2) {
+ System.out.println("npe failure");
+ }
+ System.out.println(shortTestNonNull(s));
+ System.out.println(shortTestNonNull((short) (s + 1)));
+
+ char c = System.currentTimeMillis() > 0 ? 'c' : 'd';
+ System.out.println(charUnboxTest(c));
+ System.out.println(charTest(c));
+ try {
+ System.out.println(charTest(null));
+ } catch (NullPointerException npe1) {
+ System.out.println("npe failure");
+ }
+ System.out.println(charTestNonNull(c));
+ System.out.println(charTestNonNull('e'));
+
+ boolean bool = System.currentTimeMillis() > 0;
+ System.out.println(booleanUnboxTest(bool));
+ System.out.println(booleanTest(bool));
+ try {
+ System.out.println(booleanTest(null));
+ } catch (NullPointerException npe) {
+ System.out.println("npe failure");
+ }
+ System.out.println(booleanTestNonNull(bool));
+ System.out.println(booleanTestNonNull(false));
+ }
+
+ @NeverInline
+ public static int intUnboxTest(int i) {
+ System.out.print(">");
+ return Integer.valueOf(i).intValue();
+ }
+
+ @NeverInline
+ public static Integer intTest(Integer i) {
+ System.out.print(">");
+ return Integer.valueOf(i.intValue());
+ }
+
+ @NeverInline
+ public static Integer intTestNonNull(Integer i) {
+ System.out.print(">");
+ return Integer.valueOf(i.intValue());
+ }
+
+ @NeverInline
+ public static double doubleUnboxTest(double d) {
+ System.out.print(">");
+ return Double.valueOf(d).doubleValue();
+ }
+
+ @NeverInline
+ public static Double doubleTest(Double d) {
+ System.out.print(">");
+ return Double.valueOf(d.doubleValue());
+ }
+
+ @NeverInline
+ public static Double doubleTestNonNull(Double d) {
+ System.out.print(">");
+ return Double.valueOf(d.doubleValue());
+ }
+
+ @NeverInline
+ public static long longUnboxTest(long l) {
+ System.out.print(">");
+ return Long.valueOf(l).longValue();
+ }
+
+ @NeverInline
+ public static Long longTest(Long l) {
+ System.out.print(">");
+ return Long.valueOf(l.longValue());
+ }
+
+ @NeverInline
+ public static Long longTestNonNull(Long l) {
+ System.out.print(">");
+ return Long.valueOf(l.longValue());
+ }
+
+ @NeverInline
+ public static float floatUnboxTest(float f) {
+ System.out.print(">");
+ return Float.valueOf(f).floatValue();
+ }
+
+ @NeverInline
+ public static Float floatTest(Float f) {
+ System.out.print(">");
+ return Float.valueOf(f.floatValue());
+ }
+
+ @NeverInline
+ public static Float floatTestNonNull(Float f) {
+ System.out.print(">");
+ return Float.valueOf(f.floatValue());
+ }
+
+ @NeverInline
+ public static short shortUnboxTest(short s) {
+ System.out.print(">");
+ return Short.valueOf(s).shortValue();
+ }
+
+ @NeverInline
+ public static Short shortTest(Short s) {
+ System.out.print(">");
+ return Short.valueOf(s.shortValue());
+ }
+
+ @NeverInline
+ public static Short shortTestNonNull(Short s) {
+ System.out.print(">");
+ return Short.valueOf(s.shortValue());
+ }
+
+ @NeverInline
+ public static char charUnboxTest(char c) {
+ System.out.print(">");
+ return Character.valueOf(c).charValue();
+ }
+
+ @NeverInline
+ public static Character charTest(Character c) {
+ System.out.print(">");
+ return Character.valueOf(c.charValue());
+ }
+
+ @NeverInline
+ public static Character charTestNonNull(Character c) {
+ System.out.print(">");
+ return Character.valueOf(c.charValue());
+ }
+
+ @NeverInline
+ public static byte byteUnboxTest(byte b) {
+ System.out.print(">");
+ return Byte.valueOf(b).byteValue();
+ }
+
+ @NeverInline
+ public static Byte byteTest(Byte b) {
+ System.out.print(">");
+ return Byte.valueOf(b.byteValue());
+ }
+
+ @NeverInline
+ public static Byte byteTestNonNull(Byte b) {
+ System.out.print(">");
+ return Byte.valueOf(b.byteValue());
+ }
+
+ @NeverInline
+ public static boolean booleanUnboxTest(boolean b) {
+ System.out.print(">");
+ return Boolean.valueOf(b).booleanValue();
+ }
+
+ @NeverInline
+ public static Boolean booleanTest(Boolean b) {
+ System.out.print(">");
+ return Boolean.valueOf(b.booleanValue());
+ }
+
+ @NeverInline
+ public static Boolean booleanTestNonNull(Boolean b) {
+ System.out.print(">");
+ return Boolean.valueOf(b.booleanValue());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/boxedprimitives/UnboxToCheckNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/boxedprimitives/UnboxToCheckNotNullTest.java
new file mode 100644
index 0000000..47b7834
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/boxedprimitives/UnboxToCheckNotNullTest.java
@@ -0,0 +1,163 @@
+// Copyright (c) 2023, 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.boxedprimitives;
+
+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.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class UnboxToCheckNotNullTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testValue() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters)
+ .compile()
+ .inspect(this::assertCheckNotNull)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(getExpectedResult());
+ }
+
+ private void assertCheckNotNull(CodeInspector codeInspector) {
+ DexItemFactory factory = codeInspector.getFactory();
+
+ Set<DexMethod> unboxMethods = Sets.newIdentityHashSet();
+ unboxMethods.addAll(factory.unboxPrimitiveMethod.values());
+ Set<DexType> boxedTypes = Sets.newIdentityHashSet();
+ boxedTypes.addAll(factory.primitiveToBoxed.values());
+
+ // All unbox operations should have been replaced by checkNotNull operations.
+ codeInspector
+ .clazz(Main.class)
+ .allMethods(
+ m ->
+ !m.getParameters().isEmpty()
+ && boxedTypes.contains(
+ factory.createType(
+ DescriptorUtils.javaTypeToDescriptor(m.getParameter(0).getTypeName()))))
+ .forEach(
+ m ->
+ assertTrue(
+ m.streamInstructions()
+ .noneMatch(i -> i.isInvoke() && unboxMethods.contains(i.getMethod()))));
+ }
+
+ private String getExpectedResult() {
+ List<String> result = new ArrayList<>();
+ for (int i = 0; i < 8; i++) {
+ result.add("run succeeded");
+ result.add("npe failure");
+ }
+ return StringUtils.lines(result);
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ testCheckingNPE(() -> intTest(1));
+ testCheckingNPE(() -> intTest(null));
+
+ testCheckingNPE(() -> longTest(1L));
+ testCheckingNPE(() -> longTest(null));
+
+ testCheckingNPE(() -> doubleTest(1.0));
+ testCheckingNPE(() -> doubleTest(null));
+
+ testCheckingNPE(() -> floatTest(1.0f));
+ testCheckingNPE(() -> floatTest(null));
+
+ testCheckingNPE(() -> byteTest((byte) 1));
+ testCheckingNPE(() -> byteTest(null));
+
+ testCheckingNPE(() -> shortTest((short) 1));
+ testCheckingNPE(() -> shortTest(null));
+
+ testCheckingNPE(() -> charTest('c'));
+ testCheckingNPE(() -> charTest(null));
+
+ testCheckingNPE(() -> booleanTest(true));
+ testCheckingNPE(() -> booleanTest(null));
+ }
+
+ public static void testCheckingNPE(Runnable runnable) {
+ try {
+ runnable.run();
+ System.out.println("run succeeded");
+ } catch (NullPointerException npe) {
+ System.out.println("npe failure");
+ }
+ }
+
+ @NeverInline
+ public static void intTest(Integer i) {
+ i.intValue();
+ }
+
+ @NeverInline
+ public static void doubleTest(Double d) {
+ d.doubleValue();
+ }
+
+ @NeverInline
+ public static void longTest(Long l) {
+ l.longValue();
+ }
+
+ @NeverInline
+ public static void floatTest(Float f) {
+ f.floatValue();
+ }
+
+ @NeverInline
+ public static void shortTest(Short s) {
+ s.shortValue();
+ }
+
+ @NeverInline
+ public static void charTest(Character c) {
+ c.charValue();
+ }
+
+ @NeverInline
+ public static void byteTest(Byte b) {
+ b.byteValue();
+ }
+
+ @NeverInline
+ public static void booleanTest(Boolean b) {
+ b.booleanValue();
+ }
+ }
+}