Introduce a builder for NonTrivialInstanceInitializerInfo
Change-Id: I84e7cddc9d9985c34b0bc7b94545399a55cf7e91
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index ee49c97..eac8791 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -5,6 +5,16 @@
import static com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query.DIRECTLY;
import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
+import static com.android.tools.r8.ir.code.Opcodes.ARGUMENT;
+import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_NUMBER;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_STRING;
+import static com.android.tools.r8.ir.code.Opcodes.DEX_ITEM_BASED_CONST_STRING;
+import static com.android.tools.r8.ir.code.Opcodes.GOTO;
+import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
+import static com.android.tools.r8.ir.code.Opcodes.RETURN;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -392,79 +402,78 @@
// Defining a finalize method can observe the side-effect of Object.<init> GC registration.
return null;
}
+ NonTrivialInstanceInitializerInfo.Builder builder = NonTrivialInstanceInitializerInfo.builder();
Value receiver = code.getThis();
- for (Instruction insn : code.instructions()) {
- if (insn.isReturn()) {
- continue;
- }
+ for (Instruction instruction : code.instructions()) {
+ switch (instruction.opcode()) {
+ case ARGUMENT:
+ case ASSUME:
+ case CONST_NUMBER:
+ case RETURN:
+ break;
- if (insn.isAssume()) {
- continue;
- }
-
- if (insn.isArgument()) {
- continue;
- }
-
- if (insn.isConstInstruction()) {
- if (insn.instructionInstanceCanThrow()) {
- return null;
- } else {
- continue;
- }
- }
-
- if (insn.isInvokeDirect()) {
- InvokeDirect invokedDirect = insn.asInvokeDirect();
- DexMethod invokedMethod = invokedDirect.getInvokedMethod();
- if (!dexItemFactory.isConstructor(invokedMethod)) {
- return null;
- }
- if (invokedMethod.holder != clazz.superType) {
- return null;
- }
- // java.lang.Enum.<init>() and java.lang.Object.<init>() are considered trivial.
- if (invokedMethod == dexItemFactory.enumMethods.constructor
- || invokedMethod == dexItemFactory.objectMethods.constructor) {
- continue;
- }
- DexEncodedMethod callTarget = appView.definitionFor(invokedMethod);
- if (callTarget == null
- || callTarget.getOptimizationInfo().getInstanceInitializerInfo().isDefaultInfo()
- || invokedDirect.getReceiver() != receiver) {
- return null;
- }
- for (Value value : invokedDirect.inValues()) {
- if (value != receiver && !(value.isConstant() || value.isArgument())) {
+ case CONST_CLASS:
+ case CONST_STRING:
+ case DEX_ITEM_BASED_CONST_STRING:
+ if (instruction.instructionMayTriggerMethodInvocation(appView, clazz.type)) {
return null;
}
- }
- continue;
- }
+ break;
- if (insn.isInstancePut()) {
- InstancePut instancePut = insn.asInstancePut();
- DexEncodedField field = appView.appInfo().resolveFieldOn(clazz, instancePut.getField());
- if (field == null
- || instancePut.object() != receiver
- || (instancePut.value() != receiver && !instancePut.value().isArgument())) {
+ case GOTO:
+ // Trivial goto to the next block.
+ if (!instruction.asGoto().isTrivialGotoToTheNextBlock(code)) {
+ return null;
+ }
+ break;
+
+ case INSTANCE_PUT:
+ {
+ InstancePut instancePut = instruction.asInstancePut();
+ DexEncodedField field = appView.appInfo().resolveFieldOn(clazz, instancePut.getField());
+ if (field == null
+ || instancePut.object() != receiver
+ || (instancePut.value() != receiver && !instancePut.value().isArgument())) {
+ return null;
+ }
+ }
+ break;
+
+ case INVOKE_DIRECT:
+ {
+ InvokeDirect invoke = instruction.asInvokeDirect();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ if (!dexItemFactory.isConstructor(invokedMethod)) {
+ return null;
+ }
+ if (invokedMethod.holder != clazz.superType) {
+ return null;
+ }
+ // java.lang.Enum.<init>() and java.lang.Object.<init>() are considered trivial.
+ if (invokedMethod == dexItemFactory.enumMethods.constructor
+ || invokedMethod == dexItemFactory.objectMethods.constructor) {
+ break;
+ }
+ DexEncodedMethod singleTarget = appView.definitionFor(invokedMethod);
+ if (singleTarget == null
+ || singleTarget.getOptimizationInfo().getInstanceInitializerInfo().isDefaultInfo()
+ || invoke.getReceiver() != receiver) {
+ return null;
+ }
+ for (Value value : invoke.inValues()) {
+ if (value != receiver && !(value.isConstant() || value.isArgument())) {
+ return null;
+ }
+ }
+ }
+ break;
+
+ default:
+ // Other instructions make the instance initializer not eligible.
return null;
- }
- continue;
}
-
- if (insn.isGoto()) {
- // Trivial goto to the next block.
- if (insn.asGoto().isTrivialGotoToTheNextBlock(code)) {
- continue;
- }
- return null;
- }
-
- // Other instructions make the instance initializer not eligible.
- return null;
}
- return NonTrivialInstanceInitializerInfo.INSTANCE;
+ return builder.build();
}
private void identifyInvokeSemanticsForInlining(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
index fa41621..7802e12 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
@@ -4,33 +4,108 @@
package com.android.tools.r8.ir.optimize.info.initializer;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
public final class NonTrivialInstanceInitializerInfo extends InstanceInitializerInfo {
- public static final NonTrivialInstanceInitializerInfo INSTANCE =
- new NonTrivialInstanceInitializerInfo();
+ private static final int INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT = 1 << 0;
+ private static final int NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS = 1 << 1;
+ private static final int RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN = 1 << 2;
- private NonTrivialInstanceInitializerInfo() {}
+ private final int data;
+ private final AbstractFieldSet readSet;
+
+ private NonTrivialInstanceInitializerInfo(int data, AbstractFieldSet readSet) {
+ assert verifyNoUnknownBits(data);
+ this.data = data;
+ this.readSet = readSet;
+ }
+
+ private static boolean verifyNoUnknownBits(int data) {
+ int knownBits =
+ INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT
+ | NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS
+ | RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN;
+ assert (data & ~knownBits) == 0;
+ return true;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
@Override
public AbstractFieldSet readSet() {
- return EmptyFieldSet.getInstance();
+ return readSet;
}
@Override
public boolean instanceFieldInitializationMayDependOnEnvironment() {
- return false;
+ return (data & INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT) == 0;
}
@Override
public boolean mayHaveOtherSideEffectsThanInstanceFieldAssignments() {
- return false;
+ return (data & NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS) == 0;
}
@Override
public boolean receiverNeverEscapesOutsideConstructorChain() {
- return true;
+ return (data & RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN) != 0;
+ }
+
+ public static class Builder {
+
+ private int data =
+ INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT
+ | NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS
+ | RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN;
+ private AbstractFieldSet readSet = EmptyFieldSet.getInstance();
+
+ private boolean isTrivial() {
+ return data == 0 && readSet.isTop();
+ }
+
+ public Builder markFieldAsRead(DexEncodedField field) {
+ if (readSet.isKnownFieldSet()) {
+ if (readSet.isBottom()) {
+ readSet = new ConcreteMutableFieldSet(field);
+ } else {
+ readSet.asConcreteFieldSet().add(field);
+ }
+ }
+ assert readSet.contains(field);
+ return this;
+ }
+
+ public Builder markAllFieldsAsRead() {
+ readSet = UnknownFieldSet.getInstance();
+ return this;
+ }
+
+ public Builder setInstanceFieldInitializationMayDependOnEnvironment() {
+ data &= ~INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT;
+ return this;
+ }
+
+ public Builder setMayHaveOtherSideEffectsThanInstanceFieldAssignments() {
+ data &= ~NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS;
+ return this;
+ }
+
+ public Builder setReceiverMayEscapeOutsideConstructorChain() {
+ data &= ~RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN;
+ return this;
+ }
+
+ public InstanceInitializerInfo build() {
+ return isTrivial()
+ ? DefaultInstanceInitializerInfo.getInstance()
+ : new NonTrivialInstanceInitializerInfo(data, readSet);
+ }
}
}