Store parameter names info in DexEncodedMethod

Bug: 132549918
Change-Id: I20d016defac237286865f515aa314d1c311ca739
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 0b8569c..fed2064 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -62,11 +62,14 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.IntPredicate;
@@ -114,6 +117,8 @@
   public static final DexEncodedMethod[] EMPTY_ARRAY = {};
   public static final DexEncodedMethod SENTINEL =
       new DexEncodedMethod(null, null, null, ParameterAnnotationsList.empty(), null);
+  public static final Int2ReferenceMap<DebugLocalInfo> NO_PARAMETER_INFO =
+      new Int2ReferenceArrayMap<>(0);
 
   public final DexMethod method;
   public final MethodAccessFlags accessFlags;
@@ -131,6 +136,8 @@
 
   private OptionalBool isLibraryMethodOverride = OptionalBool.unknown();
 
+  private Int2ReferenceMap<DebugLocalInfo> parameterInfo = NO_PARAMETER_INFO;
+
   // This flag indicates the current instance is no longer up-to-date as another instance was
   // created based on this. Any further (public) operations on this instance will raise an error
   // to catch potential bugs due to the inconsistency (e.g., http://b/111893131)
@@ -429,6 +436,19 @@
     setCode(builder.build());
   }
 
+  public void setParameterInfo(Int2ReferenceMap<DebugLocalInfo> parameterInfo) {
+    assert this.parameterInfo == NO_PARAMETER_INFO;
+    this.parameterInfo = parameterInfo;
+  }
+
+  public boolean hasParameterInfo() {
+    return parameterInfo != NO_PARAMETER_INFO;
+  }
+
+  public Map<Integer, DebugLocalInfo> getParameterInfo() {
+    return parameterInfo;
+  }
+
   @Override
   public String toString() {
     checkIfObsolete();
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 8e6314c..fa4e3d1 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -24,11 +24,11 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OffOrAuto;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.BitSet;
 import java.util.Iterator;
-import java.util.Map;
 import java.util.function.BiFunction;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassVisitor;
@@ -154,14 +154,14 @@
     triggerDelayedParsingIfNeccessary();
     if (!keepLocals(encodedMethod, appView.options())) {
       // If the locals are not kept, we might still need a bit of locals information to satisfy
-      // -keepparameternames for R8.
-      Map<Integer, DebugLocalInfo> parameterInfo = collectParameterInfo(encodedMethod, appView);
-      // We strip locals here because we will not be able to recover from invalid info.
-      if (canStripLocals(encodedMethod, appView)) {
-        node.localVariables.clear();
+      // -keepparameternames for R8. As locals are stripped after collection the parameter names
+      // this information can only be retrieved the first time IR is build for a method, so stick
+      // to the information if already present.
+      if (!encodedMethod.hasParameterInfo()) {
+        encodedMethod.setParameterInfo(collectParameterInfo(encodedMethod, appView));
       }
-      return internalBuild(
-          context, encodedMethod, appView, generator, callerPosition, parameterInfo);
+      node.localVariables.clear();
+      return internalBuild(context, encodedMethod, appView, generator, callerPosition);
     } else {
       return internalBuildWithLocals(context, encodedMethod, appView, generator, callerPosition);
     }
@@ -174,13 +174,11 @@
       ValueNumberGenerator generator,
       Position callerPosition) {
     try {
-      return internalBuild(
-          context, encodedMethod, appView, generator, callerPosition, IRCode.NO_PARAMETER_INFO);
+      return internalBuild(context, encodedMethod, appView, generator, callerPosition);
     } catch (InvalidDebugInfoException e) {
       appView.options().warningInvalidDebugInfo(encodedMethod, origin, e);
       node.localVariables.clear();
-      return internalBuild(
-          context, encodedMethod, appView, generator, callerPosition, IRCode.NO_PARAMETER_INFO);
+      return internalBuild(context, encodedMethod, appView, generator, callerPosition);
     }
   }
 
@@ -194,17 +192,17 @@
     return false;
   }
 
