Move type element caches from DexItemFactory to AppView
Change-Id: I5e2758864a8314345a5ce552f7623dac352c9048
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 2500f19..2393dab 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -296,7 +296,7 @@
// which will construct a graph lens.
if (options.isGeneratingDex() && !options.mainDexKeepRules.isEmpty()) {
timing.begin("Generate main-dex list");
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
MainDexInfo mainDexInfo =
new GenerateMainDexList(options).traceMainDexForD8(appView, executor);
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo));
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 6c77797..7d74e71 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -496,7 +496,7 @@
// The class type lattice elements include information about the interfaces that a class
// implements. This information can change as a result of vertical class merging, so we need
// to clear the cache, so that we will recompute the type lattice elements.
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
// This pass attempts to reduce the number of nests and nest size to allow further passes, and
// should therefore be run after the publicizer.
@@ -534,7 +534,7 @@
appView.appInfo().getMainDexInfo().clearTracedMethodRoots();
// None of the optimizations above should lead to the creation of type lattice elements.
- assert appView.dexItemFactory().verifyNoCachedTypeElements();
+ assert appView.getTypeElementFactory().verifyNoCachedTypeElements();
assert appView.checkForTesting(() -> allReferencesAssignedApiLevel(appViewWithLiveness));
// Collect switch maps and ordinals maps.
@@ -558,7 +558,7 @@
appView, ALLOW_MISSING_ENUM_UNBOXING_UTILITY_METHODS);
// Clear the reference type lattice element cache to reduce memory pressure.
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
// At this point all code has been mapped according to the graph lens. We cannot remove the
// graph lens entirely, though, since it is needed for mapping all field and method signatures
@@ -788,12 +788,12 @@
}
if (!appView.hasCfByteCodePassThroughMethods()) {
- assert appView.dexItemFactory().verifyNoCachedTypeElements();
+ assert appView.getTypeElementFactory().verifyNoCachedTypeElements();
if (appView.hasLiveness()) {
VerticalClassMerger.createForIntermediateClassMerging(appView.withLiveness(), timing)
.runIfNecessary(executorService, timing);
- assert appView.dexItemFactory().verifyNoCachedTypeElements();
+ assert appView.getTypeElementFactory().verifyNoCachedTypeElements();
}
genericContextBuilderBeforeFinalMerging = GenericSignatureContextBuilder.create(appView);
@@ -807,12 +807,12 @@
finalRuntimeTypeCheckInfoBuilder != null
? finalRuntimeTypeCheckInfoBuilder.build(appView.graphLens())
: null);
- assert appView.dexItemFactory().verifyNoCachedTypeElements();
+ assert appView.getTypeElementFactory().verifyNoCachedTypeElements();
if (appView.hasLiveness()) {
VerticalClassMerger.createForFinalClassMerging(appView.withLiveness(), timing)
.runIfNecessary(executorService, timing);
- assert appView.dexItemFactory().verifyNoCachedTypeElements();
+ assert appView.getTypeElementFactory().verifyNoCachedTypeElements();
new SingleCallerInliner(appViewWithLiveness).runIfNecessary(executorService, timing);
new ProtoNormalizer(appViewWithLiveness).run(executorService, timing);
@@ -905,7 +905,7 @@
LirConverter.finalizeLirToOutputFormat(appView, timing, executorService);
LirConverter.finalizeClasspathLirToOutputFormat(appView, timing, executorService);
- assert appView.dexItemFactory().verifyNoCachedTypeElements();
+ assert appView.getTypeElementFactory().verifyNoCachedTypeElements();
// Generate the resulting application resources.
writeKeepDeclarationsToConfigurationConsumer(keepDeclarations);
diff --git a/src/main/java/com/android/tools/r8/R8Partial.java b/src/main/java/com/android/tools/r8/R8Partial.java
index e48de4c..afbc968 100644
--- a/src/main/java/com/android/tools/r8/R8Partial.java
+++ b/src/main/java/com/android/tools/r8/R8Partial.java
@@ -223,8 +223,7 @@
}
r8Builder.validate();
R8Command r8Command =
- r8Builder.makeR8Command(
- options.dexItemFactory().clearTypeElementsCache(), options.getProguardConfiguration());
+ r8Builder.makeR8Command(options.dexItemFactory(), options.getProguardConfiguration());
AndroidApp r8App = r8Command.getInputApp();
InternalOptions r8Options = r8Command.getInternalOptions();
forwardOptions(r8Options);
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 47b2e63..a8665ac 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteShrinker;
import com.android.tools.r8.ir.analysis.proto.ProtoShrinker;
+import com.android.tools.r8.ir.analysis.type.TypeElementFactory;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueConstantPropagationJoiner;
import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueFieldJoiner;
@@ -155,6 +156,9 @@
private final Map<DexType, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
private final Map<DexType, String> sourceFileForPrunedTypes = new IdentityHashMap<>();
+ // Types.
+ private TypeElementFactory typeElementFactory = new TypeElementFactory();
+
private SeedMapper applyMappingSeedMapper;
private R8ResourceShrinkerState resourceShrinkerState = null;
@@ -352,6 +356,10 @@
this.methodResolutionOptimizationInfoCollection = getMethodResolutionOptimizationInfoCollection;
}
+ public TypeElementFactory getTypeElementFactory() {
+ return typeElementFactory;
+ }
+
public InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory() {
return instanceFieldInitializationInfoFactory;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 545981b..9df4ea2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import static com.android.tools.r8.ir.analysis.type.ClassTypeElement.computeLeastUpperBoundOfInterfaces;
import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
import com.android.tools.r8.dex.Constants;
@@ -19,11 +18,6 @@
import com.android.tools.r8.graph.DexDebugEvent.SetPositionFrame;
import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd;
import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
-import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
-import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.ReferenceTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
@@ -35,7 +29,6 @@
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.LRUCacheTable;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.BiMap;
@@ -119,14 +112,6 @@
public final DexDebugEvent.Default zeroChangeDefaultEvent = createDefault(0, 0);
public final DexDebugEvent.Default oneChangeDefaultEvent = createDefault(1, 1);
- // ReferenceTypeElement canonicalization.
- private final ConcurrentHashMap<DexType, ReferenceTypeElement> referenceTypes =
- new ConcurrentHashMap<>();
- private final ConcurrentHashMap<DexType, InterfaceCollection> classTypeInterfaces =
- new ConcurrentHashMap<>();
- public final LRUCacheTable<InterfaceCollection, InterfaceCollection, InterfaceCollection>
- leastUpperBoundOfInterfacesTable = LRUCacheTable.create(8, 8);
-
// Internal type containing only the null value.
public static final DexType nullValueType = new DexType(new DexString("NULL"));
@@ -3642,92 +3627,6 @@
return method.name == classConstructorMethodName;
}
- public DexItemFactory clearTypeElementsCache() {
- referenceTypes.clear();
- classTypeInterfaces.clear();
- leastUpperBoundOfInterfacesTable.clear();
- return this;
- }
-
- public boolean verifyNoCachedTypeElements() {
- assert referenceTypes.isEmpty();
- assert classTypeInterfaces.isEmpty();
- assert leastUpperBoundOfInterfacesTable.isEmpty();
- return true;
- }
-
- public ReferenceTypeElement createReferenceTypeElement(
- DexType type, Nullability nullability, AppView<?> appView) {
- // Class case:
- // If two concurrent threads will try to create the same class-type the concurrent hash map will
- // synchronize on the type in .computeIfAbsent and only a single class type is created.
- //
- // Array case:
- // Arrays will create a lattice element for its base type thus we take special care here.
- // Multiple threads may race recursively to create a base type. We have two cases:
- // (i) If base type is class type and the threads will race to create the class type but only a
- // single one will be created (Class case).
- // (ii) If base is ArrayLattice case we can use our induction hypothesis to get that only one
- // element is created for us up to this case. Threads will now race to return from the
- // latest recursive call and fight to get access to .computeIfAbsent to add the
- // ArrayTypeElement but only one will enter. The property that only one
- // ArrayTypeElement is created per level therefore holds inductively.
- TypeElement memberType = null;
- if (type.isArrayType()) {
- ReferenceTypeElement existing = referenceTypes.get(type);
- if (existing != null) {
- return existing.getOrCreateVariant(nullability);
- }
- memberType =
- TypeElement.fromDexType(
- type.toArrayElementType(this), Nullability.maybeNull(), appView, true);
- }
- TypeElement finalMemberType = memberType;
- return referenceTypes
- .computeIfAbsent(
- type,
- t -> {
- if (type.isClassType()) {
- if (!appView.enableWholeProgramOptimizations()) {
- // Don't reason at the level of interfaces in D8.
- return ClassTypeElement.createForD8(type, nullability);
- }
- assert appView.appInfo().hasClassHierarchy();
- if (appView.isInterface(type).isTrue()) {
- return ClassTypeElement.create(
- objectType,
- nullability,
- appView.withClassHierarchy(),
- InterfaceCollection.singleton(type));
- }
- // In theory, `interfaces` is the least upper bound of implemented interfaces.
- // It is expensive to walk through type hierarchy; collect implemented interfaces;
- // and compute the least upper bound of two interface sets. Hence, lazy
- // computations. Most likely during lattice join. See {@link
- // ClassTypeElement#getInterfaces}.
- return ClassTypeElement.create(type, nullability, appView.withClassHierarchy());
- }
- assert type.isArrayType();
- return ArrayTypeElement.create(finalMemberType, nullability);
- })
- .getOrCreateVariant(nullability);
- }
-
- public InterfaceCollection getLeastUpperBoundOfImplementedInterfacesOrDefault(
- DexType type, InterfaceCollection defaultValue) {
- return classTypeInterfaces.getOrDefault(type, defaultValue);
- }
-
- public InterfaceCollection getOrComputeLeastUpperBoundOfImplementedInterfaces(
- DexType type, AppView<? extends AppInfoWithClassHierarchy> appView) {
- return classTypeInterfaces.computeIfAbsent(
- type,
- t -> {
- InterfaceCollection itfs = appView.appInfo().implementedInterfaces(t);
- return computeLeastUpperBoundOfInterfaces(appView, itfs, itfs);
- });
- }
-
@Deprecated
synchronized public void forAllTypes(Consumer<DexType> f) {
new ArrayList<>(types.values()).forEach(f);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 40e2f1c..51c282f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -73,7 +73,7 @@
assert ArtProfileCompletenessChecker.verify(appView);
// Clear type elements cache after IR building.
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
appView.notifyOptimizationFinishedForTesting();
} else {
appView.setHorizontallyMergedClasses(HorizontallyMergedClasses.empty());
@@ -387,7 +387,7 @@
for (ClassMerger.Builder classMergerBuilder : classMergerBuilders) {
classMergers.add(classMergerBuilder.build(lensBuilder));
}
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
return classMergers;
}
@@ -407,7 +407,7 @@
syntheticInitializerConverterBuilder,
virtuallyMergedMethodsKeepInfoConsumer);
}
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
return prunedItemsBuilder.build();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/UndoConstructorInlining.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/UndoConstructorInlining.java
index af621ab..94e598d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/UndoConstructorInlining.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/UndoConstructorInlining.java
@@ -123,7 +123,7 @@
computeStronglyConnectedComponents();
new LirRewriter(appView, ensureConstructorsOnClasses, stronglyConnectedComponents)
.run(executorService);
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
}
private void ensureConstructorsOnSubclasses(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
index 4ec2c1b..84f0b5d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
@@ -38,14 +38,14 @@
public void convertClassInitializers(ExecutorService executorService) throws ExecutionException {
if (!classInitializers.isEmpty()) {
- assert appView.dexItemFactory().verifyNoCachedTypeElements();
+ assert appView.getTypeElementFactory().verifyNoCachedTypeElements();
IRConverter converter = new IRConverter(appView);
ThreadUtils.processItems(
classInitializers,
method -> processMethod(method, converter),
appView.options().getThreadingModule(),
executorService);
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index a9ee774..8447dee 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -100,7 +100,7 @@
assert appView != null;
assert appView.enableWholeProgramOptimizations();
return appView
- .dexItemFactory()
+ .getTypeElementFactory()
.getOrComputeLeastUpperBoundOfImplementedInterfaces(type, appView);
}
return lazyInterfaces;
@@ -113,7 +113,7 @@
}
InterfaceCollection defaultImplementedInterfaces =
appView
- .dexItemFactory()
+ .getTypeElementFactory()
.getLeastUpperBoundOfImplementedInterfacesOrDefault(getClassType(), null);
if (ObjectUtils.identical(lazyInterfaces, defaultImplementedInterfaces)) {
return true;
@@ -408,7 +408,7 @@
: computeLeastUpperBoundOfInterfaces(appView, interfaces, otherInterfaces);
InterfaceCollection lubInterfacesDefault =
appView
- .dexItemFactory()
+ .getTypeElementFactory()
.getOrComputeLeastUpperBoundOfImplementedInterfaces(lubType, appView);
Nullability lubNullability = nullability().join(nullability);
@@ -580,13 +580,13 @@
return InterfaceCollection.empty();
}
// Synchronization is required, see b/242286733.
- synchronized (appView.dexItemFactory().leastUpperBoundOfInterfacesTable) {
+ synchronized (appView.getTypeElementFactory().leastUpperBoundOfInterfacesTable) {
InterfaceCollection cached =
- appView.dexItemFactory().leastUpperBoundOfInterfacesTable.get(s1, s2);
+ appView.getTypeElementFactory().leastUpperBoundOfInterfacesTable.get(s1, s2);
if (cached != null) {
return cached;
}
- cached = appView.dexItemFactory().leastUpperBoundOfInterfacesTable.get(s2, s1);
+ cached = appView.getTypeElementFactory().leastUpperBoundOfInterfacesTable.get(s2, s1);
if (cached != null) {
return cached;
}
@@ -643,8 +643,8 @@
InterfaceCollection lub = lubBuilder.build();
// Cache the computation result only if the given two sets of interfaces are different.
if (!s1.equals(s2)) {
- synchronized (appView.dexItemFactory().leastUpperBoundOfInterfacesTable) {
- appView.dexItemFactory().leastUpperBoundOfInterfacesTable.put(s1, s2, lub);
+ synchronized (appView.getTypeElementFactory().leastUpperBoundOfInterfacesTable) {
+ appView.getTypeElementFactory().leastUpperBoundOfInterfacesTable.put(s1, s2, lub);
}
}
return lub;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
index 2b4ce3b..b9b357b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
@@ -436,7 +436,7 @@
DexType type, Nullability nullability, AppView<?> appView) {
assert type.isClassType();
return appView
- .dexItemFactory()
+ .getTypeElementFactory()
.createReferenceTypeElement(type, nullability, appView)
.asClassType();
}
@@ -455,7 +455,7 @@
if (type.isPrimitiveType()) {
return PrimitiveTypeElement.fromDexType(type, asArrayElementType);
}
- return appView.dexItemFactory().createReferenceTypeElement(type, nullability, appView);
+ return appView.getTypeElementFactory().createReferenceTypeElement(type, nullability, appView);
}
public boolean isValueTypeCompatible(TypeElement other) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElementFactory.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElementFactory.java
new file mode 100644
index 0000000..29690b0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElementFactory.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2025, 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.analysis.type;
+
+import static com.android.tools.r8.ir.analysis.type.ClassTypeElement.computeLeastUpperBoundOfInterfaces;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.LRUCacheTable;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class TypeElementFactory {
+
+ // ReferenceTypeElement canonicalization.
+ private final ConcurrentHashMap<DexType, ReferenceTypeElement> referenceTypes =
+ new ConcurrentHashMap<>();
+ private final ConcurrentHashMap<DexType, InterfaceCollection> classTypeInterfaces =
+ new ConcurrentHashMap<>();
+ public final LRUCacheTable<InterfaceCollection, InterfaceCollection, InterfaceCollection>
+ leastUpperBoundOfInterfacesTable = LRUCacheTable.create(8, 8);
+
+ public void clearTypeElementsCache() {
+ referenceTypes.clear();
+ classTypeInterfaces.clear();
+ leastUpperBoundOfInterfacesTable.clear();
+ }
+
+ public boolean verifyNoCachedTypeElements() {
+ assert referenceTypes.isEmpty();
+ assert classTypeInterfaces.isEmpty();
+ assert leastUpperBoundOfInterfacesTable.isEmpty();
+ return true;
+ }
+
+ public ReferenceTypeElement createReferenceTypeElement(
+ DexType type, Nullability nullability, AppView<?> appView) {
+ // Class case:
+ // If two concurrent threads will try to create the same class-type the concurrent hash map will
+ // synchronize on the type in .computeIfAbsent and only a single class type is created.
+ //
+ // Array case:
+ // Arrays will create a lattice element for its base type thus we take special care here.
+ // Multiple threads may race recursively to create a base type. We have two cases:
+ // (i) If base type is class type and the threads will race to create the class type but only a
+ // single one will be created (Class case).
+ // (ii) If base is ArrayLattice case we can use our induction hypothesis to get that only one
+ // element is created for us up to this case. Threads will now race to return from the
+ // latest recursive call and fight to get access to .computeIfAbsent to add the
+ // ArrayTypeElement but only one will enter. The property that only one
+ // ArrayTypeElement is created per level therefore holds inductively.
+ TypeElement memberType = null;
+ if (type.isArrayType()) {
+ ReferenceTypeElement existing = referenceTypes.get(type);
+ if (existing != null) {
+ return existing.getOrCreateVariant(nullability);
+ }
+ memberType =
+ TypeElement.fromDexType(
+ type.toArrayElementType(appView.dexItemFactory()),
+ Nullability.maybeNull(),
+ appView,
+ true);
+ }
+ TypeElement finalMemberType = memberType;
+ return referenceTypes
+ .computeIfAbsent(
+ type,
+ t -> {
+ if (type.isClassType()) {
+ if (!appView.enableWholeProgramOptimizations()) {
+ // Don't reason at the level of interfaces in D8.
+ return ClassTypeElement.createForD8(type, nullability);
+ }
+ assert appView.appInfo().hasClassHierarchy();
+ if (appView.isInterface(type).isTrue()) {
+ return ClassTypeElement.create(
+ appView.dexItemFactory().objectType,
+ nullability,
+ appView.withClassHierarchy(),
+ InterfaceCollection.singleton(type));
+ }
+ // In theory, `interfaces` is the least upper bound of implemented interfaces.
+ // It is expensive to walk through type hierarchy; collect implemented interfaces;
+ // and compute the least upper bound of two interface sets. Hence, lazy
+ // computations. Most likely during lattice join. See {@link
+ // ClassTypeElement#getInterfaces}.
+ return ClassTypeElement.create(type, nullability, appView.withClassHierarchy());
+ }
+ assert type.isArrayType();
+ return ArrayTypeElement.create(finalMemberType, nullability);
+ })
+ .getOrCreateVariant(nullability);
+ }
+
+ public InterfaceCollection getLeastUpperBoundOfImplementedInterfacesOrDefault(
+ DexType type, InterfaceCollection defaultValue) {
+ return classTypeInterfaces.getOrDefault(type, defaultValue);
+ }
+
+ public InterfaceCollection getOrComputeLeastUpperBoundOfImplementedInterfaces(
+ DexType type, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ return classTypeInterfaces.computeIfAbsent(
+ type,
+ t -> {
+ InterfaceCollection itfs = appView.appInfo().implementedInterfaces(t);
+ return computeLeastUpperBoundOfInterfaces(appView, itfs, itfs);
+ });
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfToLirConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/CfToLirConverter.java
index e60a217..e00941f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfToLirConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfToLirConverter.java
@@ -160,7 +160,7 @@
// Conversion to LIR via IR will allocate type elements.
// They are not needed after construction so remove them again.
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
}
private void processIdentifierNameStrings(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
index 22bb189..009008c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
@@ -104,7 +104,7 @@
executorService);
// Conversion to LIR via IR will allocate type elements.
// They are not needed after construction so remove them again.
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
}
public static void rewriteLirWithLens(
@@ -168,7 +168,7 @@
executorService);
// Clear the reference type cache after conversion to reduce memory pressure.
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
}
public static void rewriteLirMethodWithLens(
@@ -233,7 +233,7 @@
merger.end();
timing.end();
// Clear the reference type cache after conversion to reduce memory pressure.
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
// At this point all code has been mapped according to the graph lens.
assert appView.graphLens().isMemberRebindingIdentityLens()
&& appView.graphLens().asMemberRebindingIdentityLens().getPrevious() == appView.codeLens();
@@ -322,7 +322,7 @@
timing.end();
// Clear the reference type cache after conversion to reduce memory pressure.
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
}
private static void finalizeClasspathLirMethodToOutputFormat(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaring.java
index 878aa12..83e5c15 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaring.java
@@ -174,7 +174,7 @@
executorService);
timings.forEach(merger::add);
merger.end();
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
// Move the pending methods and mark them live and ready for tracing.
timing.begin("Postlude");
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeHoistingToSharedSyntheticSuperClass.java b/src/main/java/com/android/tools/r8/optimize/BridgeHoistingToSharedSyntheticSuperClass.java
index 70c47e9..1ce004a 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeHoistingToSharedSyntheticSuperClass.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeHoistingToSharedSyntheticSuperClass.java
@@ -84,7 +84,7 @@
updateArtProfiles(groups);
new BridgeHoisting(appView).run(executorService, timing);
}
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
}
/** Returns the set of (non-singleton) groups that have the same superclass. */
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 42adbc6..3824b63 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -387,7 +387,7 @@
MemberRebindingLens memberRebindingLens = lensBuilder.build();
appView.setGraphLens(memberRebindingLens);
eventConsumer.finished(appView, memberRebindingLens);
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
appView.notifyOptimizationFinishedForTesting();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index e807f0d..bd1b30f 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -447,7 +447,7 @@
DirectMappedDexApplication application, ExecutorService executorService)
throws ExecutionException {
// Clear the type elements cache due to redundant interface removal.
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
feedback.fixupOptimizationInfos(
@@ -468,7 +468,7 @@
});
// Verify that the fixup did not lead to the caching of any elements.
- assert appView.dexItemFactory().verifyNoCachedTypeElements();
+ assert appView.getTypeElementFactory().verifyNoCachedTypeElements();
}
private boolean verifyNoDeadFields(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
index 72ce1bc..a3b4137 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
@@ -156,7 +156,7 @@
// Finally update the code lens to signal that the code is fully up to date.
markRewrittenWithLens(executorService, timing);
- appView.dexItemFactory().clearTypeElementsCache();
+ appView.getTypeElementFactory().clearTypeElementsCache();
appView.notifyOptimizationFinishedForTesting();
}