Ensure API safe parameter types in instance initializer merger
Fixes: b/324383881
Fixes: b/331574594
Change-Id: I761c712671d5fe731b079a9f98e1b255d46aff96
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 e096d1e..42e9f77 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -259,15 +259,6 @@
defaultTypeRewriter(appInfo));
}
- public static <T extends AppInfo> AppView<T> createForSimulatingD8InR8(T appInfo) {
- return new AppView<>(
- appInfo,
- ArtProfileCollection.empty(),
- StartupProfile.empty(),
- WholeProgramOptimizations.OFF,
- defaultTypeRewriter(appInfo));
- }
-
public static AppView<AppInfoWithClassHierarchy> createForSimulatingR8InD8(
DirectMappedDexApplication application, MainDexInfo mainDexInfo) {
ClassToFeatureSplitMap classToFeatureSplitMap =
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java b/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
index 3250adb..30e2f54 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
@@ -12,14 +12,30 @@
public class DexTypeUtils {
+ public static DexType computeApiSafeLeastUpperBound(
+ AppView<? extends AppInfoWithClassHierarchy> appView, Iterable<DexType> types) {
+ DexType leastUpperBound = computeLeastUpperBound(appView, types);
+ return findApiSafeUpperBound(appView, leastUpperBound);
+ }
+
public static DexType computeLeastUpperBound(
AppView<? extends AppInfoWithClassHierarchy> appView, Iterable<DexType> types) {
TypeElement join =
TypeElement.join(Iterables.transform(types, type -> type.toTypeElement(appView)), appView);
- return findApiSafeUpperBound(appView, toDexType(appView, join));
+ return toDexType(appView, join);
}
- @SuppressWarnings("ReferenceEquality")
+ public static boolean isApiSafe(
+ AppView<? extends AppInfoWithClassHierarchy> appView, DexType type) {
+ DexType apiSafeUpperBound = findApiSafeUpperBound(appView, type);
+ return apiSafeUpperBound.isIdenticalTo(type);
+ }
+
+ public static boolean isLeastUpperBoundApiSafe(
+ AppView<? extends AppInfoWithClassHierarchy> appView, Iterable<DexType> types) {
+ return isApiSafe(appView, computeLeastUpperBound(appView, types));
+ }
+
public static DexType toDexType(
AppView<? extends AppInfoWithClassHierarchy> appView, TypeElement type) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
@@ -33,7 +49,7 @@
}
assert type.isClassType();
ClassTypeElement classType = type.asClassType();
- if (classType.getClassType() != dexItemFactory.objectType) {
+ if (classType.getClassType().isNotIdenticalTo(dexItemFactory.objectType)) {
return classType.getClassType();
}
if (classType.getInterfaces().hasSingleKnownInterface()) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
index 778132e..6c98445 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
@@ -189,7 +189,7 @@
DexEncodedField newField;
if (needsRelaxedType(targetField, sourceFields)) {
DexType newFieldType =
- DexTypeUtils.computeLeastUpperBound(
+ DexTypeUtils.computeApiSafeLeastUpperBound(
appView,
Iterables.transform(
Iterables.concat(IterableUtils.singleton(targetField), sourceFields),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 0db2480..3499c30 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -77,7 +77,6 @@
private ClassMerger(
AppView<?> appView,
- IRCodeProvider codeProvider,
HorizontalClassMergerGraphLens.Builder lensBuilder,
HorizontalMergeGroup group,
Collection<VirtualMethodMerger> virtualMethodMergers) {
@@ -93,8 +92,7 @@
// Method mergers.
this.classInitializerMerger = ClassInitializerMerger.create(group);
this.instanceInitializerMergers =
- InstanceInitializerMergerCollection.create(
- appView, classIdentifiers, codeProvider, group, lensBuilder);
+ InstanceInitializerMergerCollection.create(appView, classIdentifiers, group, lensBuilder);
this.virtualMethodMergers = virtualMethodMergers;
buildClassIdentifierMap();
@@ -365,14 +363,12 @@
public static class Builder {
private final AppView<?> appView;
- private final IRCodeProvider codeProvider;
private final HorizontalMergeGroup group;
private List<VirtualMethodMerger> virtualMethodMergers;
- public Builder(AppView<?> appView, IRCodeProvider codeProvider, HorizontalMergeGroup group) {
+ public Builder(AppView<?> appView, HorizontalMergeGroup group) {
this.appView = appView;
- this.codeProvider = codeProvider;
this.group = group;
}
@@ -464,7 +460,7 @@
public ClassMerger build(
HorizontalClassMergerGraphLens.Builder lensBuilder) {
- return new ClassMerger(appView, codeProvider, lensBuilder, group, virtualMethodMergers);
+ return new ClassMerger(appView, lensBuilder, group, virtualMethodMergers);
}
}
}
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 9307f89..51c1aca 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -20,8 +20,6 @@
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
import com.android.tools.r8.ir.conversion.LirConverter;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.naming.IdentifierMinifier;
import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
@@ -72,11 +70,7 @@
throws ExecutionException {
timing.begin("HorizontalClassMerger");
if (shouldRun()) {
- IRCodeProvider codeProvider =
- appView.hasClassHierarchy()
- ? IRCodeProvider.create(appView.withClassHierarchy(), this::getConversionOptions)
- : IRCodeProvider.createThrowing();
- run(runtimeTypeCheckInfo, codeProvider, executorService, timing);
+ run(runtimeTypeCheckInfo, executorService, timing);
assert ArtProfileCompletenessChecker.verify(appView);
@@ -95,13 +89,8 @@
&& !appView.hasCfByteCodePassThroughMethods();
}
- private MutableMethodConversionOptions getConversionOptions() {
- return MethodConversionOptions.forLirPhase(appView);
- }
-
private void run(
RuntimeTypeCheckInfo runtimeTypeCheckInfo,
- IRCodeProvider codeProvider,
ExecutorService executorService,
Timing timing)
throws ExecutionException {
@@ -128,7 +117,7 @@
new HorizontalClassMergerGraphLens.Builder();
// Determine which classes need a class id field.
- List<ClassMerger.Builder> classMergerBuilders = createClassMergerBuilders(codeProvider, groups);
+ List<ClassMerger.Builder> classMergerBuilders = createClassMergerBuilders(groups);
initializeClassIdFields(classMergerBuilders);
// Ensure that all allocations of classes that end up needing a class id use a constructor on
@@ -141,7 +130,7 @@
ProfileCollectionAdditions profileCollectionAdditions =
ProfileCollectionAdditions.create(appView);
SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder =
- SyntheticInitializerConverter.builder(appView, codeProvider);
+ SyntheticInitializerConverter.builder(appView);
List<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfos = new ArrayList<>();
PrunedItems prunedItems =
applyClassMergers(
@@ -184,7 +173,6 @@
// Set the new graph lens before finalizing any synthetic code.
appView.setGraphLens(horizontalClassMergerGraphLens);
- codeProvider.setGraphLens(horizontalClassMergerGraphLens);
// Must rewrite AppInfoWithLiveness before pruning the merged classes, to ensure that allocation
// sites, fields accesses, etc. are correctly transferred to the target classes.
@@ -378,13 +366,13 @@
}
private List<ClassMerger.Builder> createClassMergerBuilders(
- IRCodeProvider codeProvider, Collection<HorizontalMergeGroup> groups) {
+ Collection<HorizontalMergeGroup> groups) {
List<ClassMerger.Builder> classMergerBuilders = new ArrayList<>(groups.size());
for (HorizontalMergeGroup group : groups) {
assert group.isNonTrivial();
assert group.hasInstanceFieldMap();
assert group.hasTarget();
- classMergerBuilders.add(new ClassMerger.Builder(appView, codeProvider, group));
+ classMergerBuilders.add(new ClassMerger.Builder(appView, group));
}
return classMergerBuilders;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
deleted file mode 100644
index 95c4020..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// 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 com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.lens.GraphLens;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
-import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
-import java.util.function.Supplier;
-
-public interface IRCodeProvider {
-
- IRCode buildIR(ProgramMethod method);
-
- void setGraphLens(GraphLens graphLens);
-
- static IRCodeProvider create(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- Supplier<MutableMethodConversionOptions> getConversionOptions) {
- return new IRCodeProviderImpl(appView, getConversionOptions);
- }
-
- static IRCodeProvider createThrowing() {
- return new IRCodeProvider() {
- @Override
- public IRCode buildIR(ProgramMethod method) {
- throw new UnsupportedOperationException("Should never build IR for methods in D8");
- }
-
- @Override
- public void setGraphLens(GraphLens graphLens) {}
- };
- }
-
- class IRCodeProviderImpl implements IRCodeProvider {
-
- private final AppView<AppInfo> appViewForConversion;
- private Supplier<MutableMethodConversionOptions> getConversionOptions;
-
- private IRCodeProviderImpl(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- Supplier<MutableMethodConversionOptions> getConversionOptions) {
- // At this point the code rewritings described by repackaging and synthetic finalization have
- // not been applied to the code objects. These code rewritings will be applied in the
- // application writer. We therefore simulate that we are in D8, to allow building IR for each
- // of the class initializers without applying the unapplied code rewritings, to avoid that we
- // apply the lens more than once to the same piece of code.
- AppView<AppInfo> appViewForConversion =
- AppView.createForSimulatingD8InR8(
- AppInfo.createInitialAppInfo(
- appView.appInfo().app(), GlobalSyntheticsStrategy.forNonSynthesizing()));
- appViewForConversion.setGraphLens(appView.graphLens());
- appViewForConversion.setCodeLens(appView.codeLens());
- this.appViewForConversion = appViewForConversion;
- this.getConversionOptions = getConversionOptions;
- }
-
- @Override
- public IRCode buildIR(ProgramMethod method) {
- return method
- .getDefinition()
- .getCode()
- .buildIR(method, appViewForConversion, getConversionOptions.get());
- }
-
- @Override
- public void setGraphLens(GraphLens graphLens) {
- appViewForConversion.setGraphLens(graphLens);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
index 96aeb10..c7dfba3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
@@ -29,6 +29,7 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.Iterables;
@@ -39,7 +40,6 @@
public static InstanceInitializerDescription analyze(
AppView<? extends AppInfoWithClassHierarchy> appView,
- IRCodeProvider codeProvider,
HorizontalMergeGroup group,
InstanceInitializer instanceInitializer) {
if (instanceInitializer.isAbsent()) {
@@ -61,19 +61,17 @@
builder.addInvokeConstructor(invokedConstructor, invokedConstructorArguments);
return builder.build();
} else {
- return analyze(appView, codeProvider, group, instanceInitializer.asPresent().getMethod());
+ return analyze(appView, group, instanceInitializer.asPresent().getMethod());
}
}
- @SuppressWarnings("ReferenceEquality")
public static InstanceInitializerDescription analyze(
AppView<? extends AppInfoWithClassHierarchy> appView,
- IRCodeProvider codeProvider,
HorizontalMergeGroup group,
ProgramMethod instanceInitializer) {
InstanceInitializerDescription.Builder builder =
InstanceInitializerDescription.builder(appView, instanceInitializer);
- IRCode code = codeProvider.buildIR(instanceInitializer);
+ IRCode code = instanceInitializer.buildIR(appView, MethodConversionOptions.nonConverting());
GraphLens codeLens = instanceInitializer.getDefinition().getCode().getCodeLens(appView);
WorkList<BasicBlock> workList = WorkList.newIdentityWorkList(code.entryBlock());
while (workList.hasNext()) {
@@ -108,8 +106,9 @@
DexField fieldReference = instancePut.getField();
DexField lensRewrittenFieldReference =
appView.graphLens().lookupField(fieldReference, codeLens);
- if (lensRewrittenFieldReference.getHolderType()
- != instanceInitializer.getHolderType()) {
+ if (lensRewrittenFieldReference
+ .getHolderType()
+ .isNotIdenticalTo(instanceInitializer.getHolderType())) {
return invalid();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index 256971c..be78b39 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -101,16 +101,12 @@
DexType[] newParameters =
new DexType[representative.getParameters().size() + BooleanUtils.intValue(needsClassId)];
System.arraycopy(oldParameters, 0, newParameters, 0, oldParameters.length);
- for (int i = 0; i < oldParameters.length; i++) {
- final int parameterIndex = i;
- Set<DexType> parameterTypes =
- SetUtils.newIdentityHashSet(
- builder ->
- instanceInitializers.forEach(
- instanceInitializer ->
- builder.accept(instanceInitializer.getParameter(parameterIndex))));
+ for (int parameterIndex = 0; parameterIndex < oldParameters.length; parameterIndex++) {
+ Set<DexType> parameterTypes = getParameterTypes(instanceInitializers, parameterIndex);
if (parameterTypes.size() > 1) {
- newParameters[i] = DexTypeUtils.computeLeastUpperBound(appView, parameterTypes);
+ DexType leastUpperBound = DexTypeUtils.computeLeastUpperBound(appView, parameterTypes);
+ assert DexTypeUtils.isApiSafe(appView, leastUpperBound);
+ newParameters[parameterIndex] = leastUpperBound;
}
}
if (needsClassId) {
@@ -120,6 +116,15 @@
return dexItemFactory.createInstanceInitializer(group.getTarget().getType(), newParameters);
}
+ private static Set<DexType> getParameterTypes(
+ List<ProgramMethod> instanceInitializers, int parameterIndex) {
+ return SetUtils.newIdentityHashSet(
+ builder ->
+ instanceInitializers.forEach(
+ instanceInitializer ->
+ builder.accept(instanceInitializer.getParameter(parameterIndex))));
+ }
+
/**
* Returns a special original method signature for the synthesized constructor that did not exist
* prior to horizontal class merging. Otherwise we might accidentally think that the synthesized
@@ -169,9 +174,11 @@
createNewGroup();
}
- private void createNewGroup() {
+ private List<ProgramMethod> createNewGroup() {
estimatedDexCodeSize = 0;
- instanceInitializerGroups.add(new ArrayList<>());
+ List<ProgramMethod> newGroup = new ArrayList<>();
+ instanceInitializerGroups.add(newGroup);
+ return newGroup;
}
public Builder add(ProgramMethod instanceInitializer) {
@@ -190,10 +197,40 @@
}
public Builder addEquivalent(ProgramMethod instanceInitializer) {
- ListUtils.last(instanceInitializerGroups).add(instanceInitializer);
+ // If adding the given constructor to the current merge group leads to any API unsafe
+ // parameter types, then the constructor should be merged into a new group.
+ List<ProgramMethod> eligibleGroup = null;
+ for (List<ProgramMethod> candidateGroup : instanceInitializerGroups) {
+ if (isMergeApiSafe(candidateGroup, instanceInitializer)) {
+ eligibleGroup = candidateGroup;
+ break;
+ }
+ }
+ if (eligibleGroup == null) {
+ eligibleGroup = createNewGroup();
+ }
+ eligibleGroup.add(instanceInitializer);
return this;
}
+ private boolean isMergeApiSafe(List<ProgramMethod> group, ProgramMethod instanceInitializer) {
+ if (group.isEmpty()) {
+ return true;
+ }
+ for (int parameterIndex = 0;
+ parameterIndex < instanceInitializer.getParameters().size();
+ parameterIndex++) {
+ Set<DexType> parameterTypes = getParameterTypes(group, parameterIndex);
+ // Adding the given instance initializer to the group can only lead to an API unsafe
+ // parameter type if the instance initializer contributes a new parameter type to the group.
+ if (parameterTypes.add(instanceInitializer.getParameter(parameterIndex))
+ && !DexTypeUtils.isLeastUpperBoundApiSafe(appView, parameterTypes)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
public List<InstanceInitializerMerger> build(HorizontalMergeGroup group) {
assert instanceInitializerGroups.stream().noneMatch(List::isEmpty);
return ListUtils.map(
@@ -203,18 +240,19 @@
appView, classIdentifiers, group, instanceInitializers, lensBuilder));
}
- public InstanceInitializerMerger buildSingle(
+ public List<InstanceInitializerMerger> buildEquivalent(
HorizontalMergeGroup group, InstanceInitializerDescription instanceInitializerDescription) {
assert instanceInitializerGroups.stream().noneMatch(List::isEmpty);
- assert instanceInitializerGroups.size() == 1;
- List<ProgramMethod> instanceInitializers = ListUtils.first(instanceInitializerGroups);
- return new InstanceInitializerMerger(
- appView,
- classIdentifiers,
- group,
- instanceInitializers,
- lensBuilder,
- instanceInitializerDescription);
+ return ListUtils.map(
+ instanceInitializerGroups,
+ instanceInitializers ->
+ new InstanceInitializerMerger(
+ appView,
+ classIdentifiers,
+ group,
+ instanceInitializers,
+ lensBuilder,
+ instanceInitializerDescription));
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
index e0088ec..5b9e401 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
@@ -40,7 +40,6 @@
public static InstanceInitializerMergerCollection create(
AppView<?> appView,
Reference2IntMap<DexType> classIdentifiers,
- IRCodeProvider codeProvider,
HorizontalMergeGroup group,
HorizontalClassMergerGraphLens.Builder lensBuilder) {
if (!appView.hasClassHierarchy()) {
@@ -61,7 +60,7 @@
instanceInitializer -> {
InstanceInitializerDescription description =
InstanceInitializerAnalysis.analyze(
- appViewWithClassHierarchy, codeProvider, group, instanceInitializer);
+ appViewWithClassHierarchy, group, instanceInitializer);
if (description != null) {
buildersByDescription
.computeIfAbsent(
@@ -80,14 +79,17 @@
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);
+ List<InstanceInitializerMerger> instanceInitializerMergers =
+ builder.buildEquivalent(group, description);
+ for (InstanceInitializerMerger instanceInitializerMerger : instanceInitializerMergers) {
+ 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);
+ }
}
});
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 55a073a..85ad211 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
@@ -4,14 +4,12 @@
package com.android.tools.r8.horizontalclassmerging.code;
-import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.horizontalclassmerging.IRCodeProvider;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
-import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import java.util.ArrayList;
@@ -26,25 +24,22 @@
public class SyntheticInitializerConverter {
private final AppView<?> appView;
- private final IRCodeProvider codeProvider;
private final List<ProgramMethod> classInitializers;
- private SyntheticInitializerConverter(
- AppView<?> appView, IRCodeProvider codeProvider, List<ProgramMethod> classInitializers) {
+ private SyntheticInitializerConverter(AppView<?> appView, List<ProgramMethod> classInitializers) {
this.appView = appView;
- this.codeProvider = codeProvider;
this.classInitializers = classInitializers;
}
- public static Builder builder(AppView<?> appView, IRCodeProvider codeProvider) {
- return new Builder(appView, codeProvider);
+ public static Builder builder(AppView<?> appView) {
+ return new Builder(appView);
}
public void convertClassInitializers(ExecutorService executorService) throws ExecutionException {
if (!classInitializers.isEmpty()) {
assert appView.dexItemFactory().verifyNoCachedTypeElements();
- IRConverter converter = new IRConverter(createAppViewForConversion());
+ IRConverter converter = new IRConverter(appView);
ThreadUtils.processItems(
classInitializers,
method -> processMethod(method, converter),
@@ -54,30 +49,8 @@
}
}
- private AppView<AppInfo> createAppViewForConversion() {
- assert appView.enableWholeProgramOptimizations();
- assert appView.hasClassHierarchy();
-
- // At this point the code rewritings described by repackaging and synthetic finalization have
- // not been applied to the code objects. These code rewritings will be applied in the
- // application writer. We therefore simulate that we are in D8, to allow building IR for each of
- // the class initializers without applying the unapplied code rewritings, to avoid that we apply
- // the lens more than once to the same piece of code.
-
- // Since we are now running in D8 mode clear type elements cache.
- appView.dexItemFactory().clearTypeElementsCache();
-
- AppView<AppInfo> appViewForConversion =
- AppView.createForSimulatingD8InR8(
- AppInfo.createInitialAppInfo(
- appView.appInfo().app(), GlobalSyntheticsStrategy.forNonSynthesizing()));
- appViewForConversion.setGraphLens(appView.graphLens());
- appViewForConversion.setCodeLens(appView.codeLens());
- return appViewForConversion;
- }
-
private void processMethod(ProgramMethod method, IRConverter converter) {
- IRCode code = codeProvider.buildIR(method);
+ IRCode code = method.buildIR(appView, MethodConversionOptions.forLirPhase(appView));
converter.removeDeadCodeAndFinalizeIR(
code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
}
@@ -89,13 +62,11 @@
public static class Builder {
private final AppView<?> appView;
- private final IRCodeProvider codeProvider;
private final List<ProgramMethod> classInitializers = new ArrayList<>();
- private Builder(AppView<?> appView, IRCodeProvider codeProvider) {
+ private Builder(AppView<?> appView) {
this.appView = appView;
- this.codeProvider = codeProvider;
}
public Builder addClassInitializer(ProgramMethod method) {
@@ -104,7 +75,7 @@
}
public SyntheticInitializerConverter build() {
- return new SyntheticInitializerConverter(appView, codeProvider, classInitializers);
+ return new SyntheticInitializerConverter(appView, classInitializers);
}
}
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClInitMergeSuperTypeApiLevelTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClInitMergeSuperTypeApiLevelTest.java
index 164755b..f0de7ae 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClInitMergeSuperTypeApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClInitMergeSuperTypeApiLevelTest.java
@@ -6,10 +6,12 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -39,11 +41,14 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ private boolean canUseExecutable() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O);
+ }
+
private TypeReference getMergeReferenceForApiLevel() {
- boolean canUseExecutable =
- parameters.isDexRuntime()
- && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O);
- return Reference.typeFromTypeName(typeName(canUseExecutable ? Executable.class : Object.class));
+ return Reference.typeFromTypeName(
+ typeName(canUseExecutable() ? Executable.class : Object.class));
}
@Test
@@ -59,17 +64,34 @@
inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
.compile()
.inspect(
inspector -> {
ClassSubject clazz = inspector.clazz(A.class);
assertThat(clazz, isPresent());
- TypeReference mergeTypeRef = getMergeReferenceForApiLevel();
- MethodSubject init = clazz.init(mergeTypeRef.getTypeName(), "int");
- assertThat(init, isPresent());
+
assertTrue(
clazz.allFields().stream()
- .anyMatch(f -> mergeTypeRef.equals(f.getFinalReference().getFieldType())));
+ .anyMatch(
+ f ->
+ f.getFinalReference()
+ .getFieldType()
+ .equals(getMergeReferenceForApiLevel())));
+
+ assertEquals(
+ canUseExecutable() ? 1 : 2,
+ clazz.allMethods(MethodSubject::isInstanceInitializer).size());
+ if (canUseExecutable()) {
+ MethodSubject constructorInit = clazz.init(Executable.class.getTypeName(), "int");
+ assertThat(constructorInit, isPresent());
+ } else {
+ MethodSubject constructorInit = clazz.init(Constructor.class.getTypeName());
+ assertThat(constructorInit, isPresent());
+
+ MethodSubject methodInit = clazz.init(Method.class.getTypeName());
+ assertThat(methodInit, isPresent());
+ }
})
.run(parameters.getRuntime(), Main.class)
// The test succeeds for some unknown reason.
@@ -92,6 +114,7 @@
}
}
+ @NoVerticalClassMerging
public abstract static class Factory {
abstract Object newInstance() throws Exception;
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentInstanceInitializerMergingWithApiUnsafeParameterTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentInstanceInitializerMergingWithApiUnsafeParameterTest.java
index e2b7710..dac6de6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentInstanceInitializerMergingWithApiUnsafeParameterTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentInstanceInitializerMergingWithApiUnsafeParameterTest.java
@@ -7,9 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoFieldTypeStrengthening;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -43,29 +41,20 @@
.addHorizontallyMergedClassesInspector(
inspector ->
inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
- .enableInliningAnnotations()
.enableNoFieldTypeStrengtheningAnnotations()
.setMinApi(parameters)
.compile()
.inspect(
inspector -> {
- // Verify that the two constructors A.<init> and B.<init> have been merged.
+ // Verify that the two constructors A.<init> and B.<init> have not been merged.
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
assertEquals(
- 1, aClassSubject.allMethods(MethodSubject::isInstanceInitializer).size());
-
- MethodSubject instanceInitializer = aClassSubject.uniqueInstanceInitializer();
- assertThat(instanceInitializer, isPresent());
- assertTrue(
- instanceInitializer
- .streamInstructions()
- .noneMatch(i -> i.isIf() || i.isSwitch()));
+ 2, aClassSubject.allMethods(MethodSubject::isInstanceInitializer).size());
})
.addRunClasspathClasses(LibraryClassBase.class, LibraryClass.class)
.run(parameters.getRuntime(), Main.class)
- // TODO(b/331574594): Should succeed with "LibraryClass", "MyLibraryClass".
- .assertFailureWithErrorThatThrows(VerifyError.class);
+ .assertSuccessWithOutputLines("LibraryClass", "MyLibraryClass");
}
static class LibraryClassBase {}
@@ -101,7 +90,6 @@
@NoFieldTypeStrengthening LibraryClassBase f;
- @NeverInline
A(LibraryClass c) {
super(c);
f = c;
@@ -118,7 +106,6 @@
@NoFieldTypeStrengthening LibraryClassBase f;
- @NeverInline
B(MyLibraryClass d) {
super(d);
f = d;