Only run InstanceInitializerOutliner in primary conversion passes
This converts the InstanceInitializerOutliner to a CodeRewriterPass and
prevents it from running except when the method processor is the
D8MethodProcessor or R8's PrimaryMethodProcessor.
This allows IR processing using a custom MethodProcessor, which is
needed to apply the LensCodeRewriter after the final round of vertical
class merging.
Bug: b/315445393
Change-Id: I75a0368ac398f6a2023844884e5cc6c3465dfab7
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
index 6b13747..4fc003f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -67,6 +67,11 @@
}
@Override
+ public boolean isD8MethodProcessor() {
+ return true;
+ }
+
+ @Override
public boolean isProcessedConcurrently(ProgramMethod method) {
// In D8 all methods are considered independently compiled.
return true;
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 9a7b1e0..2dc1a3f 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
@@ -85,7 +85,6 @@
import com.android.tools.r8.shaking.KeepMethodInfo;
import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
import com.android.tools.r8.utils.Action;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -129,7 +128,6 @@
protected ServiceLoaderRewriter serviceLoaderRewriter;
protected final EnumUnboxer enumUnboxer;
protected final NumberUnboxer numberUnboxer;
- protected InstanceInitializerOutliner instanceInitializerOutliner;
protected final RemoveVerificationErrorForUnknownReturnedValues
removeVerificationErrorForUnknownReturnedValues;
@@ -218,7 +216,6 @@
this.enumUnboxer = EnumUnboxer.empty();
this.numberUnboxer = NumberUnboxer.empty();
this.assumeInserter = null;
- this.instanceInitializerOutliner = null;
this.removeVerificationErrorForUnknownReturnedValues = null;
return;
}
@@ -230,13 +227,6 @@
options.processCovariantReturnTypeAnnotations
? new CovariantReturnTypeAnnotationTransformer(appView, this)
: null;
- if (appView.options().desugarState.isOn()
- && appView.options().apiModelingOptions().enableOutliningOfMethods
- && appView.options().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L)) {
- this.instanceInitializerOutliner = new InstanceInitializerOutliner(appView);
- } else {
- this.instanceInitializerOutliner = null;
- }
removeVerificationErrorForUnknownReturnedValues =
(appView.options().apiModelingOptions().enableLibraryApiModeling
&& appView.options().canHaveVerifyErrorForUnknownUnusedReturnValue())
@@ -652,12 +642,9 @@
timing.end();
previous = printMethod(code, "IR after enum-switch optimization (SSA)", previous);
- if (instanceInitializerOutliner != null) {
- instanceInitializerOutliner.rewriteInstanceInitializers(
- code, context, methodProcessor, methodProcessingContext);
- assert code.verifyTypes(appView);
- previous = printMethod(code, "IR after instance initializer outlining (SSA)", previous);
- }
+ new InstanceInitializerOutliner(appView)
+ .run(code, methodProcessor, methodProcessingContext, timing);
+ previous = printMethod(code, "IR after instance initializer outlining (SSA)", previous);
// Update the IR code if collected call site optimization info has something useful.
// While aggregation of parameter information at call sites would be more precise than static
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
index a80055a..829c985 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
@@ -17,6 +17,10 @@
return null;
}
+ public boolean isD8MethodProcessor() {
+ return false;
+ }
+
public boolean isPrimaryMethodProcessor() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
index e1a4c6f..625a337 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
@@ -71,10 +71,6 @@
reportNestDesugarDependencies();
clearNestAttributes();
- if (instanceInitializerOutliner != null) {
- processSimpleSynthesizeMethods(
- instanceInitializerOutliner.getSynthesizedMethods(), executorService);
- }
if (assertionErrorTwoArgsConstructorRewriter != null) {
processSimpleSynthesizeMethods(
assertionErrorTwoArgsConstructorRewriter.getSynthesizedMethods(), executorService);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index e87faba..16d82e6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -430,10 +430,6 @@
if (inliner != null) {
inliner.onLastWaveDone(postMethodProcessorBuilder, executorService, timing);
}
- if (instanceInitializerOutliner != null) {
- instanceInitializerOutliner.onLastWaveDone(postMethodProcessorBuilder);
- instanceInitializerOutliner = null;
- }
if (serviceLoaderRewriter != null) {
serviceLoaderRewriter.onLastWaveDone(postMethodProcessorBuilder);
serviceLoaderRewriter = null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java b/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java
index 2a90c2e..a9dfc03 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -16,7 +17,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -29,13 +29,14 @@
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodProcessor;
-import com.android.tools.r8.ir.conversion.PostMethodProcessor;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.conversion.passes.CodeRewriterPass;
+import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
+import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.android.tools.r8.ir.synthetic.NewInstanceSourceCode;
import com.android.tools.r8.shaking.ComputeApiLevelUseRegistry;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.Sets;
-import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -46,32 +47,21 @@
* Unlike the ApiInvokeOutlinerDesugaring that works on CF, this works on IR to properly replace the
* users of the NewInstance call.
*/
-public class InstanceInitializerOutliner {
+public class InstanceInitializerOutliner extends CodeRewriterPass<AppInfo> {
- private final AppView<?> appView;
private final DexItemFactory factory;
- private final List<ProgramMethod> synthesizedMethods = new ArrayList<>();
-
public InstanceInitializerOutliner(AppView<?> appView) {
- this.appView = appView;
+ super(appView);
this.factory = appView.dexItemFactory();
}
- public List<ProgramMethod> getSynthesizedMethods() {
- return synthesizedMethods;
- }
-
- public void rewriteInstanceInitializers(
+ @Override
+ protected CodeRewriterResult rewriteCode(
IRCode code,
- ProgramMethod context,
MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
assert !methodProcessor.isPostMethodProcessor();
- // Do not outline from already synthesized methods.
- if (context.getDefinition().isD8R8Synthesized()) {
- return;
- }
Map<NewInstance, Value> rewrittenNewInstances = new IdentityHashMap<>();
ComputedApiLevel minApiLevel = appView.computedMinApiLevel();
InstructionListIterator iterator = code.instructionListIterator();
@@ -104,7 +94,7 @@
continue;
}
// Check if this is already outlined.
- if (isOutlinedAtSameOrLowerLevel(context.getHolder(), apiReferenceLevel)) {
+ if (isOutlinedAtSameOrLowerLevel(code.context().getHolder(), apiReferenceLevel)) {
continue;
}
DexEncodedMethod synthesizedInstanceInitializer =
@@ -125,7 +115,7 @@
rewrittenNewInstances.put(newInstance, outlinedMethodInvoke.outValue());
}
if (rewrittenNewInstances.isEmpty()) {
- return;
+ return CodeRewriterResult.NO_CHANGE;
}
// Scan over NewInstance calls that needs to be outlined. We insert a call to a synthetic method
// with a NewInstance to preserve class-init semantics.
@@ -173,14 +163,10 @@
// the outline again in R8 - but allow inlining of other calls to min api level methods, we have
// to recompute the api level.
if (appView.enableWholeProgramOptimizations()) {
- recomputeApiLevel(context, code);
+ recomputeApiLevel(code.context(), code);
}
- assert code.isConsistentSSA(appView);
- }
-
- public void onLastWaveDone(PostMethodProcessor.Builder postMethodProcessorBuilder) {
- postMethodProcessorBuilder.addAll(synthesizedMethods, appView.graphLens());
+ return CodeRewriterResult.HAS_CHANGED;
}
private boolean canSkipClInit(
@@ -249,9 +235,7 @@
methodProcessor
.getEventConsumer()
.acceptInstanceInitializerOutline(method, methodProcessingContext.getMethodContext());
- synchronized (synthesizedMethods) {
- synthesizedMethods.add(method);
- }
+ methodProcessor.scheduleDesugaredMethodForProcessing(method);
return method.getDefinition();
}
@@ -271,31 +255,56 @@
kinds -> kinds.API_MODEL_OUTLINE,
methodProcessingContext.createUniqueContext(),
appView,
- builder ->
- builder
- .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setProto(proto)
- .setApiLevelForDefinition(appView.computedMinApiLevel())
- .setApiLevelForCode(computedApiLevel)
- .setCode(
- m ->
- ForwardMethodBuilder.builder(appView.dexItemFactory())
- .setConstructorTargetWithNewInstance(targetMethod)
- .setStaticSource(m)
- .build()));
+ builder -> {
+ DynamicType exactDynamicReturnType =
+ DynamicType.createExact(
+ targetMethod
+ .getHolderType()
+ .toTypeElement(appView, Nullability.definitelyNotNull())
+ .asClassType());
+ builder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setProto(proto)
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(computedApiLevel)
+ .setCode(
+ m ->
+ ForwardMethodBuilder.builder(appView.dexItemFactory())
+ .setConstructorTargetWithNewInstance(targetMethod)
+ .setStaticSource(m)
+ .build())
+ .setOptimizationInfo(
+ DefaultMethodOptimizationInfo.getInstance()
+ .toMutableOptimizationInfo()
+ .setDynamicType(exactDynamicReturnType));
+ });
methodProcessor
.getEventConsumer()
.acceptInstanceInitializerOutline(method, methodProcessingContext.getMethodContext());
- synchronized (synthesizedMethods) {
- synthesizedMethods.add(method);
- ClassTypeElement exactType =
- targetMethod
- .getHolderType()
- .toTypeElement(appView, Nullability.definitelyNotNull())
- .asClassType();
- OptimizationFeedback.getSimpleFeedback()
- .setDynamicReturnType(method, appView, DynamicType.createExact(exactType));
- }
+ methodProcessor.scheduleDesugaredMethodForProcessing(method);
return method.getDefinition();
}
+
+ @Override
+ protected boolean shouldRewriteCode(IRCode code, MethodProcessor methodProcessor) {
+ if (!appView.options().desugarState.isOn()
+ || !appView.options().apiModelingOptions().enableOutliningOfMethods
+ || !appView.options().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L)) {
+ return false;
+ }
+ // Only outline in primary optimization pass.
+ if (!methodProcessor.isD8MethodProcessor() && !methodProcessor.isPrimaryMethodProcessor()) {
+ return false;
+ }
+ // Do not outline from already synthesized methods.
+ if (code.context().getDefinition().isD8R8Synthesized()) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected String getRewriterId() {
+ return "InstanceInitializerOutliner";
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 602b555..33f1160 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -710,7 +710,7 @@
setDynamicType(newDynamicType);
}
- private MutableMethodOptimizationInfo setDynamicType(DynamicType dynamicType) {
+ public MutableMethodOptimizationInfo setDynamicType(DynamicType dynamicType) {
assert !dynamicType.hasDynamicUpperBoundType()
|| !dynamicType.asDynamicTypeWithUpperBound().getDynamicUpperBoundType().isPrimitiveType();
this.dynamicType = dynamicType;