Reland "More aggressive redundant field load elimination for static final fields"
This reverts commit 1a50e3398c8f1cc19e26eb1ee6c488fe7afd977d.
Change-Id: If335861a634b64684cae45689aa0b1660bb8d50c
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 e504e54..20216c4 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -17,7 +17,7 @@
import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
import com.android.tools.r8.ir.optimize.CallSiteOptimizationInfoPropagator;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
-import com.android.tools.r8.ir.optimize.library.LibraryMethodOptimizer;
+import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.LibraryModeledPredicate;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
@@ -57,7 +57,7 @@
// Optimizations.
private final CallSiteOptimizationInfoPropagator callSiteOptimizationInfoPropagator;
- private final LibraryMethodOptimizer libraryMethodOptimizer;
+ private final LibraryMemberOptimizer libraryMemberOptimizer;
private final ProtoShrinker protoShrinker;
// Optimization results.
@@ -102,7 +102,7 @@
this.callSiteOptimizationInfoPropagator = null;
}
- this.libraryMethodOptimizer = new LibraryMethodOptimizer(this);
+ this.libraryMemberOptimizer = new LibraryMemberOptimizer(this);
if (enableWholeProgramOptimizations() && options.protoShrinking().isProtoShrinkingEnabled()) {
this.protoShrinker = new ProtoShrinker(withLiveness());
@@ -113,7 +113,7 @@
@Override
public boolean isModeled(DexType type) {
- return libraryMethodOptimizer.isModeled(type);
+ return libraryMemberOptimizer.isModeled(type);
}
public static <T extends AppInfo> AppView<T> createForD8(T appInfo, InternalOptions options) {
@@ -247,8 +247,8 @@
return callSiteOptimizationInfoPropagator;
}
- public LibraryMethodOptimizer libraryMethodOptimizer() {
- return libraryMethodOptimizer;
+ public LibraryMemberOptimizer libraryMethodOptimizer() {
+ return libraryMemberOptimizer;
}
public ProtoShrinker protoShrinker() {
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 b0a86c6..b2d7140 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -398,12 +398,34 @@
public final DexType reflectiveOperationExceptionType =
createStaticallyKnownType(reflectiveOperationExceptionDescriptor);
+ public final DexType javaIoFileType = createStaticallyKnownType("Ljava/io/File;");
+ public final DexType javaMathBigIntegerType = createStaticallyKnownType("Ljava/math/BigInteger;");
+ public final DexType javaNioByteOrderType = createStaticallyKnownType("Ljava/nio/ByteOrder;");
+ public final DexType javaUtilCollectionsType =
+ createStaticallyKnownType("Ljava/util/Collections;");
+ public final DexType javaUtilComparatorType = createStaticallyKnownType("Ljava/util/Comparator;");
+ public final DexType javaUtilConcurrentTimeUnitType =
+ createStaticallyKnownType("Ljava/util/concurrent/TimeUnit;");
+ public final DexType javaUtilListType = createStaticallyKnownType("Ljava/util/List;");
+ public final DexType javaUtilLocaleType = createStaticallyKnownType("Ljava/util/Locale;");
+ public final DexType javaUtilLoggingLevelType =
+ createStaticallyKnownType("Ljava/util/logging/Level;");
public final DexType javaUtilLoggingLoggerType =
createStaticallyKnownType("Ljava/util/logging/Logger;");
- public final DexType androidUtilLogType = createStaticallyKnownType("Landroid/util/Log;");
+ public final DexType javaUtilSetType = createStaticallyKnownType("Ljava/util/Set;");
+ public final DexType androidOsBuildType = createStaticallyKnownType("Landroid/os/Build;");
public final DexType androidOsBuildVersionType =
createStaticallyKnownType("Landroid/os/Build$VERSION;");
+ public final DexType androidOsBundleType = createStaticallyKnownType("Landroid/os/Bundle;");
+ public final DexType androidOsParcelableCreatorType =
+ createStaticallyKnownType("Landroid/os/Parcelable$Creator;");
+ public final DexType androidSystemOsConstantsType =
+ createStaticallyKnownType("Landroid/system/OsConstants;");
+ public final DexType androidUtilLogType = createStaticallyKnownType("Landroid/util/Log;");
+ public final DexType androidUtilPropertyType =
+ createStaticallyKnownType("Landroid/util/Property;");
+ public final DexType androidViewViewType = createStaticallyKnownType("Landroid/view/View;");
public final DexString nestConstructorDescriptor =
createString("L" + NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME + ";");
@@ -419,12 +441,13 @@
public final StringBuildingMethods stringBufferMethods =
new StringBuildingMethods(stringBufferType);
public final BooleanMembers booleanMembers = new BooleanMembers();
+ public final FloatMembers floatMembers = new FloatMembers();
+ public final IntegerMembers integerMembers = new IntegerMembers();
public final ObjectsMethods objectsMethods = new ObjectsMethods();
public final ObjectMembers objectMembers = new ObjectMembers();
- public final StringMethods stringMethods = new StringMethods();
- public final LongMethods longMethods = new LongMethods();
+ public final StringMembers stringMembers = new StringMembers();
+ public final LongMembers longMembers = new LongMembers();
public final DoubleMethods doubleMethods = new DoubleMethods();
- public final JavaUtilArraysMethods utilArraysMethods = new JavaUtilArraysMethods();
public final ThrowableMethods throwableMethods = new ThrowableMethods();
public final AssertionErrorMethods assertionErrorMethods = new AssertionErrorMethods();
public final ClassMethods classMethods = new ClassMethods();
@@ -441,6 +464,51 @@
public final PolymorphicMethods polymorphicMethods = new PolymorphicMethods();
public final ProxyMethods proxyMethods = new ProxyMethods();
+ // android.**
+ public final AndroidOsBuildMembers androidOsBuildMembers = new AndroidOsBuildMembers();
+ public final AndroidOsBuildVersionMembers androidOsBuildVersionMembers =
+ new AndroidOsBuildVersionMembers();
+ public final AndroidOsBundleMembers androidOsBundleMembers = new AndroidOsBundleMembers();
+ public final AndroidSystemOsConstantsMembers androidSystemOsConstantsMembers =
+ new AndroidSystemOsConstantsMembers();
+ public final AndroidViewViewMembers androidViewViewMembers = new AndroidViewViewMembers();
+
+ // java.**
+ public final JavaIoFileMembers javaIoFileMembers = new JavaIoFileMembers();
+ public final JavaMathBigIntegerMembers javaMathBigIntegerMembers =
+ new JavaMathBigIntegerMembers();
+ public final JavaNioByteOrderMembers javaNioByteOrderMembers = new JavaNioByteOrderMembers();
+ public final JavaUtilArraysMethods javaUtilArraysMethods = new JavaUtilArraysMethods();
+ public final JavaUtilComparatorMembers javaUtilComparatorMembers =
+ new JavaUtilComparatorMembers();
+ public final JavaUtilConcurrentTimeUnitMembers javaUtilConcurrentTimeUnitMembers =
+ new JavaUtilConcurrentTimeUnitMembers();
+ public final JavaUtilLocaleMembers javaUtilLocaleMembers = new JavaUtilLocaleMembers();
+ public final JavaUtilLoggingLevelMembers javaUtilLoggingLevelMembers =
+ new JavaUtilLoggingLevelMembers();
+
+ public final List<LibraryMembers> libraryMembersCollection =
+ ImmutableList.of(
+ booleanMembers,
+ floatMembers,
+ integerMembers,
+ longMembers,
+ stringMembers,
+ // android.**
+ androidOsBuildMembers,
+ androidOsBuildVersionMembers,
+ androidOsBundleMembers,
+ androidSystemOsConstantsMembers,
+ androidViewViewMembers,
+ // java.**
+ javaIoFileMembers,
+ javaMathBigIntegerMembers,
+ javaNioByteOrderMembers,
+ javaUtilComparatorMembers,
+ javaUtilConcurrentTimeUnitMembers,
+ javaUtilLocaleMembers,
+ javaUtilLoggingLevelMembers);
+
public final DexString twrCloseResourceMethodName = createString("$closeResource");
public final DexProto twrCloseResourceMethodProto =
createProto(voidType, throwableType, autoCloseableType);
@@ -604,7 +672,7 @@
objectsMethods.requireNonNull,
objectsMethods.requireNonNullWithMessage,
objectsMethods.requireNonNullWithMessageSupplier,
- stringMethods.valueOf);
+ stringMembers.valueOf);
// We assume library methods listed here are `public`, i.e., free from visibility side effects.
// If not, that library method should not be added here because it literally has side effects.
@@ -672,7 +740,12 @@
return dexMethod == metafactoryMethod || dexMethod == metafactoryAltMethod;
}
- public class BooleanMembers {
+ public interface LibraryMembers {
+
+ void forEachFinalField(Consumer<DexField> consumer);
+ }
+
+ public class BooleanMembers implements LibraryMembers {
public final DexField FALSE = createField(boxedBooleanType, boxedBooleanType, "FALSE");
public final DexField TRUE = createField(boxedBooleanType, boxedBooleanType, "TRUE");
@@ -684,16 +757,291 @@
createMethod(boxedBooleanType, createProto(boxedBooleanType, booleanType), "valueOf");
private BooleanMembers() {}
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(FALSE);
+ consumer.accept(TRUE);
+ consumer.accept(TYPE);
+ }
}
- public class LongMethods {
+ public class AndroidOsBuildMembers implements LibraryMembers {
+
+ public final DexField BOOTLOADER = createField(androidOsBuildType, stringType, "BOOTLOADER");
+ public final DexField BRAND = createField(androidOsBuildType, stringType, "BRAND");
+ public final DexField CPU_ABI = createField(androidOsBuildType, stringType, "CPU_ABI");
+ public final DexField CPU_ABI2 = createField(androidOsBuildType, stringType, "CPU_ABI2");
+ public final DexField DEVICE = createField(androidOsBuildType, stringType, "DEVICE");
+ public final DexField DISPLAY = createField(androidOsBuildType, stringType, "DISPLAY");
+ public final DexField FINGERPRINT = createField(androidOsBuildType, stringType, "FINGERPRINT");
+ public final DexField HARDWARE = createField(androidOsBuildType, stringType, "HARDWARE");
+ public final DexField MANUFACTURER =
+ createField(androidOsBuildType, stringType, "MANUFACTURER");
+ public final DexField MODEL = createField(androidOsBuildType, stringType, "MODEL");
+ public final DexField PRODUCT = createField(androidOsBuildType, stringType, "PRODUCT");
+ public final DexField SERIAL = createField(androidOsBuildType, stringType, "SERIAL");
+ public final DexField SUPPORTED_32_BIT_ABIS =
+ createField(androidOsBuildType, stringArrayType, "SUPPORTED_32_BIT_ABIS");
+ public final DexField SUPPORTED_64_BIT_ABIS =
+ createField(androidOsBuildType, stringArrayType, "SUPPORTED_64_BIT_ABIS");
+ public final DexField SUPPORTED_ABIS =
+ createField(androidOsBuildType, stringArrayType, "SUPPORTED_ABIS");
+ public final DexField TIME = createField(androidOsBuildType, longType, "TIME");
+ public final DexField TYPE = createField(androidOsBuildType, stringType, "TYPE");
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(BOOTLOADER);
+ consumer.accept(BRAND);
+ consumer.accept(CPU_ABI);
+ consumer.accept(CPU_ABI2);
+ consumer.accept(DEVICE);
+ consumer.accept(DISPLAY);
+ consumer.accept(FINGERPRINT);
+ consumer.accept(HARDWARE);
+ consumer.accept(MANUFACTURER);
+ consumer.accept(MODEL);
+ consumer.accept(PRODUCT);
+ consumer.accept(SERIAL);
+ consumer.accept(SUPPORTED_32_BIT_ABIS);
+ consumer.accept(SUPPORTED_64_BIT_ABIS);
+ consumer.accept(SUPPORTED_ABIS);
+ consumer.accept(TIME);
+ consumer.accept(TYPE);
+ }
+ }
+
+ public class AndroidOsBuildVersionMembers implements LibraryMembers {
+
+ public final DexField CODENAME = createField(androidOsBuildVersionType, stringType, "CODENAME");
+ public final DexField RELEASE = createField(androidOsBuildVersionType, stringType, "RELEASE");
+ public final DexField SDK = createField(androidOsBuildVersionType, stringType, "SDK");
+ public final DexField SDK_INT = createField(androidOsBuildVersionType, intType, "SDK_INT");
+ public final DexField SECURITY_PATCH =
+ createField(androidOsBuildVersionType, stringType, "SECURITY_PATCH");
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(CODENAME);
+ consumer.accept(RELEASE);
+ consumer.accept(SDK);
+ consumer.accept(SDK_INT);
+ consumer.accept(SECURITY_PATCH);
+ }
+ }
+
+ public class AndroidOsBundleMembers implements LibraryMembers {
+
+ public final DexField CREATOR =
+ createField(androidOsBundleType, androidOsParcelableCreatorType, "CREATOR");
+ public final DexField EMPTY = createField(androidOsBundleType, androidOsBundleType, "EMPTY");
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(CREATOR);
+ consumer.accept(EMPTY);
+ }
+ }
+
+ public class AndroidSystemOsConstantsMembers implements LibraryMembers {
+
+ public final DexField S_IRUSR = createField(androidSystemOsConstantsType, intType, "S_IRUSR");
+ public final DexField S_IXUSR = createField(androidSystemOsConstantsType, intType, "S_IXUSR");
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(S_IRUSR);
+ consumer.accept(S_IXUSR);
+ }
+ }
+
+ public class AndroidViewViewMembers implements LibraryMembers {
+
+ public final DexField TRANSLATION_Z =
+ createField(androidViewViewType, androidUtilPropertyType, "TRANSLATION_Z");
+ public final DexField EMPTY_STATE_SET =
+ createField(androidViewViewType, intArrayType, "EMPTY_STATE_SET");
+ public final DexField ENABLED_STATE_SET =
+ createField(androidViewViewType, intArrayType, "ENABLED_STATE_SET");
+ public final DexField PRESSED_ENABLED_STATE_SET =
+ createField(androidViewViewType, intArrayType, "PRESSED_ENABLED_STATE_SET");
+ public final DexField SELECTED_STATE_SET =
+ createField(androidViewViewType, intArrayType, "SELECTED_STATE_SET");
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(TRANSLATION_Z);
+ consumer.accept(EMPTY_STATE_SET);
+ consumer.accept(ENABLED_STATE_SET);
+ consumer.accept(PRESSED_ENABLED_STATE_SET);
+ consumer.accept(SELECTED_STATE_SET);
+ }
+ }
+
+ public class FloatMembers implements LibraryMembers {
+
+ public final DexField TYPE = createField(boxedFloatType, classType, "TYPE");
+
+ private FloatMembers() {}
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(TYPE);
+ }
+ }
+
+ public class JavaIoFileMembers implements LibraryMembers {
+
+ public final DexField pathSeparator = createField(javaIoFileType, stringType, "pathSeparator");
+ public final DexField separator = createField(javaIoFileType, stringType, "separator");
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(pathSeparator);
+ consumer.accept(separator);
+ }
+ }
+
+ public class JavaMathBigIntegerMembers implements LibraryMembers {
+
+ public final DexField ONE = createField(javaMathBigIntegerType, javaMathBigIntegerType, "ONE");
+ public final DexField ZERO =
+ createField(javaMathBigIntegerType, javaMathBigIntegerType, "ZERO");
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(ONE);
+ consumer.accept(ZERO);
+ }
+ }
+
+ public class JavaNioByteOrderMembers implements LibraryMembers {
+
+ public final DexField LITTLE_ENDIAN =
+ createField(javaNioByteOrderType, javaNioByteOrderType, "LITTLE_ENDIAN");
+ public final DexField BIG_ENDIAN =
+ createField(javaNioByteOrderType, javaNioByteOrderType, "BIG_ENDIAN");
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(LITTLE_ENDIAN);
+ consumer.accept(BIG_ENDIAN);
+ }
+ }
+
+ public class JavaUtilArraysMethods {
+
+ public final DexMethod asList;
+
+ private JavaUtilArraysMethods() {
+ asList =
+ createMethod(
+ arraysDescriptor,
+ createString("asList"),
+ listDescriptor,
+ new DexString[] {objectArrayDescriptor});
+ }
+ }
+
+ public class JavaUtilComparatorMembers implements LibraryMembers {
+
+ public final DexField EMPTY_LIST =
+ createField(javaUtilCollectionsType, javaUtilListType, "EMPTY_LIST");
+ public final DexField EMPTY_SET =
+ createField(javaUtilCollectionsType, javaUtilSetType, "EMPTY_SET");
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(EMPTY_LIST);
+ consumer.accept(EMPTY_SET);
+ }
+ }
+
+ public class JavaUtilConcurrentTimeUnitMembers implements LibraryMembers {
+
+ public final DexField DAYS =
+ createField(javaUtilConcurrentTimeUnitType, javaUtilConcurrentTimeUnitType, "DAYS");
+ public final DexField HOURS =
+ createField(javaUtilConcurrentTimeUnitType, javaUtilConcurrentTimeUnitType, "HOURS");
+ public final DexField MICROSECONDS =
+ createField(javaUtilConcurrentTimeUnitType, javaUtilConcurrentTimeUnitType, "MICROSECONDS");
+ public final DexField MILLISECONDS =
+ createField(javaUtilConcurrentTimeUnitType, javaUtilConcurrentTimeUnitType, "MILLISECONDS");
+ public final DexField MINUTES =
+ createField(javaUtilConcurrentTimeUnitType, javaUtilConcurrentTimeUnitType, "MINUTES");
+ public final DexField NANOSECONDS =
+ createField(javaUtilConcurrentTimeUnitType, javaUtilConcurrentTimeUnitType, "NANOSECONDS");
+ public final DexField SECONDS =
+ createField(javaUtilConcurrentTimeUnitType, javaUtilConcurrentTimeUnitType, "SECONDS");
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(DAYS);
+ consumer.accept(HOURS);
+ consumer.accept(MICROSECONDS);
+ consumer.accept(MILLISECONDS);
+ consumer.accept(MINUTES);
+ consumer.accept(NANOSECONDS);
+ consumer.accept(SECONDS);
+ }
+ }
+
+ public class JavaUtilLocaleMembers implements LibraryMembers {
+
+ public final DexField ENGLISH = createField(javaUtilLocaleType, javaUtilLocaleType, "ENGLISH");
+ public final DexField ROOT = createField(javaUtilLocaleType, javaUtilLocaleType, "ROOT");
+ public final DexField US = createField(javaUtilLocaleType, javaUtilLocaleType, "US");
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(ENGLISH);
+ consumer.accept(ROOT);
+ consumer.accept(US);
+ }
+ }
+
+ public class JavaUtilLoggingLevelMembers implements LibraryMembers {
+
+ public final DexField CONFIG =
+ createField(javaUtilLoggingLevelType, javaUtilLoggingLevelType, "CONFIG");
+ public final DexField FINE =
+ createField(javaUtilLoggingLevelType, javaUtilLoggingLevelType, "FINE");
+ public final DexField FINER =
+ createField(javaUtilLoggingLevelType, javaUtilLoggingLevelType, "FINER");
+ public final DexField FINEST =
+ createField(javaUtilLoggingLevelType, javaUtilLoggingLevelType, "FINEST");
+ public final DexField SEVERE =
+ createField(javaUtilLoggingLevelType, javaUtilLoggingLevelType, "SEVERE");
+ public final DexField WARNING =
+ createField(javaUtilLoggingLevelType, javaUtilLoggingLevelType, "WARNING");
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(CONFIG);
+ consumer.accept(FINE);
+ consumer.accept(FINER);
+ consumer.accept(FINEST);
+ consumer.accept(SEVERE);
+ consumer.accept(WARNING);
+ }
+ }
+
+ public class LongMembers implements LibraryMembers {
+
+ public final DexField TYPE = createField(boxedLongType, classType, "TYPE");
public final DexMethod compare;
- private LongMethods() {
+ private LongMembers() {
compare = createMethod(boxedLongDescriptor,
createString("compare"), intDescriptor, new DexString[]{longDescriptor, longDescriptor});
}
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(TYPE);
+ }
}
public class DoubleMethods {
@@ -710,17 +1058,13 @@
}
}
- public class JavaUtilArraysMethods {
+ public class IntegerMembers implements LibraryMembers {
- public final DexMethod asList;
+ public final DexField TYPE = createField(boxedIntType, classType, "TYPE");
- private JavaUtilArraysMethods() {
- asList =
- createMethod(
- arraysDescriptor,
- createString("asList"),
- listDescriptor,
- new DexString[] {objectArrayDescriptor});
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(TYPE);
}
}
@@ -1072,7 +1416,11 @@
}
}
- public class StringMethods {
+ public class StringMembers implements LibraryMembers {
+
+ public final DexField CASE_INSENSITIVE_ORDER =
+ createField(stringType, javaUtilComparatorType, "CASE_INSENSITIVE_ORDER");
+
public final DexMethod isEmpty;
public final DexMethod length;
@@ -1098,7 +1446,7 @@
public final DexMethod trim = createMethod(stringType, createProto(stringType), trimName);
- private StringMethods() {
+ private StringMembers() {
isEmpty = createMethod(
stringDescriptor, isEmptyMethodName, booleanDescriptor, DexString.EMPTY_ARRAY);
length = createMethod(
@@ -1145,6 +1493,11 @@
intern = createMethod(
stringDescriptor, internMethodName, stringDescriptor, DexString.EMPTY_ARRAY);
}
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(CASE_INSENSITIVE_ORDER);
+ }
}
public class StringBuildingMethods {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
index 05416df..f417d65 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
@@ -20,16 +20,12 @@
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewArrayFilledData;
import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
-import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.LongInterval;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -119,9 +115,6 @@
if (isNewInstanceWithoutEnvironmentDependentFields(root, assumedNotToDependOnEnvironment)) {
return false;
}
- if (isAliasOfValueThatIsIndependentOfEnvironment(root, assumedNotToDependOnEnvironment)) {
- return false;
- }
return true;
} finally {
boolean changed = visited.remove(root);
@@ -414,49 +407,4 @@
return false;
}
-
- private boolean isAliasOfValueThatIsIndependentOfEnvironment(
- Value value, Set<Value> assumedNotToDependOnEnvironment) {
- // If we are inside a class initializer, and we are reading a final field of the enclosing
- // class, then check if there is a single write to that field in the class initializer, and that
- // the value being written into that field does not depend on the environment.
- //
- // The reason why we do not currently treat final fields that are written in multiple places is
- // that the value of the field could then be dependent on the environment due to the control
- // flow.
- if (code.method.isClassInitializer()) {
- assert !value.hasAliasedValue();
- if (value.isPhi()) {
- return false;
- }
- Instruction definition = value.definition;
- if (definition.isStaticGet()) {
- StaticGet staticGet = definition.asStaticGet();
- DexEncodedField field = appView.appInfo().resolveField(staticGet.getField());
- if (field != null && field.holder() == context) {
- List<StaticPut> finalFieldPuts = computeFinalFieldPuts().get(field);
- if (finalFieldPuts == null || finalFieldPuts.size() != 1) {
- return false;
- }
- StaticPut staticPut = ListUtils.first(finalFieldPuts);
- return !valueMayDependOnEnvironment(staticPut.value(), assumedNotToDependOnEnvironment);
- }
- }
- }
- return false;
- }
-
- private Map<DexEncodedField, List<StaticPut>> computeFinalFieldPuts() {
- assert code.method.isClassInitializer();
- if (finalFieldPuts == null) {
- finalFieldPuts = new IdentityHashMap<>();
- for (StaticPut staticPut : code.<StaticPut>instructions(Instruction::isStaticPut)) {
- DexEncodedField field = appView.appInfo().resolveField(staticPut.getField());
- if (field != null && field.holder() == context && field.isFinal()) {
- finalFieldPuts.computeIfAbsent(field, ignore -> new ArrayList<>()).add(staticPut);
- }
- }
- }
- return finalFieldPuts;
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
index 20ab05c..0365d43 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
@@ -187,7 +187,7 @@
}
Instruction definition = root.definition;
return definition.isInvokeVirtual()
- && definition.asInvokeVirtual().getInvokedMethod() == dexItemFactory.stringMethods.hashCode;
+ && definition.asInvokeVirtual().getInvokedMethod() == dexItemFactory.stringMembers.hashCode;
}
static class StringSwitchBuilderInfo {
@@ -376,7 +376,7 @@
}
if (instruction.isInvokeVirtual()) {
InvokeVirtual invoke = instruction.asInvokeVirtual();
- if (invoke.getInvokedMethod() == dexItemFactory.stringMethods.hashCode
+ if (invoke.getInvokedMethod() == dexItemFactory.stringMembers.hashCode
&& invoke.getReceiver() == stringValue
&& invoke.outValue().onlyUsedInBlock(block)) {
continue;
@@ -503,7 +503,7 @@
InvokeVirtual theInvoke = instructionIterator.next().asInvokeVirtual();
if (theInvoke == null
- || theInvoke.getInvokedMethod() != dexItemFactory.stringMethods.equals
+ || theInvoke.getInvokedMethod() != dexItemFactory.stringMembers.equals
|| theInvoke.getReceiver() != stringValue
|| theInvoke.inValues().get(1) != theString.outValue()) {
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
index b1f53e1..fcfc1e9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
@@ -108,7 +108,7 @@
InvokeVirtual invokeInstruction =
new InvokeVirtual(
- appView.dexItemFactory().stringMethods.equals,
+ appView.dexItemFactory().stringMembers.equals,
code.createValue(PrimitiveTypeElement.getInt()),
ImmutableList.of(theSwitch.value(), constStringInstruction.outValue()));
invokeInstruction.setPosition(Position.syntheticNone());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
index 74b1631..f0b74ea 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
@@ -70,7 +70,7 @@
}
builder.add(
- new CfInvoke(INVOKESTATIC, factory.utilArraysMethods.asList, false),
+ new CfInvoke(INVOKESTATIC, factory.javaUtilArraysMethods.asList, false),
new CfInvoke(
INVOKEINTERFACE,
factory.createMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index 9f15bcb..9a9899e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -142,6 +142,13 @@
}
}
+ public boolean isFinal(DexEncodedField field) {
+ if (field.isProgramField(appView)) {
+ return field.isFinal();
+ }
+ return appView.libraryMethodOptimizer().isFinalLibraryField(field);
+ }
+
private DexEncodedField resolveField(DexField field) {
if (appView.enableWholeProgramOptimizations()) {
return appView.appInfo().resolveField(field);
@@ -198,7 +205,7 @@
Value object = instancePut.object().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field, object);
ExistingValue value = new ExistingValue(instancePut.value());
- if (definition.isFinal()) {
+ if (isFinal(definition)) {
assert method.isInstanceInitializer() || verifyWasInstanceInitializer();
activeState.putFinalInstanceField(fieldAndObject, value);
} else {
@@ -216,7 +223,12 @@
// A field get on a different class can cause <clinit> to run and change static
// field values.
killNonFinalActiveFields(staticGet);
- activeState.putNonFinalStaticField(field, new ExistingValue(staticGet.value()));
+ FieldValue value = new ExistingValue(staticGet.value());
+ if (isFinal(definition)) {
+ activeState.putFinalStaticField(field, value);
+ } else {
+ activeState.putNonFinalStaticField(field, value);
+ }
}
} else if (instruction.isStaticPut()) {
StaticPut staticPut = instruction.asStaticPut();
@@ -558,9 +570,9 @@
if (instruction.isInstanceGet()) {
Value object = instruction.asInstanceGet().object().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field, object);
- removeNonFinalInstanceField(fieldAndObject);
+ removeInstanceField(fieldAndObject);
} else if (instruction.isStaticGet()) {
- removeNonFinalStaticField(field);
+ removeStaticField(field);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
similarity index 78%
rename from src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
rename to src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
index a3acde6..46a1d7c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
@@ -5,7 +5,9 @@
package com.android.tools.r8.ir.optimize.library;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory.LibraryMembers;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.IRCode;
@@ -21,17 +23,20 @@
import java.util.Map;
import java.util.Set;
-public class LibraryMethodOptimizer implements CodeOptimization {
+public class LibraryMemberOptimizer implements CodeOptimization {
private final AppView<?> appView;
+ /** Library fields that are assumed to be final. */
+ private final Set<DexEncodedField> finalLibraryFields = Sets.newIdentityHashSet();
+
/** The library types that are modeled. */
private final Set<DexType> modeledLibraryTypes = Sets.newIdentityHashSet();
private final Map<DexType, LibraryMethodModelCollection> libraryMethodModelCollections =
new IdentityHashMap<>();
- public LibraryMethodOptimizer(AppView<?> appView) {
+ public LibraryMemberOptimizer(AppView<?> appView) {
this.appView = appView;
register(new BooleanMethodOptimizer(appView));
register(new ObjectMethodOptimizer(appView));
@@ -46,12 +51,35 @@
register(new LogMethodOptimizer(appView));
}
+ initializeFinalLibraryFields();
+
LibraryOptimizationInfoInitializer libraryOptimizationInfoInitializer =
new LibraryOptimizationInfoInitializer(appView);
libraryOptimizationInfoInitializer.run();
modeledLibraryTypes.addAll(libraryOptimizationInfoInitializer.getModeledLibraryTypes());
}
+ private void initializeFinalLibraryFields() {
+ for (LibraryMembers libraryMembers : appView.dexItemFactory().libraryMembersCollection) {
+ libraryMembers.forEachFinalField(
+ field -> {
+ DexEncodedField definition = appView.definitionFor(field);
+ if (definition != null) {
+ if (definition.isFinal()) {
+ finalLibraryFields.add(definition);
+ } else {
+ assert false : "Field `" + field.toSourceString() + "` is not final";
+ }
+ }
+ });
+ }
+ }
+
+ /** Returns true if it is safe to assume that the given library field is final. */
+ public boolean isFinalLibraryField(DexEncodedField field) {
+ return finalLibraryFields.contains(field);
+ }
+
/**
* Returns true if {@param type} is a library type that is modeled in the compiler.
*
@@ -78,9 +106,7 @@
@Override
public void optimize(
- IRCode code,
- OptimizationFeedback feedback,
- MethodProcessor methodProcessor) {
+ IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor) {
Set<Value> affectedValues = Sets.newIdentityHashSet();
InstructionListIterator instructionIterator = code.instructionListIterator();
while (instructionIterator.hasNext()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
index 09c8f4e..0713076 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
@@ -39,7 +39,7 @@
InvokeMethod invoke,
DexEncodedMethod singleTarget,
Set<Value> affectedValues) {
- if (singleTarget.method == dexItemFactory.stringMethods.equals) {
+ if (singleTarget.method == dexItemFactory.stringMembers.equals) {
optimizeEquals(code, instructionIterator, invoke);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index 140c844..07d330b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -852,7 +852,7 @@
public boolean isToStringMethod(DexMethod method) {
return method == factory.stringBuilderMethods.toString
|| method == factory.stringBufferMethods.toString
- || method == factory.stringMethods.valueOf;
+ || method == factory.stringMembers.valueOf;
}
private boolean canHandleArgumentType(DexType argType) {
@@ -910,7 +910,7 @@
for (Instruction outUser : out.uniqueUsers()) {
if (outUser.isInvokeMethodWithReceiver()
&& outUser.asInvokeMethodWithReceiver().getInvokedMethod()
- == factory.stringMethods.intern) {
+ == factory.stringMembers.intern) {
numberOfBuildersWhoseResultIsInterned++;
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index a2b8eb5..cc8a24f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -191,7 +191,7 @@
continue;
}
- if (invokedMethod == factory.stringMethods.trim) {
+ if (invokedMethod == factory.stringMembers.trim) {
Value receiver = invoke.getReceiver().getAliasedValue();
if (receiver.hasLocalInfo() || receiver.isPhi() || !receiver.definition.isConstString()) {
continue;
@@ -210,7 +210,7 @@
Function<DexString, Integer> operatorWithNoArg = null;
BiFunction<DexString, DexString, Integer> operatorWithString = null;
BiFunction<DexString, Integer, Integer> operatorWithInt = null;
- if (invokedMethod == factory.stringMethods.hashCode) {
+ if (invokedMethod == factory.stringMembers.hashCode) {
operatorWithNoArg = rcv -> {
try {
return rcv.decodedHashCode();
@@ -219,33 +219,33 @@
throw new Unreachable();
}
};
- } else if (invokedMethod == factory.stringMethods.length) {
+ } else if (invokedMethod == factory.stringMembers.length) {
operatorWithNoArg = rcv -> rcv.size;
- } else if (invokedMethod == factory.stringMethods.isEmpty) {
+ } else if (invokedMethod == factory.stringMembers.isEmpty) {
operatorWithNoArg = rcv -> rcv.size == 0 ? 1 : 0;
- } else if (invokedMethod == factory.stringMethods.contains) {
+ } else if (invokedMethod == factory.stringMembers.contains) {
operatorWithString = (rcv, arg) -> rcv.toString().contains(arg.toString()) ? 1 : 0;
- } else if (invokedMethod == factory.stringMethods.startsWith) {
+ } else if (invokedMethod == factory.stringMembers.startsWith) {
operatorWithString = (rcv, arg) -> rcv.startsWith(arg) ? 1 : 0;
- } else if (invokedMethod == factory.stringMethods.endsWith) {
+ } else if (invokedMethod == factory.stringMembers.endsWith) {
operatorWithString = (rcv, arg) -> rcv.endsWith(arg) ? 1 : 0;
- } else if (invokedMethod == factory.stringMethods.equals) {
+ } else if (invokedMethod == factory.stringMembers.equals) {
operatorWithString = (rcv, arg) -> rcv.equals(arg) ? 1 : 0;
- } else if (invokedMethod == factory.stringMethods.equalsIgnoreCase) {
+ } else if (invokedMethod == factory.stringMembers.equalsIgnoreCase) {
operatorWithString = (rcv, arg) -> rcv.toString().equalsIgnoreCase(arg.toString()) ? 1 : 0;
- } else if (invokedMethod == factory.stringMethods.contentEqualsCharSequence) {
+ } else if (invokedMethod == factory.stringMembers.contentEqualsCharSequence) {
operatorWithString = (rcv, arg) -> rcv.toString().contentEquals(arg.toString()) ? 1 : 0;
- } else if (invokedMethod == factory.stringMethods.indexOfInt) {
+ } else if (invokedMethod == factory.stringMembers.indexOfInt) {
operatorWithInt = (rcv, idx) -> rcv.toString().indexOf(idx);
- } else if (invokedMethod == factory.stringMethods.indexOfString) {
+ } else if (invokedMethod == factory.stringMembers.indexOfString) {
operatorWithString = (rcv, arg) -> rcv.toString().indexOf(arg.toString());
- } else if (invokedMethod == factory.stringMethods.lastIndexOfInt) {
+ } else if (invokedMethod == factory.stringMembers.lastIndexOfInt) {
operatorWithInt = (rcv, idx) -> rcv.toString().lastIndexOf(idx);
- } else if (invokedMethod == factory.stringMethods.lastIndexOfString) {
+ } else if (invokedMethod == factory.stringMembers.lastIndexOfString) {
operatorWithString = (rcv, arg) -> rcv.toString().lastIndexOf(arg.toString());
- } else if (invokedMethod == factory.stringMethods.compareTo) {
+ } else if (invokedMethod == factory.stringMembers.compareTo) {
operatorWithString = (rcv, arg) -> rcv.toString().compareTo(arg.toString());
- } else if (invokedMethod == factory.stringMethods.compareToIgnoreCase) {
+ } else if (invokedMethod == factory.stringMembers.compareToIgnoreCase) {
operatorWithString = (rcv, arg) -> rcv.toString().compareToIgnoreCase(arg.toString());
} else {
continue;
@@ -513,7 +513,7 @@
if (instr.isInvokeStatic()) {
InvokeStatic invoke = instr.asInvokeStatic();
DexMethod invokedMethod = invoke.getInvokedMethod();
- if (invokedMethod != factory.stringMethods.valueOf) {
+ if (invokedMethod != factory.stringMembers.valueOf) {
continue;
}
assert invoke.inValues().size() == 1;
@@ -546,7 +546,7 @@
} else if (instr.isInvokeVirtual()) {
InvokeVirtual invoke = instr.asInvokeVirtual();
DexMethod invokedMethod = invoke.getInvokedMethod();
- if (invokedMethod != factory.stringMethods.toString) {
+ if (invokedMethod != factory.stringMembers.toString) {
continue;
}
assert invoke.inValues().size() == 1;
@@ -596,9 +596,9 @@
if (escapeRoute.isInvokeMethod()) {
DexMethod invokedMethod = escapeRoute.asInvokeMethod().getInvokedMethod();
// b/120138731: Only allow known simple operations on const-string
- if (invokedMethod == appView.dexItemFactory().stringMethods.hashCode
- || invokedMethod == appView.dexItemFactory().stringMethods.isEmpty
- || invokedMethod == appView.dexItemFactory().stringMethods.length) {
+ if (invokedMethod == appView.dexItemFactory().stringMembers.hashCode
+ || invokedMethod == appView.dexItemFactory().stringMembers.isEmpty
+ || invokedMethod == appView.dexItemFactory().stringMembers.length) {
return true;
}
// Add more cases to filter out, if any.
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
index d8d62ae..b2f5a80 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
@@ -173,7 +173,7 @@
}
static boolean isClassNameComparison(InvokeVirtual invoke, DexItemFactory dexItemFactory) {
- return invoke.getInvokedMethod() == dexItemFactory.stringMethods.equals
+ return invoke.getInvokedMethod() == dexItemFactory.stringMembers.equals
&& (isClassNameValue(invoke.getReceiver(), dexItemFactory)
|| isClassNameValue(invoke.inValues().get(1), dexItemFactory));
}
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java b/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java
index 971818b..9644e0d 100644
--- a/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java
@@ -123,7 +123,7 @@
Instruction instruction, DexItemFactory dexItemFactory) {
// Intentionally using toSourceString() because `instruction.getInvokedMethod()` belongs to
// another factory than the given `dexItemFactory`.
- String signature = dexItemFactory.stringMethods.hashCode.toSourceString();
+ String signature = dexItemFactory.stringMembers.hashCode.toSourceString();
return instruction.isInvokeVirtual()
&& instruction.asInvokeVirtual().getInvokedMethod().toSourceString().equals(signature);
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EnumCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EnumCanonicalizationTest.java
index 1e323fb..589fa3f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EnumCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EnumCanonicalizationTest.java
@@ -68,8 +68,7 @@
MethodSubject mainMethodSubject = classSubject.mainMethod();
assertThat(mainMethodSubject, isPresent());
assertEquals(
- // No canonicalization when generating class files.
- parameters.isCfRuntime() ? 3 : 1,
+ 1,
mainMethodSubject
.streamInstructions()
.filter(InstructionSubject::isStaticGet)
@@ -77,7 +76,7 @@
.filter(enumFieldSubject.getField().field::equals)
.count());
assertEquals(
- 3,
+ 1,
mainMethodSubject
.streamInstructions()
.filter(InstructionSubject::isStaticGet)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java
index fe3d79a..2b910f5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java
@@ -65,9 +65,8 @@
MethodSubject mMethodSubject = aClassSubject.uniqueMethodWithName("m");
assertThat(mMethodSubject, isPresent());
- // TODO(b/152196923): Should be 0.
assertEquals(
- 2,
+ 1,
countStaticGetInstructions(
mMethodSubject.asFoundMethodSubject(), fFieldSubject.asFoundFieldSubject()));
}