blob: 0ff172c661e3739aecf2361c40413c066e9d09dd [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.horizontalclassmerging.policies;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* Identifies when instance initializer merging is required and bails out. This is needed to ensure
* that we don't need to append extra null arguments at constructor call sites, such that the result
* of the final round of class merging can be described as a renaming only.
*/
public class NoInstanceInitializerMerging extends MultiClassPolicy {
public NoInstanceInitializerMerging(Mode mode) {
assert mode.isFinal();
}
@Override
public Collection<MergeGroup> apply(MergeGroup group) {
Map<MergeGroup, Map<DexMethodSignature, ProgramMethod>> newGroups = new LinkedHashMap<>();
for (DexProgramClass clazz : group) {
Map<DexMethodSignature, ProgramMethod> classSignatures = new HashMap<>();
clazz.forEachProgramInstanceInitializer(
method -> classSignatures.put(method.getMethodSignature(), method));
MergeGroup newGroup = null;
for (Entry<MergeGroup, Map<DexMethodSignature, ProgramMethod>> entry : newGroups.entrySet()) {
Map<DexMethodSignature, ProgramMethod> groupSignatures = entry.getValue();
if (canAddClassToGroup(classSignatures.values(), groupSignatures)) {
newGroup = entry.getKey();
groupSignatures.putAll(classSignatures);
break;
}
}
if (newGroup != null) {
newGroup.add(clazz);
} else {
newGroups.put(new MergeGroup(clazz), classSignatures);
}
}
return removeTrivialGroups(newGroups.keySet());
}
private boolean canAddClassToGroup(
Iterable<ProgramMethod> classMethods,
Map<DexMethodSignature, ProgramMethod> groupSignatures) {
for (ProgramMethod classMethod : classMethods) {
ProgramMethod groupMethod = groupSignatures.get(classMethod.getMethodSignature());
if (groupMethod != null && !equivalent(classMethod, groupMethod)) {
return false;
}
}
return true;
}
// For now, only recognize constructors with 0 parameters that call the same parent constructor.
private boolean equivalent(ProgramMethod method, ProgramMethod other) {
if (!method.getProto().getParameters().isEmpty()) {
return false;
}
MethodOptimizationInfo optimizationInfo = method.getDefinition().getOptimizationInfo();
InstanceInitializerInfo instanceInitializerInfo =
optimizationInfo.getContextInsensitiveInstanceInitializerInfo();
if (instanceInitializerInfo.isDefaultInstanceInitializerInfo()) {
return false;
}
InstanceInitializerInfo otherInstanceInitializerInfo =
other.getDefinition().getOptimizationInfo().getContextInsensitiveInstanceInitializerInfo();
assert otherInstanceInitializerInfo.isNonTrivialInstanceInitializerInfo();
if (!instanceInitializerInfo.hasParent()
|| instanceInitializerInfo.getParent().getArity() > 0) {
return false;
}
if (instanceInitializerInfo.getParent() != otherInstanceInitializerInfo.getParent()) {
return false;
}
return !method.getDefinition().getOptimizationInfo().mayHaveSideEffects()
&& !other.getDefinition().getOptimizationInfo().mayHaveSideEffects();
}
@Override
public String getName() {
return "NoInstanceInitializerMerging";
}
}