blob: 97903a9885bc39f87f7c6f5a6fb7789496676d15 [file] [log] [blame]
// Copyright (c) 2023, 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.optimize.argumentpropagation;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
import com.android.tools.r8.ir.conversion.PrimaryR8IRConverter;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.FieldStateCollection;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
import com.android.tools.r8.optimize.argumentpropagation.propagation.InFlowPropagator;
import com.android.tools.r8.optimize.argumentpropagation.propagation.InterfaceMethodArgumentPropagator;
import com.android.tools.r8.optimize.argumentpropagation.propagation.VirtualDispatchMethodArgumentPropagator;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
/**
* Propagates the argument flow information collected by the {@link ArgumentPropagatorCodeScanner}.
* This is needed to propagate argument information from call sites to all possible dispatch
* targets.
*/
public class ArgumentPropagatorOptimizationInfoPropagator {
private final AppView<AppInfoWithLiveness> appView;
private final PrimaryR8IRConverter converter;
private final FieldStateCollection fieldStates;
private final MethodStateCollectionByReference methodStates;
private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
private final List<Set<DexProgramClass>> stronglyConnectedProgramComponents;
private final BiConsumer<Set<DexProgramClass>, DexMethodSignature>
interfaceDispatchOutsideProgram;
ArgumentPropagatorOptimizationInfoPropagator(
AppView<AppInfoWithLiveness> appView,
PrimaryR8IRConverter converter,
ImmediateProgramSubtypingInfo immediateSubtypingInfo,
FieldStateCollection fieldStates,
MethodStateCollectionByReference methodStates,
List<Set<DexProgramClass>> stronglyConnectedProgramComponents,
BiConsumer<Set<DexProgramClass>, DexMethodSignature> interfaceDispatchOutsideProgram) {
this.appView = appView;
this.converter = converter;
this.immediateSubtypingInfo = immediateSubtypingInfo;
this.fieldStates = fieldStates;
this.methodStates = methodStates;
this.stronglyConnectedProgramComponents = stronglyConnectedProgramComponents;
this.interfaceDispatchOutsideProgram = interfaceDispatchOutsideProgram;
}
/** Computes an over-approximation of each parameter's value and type. */
void propagateOptimizationInfo(ExecutorService executorService, Timing timing)
throws ExecutionException {
timing.begin("Propagate argument information for virtual methods");
ThreadUtils.processItems(
stronglyConnectedProgramComponents,
this::processStronglyConnectedComponent,
appView.options().getThreadingModule(),
executorService);
timing.end();
// Solve the parameter flow constraints.
timing.begin("Solve flow constraints");
new InFlowPropagator(appView, converter, fieldStates, methodStates).run(executorService);
timing.end();
}
private void processStronglyConnectedComponent(Set<DexProgramClass> stronglyConnectedComponent) {
// Invoke instructions that target interface methods may dispatch to methods that are not
// defined on a subclass of the interface method holder.
//
// Example: Calling I.m() will dispatch to A.m(), but A is not a subtype of I.
//
// class A { public void m() {} }
// interface I { void m(); }
// class B extends A implements I {}
//
// To handle this we first propagate any argument information stored for I.m() to A.m() by doing
// a top-down traversal over the interfaces in the strongly connected component.
new InterfaceMethodArgumentPropagator(
appView,
immediateSubtypingInfo,
methodStates,
signature ->
interfaceDispatchOutsideProgram.accept(stronglyConnectedComponent, signature))
.run(stronglyConnectedComponent);
// Now all the argument information for a given method is guaranteed to be stored on a supertype
// of the method's holder. All that remains is to propagate the information downwards in the
// class hierarchy to propagate the argument information for a non-private virtual method to its
// overrides.
// TODO(b/190154391): Before running the top-down traversal, consider lowering the argument
// information for non-private virtual methods. If we have some argument information with upper
// bound=B, which is stored on a method on class A, we could move this argument information
// from class A to B. This way we could potentially get rid of the "inactive argument
// information" during the depth-first class hierarchy traversal, since the argument
// information would be active by construction when it is first seen during the top-down class
// hierarchy traversal.
new VirtualDispatchMethodArgumentPropagator(appView, immediateSubtypingInfo, methodStates)
.run(stronglyConnectedComponent);
}
}