RewrittenProto: Unify argument representation

Bug: 149681096
Change-Id: I47488dec51a84fd40024d9b135812af288f4a418
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index f4552a5..663524d 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -11,16 +11,56 @@
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.BooleanUtils;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceLinkedOpenHashMap;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import com.google.common.collect.Ordering;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceRBTreeMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 import it.unimi.dsi.fastutil.ints.IntBidirectionalIterator;
 import java.util.function.Consumer;
 
 public class RewrittenPrototypeDescription {
 
-  public static class RemovedArgumentInfo {
+  public interface ArgumentInfo {
+
+    @SuppressWarnings("ConstantConditions")
+    static ArgumentInfo combine(ArgumentInfo arg1, ArgumentInfo arg2) {
+      if (arg1 == null) {
+        assert arg2 != null;
+        return arg2;
+      }
+      if (arg2 == null) {
+        assert arg1 != null;
+        return arg1;
+      }
+      return arg1.combine(arg2);
+    }
+
+    ArgumentInfo NO_INFO =
+        info -> {
+          assert false : "ArgumentInfo NO_INFO should not be combined";
+          return info;
+        };
+
+    default boolean isRemovedArgumentInfo() {
+      return false;
+    }
+
+    default RemovedArgumentInfo asRemovedArgumentInfo() {
+      return null;
+    }
+
+    default boolean isRewrittenTypeInfo() {
+      return false;
+    }
+
+    default RewrittenTypeInfo asRewrittenTypeInfo() {
+      return null;
+    }
+
+    // ArgumentInfo are combined with `this` first, and the `info` argument second.
+    ArgumentInfo combine(ArgumentInfo info);
+  }
+
+  public static class RemovedArgumentInfo implements ArgumentInfo {
 
     public static class Builder {
 
@@ -66,140 +106,25 @@
     public boolean isNeverUsed() {
       return !isAlwaysNull;
     }
-  }
 
-  public static class RemovedArgumentInfoCollection {
-
-    private static final RemovedArgumentInfoCollection EMPTY = new RemovedArgumentInfoCollection();
-
-    private final Int2ReferenceSortedMap<RemovedArgumentInfo> removedArguments;
-
-    // Specific constructor for empty.
-    private RemovedArgumentInfoCollection() {
-      this.removedArguments = new Int2ReferenceLinkedOpenHashMap<>();
+    @Override
+    public boolean isRemovedArgumentInfo() {
+      return true;
     }
 
-    private RemovedArgumentInfoCollection(
-        Int2ReferenceSortedMap<RemovedArgumentInfo> removedArguments) {
-      assert removedArguments != null : "should use empty.";
-      assert !removedArguments.isEmpty() : "should use empty.";
-      this.removedArguments = removedArguments;
+    @Override
+    public RemovedArgumentInfo asRemovedArgumentInfo() {
+      return this;
     }
 
-    public static RemovedArgumentInfoCollection empty() {
-      return EMPTY;
-    }
-
-    public RemovedArgumentInfo getArgumentInfo(int argIndex) {
-      return removedArguments.get(argIndex);
-    }
-
-    public boolean hasRemovedArguments() {
-      return !removedArguments.isEmpty();
-    }
-
-    public boolean isArgumentRemoved(int argumentIndex) {
-      return removedArguments.containsKey(argumentIndex);
-    }
-
-    public DexType[] rewriteParameters(DexEncodedMethod encodedMethod) {
-      // Currently not allowed to remove the receiver of an instance method. This would involve
-      // changing invoke-direct/invoke-virtual into invoke-static.
-      assert encodedMethod.isStatic() || !isArgumentRemoved(0);
-      DexType[] params = encodedMethod.method.proto.parameters.values;
-      if (!hasRemovedArguments()) {
-        return params;
-      }
-      DexType[] newParams = new DexType[params.length - numberOfRemovedArguments()];
-      int offset = encodedMethod.isStatic() ? 0 : 1;
-      int newParamIndex = 0;
-      for (int oldParamIndex = 0; oldParamIndex < params.length; ++oldParamIndex) {
-        if (!isArgumentRemoved(oldParamIndex + offset)) {
-          newParams[newParamIndex++] = params[oldParamIndex];
-        }
-      }
-      return newParams;
-    }
-
-    public int numberOfRemovedArguments() {
-      return removedArguments != null ? removedArguments.size() : 0;
-    }
-
-    public RemovedArgumentInfoCollection combine(RemovedArgumentInfoCollection info) {
-      if (hasRemovedArguments()) {
-        if (!info.hasRemovedArguments()) {
-          return this;
-        }
-      } else {
-        return info;
-      }
-
-      Int2ReferenceSortedMap<RemovedArgumentInfo> newRemovedArguments =
-          new Int2ReferenceLinkedOpenHashMap<>();
-      newRemovedArguments.putAll(removedArguments);
-      IntBidirectionalIterator iterator = removedArguments.keySet().iterator();
-      int offset = 0;
-      for (int pendingArgIndex : info.removedArguments.keySet()) {
-        int nextArgindex = peekNextOrMax(iterator);
-        while (nextArgindex <= pendingArgIndex + offset) {
-          iterator.nextInt();
-          nextArgindex = peekNextOrMax(iterator);
-          offset++;
-        }
-        assert !newRemovedArguments.containsKey(pendingArgIndex + offset);
-        newRemovedArguments.put(
-            pendingArgIndex + offset, info.removedArguments.get(pendingArgIndex));
-      }
-      return new RemovedArgumentInfoCollection(newRemovedArguments);
-    }
-
-    static int peekNextOrMax(IntBidirectionalIterator iterator) {
-      if (iterator.hasNext()) {
-        int i = iterator.nextInt();
-        iterator.previousInt();
-        return i;
-      }
-      return Integer.MAX_VALUE;
-    }
-
-    public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
-        DexEncodedMethod method) {
-      if (numberOfRemovedArguments() > 0 && !method.parameterAnnotationsList.isEmpty()) {
-        return builder -> {
-          int firstArgumentIndex = BooleanUtils.intValue(!method.isStatic());
-          builder.removeParameterAnnotations(
-              oldIndex -> isArgumentRemoved(oldIndex + firstArgumentIndex));
-        };
-      }
-      return null;
-    }
-
-    public static Builder builder() {
-      return new Builder();
-    }
-
-    public static class Builder {
-      private Int2ReferenceSortedMap<RemovedArgumentInfo> removedArguments;
-
-      public Builder addRemovedArgument(int argIndex, RemovedArgumentInfo argInfo) {
-        if (removedArguments == null) {
-          removedArguments = new Int2ReferenceLinkedOpenHashMap<>();
-        }
-        assert !removedArguments.containsKey(argIndex);
-        removedArguments.put(argIndex, argInfo);
-        return this;
-      }
-
-      public RemovedArgumentInfoCollection build() {
-        if (removedArguments == null || removedArguments.isEmpty()) {
-          return EMPTY;
-        }
-        return new RemovedArgumentInfoCollection(removedArguments);
-      }
+    @Override
+    public ArgumentInfo combine(ArgumentInfo info) {
+      assert false : "Once the argument is removed one cannot modify it any further.";
+      return this;
     }
   }
 
-  public static class RewrittenTypeInfo {
+  public static class RewrittenTypeInfo implements ArgumentInfo {
 
     private final DexType oldType;
     private final DexType newType;
@@ -246,56 +171,75 @@
       }
       return TypeLatticeElement.getNull();
     }
+
+    @Override
+    public boolean isRewrittenTypeInfo() {
+      return true;
+    }
+
+    @Override
+    public RewrittenTypeInfo asRewrittenTypeInfo() {
+      return this;
+    }
+
+    @Override
+    public ArgumentInfo combine(ArgumentInfo info) {
+      if (info.isRemovedArgumentInfo()) {
+        return info;
+      }
+      assert info.isRewrittenTypeInfo();
+      RewrittenTypeInfo rewrittenTypeInfo = info.asRewrittenTypeInfo();
+      assert newType == rewrittenTypeInfo.oldType;
+      return new RewrittenTypeInfo(oldType, rewrittenTypeInfo.newType);
+    }
   }
 
-  public static class RewrittenTypeArgumentInfoCollection {
+  public static class ArgumentInfoCollection {
 
-    private static final RewrittenTypeArgumentInfoCollection EMPTY =
-        new RewrittenTypeArgumentInfoCollection();
-    private final Int2ReferenceMap<RewrittenTypeInfo> rewrittenArgumentsInfo;
+    private static final ArgumentInfoCollection EMPTY = new ArgumentInfoCollection();
 
-    private RewrittenTypeArgumentInfoCollection() {
-      this.rewrittenArgumentsInfo = new Int2ReferenceOpenHashMap<>(0);
+    private final Int2ReferenceSortedMap<ArgumentInfo> argumentInfos;
+
+    // Specific constructor for empty.
+    private ArgumentInfoCollection() {
+      this.argumentInfos = new Int2ReferenceRBTreeMap<>();
     }
 
-    private RewrittenTypeArgumentInfoCollection(
-        Int2ReferenceMap<RewrittenTypeInfo> rewrittenArgumentsInfo) {
-      this.rewrittenArgumentsInfo = rewrittenArgumentsInfo;
+    private ArgumentInfoCollection(Int2ReferenceSortedMap<ArgumentInfo> argumentInfos) {
+      assert argumentInfos != null : "should use empty.";
+      assert !argumentInfos.isEmpty() : "should use empty.";
+      this.argumentInfos = argumentInfos;
     }
 
-    public static RewrittenTypeArgumentInfoCollection empty() {
+    public static ArgumentInfoCollection empty() {
       return EMPTY;
     }
 
     public boolean isEmpty() {
-      return rewrittenArgumentsInfo.isEmpty();
+      return this == EMPTY;
     }
 
-    public RewrittenTypeInfo getArgumentRewrittenTypeInfo(int argIndex) {
-      return rewrittenArgumentsInfo.get(argIndex);
-    }
-
-    public boolean isArgumentRewrittenTypeInfo(int argIndex) {
-      return rewrittenArgumentsInfo.containsKey(argIndex);
-    }
-
-    public DexType[] rewriteParameters(DexEncodedMethod encodedMethod) {
-      DexType[] params = encodedMethod.method.proto.parameters.values;
-      if (isEmpty()) {
-        return params;
-      }
-      DexType[] newParams = new DexType[params.length];
-      int offset = encodedMethod.isStatic() ? 0 : 1;
-      for (int index = 0; index < params.length; index++) {
-        RewrittenTypeInfo argInfo = getArgumentRewrittenTypeInfo(index + offset);
-        if (argInfo != null) {
-          assert params[index] == argInfo.oldType;
-          newParams[index] = argInfo.newType;
-        } else {
-          newParams[index] = params[index];
+    public boolean hasRemovedArguments() {
+      for (ArgumentInfo value : argumentInfos.values()) {
+        if (value.isRemovedArgumentInfo()) {
+          return true;
         }
       }
-      return newParams;
+      return false;
+    }
+
+    public int numberOfRemovedArguments() {
+      int removed = 0;
+      for (ArgumentInfo value : argumentInfos.values()) {
+        if (value.isRemovedArgumentInfo()) {
+          removed++;
+        }
+      }
+      return removed;
+    }
+
+    public ArgumentInfo getArgumentInfo(int argumentIndex) {
+      return argumentInfos.getOrDefault(argumentIndex, ArgumentInfo.NO_INFO);
     }
 
     public static Builder builder() {
@@ -304,69 +248,138 @@
 
     public static class Builder {
 
-      private Int2ReferenceMap<RewrittenTypeInfo> rewrittenArgumentsInfo;
+      private Int2ReferenceSortedMap<ArgumentInfo> argumentInfos;
 
-      public Builder rewriteArgument(int argIndex, DexType oldType, DexType newType) {
-        if (rewrittenArgumentsInfo == null) {
-          rewrittenArgumentsInfo = new Int2ReferenceOpenHashMap<>();
+      public void addArgumentInfo(int argIndex, ArgumentInfo argInfo) {
+        if (argumentInfos == null) {
+          argumentInfos = new Int2ReferenceRBTreeMap<>();
         }
-        rewrittenArgumentsInfo.put(argIndex, new RewrittenTypeInfo(oldType, newType));
-        return this;
+        assert !argumentInfos.containsKey(argIndex);
+        argumentInfos.put(argIndex, argInfo);
       }
 
-      public RewrittenTypeArgumentInfoCollection build() {
-        if (rewrittenArgumentsInfo == null) {
+      public ArgumentInfoCollection build() {
+        if (argumentInfos == null || argumentInfos.isEmpty()) {
           return EMPTY;
         }
-        assert !rewrittenArgumentsInfo.isEmpty();
-        return new RewrittenTypeArgumentInfoCollection(rewrittenArgumentsInfo);
+        return new ArgumentInfoCollection(argumentInfos);
       }
     }
+
+    public DexType[] rewriteParameters(DexEncodedMethod encodedMethod) {
+      // Currently not allowed to remove the receiver of an instance method. This would involve
+      // changing invoke-direct/invoke-virtual into invoke-static.
+      assert encodedMethod.isStatic() || !getArgumentInfo(0).isRemovedArgumentInfo();
+      DexType[] params = encodedMethod.method.proto.parameters.values;
+      if (isEmpty()) {
+        return params;
+      }
+      DexType[] newParams = new DexType[params.length - numberOfRemovedArguments()];
+      int offset = encodedMethod.isStatic() ? 0 : 1;
+      int newParamIndex = 0;
+      for (int oldParamIndex = 0; oldParamIndex < params.length; oldParamIndex++) {
+        ArgumentInfo argInfo = argumentInfos.get(oldParamIndex + offset);
+        if (argInfo == null) {
+          newParams[newParamIndex++] = params[oldParamIndex];
+        } else if (argInfo.isRewrittenTypeInfo()) {
+          RewrittenTypeInfo rewrittenTypeInfo = argInfo.asRewrittenTypeInfo();
+          assert params[oldParamIndex] == rewrittenTypeInfo.oldType;
+          newParams[newParamIndex++] = rewrittenTypeInfo.newType;
+        }
+      }
+      return newParams;
+    }
+
+    public ArgumentInfoCollection combine(ArgumentInfoCollection info) {
+      if (isEmpty()) {
+        return info;
+      } else {
+        if (info.isEmpty()) {
+          return this;
+        }
+      }
+
+      Int2ReferenceSortedMap<ArgumentInfo> newArgInfos = new Int2ReferenceRBTreeMap<>();
+      newArgInfos.putAll(argumentInfos);
+      IntBidirectionalIterator iterator = argumentInfos.keySet().iterator();
+      int offset = 0;
+      int nextArgIndex;
+      for (int pendingArgIndex : info.argumentInfos.keySet()) {
+        nextArgIndex = peekNextOrMax(iterator);
+        while (nextArgIndex <= pendingArgIndex + offset) {
+          iterator.nextInt();
+          ArgumentInfo argumentInfo = argumentInfos.get(nextArgIndex);
+          nextArgIndex = peekNextOrMax(iterator);
+          if (argumentInfo.isRemovedArgumentInfo()) {
+            offset++;
+          }
+        }
+        ArgumentInfo newArgInfo =
+            nextArgIndex == pendingArgIndex + offset
+                ? ArgumentInfo.combine(
+                    argumentInfos.get(nextArgIndex), info.argumentInfos.get(pendingArgIndex))
+                : info.argumentInfos.get(pendingArgIndex);
+        newArgInfos.put(pendingArgIndex + offset, newArgInfo);
+      }
+      assert Ordering.natural().isOrdered(newArgInfos.keySet());
+      return new ArgumentInfoCollection(newArgInfos);
+    }
+
+    static int peekNextOrMax(IntBidirectionalIterator iterator) {
+      if (iterator.hasNext()) {
+        int i = iterator.nextInt();
+        iterator.previousInt();
+        return i;
+      }
+      return Integer.MAX_VALUE;
+    }
+
+    public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
+        DexEncodedMethod method) {
+      if (numberOfRemovedArguments() > 0 && !method.parameterAnnotationsList.isEmpty()) {
+        return builder -> {
+          int firstArgumentIndex = BooleanUtils.intValue(!method.isStatic());
+          builder.removeParameterAnnotations(
+              oldIndex -> getArgumentInfo(oldIndex + firstArgumentIndex).isRemovedArgumentInfo());
+        };
+      }
+      return null;
+    }
   }
 
   private static final RewrittenPrototypeDescription none = new RewrittenPrototypeDescription();
 
-  // TODO(b/149681096): Unify RewrittenPrototypeDescription.
   private final boolean extraNullParameter;
-  private final RemovedArgumentInfoCollection removedArgumentInfoCollection;
+  private final ArgumentInfoCollection argumentInfoCollection;
   private final RewrittenTypeInfo rewrittenReturnInfo;
-  private final RewrittenTypeArgumentInfoCollection rewrittenTypeArgumentInfoCollection;
 
   private RewrittenPrototypeDescription() {
-    this(
-        false,
-        RemovedArgumentInfoCollection.empty(),
-        null,
-        RewrittenTypeArgumentInfoCollection.empty());
+    this(false, null, ArgumentInfoCollection.empty());
   }
 
   private RewrittenPrototypeDescription(
       boolean extraNullParameter,
-      RemovedArgumentInfoCollection removedArgumentsInfo,
       RewrittenTypeInfo rewrittenReturnInfo,
-      RewrittenTypeArgumentInfoCollection rewrittenArgumentsInfo) {
-    assert removedArgumentsInfo != null;
+      ArgumentInfoCollection argumentsInfo) {
+    assert argumentsInfo != null;
     this.extraNullParameter = extraNullParameter;
-    this.removedArgumentInfoCollection = removedArgumentsInfo;
     this.rewrittenReturnInfo = rewrittenReturnInfo;
-    this.rewrittenTypeArgumentInfoCollection = rewrittenArgumentsInfo;
+    this.argumentInfoCollection = argumentsInfo;
   }
 
   public static RewrittenPrototypeDescription createForUninstantiatedTypes(
       DexMethod method,
       AppView<AppInfoWithLiveness> appView,
-      RemovedArgumentInfoCollection removedArgumentsInfo) {
+      ArgumentInfoCollection removedArgumentsInfo) {
     DexType returnType = method.proto.returnType;
     RewrittenTypeInfo returnInfo =
         returnType.isAlwaysNull(appView) ? RewrittenTypeInfo.toVoid(returnType, appView) : null;
-    return new RewrittenPrototypeDescription(
-        false, removedArgumentsInfo, returnInfo, RewrittenTypeArgumentInfoCollection.empty());
+    return new RewrittenPrototypeDescription(false, returnInfo, removedArgumentsInfo);
   }
 
   public static RewrittenPrototypeDescription createForRewrittenTypes(
-      RewrittenTypeInfo returnInfo, RewrittenTypeArgumentInfoCollection rewrittenArgumentsInfo) {
-    return new RewrittenPrototypeDescription(
-        false, RemovedArgumentInfoCollection.empty(), returnInfo, rewrittenArgumentsInfo);
+      RewrittenTypeInfo returnInfo, ArgumentInfoCollection rewrittenArgumentsInfo) {
+    return new RewrittenPrototypeDescription(false, returnInfo, rewrittenArgumentsInfo);
   }
 
   public static RewrittenPrototypeDescription none() {
@@ -374,10 +387,7 @@
   }
 
   public boolean isEmpty() {
-    return !extraNullParameter
-        && !removedArgumentInfoCollection.hasRemovedArguments()
-        && rewrittenReturnInfo == null
-        && rewrittenTypeArgumentInfoCollection.isEmpty();
+    return !extraNullParameter && rewrittenReturnInfo == null && argumentInfoCollection.isEmpty();
   }
 
   public boolean hasExtraNullParameter() {
@@ -388,12 +398,8 @@
     return rewrittenReturnInfo != null && rewrittenReturnInfo.hasBeenChangedToReturnVoid(appView);
   }
 
-  public RemovedArgumentInfoCollection getRemovedArgumentInfoCollection() {
-    return removedArgumentInfoCollection;
-  }
-
-  public RewrittenTypeArgumentInfoCollection getRewrittenTypeArgumentInfoCollection() {
-    return rewrittenTypeArgumentInfoCollection;
+  public ArgumentInfoCollection getArgumentInfoCollection() {
+    return argumentInfoCollection;
   }
 
   public boolean hasRewrittenReturnInfo() {
@@ -419,7 +425,6 @@
     return instruction;
   }
 
-  @SuppressWarnings("ConstantConditions")
   public DexProto rewriteProto(DexEncodedMethod encodedMethod, DexItemFactory dexItemFactory) {
     if (isEmpty()) {
       return encodedMethod.method.proto;
@@ -428,18 +433,8 @@
         rewrittenReturnInfo != null
             ? rewrittenReturnInfo.newType
             : encodedMethod.method.proto.returnType;
-    // TODO(b/149681096): Unify RewrittenPrototypeDescription, have a single variable for return.
-    if (rewrittenReturnInfo != null || !rewrittenTypeArgumentInfoCollection.isEmpty()) {
-      assert !removedArgumentInfoCollection.hasRemovedArguments();
-      DexType[] newParameters =
-          rewrittenTypeArgumentInfoCollection.rewriteParameters(encodedMethod);
-      return dexItemFactory.createProto(newReturnType, newParameters);
-    } else {
-      assert rewrittenReturnInfo == null;
-      assert rewrittenTypeArgumentInfoCollection.isEmpty();
-      DexType[] newParameters = removedArgumentInfoCollection.rewriteParameters(encodedMethod);
-      return dexItemFactory.createProto(newReturnType, newParameters);
-    }
+    DexType[] newParameters = argumentInfoCollection.rewriteParameters(encodedMethod);
+    return dexItemFactory.createProto(newReturnType, newParameters);
   }
 
   public RewrittenPrototypeDescription withConstantReturn(
@@ -448,27 +443,19 @@
     return !hasBeenChangedToReturnVoid(appView)
         ? new RewrittenPrototypeDescription(
             extraNullParameter,
-            removedArgumentInfoCollection,
             RewrittenTypeInfo.toVoid(oldReturnType, appView),
-            rewrittenTypeArgumentInfoCollection)
+            argumentInfoCollection)
         : this;
   }
 
-  public RewrittenPrototypeDescription withRemovedArguments(RemovedArgumentInfoCollection other) {
+  public RewrittenPrototypeDescription withRemovedArguments(ArgumentInfoCollection other) {
     return new RewrittenPrototypeDescription(
-        extraNullParameter,
-        removedArgumentInfoCollection.combine(other),
-        rewrittenReturnInfo,
-        rewrittenTypeArgumentInfoCollection);
+        extraNullParameter, rewrittenReturnInfo, argumentInfoCollection.combine(other));
   }
 
   public RewrittenPrototypeDescription withExtraNullParameter() {
     return !extraNullParameter
-        ? new RewrittenPrototypeDescription(
-            true,
-            removedArgumentInfoCollection,
-            rewrittenReturnInfo,
-            rewrittenTypeArgumentInfoCollection)
+        ? new RewrittenPrototypeDescription(true, rewrittenReturnInfo, argumentInfoCollection)
         : this;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 3246a8b..3c33035 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -34,9 +34,9 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeArgumentInfoCollection;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.PrimitiveTypeLatticeElement;
@@ -456,11 +456,11 @@
       AppView<?> appView, DexMethod method) {
     RewrittenPrototypeDescription prototypeChanges =
         appView.graphLense().lookupPrototypeChanges(method);
-    if (Log.ENABLED && prototypeChanges.getRemovedArgumentInfoCollection().hasRemovedArguments()) {
+    if (Log.ENABLED && prototypeChanges.getArgumentInfoCollection().hasRemovedArguments()) {
       Log.info(
           IRBuilder.class,
           "Removed "
-              + prototypeChanges.getRemovedArgumentInfoCollection().numberOfRemovedArguments()
+              + prototypeChanges.getArgumentInfoCollection().numberOfRemovedArguments()
               + " arguments from "
               + method.toSourceString());
     }
@@ -522,11 +522,7 @@
 
   public void buildArgumentsWithRewrittenPrototypeChanges(
       int register, DexEncodedMethod method, BiConsumer<Integer, DexType> writeCallback) {
-    RemovedArgumentInfoCollection removedArgumentsInfo =
-        prototypeChanges.getRemovedArgumentInfoCollection();
-    RewrittenTypeArgumentInfoCollection rewrittenArgumentsInfo =
-        prototypeChanges.getRewrittenTypeArgumentInfoCollection();
-    assert !removedArgumentsInfo.hasRemovedArguments() || rewrittenArgumentsInfo.isEmpty();
+    ArgumentInfoCollection argumentsInfo = prototypeChanges.getArgumentInfoCollection();
 
     // Fill in the Argument instructions (incomingRegisterSize last registers) in the argument
     // block.
@@ -541,24 +537,24 @@
 
     int numberOfArguments =
         method.method.proto.parameters.values.length
-            + removedArgumentsInfo.numberOfRemovedArguments()
+            + argumentsInfo.numberOfRemovedArguments()
             + (method.isStatic() ? 0 : 1);
 
     int usedArgumentIndex = 0;
     while (argumentIndex < numberOfArguments) {
       TypeLatticeElement type;
-      if (removedArgumentsInfo.isArgumentRemoved(argumentIndex)) {
-        RemovedArgumentInfo argumentInfo = removedArgumentsInfo.getArgumentInfo(argumentIndex);
-        writeCallback.accept(register, argumentInfo.getType());
+      ArgumentInfo argumentInfo = argumentsInfo.getArgumentInfo(argumentIndex);
+      if (argumentInfo.isRemovedArgumentInfo()) {
+        RemovedArgumentInfo removedArgumentInfo = argumentInfo.asRemovedArgumentInfo();
+        writeCallback.accept(register, removedArgumentInfo.getType());
         type =
             TypeLatticeElement.fromDexType(
-                argumentInfo.getType(), Nullability.maybeNull(), appView);
-        addConstantOrUnusedArgument(register, argumentInfo);
+                removedArgumentInfo.getType(), Nullability.maybeNull(), appView);
+        addConstantOrUnusedArgument(register, removedArgumentInfo);
       } else {
         DexType argType;
-        if (rewrittenArgumentsInfo.isArgumentRewrittenTypeInfo(argumentIndex)) {
-          RewrittenTypeInfo argumentRewrittenTypeInfo =
-              rewrittenArgumentsInfo.getArgumentRewrittenTypeInfo(argumentIndex);
+        if (argumentInfo.isRewrittenTypeInfo()) {
+          RewrittenTypeInfo argumentRewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
           assert method.method.proto.parameters.values[usedArgumentIndex]
               == argumentRewrittenTypeInfo.getNewType();
           // The old type is used to prevent that a changed value from reference to primitive
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index eb9e7dc..0266798 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -28,8 +28,8 @@
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeArgumentInfoCollection;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
 import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
@@ -173,41 +173,50 @@
             RewrittenPrototypeDescription prototypeChanges =
                 graphLense.lookupPrototypeChanges(actualTarget);
 
-            // TODO(b/149681096): Unify RewrittenPrototypeDescription and merge rewritten type and
-            // removedArgument rewriting.
-            // When converting types the default value may change (for example default value of
-            // a reference type is null while default value of int is 0).
             List<Value> newInValues;
-            RewrittenTypeArgumentInfoCollection rewrittenTypeArgumentInfoCollection =
-                prototypeChanges.getRewrittenTypeArgumentInfoCollection();
-            if (rewrittenTypeArgumentInfoCollection.isEmpty()) {
+            ArgumentInfoCollection argumentInfoCollection =
+                prototypeChanges.getArgumentInfoCollection();
+            if (argumentInfoCollection.isEmpty()) {
               newInValues = invoke.inValues();
             } else {
+              if (argumentInfoCollection.hasRemovedArguments()) {
+                if (Log.ENABLED) {
+                  Log.info(
+                      getClass(),
+                      "Invoked method "
+                          + invokedMethod.toSourceString()
+                          + " with "
+                          + argumentInfoCollection.numberOfRemovedArguments()
+                          + " arguments removed");
+                }
+              }
               newInValues = new ArrayList<>(actualTarget.proto.parameters.size());
               for (int i = 0; i < invoke.inValues().size(); i++) {
-                RewrittenTypeInfo argInfo =
-                    rewrittenTypeArgumentInfoCollection.getArgumentRewrittenTypeInfo(i);
-                Value value = invoke.inValues().get(i);
-                if (argInfo != null
-                    && argInfo.defaultValueHasChanged()
-                    && value.isConstNumber()
-                    && value.definition.asConstNumber().isZero()) {
-                  iterator.previous();
-                  // TODO(b/150188380): Add API to insert a const instruction with a type lattice.
-                  Value rewrittenDefaultValue =
-                      iterator.insertConstIntInstruction(code, appView.options(), 0);
-                  iterator.next();
-                  rewrittenDefaultValue.setTypeLattice(argInfo.defaultValueLatticeElement(appView));
-                  newInValues.add(rewrittenDefaultValue);
-                } else {
+                ArgumentInfo argumentInfo = argumentInfoCollection.getArgumentInfo(i);
+                if (argumentInfo.isRewrittenTypeInfo()) {
+                  RewrittenTypeInfo argInfo = argumentInfo.asRewrittenTypeInfo();
+                  Value value = invoke.inValues().get(i);
+                  // When converting types the default value may change (for example default value
+                  // of a reference type is null while default value of int is 0).
+                  if (argInfo.defaultValueHasChanged()
+                      && value.isConstNumber()
+                      && value.definition.asConstNumber().isZero()) {
+                    iterator.previous();
+                    // TODO(b/150188380): Add API to insert a const instruction with a type lattice.
+                    Value rewrittenNull =
+                        iterator.insertConstIntInstruction(code, appView.options(), 0);
+                    iterator.next();
+                    rewrittenNull.setTypeLattice(argInfo.defaultValueLatticeElement(appView));
+                    newInValues.add(rewrittenNull);
+                  } else {
+                    newInValues.add(invoke.inValues().get(i));
+                  }
+                } else if (!argumentInfo.isRemovedArgumentInfo()) {
                   newInValues.add(invoke.inValues().get(i));
                 }
               }
             }
 
-            RemovedArgumentInfoCollection removedArgumentsInfo =
-                prototypeChanges.getRemovedArgumentInfoCollection();
-
             ConstInstruction constantReturnMaterializingInstruction = null;
             if (prototypeChanges.hasBeenChangedToReturnVoid(appView) && invoke.outValue() != null) {
               constantReturnMaterializingInstruction =
@@ -229,26 +238,6 @@
                     ? null
                     : makeOutValue(invoke, code);
 
-            if (removedArgumentsInfo.hasRemovedArguments()) {
-              if (Log.ENABLED) {
-                Log.info(
-                    getClass(),
-                    "Invoked method "
-                        + invokedMethod.toSourceString()
-                        + " with "
-                        + removedArgumentsInfo.numberOfRemovedArguments()
-                        + " arguments removed");
-              }
-              // Remove removed arguments from the invoke.
-              List<Value> tempNewInValues = new ArrayList<>(actualTarget.proto.parameters.size());
-              for (int i = 0; i < newInValues.size(); i++) {
-                if (!removedArgumentsInfo.isArgumentRemoved(i)) {
-                  tempNewInValues.add(newInValues.get(i));
-                }
-              }
-              newInValues = tempNewInValues;
-            }
-
             if (prototypeChanges.hasExtraNullParameter()) {
               iterator.previous();
               Value extraNullValue = iterator.insertConstNullInstruction(code, appView.options());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 98e5ceb..b8ca4bc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -19,8 +19,8 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfoCollection;
 import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
 import com.android.tools.r8.ir.analysis.AbstractError;
 import com.android.tools.r8.ir.analysis.TypeChecker;
@@ -63,11 +63,11 @@
   public static class UninstantiatedTypeOptimizationGraphLense extends NestedGraphLense {
 
     private final AppView<?> appView;
-    private final Map<DexMethod, RemovedArgumentInfoCollection> removedArgumentsInfoPerMethod;
+    private final Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod;
 
     UninstantiatedTypeOptimizationGraphLense(
         BiMap<DexMethod, DexMethod> methodMap,
-        Map<DexMethod, RemovedArgumentInfoCollection> removedArgumentsInfoPerMethod,
+        Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod,
         AppView<?> appView) {
       super(
           ImmutableMap.of(),
@@ -89,8 +89,7 @@
         if (method.proto.returnType.isVoidType() && !originalMethod.proto.returnType.isVoidType()) {
           result = result.withConstantReturn(originalMethod.proto.returnType, appView);
         }
-        RemovedArgumentInfoCollection removedArgumentsInfo =
-            removedArgumentsInfoPerMethod.get(method);
+        ArgumentInfoCollection removedArgumentsInfo = removedArgumentsInfoPerMethod.get(method);
         if (removedArgumentsInfo != null) {
           result = result.withRemovedArguments(removedArgumentsInfo);
         }
@@ -127,8 +126,7 @@
 
     Map<Wrapper<DexMethod>, Set<DexType>> changedVirtualMethods = new HashMap<>();
     BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create();
-    Map<DexMethod, RemovedArgumentInfoCollection> removedArgumentsInfoPerMethod =
-        new IdentityHashMap<>();
+    Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod = new IdentityHashMap<>();
 
     TopDownClassHierarchyTraversal.forProgramClasses(appView)
         .visit(
@@ -153,7 +151,7 @@
       Map<Wrapper<DexMethod>, Set<DexType>> changedVirtualMethods,
       BiMap<DexMethod, DexMethod> methodMapping,
       MethodPoolCollection methodPoolCollection,
-      Map<DexMethod, RemovedArgumentInfoCollection> removedArgumentsInfoPerMethod) {
+      Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod) {
     MemberPool<DexMethod> methodPool = methodPoolCollection.get(clazz);
 
     if (clazz.isInterface()) {
@@ -202,8 +200,7 @@
       RewrittenPrototypeDescription prototypeChanges =
           prototypeChangesPerMethod.getOrDefault(
               encodedMethod, RewrittenPrototypeDescription.none());
-      RemovedArgumentInfoCollection removedArgumentsInfo =
-          prototypeChanges.getRemovedArgumentInfoCollection();
+      ArgumentInfoCollection removedArgumentsInfo = prototypeChanges.getArgumentInfoCollection();
       DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
       if (newMethod != method) {
         Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
@@ -236,8 +233,7 @@
       DexMethod method = encodedMethod.method;
       RewrittenPrototypeDescription prototypeChanges =
           getPrototypeChanges(encodedMethod, DISALLOW_ARGUMENT_REMOVAL);
-      RemovedArgumentInfoCollection removedArgumentsInfo =
-          prototypeChanges.getRemovedArgumentInfoCollection();
+      ArgumentInfoCollection removedArgumentsInfo = prototypeChanges.getArgumentInfoCollection();
       DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
       if (newMethod != method) {
         Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
@@ -265,8 +261,7 @@
       DexMethod method = encodedMethod.method;
       RewrittenPrototypeDescription prototypeChanges =
           getPrototypeChanges(encodedMethod, DISALLOW_ARGUMENT_REMOVAL);
-      RemovedArgumentInfoCollection removedArgumentsInfo =
-          prototypeChanges.getRemovedArgumentInfoCollection();
+      ArgumentInfoCollection removedArgumentsInfo = prototypeChanges.getArgumentInfoCollection();
       DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
       if (newMethod != method) {
         Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
@@ -304,13 +299,13 @@
         encodedMethod.method, appView, getRemovedArgumentsInfo(encodedMethod, strategy));
   }
 
-  private RemovedArgumentInfoCollection getRemovedArgumentsInfo(
+  private ArgumentInfoCollection getRemovedArgumentsInfo(
       DexEncodedMethod encodedMethod, Strategy strategy) {
     if (strategy == DISALLOW_ARGUMENT_REMOVAL) {
-      return RemovedArgumentInfoCollection.empty();
+      return ArgumentInfoCollection.empty();
     }
 
-    RemovedArgumentInfoCollection.Builder argInfosBuilder = RemovedArgumentInfoCollection.builder();
+    ArgumentInfoCollection.Builder argInfosBuilder = ArgumentInfoCollection.builder();
     DexProto proto = encodedMethod.method.proto;
     int offset = encodedMethod.isStatic() ? 0 : 1;
     for (int i = 0; i < proto.parameters.size(); ++i) {
@@ -318,7 +313,7 @@
       if (type.isAlwaysNull(appView)) {
         RemovedArgumentInfo removedArg =
             RemovedArgumentInfo.builder().setIsAlwaysNull().setType(type).build();
-        argInfosBuilder.addRemovedArgument(i + offset, removedArg);
+        argInfosBuilder.addArgumentInfo(i + offset, removedArg);
       }
     }
     return argInfosBuilder.build();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index e99ba9a..7c71bd9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -17,8 +17,8 @@
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfoCollection;
 import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
@@ -50,12 +50,11 @@
   private final MethodPoolCollection methodPoolCollection;
 
   private final BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create();
-  private final Map<DexMethod, RemovedArgumentInfoCollection> removedArguments =
-      new IdentityHashMap<>();
+  private final Map<DexMethod, ArgumentInfoCollection> removedArguments = new IdentityHashMap<>();
 
   public static class UnusedArgumentsGraphLense extends NestedGraphLense {
 
-    private final Map<DexMethod, RemovedArgumentInfoCollection> removedArguments;
+    private final Map<DexMethod, ArgumentInfoCollection> removedArguments;
 
     UnusedArgumentsGraphLense(
         Map<DexType, DexType> typeMap,
@@ -65,7 +64,7 @@
         BiMap<DexMethod, DexMethod> originalMethodSignatures,
         GraphLense previousLense,
         DexItemFactory dexItemFactory,
-        Map<DexMethod, RemovedArgumentInfoCollection> removedArguments) {
+        Map<DexMethod, ArgumentInfoCollection> removedArguments) {
       super(
           typeMap,
           methodMap,
@@ -84,7 +83,7 @@
               ? originalMethodSignatures.getOrDefault(method, method)
               : method;
       RewrittenPrototypeDescription result = previousLense.lookupPrototypeChanges(originalMethod);
-      RemovedArgumentInfoCollection removedArguments = this.removedArguments.get(method);
+      ArgumentInfoCollection removedArguments = this.removedArguments.get(method);
       return removedArguments != null ? result.withRemovedArguments(removedArguments) : result;
     }
   }
@@ -167,7 +166,7 @@
     }
 
     DexEncodedMethod removeArguments(
-        DexEncodedMethod method, DexMethod newSignature, RemovedArgumentInfoCollection unused) {
+        DexEncodedMethod method, DexMethod newSignature, ArgumentInfoCollection unused) {
       boolean removed = usedSignatures.remove(equivalence.wrap(method.method));
       assert removed;
 
@@ -208,7 +207,7 @@
     }
 
     DexEncodedMethod removeArguments(
-        DexEncodedMethod method, DexMethod newSignature, RemovedArgumentInfoCollection unused) {
+        DexEncodedMethod method, DexMethod newSignature, ArgumentInfoCollection unused) {
       methodPool.seen(equivalence.wrap(newSignature));
       return method.toTypeSubstitutedMethod(
           newSignature, unused.createParameterAnnotationsRemover(method));
@@ -234,7 +233,7 @@
         continue;
       }
 
-      RemovedArgumentInfoCollection unused = collectUnusedArguments(method);
+      ArgumentInfoCollection unused = collectUnusedArguments(method);
       if (unused != null && unused.hasRemovedArguments()) {
         DexProto newProto = createProtoWithRemovedArguments(method, unused);
         DexMethod newSignature = signatures.getNewSignature(method, newProto);
@@ -259,7 +258,7 @@
     List<DexEncodedMethod> virtualMethods = clazz.virtualMethods();
     for (int i = 0; i < virtualMethods.size(); i++) {
       DexEncodedMethod method = virtualMethods.get(i);
-      RemovedArgumentInfoCollection unused = collectUnusedArguments(method, methodPool);
+      ArgumentInfoCollection unused = collectUnusedArguments(method, methodPool);
       if (unused != null && unused.hasRemovedArguments()) {
         DexProto newProto = createProtoWithRemovedArguments(method, unused);
         DexMethod newSignature = signatures.getNewSignature(method, newProto);
@@ -279,11 +278,11 @@
     }
   }
 
-  private RemovedArgumentInfoCollection collectUnusedArguments(DexEncodedMethod method) {
+  private ArgumentInfoCollection collectUnusedArguments(DexEncodedMethod method) {
     return collectUnusedArguments(method, null);
   }
 
-  private RemovedArgumentInfoCollection collectUnusedArguments(
+  private ArgumentInfoCollection collectUnusedArguments(
       DexEncodedMethod method, MemberPool<DexMethod> methodPool) {
     if (ArgumentRemovalUtils.isPinned(method, appView)
         || appView.appInfo().keepUnusedArguments.contains(method.method)) {
@@ -314,15 +313,14 @@
     method.getCode().registerArgumentReferences(method, collector);
     BitSet used = collector.getUsedArguments();
     if (used.cardinality() < argumentCount) {
-      RemovedArgumentInfoCollection.Builder argInfosBuilder =
-          RemovedArgumentInfoCollection.builder();
+      ArgumentInfoCollection.Builder argInfosBuilder = ArgumentInfoCollection.builder();
       for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) {
         if (!used.get(argumentIndex)) {
           RemovedArgumentInfo removedArg =
               RemovedArgumentInfo.builder()
                   .setType(method.method.proto.parameters.values[argumentIndex - offset])
                   .build();
-          argInfosBuilder.addRemovedArgument(argumentIndex, removedArg);
+          argInfosBuilder.addArgumentInfo(argumentIndex, removedArg);
         }
       }
       return argInfosBuilder.build();
@@ -331,7 +329,7 @@
   }
 
   private DexProto createProtoWithRemovedArguments(
-      DexEncodedMethod encodedMethod, RemovedArgumentInfoCollection unused) {
+      DexEncodedMethod encodedMethod, ArgumentInfoCollection unused) {
     DexType[] parameters = unused.rewriteParameters(encodedMethod);
     return appView.dexItemFactory().createProto(encodedMethod.method.proto.returnType, parameters);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 23fa539..8bbc20e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -21,7 +21,7 @@
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeArgumentInfoCollection;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
 import com.android.tools.r8.ir.analysis.type.ArrayTypeLatticeElement;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -651,13 +651,12 @@
       public void move(DexMethod from, DexMethod to, boolean isStatic) {
         super.move(from, to);
         int offset = BooleanUtils.intValue(!isStatic);
-        RewrittenTypeArgumentInfoCollection.Builder builder =
-            RewrittenTypeArgumentInfoCollection.builder();
+        ArgumentInfoCollection.Builder builder = ArgumentInfoCollection.builder();
         for (int i = 0; i < from.proto.parameters.size(); i++) {
           DexType fromType = from.proto.parameters.values[i];
           DexType toType = to.proto.parameters.values[i];
           if (fromType != toType) {
-            builder.rewriteArgument(i + offset, fromType, toType);
+            builder.addArgumentInfo(i + offset, new RewrittenTypeInfo(fromType, toType));
           }
         }
         RewrittenTypeInfo returnInfo =
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
new file mode 100644
index 0000000..ddef0b4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.enumunboxing;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
+import org.junit.Test;
+
+public class ArgumentInfoCollectionTest extends TestBase {
+
+  @Test
+  public void testCombineRewritten() {
+    DexItemFactory factory = new DexItemFactory();
+    ArgumentInfoCollection.Builder builder1 = ArgumentInfoCollection.builder();
+    builder1.addArgumentInfo(1, new RewrittenTypeInfo(factory.intType, factory.longType));
+    builder1.addArgumentInfo(3, new RewrittenTypeInfo(factory.intType, factory.longType));
+    ArgumentInfoCollection arguments1 = builder1.build();
+
+    ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder();
+    builder2.addArgumentInfo(2, new RewrittenTypeInfo(factory.floatType, factory.doubleType));
+    builder2.addArgumentInfo(4, new RewrittenTypeInfo(factory.floatType, factory.doubleType));
+    ArgumentInfoCollection arguments2 = builder2.build();
+
+    ArgumentInfoCollection combine = arguments1.combine(arguments2);
+
+    RewrittenTypeInfo arg1 = combine.getArgumentInfo(1).asRewrittenTypeInfo();
+    assertEquals(arg1.getOldType(), factory.intType);
+    assertEquals(arg1.getNewType(), factory.longType);
+    RewrittenTypeInfo arg2 = combine.getArgumentInfo(2).asRewrittenTypeInfo();
+    assertEquals(arg2.getOldType(), factory.floatType);
+    assertEquals(arg2.getNewType(), factory.doubleType);
+    RewrittenTypeInfo arg3 = combine.getArgumentInfo(3).asRewrittenTypeInfo();
+    assertEquals(arg3.getOldType(), factory.intType);
+    assertEquals(arg3.getNewType(), factory.longType);
+    RewrittenTypeInfo arg4 = combine.getArgumentInfo(4).asRewrittenTypeInfo();
+    assertEquals(arg4.getOldType(), factory.floatType);
+    assertEquals(arg4.getNewType(), factory.doubleType);
+  }
+
+  @Test
+  public void testCombineRemoved() {
+    DexItemFactory factory = new DexItemFactory();
+
+    // Arguments removed: 0 1 2 3 4 -> 0 2 4.
+    ArgumentInfoCollection.Builder builder1 = ArgumentInfoCollection.builder();
+    builder1.addArgumentInfo(
+        1, RemovedArgumentInfo.builder().setType(factory.intType).setIsAlwaysNull().build());
+    builder1.addArgumentInfo(
+        3, RemovedArgumentInfo.builder().setType(factory.intType).setIsAlwaysNull().build());
+    ArgumentInfoCollection arguments1 = builder1.build();
+
+    // Arguments removed: 0 2 4 -> 0. Arguments 2 and 4 are at position 1 and 2 after first removal.
+    ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder();
+    builder2.addArgumentInfo(1, RemovedArgumentInfo.builder().setType(factory.doubleType).build());
+    builder2.addArgumentInfo(2, RemovedArgumentInfo.builder().setType(factory.doubleType).build());
+    ArgumentInfoCollection arguments2 = builder2.build();
+
+    // Arguments removed: 0 1 2 3 4 -> 0.
+    ArgumentInfoCollection combine = arguments1.combine(arguments2);
+
+    RemovedArgumentInfo arg1 = combine.getArgumentInfo(1).asRemovedArgumentInfo();
+    assertEquals(arg1.getType(), factory.intType);
+    assertTrue(arg1.isAlwaysNull());
+    RemovedArgumentInfo arg2 = combine.getArgumentInfo(2).asRemovedArgumentInfo();
+    assertEquals(arg2.getType(), factory.doubleType);
+    assertFalse(arg2.isAlwaysNull());
+    RemovedArgumentInfo arg3 = combine.getArgumentInfo(3).asRemovedArgumentInfo();
+    assertEquals(arg3.getType(), factory.intType);
+    assertTrue(arg3.isAlwaysNull());
+    RemovedArgumentInfo arg4 = combine.getArgumentInfo(4).asRemovedArgumentInfo();
+    assertEquals(arg4.getType(), factory.doubleType);
+    assertFalse(arg4.isAlwaysNull());
+  }
+
+  @Test
+  public void testCombineRemoveRewritten() {
+    DexItemFactory factory = new DexItemFactory();
+
+    ArgumentInfoCollection.Builder builder1 = ArgumentInfoCollection.builder();
+    builder1.addArgumentInfo(
+        1, RemovedArgumentInfo.builder().setType(factory.intType).setIsAlwaysNull().build());
+    builder1.addArgumentInfo(
+        3, RemovedArgumentInfo.builder().setType(factory.intType).setIsAlwaysNull().build());
+    ArgumentInfoCollection arguments1 = builder1.build();
+
+    ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder();
+    builder2.addArgumentInfo(1, new RewrittenTypeInfo(factory.floatType, factory.doubleType));
+    builder2.addArgumentInfo(2, new RewrittenTypeInfo(factory.floatType, factory.doubleType));
+    ArgumentInfoCollection arguments2 = builder2.build();
+
+    ArgumentInfoCollection combine = arguments1.combine(arguments2);
+
+    RemovedArgumentInfo arg1 = combine.getArgumentInfo(1).asRemovedArgumentInfo();
+    assertEquals(arg1.getType(), factory.intType);
+    assertTrue(arg1.isAlwaysNull());
+    RewrittenTypeInfo arg2 = combine.getArgumentInfo(2).asRewrittenTypeInfo();
+    assertEquals(arg2.getOldType(), factory.floatType);
+    assertEquals(arg2.getNewType(), factory.doubleType);
+    RemovedArgumentInfo arg3 = combine.getArgumentInfo(3).asRemovedArgumentInfo();
+    assertEquals(arg3.getType(), factory.intType);
+    assertTrue(arg3.isAlwaysNull());
+    RewrittenTypeInfo arg4 = combine.getArgumentInfo(4).asRewrittenTypeInfo();
+    assertEquals(arg4.getOldType(), factory.floatType);
+    assertEquals(arg4.getNewType(), factory.doubleType);
+  }
+}