-  private Map<Integer, DebugLocalInfo> collectParameterInfo(
+  private Int2ReferenceMap<DebugLocalInfo> collectParameterInfo(
       DexEncodedMethod encodedMethod, AppView<?> appView) {
     if (!appView.options().hasProguardConfiguration()
         || !appView.options().getProguardConfiguration().isKeepParameterNames()) {
-      return IRCode.NO_PARAMETER_INFO;
+      return DexEncodedMethod.NO_PARAMETER_INFO;
     }
     // The enqueuer might build IR to trace reflective behaviour. At that point liveness is not
     // known, so be conservative with collection parameter name information.
     if (appView.appInfo().hasLiveness()
         && !appView.appInfo().withLiveness().isPinned(encodedMethod.method)) {
-      return IRCode.NO_PARAMETER_INFO;
+      return DexEncodedMethod.NO_PARAMETER_INFO;
     }
     // Collect the local slots used for parameters.
     BitSet localSlotsForParameters = new BitSet(0);
@@ -220,7 +218,7 @@
     // assuming that that does actually describe the parameter (name, type and possibly
     // signature).
     DexItemFactory factory = appView.options().itemFactory;
-    Map<Integer, DebugLocalInfo> parameterInfo =
+    Int2ReferenceMap<DebugLocalInfo> parameterInfo =
         new Int2ReferenceArrayMap<>(localSlotsForParameters.cardinality());
     for (Object o : node.localVariables) {
       LocalVariableNode node = (LocalVariableNode) o;
@@ -238,31 +236,13 @@
     return parameterInfo;
   }
 
-  private boolean canStripLocals(DexEncodedMethod encodedMethod, AppView<?> appView) {
-    // If not keeping parameter names the locals can always be stripped.
-    if (!appView.options().hasProguardConfiguration()
-        || !appView.options().getProguardConfiguration().isKeepParameterNames()) {
-      return true;
-    }
-    // The enqueuer might build IR to trace reflective behaviour. At that point liveness is not
-    // known, so locals cannot be stripped as IR will built again in the IR converter.
-    if (appView.appInfo().hasLiveness()
-        && !appView.appInfo().withLiveness().isPinned(encodedMethod.method)) {
-      return true;
-    }
-    return false;
-  }
-
   private IRCode internalBuild(
       DexEncodedMethod context,
       DexEncodedMethod encodedMethod,
       AppView<?> appView,
       ValueNumberGenerator generator,
-      Position callerPosition,
-      Map<Integer, DebugLocalInfo> parameterInfo) {
-    assert node.localVariables.isEmpty()
-        || keepLocals(encodedMethod, appView.options())
-        || !canStripLocals(encodedMethod, appView);
+      Position callerPosition) {
+    assert node.localVariables.isEmpty() || keepLocals(encodedMethod, appView.options());
     JarSourceCode source =
         new JarSourceCode(
             method.holder,
@@ -271,7 +251,7 @@
             appView.graphLense().getOriginalMethodSignature(encodedMethod.method),
             callerPosition);
     IRBuilder builder = new IRBuilder(encodedMethod, appView, source, origin, generator);
-    return builder.build(context, parameterInfo);
+    return builder.build(context);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index a48f44a..6b84e42 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -21,7 +21,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -90,8 +89,6 @@
   // When numbering instructions we number instructions only with even numbers. This allows us to
   // use odd instruction numbers for the insertion of moves during spilling.
   public static final int INSTRUCTION_NUMBER_DELTA = 2;
-  public static final Map<Integer, DebugLocalInfo> NO_PARAMETER_INFO =
-      new Int2ReferenceArrayMap<>(0);
 
   public final DexEncodedMethod method;
 
@@ -116,8 +113,6 @@
 
   public final Origin origin;
 
-  public final Map<Integer, DebugLocalInfo> parameterInfo;
-
   public IRCode(
       InternalOptions options,
       DexEncodedMethod method,
@@ -126,8 +121,7 @@
       boolean hasDebugPositions,
       boolean hasMonitorInstruction,
       boolean hasConstString,
-      Origin origin,
-      Map<Integer, DebugLocalInfo> parameterInfo) {
+      Origin origin) {
     assert options != null;
     this.options = options;
     this.method = method;
@@ -137,10 +131,8 @@
     this.hasMonitorInstruction = hasMonitorInstruction;
     this.hasConstString = hasConstString;
     this.origin = origin;
-    this.parameterInfo = parameterInfo;
     // TODO(zerny): Remove or update this property now that all instructions have positions.
     allThrowingInstructionsHavePositions = computeAllThrowingInstructionsHavePositions();
-    assert parameterInfo != null;
   }
 
   public void copyMetadataFromInlinee(IRCode inlinee) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index ffe8806..657032a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -311,7 +311,7 @@
       assert !block.exit().isReturn() || stackHeightTracker.isEmpty();
 
       if (firstBlock) {
-        addParameterNamesIfRequired(block, code.parameterInfo);
+        addParameterNamesIfRequired(block);
         firstBlock = false;
       }
 
@@ -631,15 +631,19 @@
     }
   }
 
-  private void addParameterNamesIfRequired(
-      BasicBlock block, Map<Integer, DebugLocalInfo> parameterInfo) {
+  private void addParameterNamesIfRequired(BasicBlock block) {
     // Don't add this information if the code already have full debug information.
     if (appView.options().debug) {
       return;
     }
 
-    if (code.parameterInfo != IRCode.NO_PARAMETER_INFO) {
-      for (Map.Entry<Integer, DebugLocalInfo> entries : parameterInfo.entrySet()) {
+    if (appView.appInfo().hasLiveness()
+        && !appView.appInfo().withLiveness().isPinned(method.method)) {
+      return;
+    }
+
+    if (method.hasParameterInfo()) {
+      for (Map.Entry<Integer, DebugLocalInfo> entries : method.getParameterInfo().entrySet()) {
         LocalVariableInfo localVariableInfo =
             new LocalVariableInfo(entries.getKey(), entries.getValue(), getLabel(block));
         CfLabel endLabel = ensureLabel();
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 eae998d..e85b1cc 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
@@ -495,19 +495,6 @@
    * @return The list of basic blocks. First block is the main entry.
    */
   public IRCode build(DexEncodedMethod context) {
-    return build(context, IRCode.NO_PARAMETER_INFO);
-  }
-
-  /**
-   * Build the high-level IR in SSA form.
-   *
-   * @param context Under what context this IRCode is built. Either the current method or caller.
-   * @param parameterInfo Parameter information to include in the output. Pass <code>
-   *     IRCode.NO_PARAMETER_INFO</code> if this is not relevant. This information is only used if
-   *     the generated code does not contain any debug information.
-   * @return The list of basic blocks. First block is the main entry.
-   */
-  public IRCode build(DexEncodedMethod context, Map<Integer, DebugLocalInfo> parameterInfo) {
     assert source != null;
     source.setUp();
 
@@ -608,8 +595,7 @@
             hasDebugPositions,
             hasMonitorInstruction,
             hasConstString,
-            origin,
-            parameterInfo);
+            origin);
 
     // Verify critical edges are split so we have a place to insert phi moves if necessary.
     assert ir.verifySplitCriticalEdges();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
index 3284be0..d32606d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
@@ -141,8 +141,7 @@
             false,
             false,
             false,
-            Origin.unknown(),
-            IRCode.NO_PARAMETER_INFO);
+            Origin.unknown());
     PeepholeOptimizer.optimize(code, new MockLinearScanRegisterAllocator(appView, code));
 
     // Check that all four constant number instructions remain.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
index 40ac512..c3e6823 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
@@ -81,8 +81,7 @@
             false,
             false,
             false,
-            Origin.unknown(),
-            IRCode.NO_PARAMETER_INFO);
+            Origin.unknown());
     CodeRewriter.collapseTrivialGotos(null, code);
     assertTrue(code.entryBlock().isTrivialGoto());
     assertTrue(blocks.contains(block0));
@@ -169,8 +168,7 @@
             false,
             false,
             false,
-            Origin.unknown(),
-            IRCode.NO_PARAMETER_INFO);
+            Origin.unknown());
     CodeRewriter.collapseTrivialGotos(null, code);
     assertTrue(block0.getInstructions().get(1).isIf());
     assertEquals(block1, block0.getInstructions().get(1).asIf().fallthroughBlock());