Always allow force inlining of instance initializers
Change-Id: I969bd03823c6b1a82575d010e6e640748b222e78
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 2692240..61b64d5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -6,12 +6,17 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.InvokeStatic;
@@ -449,6 +454,86 @@
}
@Override
+ public boolean canInlineInstanceInitializer(
+ InvokeDirect invoke,
+ IRCode inlinee,
+ WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
+ // In the Java VM Specification section "4.10.2.4. Instance Initialization Methods and
+ // Newly Created Objects" it says:
+ //
+ // Before that method invokes another instance initialization method of myClass or its direct
+ // superclass on this, the only operation the method can perform on this is assigning fields
+ // declared within myClass.
+
+ // Allow inlining a constructor into a constructor of the same class, as the constructor code
+ // is expected to adhere to the VM specification.
+ DexType callerMethodHolder = method.method.holder;
+ boolean callerMethodIsConstructor = method.isInstanceInitializer();
+ DexType calleeMethodHolder = invoke.asInvokeMethod().getInvokedMethod().holder;
+ // Calling a constructor on the same class from a constructor can always be inlined.
+ if (callerMethodIsConstructor && callerMethodHolder == calleeMethodHolder) {
+ return true;
+ }
+
+ // We cannot invoke <init> on other values than |this| on Dalvik 4.4.4. Compute whether
+ // the receiver to the call was the this value at the call-site.
+ boolean receiverOfInnerCallIsThisOfOuter = invoke.getReceiver().isThis();
+
+ // Don't allow inlining a constructor into a non-constructor if the first use of the
+ // un-initialized object is not an argument of an invoke of <init>.
+ // Also, we cannot inline a constructor if it initializes final fields, as such is only allowed
+ // from within a constructor of the corresponding class.
+ // Lastly, we can only inline a constructor, if its own <init> call is on the method's class. If
+ // we inline into a constructor, calls to super.<init> are also OK if the receiver of the
+ // super.<init> call is the this argument.
+ InstructionIterator iterator = inlinee.instructionIterator();
+ Instruction instruction = iterator.next();
+ // A constructor always has the un-initialized object as the first argument.
+ assert instruction.isArgument();
+ Value unInitializedObject = instruction.outValue();
+ boolean seenSuperInvoke = false;
+ while (iterator.hasNext()) {
+ instruction = iterator.next();
+ if (instruction.inValues().contains(unInitializedObject)) {
+ if (instruction.isInvokeDirect() && !seenSuperInvoke) {
+ DexMethod target = instruction.asInvokeDirect().getInvokedMethod();
+ seenSuperInvoke = appView.dexItemFactory().isConstructor(target);
+ boolean callOnConstructorThatCallsConstructorSameClass =
+ calleeMethodHolder == target.holder;
+ boolean callOnSupertypeOfThisInConstructor =
+ appView.appInfo().isDirectSubtype(callerMethodHolder, target.holder)
+ && instruction.asInvokeDirect().getReceiver() == unInitializedObject
+ && receiverOfInnerCallIsThisOfOuter
+ && callerMethodIsConstructor;
+ if (seenSuperInvoke
+ // Calls to init on same class than the called constructor are OK.
+ && !callOnConstructorThatCallsConstructorSameClass
+ // If we are inlining into a constructor, calls to superclass init are only OK on the
+ // |this| value in the outer context.
+ && !callOnSupertypeOfThisInConstructor) {
+ return false;
+ }
+ }
+ if (!seenSuperInvoke) {
+ return false;
+ }
+ }
+ if (instruction.isInstancePut()) {
+ // Fields may not be initialized outside of a constructor.
+ if (!callerMethodIsConstructor) {
+ return false;
+ }
+ DexField field = instruction.asInstancePut().getField();
+ DexEncodedField target = appView.appInfo().lookupInstanceTarget(field.holder, field);
+ if (target != null && target.accessFlags.isFinal()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
public boolean stillHasBudget(
InlineAction action, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
return instructionAllowance > 0 || action.reason.mustBeInlined();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
index 7767bed..8849066 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.InvokeStatic;
@@ -80,6 +81,12 @@
IRCode inlinee, ListIterator<BasicBlock> blockIterator, BasicBlock block) {}
@Override
+ public boolean canInlineInstanceInitializer(
+ InvokeDirect invoke, IRCode code, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
+ return true;
+ }
+
+ @Override
public boolean stillHasBudget(
InlineAction action, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
return true; // Unlimited allowance.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 73dbddb..3ed156a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -7,9 +7,7 @@
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -24,7 +22,6 @@
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Throw;
@@ -651,86 +648,6 @@
return numberOfInstructions;
}
- private boolean canInlineInstanceInitializer(
- DexEncodedMethod method,
- InvokeDirect invoke,
- IRCode code,
- WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
- // In the Java VM Specification section "4.10.2.4. Instance Initialization Methods and
- // Newly Created Objects" it says:
- //
- // Before that method invokes another instance initialization method of myClass or its direct
- // superclass on this, the only operation the method can perform on this is assigning fields
- // declared within myClass.
-
- // Allow inlining a constructor into a constructor of the same class, as the constructor code
- // is expected to adhere to the VM specification.
- DexType callerMethodHolder = method.method.holder;
- boolean callerMethodIsConstructor = method.isInstanceInitializer();
- DexType calleeMethodHolder = invoke.asInvokeMethod().getInvokedMethod().holder;
- // Calling a constructor on the same class from a constructor can always be inlined.
- if (callerMethodIsConstructor && callerMethodHolder == calleeMethodHolder) {
- return true;
- }
-
- // We cannot invoke <init> on other values than |this| on Dalvik 4.4.4. Compute whether
- // the receiver to the call was the this value at the call-site.
- boolean receiverOfInnerCallIsThisOfOuter = invoke.getReceiver().isThis();
-
- // Don't allow inlining a constructor into a non-constructor if the first use of the
- // un-initialized object is not an argument of an invoke of <init>.
- // Also, we cannot inline a constructor if it initializes final fields, as such is only allowed
- // from within a constructor of the corresponding class.
- // Lastly, we can only inline a constructor, if its own <init> call is on the method's class. If
- // we inline into a constructor, calls to super.<init> are also OK if the receiver of the
- // super.<init> call is the this argument.
- InstructionIterator iterator = code.instructionIterator();
- Instruction instruction = iterator.next();
- // A constructor always has the un-initialized object as the first argument.
- assert instruction.isArgument();
- Value unInitializedObject = instruction.outValue();
- boolean seenSuperInvoke = false;
- while (iterator.hasNext()) {
- instruction = iterator.next();
- if (instruction.inValues().contains(unInitializedObject)) {
- if (instruction.isInvokeDirect() && !seenSuperInvoke) {
- DexMethod target = instruction.asInvokeDirect().getInvokedMethod();
- seenSuperInvoke = appView.dexItemFactory().isConstructor(target);
- boolean callOnConstructorThatCallsConstructorSameClass =
- calleeMethodHolder == target.holder;
- boolean callOnSupertypeOfThisInConstructor =
- appView.appInfo().isDirectSubtype(callerMethodHolder, target.holder)
- && instruction.asInvokeDirect().getReceiver() == unInitializedObject
- && receiverOfInnerCallIsThisOfOuter
- && callerMethodIsConstructor;
- if (seenSuperInvoke
- // Calls to init on same class than the called constructor are OK.
- && !callOnConstructorThatCallsConstructorSameClass
- // If we are inlining into a constructor, calls to superclass init are only OK on the
- // |this| value in the outer context.
- && !callOnSupertypeOfThisInConstructor) {
- return false;
- }
- }
- if (!seenSuperInvoke) {
- return false;
- }
- }
- if (instruction.isInstancePut()) {
- // Fields may not be initialized outside of a constructor.
- if (!callerMethodIsConstructor) {
- return false;
- }
- DexField field = instruction.asInstancePut().getField();
- DexEncodedField target = appView.appInfo().lookupInstanceTarget(field.holder, field);
- if (target != null && target.accessFlags.isFinal()) {
- return false;
- }
- }
- }
- return true;
- }
-
public static class InliningInfo {
public final DexEncodedMethod target;
public final DexType receiverType; // null, if unknown
@@ -852,8 +769,8 @@
// Make sure constructor inlining is legal.
assert !singleTarget.isClassInitializer();
if (singleTarget.isInstanceInitializer()
- && !canInlineInstanceInitializer(
- context, invoke.asInvokeDirect(), inlinee.code, whyAreYouNotInliningReporter)) {
+ && !strategy.canInlineInstanceInitializer(
+ invoke.asInvokeDirect(), inlinee.code, whyAreYouNotInliningReporter)) {
// TODO(b/142108662): Enable assertion once reporting is complete.
// assert whyAreYouNotInliningReporter.verifyReasonHasBeenReported();
continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
index df5352d..5d7bdc2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.Inliner.InlineeWithReason;
@@ -17,6 +18,9 @@
interface InliningStrategy {
+ boolean canInlineInstanceInitializer(
+ InvokeDirect invoke, IRCode code, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
+
/** Return true if there is still budget for inlining into this method. */
boolean stillHasBudget(
InlineAction action, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);