Rewrite outliner state with lens
Change-Id: I79e59d37c3110fa860469943cbce6245ff3d1f2c
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 83fc48c..8de51ce 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.optimize.Outliner.OutlineCode;
+import com.android.tools.r8.ir.optimize.OutlinerImpl.OutlineCode;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.origin.Origin;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
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 8f2a62f..0235660 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
@@ -73,7 +73,6 @@
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.MemberValuePropagation;
-import com.android.tools.r8.ir.optimize.Outliner;
import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
import com.android.tools.r8.ir.optimize.RedundantFieldLoadElimination;
import com.android.tools.r8.ir.optimize.ReflectionOptimizer;
@@ -88,6 +87,7 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
+import com.android.tools.r8.ir.optimize.outliner.Outliner;
import com.android.tools.r8.ir.optimize.staticizer.ClassStaticizer;
import com.android.tools.r8.ir.optimize.string.StringBuilderOptimizer;
import com.android.tools.r8.ir.optimize.string.StringOptimizer;
@@ -141,7 +141,7 @@
private final ClassStaticizer classStaticizer;
private final InternalOptions options;
private final CfgPrinter printer;
- private final CodeRewriter codeRewriter;
+ public final CodeRewriter codeRewriter;
private final ConstantCanonicalizer constantCanonicalizer;
private final MemberValuePropagation memberValuePropagation;
private final LensCodeRewriter lensCodeRewriter;
@@ -159,7 +159,7 @@
private final DynamicTypeOptimization dynamicTypeOptimization;
final AssertionsRewriter assertionsRewriter;
- final DeadCodeRemover deadCodeRemover;
+ public final DeadCodeRemover deadCodeRemover;
private final MethodOptimizationInfoCollector methodOptimizationInfoCollector;
@@ -229,7 +229,7 @@
this.fieldAccessAnalysis = null;
this.libraryMethodOverrideAnalysis = null;
this.inliner = null;
- this.outliner = null;
+ this.outliner = Outliner.empty();
this.memberValuePropagation = null;
this.lensCodeRewriter = null;
this.identifierNameStringMarker = null;
@@ -270,7 +270,7 @@
this.enumUnboxer = options.enableEnumUnboxing ? new EnumUnboxer(appViewWithLiveness) : null;
this.lensCodeRewriter = new LensCodeRewriter(appViewWithLiveness, enumUnboxer);
this.inliner = new Inliner(appViewWithLiveness, lensCodeRewriter);
- this.outliner = new Outliner(appViewWithLiveness);
+ this.outliner = Outliner.create(appViewWithLiveness);
this.memberValuePropagation =
options.enableValuePropagation ? new MemberValuePropagation(appViewWithLiveness) : null;
this.methodOptimizationInfoCollector =
@@ -297,7 +297,7 @@
this.fieldAccessAnalysis = null;
this.libraryMethodOverrideAnalysis = null;
this.inliner = null;
- this.outliner = null;
+ this.outliner = Outliner.empty();
this.memberValuePropagation = null;
this.lensCodeRewriter = null;
this.identifierNameStringMarker = null;
@@ -651,6 +651,7 @@
ConsumerUtils.acceptIfNotNull(
inliner,
inliner -> inliner.initializeDoubleInlineCallers(graphLensForPrimaryOptimizationPass));
+ outliner.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
if (fieldAccessAnalysis != null) {
fieldAccessAnalysis.fieldAssignmentTracker().initialize();
@@ -667,9 +668,6 @@
appView.withLiveness(), postMethodProcessorBuilder, executorService, timing);
timing.end();
timing.begin("IR conversion phase 1");
- if (outliner != null) {
- outliner.createOutlineMethodIdentifierGenerator();
- }
assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
primaryMethodProcessor.forEachMethod(
(method, methodProcessingContext) ->
@@ -727,6 +725,7 @@
}
if (enumUnboxer != null) {
+ outliner.rewriteWithLens();
enumUnboxer.unboxEnums(this, postMethodProcessorBuilder, executorService, feedback);
} else {
appView.setUnboxedEnums(EnumDataMap.empty());
@@ -739,6 +738,7 @@
classStaticizer ->
classStaticizer.prepareForSecondaryOptimizationPass(
graphLensForSecondaryOptimizationPass));
+ outliner.rewriteWithLens();
timing.begin("IR conversion phase 2");
PostMethodProcessor postMethodProcessor =
@@ -794,35 +794,7 @@
feedback.updateVisibleOptimizationInfo();
// TODO(b/127694949): Adapt to PostOptimization.
- if (outliner != null) {
- printPhase("Outlining");
- timing.begin("IR conversion phase 3");
- ProgramMethodSet methodsSelectedForOutlining = outliner.selectMethodsForOutlining();
- if (!methodsSelectedForOutlining.isEmpty()) {
- forEachSelectedOutliningMethod(
- methodsSelectedForOutlining,
- code -> {
- printMethod(code, "IR before outlining (SSA)", null);
- outliner.identifyOutlineSites(code);
- },
- executorService);
- List<ProgramMethod> outlineMethods = outliner.buildOutlineMethods();
- optimizeSynthesizedMethods(outlineMethods, executorService);
- forEachSelectedOutliningMethod(
- methodsSelectedForOutlining,
- code -> {
- outliner.applyOutliningCandidate(code);
- printMethod(code, "IR after outlining (SSA)", null);
- removeDeadCodeAndFinalizeIR(
- code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
- },
- executorService);
- feedback.updateVisibleOptimizationInfo();
- assert outliner.checkAllOutlineSitesFoundAgain();
- outlineMethods.forEach(m -> m.getDefinition().markNotProcessed());
- }
- timing.end();
- }
+ outliner.performOutlining(this, feedback, executorService, timing);
clearDexMethodCompilationState();
if (identifierNameStringMarker != null) {
@@ -1463,12 +1435,7 @@
// TODO(b/140766440): an ideal solution would be puttting CodeOptimization for this into
// the list for primary processing only.
- if (options.outline.enabled && outliner != null && methodProcessor.isPrimaryMethodProcessor()) {
- timing.begin("Identify outlines");
- outliner.getOutlineMethodIdentifierGenerator().accept(code);
- timing.end();
- assert code.isConsistentSSA();
- }
+ outliner.collectOutlineSites(code, timing);
assert code.verifyTypes(appView);
@@ -1957,13 +1924,13 @@
}
}
- private void printPhase(String phase) {
+ public void printPhase(String phase) {
if (!options.extensiveLoggingFilter.isEmpty()) {
System.out.println("Entering phase: " + phase);
}
}
- private String printMethod(IRCode code, String title, String previous) {
+ public String printMethod(IRCode code, String title, String previous) {
if (printer != null) {
printer.resetUnusedValue();
printer.begin("cfg");
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
similarity index 88%
rename from src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
rename to src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
index dfa9eac..958d8ac 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
@@ -53,8 +53,13 @@
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.code.ValueTypeConstraint;
import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.SourceCode;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
+import com.android.tools.r8.ir.optimize.outliner.OutlineCollection;
+import com.android.tools.r8.ir.optimize.outliner.Outliner;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -65,8 +70,8 @@
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
-import com.android.tools.r8.utils.collections.LongLivedProgramMethodMultisetBuilder;
-import com.android.tools.r8.utils.collections.ProgramMethodMultiset;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
@@ -78,6 +83,8 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
/**
@@ -87,37 +94,41 @@
*
* <ul>
* <li>First, all methods are converted to IR and passed to {@link
- * Outliner#createOutlineMethodIdentifierGenerator()}} to identify outlining candidates and
- * the methods containing each candidate. IR is converted to the output format (DEX or CF) and
- * thrown away along with the outlining candidates; only a list of lists of methods is kept,
- * where each list of methods corresponds to methods containing an outlining candidate.
- * <li>Second, {@link Outliner#selectMethodsForOutlining()} is called to retain the lists of
+ * OutlinerImpl#collectOutlineSites} to identify outlining candidates and the methods
+ * containing each candidate. IR is converted to the output format (DEX or CF) and thrown away
+ * along with the outlining candidates; only a list of lists of methods is kept, where each
+ * list of methods corresponds to methods containing an outlining candidate.
+ * <li>Second, {@link OutlinerImpl#selectMethodsForOutlining()} is called to retain the lists of
* methods found in the first step that are large enough (see {@link InternalOptions#outline}
* {@link OutlineOptions#threshold}). Each selected method is then converted back to IR and
- * passed to {@link Outliner#identifyOutlineSites(IRCode)}, which then stores concrete
- * outlining candidates in {@link Outliner#outlineSites}.
- * <li>Third, {@link Outliner#buildOutlineMethods()} is called to construct the <em>outline
+ * passed to {@link OutlinerImpl#identifyOutlineSites(IRCode)}, which then stores concrete
+ * outlining candidates in {@link OutlinerImpl#outlineSites}.
+ * <li>Third, {@link OutlinerImpl#buildOutlineMethods()} is called to construct the <em>outline
* support classes</em> containing a static helper method for each outline candidate that
* occurs frequently enough. Each selected method is then converted to IR, passed to {@link
- * Outliner#applyOutliningCandidate(IRCode)} to perform the outlining, and converted back to
- * the output format (DEX or CF).
+ * OutlinerImpl#applyOutliningCandidate(IRCode)} to perform the outlining, and converted back
+ * to the output format (DEX or CF).
* </ul>
*/
-public class Outliner {
+public class OutlinerImpl extends Outliner {
- /** Result of first step (see {@link Outliner#createOutlineMethodIdentifierGenerator()}. */
- private final List<LongLivedProgramMethodMultisetBuilder> candidateMethodLists =
- new ArrayList<>();
- /** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */
+ /**
+ * Result of first step (see {@link OutlinerImpl#prepareForPrimaryOptimizationPass(GraphLens)}
+ * ()}.
+ */
+ private OutlineCollection outlineCollection;
+
+ /** Result of second step (see {@link OutlinerImpl#selectMethodsForOutlining()}. */
private final Map<Outline, List<ProgramMethod>> outlineSites = new HashMap<>();
- /** Result of third step (see {@link Outliner#buildOutlineMethods()}. */
+
+ /** Result of third step (see {@link OutlinerImpl#buildOutlineMethods()}. */
private final Map<Outline, DexMethod> generatedOutlines = new HashMap<>();
static final int MAX_IN_SIZE = 5; // Avoid using ranged calls for outlined code.
private final AppView<AppInfoWithLiveness> appView;
private final InliningConstraints inliningConstraints;
-
+
private abstract static class OutlineInstruction {
// Value signaling that this is the one allowed temporary register for an outline.
@@ -196,6 +207,8 @@
public abstract int numberOfInputs();
public abstract int createInstruction(IRBuilder builder, Outline outline, int argumentMapIndex);
+
+ public abstract boolean needsLensRewriting(GraphLens currentGraphLens);
}
private static class BinOpOutlineInstruction extends OutlineInstruction {
@@ -301,6 +314,11 @@
builder.add(newInstruction);
return argumentMapIndex;
}
+
+ @Override
+ public boolean needsLensRewriting(GraphLens currentGraphLens) {
+ return false;
+ }
}
private static class NewInstanceOutlineInstruction extends OutlineInstruction {
@@ -365,6 +383,11 @@
builder.add(newInstruction);
return argumentMapIndex;
}
+
+ @Override
+ public boolean needsLensRewriting(GraphLens currentGraphLens) {
+ return currentGraphLens.lookupType(clazz) != clazz;
+ }
}
private static class InvokeOutlineInstruction extends OutlineInstruction {
@@ -503,6 +526,11 @@
builder.add(newInstruction);
return argumentMapIndex;
}
+
+ @Override
+ public boolean needsLensRewriting(GraphLens currentGraphLens) {
+ return currentGraphLens.getRenamedMethodSignature(method) != method;
+ }
}
// Representation of an outline.
@@ -575,6 +603,28 @@
return proto;
}
+ public Outline rewrittenWithLens(GraphLens currentGraphLens) {
+ if (needsLensRewriting(currentGraphLens)) {
+ // Discard this outline.
+ return null;
+ }
+ return this;
+ }
+
+ private boolean needsLensRewriting(GraphLens currentGraphLens) {
+ for (DexType argumentType : argumentTypes) {
+ if (currentGraphLens.lookupType(argumentType) != argumentType) {
+ return true;
+ }
+ }
+ if (currentGraphLens.lookupType(returnType) != returnType) {
+ return true;
+ }
+ return Iterables.any(
+ templateInstructions,
+ templateInstruction -> templateInstruction.needsLensRewriting(currentGraphLens));
+ }
+
@Override
public boolean equals(Object other) {
if (!(other instanceof Outline)) {
@@ -1135,27 +1185,17 @@
// TODO(sgjesse): This does not take several usages in the same method into account.
private class OutlineMethodIdentifier extends OutlineSpotter {
- private final Map<Outline, LongLivedProgramMethodMultisetBuilder> candidateMap;
+ private final List<Outline> outlinesForMethod;
OutlineMethodIdentifier(
- ProgramMethod method,
- BasicBlock block,
- Map<Outline, LongLivedProgramMethodMultisetBuilder> candidateMap) {
+ ProgramMethod method, BasicBlock block, List<Outline> outlinesForMethod) {
super(method, block);
- this.candidateMap = candidateMap;
+ this.outlinesForMethod = outlinesForMethod;
}
@Override
protected void handle(int start, int end, Outline outline) {
- synchronized (candidateMap) {
- candidateMap.computeIfAbsent(outline, this::addOutlineMethodList).add(method);
- }
- }
-
- private LongLivedProgramMethodMultisetBuilder addOutlineMethodList(Outline outline) {
- LongLivedProgramMethodMultisetBuilder result = LongLivedProgramMethodMultisetBuilder.create();
- candidateMethodLists.add(result);
- return result;
+ outlinesForMethod.add(outline);
}
}
@@ -1270,37 +1310,107 @@
}
}
- public Outliner(AppView<AppInfoWithLiveness> appView) {
+ public OutlinerImpl(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
this.inliningConstraints = new InliningConstraints(appView, GraphLens.getIdentityLens());
}
- public void createOutlineMethodIdentifierGenerator() {
- // Since optimizations may change the map identity of Outline objects (e.g. by setting the
- // out-value of invokes to null), this map must not be used except for identifying methods
- // potentially relevant to outlining. OutlineMethodIdentifier will add method lists to
- // candidateMethodLists whenever it adds an entry to candidateMap.
- Map<Outline, LongLivedProgramMethodMultisetBuilder> candidateMap = new HashMap<>();
- assert candidateMethodLists.isEmpty();
- assert outlineMethodIdentifierGenerator == null;
- outlineMethodIdentifierGenerator =
- code -> {
- ProgramMethod context = code.context();
- assert !context.getDefinition().getCode().isOutlineCode();
- if (ClassToFeatureSplitMap.isInFeature(context.getHolder(), appView)) {
- return;
- }
- for (BasicBlock block : code.blocks) {
- new OutlineMethodIdentifier(context, block, candidateMap).process();
- }
- };
+ @Override
+ public void prepareForPrimaryOptimizationPass(GraphLens graphLensForPrimaryOptimizationPass) {
+ assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
+ assert outlineCollection == null;
+ outlineCollection = new OutlineCollection(graphLensForPrimaryOptimizationPass);
}
- private Consumer<IRCode> outlineMethodIdentifierGenerator;
+ @Override
+ public void performOutlining(
+ IRConverter converter,
+ OptimizationFeedbackDelayed feedback,
+ ExecutorService executorService,
+ Timing timing)
+ throws ExecutionException {
+ converter.printPhase("Outlining");
+ timing.begin("IR conversion phase 3");
+ ProgramMethodSet methodsSelectedForOutlining = selectMethodsForOutlining();
+ if (!methodsSelectedForOutlining.isEmpty()) {
+ forEachSelectedOutliningMethod(
+ converter,
+ methodsSelectedForOutlining,
+ code -> {
+ converter.printMethod(code, "IR before outlining (SSA)", null);
+ identifyOutlineSites(code);
+ },
+ executorService);
+ List<ProgramMethod> outlineMethods = buildOutlineMethods();
+ converter.optimizeSynthesizedMethods(outlineMethods, executorService);
+ forEachSelectedOutliningMethod(
+ converter,
+ methodsSelectedForOutlining,
+ code -> {
+ applyOutliningCandidate(code);
+ converter.printMethod(code, "IR after outlining (SSA)", null);
+ converter.removeDeadCodeAndFinalizeIR(
+ code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
+ },
+ executorService);
+ feedback.updateVisibleOptimizationInfo();
+ assert checkAllOutlineSitesFoundAgain();
+ outlineMethods.forEach(m -> m.getDefinition().markNotProcessed());
+ }
+ timing.end();
+ }
- public Consumer<IRCode> getOutlineMethodIdentifierGenerator() {
- assert outlineMethodIdentifierGenerator != null;
- return outlineMethodIdentifierGenerator;
+ private void forEachSelectedOutliningMethod(
+ IRConverter converter,
+ ProgramMethodSet methodsSelectedForOutlining,
+ Consumer<IRCode> consumer,
+ ExecutorService executorService)
+ throws ExecutionException {
+ assert !appView.options().skipIR;
+ ThreadUtils.processItems(
+ methodsSelectedForOutlining,
+ method -> {
+ IRCode code = method.buildIR(appView);
+ assert code != null;
+ assert !method.getDefinition().getCode().isOutlineCode();
+ // Instead of repeating all the optimizations of rewriteCode(), only run the
+ // optimizations needed for outlining: rewriteMoveResult() to remove out-values on
+ // StringBuilder/StringBuffer method invocations, and removeDeadCode() to remove
+ // unused out-values.
+ converter.codeRewriter.rewriteMoveResult(code);
+ converter.deadCodeRemover.run(code, Timing.empty());
+ CodeRewriter.removeAssumeInstructions(appView, code);
+ consumer.accept(code);
+ },
+ executorService);
+ }
+
+ public void rewriteWithLens() {
+ // Rewrite the outline collection with the graph lens, such that the reprocessing of methods
+ // will correctly delete/rewrite entries in the outline collection.
+ outlineCollection.rewriteWithLens(appView.graphLens());
+ }
+
+ @Override
+ public void collectOutlineSites(IRCode code, Timing timing) {
+ if (outlineCollection == null) {
+ return;
+ }
+
+ ProgramMethod context = code.context();
+ assert !context.getDefinition().getCode().isOutlineCode();
+
+ if (ClassToFeatureSplitMap.isInFeature(context.getHolder(), appView)) {
+ return;
+ }
+
+ timing.begin("Collect outlines");
+ List<Outline> outlinesForMethod = new ArrayList<>();
+ for (BasicBlock block : code.blocks) {
+ new OutlineMethodIdentifier(context, block, outlinesForMethod).process();
+ }
+ outlineCollection.set(appView, context, outlinesForMethod);
+ timing.end();
}
public void identifyOutlineSites(IRCode code) {
@@ -1313,16 +1423,9 @@
}
public ProgramMethodSet selectMethodsForOutlining() {
- ProgramMethodSet methodsSelectedForOutlining = ProgramMethodSet.create();
- assert outlineSites.isEmpty();
- for (LongLivedProgramMethodMultisetBuilder outlineMethods : candidateMethodLists) {
- if (outlineMethods.size() >= appView.options().outline.threshold) {
- ProgramMethodMultiset multiset = outlineMethods.build(appView);
- multiset.forEachEntry((method, ignore) -> methodsSelectedForOutlining.add(method));
- }
- }
- candidateMethodLists.clear();
- return methodsSelectedForOutlining;
+ ProgramMethodSet result = outlineCollection.computeMethodsSubjectToOutlining(appView);
+ outlineCollection = null;
+ return result;
}
public List<ProgramMethod> buildOutlineMethods() {
@@ -1374,7 +1477,6 @@
private List<Outline> selectOutlines() {
assert !outlineSites.isEmpty();
- assert candidateMethodLists.isEmpty();
List<Outline> result = new ArrayList<>();
for (Entry<Outline, List<ProgramMethod>> entry : outlineSites.entrySet()) {
if (entry.getValue().size() >= appView.options().outline.threshold) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java
new file mode 100644
index 0000000..16e4dab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java
@@ -0,0 +1,122 @@
+// 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 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) {
+ for (Outline outline : outlinesForMethod) {
+ methodsPerOutline.computeIfAbsent(outline, ignoreKey(ArrayList::new)).add(method);
+ }
+ } else {
+ assert false;
+ }
+ });
+ 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());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/Outliner.java
new file mode 100644
index 0000000..a2bd90f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/Outliner.java
@@ -0,0 +1,66 @@
+// 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 com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.optimize.OutlinerImpl;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Timing;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public abstract class Outliner {
+
+ public static Outliner create(AppView<AppInfoWithLiveness> appView) {
+ return appView.options().outline.enabled ? new OutlinerImpl(appView) : empty();
+ }
+
+ public static Outliner empty() {
+ return new Outliner() {
+ @Override
+ public void collectOutlineSites(IRCode code, Timing timing) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void prepareForPrimaryOptimizationPass(GraphLens graphLensForPrimaryOptimizationPass) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void performOutlining(
+ IRConverter converter,
+ OptimizationFeedbackDelayed feedback,
+ ExecutorService executorService,
+ Timing timing)
+ throws ExecutionException {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void rewriteWithLens() {
+ // Intentionally empty.
+ }
+ };
+ }
+
+ public abstract void collectOutlineSites(IRCode code, Timing timing);
+
+ public abstract void prepareForPrimaryOptimizationPass(
+ GraphLens graphLensForPrimaryOptimizationPass);
+
+ public abstract void performOutlining(
+ IRConverter converter,
+ OptimizationFeedbackDelayed feedback,
+ ExecutorService executorService,
+ Timing timing)
+ throws ExecutionException;
+
+ public abstract void rewriteWithLens();
+}