| // 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()); |
| } |
| } |