blob: 713322d2985e978cb7ff47748a6e816c00f91338 [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.ir.optimize.outliner;
import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.optimize.OutlinerImpl.Outline;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
// Maps each method to the outline candidates found in the method.
public class OutlineCollection {
private final Map<Outline, Outline> canonicalization = new ConcurrentHashMap<>();
private GraphLens appliedGraphLens;
private Map<DexMethod, List<Outline>> outlines = new ConcurrentHashMap<>();
public OutlineCollection(GraphLens graphLensForPrimaryOptimizationPass) {
this.appliedGraphLens = graphLensForPrimaryOptimizationPass;
}
public void remove(AppView<AppInfoWithLiveness> appView, ProgramMethod method) {
assert appView.graphLens() == appliedGraphLens;
outlines.remove(method.getReference());
}
public void set(
AppView<AppInfoWithLiveness> appView, ProgramMethod method, List<Outline> outlinesForMethod) {
assert appView.graphLens() == appliedGraphLens;
if (outlinesForMethod.isEmpty()) {
// If we are reprocessing the method, and found no instructions sequences eligible for
// outlining, then clear the outline candidates for the given method.
outlines.remove(method.getReference());
} else {
outlines.put(method.getReference(), canonicalize(outlinesForMethod));
}
}
public void rewriteWithLens(GraphLens currentGraphLens) {
if (currentGraphLens == appliedGraphLens) {
return;
}
Map<DexMethod, List<Outline>> rewrittenOutlines = new ConcurrentHashMap<>(outlines.size());
outlines.forEach(
(method, outlinesForMethod) -> {
DexMethod rewrittenMethod =
currentGraphLens.getRenamedMethodSignature(method, appliedGraphLens);
assert !rewrittenOutlines.containsKey(rewrittenMethod);
List<Outline> rewrittenOutlinesForMethod =
rewriteOutlinesWithLens(outlinesForMethod, currentGraphLens);
if (!rewrittenOutlinesForMethod.isEmpty()) {
rewrittenOutlines.put(rewrittenMethod, rewrittenOutlinesForMethod);
}
});
outlines = rewrittenOutlines;
// Record that this collection is now rewritten up until the point of the given graph lens.
appliedGraphLens = currentGraphLens;
}
private List<Outline> rewriteOutlinesWithLens(
List<Outline> outlines, GraphLens currentGraphLens) {
assert currentGraphLens != appliedGraphLens;
return ListUtils.mapOrElse(outlines, outline -> outline.rewrittenWithLens(currentGraphLens));
}
public ProgramMethodSet computeMethodsSubjectToOutlining(AppView<AppInfoWithLiveness> appView) {
ProgramMethodSet result = ProgramMethodSet.create();
Map<Outline, List<ProgramMethod>> methodsPerOutline = computeMethodsPerOutline(appView);
for (List<ProgramMethod> methodsWithSameOutline : methodsPerOutline.values()) {
if (methodsWithSameOutline.size() >= appView.options().outline.threshold) {
result.addAll(methodsWithSameOutline);
}
}
return result;
}
private Map<Outline, List<ProgramMethod>> computeMethodsPerOutline(
AppView<AppInfoWithLiveness> appView) {
Map<Outline, List<ProgramMethod>> methodsPerOutline = new HashMap<>();
outlines.forEach(
(reference, outlinesForMethod) -> {
DexMethod rewrittenReference =
appView.graphLens().getRenamedMethodSignature(reference, appliedGraphLens);
DexProgramClass holder =
DexProgramClass.asProgramClassOrNull(
appView.definitionFor(rewrittenReference.getHolderType()));
ProgramMethod method = rewrittenReference.lookupOnProgramClass(holder);
if (method == null) {
assert false;
return;
}
assert !method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite();
for (Outline outline : outlinesForMethod) {
methodsPerOutline.computeIfAbsent(outline, ignoreKey(ArrayList::new)).add(method);
}
});
return methodsPerOutline;
}
private List<Outline> canonicalize(List<Outline> outlines) {
List<Outline> canonicalizedOutlines = new ArrayList<>(outlines.size());
for (Outline outline : outlines) {
canonicalizedOutlines.add(canonicalize(outline));
}
return canonicalizedOutlines;
}
private Outline canonicalize(Outline outline) {
return canonicalization.computeIfAbsent(outline, Function.identity());
}
}