blob: 0239e022ea1ae584c8baa05f42f3ab62ac45ff2e [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.ir.optimize.numberunboxer;
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.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.fixup.ConcurrentMethodFixup;
import com.android.tools.r8.graph.fixup.ConcurrentMethodFixup.ProgramClassFixer;
import com.android.tools.r8.graph.fixup.MethodNamingUtility;
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.optimize.numberunboxer.NumberUnboxerBoxingStatusResolution.MethodBoxingStatusResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Timing;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
public class NumberUnboxerTreeFixer implements ProgramClassFixer {
private final AppView<AppInfoWithLiveness> appView;
private final Map<DexMethod, MethodBoxingStatusResult> unboxingResult;
private final Map<DexMethod, DexMethod> virtualMethodsRepresentative;
private final NumberUnboxerLens.Builder lensBuilder = NumberUnboxerLens.builder();
public NumberUnboxerTreeFixer(
AppView<AppInfoWithLiveness> appView,
Map<DexMethod, MethodBoxingStatusResult> unboxingResult,
Map<DexMethod, DexMethod> virtualMethodsRepresentative) {
this.appView = appView;
this.unboxingResult = unboxingResult;
this.virtualMethodsRepresentative = virtualMethodsRepresentative;
}
public NumberUnboxerLens fixupTree(ExecutorService executorService, Timing timing)
throws ExecutionException {
// Take strongly connected components, for each merge data from call-sites and methods and
// unbox.
new ConcurrentMethodFixup(appView, this)
.fixupClassesConcurrentlyByConnectedProgramComponents(timing, executorService);
return lensBuilder.build(appView, new NumberUnboxerRewriter(appView));
}
@Override
public void fixupProgramClass(DexProgramClass clazz, MethodNamingUtility utility) {
clazz.getMethodCollection().replaceMethods(m -> fixupEncodedMethod(m, utility));
}
@Override
public boolean shouldReserveAsIfPinned(ProgramMethod method) {
// We don't reprocess dependencies of unchanged methods so we have to maintain them
// with the same signature.
return !unboxingResult.containsKey(representative(method.getReference()));
}
private DexMethod representative(DexMethod method) {
return virtualMethodsRepresentative.getOrDefault(method, method);
}
private DexEncodedMethod fixupEncodedMethod(
DexEncodedMethod method, MethodNamingUtility utility) {
if (!unboxingResult.containsKey(representative(method.getReference()))) {
assert method
.getReference()
.isIdenticalTo(
utility.nextUniqueMethod(
method, method.getProto(), appView.dexItemFactory().shortType));
return method;
}
MethodBoxingStatusResult methodBoxingStatus =
unboxingResult.get(representative(method.getReference()));
assert !methodBoxingStatus.isNoneUnboxable();
DexProto newProto = fixupProto(method.getProto(), methodBoxingStatus);
DexMethod newMethod =
utility.nextUniqueMethod(method, newProto, appView.dexItemFactory().shortType);
RewrittenPrototypeDescription prototypeChanges = lensBuilder.move(method, newMethod);
return method.toTypeSubstitutedMethodAsInlining(
newMethod,
appView.dexItemFactory(),
builder ->
builder
.fixupOptimizationInfo(
appView, prototypeChanges.createMethodOptimizationInfoFixer())
.setCompilationState(method.getCompilationState())
.setIsLibraryMethodOverrideIf(
method.isNonPrivateVirtualMethod(), OptionalBool.FALSE));
}
private DexType fixupType(DexType type, boolean unbox) {
if (!unbox) {
return type;
}
DexType newType = appView.dexItemFactory().primitiveToBoxed.inverse().get(type);
assert newType != null;
return newType;
}
private DexProto fixupProto(DexProto proto, MethodBoxingStatusResult methodBoxingStatus) {
DexType[] argTypes = proto.getParameters().values;
DexType[] newArgTypes =
ArrayUtils.initialize(
new DexType[argTypes.length],
i -> fixupType(argTypes[i], methodBoxingStatus.shouldUnboxArg(i)));
DexType newReturnType = fixupType(proto.getReturnType(), methodBoxingStatus.shouldUnboxRet());
DexProto newProto = appView.dexItemFactory().createProto(newReturnType, newArgTypes);
assert newProto.isNotIdenticalTo(proto);
return newProto;
}
}