blob: dbf0943145066aef60be40dbe22a5a70fc26bc51 [file] [log] [blame]
// Copyright (c) 2021, 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.horizontalclassmerging;
import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.InstanceInitializerMerger.Builder;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
public class InstanceInitializerMergerCollection {
private final List<InstanceInitializerMerger> instanceInitializerMergers;
private final Map<InstanceInitializerDescription, InstanceInitializerMerger>
equivalentInstanceInitializerMergers;
private InstanceInitializerMergerCollection(
List<InstanceInitializerMerger> instanceInitializerMergers,
Map<InstanceInitializerDescription, InstanceInitializerMerger>
equivalentInstanceInitializerMergers) {
this.instanceInitializerMergers = instanceInitializerMergers;
this.equivalentInstanceInitializerMergers = equivalentInstanceInitializerMergers;
}
public static InstanceInitializerMergerCollection create(
AppView<? extends AppInfoWithClassHierarchy> appView,
Reference2IntMap<DexType> classIdentifiers,
IRCodeProvider codeProvider,
MergeGroup group,
HorizontalClassMergerGraphLens.Builder lensBuilder,
Mode mode) {
// Create an instance initializer merger for each group of instance initializers that are
// equivalent.
Map<InstanceInitializerDescription, Builder> buildersByDescription = new LinkedHashMap<>();
ProgramMethodSet buildersWithoutDescription = ProgramMethodSet.createLinked();
group.forEach(
clazz ->
clazz.forEachProgramDirectMethodMatching(
DexEncodedMethod::isInstanceInitializer,
instanceInitializer -> {
InstanceInitializerDescription description =
InstanceInitializerAnalysis.analyze(
appView, codeProvider, group, instanceInitializer);
if (description != null) {
buildersByDescription
.computeIfAbsent(
description,
ignoreKey(
() ->
new InstanceInitializerMerger.Builder(
appView, classIdentifiers, lensBuilder, mode)))
.addEquivalent(instanceInitializer);
} else {
buildersWithoutDescription.add(instanceInitializer);
}
}));
Map<InstanceInitializerDescription, InstanceInitializerMerger>
equivalentInstanceInitializerMergers = new LinkedHashMap<>();
buildersByDescription.forEach(
(description, builder) -> {
InstanceInitializerMerger instanceInitializerMerger =
builder.buildSingle(group, description);
if (instanceInitializerMerger.size() == 1) {
// If there is only one constructor with a specific behavior, then consider it for
// normal instance initializer merging below.
buildersWithoutDescription.addAll(instanceInitializerMerger.getInstanceInitializers());
} else {
equivalentInstanceInitializerMergers.put(description, instanceInitializerMerger);
}
});
// Merge instance initializers with different behavior.
List<InstanceInitializerMerger> instanceInitializerMergers = new ArrayList<>();
if (appView.options().horizontalClassMergerOptions().isConstructorMergingEnabled()) {
Map<DexProto, Builder> buildersByProto = new LinkedHashMap<>();
buildersWithoutDescription.forEach(
instanceInitializer ->
buildersByProto
.computeIfAbsent(
instanceInitializer.getDefinition().getProto(),
ignore ->
new InstanceInitializerMerger.Builder(
appView, classIdentifiers, lensBuilder, mode))
.add(instanceInitializer));
for (InstanceInitializerMerger.Builder builder : buildersByProto.values()) {
instanceInitializerMergers.addAll(builder.build(group));
}
} else {
buildersWithoutDescription.forEach(
instanceInitializer ->
instanceInitializerMergers.addAll(
new InstanceInitializerMerger.Builder(
appView, classIdentifiers, lensBuilder, mode)
.add(instanceInitializer)
.build(group)));
}
// Try and merge the constructors with the most arguments first, to avoid using synthetic
// arguments if possible.
instanceInitializerMergers.sort(
Comparator.comparing(InstanceInitializerMerger::getArity).reversed());
return new InstanceInitializerMergerCollection(
instanceInitializerMergers, equivalentInstanceInitializerMergers);
}
public void forEach(Consumer<InstanceInitializerMerger> consumer) {
instanceInitializerMergers.forEach(consumer);
equivalentInstanceInitializerMergers.values().forEach(consumer);
}
}