| // Copyright (c) 2019, 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.conversion; |
| |
| import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; |
| |
| import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext; |
| import com.android.tools.r8.contexts.CompilationContext.ProcessorContext; |
| import com.android.tools.r8.graph.AppView; |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| 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.conversion.PrimaryMethodProcessor.MethodAction; |
| import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed; |
| import com.android.tools.r8.logging.Log; |
| import com.android.tools.r8.shaking.AppInfoWithLiveness; |
| import com.android.tools.r8.utils.ThreadUtils; |
| import com.android.tools.r8.utils.Timing; |
| import com.android.tools.r8.utils.Timing.TimingMerger; |
| import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder; |
| import com.android.tools.r8.utils.collections.ProgramMethodSet; |
| import java.util.ArrayDeque; |
| import java.util.Collection; |
| import java.util.Deque; |
| import java.util.Set; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.ExecutorService; |
| |
| public class PostMethodProcessor extends MethodProcessorWithWave { |
| |
| private final ProcessorContext processorContext; |
| private final Deque<ProgramMethodSet> waves; |
| private final ProgramMethodSet processed = ProgramMethodSet.create(); |
| |
| private PostMethodProcessor( |
| AppView<AppInfoWithLiveness> appView, |
| CallGraph callGraph) { |
| this.processorContext = appView.createProcessorContext(); |
| this.waves = createWaves(callGraph); |
| } |
| |
| @Override |
| public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) { |
| return processorContext.createMethodProcessingContext(method); |
| } |
| |
| @Override |
| public boolean isPostMethodProcessor() { |
| return true; |
| } |
| |
| @Override |
| public boolean shouldApplyCodeRewritings(ProgramMethod method) { |
| assert !wave.contains(method); |
| return !processed.contains(method); |
| } |
| |
| public static class Builder { |
| |
| private final LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToReprocessBuilder; |
| |
| Builder(GraphLens graphLensForPrimaryOptimizationPass) { |
| this.methodsToReprocessBuilder = |
| LongLivedProgramMethodSetBuilder.createForIdentitySet( |
| graphLensForPrimaryOptimizationPass); |
| } |
| |
| public void add(ProgramMethod method) { |
| methodsToReprocessBuilder.add(method); |
| } |
| |
| public LongLivedProgramMethodSetBuilder<ProgramMethodSet> getMethodsToReprocessBuilder() { |
| return methodsToReprocessBuilder; |
| } |
| |
| public void put(ProgramMethodSet methodsToRevisit) { |
| methodsToRevisit.forEach(this::add); |
| } |
| |
| // Some optimizations may change methods, creating new instances of the encoded methods with a |
| // new signature. The compiler needs to update the set of methods that must be reprocessed |
| // according to the graph lens. |
| public void rewrittenWithLens(AppView<AppInfoWithLiveness> appView) { |
| methodsToReprocessBuilder.rewrittenWithLens(appView); |
| } |
| |
| PostMethodProcessor build( |
| AppView<AppInfoWithLiveness> appView, ExecutorService executorService, Timing timing) |
| throws ExecutionException { |
| Set<DexMethod> reprocessMethods = appView.appInfo().getReprocessMethods(); |
| if (!reprocessMethods.isEmpty()) { |
| ProgramMethodSet set = ProgramMethodSet.create(); |
| reprocessMethods.forEach( |
| reference -> { |
| DexProgramClass clazz = asProgramClassOrNull(appView.definitionForHolder(reference)); |
| DexEncodedMethod definition = reference.lookupOnClass(clazz); |
| if (definition != null) { |
| set.createAndAdd(clazz, definition); |
| } |
| }); |
| put(set); |
| } |
| if (methodsToReprocessBuilder.isEmpty()) { |
| // Nothing to revisit. |
| return null; |
| } |
| ProgramMethodSet methodsToReprocess = methodsToReprocessBuilder.build(appView); |
| CallGraph callGraph = |
| new PartialCallGraphBuilder(appView, methodsToReprocess).build(executorService, timing); |
| return new PostMethodProcessor(appView, callGraph); |
| } |
| } |
| |
| private Deque<ProgramMethodSet> createWaves(CallGraph callGraph) { |
| Deque<ProgramMethodSet> waves = new ArrayDeque<>(); |
| int waveCount = 1; |
| while (!callGraph.isEmpty()) { |
| ProgramMethodSet wave = callGraph.extractLeaves(); |
| waves.addLast(wave); |
| if (Log.ENABLED && Log.isLoggingEnabledFor(PostMethodProcessor.class)) { |
| Log.info(getClass(), "Wave #%d: %d", waveCount++, wave.size()); |
| } |
| } |
| return waves; |
| } |
| |
| <E extends Exception> void forEachMethod( |
| MethodAction<E> consumer, |
| OptimizationFeedbackDelayed feedback, |
| ExecutorService executorService, |
| Timing timing) |
| throws ExecutionException { |
| TimingMerger merger = |
| timing.beginMerger("secondary-processor", ThreadUtils.getNumberOfThreads(executorService)); |
| while (!waves.isEmpty()) { |
| wave = waves.removeFirst(); |
| assert !wave.isEmpty(); |
| assert waveExtension.isEmpty(); |
| do { |
| assert feedback.noUpdatesLeft(); |
| Collection<Timing> timings = |
| ThreadUtils.processItemsWithResults( |
| wave, |
| method -> { |
| Timing time = consumer.apply(method, createMethodProcessingContext(method)); |
| time.end(); |
| return time; |
| }, |
| executorService); |
| merger.add(timings); |
| feedback.updateVisibleOptimizationInfo(); |
| processed.addAll(wave); |
| prepareForWaveExtensionProcessing(); |
| } while (!wave.isEmpty()); |
| } |
| merger.end(); |
| } |
| } |