Implement support for not overloading aggressively, making that the default.

Bug: 67757251
Change-Id: Ic814678649b165e48629f218bdc94a24af9ac7d8
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index 7cbdf9b..a4f4912 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -9,10 +9,12 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
 import java.util.Map;
+import java.util.function.Function;
 
 class FieldNameMinifier extends MemberNameMinifier<DexField, DexType> {
 
@@ -20,6 +22,17 @@
     super(appInfo, rootSet, options);
   }
 
+  @Override
+  Function<DexType, ?> getKeyTransform(ProguardConfiguration config) {
+    if (config.isOverloadAggressively()) {
+      // Use the type as the key, hence reuse names per type.
+      return a -> a;
+    } else {
+      // Always use the same key, hence do not reuse names per type.
+      return a -> Void.class;
+    }
+  }
+
   Map<DexField, DexString> computeRenaming(Timing timing) {
     // Reserve names in all classes first. We do this in subtyping order so we do not
     // shadow a reserved field in subclasses. While there is no concept of virtual field
@@ -41,19 +54,19 @@
     return renaming;
   }
 
-  private void reserveNamesInSubtypes(DexType type, NamingState<DexType> state) {
+  private void reserveNamesInSubtypes(DexType type, NamingState<DexType, ?> state) {
     DexClass holder = appInfo.definitionFor(type);
     if (holder == null) {
       return;
     }
-    NamingState<DexType> newState = computeStateIfAbsent(type, t -> state.createChild());
+    NamingState<DexType, ?> newState = computeStateIfAbsent(type, t -> state.createChild());
     holder.forEachField(field -> reserveFieldName(field, newState, holder.isLibraryClass()));
     type.forAllExtendsSubtypes(subtype -> reserveNamesInSubtypes(subtype, newState));
   }
 
   private void reserveFieldName(
       DexEncodedField encodedField,
-      NamingState<DexType> state,
+      NamingState<DexType, ?> state,
       boolean isLibrary) {
     if (isLibrary || rootSet.noObfuscation.contains(encodedField)) {
       DexField field = encodedField.field;
@@ -66,13 +79,13 @@
     if (clazz == null) {
       return;
     }
-    NamingState<DexType> state = getState(clazz.type);
+    NamingState<DexType, ?> state = getState(clazz.type);
     assert state != null;
     clazz.forEachField(field -> renameField(field, state));
     type.forAllExtendsSubtypes(this::renameFieldsInSubtypes);
   }
 
-  private void renameField(DexEncodedField encodedField, NamingState<DexType> state) {
+  private void renameField(DexEncodedField encodedField, NamingState<DexType, ?> state) {
     DexField field = encodedField.field;
     if (!state.isReserved(field.name, field.type)) {
       renaming.put(field, state.assignNewNameFor(field.name, field.type, false));
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
index 37cdb34..4a288be 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.CachedHashValueDexItem;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
@@ -21,25 +22,27 @@
   protected final ImmutableList<String> dictionary;
 
   protected final Map<MemberType, DexString> renaming = new IdentityHashMap<>();
-  protected final Map<DexType, NamingState<StateType>> states = new IdentityHashMap<>();
-  protected final NamingState<StateType> globalState;
+  protected final Map<DexType, NamingState<StateType, ?>> states = new IdentityHashMap<>();
+  protected final NamingState<StateType, ?> globalState;
   protected final boolean useUniqueMemberNames;
 
   MemberNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
     this.appInfo = appInfo;
     this.rootSet = rootSet;
     this.dictionary = options.proguardConfiguration.getObfuscationDictionary();
-
-    this.globalState = NamingState.createRoot(appInfo.dexItemFactory, dictionary);
+    this.globalState = NamingState.createRoot(appInfo.dexItemFactory, dictionary,
+        getKeyTransform(options.proguardConfiguration));
     this.useUniqueMemberNames = options.proguardConfiguration.isUseUniqueClassMemberNames();
   }
 
-  protected NamingState<StateType> computeStateIfAbsent(
-      DexType type, Function<DexType, NamingState<StateType>> f) {
+  abstract Function<StateType, ?> getKeyTransform(ProguardConfiguration config);
+
+  protected NamingState<StateType, ?> computeStateIfAbsent(
+      DexType type, Function<DexType, NamingState<StateType, ?>> f) {
     return useUniqueMemberNames ? globalState : states.computeIfAbsent(type, f);
   }
 
-  protected NamingState<StateType> getState(DexType type) {
+  protected NamingState<StateType, ?> getState(DexType type) {
     return useUniqueMemberNames ? globalState : states.get(type);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index a649984..7b939ae 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -3,10 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
-import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.Sets;
-
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -14,10 +10,15 @@
 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.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.MethodJavaSignatureEquivalence;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.Timing;
-
+import com.google.common.base.Equivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -25,6 +26,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 
 /**
  * A pass to rename methods using common, short names.
@@ -87,10 +89,26 @@
  */
 class MethodNameMinifier extends MemberNameMinifier<DexMethod, DexProto> {
 
-  private MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
+  private final Equivalence<DexMethod> equivalence;
+  private final ProguardConfiguration config;
 
   MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
     super(appInfo, rootSet, options);
+    this.config = options.proguardConfiguration;
+    equivalence = config.isOverloadAggressively()
+        ? MethodSignatureEquivalence.get()
+        : MethodJavaSignatureEquivalence.get();
+  }
+
+  @Override
+  Function<DexProto, ?> getKeyTransform(ProguardConfiguration config) {
+    if (config.isOverloadAggressively()) {
+      // Use the full proto as key, hence reuse names based on full signature.
+      return a -> a;
+    } else {
+      // Only use the parameters as key, hence do not reuse names on return type.
+      return proto -> proto.parameters;
+    }
   }
 
   Map<DexMethod, DexString> computeRenaming(Timing timing) {
@@ -128,7 +146,7 @@
   private void assignNamesToClassesMethods(DexType type, boolean doPrivates) {
     DexClass holder = appInfo.definitionFor(type);
     if (holder != null && !holder.isLibraryClass()) {
-      NamingState<DexProto> state =
+      NamingState<DexProto, ?> state =
           computeStateIfAbsent(type, k -> getState(holder.superType).createChild());
       holder.forEachMethod(method -> assignNameToMethod(method, state, doPrivates));
     }
@@ -136,7 +154,7 @@
   }
 
   private void assignNameToMethod(
-      DexEncodedMethod encodedMethod, NamingState<DexProto> state, boolean doPrivates) {
+      DexEncodedMethod encodedMethod, NamingState<DexProto, ?> state, boolean doPrivates) {
     if (encodedMethod.accessFlags.isPrivate() != doPrivates) {
       return;
     }
@@ -147,19 +165,19 @@
     }
   }
 
-  private Set<NamingState<DexProto>> getReachableStates(DexType type,
+  private Set<NamingState<DexProto, ?>> getReachableStates(DexType type,
       Map<DexType, DexType> frontierMap) {
     Set<DexType> interfaces = Sets.newIdentityHashSet();
     interfaces.add(type);
     collectSuperInterfaces(type, interfaces);
     collectSubInterfaces(type, interfaces);
-    Set<NamingState<DexProto>> reachableStates = new HashSet<>();
+    Set<NamingState<DexProto, ?>> reachableStates = new HashSet<>();
     for (DexType iface : interfaces) {
       // Add the interface itself
       reachableStates.add(getState(iface));
       // And the frontiers that correspond to the classes that implement the interface.
       iface.forAllImplementsSubtypes(t -> {
-        NamingState<DexProto> state = getState(frontierMap.get(t));
+        NamingState<DexProto, ?> state = getState(frontierMap.get(t));
         assert state != null;
         reachableStates.add(state);
       });
@@ -173,16 +191,16 @@
     // reserve the names for later method naming.
     timing.begin("Compute map");
     // A map from DexMethods to all the states linked to interfaces they appear in.
-    Map<Wrapper<DexMethod>, Set<NamingState<DexProto>>> globalStateMap = new HashMap<>();
+    Map<Wrapper<DexMethod>, Set<NamingState<DexProto, ?>>> globalStateMap = new HashMap<>();
     // A map from DexMethods to all the definitions seen. Needed as the Wrapper equalizes them all.
     Map<Wrapper<DexMethod>, Set<DexMethod>> sourceMethodsMap = new HashMap<>();
     // A map from DexMethods to the first interface state it was seen in. Used to pick good names.
-    Map<Wrapper<DexMethod>, NamingState<DexProto>> originStates = new HashMap<>();
+    Map<Wrapper<DexMethod>, NamingState<DexProto, ?>> originStates = new HashMap<>();
     DexType.forAllInterfaces(appInfo.dexItemFactory, iface -> {
       assert iface.isInterface();
       DexClass clazz = appInfo.definitionFor(iface);
       if (clazz != null) {
-        Set<NamingState<DexProto>> collectedStates = getReachableStates(iface, frontierMap);
+        Set<NamingState<DexProto, ?>> collectedStates = getReachableStates(iface, frontierMap);
         clazz.forEachMethod(method -> addStatesToGlobalMapForMethod(
             method, collectedStates, globalStateMap, sourceMethodsMap, originStates, iface));
       }
@@ -228,12 +246,12 @@
   }
 
   private void addStatesToGlobalMapForMethod(
-      DexEncodedMethod method, Set<NamingState<DexProto>> collectedStates,
-      Map<Wrapper<DexMethod>, Set<NamingState<DexProto>>> globalStateMap,
+      DexEncodedMethod method, Set<NamingState<DexProto, ?>> collectedStates,
+      Map<Wrapper<DexMethod>, Set<NamingState<DexProto, ?>>> globalStateMap,
       Map<Wrapper<DexMethod>, Set<DexMethod>> sourceMethodsMap,
-      Map<Wrapper<DexMethod>, NamingState<DexProto>> originStates, DexType originInterface) {
+      Map<Wrapper<DexMethod>, NamingState<DexProto, ?>> originStates, DexType originInterface) {
     Wrapper<DexMethod> key = equivalence.wrap(method.method);
-    Set<NamingState<DexProto>> stateSet =
+    Set<NamingState<DexProto, ?>> stateSet =
         globalStateMap.computeIfAbsent(key, k -> new HashSet<>());
     stateSet.addAll(collectedStates);
     sourceMethodsMap.computeIfAbsent(key, k -> new HashSet<>()).add(method.method);
@@ -242,12 +260,12 @@
 
   private void assignNameForInterfaceMethodInAllStates(
       DexMethod method,
-      Set<NamingState<DexProto>> collectedStates,
+      Set<NamingState<DexProto, ?>> collectedStates,
       Set<DexMethod> sourceMethods,
-      NamingState<DexProto> originState) {
+      NamingState<DexProto, ?> originState) {
     boolean isReserved = false;
     if (globalState.isReserved(method.name, method.proto)) {
-      for (NamingState<DexProto> state : collectedStates) {
+      for (NamingState<DexProto, ?> state : collectedStates) {
         if (state.isReserved(method.name, method.proto)) {
           isReserved = true;
           break;
@@ -255,7 +273,7 @@
       }
       if (isReserved) {
         // This method's name is reserved in at least on naming state, so reserve it everywhere.
-        for (NamingState<DexProto> state : collectedStates) {
+        for (NamingState<DexProto, ?> state : collectedStates) {
           state.reserveName(method.name, method.proto);
         }
         return;
@@ -267,14 +285,14 @@
     DexString candidate = null;
     do {
       candidate = originState.assignNewNameFor(method.name, method.proto, false);
-      for (NamingState<DexProto> state : collectedStates) {
+      for (NamingState<DexProto, ?> state : collectedStates) {
         if (!state.isAvailable(method.name, method.proto, candidate)) {
           candidate = null;
           break;
         }
       }
     } while (candidate == null);
-    for (NamingState<DexProto> state : collectedStates) {
+    for (NamingState<DexProto, ?> state : collectedStates) {
       state.addRenaming(method.name, method.proto, candidate);
     }
     // Rename all methods in interfaces that gave rise to this renaming.
@@ -284,11 +302,11 @@
   }
 
   private void reserveNamesInClasses(DexType type, DexType libraryFrontier,
-      NamingState<DexProto> parent,
+      NamingState<DexProto, ?> parent,
       Map<DexType, DexType> frontierMap) {
     assert !type.isInterface();
     DexClass holder = appInfo.definitionFor(type);
-    NamingState<DexProto> state = allocateNamingStateAndReserve(holder, type, libraryFrontier,
+    NamingState<DexProto, ?> state = allocateNamingStateAndReserve(holder, type, libraryFrontier,
         parent, frontierMap);
     // If this is a library class (or effectively a library class as it is missing) move the
     // frontier forward.
@@ -307,16 +325,17 @@
     allocateNamingStateAndReserve(holder, type, type, null, frontierMap);
   }
 
-  private NamingState<DexProto> allocateNamingStateAndReserve(DexClass holder, DexType type,
+  private NamingState<DexProto, ?> allocateNamingStateAndReserve(DexClass holder, DexType type,
       DexType libraryFrontier,
-      NamingState<DexProto> parent,
+      NamingState<DexProto, ?> parent,
       Map<DexType, DexType> frontierMap) {
     frontierMap.put(type, libraryFrontier);
-    NamingState<DexProto> state =
+    NamingState<DexProto, ?> state =
         computeStateIfAbsent(
             libraryFrontier,
             ignore -> parent == null
-                ? NamingState.createRoot(appInfo.dexItemFactory, dictionary)
+                ? NamingState
+                .createRoot(appInfo.dexItemFactory, dictionary, getKeyTransform(config))
                 : parent.createChild());
     if (holder != null) {
       boolean keepAll = holder.isLibraryClass() || holder.accessFlags.isAnnotation();
@@ -326,7 +345,7 @@
   }
 
   private void reserveNamesForMethod(DexEncodedMethod method,
-      boolean keepAll, NamingState<DexProto> state) {
+      boolean keepAll, NamingState<DexProto, ?> state) {
     if (keepAll || rootSet.noObfuscation.contains(method)) {
       state.reserveName(method.method.name, method.method.proto);
       globalState.reserveName(method.method.name, method.method.proto);
diff --git a/src/main/java/com/android/tools/r8/naming/NamingState.java b/src/main/java/com/android/tools/r8/naming/NamingState.java
index 2011530..874d506 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingState.java
@@ -10,48 +10,54 @@
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
-import java.util.IdentityHashMap;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 
-class NamingState<T extends CachedHashValueDexItem> {
+class NamingState<ProtoType extends CachedHashValueDexItem, KeyType> {
 
-  private final NamingState<T> parent;
-  private final Map<T, InternalState> usedNames = new IdentityHashMap<>();
+  private final NamingState<ProtoType, KeyType> parent;
+  private final Map<KeyType, InternalState> usedNames = new HashMap<>();
   private final DexItemFactory itemFactory;
   private final ImmutableList<String> dictionary;
+  private final Function<ProtoType, KeyType> keyTransform;
 
-  static <T extends CachedHashValueDexItem> NamingState<T> createRoot(
-      DexItemFactory itemFactory, ImmutableList<String> dictionary) {
-    return new NamingState<>(null, itemFactory, dictionary);
+  static <S, T extends CachedHashValueDexItem> NamingState<T, S> createRoot(
+      DexItemFactory itemFactory, ImmutableList<String> dictionary, Function<T, S> keyTransform) {
+    return new NamingState<>(null, itemFactory, dictionary, keyTransform);
   }
 
   private NamingState(
-      NamingState<T> parent,
+      NamingState<ProtoType, KeyType> parent,
       DexItemFactory itemFactory,
-      ImmutableList<String> dictionary) {
+      ImmutableList<String> dictionary,
+      Function<ProtoType, KeyType> keyTransform) {
     this.parent = parent;
     this.itemFactory = itemFactory;
     this.dictionary = dictionary;
+    this.keyTransform = keyTransform;
   }
 
-  public NamingState<T> createChild() {
-    return new NamingState<>(this, itemFactory, dictionary);
+  public NamingState<ProtoType, KeyType> createChild() {
+    return new NamingState<>(this, itemFactory, dictionary, keyTransform);
   }
 
-  private InternalState findInternalStateFor(T proto) {
-    InternalState result = usedNames.get(proto);
+  private InternalState findInternalStateFor(ProtoType proto) {
+    KeyType key = keyTransform.apply(proto);
+    InternalState result = usedNames.get(key);
     if (result == null && parent != null) {
       result = parent.findInternalStateFor(proto);
     }
     return result;
   }
 
-  private InternalState getOrCreateInternalStateFor(T proto) {
+  private InternalState getOrCreateInternalStateFor(ProtoType proto) {
     // TODO(herhut): Maybe allocate these sparsely and search via state chain.
-    InternalState result = usedNames.get(proto);
+    KeyType key = keyTransform.apply(proto);
+    InternalState result = usedNames.get(key);
     if (result == null) {
       if (parent != null) {
         InternalState parentState = parent.getOrCreateInternalStateFor(proto);
@@ -59,12 +65,12 @@
       } else {
         result = new InternalState(itemFactory, null, dictionary);
       }
-      usedNames.put(proto, result);
+      usedNames.put(key, result);
     }
     return result;
   }
 
-  public DexString getAssignedNameFor(DexString name, T proto) {
+  public DexString getAssignedNameFor(DexString name, ProtoType proto) {
     InternalState state = findInternalStateFor(proto);
     if (state == null) {
       return null;
@@ -72,7 +78,7 @@
     return state.getAssignedNameFor(name);
   }
 
-  public DexString assignNewNameFor(DexString original, T proto, boolean markAsUsed) {
+  public DexString assignNewNameFor(DexString original, ProtoType proto, boolean markAsUsed) {
     DexString result = getAssignedNameFor(original, proto);
     if (result == null) {
       InternalState state = getOrCreateInternalStateFor(proto);
@@ -81,12 +87,12 @@
     return result;
   }
 
-  public void reserveName(DexString name, T proto) {
+  public void reserveName(DexString name, ProtoType proto) {
     InternalState state = getOrCreateInternalStateFor(proto);
     state.reserveName(name);
   }
 
-  public boolean isReserved(DexString name, T proto) {
+  public boolean isReserved(DexString name, ProtoType proto) {
     InternalState state = findInternalStateFor(proto);
     if (state == null) {
       return false;
@@ -94,7 +100,7 @@
     return state.isReserved(name);
   }
 
-  public boolean isAvailable(DexString original, T proto, DexString candidate) {
+  public boolean isAvailable(DexString original, ProtoType proto, DexString candidate) {
     InternalState state = findInternalStateFor(proto);
     if (state == null) {
       return true;
@@ -103,7 +109,7 @@
     return state.isAvailable(candidate);
   }
 
-  public void addRenaming(DexString original, T proto, DexString newName) {
+  public void addRenaming(DexString original, ProtoType proto, DexString newName) {
     InternalState state = getOrCreateInternalStateFor(proto);
     state.addRenaming(original, newName);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index b3cfcb2..bdf7748 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -45,6 +45,7 @@
     private boolean keepParameterNames;
     private ProguardClassFilter.Builder adaptClassStrings = ProguardClassFilter.builder();
     private boolean forceProguardCompatibility = false;
+    private boolean overloadAggressively;
 
     private Builder(DexItemFactory dexItemFactory) {
       this.dexItemFactory = dexItemFactory;
@@ -211,10 +212,13 @@
       this.forceProguardCompatibility = forceProguardCompatibility;
     }
 
+    public void setOverloadAggressively(boolean overloadAggressively) {
+      this.overloadAggressively = overloadAggressively;
+    }
+
     public ProguardConfiguration build() throws CompilationException {
       ProguardKeepAttributes keepAttributes;
 
-
       if (forceProguardCompatibility
           && !isObfuscating()
           && keepAttributePatterns.size() == 0) {
@@ -246,6 +250,7 @@
           rules,
           printSeeds,
           seedFile,
+          overloadAggressively,
           DictionaryReader.readAllNames(obfuscationDictionary),
           DictionaryReader.readAllNames(classObfuscationDictionary),
           DictionaryReader.readAllNames(packageObfuscationDictionary),
@@ -277,6 +282,7 @@
   protected final ImmutableList<ProguardConfigurationRule> rules;
   private final boolean printSeeds;
   private final Path seedFile;
+  private final boolean overloadAggressively;
   private final ImmutableList<String> obfuscationDictionary;
   private final ImmutableList<String> classObfuscationDictionary;
   private final ImmutableList<String> packageObfuscationDictionary;
@@ -307,6 +313,7 @@
       List<ProguardConfigurationRule> rules,
       boolean printSeeds,
       Path seedFile,
+      boolean overloadAggressively,
       ImmutableList<String> obfuscationDictionary,
       ImmutableList<String> classObfuscationDictionary,
       ImmutableList<String> packageObfuscationDictionary,
@@ -335,6 +342,7 @@
     this.rules = ImmutableList.copyOf(rules);
     this.printSeeds = printSeeds;
     this.seedFile = seedFile;
+    this.overloadAggressively = overloadAggressively;
     this.obfuscationDictionary = obfuscationDictionary;
     this.classObfuscationDictionary = classObfuscationDictionary;
     this.packageObfuscationDictionary = packageObfuscationDictionary;
@@ -443,6 +451,10 @@
     return rules;
   }
 
+  public boolean isOverloadAggressively() {
+    return overloadAggressively;
+  }
+
   public ImmutableList<String> getObfuscationDictionary() {
     return obfuscationDictionary;
   }
@@ -470,7 +482,7 @@
   public static ProguardConfiguration defaultConfiguration(DexItemFactory dexItemFactory) {
     try {
       return builderInitializedWithDefaults(dexItemFactory).build();
-    } catch(CompilationException e) {
+    } catch (CompilationException e) {
       // Building a builder initialized with defaults will not throw CompilationException because
       // DictionaryReader is called with empty lists.
       throw new RuntimeException();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index d24a357..a24bdb9 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -51,7 +51,6 @@
           "filterlibraryjarswithorginalprogramjars",
           "dontskipnonpubliclibraryclasses",
           "dontskipnonpubliclibraryclassmembers",
-          "overloadaggressively",
           "invokebasemethod");
   private static final List<String> IGNORED_CLASS_DESCRIPTOR_OPTIONS = ImmutableList
       .of("isclassnamestring",
@@ -245,6 +244,8 @@
             configurationBuilder.setFlattenPackagePrefix("");
           }
         }
+      } else if (acceptString("overloadaggressively")) {
+        configurationBuilder.setOverloadAggressively(true);
       } else if (acceptString("allowaccessmodification")) {
         configurationBuilder.setAllowAccessModification(true);
       } else if (acceptString("printmapping")) {
diff --git a/src/test/examples/uniquemembernames/BaseCls.java b/src/test/examples/uniquemembernames/BaseCls.java
index 9843c7b..2ee24fe 100644
--- a/src/test/examples/uniquemembernames/BaseCls.java
+++ b/src/test/examples/uniquemembernames/BaseCls.java
@@ -5,17 +5,17 @@
 
 public abstract class BaseCls {
 
-  protected int a;
+  protected int c;
   protected double f2;
 
-  protected abstract int a();
+  protected abstract int c();
 
   protected int foo() {
-    return a * (int) f2;
+    return c * (int) f2;
   }
 
   protected double bar() {
-    return a * f2;
+    return c * f2;
   }
 
 }
diff --git a/src/test/examples/uniquemembernames/ClsA.java b/src/test/examples/uniquemembernames/ClsA.java
index 430a255..563a070 100644
--- a/src/test/examples/uniquemembernames/ClsA.java
+++ b/src/test/examples/uniquemembernames/ClsA.java
@@ -6,8 +6,8 @@
 public class ClsA extends BaseCls {
 
   @Override
-  protected int a() {
-    return foo() + a;
+  protected int c() {
+    return foo() + c;
   }
 
   @Override
diff --git a/src/test/examples/uniquemembernames/ClsB.java b/src/test/examples/uniquemembernames/ClsB.java
index 2659d5a..f9d4ecd 100644
--- a/src/test/examples/uniquemembernames/ClsB.java
+++ b/src/test/examples/uniquemembernames/ClsB.java
@@ -6,13 +6,13 @@
 public class ClsB extends BaseCls {
 
   @Override
-  protected int a() {
-    return foo() - a;
+  protected int c() {
+    return foo() - c;
   }
 
   @Override
   protected double bar() {
-    return f2 != 0 ? a / f2 : Double.MAX_VALUE;
+    return f2 != 0 ? c / f2 : Double.MAX_VALUE;
   }
 
 }
diff --git a/src/test/examples/uniquemembernames/Shaking.java b/src/test/examples/uniquemembernames/Shaking.java
index ce7ed5d..a1b90ba 100644
--- a/src/test/examples/uniquemembernames/Shaking.java
+++ b/src/test/examples/uniquemembernames/Shaking.java
@@ -11,7 +11,7 @@
   public static void main(String[] args) {
     List<BaseCls> bases = Arrays.asList(new ClsA(), new ClsB());
     for (BaseCls base : bases) {
-      base.a();
+      base.c();
       base.foo();
       base.bar();
     }
diff --git a/src/test/examples/uniquemembernames/keep-rules-1.txt b/src/test/examples/uniquemembernames/keep-rules-1.txt
index 2acd66a..40057e4 100644
--- a/src/test/examples/uniquemembernames/keep-rules-1.txt
+++ b/src/test/examples/uniquemembernames/keep-rules-1.txt
@@ -7,8 +7,8 @@
 
 # Keep test fields/methods for deterministic naming
 -keepclassmembers public class **.BaseCls {
-  *** a;
-  *** a(...);
+  *** c;
+  *** c(...);
 }
 
 -keepclassmembers public class **.AnotherCls {
diff --git a/src/test/examples/uniquemembernames/keep-rules-2.txt b/src/test/examples/uniquemembernames/keep-rules-2.txt
index 3f1bdad..b5c5dcf 100644
--- a/src/test/examples/uniquemembernames/keep-rules-2.txt
+++ b/src/test/examples/uniquemembernames/keep-rules-2.txt
@@ -7,8 +7,8 @@
 
 # Keep test fields/methods for deterministic naming
 -keepclassmembers public class **.BaseCls {
-  *** a;
-  *** a(...);
+  *** c;
+  *** c(...);
 }
 
 -keepclassmembers public class **.AnotherCls {
diff --git a/src/test/java/com/android/tools/r8/naming/UseUniqueMemberNameTest.java b/src/test/java/com/android/tools/r8/naming/UseUniqueMemberNameTest.java
index 3094756..498f4df 100644
--- a/src/test/java/com/android/tools/r8/naming/UseUniqueMemberNameTest.java
+++ b/src/test/java/com/android/tools/r8/naming/UseUniqueMemberNameTest.java
@@ -103,9 +103,9 @@
 
     // With -useuniquemembernames, foo() with the same signature should be renamed to the same name.
     assertEquals(foo_renamed, another_foo_renamed);
-    // But, those cannot be renamed to a and b, as those are _globally_ reserved.
-    assertNotEquals("a", foo_renamed);
-    assertNotEquals("a", another_foo_renamed);
+    // But, those cannot be renamed to c and b, as those are _globally_ reserved.
+    assertNotEquals("c", foo_renamed);
+    assertNotEquals("c", another_foo_renamed);
     assertNotEquals("b", foo_renamed);
     assertNotEquals("b", another_foo_renamed);