| // 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 com.android.tools.r8.contexts.CompilationContext.ProcessorContext; |
| import com.android.tools.r8.errors.Unreachable; |
| import com.android.tools.r8.graph.DexProgramClass; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.ProgramMethod; |
| import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer; |
| import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.D8CfInstructionDesugaringEventConsumer; |
| import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore; |
| import com.android.tools.r8.utils.ThreadUtils; |
| import com.google.common.collect.Sets; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Future; |
| |
| public class D8MethodProcessor extends MethodProcessor { |
| |
| private final IRConverter converter; |
| private final ExecutorService executorService; |
| private final Set<DexType> scheduled = Sets.newIdentityHashSet(); |
| |
| // Asynchronous method processing actions. These are "terminal" method processing actions in the |
| // sense that the method processing is known not to fork any other futures. |
| private final List<Future<?>> terminalFutures = Collections.synchronizedList(new ArrayList<>()); |
| |
| // Asynchronous method processing actions. This list includes both "terminal" and "non-terminal" |
| // method processing actions. Thus, before the asynchronous method processing finishes, it may |
| // fork the processing of another method. |
| private final List<Future<?>> nonTerminalFutures = |
| Collections.synchronizedList(new ArrayList<>()); |
| |
| private ProcessorContext processorContext; |
| |
| public D8MethodProcessor(IRConverter converter, ExecutorService executorService) { |
| this.converter = converter; |
| this.executorService = executorService; |
| this.processorContext = converter.appView.createProcessorContext(); |
| } |
| |
| public void addScheduled(DexProgramClass clazz) { |
| boolean added = scheduled.add(clazz.getType()); |
| assert added; |
| } |
| |
| public void newWave() { |
| this.processorContext = converter.appView.createProcessorContext(); |
| } |
| |
| @Override |
| public boolean isProcessedConcurrently(ProgramMethod method) { |
| // In D8 all methods are considered independently compiled. |
| return true; |
| } |
| |
| @Override |
| public boolean shouldApplyCodeRewritings(ProgramMethod method) { |
| return true; |
| } |
| |
| public void scheduleMethodForProcessing( |
| ProgramMethod method, D8CfInstructionDesugaringEventConsumer eventConsumer) { |
| // TODO(b/179755192): By building up waves of methods in the class converter, we can avoid the |
| // following check and always process the method asynchronously. |
| if (!scheduled.contains(method.getHolderType()) |
| && !converter.appView.getSyntheticItems().isNonLegacySynthetic(method.getHolder())) { |
| // The non-synthetic holder is not scheduled. It will be processed once holder is scheduled. |
| return; |
| } |
| nonTerminalFutures.add( |
| ThreadUtils.processAsynchronously( |
| () -> |
| converter.rewriteCode( |
| method, |
| eventConsumer, |
| OptimizationFeedbackIgnore.getInstance(), |
| this, |
| processorContext.createMethodProcessingContext(method)), |
| executorService)); |
| } |
| |
| @Override |
| public void scheduleDesugaredMethodForProcessing(ProgramMethod method) { |
| // TODO(b/179755192): By building up waves of methods in the class converter, we can avoid the |
| // following check and always process the method asynchronously. |
| if (!scheduled.contains(method.getHolderType()) |
| && !converter.appView.getSyntheticItems().isNonLegacySynthetic(method.getHolder())) { |
| // The non-synthetic holder is not scheduled. It will be processed once holder is scheduled. |
| return; |
| } |
| terminalFutures.add( |
| ThreadUtils.processAsynchronously( |
| () -> |
| converter.rewriteDesugaredCode( |
| method, |
| OptimizationFeedbackIgnore.getInstance(), |
| this, |
| processorContext.createMethodProcessingContext(method)), |
| executorService)); |
| } |
| |
| public D8MethodProcessor scheduleDesugaredMethodsForProcessing(Iterable<ProgramMethod> methods) { |
| methods.forEach(this::scheduleDesugaredMethodForProcessing); |
| return this; |
| } |
| |
| @Override |
| public CallSiteInformation getCallSiteInformation() { |
| throw new Unreachable("Invalid attempt to obtain call-site information in D8"); |
| } |
| |
| public void awaitMethodProcessing() throws ExecutionException { |
| // Await the non-terminal futures until there are only terminal futures left. |
| while (!nonTerminalFutures.isEmpty()) { |
| List<Future<?>> futuresToAwait; |
| synchronized (nonTerminalFutures) { |
| futuresToAwait = new ArrayList<>(nonTerminalFutures); |
| nonTerminalFutures.clear(); |
| } |
| ThreadUtils.awaitFutures(futuresToAwait); |
| } |
| |
| // Await the terminal futures. There futures will by design not to fork new method processing. |
| int numberOfTerminalFutures = terminalFutures.size(); |
| ThreadUtils.awaitFutures(terminalFutures); |
| assert terminalFutures.size() == numberOfTerminalFutures; |
| terminalFutures.clear(); |
| } |
| |
| public void processMethod( |
| ProgramMethod method, CfInstructionDesugaringEventConsumer desugaringEventConsumer) { |
| converter.convertMethod( |
| method, |
| desugaringEventConsumer, |
| this, |
| processorContext.createMethodProcessingContext(method)); |
| } |
| |
| public boolean verifyNoPendingMethodProcessing() { |
| assert terminalFutures.isEmpty(); |
| assert nonTerminalFutures.isEmpty(); |
| return true; |
| } |
| } |