[LIR] Reallocate collections when building LirCode

This saves about half of the memory associated with the code objects.

Bug: b/225838009
Change-Id: I9ebb4130dd7637c12c62c7117a47adc47b17cae3
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index 45e136d..ea4f7b1 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -93,6 +93,7 @@
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
 import com.android.tools.r8.lightir.LirCode.PositionEntry;
+import com.android.tools.r8.lightir.LirCode.TryCatchTable;
 import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
 import com.android.tools.r8.utils.ListUtils;
 import com.google.common.collect.ImmutableList;
@@ -172,12 +173,14 @@
       // instruction denotes a new block.
       if (currentBlock == null) {
         currentBlock = getBasicBlock(nextInstructionIndex);
-        CatchHandlers<Integer> handlers =
-            code.getTryCatchTable().getHandlersForBlock(nextInstructionIndex);
-        if (handlers != null) {
-          List<BasicBlock> targets = ListUtils.map(handlers.getAllTargets(), this::getBasicBlock);
-          targets.forEach(currentBlock::link);
-          currentBlock.linkCatchSuccessors(handlers.getGuards(), targets);
+        TryCatchTable tryCatchTable = code.getTryCatchTable();
+        if (tryCatchTable != null) {
+          CatchHandlers<Integer> handlers = tryCatchTable.getHandlersForBlock(nextInstructionIndex);
+          if (handlers != null) {
+            List<BasicBlock> targets = ListUtils.map(handlers.getAllTargets(), this::getBasicBlock);
+            targets.forEach(currentBlock::link);
+            currentBlock.linkCatchSuccessors(handlers.getGuards(), targets);
+          }
         }
       } else {
         assert !blocks.containsKey(nextInstructionIndex);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index 3a44a61..b88f1f7 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -181,6 +181,7 @@
   private void setPositionIndex(int instructionIndex, Position position) {
     assert positionTable.isEmpty()
         || ListUtils.last(positionTable).fromInstructionIndex < instructionIndex;
+    assert positionTable.isEmpty() || !ListUtils.last(positionTable).position.equals(position);
     positionTable.add(new PositionEntry(instructionIndex, position));
   }
 
@@ -707,6 +708,8 @@
     constants.forEach((item, index) -> constantTable[index] = item);
     DebugLocalInfoTable<EV> debugTable =
         debugLocals.isEmpty() ? null : new DebugLocalInfoTable<>(debugLocals, debugLocalEnds);
+    TryCatchTable tryCatchTable =
+        tryCatchRanges.isEmpty() ? null : new TryCatchTable(tryCatchRanges);
     return new LirCode<>(
         metadata,
         constantTable,
@@ -714,7 +717,7 @@
         argumentCount,
         byteWriter.toByteArray(),
         instructionCount,
-        new TryCatchTable(tryCatchRanges),
+        tryCatchTable,
         debugTable,
         strategy.getStrategyInfo());
   }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirCode.java b/src/main/java/com/android/tools/r8/lightir/LirCode.java
index 1a07558..f1c04ec 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirCode.java
@@ -10,7 +10,9 @@
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.IRMetadata;
 import com.android.tools.r8.ir.code.Position;
+import com.google.common.collect.ImmutableMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
 import java.util.Map;
 import java.util.function.BiConsumer;
 
@@ -30,7 +32,9 @@
     final Int2ReferenceMap<CatchHandlers<Integer>> tryCatchHandlers;
 
     public TryCatchTable(Int2ReferenceMap<CatchHandlers<Integer>> tryCatchHandlers) {
-      this.tryCatchHandlers = tryCatchHandlers;
+      assert !tryCatchHandlers.isEmpty();
+      // Copy the map to ensure it has not over-allocated the backing store.
+      this.tryCatchHandlers = new Int2ReferenceOpenHashMap<>(tryCatchHandlers);
     }
 
     public CatchHandlers<Integer> getHandlersForBlock(int blockIndex) {
@@ -46,8 +50,19 @@
         Map<EV, DebugLocalInfo> valueToLocalMap, Int2ReferenceMap<int[]> instructionToEndUseMap) {
       assert !valueToLocalMap.isEmpty();
       // TODO(b/283049198): Debug ends may not be maintained so we can't assume they are non-empty.
-      this.valueToLocalMap = valueToLocalMap;
-      this.instructionToEndUseMap = instructionToEndUseMap;
+      // Copy the maps to ensure they have not over-allocated the backing store.
+      this.valueToLocalMap = ImmutableMap.copyOf(valueToLocalMap);
+      this.instructionToEndUseMap =
+          instructionToEndUseMap.isEmpty()
+              ? null
+              : new Int2ReferenceOpenHashMap<>(instructionToEndUseMap);
+    }
+
+    public int[] getEnds(int index) {
+      if (instructionToEndUseMap == null) {
+        return null;
+      }
+      return instructionToEndUseMap.get(index);
     }
 
     public void forEachLocalDefinition(BiConsumer<EV, DebugLocalInfo> fn) {
@@ -73,7 +88,7 @@
   /** Cached value for the number of logical instructions (excludes arguments, includes phis). */
   private final int instructionCount;
 
-  /** Table of try-catch handlers for each basic block. */
+  /** Table of try-catch handlers for each basic block (if present). */
   private final TryCatchTable tryCatchTable;
 
   /** Table of debug local information for each SSA value (if present). */
@@ -153,9 +168,7 @@
   }
 
   public int[] getDebugLocalEnds(int instructionValueIndex) {
-    return debugLocalInfoTable == null
-        ? null
-        : debugLocalInfoTable.instructionToEndUseMap.get(instructionValueIndex);
+    return debugLocalInfoTable == null ? null : debugLocalInfoTable.getEnds(instructionValueIndex);
   }
 
   @Override