blob: af2bdd4c2e8807aa22a8651142cddbafe8d31343 [file] [log] [blame]
// Copyright (c) 2020, 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.graph;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import com.android.tools.r8.shaking.GraphReporter;
import com.android.tools.r8.shaking.InstantiationReason;
import com.android.tools.r8.shaking.KeepReason;
import com.android.tools.r8.utils.LensUtils;
import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
/** Stores the set of instantiated classes along with their allocation sites. */
public class ObjectAllocationInfoCollectionImpl implements ObjectAllocationInfoCollection {
private final Map<DexProgramClass, Set<DexEncodedMethod>> classesWithAllocationSiteTracking;
private final Set<DexProgramClass> classesWithoutAllocationSiteTracking;
private ObjectAllocationInfoCollectionImpl(
Map<DexProgramClass, Set<DexEncodedMethod>> classesWithAllocationSiteTracking,
Set<DexProgramClass> classesWithoutAllocationSiteTracking) {
this.classesWithAllocationSiteTracking = classesWithAllocationSiteTracking;
this.classesWithoutAllocationSiteTracking = classesWithoutAllocationSiteTracking;
}
public static Builder builder(boolean trackAllocationSites, GraphReporter reporter) {
return new Builder(trackAllocationSites, reporter);
}
public void markNoLongerInstantiated(DexProgramClass clazz) {
classesWithAllocationSiteTracking.remove(clazz);
classesWithoutAllocationSiteTracking.remove(clazz);
}
@Override
public void forEachClassWithKnownAllocationSites(
BiConsumer<DexProgramClass, Set<DexEncodedMethod>> consumer) {
classesWithAllocationSiteTracking.forEach(consumer);
}
@Override
public boolean isAllocationSitesKnown(DexProgramClass clazz) {
return classesWithAllocationSiteTracking.containsKey(clazz);
}
@Override
public boolean isInstantiatedDirectly(DexProgramClass clazz) {
if (classesWithAllocationSiteTracking.containsKey(clazz)) {
assert !classesWithAllocationSiteTracking.get(clazz).isEmpty();
return true;
}
return classesWithoutAllocationSiteTracking.contains(clazz);
}
@Override
public ObjectAllocationInfoCollectionImpl rewrittenWithLens(
DexDefinitionSupplier definitions, GraphLense lens) {
return builder(true, null).rewrittenWithLens(this, definitions, lens).build();
}
public static class Builder {
private final boolean trackAllocationSites;
private final Map<DexProgramClass, Set<DexEncodedMethod>> classesWithAllocationSiteTracking =
new IdentityHashMap<>();
private final Set<DexProgramClass> classesWithoutAllocationSiteTracking =
Sets.newIdentityHashSet();
private GraphReporter reporter;
private Builder(boolean trackAllocationSites, GraphReporter reporter) {
this.trackAllocationSites = trackAllocationSites;
this.reporter = reporter;
}
private boolean shouldTrackAllocationSitesForClass(
DexProgramClass clazz, InstantiationReason instantiationReason) {
if (!trackAllocationSites) {
return false;
}
if (instantiationReason != InstantiationReason.NEW_INSTANCE_INSTRUCTION) {
// There is an allocation site which is not a new-instance instruction.
return false;
}
if (classesWithoutAllocationSiteTracking.contains(clazz)) {
// We already gave up on tracking the allocation sites for `clazz` previously.
return false;
}
// We currently only use allocation site information for instance field value propagation.
return !clazz.instanceFields().isEmpty();
}
public boolean isInstantiatedDirectly(DexProgramClass clazz) {
if (classesWithAllocationSiteTracking.containsKey(clazz)) {
assert !classesWithAllocationSiteTracking.get(clazz).isEmpty();
return true;
}
return classesWithoutAllocationSiteTracking.contains(clazz);
}
/**
* Records that {@param clazz} is instantiated in {@param context}.
*
* @return true if {@param clazz} was not instantiated before.
*/
public boolean recordDirectAllocationSite(
DexProgramClass clazz,
DexEncodedMethod context,
InstantiationReason instantiationReason,
KeepReason keepReason) {
assert !clazz.isInterface();
if (reporter != null) {
reporter.registerClass(clazz, keepReason);
}
if (shouldTrackAllocationSitesForClass(clazz, instantiationReason)) {
assert context != null;
Set<DexEncodedMethod> allocationSitesForClass =
classesWithAllocationSiteTracking.computeIfAbsent(
clazz, ignore -> Sets.newIdentityHashSet());
allocationSitesForClass.add(context);
return allocationSitesForClass.size() == 1;
}
if (classesWithoutAllocationSiteTracking.add(clazz)) {
Set<DexEncodedMethod> allocationSitesForClass =
classesWithAllocationSiteTracking.remove(clazz);
return allocationSitesForClass == null;
}
return false;
}
Builder rewrittenWithLens(
ObjectAllocationInfoCollectionImpl objectAllocationInfos,
DexDefinitionSupplier definitions,
GraphLense lens) {
objectAllocationInfos.classesWithAllocationSiteTracking.forEach(
(clazz, allocationSitesForClass) -> {
DexProgramClass rewrittenClass =
asProgramClassOrNull(definitions.definitionFor(lens.lookupType(clazz.type)));
assert rewrittenClass != null;
assert !classesWithAllocationSiteTracking.containsKey(rewrittenClass);
classesWithAllocationSiteTracking.put(
rewrittenClass,
LensUtils.rewrittenWithRenamedSignature(
allocationSitesForClass, definitions, lens));
});
objectAllocationInfos.classesWithoutAllocationSiteTracking.forEach(
clazz -> {
DexProgramClass rewrittenClass =
asProgramClassOrNull(definitions.definitionFor(lens.lookupType(clazz.type)));
assert rewrittenClass != null;
assert !classesWithAllocationSiteTracking.containsKey(rewrittenClass);
assert !classesWithoutAllocationSiteTracking.contains(rewrittenClass);
classesWithoutAllocationSiteTracking.add(rewrittenClass);
});
return this;
}
public ObjectAllocationInfoCollectionImpl build() {
return new ObjectAllocationInfoCollectionImpl(
classesWithAllocationSiteTracking, classesWithoutAllocationSiteTracking);
}
}
}