Do not trace dead fields in Enqueuer
Change-Id: I40e6f07e64482e02f4a4d32cca20f9697f42e139
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index af3ab41..a0b5581 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.analysis.proto.GeneratedExtensionRegistryShrinker;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.R8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.optimize.EnumOrdinalMapCollector;
@@ -35,6 +36,7 @@
import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector;
import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.kotlin.Kotlin;
+import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.Minifier;
import com.android.tools.r8.naming.NamingLens;
@@ -327,6 +329,16 @@
.run(executorService));
Enqueuer enqueuer = new Enqueuer(appView, options, null, compatibility);
+
+ // If shrinking of the generated proto extension registry is enabled, then the Enqueuer
+ // won't trace the dead proto extensions fields. However, for the purpose of member value
+ // propagation, we should keep the dead proto extension fields in the program, such that
+ // member value propagation can find their definitions and the corresponding optimization
+ // info. This is handled by simply marking the dead proto extension types as live after the
+ // Enqueuer has finished. This way we don't actually trace these types.
+ enqueuer.markSkippedProtoExtensionTypesAsLive(
+ options.enableGeneratedExtensionRegistryShrinking);
+
AppView<AppInfoWithLiveness> appViewWithLiveness =
appView.setAppInfo(
enqueuer.traceApplication(
@@ -356,6 +368,9 @@
.withLiveness()
.prunedCopyFrom(application, pruner.getRemovedClasses()));
new AbstractMethodRemover(appView.appInfo().withLiveness()).run();
+
+ // Mark dead proto extensions fields as neither being read nor written.
+ appView.withGeneratedExtensionRegistryShrinker(GeneratedExtensionRegistryShrinker::run);
}
classesToRetainInnerClassAttributeFor =
@@ -629,6 +644,11 @@
executorService,
timing));
+ if (Log.ENABLED) {
+ appView.withGeneratedExtensionRegistryShrinker(
+ GeneratedExtensionRegistryShrinker::logDeadProtoExtensionFields);
+ }
+
if (options.isShrinking()) {
TreePruner pruner = new TreePruner(application, appViewWithLiveness);
application = pruner.run();
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 800feb6..1d48684 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.OptionalBool;
+import com.android.tools.r8.ir.analysis.proto.GeneratedExtensionRegistryShrinker;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.shaking.VerticalClassMerger.VerticallyMergedClasses;
@@ -12,6 +13,8 @@
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Predicate;
public class AppView<T extends AppInfo> implements DexDefinitionSupplier {
@@ -29,6 +32,10 @@
private final InternalOptions options;
private RootSet rootSet;
+ // Optimizations.
+ private final GeneratedExtensionRegistryShrinker generatedExtensionRegistryShrinker;
+
+ // Optimization results.
private Predicate<DexType> classesEscapingIntoLibrary = Predicates.alwaysTrue();
private Set<DexMethod> unneededVisibilityBridgeMethods = ImmutableSet.of();
private VerticallyMergedClasses verticallyMergedClasses;
@@ -40,6 +47,15 @@
this.wholeProgramOptimizations = wholeProgramOptimizations;
this.graphLense = GraphLense.getIdentityLense();
this.options = options;
+
+ if (enableWholeProgramOptimizations()) {
+ this.generatedExtensionRegistryShrinker =
+ options.enableGeneratedExtensionRegistryShrinking
+ ? new GeneratedExtensionRegistryShrinker(this.withLiveness())
+ : null;
+ } else {
+ this.generatedExtensionRegistryShrinker = null;
+ }
}
public static <T extends AppInfo> AppView<T> createForD8(T appInfo, InternalOptions options) {
@@ -127,6 +143,21 @@
return wholeProgramOptimizations == WholeProgramOptimizations.ON;
}
+ public void withGeneratedExtensionRegistryShrinker(
+ Consumer<GeneratedExtensionRegistryShrinker> consumer) {
+ if (generatedExtensionRegistryShrinker != null) {
+ consumer.accept(generatedExtensionRegistryShrinker);
+ }
+ }
+
+ public <U> U withGeneratedExtensionRegistryShrinker(
+ Function<GeneratedExtensionRegistryShrinker, U> fn, U defaultValue) {
+ if (generatedExtensionRegistryShrinker != null) {
+ return fn.apply(generatedExtensionRegistryShrinker);
+ }
+ return defaultValue;
+ }
+
public GraphLense graphLense() {
return graphLense;
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
index 975776a..7b4abd6 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
@@ -11,13 +11,19 @@
/** Provides immutable access to {@link FieldAccessInfoImpl}. */
public interface FieldAccessInfo {
+ FieldAccessInfoImpl asMutable();
+
DexField getField();
+ DexEncodedMethod getUniqueReadContext();
+
void forEachIndirectAccess(Consumer<DexField> consumer);
void forEachIndirectAccessWithContexts(BiConsumer<DexField, Set<DexEncodedMethod>> consumer);
boolean isRead();
+ boolean isReadOnlyIn(DexEncodedMethod method);
+
boolean isWritten();
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
index 340d606..1f07f43 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
@@ -9,5 +9,7 @@
/** Provides immutable access to {@link FieldAccessInfoCollectionImpl}. */
public interface FieldAccessInfoCollection<T extends FieldAccessInfo> {
+ T get(DexField field);
+
void forEach(Consumer<T> consumer);
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
index 8427d53..1478f2d 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
@@ -15,6 +15,7 @@
private Map<DexField, FieldAccessInfoImpl> infos = new IdentityHashMap<>();
+ @Override
public FieldAccessInfoImpl get(DexField field) {
return infos.get(field);
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index 23cb198..6198d50 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -37,11 +37,27 @@
}
@Override
+ public FieldAccessInfoImpl asMutable() {
+ return this;
+ }
+
+ @Override
public DexField getField() {
return field;
}
@Override
+ public DexEncodedMethod getUniqueReadContext() {
+ if (readsWithContexts != null && readsWithContexts.size() == 1) {
+ Set<DexEncodedMethod> contexts = readsWithContexts.values().iterator().next();
+ if (contexts.size() == 1) {
+ return contexts.iterator().next();
+ }
+ }
+ return null;
+ }
+
+ @Override
public void forEachIndirectAccess(Consumer<DexField> consumer) {
// There can be indirect reads and writes of the same field reference, so we need to keep track
// of the previously-seen indirect accesses to avoid reporting duplicates.
@@ -99,6 +115,14 @@
return readsWithContexts != null && !readsWithContexts.isEmpty();
}
+ @Override
+ public boolean isReadOnlyIn(DexEncodedMethod method) {
+ assert isRead();
+ assert method != null;
+ DexEncodedMethod uniqueReadContext = getUniqueReadContext();
+ return uniqueReadContext != null && uniqueReadContext == method;
+ }
+
/** Returns true if this field is written by the program. */
@Override
public boolean isWritten() {
@@ -123,6 +147,10 @@
.add(context);
}
+ public void clearReads() {
+ readsWithContexts = null;
+ }
+
public void clearWrites() {
writesWithContexts = null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
new file mode 100644
index 0000000..e30bf58
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -0,0 +1,171 @@
+// Copyright (c) 2019, 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.proto;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessInfo;
+import com.android.tools.r8.graph.FieldAccessInfoCollection;
+import com.android.tools.r8.graph.FieldAccessInfoImpl;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.function.Consumer;
+
+/**
+ * This optimization is responsible for pruning dead proto extensions.
+ *
+ * <p>When using proto lite, a registry for all proto extensions is created. The generated extension
+ * registry roughly looks as follows:
+ *
+ * <pre>
+ * class GeneratedExtensionRegistry {
+ * public static GeneratedMessageLite$GeneratedExtension findLiteExtensionByNumber(
+ * MessageLite message, int number) {
+ * ...
+ * switch (...) {
+ * case ...:
+ * return SomeExtension.extensionField;
+ * case ...:
+ * return SomeOtherExtension.extensionField;
+ * ... // Many other cases.
+ * default:
+ * return null;
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * <p>We consider an extension to be dead if it is only accessed via a static-get instruction inside
+ * the GeneratedExtensionRegistry. For such dead extensions, we simply rewrite the static-get
+ * instructions inside the GeneratedExtensionRegistry to null. This ensures that the extensions will
+ * be removed as a result of tree shaking.
+ */
+public class GeneratedExtensionRegistryShrinker {
+
+ static class ProtoReferences {
+
+ public final DexType generatedExtensionType;
+ public final DexType generatedMessageLiteType;
+ public final DexType messageLiteType;
+
+ public final DexString findLiteExtensionByNumberName;
+ public final DexString findLiteExtensionByNumber1Name;
+ public final DexString findLiteExtensionByNumber2Name;
+ public final DexProto findLiteExtensionByNumberProto;
+
+ private ProtoReferences(DexItemFactory factory) {
+ generatedExtensionType =
+ factory.createType(
+ factory.createString(
+ "Lcom/google/protobuf/GeneratedMessageLite$GeneratedExtension;"));
+ generatedMessageLiteType =
+ factory.createType(factory.createString("Lcom/google/protobuf/GeneratedMessageLite;"));
+ messageLiteType =
+ factory.createType(factory.createString("Lcom/google/protobuf/MessageLite;"));
+ findLiteExtensionByNumberName = factory.createString("findLiteExtensionByNumber");
+ findLiteExtensionByNumber1Name = factory.createString("findLiteExtensionByNumber1");
+ findLiteExtensionByNumber2Name = factory.createString("findLiteExtensionByNumber2");
+ findLiteExtensionByNumberProto =
+ factory.createProto(generatedExtensionType, messageLiteType, factory.intType);
+ }
+
+ public boolean isFindLiteExtensionByNumberMethod(DexMethod method) {
+ if (method.proto == findLiteExtensionByNumberProto) {
+ assert method.name != findLiteExtensionByNumber2Name;
+ return method.name == findLiteExtensionByNumberName
+ || method.name == findLiteExtensionByNumber1Name;
+ }
+ return false;
+ }
+ }
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final ProtoReferences references;
+
+ public GeneratedExtensionRegistryShrinker(AppView<AppInfoWithLiveness> appView) {
+ assert appView.options().enableGeneratedExtensionRegistryShrinking;
+ this.appView = appView;
+ this.references = new ProtoReferences(appView.dexItemFactory());
+ }
+
+ /**
+ * Will be run after the initial round of tree shaking. This clears the reads and writes to fields
+ * that store dead proto extensions. As a result of this, the member value propagation will
+ * automatically rewrite the reads of this field by null.
+ */
+ public void run() {
+ FieldAccessInfoCollection<?> fieldAccessInfoCollection =
+ appView.appInfo().getFieldAccessInfoCollection();
+ forEachDeadProtoExtensionField(
+ field -> {
+ FieldAccessInfoImpl fieldAccessInfo = fieldAccessInfoCollection.get(field).asMutable();
+ fieldAccessInfo.clearReads();
+ fieldAccessInfo.clearWrites();
+ });
+ }
+
+ public boolean isDeadProtoExtensionField(DexField field) {
+ return isDeadProtoExtensionField(field, appView.appInfo().getFieldAccessInfoCollection());
+ }
+
+ public boolean isDeadProtoExtensionField(
+ DexField field, FieldAccessInfoCollection<?> fieldAccessInfoCollection) {
+ if (field.type != references.generatedExtensionType) {
+ return false;
+ }
+
+ DexEncodedField encodedField = appView.appInfo().resolveField(field);
+ if (encodedField == null) {
+ return false;
+ }
+
+ DexClass clazz = appView.definitionFor(encodedField.field.holder);
+ if (clazz == null || !clazz.isProgramClass()) {
+ return false;
+ }
+
+ if (!appView.isSubtype(clazz.type, references.generatedMessageLiteType).isTrue()) {
+ return false;
+ }
+
+ FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(encodedField.field);
+ if (fieldAccessInfo == null) {
+ return false;
+ }
+
+ DexEncodedMethod uniqueReadContext = fieldAccessInfo.getUniqueReadContext();
+ return uniqueReadContext != null
+ && references.isFindLiteExtensionByNumberMethod(uniqueReadContext.method);
+ }
+
+ private void forEachDeadProtoExtensionField(Consumer<DexField> consumer) {
+ FieldAccessInfoCollection<?> fieldAccessInfoCollection =
+ appView.appInfo().getFieldAccessInfoCollection();
+ fieldAccessInfoCollection.forEach(
+ info -> {
+ DexField field = info.getField();
+ if (isDeadProtoExtensionField(field)) {
+ consumer.accept(field);
+ }
+ });
+ }
+
+ /** For debugging. */
+ public void logDeadProtoExtensionFields() {
+ if (Log.isLoggingEnabledFor(GeneratedExtensionRegistryShrinker.class)) {
+ forEachDeadProtoExtensionField(
+ field ->
+ System.out.println("Dead proto extension field: `" + field.toSourceString() + "`"));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/logging/Log.java b/src/main/java/com/android/tools/r8/logging/Log.java
index 6cb91b4..2ed1b30 100644
--- a/src/main/java/com/android/tools/r8/logging/Log.java
+++ b/src/main/java/com/android/tools/r8/logging/Log.java
@@ -5,39 +5,39 @@
public class Log {
- static public final boolean ENABLED = false;
+ public static final boolean ENABLED = false;
- static private final boolean VERBOSE_ENABLED = false;
- static private final boolean INFO_ENABLED = true;
- static private final boolean DEBUG_ENABLED = true;
- static private final boolean WARN_ENABLED = true;
+ private static final boolean VERBOSE_ENABLED = false;
+ private static final boolean INFO_ENABLED = true;
+ private static final boolean DEBUG_ENABLED = true;
+ private static final boolean WARN_ENABLED = true;
public static void verbose(Class<?> from, String message, Object... arguments) {
- if (ENABLED && VERBOSE_ENABLED && isClassEnabled(from)) {
+ if (isLoggingEnabledFor(from) && VERBOSE_ENABLED) {
log("VERB", from, message, arguments);
}
}
public static void info(Class<?> from, String message, Object... arguments) {
- if (ENABLED && INFO_ENABLED && isClassEnabled(from)) {
+ if (isLoggingEnabledFor(from) && INFO_ENABLED) {
log("INFO", from, message, arguments);
}
}
public static void debug(Class<?> from, String message, Object... arguments) {
- if (ENABLED && DEBUG_ENABLED && isClassEnabled(from)) {
+ if (isLoggingEnabledFor(from) && DEBUG_ENABLED) {
log("DBG", from, message, arguments);
}
}
public static void warn(Class<?> from, String message, Object... arguments) {
- if (ENABLED && WARN_ENABLED && isClassEnabled(from)) {
+ if (isLoggingEnabledFor(from) && WARN_ENABLED) {
log("WARN", from, message, arguments);
}
}
- private static boolean isClassEnabled(Class<?> clazz) {
- return true;
+ public static boolean isLoggingEnabledFor(Class<?> clazz) {
+ return ENABLED;
}
synchronized private static void log(String kind, Class<?> from, String message, Object... args) {
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 9d43839..3560e2f 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -111,6 +111,14 @@
private final boolean forceProguardCompatibility;
private boolean tracingMainDex = false;
+ // If shrinking of the generated proto extension registry is enabled, then the Enqueuer
+ // won't trace the dead proto extensions fields. However, for the purpose of member value
+ // propagation, we should keep the dead proto extension fields in the program, such that
+ // member value propagation can find their definitions and the corresponding optimization
+ // info. This is handled by simply marking the dead proto extension types as live after the
+ // Enqueuer has finished. This way we don't actually trace these types.
+ private boolean markSkippedProtoExtensionTypesAsLive = false;
+
private final AppInfoWithSubtyping appInfo;
private final AppView<? extends AppInfoWithSubtyping> appView;
private final InternalOptions options;
@@ -168,6 +176,14 @@
*/
private final Set<DexType> liveTypes = Sets.newIdentityHashSet();
+ /**
+ * Set of proto extension types that are technically live, but which we have not traced because
+ * they are dead according to the generated extension registry shrinker.
+ *
+ * <p>Only used if {@link InternalOptions#enableGeneratedExtensionRegistryShrinking} is set.
+ */
+ private final Set<DexType> skippedProtoExtensionTypes = Sets.newIdentityHashSet();
+
/** Set of annotation types that are instantiated. */
private final SetWithReason<DexAnnotation> liveAnnotations =
new SetWithReason<>(this::registerAnnotation);
@@ -287,6 +303,10 @@
this.options = options;
}
+ public void markSkippedProtoExtensionTypesAsLive(boolean value) {
+ markSkippedProtoExtensionTypesAsLive = value;
+ }
+
private Set<DexField> staticFieldsWrittenOnlyInEnclosingStaticInitializer() {
Set<DexField> result = Sets.newIdentityHashSet();
fieldAccessInfoCollection.forEach(
@@ -609,9 +629,30 @@
if (!registerFieldRead(field, currentMethod)) {
return false;
}
+
if (Log.ENABLED) {
Log.verbose(getClass(), "Register Sget `%s`.", field);
}
+
+ DexEncodedField encodedField = appInfo.resolveField(field);
+ if (encodedField != null && encodedField.isProgramField(appView)) {
+ if (appView.options().enableGeneratedExtensionRegistryShrinking) {
+ // If it is a dead proto extension field, don't trace onwards.
+ boolean skipTracing =
+ appView.withGeneratedExtensionRegistryShrinker(
+ shrinker ->
+ shrinker.isDeadProtoExtensionField(
+ encodedField.field, fieldAccessInfoCollection),
+ false);
+ if (skipTracing) {
+ if (markSkippedProtoExtensionTypesAsLive) {
+ skippedProtoExtensionTypes.add(encodedField.field.holder);
+ }
+ return false;
+ }
+ }
+ }
+
markStaticFieldAsLive(field, KeepReason.fieldReferencedIn(currentMethod));
return true;
}
@@ -621,12 +662,30 @@
if (!registerFieldWrite(field, currentMethod)) {
return false;
}
+
if (Log.ENABLED) {
Log.verbose(getClass(), "Register Sput `%s`.", field);
}
DexEncodedField encodedField = appInfo.resolveField(field);
if (encodedField != null && encodedField.isProgramField(appView)) {
+ if (appView.options().enableGeneratedExtensionRegistryShrinking) {
+ // If it is a dead proto extension field, don't trace onwards.
+ boolean skipTracing =
+ appView.withGeneratedExtensionRegistryShrinker(
+ shrinker ->
+ shrinker.isDeadProtoExtensionField(
+ encodedField.field, fieldAccessInfoCollection),
+ false);
+ if (skipTracing) {
+ if (markSkippedProtoExtensionTypesAsLive) {
+ skippedProtoExtensionTypes.add(encodedField.field.holder);
+ }
+ return false;
+ }
+ }
+
+ // If it is written outside of the <clinit> of its enclosing class, record it.
boolean isWrittenOutsideEnclosingStaticInitializer =
currentMethod.method.holder != encodedField.field.holder
|| !currentMethod.isClassInitializer();
@@ -1564,6 +1623,14 @@
(field, info) -> field != info.getField() || info == MISSING_FIELD_ACCESS_INFO);
assert fieldAccessInfoCollection.verifyMappingIsOneToOne();
+ // Mark the proto extensions types that have not been traced as live if specified by the
+ // configuration. This enables the member value propagation to find the definition of the dead
+ // proto extension fields, and thereby optimize the corresponding static-get instructions (will
+ // always be null).
+ if (markSkippedProtoExtensionTypesAsLive) {
+ liveTypes.addAll(skippedProtoExtensionTypes);
+ }
+
AppInfoWithLiveness appInfoWithLiveness =
new AppInfoWithLiveness(
appInfo,
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 0f6aace..8318f91 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -293,6 +293,9 @@
public boolean enableLambdaMerging = false;
// Flag to turn on/off desugaring in D8/R8.
public boolean enableDesugaring = true;
+ // Flag to turn on/off GeneratedExtensionRegistry shrinking.
+ public boolean enableGeneratedExtensionRegistryShrinking =
+ System.getProperty("com.android.tools.r8.generatedExtensionRegistryShrinking") != null;
// Flag to turn on/off JDK11+ nest-access control
public boolean enableNestBasedAccessDesugaring = true;
// Flag to turn on/off reduction of nest to improve class merging optimizations.
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index b8bd0ed..4c9a844 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -317,6 +317,8 @@
args.extend(['--main-dex-rules', rules])
if 'allow-type-errors' in values:
extra_args.append('-Dcom.android.tools.r8.allowTypeErrors=1')
+ if 'proto-shrinking' in values:
+ extra_args.append('-Dcom.android.tools.r8.generatedExtensionRegistryShrinking=1')
if not options.no_libraries and 'libraries' in values:
for lib in values['libraries']:
diff --git a/tools/youtube_data.py b/tools/youtube_data.py
index f26af8d..94a97ce 100644
--- a/tools/youtube_data.py
+++ b/tools/youtube_data.py
@@ -131,6 +131,7 @@
'pgconf': [
'%s_proguard.config' % V14_19_PREFIX,
'%s/proguardsettings/YouTubeRelease_proguard.config' % utils.THIRD_PARTY],
+ 'proto-shrinking': 1,
# Build for native multi dex, as Currently R8 cannot meet the main-dex
# constraints.
#'maindexrules' : [