Remove original definitions in value propagation
This extends member value propagation to:
* remove static-get instructions that do not trigger class initialization
* replace static-get instructions that do trigger class initialization by init-class instructions
* remove invoke-static instructions that do not trigger class initialization and do not have side effects
* replace invoke-static instructions that trigger class initialization but do not have side effects by init-class instructions
Change-Id: Idcfabdca77c3bc12658f055b1a9dec592e87b61a
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
index 12ff943..da92bf3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -83,7 +83,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow(appView, context).isThrowing();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index 8ab298a..589710f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -144,7 +144,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
// In debug mode, ArrayPut has a side-effect on the locals.
if (appView.options().debug) {
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index c28bc4a..a084a0f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -95,7 +95,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow(appView, context).isThrowing();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index 1574275..a9089f8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -135,7 +135,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow(appView, context).isThrowing();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 366f0cf..507ae25 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -28,20 +28,6 @@
public abstract class FieldInstruction extends Instruction {
- public enum Assumption {
- NONE,
- CLASS_ALREADY_INITIALIZED,
- RECEIVER_NOT_NULL;
-
- boolean canAssumeClassIsAlreadyInitialized() {
- return this == CLASS_ALREADY_INITIALIZED;
- }
-
- boolean canAssumeReceiverIsNotNull() {
- return this == RECEIVER_NOT_NULL;
- }
- }
-
private final DexField field;
protected FieldInstruction(DexField field, Value dest, Value value) {
@@ -76,11 +62,11 @@
@Override
public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
- return instructionInstanceCanThrow(appView, context, Assumption.NONE);
+ return instructionInstanceCanThrow(appView, context, SideEffectAssumption.NONE);
}
public AbstractError instructionInstanceCanThrow(
- AppView<?> appView, DexType context, Assumption assumption) {
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
DexEncodedField resolvedField;
if (appView.enableWholeProgramOptimizations()) {
// TODO(b/123857022): Should be possible to use definitionFor().
diff --git a/src/main/java/com/android/tools/r8/ir/code/InitClass.java b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
index 667e733..a4d0704 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InitClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
@@ -108,7 +108,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow(appView, context).isThrowing();
}
@@ -159,4 +160,9 @@
public boolean hasInvariantOutType() {
return true;
}
+
+ @Override
+ public String toString() {
+ return super.toString() + "; " + clazz.toSourceString();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java
index 94bfdaf..e78d3a7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.FieldInstruction.Assumption;
+import com.android.tools.r8.ir.code.Instruction.SideEffectAssumption;
public interface InstanceFieldInstruction {
@@ -16,10 +16,13 @@
Value object();
- boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context, Assumption assumption);
+ boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption);
FieldInstruction asFieldInstruction();
+ boolean isInstanceFieldInstruction();
+
boolean isInstanceGet();
InstanceGet asInstanceGet();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index b070660..6d9b8e9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -115,13 +115,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
- return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
- }
-
- @Override
public boolean instructionMayHaveSideEffects(
- AppView<?> appView, DexType context, Assumption assumption) {
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow(appView, context, assumption).isThrowing();
}
@@ -161,6 +156,16 @@
}
@Override
+ public boolean isInstanceFieldInstruction() {
+ return true;
+ }
+
+ @Override
+ public InstanceFieldInstruction asInstanceFieldInstruction() {
+ return this;
+ }
+
+ @Override
public boolean isInstanceGet() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 00a5f30..5680260 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -114,13 +114,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
- return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
- }
-
- @Override
public boolean instructionMayHaveSideEffects(
- AppView<?> appView, DexType context, Assumption assumption) {
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
if (appView.appInfo().hasLiveness()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
@@ -200,6 +195,16 @@
}
@Override
+ public boolean isInstanceFieldInstruction() {
+ return true;
+ }
+
+ @Override
+ public InstanceFieldInstruction asInstanceFieldInstruction() {
+ return this;
+ }
+
+ @Override
public boolean isInstancePut() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index d49e290..a56f0a2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -571,6 +571,11 @@
}
public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ return instructionMayHaveSideEffects(appView, context, SideEffectAssumption.NONE);
+ }
+
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow();
}
@@ -888,6 +893,14 @@
return null;
}
+ public boolean isInstanceFieldInstruction() {
+ return false;
+ }
+
+ public InstanceFieldInstruction asInstanceFieldInstruction() {
+ return null;
+ }
+
public boolean isInstanceGet() {
return false;
}
@@ -1460,4 +1473,18 @@
public boolean outTypeKnownToBeBoolean(Set<Phi> seen) {
return false;
}
+
+ public enum SideEffectAssumption {
+ NONE,
+ CLASS_ALREADY_INITIALIZED,
+ RECEIVER_NOT_NULL;
+
+ boolean canAssumeClassIsAlreadyInitialized() {
+ return this == CLASS_ALREADY_INITIALIZED;
+ }
+
+ boolean canAssumeReceiverIsNotNull() {
+ return this == RECEIVER_NOT_NULL;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index ec0c68f..b4531c3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -158,14 +158,15 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
if (appView.options().debug) {
return true;
}
// Check if it could throw a NullPointerException as a result of the receiver being null.
Value receiver = getReceiver();
- if (receiver.getTypeLattice().isNullable()) {
+ if (!assumption.canAssumeReceiverIsNotNull() && receiver.getTypeLattice().isNullable()) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index 831c65f..02e45ef 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -171,7 +171,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
// Check if the instruction has a side effect on the locals environment.
if (hasOutValue() && outValue().hasLocalInfo()) {
assert appView.options().debug;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index 8378a5d..ce75c7c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -184,7 +184,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
// Check if the instruction has a side effect on the locals environment.
if (hasOutValue() && outValue().hasLocalInfo()) {
assert appView.options().debug;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index da1c8fd..2d29972 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -156,7 +156,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
if (!appView.enableWholeProgramOptimizations()) {
return true;
}
@@ -187,22 +188,24 @@
}
// Verify that the target method does not have side-effects.
- boolean targetMayHaveSideEffects;
if (appViewWithLiveness.appInfo().noSideEffects.containsKey(target.method)) {
- targetMayHaveSideEffects = false;
- } else {
- targetMayHaveSideEffects =
- target.getOptimizationInfo().mayHaveSideEffects()
- // Verify that calling the target method won't lead to class initialization.
- || target.method.holder.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized
- // already.
- type -> appView.isSubtype(context, type).isTrue(),
- Sets.newIdentityHashSet());
+ return false;
}
- return targetMayHaveSideEffects;
+ if (target.getOptimizationInfo().mayHaveSideEffects()) {
+ return true;
+ }
+
+ if (assumption.canAssumeClassIsAlreadyInitialized()) {
+ return false;
+ }
+
+ return target.method.holder.classInitializationMayHaveSideEffects(
+ appView,
+ // Types that are a super type of `context` are guaranteed to be initialized
+ // already.
+ type -> appView.isSubtype(context, type).isTrue(),
+ Sets.newIdentityHashSet());
}
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 1ec8873..1218f08 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -142,7 +142,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
if (!appView.enableWholeProgramOptimizations()) {
return true;
}
@@ -153,7 +154,7 @@
// Check if it could throw a NullPointerException as a result of the receiver being null.
Value receiver = getReceiver();
- if (receiver.getTypeLattice().isNullable()) {
+ if (!assumption.canAssumeReceiverIsNotNull() && receiver.getTypeLattice().isNullable()) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
index 226b771..afd87ff 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -129,7 +129,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
// Treat the instruction as possibly having side-effects if it may throw or the array is used.
if (instructionInstanceCanThrow(appView, context).isThrowing()
|| src().numberOfAllUsers() > 1) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 23ac981..384a27a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -141,7 +141,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
if (!appView.enableWholeProgramOptimizations()) {
return !(dexItemFactory.libraryTypesAssumedToBePresent.contains(clazz)
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
index c4b5f35..286b470 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.FieldInstruction.Assumption;
+import com.android.tools.r8.ir.code.Instruction.SideEffectAssumption;
public interface StaticFieldInstruction {
@@ -16,7 +16,8 @@
boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context);
- boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context, Assumption assumption);
+ boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption);
FieldInstruction asFieldInstruction();
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index e7c2dee..a3c202b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -137,12 +137,12 @@
@Override
public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
- return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
+ return instructionMayHaveSideEffects(appView, context, SideEffectAssumption.NONE);
}
@Override
public boolean instructionMayHaveSideEffects(
- AppView<?> appView, DexType context, Assumption assumption) {
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow(appView, context, assumption).isThrowing();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 9d25691..e7d2bdc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -94,13 +94,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
- return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
- }
-
- @Override
public boolean instructionMayHaveSideEffects(
- AppView<?> appView, DexType context, Assumption assumption) {
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
if (appView.appInfo().hasLiveness()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index c6a84aa..7e25c8a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -24,7 +24,6 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.IRMetadata;
import com.android.tools.r8.ir.code.InitClass;
-import com.android.tools.r8.ir.code.InstanceFieldInstruction;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -32,7 +31,6 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.StaticFieldInstruction;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
@@ -212,7 +210,7 @@
}
if (current.isStaticGet()) {
StaticGet staticGet = current.asStaticGet();
- replaceStaticFieldInstructionByClinitAccessIfPossible(
+ replaceInstructionByInitClassIfPossible(
staticGet, staticGet.getField().holder, code, iterator, code.method.holder());
}
replacement.setPosition(position);
@@ -227,7 +225,7 @@
private void rewriteInvokeMethodWithConstantValues(
IRCode code,
- DexType callingContext,
+ DexType context,
Set<Value> affectedValues,
ListIterator<BasicBlock> blocks,
InstructionListIterator iterator,
@@ -237,7 +235,7 @@
if (!invokedHolder.isClassType()) {
return;
}
- DexEncodedMethod target = current.lookupSingleTarget(appView, callingContext);
+ DexEncodedMethod target = current.lookupSingleTarget(appView, context);
if (target != null && target.isInstanceInitializer()) {
// Member value propagation does not apply to constructors. Removing a call to a constructor
// that is marked as having no side effects could lead to verification errors, due to
@@ -292,15 +290,27 @@
AbstractValue abstractReturnValue = target.getOptimizationInfo().getAbstractReturnValue();
if (abstractReturnValue.isSingleValue()) {
SingleValue singleReturnValue = abstractReturnValue.asSingleValue();
- if (singleReturnValue.isMaterializableInContext(appView, callingContext)) {
+ if (singleReturnValue.isMaterializableInContext(appView, context)) {
+ BasicBlock block = current.getBlock();
+ Position position = current.getPosition();
+
Instruction replacement =
singleReturnValue.createMaterializingInstruction(appView, code, current);
affectedValues.addAll(current.outValue().affectedValues());
+ current.moveDebugValues(replacement);
current.outValue().replaceUsers(replacement.outValue());
current.setOutValue(null);
- replacement.setPosition(current.getPosition());
- current.moveDebugValues(replacement);
- if (current.getBlock().hasCatchHandlers()) {
+
+ if (current.isInvokeMethodWithReceiver()) {
+ replaceInstructionByNullCheckIfPossible(current, iterator, context);
+ } else if (current.isInvokeStatic()) {
+ replaceInstructionByInitClassIfPossible(
+ current, target.holder(), code, iterator, context);
+ }
+
+ // Insert the definition of the replacement.
+ replacement.setPosition(position);
+ if (block.hasCatchHandlers()) {
iterator.split(code, blocks).listIterator(code).add(replacement);
} else {
iterator.add(replacement);
@@ -355,47 +365,45 @@
Instruction replacement =
target.valueAsConstInstruction(code, current.outValue().getLocalInfo(), appView);
if (replacement != null) {
+ BasicBlock block = current.getBlock();
+ DexType context = code.method.holder();
+ Position position = current.getPosition();
+
+ // All usages are replaced by the replacement value.
affectedValues.addAll(current.outValue().affectedValues());
- DexType context = code.method.method.holder;
- if (current.instructionMayHaveSideEffects(appView, context)) {
- BasicBlock block = current.getBlock();
- Position position = current.getPosition();
+ current.outValue().replaceUsers(replacement.outValue());
- // All usages are replaced by the replacement value.
- current.outValue().replaceUsers(replacement.outValue());
-
- // To preserve side effects, original field-get is replaced by an explicit null-check, if
- // the field-get instruction may only fail with an NPE, or the field-get remains as-is.
- if (current.isInstanceGet()) {
- replaceInstanceFieldInstructionByNullCheckIfPossible(
- current.asInstanceGet(), iterator, context);
- } else {
- replaceStaticFieldInstructionByClinitAccessIfPossible(
- current.asStaticGet(), target.holder(), code, iterator, context);
- }
-
- // Insert the definition of the replacement.
- replacement.setPosition(position);
- if (block.hasCatchHandlers()) {
- iterator.split(code, blocks).listIterator(code).add(replacement);
- } else {
- iterator.add(replacement);
- }
+ // To preserve side effects, original field-get is replaced by an explicit null-check, if
+ // the field-get instruction may only fail with an NPE, or the field-get remains as-is.
+ if (current.isInstanceGet()) {
+ replaceInstructionByNullCheckIfPossible(current, iterator, context);
} else {
- iterator.replaceCurrentInstruction(replacement);
+ replaceInstructionByInitClassIfPossible(current, target.holder(), code, iterator, context);
+ }
+
+ // Insert the definition of the replacement.
+ replacement.setPosition(position);
+ if (block.hasCatchHandlers()) {
+ iterator.split(code, blocks).listIterator(code).add(replacement);
+ } else {
+ iterator.add(replacement);
}
feedback.markFieldAsPropagated(target);
}
}
- private void replaceInstanceFieldInstructionByNullCheckIfPossible(
- InstanceFieldInstruction instruction, InstructionListIterator iterator, DexType context) {
+ private void replaceInstructionByNullCheckIfPossible(
+ Instruction instruction, InstructionListIterator iterator, DexType context) {
+ assert instruction.isInstanceFieldInstruction() || instruction.isInvokeMethodWithReceiver();
assert !instruction.hasOutValue() || !instruction.outValue().hasAnyUsers();
if (instruction.instructionMayHaveSideEffects(
- appView, context, FieldInstruction.Assumption.RECEIVER_NOT_NULL)) {
+ appView, context, Instruction.SideEffectAssumption.RECEIVER_NOT_NULL)) {
return;
}
- Value receiver = instruction.object();
+ Value receiver =
+ instruction.isInstanceFieldInstruction()
+ ? instruction.asInstanceFieldInstruction().object()
+ : instruction.asInvokeMethodWithReceiver().getReceiver();
if (receiver.isNeverNull()) {
iterator.removeOrReplaceByDebugLocalRead();
return;
@@ -411,6 +419,38 @@
iterator.replaceCurrentInstruction(replacement);
}
+ private void replaceInstructionByInitClassIfPossible(
+ Instruction instruction,
+ DexType holder,
+ IRCode code,
+ InstructionListIterator iterator,
+ DexType context) {
+ assert instruction.isStaticFieldInstruction() || instruction.isInvokeStatic();
+ if (instruction.instructionMayHaveSideEffects(
+ appView, context, Instruction.SideEffectAssumption.CLASS_ALREADY_INITIALIZED)) {
+ return;
+ }
+ boolean classInitializationMayHaveSideEffects =
+ holder.classInitializationMayHaveSideEffects(
+ appView,
+ // Types that are a super type of `context` are guaranteed to be initialized
+ // already.
+ type -> appView.isSubtype(context, type).isTrue(),
+ Sets.newIdentityHashSet());
+ if (!classInitializationMayHaveSideEffects) {
+ iterator.removeOrReplaceByDebugLocalRead();
+ return;
+ }
+ if (!appView.canUseInitClass()) {
+ return;
+ }
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(holder));
+ if (clazz != null) {
+ Value dest = code.createValue(TypeLatticeElement.getInt());
+ iterator.replaceCurrentInstruction(new InitClass(dest, clazz.type));
+ }
+ }
+
private void replaceInstancePutByNullCheckIfNeverRead(
IRCode code, InstructionListIterator iterator, InstancePut current) {
DexEncodedField target = appView.appInfo().resolveField(current.getField());
@@ -422,10 +462,10 @@
return;
}
- replaceInstanceFieldInstructionByNullCheckIfPossible(current, iterator, code.method.holder());
+ replaceInstructionByNullCheckIfPossible(current, iterator, code.method.holder());
}
- private void replaceStaticPutByClinitAccessIfNeverRead(
+ private void replaceStaticPutByInitClassIfNeverRead(
IRCode code, InstructionListIterator iterator, StaticPut current) {
DexEncodedField field = appView.appInfo().resolveField(current.getField());
if (field == null || appView.appInfo().isFieldRead(field)) {
@@ -436,34 +476,10 @@
return;
}
- replaceStaticFieldInstructionByClinitAccessIfPossible(
+ replaceInstructionByInitClassIfPossible(
current, field.holder(), code, iterator, code.method.holder());
}
- private void replaceStaticFieldInstructionByClinitAccessIfPossible(
- StaticFieldInstruction instruction,
- DexType holder,
- IRCode code,
- InstructionListIterator iterator,
- DexType context) {
- if (!instruction.instructionMayHaveSideEffects(appView, context)) {
- iterator.removeOrReplaceByDebugLocalRead();
- return;
- }
- if (!appView.canUseInitClass()) {
- return;
- }
- if (instruction.instructionMayHaveSideEffects(
- appView, context, FieldInstruction.Assumption.CLASS_ALREADY_INITIALIZED)) {
- return;
- }
- DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(holder));
- if (clazz != null) {
- Value dest = code.createValue(TypeLatticeElement.getInt());
- iterator.replaceCurrentInstruction(new InitClass(dest, clazz.type));
- }
- }
-
/**
* Replace invoke targets and field accesses with constant values where possible.
*
@@ -491,7 +507,7 @@
} else if (current.isInstancePut()) {
replaceInstancePutByNullCheckIfNeverRead(code, iterator, current.asInstancePut());
} else if (current.isStaticPut()) {
- replaceStaticPutByClinitAccessIfNeverRead(code, iterator, current.asStaticPut());
+ replaceStaticPutByInitClassIfNeverRead(code, iterator, current.asStaticPut());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/InliningAfterStaticClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/InliningAfterStaticClassMergerTest.java
index d59f954..23c2d55 100644
--- a/src/test/java/com/android/tools/r8/classmerging/InliningAfterStaticClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/InliningAfterStaticClassMergerTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -35,6 +36,7 @@
// Cannot be inlined into TestClass.main() because the static initialization of this class could
// have side-effects; in order for R8 to be conservative, library classes are treated as if
// their static initialization could have side-effects.
+ @NeverPropagateValue
public static String m() {
return "StaticMergeCandidateA.m()";
}
@@ -44,6 +46,7 @@
// Can be inlined into TestClass.main() because the static initialization of this class has no
// side-effects.
+ @NeverPropagateValue
public static String m() {
return "StaticMergeCandidateB.m()";
}
@@ -65,7 +68,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
@Test
@@ -82,8 +85,9 @@
.addKeepMainRule(TestClass.class)
.addOptionsModification(
options -> options.libraryInterfacesMayHaveStaticInitialization = true)
+ .enableMemberValuePropagationAnnotations()
.noMinification()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expected)
.inspector();
@@ -94,11 +98,13 @@
.filter(clazz -> clazz.getOriginalName().contains("StaticMergeCandidate"))
.collect(Collectors.toList());
assertEquals(1, classes.size());
- assertEquals(StaticMergeCandidateA.class.getTypeName(), classes.get(0).getOriginalName());
+
+ FoundClassSubject clazz = classes.get(0);
+ assertEquals(StaticMergeCandidateA.class.getTypeName(), clazz.getOriginalName());
// Check that StaticMergeCandidateB.m() has not been moved into StaticMergeCandidateA, because
// that would disable inlining of it.
- assertEquals(1, classes.get(0).allMethods().size());
+ assertEquals(1, clazz.allMethods().size());
// Check that the test class only has a main method.
ClassSubject classSubject = inspector.clazz(TestClass.class);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagationWithClassInitializationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagationWithClassInitializationTest.java
index 3090720..c87d144 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagationWithClassInitializationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagationWithClassInitializationTest.java
@@ -66,7 +66,7 @@
assertThat(bClassSubject, isPresent());
MethodSubject methodSubject = bClassSubject.uniqueMethodWithName("method");
- assertThat(methodSubject, isPresent());
+ assertThat(methodSubject, not(isPresent()));
// TestClass.missingFieldValuePropagation() and TestClass.missingMethodValuePropagation() are
// absent.
@@ -85,11 +85,6 @@
.streamInstructions()
.filter(InstructionSubject::isStaticGet)
.anyMatch(x -> x.getField() == clinitFieldSubject.getField().field));
- assertTrue(
- mainMethodSubject
- .streamInstructions()
- .filter(InstructionSubject::isInvokeStatic)
- .anyMatch(x -> x.getMethod() == methodSubject.getMethod().method));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
index f9d9d21..f743bf2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
@@ -85,12 +85,12 @@
ClassSubject mainClass = codeInspector.clazz(MAIN);
MethodSubject mainMethod = mainClass.mainMethod();
assertThat(mainMethod, isPresent());
- int expectedCount = isR8 ? 3 : (isRelease ? 5 : 7);
+ int expectedCount = isR8 ? 4 : (isRelease ? 5 : 7);
assertEquals(expectedCount, countCall(mainMethod, "String", "valueOf"));
// Due to the different behavior regarding constant canonicalization.
- expectedCount = isR8 ? (parameters.isCfRuntime() ? 2 : 1) : 1;
+ expectedCount = isR8 ? (parameters.isCfRuntime() ? 3 : 1) : 1;
assertEquals(expectedCount, countConstNullNumber(mainMethod));
- expectedCount = isR8 ? (parameters.isCfRuntime() ? 2 : 1) : (isRelease ? 1 : 0);
+ expectedCount = isR8 ? 1 : (isRelease ? 1 : 0);
assertEquals(expectedCount, countNullStringNumber(mainMethod));
MethodSubject hideNPE = mainClass.uniqueMethodWithName("hideNPE");
diff --git a/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java b/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
index 809a521..3c80a6a 100644
--- a/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
@@ -125,8 +125,6 @@
isSameExceptForFileNameAndLineNumber(
createStackTraceBuilder()
.addWithoutFileNameAndLineNumber(
- Result.class, "methodWhichAccessInstanceMethod")
- .addWithoutFileNameAndLineNumber(
A.class, "inlineMethodWhichAccessInstanceMethod")
.addWithoutFileNameAndLineNumber(TestClassForInlineMethod.class, "main")
.build())));
@@ -153,8 +151,6 @@
isSameExceptForFileNameAndLineNumber(
createStackTraceBuilder()
.addWithoutFileNameAndLineNumber(
- Result.class, "methodWhichAccessInstanceField")
- .addWithoutFileNameAndLineNumber(
A.class, "inlineMethodWhichAccessInstanceField")
.addWithoutFileNameAndLineNumber(TestClassForInlineField.class, "main")
.build())));
@@ -183,8 +179,6 @@
isSameExceptForFileNameAndLineNumber(
createStackTraceBuilder()
.addWithoutFileNameAndLineNumber(
- Result.class, "methodWhichAccessStaticField")
- .addWithoutFileNameAndLineNumber(
A.class, "inlineMethodWhichAccessStaticField")
.addWithoutFileNameAndLineNumber(
TestClassForInlineStaticField.class, "main")
diff --git a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
index 0212a16..e23b80b 100644
--- a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
@@ -14,6 +14,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -48,7 +49,8 @@
@Parameters(name = "{1}, include WorldGreeter: {0}")
public static List<Object[]> data() {
- return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
public ServiceLoaderTest(boolean includeWorldGreeter, TestParameters parameters) {
@@ -89,7 +91,8 @@
options.enableInliningOfInvokesWithNullableReceivers = false;
})
.enableGraphInspector()
- .setMinApi(parameters.getRuntime())
+ .enableMemberValuePropagationAnnotations()
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput);
@@ -226,6 +229,7 @@
public static class HelloGreeter implements Greeter {
+ @NeverPropagateValue
@Override
public String greeting() {
return "Hello";
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java b/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
index 8cb9074..e47222b 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
@@ -9,18 +9,14 @@
import com.android.tools.r8.ArchiveClassFileProvider;
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.R8Command;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.io.ByteStreams;
import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
import org.junit.Test;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
@@ -40,6 +36,8 @@
}
private static class Inlinee {
+
+ @NeverPropagateValue
public static String foo() {
return "Hello from Inlinee!";
}
@@ -80,7 +78,13 @@
assertEquals(OLD_VERSION, getBaseClassVersion(inputJar));
ProcessResult runInput = run(inputJar);
assertEquals(0, runInput.exitCode);
- Path outputJar = runR8(inputJar);
+ Path outputJar =
+ testForR8(Backend.CF)
+ .addProgramFiles(inputJar)
+ .addKeepMainRule(Base.class)
+ .enableMemberValuePropagationAnnotations()
+ .compile()
+ .writeToZip();
ProcessResult runOutput = run(outputJar);
assertEquals(runInput.toString(), runOutput.toString());
assertNotEquals(
@@ -141,19 +145,4 @@
private ProcessResult run(Path jar) throws Exception {
return ToolHelper.runJava(jar, Base.class.getName());
}
-
- private Path runR8(Path inputJar) throws Exception {
- List<String> keepRule =
- Collections.singletonList(
- "-keep class " + Base.class.getName() + " { public static void main(...); }");
- Path outputJar = temp.getRoot().toPath().resolve("output.jar");
- ToolHelper.runR8(
- R8Command.builder()
- .addProgramFiles(inputJar)
- .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
- .addProguardConfiguration(keepRule, Origin.unknown())
- .setOutput(outputJar, OutputMode.ClassFile)
- .build());
- return outputJar;
- }
}