Optimize calls to dynamicMethod() with known MethodToInvoke after class inlining
Change-Id: If6421d5818d1882d00361b3c0d2696b8c4549574
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index 4660f87..552fa07 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.analysis.proto;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -11,8 +12,23 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.code.CheckCast;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.CallGraph.Node;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.CodeRewriter;
+import com.android.tools.r8.ir.optimize.Inliner;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.inliner.FixedInliningReasonStrategy;
import com.android.tools.r8.utils.PredicateSet;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -23,9 +39,12 @@
// references a dead proto builder.
public class GeneratedMessageLiteBuilderShrinker {
+ private final AppView<? extends AppInfoWithSubtyping> appView;
private final ProtoReferences references;
- GeneratedMessageLiteBuilderShrinker(ProtoReferences references) {
+ GeneratedMessageLiteBuilderShrinker(
+ AppView<? extends AppInfoWithSubtyping> appView, ProtoReferences references) {
+ this.appView = appView;
this.references = references;
}
@@ -64,6 +83,98 @@
}
}
+ public void inlineCallsToDynamicMethod(
+ DexEncodedMethod method,
+ IRCode code,
+ CodeRewriter codeRewriter,
+ OptimizationFeedback feedback,
+ MethodProcessor methodProcessor,
+ Inliner inliner) {
+ if (method.method.toSourceString().contains("proto2.BuilderWithReusedSettersTestClass")) {
+ System.out.println();
+ }
+ strengthenCheckCastInstructions(code);
+
+ ProtoInliningReasonStrategy inliningReasonStrategy =
+ new ProtoInliningReasonStrategy(appView, new FixedInliningReasonStrategy(Reason.NEVER));
+ inliner.performInlining(method, code, feedback, methodProcessor, inliningReasonStrategy);
+
+ // Run the enum optimization to optimize all Enum.ordinal() invocations. This is required to
+ // get rid of the enum switch in dynamicMethod().
+ if (appView.options().enableEnumValueOptimization) {
+ codeRewriter.rewriteConstantEnumMethodCalls(code);
+ }
+ }
+
+ /**
+ * This method tries to strengthen the type of check-cast instructions that cast a value to
+ * GeneratedMessageLite.
+ *
+ * <p>New proto messages are created by calling dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE)
+ * and casting the result to GeneratedMessageLite.
+ *
+ * <p>If we encounter the following pattern, then we cannot inline the second call to
+ * dynamicMethod, because we don't have a precise receiver type.
+ *
+ * <pre>
+ * GeneratedMessageLite msg =
+ * (GeneratedMessageLite)
+ * Message.DEFAULT_INSTANCE.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE);
+ * GeneratedMessageLite msg2 =
+ * (GeneratedMessageLite) msg.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE);
+ * </pre>
+ *
+ * <p>This method therefore optimizes the code above into:
+ *
+ * <pre>
+ * Message msg =
+ * (Message) Message.DEFAULT_INSTANCE.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE);
+ * Message msg2 = (Message) msg.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE);
+ * </pre>
+ *
+ * <p>This is assuming that calling dynamicMethod() on a proto message with
+ * MethodToInvoke.NEW_MUTABLE_INSTANCE will create an instance of the enclosing class.
+ */
+ private void strengthenCheckCastInstructions(IRCode code) {
+ Set<Value> affectedValues = Sets.newIdentityHashSet();
+ InstructionListIterator instructionIterator = code.instructionListIterator();
+ CheckCast checkCast;
+ while ((checkCast = instructionIterator.nextUntil(Instruction::isCheckCast)) != null) {
+ if (checkCast.getType() != references.generatedMessageLiteType) {
+ continue;
+ }
+ Value root = checkCast.object().getAliasedValue();
+ if (root.isPhi() || !root.definition.isInvokeVirtual()) {
+ continue;
+ }
+ InvokeVirtual invoke = root.definition.asInvokeVirtual();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ if (!references.isDynamicMethod(invokedMethod)
+ && !references.isDynamicMethodBridge(invokedMethod)) {
+ continue;
+ }
+ assert invokedMethod.proto.parameters.values[0] == references.methodToInvokeType;
+ Value methodToInvokeValue = invoke.arguments().get(1);
+ if (!references.methodToInvokeMembers.isNewMutableInstanceEnum(methodToInvokeValue)) {
+ continue;
+ }
+ ClassTypeLatticeElement receiverType =
+ invoke.getReceiver().getDynamicUpperBoundType(appView).asClassTypeLatticeElement();
+ if (receiverType != null) {
+ AppInfoWithClassHierarchy appInfo = appView.appInfo();
+ DexType rawReceiverType = receiverType.getClassType();
+ if (appInfo.isStrictSubtypeOf(rawReceiverType, references.generatedMessageLiteType)) {
+ Value dest = code.createValue(receiverType.asMaybeNull(), checkCast.getLocalInfo());
+ CheckCast replacement = new CheckCast(dest, checkCast.object(), rawReceiverType);
+ instructionIterator.replaceCurrentInstruction(replacement, affectedValues);
+ }
+ }
+ }
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
+ }
+
private static class RootSetExtension {
private final AppView<? extends AppInfoWithSubtyping> appView;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java
index 4d04a73..56fd8e9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java
@@ -32,7 +32,7 @@
@Override
public Reason computeInliningReason(InvokeMethod invoke, DexEncodedMethod target) {
- return references.isDynamicMethod(target)
+ return references.isDynamicMethod(target) || references.isDynamicMethodBridge(target)
? computeInliningReasonForDynamicMethod(invoke)
: parent.computeInliningReason(invoke, target);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
index c90eaf2..aaf3483 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Value;
public class ProtoReferences {
@@ -195,6 +196,17 @@
methodToInvokeType, methodToInvokeType, "SET_MEMOIZED_IS_INITIALIZED");
}
+ public boolean isNewMutableInstanceEnum(DexField field) {
+ return field == newMutableInstanceField;
+ }
+
+ public boolean isNewMutableInstanceEnum(Value value) {
+ Value root = value.getAliasedValue();
+ return !root.isPhi()
+ && root.definition.isStaticGet()
+ && isNewMutableInstanceEnum(root.definition.asStaticGet().getField());
+ }
+
public boolean isMethodToInvokeWithSimpleBody(DexField field) {
return field == getDefaultInstanceField
|| field == getMemoizedIsInitializedField
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
index d7ba0cc..b7b8ef1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
@@ -32,7 +32,7 @@
: null;
this.generatedMessageLiteBuilderShrinker =
appView.options().protoShrinking().enableGeneratedMessageLiteBuilderShrinking
- ? new GeneratedMessageLiteBuilderShrinker(references)
+ ? new GeneratedMessageLiteBuilderShrinker(appView, references)
: null;
this.references = references;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
index 91086e1..b888ae5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
@@ -83,6 +83,10 @@
return getOrCreateVariant(Nullability.definitelyNotNull());
}
+ public TypeLatticeElement asMaybeNull() {
+ return getOrCreateVariant(Nullability.maybeNull());
+ }
+
@Override
public boolean isReference() {
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 085c936..301be93 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -181,7 +181,7 @@
}
@Override
- public void replaceCurrentInstruction(Instruction newInstruction) {
+ public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) {
if (current == null) {
throw new IllegalStateException();
}
@@ -190,6 +190,9 @@
}
if (current.outValue() != null && current.outValue().isUsed()) {
assert newInstruction.outValue() != null;
+ if (affectedValues != null) {
+ current.outValue().addAffectedValuesTo(affectedValues);
+ }
current.outValue().replaceUsers(newInstruction.outValue());
}
current.moveDebugValues(newInstruction);
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index fdea0f6..f070e0f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -147,8 +147,8 @@
}
@Override
- public void replaceCurrentInstruction(Instruction newInstruction) {
- instructionIterator.replaceCurrentInstruction(newInstruction);
+ public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) {
+ instructionIterator.replaceCurrentInstruction(newInstruction, affectedValues);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index 315568e..a8b539e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -17,6 +17,11 @@
public interface InstructionListIterator
extends InstructionIterator, ListIterator<Instruction>, PreviousUntilIterator<Instruction> {
+ /** See {@link #replaceCurrentInstruction(Instruction, Set)}. */
+ default void replaceCurrentInstruction(Instruction newInstruction) {
+ replaceCurrentInstruction(newInstruction, null);
+ }
+
/**
* Replace the current instruction (aka the {@link Instruction} returned by the previous call to
* {@link #next} with the passed in <code>newInstruction</code>.
@@ -31,8 +36,9 @@
* <p>The debug information of the current instruction will be attached to the new instruction.
*
* @param newInstruction the instruction to insert instead of the current.
+ * @param affectedValues if non-null, all users of the out value will be added to this set.
*/
- void replaceCurrentInstruction(Instruction newInstruction);
+ void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues);
// Do not show a deprecation warning for InstructionListIterator.remove().
@SuppressWarnings("deprecation")
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index 3a098ca..6de9e58 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -36,8 +36,8 @@
}
@Override
- public void replaceCurrentInstruction(Instruction newInstruction) {
- currentBlockIterator.replaceCurrentInstruction(newInstruction);
+ public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) {
+ currentBlockIterator.replaceCurrentInstruction(newInstruction, affectedValues);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NextUntilIterator.java b/src/main/java/com/android/tools/r8/ir/code/NextUntilIterator.java
index d15376e..555db34 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NextUntilIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NextUntilIterator.java
@@ -11,14 +11,14 @@
/**
* Continue to call {@link #next} while {@code predicate} tests {@code false}.
*
- * @returns the item that matched the predicate or {@code null} if all items fail
- * the predicate test
+ * @returns the item that matched the predicate or {@code null} if all items fail the predicate
+ * test
*/
- default T nextUntil(Predicate<T> predicate) {
+ default <S extends T> S nextUntil(Predicate<T> predicate) {
while (hasNext()) {
T item = next();
if (predicate.test(item)) {
- return item;
+ return (S) item;
}
}
return null;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 3079026..bba9148 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -45,6 +45,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.function.Predicate;
public class Value implements Comparable<Value> {
@@ -642,13 +643,21 @@
// Returns the set of Value that are affected if the current value's type lattice is updated.
public Set<Value> affectedValues() {
ImmutableSet.Builder<Value> affectedValues = ImmutableSet.builder();
+ forEachAffectedValue(affectedValues::add);
+ return affectedValues.build();
+ }
+
+ public void addAffectedValuesTo(Set<Value> affectedValues) {
+ forEachAffectedValue(affectedValues::add);
+ }
+
+ public void forEachAffectedValue(Consumer<Value> consumer) {
for (Instruction user : uniqueUsers()) {
- if (user.outValue() != null) {
- affectedValues.add(user.outValue());
+ if (user.hasOutValue()) {
+ consumer.accept(user.outValue());
}
}
- affectedValues.addAll(uniquePhiUsers());
- return affectedValues.build();
+ uniquePhiUsers().forEach(consumer::accept);
}
public void replaceUsers(Value newValue) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 3547e12..e501c31 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1317,6 +1317,7 @@
stringOptimizer,
method,
code,
+ feedback,
methodProcessor,
inliner,
Suppliers.memoize(
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 c7e511a..145bb62 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
@@ -16,7 +16,6 @@
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.analysis.proto.ProtoInliningReasonStrategy;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstancePut;
@@ -32,7 +31,6 @@
import com.android.tools.r8.ir.optimize.Inliner.InlineeWithReason;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.ir.optimize.inliner.DefaultInliningReasonStrategy;
import com.android.tools.r8.ir.optimize.inliner.InliningReasonStrategy;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.logging.Log;
@@ -64,6 +62,7 @@
DefaultInliningOracle(
AppView<AppInfoWithLiveness> appView,
Inliner inliner,
+ InliningReasonStrategy inliningReasonStrategy,
DexEncodedMethod method,
IRCode code,
MethodProcessor methodProcessor,
@@ -71,20 +70,13 @@
int inliningInstructionAllowance) {
this.appView = appView;
this.inliner = inliner;
+ this.reasonStrategy = inliningReasonStrategy;
this.method = method;
this.code = code;
this.methodProcessor = methodProcessor;
this.isProcessedConcurrently = methodProcessor::isProcessedConcurrently;
this.inliningInstructionLimit = inliningInstructionLimit;
this.instructionAllowance = inliningInstructionAllowance;
-
- DefaultInliningReasonStrategy defaultInliningReasonStrategy =
- new DefaultInliningReasonStrategy(
- appView, methodProcessor.getCallSiteInformation(), inliner);
- this.reasonStrategy =
- appView.withGeneratedMessageLiteShrinker(
- ignore -> new ProtoInliningReasonStrategy(appView, defaultInliningReasonStrategy),
- defaultInliningReasonStrategy);
}
@Override
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 ed35f73..eeff808 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
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
+import com.android.tools.r8.ir.analysis.proto.ProtoInliningReasonStrategy;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -44,7 +45,9 @@
import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
+import com.android.tools.r8.ir.optimize.inliner.DefaultInliningReasonStrategy;
import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
+import com.android.tools.r8.ir.optimize.inliner.InliningReasonStrategy;
import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
@@ -830,12 +833,25 @@
performInliningImpl(
oracle, oracle, method, code, OptimizationFeedbackIgnore.getInstance(), inliningIRProvider);
}
-
public void performInlining(
DexEncodedMethod method,
IRCode code,
OptimizationFeedback feedback,
MethodProcessor methodProcessor) {
+ performInlining(
+ method,
+ code,
+ feedback,
+ methodProcessor,
+ createDefaultInliningReasonStrategy(methodProcessor));
+ }
+
+ public void performInlining(
+ DexEncodedMethod method,
+ IRCode code,
+ OptimizationFeedback feedback,
+ MethodProcessor methodProcessor,
+ InliningReasonStrategy inliningReasonStrategy) {
InternalOptions options = appView.options();
DefaultInliningOracle oracle =
createDefaultOracle(
@@ -843,21 +859,48 @@
code,
methodProcessor,
options.inliningInstructionLimit,
- options.inliningInstructionAllowance - numberOfInstructions(code));
+ options.inliningInstructionAllowance - numberOfInstructions(code),
+ inliningReasonStrategy);
InliningIRProvider inliningIRProvider = new InliningIRProvider(appView, method, code);
assert inliningIRProvider.verifyIRCacheIsEmpty();
performInliningImpl(oracle, oracle, method, code, feedback, inliningIRProvider);
}
+ public InliningReasonStrategy createDefaultInliningReasonStrategy(
+ MethodProcessor methodProcessor) {
+ DefaultInliningReasonStrategy defaultInliningReasonStrategy =
+ new DefaultInliningReasonStrategy(appView, methodProcessor.getCallSiteInformation(), this);
+ return appView.withGeneratedMessageLiteShrinker(
+ ignore -> new ProtoInliningReasonStrategy(appView, defaultInliningReasonStrategy),
+ defaultInliningReasonStrategy);
+ }
+
public DefaultInliningOracle createDefaultOracle(
DexEncodedMethod method,
IRCode code,
MethodProcessor methodProcessor,
int inliningInstructionLimit,
int inliningInstructionAllowance) {
+ return createDefaultOracle(
+ method,
+ code,
+ methodProcessor,
+ inliningInstructionLimit,
+ inliningInstructionAllowance,
+ createDefaultInliningReasonStrategy(methodProcessor));
+ }
+
+ public DefaultInliningOracle createDefaultOracle(
+ DexEncodedMethod method,
+ IRCode code,
+ MethodProcessor methodProcessor,
+ int inliningInstructionLimit,
+ int inliningInstructionAllowance,
+ InliningReasonStrategy inliningReasonStrategy) {
return new DefaultInliningOracle(
appView,
this,
+ inliningReasonStrategy,
method,
code,
methodProcessor,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index c71006f..c4a761b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -4,10 +4,14 @@
package com.android.tools.r8.ir.optimize.classinliner;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -18,6 +22,7 @@
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.InliningOracle;
import com.android.tools.r8.ir.optimize.classinliner.InlineCandidateProcessor.IllegalClassInlinerStateException;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
import com.android.tools.r8.ir.optimize.string.StringOptimizer;
import com.android.tools.r8.logging.Log;
@@ -161,6 +166,7 @@
StringOptimizer stringOptimizer,
DexEncodedMethod method,
IRCode code,
+ OptimizationFeedback feedback,
MethodProcessor methodProcessor,
Inliner inliner,
Supplier<InliningOracle> defaultOracle) {
@@ -174,7 +180,7 @@
// We loop inlining iterations until there was no inlining, but still use same set
// of roots to avoid infinite inlining. Looping makes possible for some roots to
// become eligible after other roots are inlined.
-
+ boolean anyInlinedGeneratedMessageLiteBuilders = false;
boolean anyInlinedMethods = false;
boolean repeat;
do {
@@ -233,6 +239,15 @@
continue;
}
+ if (appView.protoShrinker() != null && root.isNewInstance()) {
+ DexType instantiatedType = root.asNewInstance().clazz;
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(instantiatedType));
+ if (clazz != null
+ && appView.protoShrinker().references.isGeneratedMessageLiteBuilder(clazz)) {
+ anyInlinedGeneratedMessageLiteBuilders = true;
+ }
+ }
+
// Inline the class instance.
try {
anyInlinedMethods |= processor.processInlining(code, defaultOracle, inliningIRProvider);
@@ -262,6 +277,14 @@
}
} while (repeat);
+ if (anyInlinedGeneratedMessageLiteBuilders) {
+ // Inline all calls to dynamicMethod() where the given MethodToInvoke is considered simple.
+ appView.withGeneratedMessageLiteBuilderShrinker(
+ shrinker ->
+ shrinker.inlineCallsToDynamicMethod(
+ method, code, codeRewriter, feedback, methodProcessor, inliner));
+ }
+
if (anyInlinedMethods) {
// If a method was inlined we may be able to remove check-cast instructions because we may
// have more information about the types of the arguments at the call site. This is
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java
new file mode 100644
index 0000000..90fc4f0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2020, 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.inliner;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
+
+public class FixedInliningReasonStrategy implements InliningReasonStrategy {
+
+ private final Reason reason;
+
+ public FixedInliningReasonStrategy(Reason reason) {
+ this.reason = reason;
+ }
+
+ @Override
+ public Reason computeInliningReason(InvokeMethod invoke, DexEncodedMethod target) {
+ return reason;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index a717189..b17cae5 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
@@ -14,6 +15,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
@@ -179,13 +181,14 @@
for (String main : mains) {
MethodSubject mainMethodSubject = outputInspector.clazz(main).mainMethod();
assertThat(mainMethodSubject, isPresent());
- // TODO(christofferqa): Enable assertion.
- // assertTrue(
- // mainMethodSubject
- // .streamInstructions()
- // .filter(InstructionSubject::isStaticGet)
- // .map(instruction -> instruction.getField().type)
- // .noneMatch(methodToInvokeType::equals));
+ assertTrue(
+ main,
+ mainMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticGet)
+ .map(instruction -> instruction.getField().type)
+ .noneMatch(methodToInvokeType::equals));
+ assertTrue(mainMethodSubject.streamInstructions().noneMatch(InstructionSubject::isSwitch));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index 555b94c..a449c5d 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -44,7 +44,7 @@
}
@Override
- public void replaceCurrentInstruction(Instruction newInstruction) {
+ public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) {
throw new Unimplemented();
}