Merge "Create gradle target for bootstrap test and use it"
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index ec863ee..46f4876 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -476,21 +476,19 @@
 
       ProguardMapSupplier proguardMapSupplier;
 
-      if (options.lineNumberOptimization != LineNumberOptimization.OFF) {
-        timing.begin("Line number remapping");
-        ClassNameMapper classNameMapper =
-            LineNumberOptimizer.run(
-                application,
-                appView.graphLense(),
-                namingLens,
-                options.lineNumberOptimization == LineNumberOptimization.IDENTITY_MAPPING);
-        timing.end();
-        proguardMapSupplier =
-            ProguardMapSupplier.fromClassNameMapper(classNameMapper, options.minApiLevel);
-      } else {
-        proguardMapSupplier =
-            ProguardMapSupplier.fromNamingLens(namingLens, application, options.minApiLevel);
-      }
+      timing.begin("Line number remapping");
+      // When line number optimization is turned off the identity mapping for line numbers is
+      // used. We still run the line number optimizer to collect line numbers and inline frame
+      // information for the mapping file.
+      ClassNameMapper classNameMapper =
+          LineNumberOptimizer.run(
+              application,
+              appView.graphLense(),
+              namingLens,
+              options.lineNumberOptimization == LineNumberOptimization.OFF);
+      timing.end();
+      proguardMapSupplier =
+          ProguardMapSupplier.fromClassNameMapper(classNameMapper, options.minApiLevel);
 
       // If a method filter is present don't produce output since the application is likely partial.
       if (options.hasMethodsFilter()) {
diff --git a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
index d640a30..83f2e8e 100644
--- a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
@@ -52,23 +52,23 @@
       return types.iterator().next();
     }
     Iterator<DexType> iterator = types.iterator();
-    TypeLatticeElement join = getLatticeElement(iterator.next());
+    TypeLatticeElement result = getLatticeElement(iterator.next());
     while (iterator.hasNext()) {
-      join = TypeLatticeElement.join(appInfo, join, getLatticeElement(iterator.next()));
+      result = result.join(getLatticeElement(iterator.next()), appInfo);
     }
     // All types are reference types so the join is either a class or an array.
