Merge "Use a pool for DexDebugEvents."
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index cd92f2d..d273090 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -109,7 +109,8 @@
     if (!options.skipDebugInfoOpt && (application.getProguardMap() != null)) {
       try {
         timing.begin("DebugStripper");
-        DebugStripper stripper = new DebugStripper(application.getProguardMap(), options);
+        DebugStripper stripper =
+            new DebugStripper(application.getProguardMap(), options, appInfo.dexItemFactory);
         application.classes().forEach(stripper::processClass);
       } finally {
         timing.end();
diff --git a/src/main/java/com/android/tools/r8/dex/DexFileReader.java b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
index 627d106..02b0ce8 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFileReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -24,7 +24,6 @@
 import com.android.tools.r8.graph.DexCode.TryHandler;
 import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
 import com.android.tools.r8.graph.DexDebugEvent;
-import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin;
 import com.android.tools.r8.graph.DexDebugInfo;
 import com.android.tools.r8.graph.DexEncodedAnnotation;
 import com.android.tools.r8.graph.DexEncodedArray;
@@ -446,10 +445,10 @@
     for (int head = file.getUbyte(); head != Constants.DBG_END_SEQUENCE; head = file.getUbyte()) {
       switch (head) {
         case Constants.DBG_ADVANCE_PC:
-          events.add(new DexDebugEvent.AdvancePC(file.getUleb128()));
+          events.add(dexItemFactory.createAdvancePC(file.getUleb128()));
           break;
         case Constants.DBG_ADVANCE_LINE:
-          events.add(new DexDebugEvent.AdvanceLine(file.getSleb128()));
+          events.add(dexItemFactory.createAdvanceLine(file.getSleb128()));
           break;
         case Constants.DBG_START_LOCAL: {
           int registerNum = file.getUleb128();
@@ -475,30 +474,30 @@
           break;
         }
         case Constants.DBG_END_LOCAL: {
-          events.add(new DexDebugEvent.EndLocal(file.getUleb128()));
+          events.add(dexItemFactory.createEndLocal(file.getUleb128()));
           break;
         }
         case Constants.DBG_RESTART_LOCAL: {
-          events.add(new DexDebugEvent.RestartLocal(file.getUleb128()));
+          events.add(dexItemFactory.createRestartLocal(file.getUleb128()));
           break;
         }
         case Constants.DBG_SET_PROLOGUE_END: {
-          events.add(new DexDebugEvent.SetPrologueEnd());
+          events.add(dexItemFactory.createSetPrologueEnd());
           break;
         }
         case Constants.DBG_SET_EPILOGUE_BEGIN: {
-          events.add(new SetEpilogueBegin());
+          events.add(dexItemFactory.createSetEpilogueBegin());
           break;
         }
         case Constants.DBG_SET_FILE: {
           int nameIdx = file.getUleb128p1();
           DexString sourceFile = nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx);
-          events.add(new DexDebugEvent.SetFile(sourceFile));
+          events.add(dexItemFactory.createSetFile(sourceFile));
           break;
         }
         default: {
           assert head >= 0x0a && head <= 0xff;
-          events.add(new DexDebugEvent.Default(head));
+          events.add(dexItemFactory.createDefault(head));
         }
       }
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index b8bab7e..572e655 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -41,7 +41,7 @@
       writer.putUleb128(delta);
     }
 
-    public AdvancePC(int delta) {
+    AdvancePC(int delta) {
       this.delta = delta;
     }
 
@@ -68,7 +68,7 @@
 
   public static class SetPrologueEnd extends DexDebugEvent {
 
-    public SetPrologueEnd() {
+    SetPrologueEnd() {
     }
 
     public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
@@ -96,7 +96,7 @@
 
   public static class SetEpilogueBegin extends DexDebugEvent {
 
-    public SetEpilogueBegin() {
+    SetEpilogueBegin() {
     }
 
     public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
@@ -124,7 +124,7 @@
 
     final int delta;
 
-    public AdvanceLine(int delta) {
+    AdvanceLine(int delta) {
       this.delta = delta;
     }
 
@@ -233,7 +233,7 @@
 
     final int registerNum;
 
-    public EndLocal(int registerNum) {
+    EndLocal(int registerNum) {
       this.registerNum = registerNum;
     }
 
@@ -265,7 +265,7 @@
 
     final int registerNum;
 
-    public RestartLocal(int registerNum) {
+    RestartLocal(int registerNum) {
       this.registerNum = registerNum;
     }
 
@@ -297,7 +297,7 @@
 
     final DexString fileName;
 
-    public SetFile(DexString fileName) {
+    SetFile(DexString fileName) {
       this.fileName = fileName;
     }
 
@@ -334,7 +334,7 @@
 
     final int value;
 
-    public Default(int value) {
+    Default(int value) {
       assert (value >= Constants.DBG_FIRST_SPECIAL) && (value <= Constants.DBG_LAST_SPECIAL);
       this.value = value;
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index 5295c55..26349c2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -40,6 +40,8 @@
 
   private final DexMethod method;
 
+  private final DexItemFactory dexItemFactory;
+
   // Previous and current position info to delay emitting position changes.
   private final PositionState previous;
   private final PositionState current;
@@ -59,8 +61,9 @@
 
   private int startLine = NO_LINE_INFO;
 
-  public DexDebugEventBuilder(DexMethod method) {
+  public DexDebugEventBuilder(DexMethod method, DexItemFactory dexItemFactory) {
     this.method = method;
+    this.dexItemFactory = dexItemFactory;
     arguments = new ArrayList<>(method.proto.parameters.values.length);
     current = new PositionState();
     previous = new PositionState();
@@ -147,16 +150,16 @@
     assert pcDelta >= 0;
     if (current.file != previous.file) {
       assert current.file == null || !current.file.equals(previous.file);
-      events.add(new SetFile(current.file));
+      events.add(dexItemFactory.createSetFile(current.file));
     }
     if (lineDelta < Constants.DBG_LINE_BASE
         || lineDelta - Constants.DBG_LINE_BASE >= Constants.DBG_LINE_RANGE) {
-      events.add(new AdvanceLine(lineDelta));
+      events.add(dexItemFactory.createAdvanceLine(lineDelta));
       // TODO(herhut): To be super clever, encode only the part that is above limit.
       lineDelta = 0;
     }
     if (pcDelta >= Constants.DBG_ADDRESS_RANGE) {
-      events.add(new AdvancePC(pcDelta));
+      events.add(dexItemFactory.createAdvancePC(pcDelta));
       pcDelta = 0;
     }
     // TODO(herhut): Maybe only write this one if needed (would differ from DEX).
@@ -164,7 +167,7 @@
         0x0a + (lineDelta - Constants.DBG_LINE_BASE) + Constants.DBG_LINE_RANGE * pcDelta;
     assert specialOpcode >= 0x0a;
     assert specialOpcode <= 0xff;
-    events.add(new Default(specialOpcode));
+    events.add(dexItemFactory.createDefault(specialOpcode));
     previous.pc = current.pc;
     previous.line = current.line;
     previous.file = current.file;
@@ -178,7 +181,7 @@
     SortedSet<Integer> positionRegisters = new TreeSet<>(current.locals.keySet());
     for (Integer register : currentRegisters) {
       if (!positionRegisters.contains(register)) {
-        events.add(new EndLocal(register));
+        events.add(dexItemFactory.createEndLocal(register));
         openLocals.put(register, null);
       }
     }
@@ -188,7 +191,7 @@
       if (currentLocal != positionLocal) {
         openLocals.put(register, positionLocal);
         if (currentLocal == null && lastKnownLocals.get(register) == positionLocal) {
-          events.add(new RestartLocal(register));
+          events.add(dexItemFactory.createRestartLocal(register));
         } else {
           events.add(new StartLocal(register, positionLocal));
           lastKnownLocals.put(register, positionLocal);
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 5ea8edd..8b86ca8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -121,15 +121,17 @@
         : code.asDexCode().buildIR(this, valueNumberGenerator, options);
   }
 
-  public void setCode(IRCode ir, RegisterAllocator registerAllocator) {
-    final DexBuilder builder = new DexBuilder(ir, registerAllocator);
+  public void setCode(
+      IRCode ir, RegisterAllocator registerAllocator, DexItemFactory dexItemFactory) {
+    final DexBuilder builder = new DexBuilder(ir, registerAllocator, dexItemFactory);
     code = builder.build(method.proto.parameters.values.length);
   }
 
   // Replaces the dex code in the method by setting code to result of compiling the IR.
   public void setCode(IRCode ir, RegisterAllocator registerAllocator,
-      DexString firstJumboString) {
-    final DexBuilder builder = new DexBuilder(ir, registerAllocator, firstJumboString);
+      DexItemFactory dexItemFactory, DexString firstJumboString) {
+    final DexBuilder builder =
+        new DexBuilder(ir, registerAllocator, dexItemFactory, firstJumboString);
     code = builder.build(method.proto.parameters.values.length);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index ce974ce..5bff70d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -4,9 +4,19 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
+import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
+import com.android.tools.r8.graph.DexDebugEvent.Default;
+import com.android.tools.r8.graph.DexDebugEvent.EndLocal;
+import com.android.tools.r8.graph.DexDebugEvent.RestartLocal;
+import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin;
+import com.android.tools.r8.graph.DexDebugEvent.SetFile;
+import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd;
 import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
 import com.android.tools.r8.naming.NamingLens;
 import com.google.common.collect.ImmutableSet;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -25,6 +35,16 @@
   private final Map<DexCallSite, DexCallSite> callSites = new HashMap<>();
   private final Map<DexMethodHandle, DexMethodHandle> methodHandles = new HashMap<>();
 
+  // DexDebugEvent Canonicalization.
+  private final Int2ObjectMap<AdvanceLine> advanceLines = new Int2ObjectOpenHashMap<>();
+  private final Int2ObjectMap<AdvancePC> advancePCs = new Int2ObjectOpenHashMap<>();
+  private final Int2ObjectMap<Default> defaults = new Int2ObjectOpenHashMap<>();
+  private final Int2ObjectMap<EndLocal> endLocals = new Int2ObjectOpenHashMap<>();
+  private final Int2ObjectMap<RestartLocal> restartLocals = new Int2ObjectOpenHashMap<>();
+  private final SetEpilogueBegin setEpilogueBegin = new SetEpilogueBegin();
+  private final SetPrologueEnd setPrologueEnd = new SetPrologueEnd();
+  private final Map<DexString, SetFile> setFiles = new HashMap<>();
+
   boolean sorted = false;
 
   public static final DexType catchAllType = new DexType(new DexString("CATCH_ALL"));
@@ -306,6 +326,80 @@
     return createMethod(clazz, proto, name);
   }
 
+  public AdvanceLine createAdvanceLine(int delta) {
+    synchronized (advanceLines) {
+      AdvanceLine result = advanceLines.get(delta);
+      if (result == null) {
+        result = new AdvanceLine(delta);
+        advanceLines.put(delta, result);
+      }
+      return result;
+    }
+  }
+
+  public AdvancePC createAdvancePC(int delta) {
+    synchronized (advancePCs) {
+      AdvancePC result = advancePCs.get(delta);
+      if (result == null) {
+        result = new AdvancePC(delta);
+        advancePCs.put(delta, result);
+      }
+      return result;
+    }
+  }
+
+  public Default createDefault(int value) {
+    synchronized (defaults) {
+      Default result = defaults.get(value);
+      if (result == null) {
+        result = new Default(value);
+        defaults.put(value, result);
+      }
+      return result;
+    }
+  }
+
+  public EndLocal createEndLocal(int registerNum) {
+    synchronized (endLocals) {
+      EndLocal result = endLocals.get(registerNum);
+      if (result == null) {
+        result = new EndLocal(registerNum);
+        endLocals.put(registerNum, result);
+      }
+      return result;
+    }
+  }
+
+  public RestartLocal createRestartLocal(int registerNum) {
+    synchronized (restartLocals) {
+      RestartLocal result = restartLocals.get(registerNum);
+      if (result == null) {
+        result = new RestartLocal(registerNum);
+        restartLocals.put(registerNum, result);
+      }
+      return result;
+    }
+  }
+
+  public SetEpilogueBegin createSetEpilogueBegin() {
+    return setEpilogueBegin;
+  }
+
+  public SetPrologueEnd createSetPrologueEnd() {
+    return setPrologueEnd;
+  }
+
+  public SetFile createSetFile(DexString fileName) {
+    synchronized (setFiles) {
+      SetFile result = setFiles.get(fileName);
+      if (result == null) {
+        result = new SetFile(fileName);
+        setFiles.put(fileName, result);
+      }
+      return result;
+    }
+  }
+
   public boolean isConstructor(DexMethod method) {
     return method.name == constructorMethodName;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index 511f36b..4484aec 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -74,6 +74,8 @@
   // The register allocator providing register assignments for the code to build.
   private final RegisterAllocator registerAllocator;
 
+  private final DexItemFactory dexItemFactory;
+
   // List of information about switch payloads that have to be created at the end of the
   // dex code.
   private final List<SwitchPayloadInfo> switchPayloadInfos = new ArrayList<>();
@@ -105,19 +107,24 @@
 
   BasicBlock nextBlock;
 
-  public DexBuilder(IRCode ir, RegisterAllocator registerAllocator) {
+  public DexBuilder(IRCode ir, RegisterAllocator registerAllocator, DexItemFactory dexItemFactory) {
     assert ir != null;
     assert registerAllocator != null;
+    assert dexItemFactory != null;
     this.ir = ir;
     this.registerAllocator = registerAllocator;
+    this.dexItemFactory = dexItemFactory;
     this.firstJumboString = null;
   }
 
-  public DexBuilder(IRCode ir, RegisterAllocator registerAllocator, DexString firstJumboString) {
+  public DexBuilder(IRCode ir, RegisterAllocator registerAllocator,
+      DexItemFactory dexItemFactory, DexString firstJumboString) {
     assert ir != null;
     assert registerAllocator != null;
+    assert dexItemFactory != null;
     this.ir = ir;
     this.registerAllocator = registerAllocator;
+    this.dexItemFactory = dexItemFactory;
     this.firstJumboString = firstJumboString;
   }
 
@@ -184,7 +191,8 @@
     } while (!ifsNeedingRewrite.isEmpty());
 
     // Build instructions.
-    DexDebugEventBuilder debugEventBuilder = new DexDebugEventBuilder(ir.method.method);
+    DexDebugEventBuilder debugEventBuilder =
+        new DexDebugEventBuilder(ir.method.method, dexItemFactory);
     List<Instruction> dexInstructions = new ArrayList<>(numberOfInstructions);
     int instructionOffset = 0;
     InstructionIterator instructionIterator = ir.instructionIterator();
@@ -1124,4 +1132,4 @@
       this.dex = dex;
     }
   }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index d4b03e3..fa7ee4d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -316,7 +316,7 @@
     }
     assert code.isConsistentSSA();
     RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
-    method.setCode(code, registerAllocator);
+    method.setCode(code, registerAllocator, appInfo.dexItemFactory);
     if (Log.ENABLED) {
       Log.debug(getClass(), "Resulting dex code for %s:\n%s",
           method.toSourceString(), logCode(options, method));
@@ -448,7 +448,7 @@
     printMethod(code, "Optimized IR (SSA)");
     // Perform register allocation.
     RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
-    method.setCode(code, registerAllocator);
+    method.setCode(code, registerAllocator, appInfo.dexItemFactory);
     updateHighestSortingStrings(method);
     if (Log.ENABLED) {
       Log.debug(getClass(), "Resulting dex code for %s:\n%s",
@@ -502,7 +502,7 @@
 
     // Perform register allocation.
     RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
-    method.setCode(code, registerAllocator, firstJumboString);
+    method.setCode(code, registerAllocator, appInfo.dexItemFactory, firstJumboString);
 
     if (Log.ENABLED) {
       Log.debug(getClass(), "Resulting dex code for %s:\n%s",
diff --git a/src/main/java/com/android/tools/r8/optimize/DebugStripper.java b/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
index 998bc8d..9d39d29 100644
--- a/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
+++ b/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexDebugEventBuilder;
 import com.android.tools.r8.graph.DexDebugInfo;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
@@ -30,10 +31,13 @@
 
   private final ClassNameMapper classNameMapper;
   private final InternalOptions options;
+  private final DexItemFactory dexItemFactory;
 
-  public DebugStripper(ClassNameMapper classNameMapper, InternalOptions options) {
+  public DebugStripper(
+      ClassNameMapper classNameMapper, InternalOptions options, DexItemFactory dexItemFactory) {
     this.classNameMapper = classNameMapper;
     this.options = options;
+    this.dexItemFactory = dexItemFactory;
   }
 
   private String descriptorToName(String descriptor) {
@@ -70,7 +74,7 @@
     // Maintain line and address but only when entering or leaving a range of line numbers
     // that pertains to a different method body.
     Range currentRange = naming.topLevelRange;
-    DexDebugEventBuilder builder = new DexDebugEventBuilder(method);
+    DexDebugEventBuilder builder = new DexDebugEventBuilder(method, dexItemFactory);
     // Always start with a no-op bytecode to make sure that the start-line is manifested by
     // the Dalvik VM and the event based processing in R8. This also avoids empty bytecode
     // sequences.
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 7840b6e..7c75610 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -283,7 +283,7 @@
                 code);
         IRCode ir = code.buildIR(method, options);
         RegisterAllocator allocator = new LinearScanRegisterAllocator(ir);
-        method.setCode(ir, allocator);
+        method.setCode(ir, allocator, factory);
         virtualMethods[i] = method;
       }
       builder.addClassPromise(