blob: 4ba4555b5f3090197e3315934b73a317944e91e7 [file] [log] [blame]
// 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;
}
if (method.getDefinition().isAbstract()) {
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;
}
}