-    if (join.isClassType()) {
-      return join.asClassTypeLatticeElement().getClassType();
-    } else if (join.isArrayType()) {
-      return join.asArrayTypeLatticeElement().getArrayType();
+    if (result.isClassType()) {
+      return result.asClassTypeLatticeElement().getClassType();
+    } else if (result.isArrayType()) {
+      return result.asArrayTypeLatticeElement().getArrayType();
     }
-    throw new CompilationError("Unexpected join " + join + " of types: " +
+    throw new CompilationError("Unexpected join " + result + " of types: " +
         String.join(", ",
             types.stream().map(DexType::toSourceString).collect(Collectors.toList())));
   }
 
   private TypeLatticeElement getLatticeElement(DexType type) {
-    return TypeLatticeElement.fromDexType(type, appInfo, true);
+    return TypeLatticeElement.fromDexType(type, true, appInfo);
   }
 
   public Map<Value, DexType> computeVerificationTypes() {
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 1264a26..779678d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -47,7 +47,7 @@
       writer.putUleb128(delta);
     }
 
-    AdvancePC(int delta) {
+    public AdvancePC(int delta) {
       this.delta = delta;
     }
 
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 fa856cb..6c6fda4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -206,7 +206,10 @@
     if (localsChanged()) {
       assert emittedPc != pc;
       int pcDelta = emittedPc == NO_PC_INFO ? pc : pc - emittedPc;
-      events.add(factory.createAdvancePC(pcDelta));
+      assert pcDelta > 0 || emittedPc == NO_PC_INFO;
+      if (pcDelta > 0) {
+        events.add(factory.createAdvancePC(pcDelta));
+      }
       emittedPc = pc;
       emitLocalChangeEvents(emittedLocals, pendingLocals, lastKnownLocals, events, factory);
       pendingLocalChanges = false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
index 6b368b3..128e640 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
@@ -52,7 +52,7 @@
 
   @Override
   public TypeLatticeElement arrayGet(AppInfo appInfo) {
-    return fromDexType(getArrayElementType(appInfo.dexItemFactory), appInfo, true);
+    return fromDexType(getArrayElementType(appInfo.dexItemFactory), true, appInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
index b8988c1..9711079 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
@@ -84,12 +84,12 @@
         TypeLatticeElement derived;
         if (argumentsSeen < 0) {
           // Receiver
-          derived = fromDexType(encodedMethod.method.holder, appInfo,
+          derived = fromDexType(encodedMethod.method.holder,
               // Now we try inlining even when the receiver could be null.
-              encodedMethod != context);
+              encodedMethod != context, appInfo);
         } else {
           DexType argType = encodedMethod.method.proto.parameters.values[argumentsSeen];
-          derived = fromDexType(argType, appInfo, true);
+          derived = fromDexType(argType, true, appInfo);
         }
         argumentsSeen++;
         updateTypeOfValue(outValue, derived);
@@ -149,8 +149,7 @@
 
   private TypeLatticeElement computePhiType(Phi phi) {
     // Type of phi(v1, v2, ..., vn) is the least upper bound of all those n operands.
-    return TypeLatticeElement.join(
-        appInfo, phi.getOperands().stream().map(Value::getTypeLattice));
+    return TypeLatticeElement.join(phi.getOperands().stream().map(Value::getTypeLattice), appInfo);
   }
 
   public static DexType getRefinedReceiverType(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
index a09039c..b63dbb2 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.Value;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Streams;
 import java.util.ArrayDeque;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
@@ -81,57 +82,55 @@
   /**
    * Computes the least upper bound of the current and the other elements.
    *
+   * @param other {@link TypeLatticeElement} to join.
    * @param appInfo {@link AppInfo}.
-   * @param l1 {@link TypeLatticeElement} to join.
-   * @param l2 {@link TypeLatticeElement} to join.
-   * @return {@link TypeLatticeElement}, a least upper bound of {@param l1} and {@param l2}.
+   * @return {@link TypeLatticeElement}, a least upper bound of {@param this} and {@param other}.
    */
-  public static TypeLatticeElement join(
-      AppInfo appInfo, TypeLatticeElement l1, TypeLatticeElement l2) {
-    if (l1.isBottom()) {
-      return l2;
+  public TypeLatticeElement join(TypeLatticeElement other, AppInfo appInfo) {
+    if (isBottom()) {
+      return other;
     }
-    if (l2.isBottom()) {
-      return l1;
+    if (other.isBottom()) {
+      return this;
     }
-    if (l1.isTop() || l2.isTop()) {
+    if (isTop() || other.isTop()) {
       return TOP;
     }
-    if (l1.isNull()) {
-      return l2.asNullable();
+    if (isNull()) {
+      return other.asNullable();
     }
-    if (l2.isNull()) {
-      return l1.asNullable();
+    if (other.isNull()) {
+      return asNullable();
     }
-    if (l1.isPrimitive()) {
-      return l2.isPrimitive()
+    if (isPrimitive()) {
+      return other.isPrimitive()
           ? PrimitiveTypeLatticeElement.join(
-              l1.asPrimitiveTypeLatticeElement(), l2.asPrimitiveTypeLatticeElement())
+              asPrimitiveTypeLatticeElement(), other.asPrimitiveTypeLatticeElement())
           : TOP;
     }
-    if (l2.isPrimitive()) {
-      // By the above case, !(l1.isPrimitive())
+    if (other.isPrimitive()) {
+      // By the above case, !(isPrimitive())
       return TOP;
     }
-    // From now on, l1 and l2 are reference types, but might be imprecise yet.
-    assert l1.isReference() && l2.isReference();
-    if (!l1.isPreciseType() || !l2.isPreciseType()) {
-      if (l1.isReferenceInstance()) {
-        return l1;
+    // From now on, this and other are reference types, but might be imprecise yet.
+    assert isReference() && other.isReference();
+    if (!isPreciseType() || !other.isPreciseType()) {
+      if (isReferenceInstance()) {
+        return this;
       }
-      assert l2.isReferenceInstance();
-      return l2;
+      assert other.isReferenceInstance();
+      return other;
     }
-    // From now on, l1 and l2 are precise reference types, i.e., either ArrayType or ClassType.
-    boolean isNullable = l1.isNullable() || l2.isNullable();
-    if (l1.getClass() != l2.getClass()) {
+    // From now on, this and other are precise reference types, i.e., either ArrayType or ClassType.
+    boolean isNullable = isNullable() || other.isNullable();
+    if (getClass() != other.getClass()) {
       return objectClassType(appInfo, isNullable);
     }
-    // From now on, l1.getClass() == l2.getClass()
-    if (l1.isArrayType()) {
-      assert l2.isArrayType();
-      ArrayTypeLatticeElement a1 = l1.asArrayTypeLatticeElement();
-      ArrayTypeLatticeElement a2 = l2.asArrayTypeLatticeElement();
+    // From now on, getClass() == other.getClass()
+    if (isArrayType()) {
+      assert other.isArrayType();
+      ArrayTypeLatticeElement a1 = asArrayTypeLatticeElement();
+      ArrayTypeLatticeElement a2 = other.asArrayTypeLatticeElement();
       // Identical types are the same elements
       if (a1.getArrayType() == a2.getArrayType()) {
         return a1.isNullable() ? a1 : a2;
@@ -166,10 +165,10 @@
       DexType arrayTypeLub = appInfo.dexItemFactory.createArrayType(a1Nesting, lub);
       return new ArrayTypeLatticeElement(arrayTypeLub, isNullable);
     }
-    if (l1.isClassType()) {
-      assert l2.isClassType();
-      ClassTypeLatticeElement c1 = l1.asClassTypeLatticeElement();
-      ClassTypeLatticeElement c2 = l2.asClassTypeLatticeElement();
+    if (isClassType()) {
+      assert other.isClassType();
+      ClassTypeLatticeElement c1 = asClassTypeLatticeElement();
+      ClassTypeLatticeElement c2 = other.asClassTypeLatticeElement();
       DexType lubType =
           c1.getClassType().computeLeastUpperBoundOfClasses(appInfo, c2.getClassType());
       return new ClassTypeLatticeElement(lubType, isNullable,
@@ -258,47 +257,43 @@
   }
 
   public static BinaryOperator<TypeLatticeElement> joiner(AppInfo appInfo) {
-    return (l1, l2) -> join(appInfo, l1, l2);
+    return (l1, l2) -> l1.join(l2, appInfo);
   }
 
-  public static TypeLatticeElement join(AppInfo appInfo, Stream<TypeLatticeElement> types) {
+  public static TypeLatticeElement join(Stream<TypeLatticeElement> types, AppInfo appInfo) {
     BinaryOperator<TypeLatticeElement> joiner = joiner(appInfo);
     return types.reduce(BottomTypeLatticeElement.getInstance(), joiner, joiner);
   }
 
-  public static TypeLatticeElement join(
-      AppInfo appInfo, Stream<DexType> types, boolean isNullable) {
-    return join(appInfo, types.map(t -> fromDexType(t, appInfo, isNullable)));
+  public static TypeLatticeElement joinTypes(
+      Iterable<DexType> types, boolean isNullable, AppInfo appInfo) {
+    return join(Streams.stream(types).map(t -> fromDexType(t, isNullable, appInfo)), appInfo);
   }
 
   /**
    * Determines the strict partial order of the given {@link TypeLatticeElement}s.
    *
+   * @param other expected to be *strictly* bigger than {@param this}
    * @param appInfo {@link AppInfo} to compute the least upper bound of {@link TypeLatticeElement}
-   * @param l1 subject {@link TypeLatticeElement}
-   * @param l2 expected to be *strict* bigger than {@param l1}
-   * @return {@code true} if {@param l1} is strictly less than {@param l2}.
+   * @return {@code true} if {@param this} is strictly less than {@param other}.
    */
-  public static boolean strictlyLessThan(
-      AppInfo appInfo, TypeLatticeElement l1, TypeLatticeElement l2) {
-    if (l1.equals(l2)) {
+  public boolean strictlyLessThan(TypeLatticeElement other, AppInfo appInfo) {
+    if (equals(other)) {
       return false;
     }
-    TypeLatticeElement lub = join(appInfo, Stream.of(l1, l2));
-    return !l1.equals(lub) && l2.equals(lub);
+    TypeLatticeElement lub = join(other, appInfo);
+    return !equals(lub) && other.equals(lub);
   }
 
   /**
    * Determines the partial order of the given {@link TypeLatticeElement}s.
    *
+   * @param other expected to be bigger than or equal to {@param this}
    * @param appInfo {@link AppInfo} to compute the least upper bound of {@link TypeLatticeElement}
-   * @param l1 subject {@link TypeLatticeElement}
-   * @param l2 expected to be bigger than or equal to {@param l1}
-   * @return {@code true} if {@param l1} is less than or equal to {@param l2}.
+   * @return {@code true} if {@param this} is less than or equal to {@param other}.
    */
-  public static boolean lessThanOrEqual(
-      AppInfo appInfo, TypeLatticeElement l1, TypeLatticeElement l2) {
-    return l1.equals(l2) || strictlyLessThan(appInfo, l1, l2);
+  public boolean lessThanOrEqual(TypeLatticeElement other, AppInfo appInfo) {
+    return equals(other) || strictlyLessThan(other, appInfo);
   }
 
   /**
@@ -404,14 +399,14 @@
   }
 
   public static TypeLatticeElement classClassType(AppInfo appInfo) {
-    return fromDexType(appInfo.dexItemFactory.classType, appInfo, false);
+    return fromDexType(appInfo.dexItemFactory.classType, false, appInfo);
   }
 
   public static TypeLatticeElement stringClassType(AppInfo appInfo) {
-    return fromDexType(appInfo.dexItemFactory.stringType, appInfo, false);
+    return fromDexType(appInfo.dexItemFactory.stringType, false, appInfo);
   }
 
-  public static TypeLatticeElement fromDexType(DexType type, AppInfo appInfo, boolean isNullable) {
+  public static TypeLatticeElement fromDexType(DexType type, boolean isNullable, AppInfo appInfo) {
     if (type == DexItemFactory.nullValueType) {
       return NULL;
     }
@@ -483,8 +478,8 @@
   }
 
   public TypeLatticeElement checkCast(AppInfo appInfo, DexType castType) {
-    TypeLatticeElement castTypeLattice = fromDexType(castType, appInfo, isNullable());
-    if (lessThanOrEqual(appInfo, this, castTypeLattice)) {
+    TypeLatticeElement castTypeLattice = fromDexType(castType, isNullable(), appInfo);
+    if (lessThanOrEqual(castTypeLattice, appInfo)) {
       return this;
     }
     return castTypeLattice;
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index 7caaff9..705bb97 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -366,7 +366,7 @@
     if (downcast != null) {
       Value receiver = invoke.inValues().get(0);
       TypeLatticeElement castTypeLattice = TypeLatticeElement.fromDexType(
-          downcast, appInfo, receiver.getTypeLattice().isNullable());
+          downcast, receiver.getTypeLattice().isNullable(), appInfo);
       CheckCast castInstruction =
           new CheckCast(code.createValue(castTypeLattice), receiver, downcast);
       castInstruction.setPosition(invoke.getPosition());
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index 8d461bd..03d718f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -133,12 +133,12 @@
 
     TypeLatticeElement outType = outValue().getTypeLattice();
     TypeLatticeElement castType =
-        TypeLatticeElement.fromDexType(getType(), appInfo, inType.isNullable());
+        TypeLatticeElement.fromDexType(getType(), inType.isNullable(), appInfo);
 
-    if (TypeLatticeElement.lessThanOrEqual(appInfo, inType, castType)) {
+    if (inType.lessThanOrEqual(castType, appInfo)) {
       // Cast can be removed. Check that it is sound to replace all users of the out-value by the
       // in-value.
-      assert TypeLatticeElement.lessThanOrEqual(appInfo, inType, outType);
+      assert inType.lessThanOrEqual(outType, appInfo);
 
       // TODO(b/72693244): Consider checking equivalence. This requires that the types are always
       // as precise as possible, though, meaning that almost all changes to the IR must be followed
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index 401656c..7aa9a62 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -105,7 +105,7 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.classType, appInfo, false);
+    return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.classType, false, appInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
index ffc4359..95aa81c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -91,7 +91,7 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodHandleType, appInfo, false);
+    return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodHandleType, false, appInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
index 81d7b28..d258839 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -91,7 +91,7 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodTypeType, appInfo, false);
+    return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodTypeType, false, appInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index 85fd784..e05a55f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -133,6 +133,6 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.stringType, appInfo, false);
+    return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.stringType, false, appInfo);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 0c8c372..3d30b82 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -135,7 +135,7 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(field.type, appInfo, true);
+    return TypeLatticeElement.fromDexType(field.type, true, appInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index b755336..468f3d05 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -280,6 +280,6 @@
     if (returnType.isVoidType()) {
       throw new Unreachable("void methods have no type.");
     }
-    return TypeLatticeElement.fromDexType(returnType, appInfo, true);
+    return TypeLatticeElement.fromDexType(returnType, true, appInfo);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
index 562b33d..8166f6e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MoveException.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -105,7 +105,7 @@
         if (targets.get(i) == currentBlock) {
           DexType guard = guards.get(i);
           exceptionTypes.add(
-              guard == dexItemFactory.catchAllType
+              guard == DexItemFactory.catchAllType
                   ? dexItemFactory.throwableType
                   : guard);
         }
@@ -122,8 +122,6 @@
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
     Set<DexType> exceptionTypes = collectExceptionTypes(getBlock(), appInfo.dexItemFactory);
-    return TypeLatticeElement.join(
-        appInfo,
-        exceptionTypes.stream().map(t -> TypeLatticeElement.fromDexType(t, appInfo, false)));
+    return TypeLatticeElement.joinTypes(exceptionTypes, false, appInfo);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 29b263f..18a60d4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -107,7 +107,7 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(clazz, appInfo, false);
+    return TypeLatticeElement.fromDexType(clazz, false, appInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 9ef7ed4..e767246 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -145,7 +145,7 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(field.type, appInfo, true);
+    return TypeLatticeElement.fromDexType(field.type, true, appInfo);
   }
 
   @Override
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 b471e12..1853e8b 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
@@ -680,8 +680,7 @@
     Position position = source.getCanonicalDebugPositionAtOffset(moveExceptionItem.targetOffset);
     if (moveExceptionDest >= 0) {
       Set<DexType> exceptionTypes = MoveException.collectExceptionTypes(currentBlock, getFactory());
-      TypeLatticeElement typeLattice = TypeLatticeElement.join(appInfo,
-          exceptionTypes.stream().map(t -> TypeLatticeElement.fromDexType(t, appInfo, false)));
+      TypeLatticeElement typeLattice = TypeLatticeElement.joinTypes(exceptionTypes, false, appInfo);
       Value out = writeRegister(moveExceptionDest, typeLattice, ThrowingInfo.NO_THROW, null);
       MoveException moveException = new MoveException(out);
       moveException.setPosition(position);
@@ -731,7 +730,7 @@
     DebugLocalInfo local = getOutgoingLocal(register);
     // TODO(b/72693244): Update nullability if this is for building inlinee's IR.
     TypeLatticeElement receiver =
-        TypeLatticeElement.fromDexType(method.method.getHolder(), appInfo, false);
+        TypeLatticeElement.fromDexType(method.method.getHolder(), false, appInfo);
     Value value = writeRegister(register, receiver, ThrowingInfo.NO_THROW, local);
     addInstruction(new Argument(value));
     value.markAsThis();
@@ -872,7 +871,7 @@
   public void addCheckCast(int value, DexType type) {
     Value in = readRegister(value, ValueType.OBJECT);
     TypeLatticeElement castTypeLattice =
-        TypeLatticeElement.fromDexType(type, appInfo, in.getTypeLattice().isNullable());
+        TypeLatticeElement.fromDexType(type, in.getTypeLattice().isNullable(), appInfo);
     Value out = writeRegister(value, castTypeLattice, ThrowingInfo.CAN_THROW);
     CheckCast instruction = new CheckCast(out, in, type);
     assert instruction.instructionTypeCanThrow();
@@ -931,7 +930,7 @@
           null /* sourceString */);
     }
     TypeLatticeElement typeLattice =
-        TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodHandleType, appInfo, false);
+        TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodHandleType, false, appInfo);
     Value out = writeRegister(dest, typeLattice, ThrowingInfo.CAN_THROW);
     ConstMethodHandle instruction = new ConstMethodHandle(out, methodHandle);
     add(instruction);
@@ -945,7 +944,7 @@
           null /* sourceString */);
     }
     TypeLatticeElement typeLattice =
-        TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodTypeType, appInfo, false);
+        TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodTypeType, false, appInfo);
     Value out = writeRegister(dest, typeLattice, ThrowingInfo.CAN_THROW);
     ConstMethodType instruction = new ConstMethodType(out, methodType);
     add(instruction);
@@ -1104,7 +1103,7 @@
     MemberType type = MemberType.fromDexType(field.type);
     Value in = readRegister(object, ValueType.OBJECT);
     Value out = writeRegister(
-        dest, TypeLatticeElement.fromDexType(field.type, appInfo, true), ThrowingInfo.CAN_THROW);
+        dest, TypeLatticeElement.fromDexType(field.type, true, appInfo), ThrowingInfo.CAN_THROW);
     out.setKnownToBeBoolean(type == MemberType.BOOLEAN);
     InstanceGet instruction = new InstanceGet(type, out, in, field);
     assert instruction.instructionTypeCanThrow();
@@ -1415,7 +1414,7 @@
   public void addNewArrayEmpty(int dest, int size, DexType type) {
     assert type.isArrayType();
     Value in = readRegister(size, ValueType.INT);
-    TypeLatticeElement arrayTypeLattice = TypeLatticeElement.fromDexType(type, appInfo, false);
+    TypeLatticeElement arrayTypeLattice = TypeLatticeElement.fromDexType(type, false, appInfo);
     Value out = writeRegister(dest, arrayTypeLattice, ThrowingInfo.CAN_THROW);
     NewArrayEmpty instruction = new NewArrayEmpty(out, in, type);
     assert instruction.instructionTypeCanThrow();
@@ -1427,7 +1426,7 @@
   }
 
   public void addNewInstance(int dest, DexType type) {
-    TypeLatticeElement instanceType = TypeLatticeElement.fromDexType(type, appInfo, false);
+    TypeLatticeElement instanceType = TypeLatticeElement.fromDexType(type, false, appInfo);
     Value out = writeRegister(dest, instanceType, ThrowingInfo.CAN_THROW);
     NewInstance instruction = new NewInstance(type, out);
     assert instruction.instructionTypeCanThrow();
@@ -1455,7 +1454,7 @@
   public void addStaticGet(int dest, DexField field) {
     MemberType type = MemberType.fromDexType(field.type);
     Value out = writeRegister(
-        dest, TypeLatticeElement.fromDexType(field.type, appInfo, true), ThrowingInfo.CAN_THROW);
+        dest, TypeLatticeElement.fromDexType(field.type, true, appInfo), ThrowingInfo.CAN_THROW);
     out.setKnownToBeBoolean(type == MemberType.BOOLEAN);
     StaticGet instruction = new StaticGet(type, out, field);
     assert instruction.instructionTypeCanThrow();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 129ed58..22113f8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -277,7 +277,7 @@
     if (lambdaInstanceValue == null) {
       // The out value might be empty in case it was optimized out.
       lambdaInstanceValue = code.createValue(
-          TypeLatticeElement.fromDexType(lambdaClass.type, appInfo, true));
+          TypeLatticeElement.fromDexType(lambdaClass.type, true, appInfo));
     }
 
     // For stateless lambdas we replace InvokeCustom instruction with StaticGet
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/StringConcatRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/StringConcatRewriter.java
index c805494..1b13398 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/StringConcatRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/StringConcatRewriter.java
@@ -337,7 +337,7 @@
 
       // new-instance v0, StringBuilder
       TypeLatticeElement stringBuilderTypeLattice =
-          TypeLatticeElement.fromDexType(factory.stringBuilderType, appInfo, false);
+          TypeLatticeElement.fromDexType(factory.stringBuilderType, false, appInfo);
       Value sbInstance = code.createValue(stringBuilderTypeLattice);
       appendInstruction(new NewInstance(factory.stringBuilderType, sbInstance));
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index f8b85de..93df273 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -297,7 +297,7 @@
       BasicBlock rethrowBlock = BasicBlock.createRethrowBlock(
           code,
           lastSelfRecursiveCall.getPosition(),
-          TypeLatticeElement.fromDexType(guard, appInfo, true));
+          TypeLatticeElement.fromDexType(guard, true, appInfo));
       code.blocks.add(rethrowBlock);
       // Add catch handler to the block containing the last recursive call.
       newBlock.addCatchHandler(rethrowBlock, guard);
@@ -1660,11 +1660,11 @@
       if (inTypeLattice.isPreciseType() || inTypeLattice.isNull()) {
         TypeLatticeElement outTypeLattice = outValue.getTypeLattice();
         TypeLatticeElement castTypeLattice =
-            TypeLatticeElement.fromDexType(castType, appInfo, inTypeLattice.isNullable());
+            TypeLatticeElement.fromDexType(castType, inTypeLattice.isNullable(), appInfo);
 
         assert inTypeLattice.nullElement().lessThanOrEqual(outTypeLattice.nullElement());
 
-        if (TypeLatticeElement.lessThanOrEqual(appInfo, inTypeLattice, castTypeLattice)) {
+        if (inTypeLattice.lessThanOrEqual(castTypeLattice, appInfo)) {
           // 1) Trivial cast.
           //   A a = ...
           //   A a' = (A) a;
@@ -1672,7 +1672,7 @@
           //   A < B
           //   A a = ...
           //   B b = (B) a;
-          assert TypeLatticeElement.lessThanOrEqual(appInfo, inTypeLattice, outTypeLattice);
+          assert inTypeLattice.lessThanOrEqual(outTypeLattice, appInfo);
           needToRemoveTrivialPhis = needToRemoveTrivialPhis || outValue.numberOfPhiUsers() != 0;
           removeOrReplaceByDebugLocalWrite(checkCast, it, inValue, outValue);
         } else {
@@ -3013,7 +3013,7 @@
     DexType javaLangSystemType = dexItemFactory.createType("Ljava/lang/System;");
     DexType javaIoPrintStreamType = dexItemFactory.createType("Ljava/io/PrintStream;");
     Value out = code.createValue(
-        TypeLatticeElement.fromDexType(javaIoPrintStreamType, appInfo, false));
+        TypeLatticeElement.fromDexType(javaIoPrintStreamType, false, appInfo));
 
     DexProto proto = dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.objectType);
     DexMethod print = dexItemFactory.createMethod(javaIoPrintStreamType, proto, "print");
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 37cfb44..e7b6ca8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -110,14 +110,13 @@
           Value receiver = invoke.getReceiver();
           TypeLatticeElement receiverTypeLattice = receiver.getTypeLattice();
           TypeLatticeElement castTypeLattice =
-              TypeLatticeElement.fromDexType(holderType, appInfo, receiverTypeLattice.isNullable());
+              TypeLatticeElement.fromDexType(holderType, receiverTypeLattice.isNullable(), appInfo);
           // Avoid adding trivial cast and up-cast.
           // We should not use strictlyLessThan(castType, receiverType), which detects downcast,
           // due to side-casts, e.g., A (unused) < I, B < I, and cast from A to B.
           // TODO(b/72693244): Soon, there won't be a value with imprecise type at this point.
           if (!receiverTypeLattice.isPreciseType()
-              || !TypeLatticeElement.lessThanOrEqual(
-                  appInfo, receiverTypeLattice, castTypeLattice)) {
+              || !receiverTypeLattice.lessThanOrEqual(castTypeLattice, appInfo)) {
             Value newReceiver = null;
             // If this value is ever downcast'ed to the same holder type before, and that casted
             // value is safely accessible, i.e., the current line is dominated by that cast, use it.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
index 538d7be..51da91d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
@@ -115,7 +115,7 @@
     NewInstance patchedNewInstance = new NewInstance(
         group.getGroupClassType(),
         context.code.createValue(
-            TypeLatticeElement.fromDexType(newInstance.clazz, context.appInfo, false)));
+            TypeLatticeElement.fromDexType(newInstance.clazz, false, context.appInfo)));
     context.instructions().replaceCurrentInstruction(patchedNewInstance);
   }
 
@@ -160,7 +160,7 @@
     // Since all captured values of non-primitive types are stored in fields of type
     // java.lang.Object, we need to cast them to appropriate type to satisfy the verifier.
     TypeLatticeElement castTypeLattice =
-        TypeLatticeElement.fromDexType(fieldType, context.appInfo, false);
+        TypeLatticeElement.fromDexType(fieldType, false, context.appInfo);
     Value newValue = context.code.createValue(castTypeLattice, newInstanceGet.getLocalInfo());
     newInstanceGet.outValue().replaceUsers(newValue);
     CheckCast cast = new CheckCast(newValue, newInstanceGet.outValue(), fieldType);
@@ -187,7 +187,7 @@
         new StaticGet(
             staticGet.getType(),
             context.code.createValue(
-                TypeLatticeElement.fromDexType(staticGet.getField().type, context.appInfo, true)),
+                TypeLatticeElement.fromDexType(staticGet.getField().type, true, context.appInfo)),
             mapSingletonInstanceField(context.factory, staticGet.getField())));
   }
 
@@ -222,7 +222,7 @@
   private Value createValueForType(CodeProcessor context, DexType returnType) {
     return returnType == context.factory.voidType ? null :
         context.code.createValue(
-            TypeLatticeElement.fromDexType(returnType, context.appInfo, true));
+            TypeLatticeElement.fromDexType(returnType, true, context.appInfo));
   }
 
   private List<Value> mapInitializerArgs(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 669baf9..ac3a8af 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -273,7 +273,7 @@
               new StaticGet(
                   MemberType.fromDexType(field.type),
                   code.createValue(
-                      TypeLatticeElement.fromDexType(field.type, classStaticizer.appInfo, true),
+                      TypeLatticeElement.fromDexType(field.type, true, classStaticizer.appInfo),
                       outValue.getLocalInfo()),
                   field
               )
@@ -303,7 +303,7 @@
           Value newOutValue = method.proto.returnType.isVoidType() ? null
               : code.createValue(
                   TypeLatticeElement.fromDexType(
-                      method.proto.returnType, classStaticizer.appInfo, true),
+                      method.proto.returnType, true, classStaticizer.appInfo),
                   outValue == null ? null : outValue.getLocalInfo());
           it.replaceCurrentInstruction(
               new InvokeStatic(newMethod, newOutValue, invoke.inValues()));
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index f45a3de..c47a7bd 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -401,7 +401,7 @@
         builder.append(minifiedRange).append(':');
       }
       builder.append(signature);
-      if (originalRange != null) {
+      if (originalRange != null && !minifiedRange.equals(originalRange)) {
         builder.append(":").append(originalRange);
       }
       builder.append(" -> ").append(renamedName);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 828a463..e6fd878 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -41,8 +41,7 @@
 
   public enum LineNumberOptimization {
     OFF,
-    ON,
-    IDENTITY_MAPPING
+    ON
   }
 
   public final DexItemFactory itemFactory;
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 66490b2..e81d920 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -10,6 +10,16 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexDebugEvent;
+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.SetInlineFrame;
+import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd;
+import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
 import com.android.tools.r8.graph.DexDebugEventBuilder;
 import com.android.tools.r8.graph.DexDebugEventVisitor;
 import com.android.tools.r8.graph.DexDebugInfo;
@@ -41,86 +51,6 @@
 
 public class LineNumberOptimizer {
 
-  // EventFilter is a visitor for DebugEvents, splits events into two sinks:
-  // - Forwards non-positional events unchanged into a BypassedEventReceiver
-  // - Forwards positional events, accumulated into DexDebugPositionStates, into
-  //   positionEventReceiver.
-  private static class EventFilter implements DexDebugEventVisitor {
-    private final BypassedEventReceiver bypassedEventReceiver;
-    private final PositionEventReceiver positionEventReceiver;
-
-    private interface BypassedEventReceiver {
-      void receiveBypassedEvent(DexDebugEvent event);
-    }
-
-    private interface PositionEventReceiver {
-      void receivePositionEvent(DexDebugPositionState positionState);
-    }
-
-    private final DexDebugPositionState positionState;
-
-    private EventFilter(
-        int startLine,
-        DexMethod method,
-        BypassedEventReceiver bypassedEventReceiver,
-        PositionEventReceiver positionEventReceiver) {
-      positionState = new DexDebugPositionState(startLine, method);
-      this.bypassedEventReceiver = bypassedEventReceiver;
-      this.positionEventReceiver = positionEventReceiver;
-    }
-
-    @Override
-    public void visit(DexDebugEvent.SetPrologueEnd event) {
-      bypassedEventReceiver.receiveBypassedEvent(event);
-    }
-
-    @Override
-    public void visit(DexDebugEvent.SetEpilogueBegin event) {
-      bypassedEventReceiver.receiveBypassedEvent(event);
-    }
-
-    @Override
-    public void visit(DexDebugEvent.StartLocal event) {
-      bypassedEventReceiver.receiveBypassedEvent(event);
-    }
-
-    @Override
-    public void visit(DexDebugEvent.EndLocal event) {
-      bypassedEventReceiver.receiveBypassedEvent(event);
-    }
-
-    @Override
-    public void visit(DexDebugEvent.RestartLocal event) {
-      bypassedEventReceiver.receiveBypassedEvent(event);
-    }
-
-    @Override
-    public void visit(DexDebugEvent.AdvancePC advancePC) {
-      positionState.visit(advancePC);
-    }
-
-    @Override
-    public void visit(DexDebugEvent.AdvanceLine advanceLine) {
-      positionState.visit(advanceLine);
-    }
-
-    @Override
-    public void visit(DexDebugEvent.SetInlineFrame setInlineFrame) {
-      positionState.visit(setInlineFrame);
-    }
-
-    @Override
-    public void visit(DexDebugEvent.Default defaultEvent) {
-      positionState.visit(defaultEvent);
-      positionEventReceiver.receivePositionEvent(positionState);
-    }
-
-    @Override
-    public void visit(DexDebugEvent.SetFile setFile) {
-      positionState.visit(setFile);
-    }
-  }
-
   // PositionRemapper is a stateful function which takes a position (represented by a
   // DexDebugPositionState) and returns a remapped Position.
   private interface PositionRemapper {
@@ -165,6 +95,11 @@
       this.processedEvents = processedEvents;
     }
 
+    private void emitAdvancePc(int pc) {
+      processedEvents.add(new AdvancePC(pc - previousPc));
+      previousPc = pc;
+    }
+
     private void emitPositionEvents(int currentPc, Position currentPosition) {
       if (previousPosition == null) {
         startLine = currentPosition.line;
@@ -255,7 +190,6 @@
 
         for (DexEncodedMethod method : methods) {
           List<MappedPosition> mappedPositions = new ArrayList<>();
-
           Code code = method.getCode();
           if (code != null) {
             if (code.isDexCode() && doesContainPositions(code.asDexCode())) {
@@ -469,36 +403,96 @@
     DexDebugInfo debugInfo = dexCode.getDebugInfo();
     List<DexDebugEvent> processedEvents = new ArrayList<>();
 
-    // Our pipeline will be:
-    // [debugInfo.events] -> eventFilter -> positionRemapper -> positionEventEmitter ->
-    // [processedEvents]
     PositionEventEmitter positionEventEmitter =
         new PositionEventEmitter(application.dexItemFactory, method.method, processedEvents);
 
-    EventFilter eventFilter =
-        new EventFilter(
-            debugInfo.startLine,
-            method.method,
-            processedEvents::add,
-            positionState -> {
-              int currentLine = positionState.getCurrentLine();
-              assert currentLine >= 0;
-              Position position =
-                  positionRemapper.createRemappedPosition(
-                      positionState.getCurrentLine(),
-                      positionState.getCurrentFile(),
-                      positionState.getCurrentMethod(),
-                      positionState.getCurrentCallerPosition());
-              mappedPositions.add(
-                  new MappedPosition(
-                      positionState.getCurrentMethod(),
-                      currentLine,
-                      positionState.getCurrentCallerPosition(),
-                      position.line));
-              positionEventEmitter.emitPositionEvents(positionState.getCurrentPc(), position);
-            });
+    // Debug event visitor to map line numbers.
+    // TODO(117268618): Cleanup the duplicate pc tracking.
+    DexDebugEventVisitor visitor =
+        new DexDebugEventVisitor() {
+          DexDebugPositionState state =
+              new DexDebugPositionState(debugInfo.startLine, method.method);
+          int currentPc = 0;
+
+          private void flushPc() {
+            if (currentPc != state.getCurrentPc()) {
+              positionEventEmitter.emitAdvancePc(state.getCurrentPc());
+              currentPc = state.getCurrentPc();
+            }
+          }
+
+          @Override
+          public void visit(AdvancePC advancePC) {
+            state.visit(advancePC);
+          }
+
+          @Override
+          public void visit(AdvanceLine advanceLine) {
+            state.visit(advanceLine);
+          }
+
+          @Override
+          public void visit(SetInlineFrame setInlineFrame) {
+            state.visit(setInlineFrame);
+          }
+
+          @Override
+          public void visit(Default defaultEvent) {
+            state.visit(defaultEvent);
+            int currentLine = state.getCurrentLine();
+            assert currentLine >= 0;
+            Position position =
+                positionRemapper.createRemappedPosition(
+                    state.getCurrentLine(),
+                    state.getCurrentFile(),
+                    state.getCurrentMethod(),
+                    state.getCurrentCallerPosition());
+            mappedPositions.add(
+                new MappedPosition(
+                    state.getCurrentMethod(),
+                    currentLine,
+                    state.getCurrentCallerPosition(),
+                    position.line));
+            positionEventEmitter.emitPositionEvents(state.getCurrentPc(), position);
+            currentPc = state.getCurrentPc();
+          }
+
+          @Override
+          public void visit(SetFile setFile) {
+            processedEvents.add(setFile);
+          }
+
+          @Override
+          public void visit(SetPrologueEnd setPrologueEnd) {
+            processedEvents.add(setPrologueEnd);
+          }
+
+          @Override
+          public void visit(SetEpilogueBegin setEpilogueBegin) {
+            processedEvents.add(setEpilogueBegin);
+          }
+
+          @Override
+          public void visit(StartLocal startLocal) {
+            flushPc();
+            processedEvents.add(startLocal);
+          }
+
+          @Override
+          public void visit(EndLocal endLocal) {
+            flushPc();
+            processedEvents.add(endLocal);
+          }
+
+          @Override
+          public void visit(RestartLocal restartLocal) {
+            flushPc();
+            processedEvents.add(restartLocal);
+          }
+        };
+
     for (DexDebugEvent event : debugInfo.events) {
-      event.accept(eventFilter);
+      event.accept(visitor);
     }
 
     DexDebugInfo optimizedDebugInfo =
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 3adece1..515d191 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -105,6 +105,9 @@
   private static final String PROGUARD6_0_1 = "third_party/proguard/proguard6.0.1/bin/proguard";
   private static final String PROGUARD = PROGUARD5_2_1;
 
+  private static final String RETRACE6_0_1 = "third_party/proguard/proguard6.0.1/bin/retrace";
+  private static final String RETRACE = RETRACE6_0_1;
+
   public static final Path R8_JAR = Paths.get(LIBS_DIR, "r8.jar");
   public static final Path R8_LIB_JAR = Paths.get(LIBS_DIR, "r8lib_with_deps.jar");
 
@@ -525,6 +528,13 @@
     return PROGUARD6_0_1 + ".sh";
   }
 
+  private static String getRetraceScript() {
+    if (isWindows()) {
+      return RETRACE + ".bat";
+    }
+    return RETRACE + ".sh";
+  }
+
   private static Path getDxExecutablePath() {
     String toolsDir = toolsDir();
     String executableName = toolsDir.equals("windows") ? "dx.bat" : "dx";
@@ -1499,6 +1509,23 @@
     return runProguard(getProguard6Script(), inJar, outJar, configs, map);
   }
 
+  public static ProcessResult runRetraceRaw(Path map, Path stackTrace) throws IOException {
+    List<String> command = new ArrayList<>();
+    command.add(getRetraceScript());
+    command.add(map.toString());
+    command.add(stackTrace.toString());
+    ProcessBuilder builder = new ProcessBuilder(command);
+    return ToolHelper.runProcess(builder);
+  }
+
+  public static String runRetrace(Path map, Path stackTrace) throws IOException {
+    ProcessResult result = runRetraceRaw(map, stackTrace);
+    if (result.exitCode != 0) {
+      fail("Retrace failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
+    }
+    return result.stdout;
+  }
+
   public static class ProcessResult {
 
     public final int exitCode;
diff --git a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
index 616256e..78e9a56 100644
--- a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
@@ -96,12 +96,6 @@
   }
 
   @Test
-  public void testIdentityCompilation() throws Throwable {
-    // Compilation will fail if the identity translation does.
-    makeConfig(LineNumberOptimization.IDENTITY_MAPPING, true, false, runtimeKind);
-  }
-
-  @Test
   public void testNotOptimized() throws Throwable {
     testRelease(
         makeConfig(LineNumberOptimization.OFF, false, false, runtimeKind), ORIGINAL_LINE_NUMBERS);
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index 109a6cb..bd0f8fb 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -120,9 +120,9 @@
       DexType mainClass = appInfo.dexItemFactory.createType(
           DescriptorUtils.javaTypeToDescriptor(NonNullAfterInvoke.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
-          InvokeVirtual.class, fromDexType(appInfo.dexItemFactory.stringType, appInfo, true),
-          NonNull.class, fromDexType(appInfo.dexItemFactory.stringType, appInfo, false),
-          NewInstance.class, fromDexType(assertionErrorType, appInfo, false));
+          InvokeVirtual.class, fromDexType(appInfo.dexItemFactory.stringType, true, appInfo),
+          NonNull.class, fromDexType(appInfo.dexItemFactory.stringType, false, appInfo),
+          NewInstance.class, fromDexType(assertionErrorType, false, appInfo));
       forEachOutValue(irCode, (v, l) -> verifyClassTypeLattice(expectedLattices, mainClass, v, l));
     });
   }
@@ -136,9 +136,9 @@
       DexType mainClass = appInfo.dexItemFactory.createType(
           DescriptorUtils.javaTypeToDescriptor(NonNullAfterInvoke.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
-          InvokeVirtual.class, fromDexType(appInfo.dexItemFactory.stringType, appInfo, true),
-          NonNull.class, fromDexType(appInfo.dexItemFactory.stringType, appInfo, false),
-          NewInstance.class, fromDexType(assertionErrorType, appInfo, false));
+          InvokeVirtual.class, fromDexType(appInfo.dexItemFactory.stringType, true, appInfo),
+          NonNull.class, fromDexType(appInfo.dexItemFactory.stringType, false, appInfo),
+          NewInstance.class, fromDexType(assertionErrorType, false, appInfo));
       forEachOutValue(irCode, (v, l) -> verifyClassTypeLattice(expectedLattices, mainClass, v, l));
     });
   }
@@ -153,8 +153,8 @@
           DescriptorUtils.javaTypeToDescriptor(NonNullAfterArrayAccess.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
           // An element inside a non-null array could be null.
-          ArrayGet.class, fromDexType(appInfo.dexItemFactory.stringType, appInfo, true),
-          NewInstance.class, fromDexType(assertionErrorType, appInfo, false));
+          ArrayGet.class, fromDexType(appInfo.dexItemFactory.stringType, true, appInfo),
+          NewInstance.class, fromDexType(assertionErrorType, false, appInfo));
       forEachOutValue(irCode, (v, l) -> {
         if (l.isArrayType()) {
           ArrayTypeLatticeElement lattice = l.asArrayTypeLatticeElement();
@@ -179,8 +179,8 @@
           DescriptorUtils.javaTypeToDescriptor(NonNullAfterArrayAccess.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
           // An element inside a non-null array could be null.
-          ArrayGet.class, fromDexType(appInfo.dexItemFactory.stringType, appInfo, true),
-          NewInstance.class, fromDexType(assertionErrorType, appInfo, false));
+          ArrayGet.class, fromDexType(appInfo.dexItemFactory.stringType, true, appInfo),
+          NewInstance.class, fromDexType(assertionErrorType, false, appInfo));
       forEachOutValue(irCode, (v, l) -> {
         if (l.isArrayType()) {
           ArrayTypeLatticeElement lattice = l.asArrayTypeLatticeElement();
@@ -206,11 +206,11 @@
       DexType testClass = appInfo.dexItemFactory.createType(
           DescriptorUtils.javaTypeToDescriptor(FieldAccessTest.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
-          Argument.class, fromDexType(testClass, appInfo, true),
-          NonNull.class, fromDexType(testClass, appInfo, false),
+          Argument.class, fromDexType(testClass, true, appInfo),
+          NonNull.class, fromDexType(testClass, false, appInfo),
           // instance may not be initialized.
-          InstanceGet.class, fromDexType(appInfo.dexItemFactory.stringType, appInfo, true),
-          NewInstance.class, fromDexType(assertionErrorType, appInfo, false));
+          InstanceGet.class, fromDexType(appInfo.dexItemFactory.stringType, true, appInfo),
+          NewInstance.class, fromDexType(assertionErrorType, false, appInfo));
       forEachOutValue(irCode, (v, l) -> verifyClassTypeLattice(expectedLattices, mainClass, v, l));
     });
   }
@@ -226,11 +226,11 @@
       DexType testClass = appInfo.dexItemFactory.createType(
           DescriptorUtils.javaTypeToDescriptor(FieldAccessTest.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
-          Argument.class, fromDexType(testClass, appInfo, true),
-          NonNull.class, fromDexType(testClass, appInfo, false),
+          Argument.class, fromDexType(testClass, true, appInfo),
+          NonNull.class, fromDexType(testClass, false, appInfo),
           // instance may not be initialized.
-          InstanceGet.class, fromDexType(appInfo.dexItemFactory.stringType, appInfo, true),
-          NewInstance.class, fromDexType(assertionErrorType, appInfo, false));
+          InstanceGet.class, fromDexType(appInfo.dexItemFactory.stringType, true, appInfo),
+          NewInstance.class, fromDexType(assertionErrorType, false, appInfo));
       forEachOutValue(irCode, (v, l) -> verifyClassTypeLattice(expectedLattices, mainClass, v, l));
     });
   }
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
index 8d956a8..08a220f 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
@@ -75,7 +75,7 @@
   }
 
   private TypeLatticeElement element(DexType type) {
-    return TypeLatticeElement.fromDexType(type, appInfo, true);
+    return TypeLatticeElement.fromDexType(type, true, appInfo);
   }
 
   private ArrayTypeLatticeElement array(int nesting, DexType base) {
@@ -84,15 +84,15 @@
 
   private TypeLatticeElement join(TypeLatticeElement... elements) {
     assertTrue(elements.length > 1);
-    return TypeLatticeElement.join(appInfo, Arrays.stream(elements));
+    return TypeLatticeElement.join(Arrays.stream(elements), appInfo);
   }
 
   private boolean strictlyLessThan(TypeLatticeElement l1, TypeLatticeElement l2) {
-    return TypeLatticeElement.strictlyLessThan(appInfo, l1, l2);
+    return l1.strictlyLessThan(l2, appInfo);
   }
 
   private boolean lessThanOrEqual(TypeLatticeElement l1, TypeLatticeElement l2) {
-    return TypeLatticeElement.lessThanOrEqual(appInfo, l1, l2);
+    return l1.lessThanOrEqual(l2, appInfo);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java
new file mode 100644
index 0000000..7ef1c3f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java
@@ -0,0 +1,135 @@
+// Copyright (c) 2018, 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.naming.retrace;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.BiConsumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RetraceTest extends TestBase {
+  private Backend backend;
+  private CompilationMode mode;
+
+  @Parameters(name = "Backend: {0}, mode: {1}")
+  public static Collection<Object[]> data() {
+    List<Object[]> parameters = new ArrayList<>();
+    for (Backend backend : Backend.values()) {
+      for (CompilationMode mode : CompilationMode.values()) {
+        parameters.add(new Object[] {backend, mode});
+      }
+    }
+    return parameters;
+  }
+
+  public RetraceTest(Backend backend, CompilationMode mode) {
+    this.backend = backend;
+    this.mode = mode;
+  }
+
+  private List<String> retrace(String map, List<String> stackTrace) throws IOException {
+    Path t = temp.newFolder().toPath();
+    Path mapFile = t.resolve("map");
+    Path stackTraceFile = t.resolve("stackTrace");
+    FileUtils.writeTextFile(mapFile, map);
+    FileUtils.writeTextFile(stackTraceFile, stackTrace);
+    return StringUtils.splitLines(ToolHelper.runRetrace(mapFile, stackTraceFile));
+  }
+
+  private List<String> extractStackTrace(ProcessResult result) {
+    ImmutableList.Builder<String> builder = ImmutableList.builder();
+    List<String> stderr = StringUtils.splitLines(result.stderr);
+    Iterator<String> iterator = stderr.iterator();
+    while (iterator.hasNext()) {
+      String line = iterator.next();
+      if (line.startsWith("Exception in thread \"main\"")) {
+        break;
+      }
+    }
+    iterator.forEachRemaining(builder::add);
+    return builder.build();
+  }
+
+  public void runTest(Class<?> mainClass, BiConsumer<List<String>, List<String>> checker)
+      throws Exception {
+    StringBuilder proguardMapBuilder = new StringBuilder();
+    AndroidApp output =
+        ToolHelper.runR8(
+            R8Command.builder()
+                .setMode(mode)
+                .addClassProgramData(ToolHelper.getClassAsBytes(mainClass), Origin.unknown())
+                .addProguardConfiguration(
+                    ImmutableList.of(keepMainProguardConfiguration(mainClass)), Origin.unknown())
+                .setProgramConsumer(emptyConsumer(backend))
+                .setProguardMapConsumer((string, ignore) -> proguardMapBuilder.append(string))
+                .build());
+
+    ProcessResult result = runOnVMRaw(output, mainClass, backend);
+    List<String> stackTrace = extractStackTrace(result);
+    List<String> retracesStackTrace = retrace(proguardMapBuilder.toString(), stackTrace);
+    checker.accept(stackTrace, retracesStackTrace);
+  }
+
+  @Test
+  public void test() throws Exception {
+    runTest(
+        Main.class,
+        (List<String> stackTrace, List<String> retracesStackTrace) -> {
+          assertEquals(
+              mode == CompilationMode.RELEASE, stackTrace.size() != retracesStackTrace.size());
+          if (mode == CompilationMode.DEBUG) {
+            assertThat(stackTrace.get(0), not(containsString("method2")));
+            assertThat(stackTrace.get(1), not(containsString("method1")));
+            assertThat(stackTrace.get(2), containsString("main"));
+          }
+          assertEquals(3, retracesStackTrace.size());
+          assertThat(retracesStackTrace.get(0), containsString("method2"));
+          assertThat(retracesStackTrace.get(1), containsString("method1"));
+          assertThat(retracesStackTrace.get(2), containsString("main"));
+        });
+  }
+}
+
+class Main {
+  public static void method2(int i) {
+    System.out.println("In method2");
+    throw null;
+  }
+
+  public static void method1(String s) {
+    System.out.println("In method1");
+    for (int i = 0; i < 10; i++) {
+      method2(Integer.parseInt(s));
+    }
+  }
+
+  public static void main(String[] args) {
+    System.out.println("In main");
+    method1("1");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java
index b518683..96ce54a 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -13,23 +13,36 @@
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.debuginfo.DebugInfoInspector;
-import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.forceproguardcompatibility.keepattributes.TestKeepAttributes;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
-import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class KeepAttributesTest extends TestBase {
 
-  public static final Class CLASS = TestKeepAttributes.class;
+  private static final Class CLASS = TestKeepAttributes.class;
+
+  @Parameters(name = "{0}")
+  public static Backend[] parameters() {
+    return new Backend[] { Backend.CF, Backend.DEX};
+  }
+
+  private final Backend backend;
+
+  public KeepAttributesTest(Backend backend) {
+    this.backend = backend;
+  }
 
   @Test
   public void keepAllAttributesInDebugMode()
@@ -37,10 +50,9 @@
     List<String> keepRules = ImmutableList.of(
         "-keep class ** { *; }"
     );
-    CodeInspector inspector = compile(keepRules, CompilationMode.DEBUG);
-    DebugInfoInspector debugInfo = debugInfoForMain(inspector);
-    checkLineNumbers(true, debugInfo);
-    checkLocals(true, debugInfo);
+    MethodSubject mainMethod = compileRunAndGetMain(keepRules, CompilationMode.DEBUG);
+    assertTrue(mainMethod.hasLineNumberTable());
+    assertTrue(mainMethod.hasLocalVariableTable());
   }
 
   @Test
@@ -49,10 +61,9 @@
     List<String> keepRules = ImmutableList.of(
         "-keep class ** { *; }"
     );
-    CodeInspector inspector = compile(keepRules, CompilationMode.RELEASE);
-    DebugInfoInspector debugInfo = debugInfoForMain(inspector);
-    checkLineNumbers(false, debugInfo);
-    checkLocals(false, debugInfo);
+    MethodSubject mainMethod = compileRunAndGetMain(keepRules, CompilationMode.RELEASE);
+    assertFalse(mainMethod.hasLineNumberTable());
+    assertFalse(mainMethod.hasLocalVariableTable());
   }
 
   @Test
@@ -62,10 +73,9 @@
         "-keep class ** { *; }",
         "-keepattributes " + ProguardKeepAttributes.LINE_NUMBER_TABLE
     );
-    CodeInspector inspector = compile(keepRules, CompilationMode.RELEASE);
-    DebugInfoInspector debugInfo = debugInfoForMain(inspector);
-    checkLineNumbers(true, debugInfo);
-    checkLocals(false, debugInfo);
+    MethodSubject mainMethod = compileRunAndGetMain(keepRules, CompilationMode.RELEASE);
+    assertTrue(mainMethod.hasLineNumberTable());
+    assertFalse(mainMethod.hasLocalVariableTable());
   }
 
   @Test
@@ -78,11 +88,10 @@
             + ", "
             + ProguardKeepAttributes.LOCAL_VARIABLE_TABLE
     );
-    CodeInspector inspector = compile(keepRules, CompilationMode.RELEASE);
-    DebugInfoInspector debugInfo = debugInfoForMain(inspector);
-    checkLineNumbers(true, debugInfo);
+    MethodSubject mainMethod = compileRunAndGetMain(keepRules, CompilationMode.RELEASE);
+    assertTrue(mainMethod.hasLineNumberTable());
     // Locals are never included in release builds.
-    checkLocals(false, debugInfo);
+    assertFalse(mainMethod.hasLocalVariableTable());
   }
 
   @Test
@@ -93,7 +102,7 @@
     );
     // Compiling with a keep rule for locals but no line results in an error in R8.
     try {
-      compile(keepRules, CompilationMode.RELEASE);
+      compileRunAndGetMain(keepRules, CompilationMode.RELEASE);
     } catch (CompilationFailedException e) {
       assertTrue(e.getCause().getMessage().contains(ProguardKeepAttributes.LOCAL_VARIABLE_TABLE));
       assertTrue(e.getCause().getMessage().contains(ProguardKeepAttributes.LINE_NUMBER_TABLE));
@@ -102,7 +111,7 @@
     fail("Expected error");
   }
 
-  private CodeInspector compile(List<String> keepRules, CompilationMode mode)
+  private MethodSubject compileRunAndGetMain(List<String> keepRules, CompilationMode mode)
       throws CompilationFailedException, IOException, ExecutionException {
     AndroidAppConsumers sink = new AndroidAppConsumers();
     R8.run(
@@ -111,30 +120,14 @@
             .addProgramFiles(
                 ToolHelper.getClassFilesForTestDirectory(
                     ToolHelper.getClassFileForTestClass(CLASS).getParent()))
-            .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
+            .addLibraryFiles(runtimeJar(backend))
             .addProguardConfiguration(keepRules, Origin.unknown())
-            .setProgramConsumer(sink.wrapProgramConsumer(emptyConsumer(Backend.DEX)))
+            .setProgramConsumer(sink.wrapProgramConsumer(emptyConsumer(backend)))
             .build());
     AndroidApp app = sink.build();
     CodeInspector codeInspector = new CodeInspector(app);
-    runOnArt(app, CLASS.getTypeName());
-    return codeInspector;
+    runOnVM(app, CLASS.getTypeName(), backend);
+    return codeInspector.clazz(CLASS).mainMethod();
   }
 
-  private DebugInfoInspector debugInfoForMain(CodeInspector inspector) {
-    return new DebugInfoInspector(
-        inspector,
-        CLASS.getTypeName(),
-        new MethodSignature("main", "void", Collections.singleton("java.lang.String[]")));
-  }
-
-  private void checkLineNumbers(boolean expected, DebugInfoInspector debugInfo) {
-    assertEquals("Expected " + (expected ? "line entries" : "no line entries"),
-        expected, debugInfo.getEntries().stream().anyMatch(e -> e.lineEntry));
-  }
-
-  private void checkLocals(boolean expected, DebugInfoInspector debugInfo) {
-    assertEquals("Expected " + (expected ? "locals" : "no locals"),
-        expected, debugInfo.getEntries().stream().anyMatch(e -> !e.locals.isEmpty()));
-  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index 1396be9..adee8cf 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -78,4 +78,14 @@
   public String getFinalSignatureAttribute() {
     return null;
   }
+
+  @Override
+  public boolean hasLineNumberTable() {
+    return false;
+  }
+
+  @Override
+  public boolean hasLocalVariableTable() {
+    return false;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 3fe8186..f205594 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -4,12 +4,22 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfPosition;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugEvent;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.naming.MemberNaming;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.signature.GenericSignatureParser;
 import java.util.Iterator;
+import java.util.ListIterator;
 import java.util.function.Predicate;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.LineNumberNode;
 
 public class FoundMethodSubject extends MethodSubject {
 
@@ -134,6 +144,69 @@
   }
 
   @Override
+  public boolean hasLineNumberTable() {
+    Code code = getMethod().getCode();
+    if (code.isDexCode()) {
+      DexCode dexCode = code.asDexCode();
+      if (dexCode.getDebugInfo() != null) {
+        for (DexDebugEvent event : dexCode.getDebugInfo().events) {
+          if (event instanceof DexDebugEvent.Default) {
+            return true;
+          }
+        }
+      }
+      return false;
+    }
+    if (code.isCfCode()) {
+      for (CfInstruction insn : code.asCfCode().getInstructions()) {
+        if (insn instanceof CfPosition) {
+          return true;
+        }
+      }
+      return false;
+    }
+    if (code.isJarCode()) {
+      ListIterator<AbstractInsnNode> it = code.asJarCode().getNode().instructions.iterator();
+      while (it.hasNext()) {
+        if (it.next() instanceof LineNumberNode) {
+          return true;
+        }
+      }
+      return false;
+    }
+    throw new Unreachable("Unexpected code type: " + code.getClass().getSimpleName());
+  }
+
+  @Override
+  public boolean hasLocalVariableTable() {
+    Code code = getMethod().getCode();
+    if (code.isDexCode()) {
+      DexCode dexCode = code.asDexCode();
+      if (dexCode.getDebugInfo() != null) {
+        for (DexString parameter : dexCode.getDebugInfo().parameters) {
+          if (parameter != null) {
+            return true;
+          }
+        }
+        for (DexDebugEvent event : dexCode.getDebugInfo().events) {
+          if (event instanceof DexDebugEvent.StartLocal) {
+            return true;
+          }
+        }
+      }
+      return false;
+    }
+    if (code.isCfCode()) {
+      return !code.asCfCode().getLocalVariables().isEmpty();
+    }
+    if (code.isJarCode()) {
+      return code.asJarCode().getNode().localVariables != null
+          && !code.asJarCode().getNode().localVariables.isEmpty();
+    }
+    throw new Unreachable("Unexpected code type: " + code.getClass().getSimpleName());
+  }
+
+  @Override
   public String toString() {
     return dexMethod.toSourceString();
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 6eaaf5f..55030ca 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -32,4 +32,8 @@
       Predicate<InstructionSubject> filter) {
     return null;
   }
+
+  public abstract boolean hasLineNumberTable();
+
+  public abstract boolean hasLocalVariableTable();
 }