blob: 2761296a3fc79394e776815035a13aba79dfcb93 [file] [log] [blame]
// Copyright (c) 2021, 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.optimize.argumentpropagation;
import static com.android.tools.r8.optimize.argumentpropagation.utils.StronglyConnectedProgramClasses.computeStronglyConnectedProgramClasses;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.PostMethodProcessor;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.VirtualRootMethodsAnalysis;
import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ArgumentPropagatorReprocessingCriteriaCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
/** Optimization that propagates information about arguments from call sites to method entries. */
public class ArgumentPropagator {
private final AppView<AppInfoWithLiveness> appView;
/**
* Collects information about arguments from call sites, meanwhile pruning redundant information.
*
* <p>The data held by this instance is incomplete and should not be used for optimization until
* processed by {@link ArgumentPropagatorOptimizationInfoPopulator}.
*/
private ArgumentPropagatorCodeScanner codeScanner;
/**
* Analyzes the uses of arguments in methods to determine when reprocessing of methods will likely
* not lead to any additional code optimizations.
*/
private ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection;
public ArgumentPropagator(AppView<AppInfoWithLiveness> appView) {
assert appView.enableWholeProgramOptimizations();
assert appView.options().isOptimizing();
assert appView.options().callSiteOptimizationOptions().isEnabled();
assert appView
.options()
.callSiteOptimizationOptions()
.isExperimentalArgumentPropagationEnabled();
this.appView = appView;
}
/**
* Called by {@link IRConverter} *before* the primary optimization pass to setup the scanner for
* collecting argument information from the code objects.
*/
public void initializeCodeScanner(ExecutorService executorService, Timing timing)
throws ExecutionException {
assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
timing.begin("Argument propagator");
timing.begin("Initialize code scanner");
codeScanner = new ArgumentPropagatorCodeScanner(appView);
reprocessingCriteriaCollection = new ArgumentPropagatorReprocessingCriteriaCollection(appView);
ImmediateProgramSubtypingInfo immediateSubtypingInfo =
ImmediateProgramSubtypingInfo.create(appView);
List<Set<DexProgramClass>> stronglyConnectedProgramClasses =
computeStronglyConnectedProgramClasses(appView, immediateSubtypingInfo);
ThreadUtils.processItems(
stronglyConnectedProgramClasses,
classes -> {
// Disable argument propagation for methods that should not be optimized by setting their
// method state to unknown.
new ArgumentPropagatorUnoptimizableMethods(
appView, immediateSubtypingInfo, codeScanner.getMethodStates())
.disableArgumentPropagationForUnoptimizableMethods(classes);
// Compute the mapping from virtual methods to their root virtual method and the set of
// monomorphic virtual methods.
new VirtualRootMethodsAnalysis(appView, immediateSubtypingInfo)
.extendVirtualRootMethods(classes, codeScanner);
},
executorService);
timing.end();
timing.end();
}
/** Called by {@link IRConverter} prior to finalizing methods. */
public void scan(
ProgramMethod method, IRCode code, MethodProcessor methodProcessor, Timing timing) {
if (codeScanner != null) {
assert methodProcessor.isPrimaryMethodProcessor();
codeScanner.scan(method, code, timing);
assert reprocessingCriteriaCollection != null;
reprocessingCriteriaCollection.analyzeArgumentUses(method, code);
} else {
assert !methodProcessor.isPrimaryMethodProcessor();
assert reprocessingCriteriaCollection == null;
}
}
public void transferArgumentInformation(ProgramMethod from, ProgramMethod to) {
assert codeScanner != null;
MethodStateCollectionByReference methodStates = codeScanner.getMethodStates();
MethodState methodState = methodStates.remove(from);
if (!methodState.isBottom()) {
methodStates.addMethodState(appView, to, methodState);
}
}
public void tearDownCodeScanner(
PostMethodProcessor.Builder postMethodProcessorBuilder,
ExecutorService executorService,
Timing timing)
throws ExecutionException {
assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
timing.begin("Argument propagator");
ImmediateProgramSubtypingInfo immediateSubtypingInfo =
ImmediateProgramSubtypingInfo.create(appView);
List<Set<DexProgramClass>> stronglyConnectedProgramComponents =
computeStronglyConnectedProgramClasses(appView, immediateSubtypingInfo);
populateParameterOptimizationInfo(
immediateSubtypingInfo, stronglyConnectedProgramComponents, executorService, timing);
optimizeMethodParameters(
immediateSubtypingInfo, stronglyConnectedProgramComponents, executorService);
enqueueMethodsForProcessing(postMethodProcessorBuilder);
timing.end();
}
/**
* Called by {@link IRConverter} *after* the primary optimization pass to populate the parameter
* optimization info.
*/
private void populateParameterOptimizationInfo(
ImmediateProgramSubtypingInfo immediateSubtypingInfo,
List<Set<DexProgramClass>> stronglyConnectedProgramComponents,
ExecutorService executorService,
Timing timing)
throws ExecutionException {
// Unset the scanner since all code objects have been scanned at this point.
assert appView.isAllCodeProcessed();
MethodStateCollectionByReference codeScannerResult = codeScanner.getMethodStates();
appView.testing().argumentPropagatorEventConsumer.acceptCodeScannerResult(codeScannerResult);
codeScanner = null;
timing.begin("Compute optimization info");
new ArgumentPropagatorOptimizationInfoPopulator(
appView,
immediateSubtypingInfo,
codeScannerResult,
reprocessingCriteriaCollection,
stronglyConnectedProgramComponents)
.populateOptimizationInfo(executorService, timing);
reprocessingCriteriaCollection = null;
timing.end();
}
/** Called by {@link IRConverter} to optimize method definitions. */
private void optimizeMethodParameters(
ImmediateProgramSubtypingInfo immediateSubtypingInfo,
List<Set<DexProgramClass>> stronglyConnectedProgramComponents,
ExecutorService executorService)
throws ExecutionException {
Collection<ArgumentPropagatorGraphLens.Builder> partialGraphLensBuilders =
ThreadUtils.processItemsWithResults(
stronglyConnectedProgramComponents,
classes ->
new ArgumentPropagatorProgramOptimizer(appView, immediateSubtypingInfo)
.optimize(classes),
executorService);
// Merge all the partial, disjoint graph lens builders into a single graph lens.
ArgumentPropagatorGraphLens.Builder graphLensBuilder =
ArgumentPropagatorGraphLens.builder(appView);
partialGraphLensBuilders.forEach(graphLensBuilder::mergeDisjoint);
ArgumentPropagatorGraphLens graphLens = graphLensBuilder.build();
if (graphLens != null) {
appView.setGraphLens(graphLens);
}
}
/**
* Called by {@link IRConverter} to add all methods that require reprocessing to {@param
* postMethodProcessorBuilder}.
*/
private void enqueueMethodsForProcessing(PostMethodProcessor.Builder postMethodProcessorBuilder) {
for (DexProgramClass clazz : appView.appInfo().classes()) {
clazz.forEachProgramMethodMatching(
DexEncodedMethod::hasCode,
method -> {
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getDefinition().getCallSiteOptimizationInfo();
if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo()
&& !appView.appInfo().isNeverReprocessMethod(method.getReference())) {
postMethodProcessorBuilder.add(method);
appView.testing().callSiteOptimizationInfoInspector.accept(method);
}
});
}
}
}