blob: 10dc3444c0cad88a5ddfbd581a7275764287c218 [file] [log] [blame]
// Copyright (c) 2022, 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.experimental.startup;
import com.android.tools.r8.experimental.startup.profile.StartupClass;
import com.android.tools.r8.experimental.startup.profile.StartupItem;
import com.android.tools.r8.experimental.startup.profile.StartupMethod;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.synthesis.SyntheticItems;
import java.util.Collection;
import java.util.LinkedHashMap;
public class NonEmptyStartupOrder extends StartupOrder {
private final LinkedHashMap<DexReference, StartupItem> startupItems;
NonEmptyStartupOrder(LinkedHashMap<DexReference, StartupItem> startupItems) {
assert !startupItems.isEmpty();
this.startupItems = startupItems;
}
@Override
public boolean contains(DexMethod method) {
return startupItems.containsKey(method);
}
@Override
public boolean contains(DexType type) {
return startupItems.containsKey(type);
}
@Override
public Collection<StartupItem> getItems() {
return startupItems.values();
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public StartupOrder rewrittenWithLens(GraphLens graphLens) {
LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems =
new LinkedHashMap<>(startupItems.size());
for (StartupItem startupItem : startupItems.values()) {
// TODO(b/271822426): This should account for one-to-many mappings. e.g., when a bridge is
// created.
startupItem.apply(
startupClass ->
rewrittenStartupItems.put(
startupClass.getReference(),
StartupClass.builder()
.setClassReference(graphLens.lookupType(startupClass.getReference()))
.build()),
startupMethod ->
rewrittenStartupItems.put(
startupMethod.getReference(),
StartupMethod.builder()
.setMethodReference(
graphLens.getRenamedMethodSignature(startupMethod.getReference()))
.build()));
}
return createNonEmpty(rewrittenStartupItems);
}
/**
* This is called to process the startup order before computing the startup layouts.
*
* <p>This processing makes two key changes to the startup order:
*
* <ul>
* <li>Synthetic startup classes on the form "SLcom/example/SyntheticContext;" represents that
* any method of any synthetic class that have been synthesized from SyntheticContext has
* been executed. This pass removes such entries from the startup order, and replaces them
* by all the methods from all of the synthetics that have been synthesized from
* SyntheticContext.
* <li>Moreover, this inserts a StartupClass event for all supertypes of a given class next to
* the class in the startup order. This ensures that the classes from the super hierarchy
* will be laid out close to their subclasses, at the point where the subclasses are used
* during startup.
* <p>Note that this normally follows from the trace already, except that the class
* initializers of interfaces are not executed when a subclass is used.
* </ul>
*/
@Override
public StartupOrder toStartupOrderForWriting(AppView<?> appView) {
LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems =
new LinkedHashMap<>(startupItems.size());
for (StartupItem startupItem : startupItems.values()) {
addStartupItem(startupItem, rewrittenStartupItems, appView);
}
return createNonEmpty(rewrittenStartupItems);
}
private static void addStartupItem(
StartupItem startupItem,
LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems,
AppView<?> appView) {
if (startupItem.isStartupClass()) {
addClassAndParentClasses(
startupItem.asStartupClass().getReference(), rewrittenStartupItems, appView);
} else {
rewrittenStartupItems.put(startupItem.getReference(), startupItem);
}
}
private static boolean addClass(
DexProgramClass clazz, LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems) {
StartupItem previous =
rewrittenStartupItems.put(
clazz.getType(), StartupClass.builder().setClassReference(clazz.getType()).build());
return previous == null;
}
private static void addClassAndParentClasses(
DexType type,
LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems,
AppView<?> appView) {
DexProgramClass definition = appView.app().programDefinitionFor(type);
if (definition != null) {
addClassAndParentClasses(definition, rewrittenStartupItems, appView);
}
}
private static void addClassAndParentClasses(
DexProgramClass clazz,
LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems,
AppView<?> appView) {
if (addClass(clazz, rewrittenStartupItems)) {
addParentClasses(clazz, rewrittenStartupItems, appView);
}
}
private static void addParentClasses(
DexProgramClass clazz,
LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems,
AppView<?> appView) {
clazz.forEachImmediateSupertype(
supertype -> addClassAndParentClasses(supertype, rewrittenStartupItems, appView));
}
@Override
public StartupOrder withoutPrunedItems(PrunedItems prunedItems, SyntheticItems syntheticItems) {
LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems =
new LinkedHashMap<>(startupItems.size());
for (StartupItem startupItem : startupItems.values()) {
// Only prune non-synthetic classes, since the pruning of a class does not imply that all
// classes synthesized from it have been pruned.
startupItem.accept(
startupClass -> {
if (!prunedItems.isRemoved(startupClass.getReference())) {
rewrittenStartupItems.put(startupClass.getReference(), startupItem);
}
},
startupMethod -> {
if (!prunedItems.isRemoved(startupMethod.getReference())) {
rewrittenStartupItems.put(startupMethod.getReference(), startupItem);
}
});
}
return createNonEmpty(rewrittenStartupItems);
}
private StartupOrder createNonEmpty(LinkedHashMap<DexReference, StartupItem> startupItems) {
if (startupItems.isEmpty()) {
assert false;
return empty();
}
return new NonEmptyStartupOrder(startupItems);
}
}