Merge commit '3ac5a16eb35ca70b690c7b85964e397114a921dd' into dev-release
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 74a3d20..068f2ec 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -274,7 +274,7 @@
       boolean hasClassResources = appView.appInfo().app().getFlags().hasReadProgramClassFromCf();
       boolean hasDexResources = appView.appInfo().app().getFlags().hasReadProgramClassFromDex();
 
-      Marker marker = options.getMarker(Tool.D8);
+      Marker marker = hasClassResources ? options.getMarker(Tool.D8) : null;
 
       timing.time(
           "Run inspections",
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 21fccf4..a8dea77 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -283,7 +283,7 @@
         new SameNestHost(appView),
         new SameParentClass(),
         new SyntheticItemsPolicy(appView, mode),
-        new RespectPackageBoundaries(appView),
+        new RespectPackageBoundaries(appView, mode),
         new NoDifferentApiReferenceLevel(appView),
         new NoIndirectRuntimeTypeChecks(appView, runtimeTypeCheckInfo),
         new NoWeakerAccessPrivileges(appView, immediateSubtypingInfo),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
index e3986c8..ded7e1d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
@@ -7,11 +7,13 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.MergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
 import com.android.tools.r8.shaking.VerticalClassMerger.IllegalAccessDetector;
@@ -25,9 +27,11 @@
 public class RespectPackageBoundaries extends MultiClassPolicy {
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
+  private final Mode mode;
 
-  public RespectPackageBoundaries(AppView<? extends AppInfoWithClassHierarchy> appView) {
+  public RespectPackageBoundaries(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
     this.appView = appView;
+    this.mode = mode;
   }
 
   boolean shouldRestrictMergingAcrossPackageBoundary(DexProgramClass clazz) {
@@ -83,7 +87,29 @@
             method -> {
               boolean foundIllegalAccess =
                   method.registerCodeReferencesWithResult(
-                      new IllegalAccessDetector(appView, method));
+                      new IllegalAccessDetector(appView, method) {
+
+                        @Override
+                        protected boolean checkRewrittenFieldType(DexClassAndField field) {
+                          if (mode.isInitial()) {
+                            // If the type of the field is package private, we need to keep the
+                            // current class in its package in case we end up synthesizing a
+                            // check-cast for the field type after relaxing the type of the field
+                            // after instance field merging.
+                            DexType fieldBaseType = field.getType().toBaseType(dexItemFactory());
+                            if (fieldBaseType.isClassType()) {
+                              DexClass fieldBaseClass = appView.definitionFor(fieldBaseType);
+                              if (fieldBaseClass == null || !fieldBaseClass.isPublic()) {
+                                return setFoundPackagePrivateAccess();
+                              }
+                            }
+                          } else {
+                            // No relaxing of field types, hence no insertion of casts where we need
+                            // to guarantee visibility.
+                          }
+                          return continueSearchForPackagePrivateAccess();
+                        }
+                      });
               if (foundIllegalAccess) {
                 return TraversalContinuation.doBreak();
               }
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
index b17536e..834fd24 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
 import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.lightir.LIRBuilder;
 
 /**
  * Instruction introducing an SSA value with attached local information.
@@ -87,4 +88,9 @@
     assert verifyTypesHelper.isAssignable(src().getType(), getOutType());
     return true;
   }
+
+  @Override
+  public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
+    builder.addDebugLocalWrite(src());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java
index 06d9d8f..6d5083c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java
@@ -11,19 +11,24 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.Set;
 
 public class AssertionErrorTwoArgsConstructorRewriter {
 
@@ -41,11 +46,12 @@
     if (options.canUseAssertionErrorTwoArgumentConstructor()) {
       return;
     }
-
+    Set<Value> newOutValues = Sets.newIdentityHashSet();
     ListIterator<BasicBlock> blockIterator = code.listIterator();
     while (blockIterator.hasNext()) {
       BasicBlock block = blockIterator.next();
       InstructionListIterator insnIterator = block.listIterator(code);
+      List<NewInstance> newInstancesToRemove = new ArrayList<>();
       while (insnIterator.hasNext()) {
         Instruction current = insnIterator.next();
         if (current.isInvokeMethod()) {
@@ -53,24 +59,31 @@
           if (invokedMethod == dexItemFactory.assertionErrorMethods.initMessageAndCause) {
             List<Value> inValues = current.inValues();
             assert inValues.size() == 3; // receiver, message, cause
-
-            Value assertionError =
-                code.createValue(
-                    TypeElement.fromDexType(
-                        dexItemFactory.assertionErrorType,
-                        Nullability.definitelyNotNull(),
-                        appView));
-            Instruction invoke =
-                new InvokeStatic(
-                    createSynthetic(methodProcessingContext).getReference(),
-                    assertionError,
-                    inValues.subList(1, 3));
+            Value newInstanceValue = current.getFirstOperand();
+            Instruction definition = newInstanceValue.getDefinition();
+            if (!definition.isNewInstance()) {
+              continue;
+            }
+            InvokeStatic invoke =
+                InvokeStatic.builder()
+                    .setMethod(createSynthetic(methodProcessingContext).getReference())
+                    .setFreshOutValue(
+                        code, dexItemFactory.assertionErrorType.toTypeElement(appView))
+                    .setPosition(current)
+                    .setArguments(inValues.subList(1, 3))
+                    .build();
             insnIterator.replaceCurrentInstruction(invoke);
-            inValues.get(0).replaceUsers(assertionError);
-            inValues.get(0).definition.removeOrReplaceByDebugLocalRead(code);
+            newInstanceValue.replaceUsers(invoke.outValue());
+            newOutValues.add(invoke.outValue());
+            newInstancesToRemove.add(definition.asNewInstance());
           }
         }
       }
+      newInstancesToRemove.forEach(
+          newInstance -> newInstance.removeOrReplaceByDebugLocalRead(code));
+    }
+    if (!newOutValues.isEmpty()) {
+      new TypeAnalysis(appView).widening(newOutValues);
     }
     assert code.isConsistentSSA(appView);
   }
@@ -103,6 +116,15 @@
                                     factory, methodSig)));
     synchronized (synthesizedMethods) {
       synthesizedMethods.add(method);
+      OptimizationFeedback.getSimpleFeedback()
+          .setDynamicReturnType(
+              method,
+              appView,
+              DynamicType.createExact(
+                  dexItemFactory
+                      .assertionErrorType
+                      .toTypeElement(appView, Nullability.definitelyNotNull())
+                      .asClassType()));
     }
     return method;
   }
diff --git a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
index 4e148ee..9bdc15a 100644
--- a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
@@ -27,35 +27,45 @@
 
 public class IR2LIRConverter {
 
-  private IR2LIRConverter() {}
+  private final DexItemFactory factory;
+  private final IRCode irCode;
+  private final Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>();
+  private final Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
+  private final LIRBuilder<Value, BasicBlock> builder;
 
-  public static LIRCode translate(IRCode irCode, DexItemFactory factory) {
-    irCode.traceBlocks();
-    Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>();
-    Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
-    int instructionIndex = 0;
-    int valueIndex = 0;
-    for (BasicBlock block : irCode.blocks) {
-      blocks.put(block, instructionIndex);
-      for (Phi phi : block.getPhis()) {
-        values.put(phi, valueIndex);
-        valueIndex++;
-        instructionIndex++;
-      }
-      for (Instruction instruction : block.getInstructions()) {
-        if (instruction.hasOutValue()) {
-          values.put(instruction.outValue(), valueIndex);
-        }
-        valueIndex++;
-        if (!instruction.isArgument()) {
-          instructionIndex++;
-        }
-      }
-    }
-    LIRBuilder<Value, BasicBlock> builder =
+  private IR2LIRConverter(DexItemFactory factory, IRCode irCode) {
+    this.factory = factory;
+    this.irCode = irCode;
+    this.builder =
         new LIRBuilder<Value, BasicBlock>(
                 irCode.context().getReference(), values::getInt, blocks::getInt, factory)
             .setMetadata(irCode.metadata());
+  }
+
+  public static LIRCode translate(IRCode irCode, DexItemFactory factory) {
+    return new IR2LIRConverter(factory, irCode).internalTranslate();
+  }
+
+  private void recordBlock(BasicBlock block, int blockIndex) {
+    blocks.put(block, blockIndex);
+  }
+
+  private void recordValue(Value value, int valueIndex) {
+    values.put(value, valueIndex);
+    if (value.hasLocalInfo()) {
+      builder.setDebugValue(value.getLocalInfo(), valueIndex);
+    }
+  }
+
+  private LIRCode internalTranslate() {
+    irCode.traceBlocks();
+    computeBlockAndValueTables();
+    computeInstructions();
+    return builder.build();
+  }
+
+  private void computeInstructions() {
+    int currentInstructionIndex = 0;
     BasicBlockIterator blockIt = irCode.listIterator();
     while (blockIt.hasNext()) {
       BasicBlock block = blockIt.next();
@@ -67,6 +77,7 @@
         for (Phi phi : block.getPhis()) {
           permuteOperands(phi.getOperands(), permutation, operands);
           builder.addPhi(phi.getType(), Arrays.asList(operands));
+          currentInstructionIndex++;
         }
       }
       if (block.hasCatchHandlers()) {
@@ -79,20 +90,47 @@
       InstructionIterator it = block.iterator();
       while (it.hasNext()) {
         Instruction instruction = it.next();
+        assert !instruction.hasOutValue()
+            || currentInstructionIndex == values.getInt(instruction.outValue());
         builder.setCurrentPosition(instruction.getPosition());
+        if (!instruction.getDebugValues().isEmpty()) {
+          builder.setDebugLocalEnds(currentInstructionIndex, instruction.getDebugValues());
+        }
+
         if (instruction.isGoto()) {
           BasicBlock nextBlock = blockIt.peekNext();
           if (instruction.asGoto().getTarget() == nextBlock) {
             builder.addFallthrough();
-          } else {
-            instruction.buildLIR(builder);
+            currentInstructionIndex++;
+            continue;
           }
-        } else {
-          instruction.buildLIR(builder);
+        }
+        instruction.buildLIR(builder);
+        currentInstructionIndex++;
+      }
+    }
+  }
+
+  private void computeBlockAndValueTables() {
+    int instructionIndex = 0;
+    int valueIndex = 0;
+    for (BasicBlock block : irCode.blocks) {
+      recordBlock(block, instructionIndex);
+      for (Phi phi : block.getPhis()) {
+        recordValue(phi, valueIndex);
+        valueIndex++;
+        instructionIndex++;
+      }
+      for (Instruction instruction : block.getInstructions()) {
+        if (instruction.hasOutValue()) {
+          recordValue(instruction.outValue(), valueIndex);
+        }
+        valueIndex++;
+        if (!instruction.isArgument()) {
+          instructionIndex++;
         }
       }
     }
-    return builder.build();
   }
 
   private static void permuteOperands(List<Value> operands, int[] permutation, Value[] output) {
diff --git a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
index 056129b..28547c0 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.ConstString;
+import com.android.tools.r8.ir.code.DebugLocalWrite;
 import com.android.tools.r8.ir.code.DebugPosition;
 import com.android.tools.r8.ir.code.Div;
 import com.android.tools.r8.ir.code.Goto;
@@ -192,18 +193,21 @@
       return arguments;
     }
 
+    public int toInstructionIndexInIR(int lirIndex) {
+      return lirIndex + code.getArgumentCount();
+    }
+
     public int peekNextInstructionIndex() {
       return nextInstructionIndex;
     }
 
     public Value getOutValueForNextInstruction(TypeElement type) {
-      // TODO(b/225838009): Support debug locals.
-      DebugLocalInfo localInfo = null;
-      int index = peekNextInstructionIndex() + code.getArgumentCount();
-      Value value = values[index];
+      int valueIndex = toInstructionIndexInIR(peekNextInstructionIndex());
+      DebugLocalInfo localInfo = code.getDebugLocalInfo(valueIndex);
+      Value value = values[valueIndex];
       if (value == null) {
         value = new Value(valueNumberGenerator.next(), type, localInfo);
-        values[index] = value;
+        values[valueIndex] = value;
       } else {
         value.setType(type);
         if (localInfo != null) {
@@ -214,17 +218,17 @@
     }
 
     public Phi getPhiForNextInstructionAndAdvanceState(TypeElement type) {
-      int index = peekNextInstructionIndex() + code.getArgumentCount();
+      int valueIndex = toInstructionIndexInIR(peekNextInstructionIndex());
+      DebugLocalInfo localInfo = code.getDebugLocalInfo(valueIndex);
       // TODO(b/225838009): The phi constructor implicitly adds to the block, so we need to ensure
       //  the block. However, we must grab the index above. Find a way to clean this up so it is
       //  uniform with instructions.
       advanceInstructionState();
       // Creating the phi implicitly adds it to currentBlock.
-      DebugLocalInfo localInfo = null;
       Phi phi =
           new Phi(
               valueNumberGenerator.next(), currentBlock, type, localInfo, RegisterReadType.NORMAL);
-      Value value = values[index];
+      Value value = values[valueIndex];
       if (value != null) {
         // A fake ssa value has already been created, replace the users by the actual phi.
         // TODO(b/225838009): We could consider encoding the value type as a bit in the value index
@@ -232,7 +236,7 @@
         assert !value.isPhi();
         value.replaceUsers(phi);
       }
-      values[index] = phi;
+      values[valueIndex] = phi;
       return phi;
     }
 
@@ -243,10 +247,18 @@
     }
 
     private void addInstruction(Instruction instruction) {
+      int index = toInstructionIndexInIR(peekNextInstructionIndex());
       advanceInstructionState();
       instruction.setPosition(currentPosition);
       currentBlock.getInstructions().add(instruction);
       instruction.setBlock(currentBlock);
+      int[] debugEndIndices = code.getDebugLocalEnds(index);
+      if (debugEndIndices != null) {
+        for (int debugEndIndex : debugEndIndices) {
+          Value debugValue = getValue(debugEndIndex);
+          debugValue.addDebugLocalEnd(instruction);
+        }
+      }
     }
 
     private void addThisArgument(DexType type) {
@@ -259,6 +271,10 @@
       // which would otherwise advance the state.
       Value dest = getValue(index);
       dest.setType(type.toTypeElement(appView));
+      DebugLocalInfo localInfo = code.getDebugLocalInfo(index);
+      if (localInfo != null) {
+        dest.setLocalInfo(localInfo);
+      }
       Argument argument = new Argument(dest, index, type.isBooleanType());
       assert currentBlock != null;
       assert currentPosition.isSyntheticPosition();
@@ -380,5 +396,15 @@
       Value dest = getOutValueForNextInstruction(exceptionType.toTypeElement(appView));
       addInstruction(new MoveException(dest, exceptionType, appView.options()));
     }
+
+    @Override
+    public void onDebugLocalWrite(int srcIndex) {
+      Value src = getValue(srcIndex);
+      // The type is in the local table, so initialize it with bottom and reset with the local info.
+      Value dest = getOutValueForNextInstruction(TypeElement.getBottom());
+      TypeElement type = dest.getLocalInfo().type.toTypeElement(appView);
+      dest.setType(type);
+      addInstruction(new DebugLocalWrite(dest, src));
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
index 2971d2e..44876ed 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -19,6 +20,7 @@
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.lightir.LIRCode.DebugLocalInfoTable;
 import com.android.tools.r8.lightir.LIRCode.PositionEntry;
 import com.android.tools.r8.lightir.LIRCode.TryCatchTable;
 import com.android.tools.r8.utils.ListUtils;
@@ -29,7 +31,9 @@
 import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Builder for constructing LIR code from IR.
@@ -68,6 +72,11 @@
   private final Int2ReferenceMap<CatchHandlers<Integer>> tryCatchRanges =
       new Int2ReferenceOpenHashMap<>();
 
+  // Mapping from SSA value definition to the local name index in the constant pool.
+  private final Int2ReferenceMap<DebugLocalInfo> debugLocals = new Int2ReferenceOpenHashMap<>();
+  // Mapping from instruction to the end usage of SSA values with debug local info.
+  private final Int2ReferenceMap<int[]> debugLocalEnds = new Int2ReferenceOpenHashMap();
+
   // TODO(b/225838009): Reconsider this fixed space as the operand count for phis is much larger.
   // Pre-allocated space for caching value indexes when writing instructions.
   private static final int MAX_VALUE_COUNT = 10;
@@ -87,6 +96,16 @@
     flushedPosition = currentPosition;
   }
 
+  public DexType toDexType(TypeElement typeElement) {
+    if (typeElement.isPrimitiveType()) {
+      return typeElement.asPrimitiveType().toDexType(factory);
+    }
+    if (typeElement.isReferenceType()) {
+      return typeElement.asReferenceType().toDexType(factory);
+    }
+    throw new Unreachable("Unexpected type element: " + typeElement);
+  }
+
   public void addTryCatchHanders(int blockIndex, CatchHandlers<Integer> handlers) {
     tryCatchRanges.put(blockIndex, handlers);
   }
@@ -149,6 +168,23 @@
     return this;
   }
 
+  public LIRBuilder<V, B> setDebugValue(DebugLocalInfo debugInfo, int valueIndex) {
+    DebugLocalInfo old = debugLocals.put(valueIndex, debugInfo);
+    assert old == null;
+    return this;
+  }
+
+  public LIRBuilder<V, B> setDebugLocalEnds(int instructionIndex, Set<V> endValues) {
+    int size = endValues.size();
+    int[] indices = new int[size];
+    Iterator<V> iterator = endValues.iterator();
+    for (int i = 0; i < size; i++) {
+      indices[i] = getValueIndex(iterator.next());
+    }
+    debugLocalEnds.put(instructionIndex, indices);
+    return this;
+  }
+
   public LIRBuilder<V, B> addArgument(int index, boolean knownToBeBoolean) {
     // Arguments are implicitly given by method descriptor and not an actual instruction.
     assert argumentCount == index;
@@ -387,18 +423,21 @@
   }
 
   public LIRBuilder<V, B> addPhi(TypeElement type, List<V> operands) {
-    DexType dexType =
-        type.isPrimitiveType()
-            ? type.asPrimitiveType().toDexType(factory)
-            : type.asReferenceType().toDexType(factory);
+    DexType dexType = toDexType(type);
     return addInstructionTemplate(LIROpcodes.PHI, Collections.singletonList(dexType), operands);
   }
 
+  public LIRBuilder<V, B> addDebugLocalWrite(V src) {
+    return addOneValueInstruction(LIROpcodes.DEBUGLOCALWRITE, src);
+  }
+
   public LIRCode build() {
     assert metadata != null;
     int constantsCount = constants.size();
     DexItem[] constantTable = new DexItem[constantsCount];
     constants.forEach((item, index) -> constantTable[index] = item);
+    DebugLocalInfoTable debugTable =
+        debugLocals.isEmpty() ? null : new DebugLocalInfoTable(debugLocals, debugLocalEnds);
     return new LIRCode(
         metadata,
         constantTable,
@@ -406,6 +445,7 @@
         argumentCount,
         byteWriter.toByteArray(),
         instructionCount,
-        new TryCatchTable(tryCatchRanges));
+        new TryCatchTable(tryCatchRanges),
+        debugTable);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRCode.java b/src/main/java/com/android/tools/r8/lightir/LIRCode.java
index 8caa8d6..09619f4 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRCode.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.lightir;
 
+import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
@@ -40,6 +41,20 @@
     }
   }
 
+  public static class DebugLocalInfoTable {
+    private final Int2ReferenceMap<DebugLocalInfo> valueToLocalMap;
+    private final Int2ReferenceMap<int[]> instructionToEndUseMap;
+
+    public DebugLocalInfoTable(
+        Int2ReferenceMap<DebugLocalInfo> valueToLocalMap,
+        Int2ReferenceMap<int[]> instructionToEndUseMap) {
+      assert !valueToLocalMap.isEmpty();
+      assert !instructionToEndUseMap.isEmpty();
+      this.valueToLocalMap = valueToLocalMap;
+      this.instructionToEndUseMap = instructionToEndUseMap;
+    }
+  }
+
   private final IRMetadata metadata;
 
   /** Constant pool of items. */
@@ -59,6 +74,9 @@
   /** Table of try-catch handlers for each basic block. */
   private final TryCatchTable tryCatchTable;
 
+  /** Table of debug local information for each SSA value (if present). */
+  private final DebugLocalInfoTable debugLocalInfoTable;
+
   public static <V, B> LIRBuilder<V, B> builder(
       DexMethod method,
       ValueIndexGetter<V> valueIndexGetter,
@@ -75,7 +93,8 @@
       int argumentCount,
       byte[] instructions,
       int instructionCount,
-      TryCatchTable tryCatchTable) {
+      TryCatchTable tryCatchTable,
+      DebugLocalInfoTable debugLocalInfoTable) {
     this.metadata = metadata;
     this.constants = constants;
     this.positionTable = positions;
@@ -83,6 +102,7 @@
     this.instructions = instructions;
     this.instructionCount = instructionCount;
     this.tryCatchTable = tryCatchTable;
+    this.debugLocalInfoTable = debugLocalInfoTable;
   }
 
   public int getArgumentCount() {
@@ -113,6 +133,20 @@
     return tryCatchTable;
   }
 
+  public DebugLocalInfoTable getDebugLocalInfoTable() {
+    return debugLocalInfoTable;
+  }
+
+  public DebugLocalInfo getDebugLocalInfo(int valueIndex) {
+    return debugLocalInfoTable == null ? null : debugLocalInfoTable.valueToLocalMap.get(valueIndex);
+  }
+
+  public int[] getDebugLocalEnds(int instructionIndex) {
+    return debugLocalInfoTable == null
+        ? null
+        : debugLocalInfoTable.instructionToEndUseMap.get(instructionIndex);
+  }
+
   @Override
   public LIRIterator iterator() {
     return new LIRIterator(new ByteArrayIterator(instructions));
diff --git a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
index 0b0dfc8..abfea89 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
@@ -186,6 +186,7 @@
   int PHI = 206;
   int FALLTHROUGH = 207;
   int MOVEEXCEPTION = 208;
+  int DEBUGLOCALWRITE = 209;
 
   static String toString(int opcode) {
     switch (opcode) {
@@ -498,6 +499,8 @@
         return "FALLTHROUGH";
       case MOVEEXCEPTION:
         return "MOVEEXCEPTION";
+      case DEBUGLOCALWRITE:
+        return "DEBUGLOCALWRITE";
 
       default:
         throw new Unreachable("Unexpected LIR opcode: " + opcode);
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
index 5c87faf..f0bc3f3 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
@@ -57,6 +57,8 @@
 
   public void onMoveException(DexType exceptionType) {}
 
+  public void onDebugLocalWrite(int srcIndex) {}
+
   public void onInvokeMethodInstruction(DexMethod method, IntList arguments) {}
 
   public void onInvokeDirect(DexMethod method, IntList arguments) {
@@ -199,6 +201,12 @@
           onMoveException(type);
           return;
         }
+      case LIROpcodes.DEBUGLOCALWRITE:
+        {
+          int srcIndex = view.getNextValueOperand();
+          onDebugLocalWrite(srcIndex);
+          return;
+        }
       default:
         throw new Unimplemented("No dispatch for opcode " + LIROpcodes.toString(opcode));
     }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
index 9a74b4e..bf9123e 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
@@ -20,7 +20,7 @@
   // that allow for retracing classes in the form <class>: lorem ipsum...
   // Seems like Proguard retrace is expecting the form "Caused by: <class>".
   public static final String DEFAULT_REGULAR_EXPRESSION =
-      "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%S\\)\\s*(?:~\\[.*\\])?)"
+      "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%S\\)\\p{Z}*(?:~\\[.*\\])?)"
           + "|(?:(?:(?:%c|.*)?[:\"]\\s+)?%c(?::.*)?)";
 
   private final Pattern compiledPattern;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index bc1ce29..b479808 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.graph.DefaultInstanceInitializerCode;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMember;
@@ -2106,8 +2107,8 @@
     }
   }
 
-  // Searches for a reference to a non-public class, field or method declared in the same package
-  // as [source].
+  // Searches for a reference to a non-private, non-public class, field or method declared in the
+  // same package as [source].
   public static class IllegalAccessDetector extends UseRegistryWithResult<Boolean, ProgramMethod> {
 
     private final AppView<? extends AppInfoWithClassHierarchy> appView;
@@ -2118,106 +2119,144 @@
       this.appView = appView;
     }
 
-    private boolean checkFieldReference(DexField field) {
-      DexType baseType =
-          appView.graphLens().lookupType(field.holder.toBaseType(appView.dexItemFactory()));
-      if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
-        if (checkTypeReference(field.holder) || checkTypeReference(field.type)) {
-          return true;
-        }
+    protected boolean checkFoundPackagePrivateAccess() {
+      assert getResult();
+      return true;
+    }
 
-        DexEncodedField definition = appView.appInfo().resolveField(field).getResolvedField();
-        if (definition == null || !definition.accessFlags.isPublic()) {
-          setResult(true);
-          return true;
-        }
-      }
+    protected boolean setFoundPackagePrivateAccess() {
+      setResult(true);
+      return true;
+    }
+
+    protected static boolean continueSearchForPackagePrivateAccess() {
       return false;
     }
 
-    private boolean checkMethodReference(DexMethod method, OptionalBool isInterface) {
-      DexType baseType =
-          appView.graphLens().lookupType(method.holder.toBaseType(appView.dexItemFactory()));
-      if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
-        if (checkTypeReference(method.holder)
-            || checkTypeReference(method.proto.returnType)
-            || Iterables.any(method.getParameters(), this::checkTypeReference)) {
-          return true;
-        }
+    private boolean checkFieldReference(DexField field) {
+      return checkRewrittenFieldReference(appView.graphLens().lookupField(field));
+    }
 
-        MethodResolutionResult resolutionResult =
-            isInterface.isUnknown()
-                ? appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(method)
-                : appView.appInfo().resolveMethodLegacy(method, isInterface.isTrue());
-        if (!resolutionResult.isSingleResolution()
-            || !resolutionResult.asSingleResolution().getResolvedMethod().isPublic()) {
-          setResult(true);
-          return true;
+    private boolean checkRewrittenFieldReference(DexField field) {
+      assert field.getHolderType().isClassType();
+      DexType fieldHolder = field.getHolderType();
+      if (fieldHolder.isSamePackage(getContext().getHolderType())) {
+        if (checkRewrittenTypeReference(fieldHolder)) {
+          return checkFoundPackagePrivateAccess();
+        }
+        DexClassAndField resolvedField = appView.appInfo().resolveField(field).getResolutionPair();
+        if (resolvedField == null) {
+          return setFoundPackagePrivateAccess();
+        }
+        if (resolvedField.getHolder() != getContext().getHolder()
+            && !resolvedField.getAccessFlags().isPublic()) {
+          return setFoundPackagePrivateAccess();
+        }
+        if (checkRewrittenFieldType(resolvedField)) {
+          return checkFoundPackagePrivateAccess();
         }
       }
-      return false;
+      return continueSearchForPackagePrivateAccess();
+    }
+
+    protected boolean checkRewrittenFieldType(DexClassAndField field) {
+      return continueSearchForPackagePrivateAccess();
+    }
+
+    private boolean checkRewrittenMethodReference(
+        DexMethod rewrittenMethod, OptionalBool isInterface) {
+      DexType baseType = rewrittenMethod.getHolderType().toBaseType(appView.dexItemFactory());
+      if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
+        if (checkTypeReference(rewrittenMethod.getHolderType())) {
+          return checkFoundPackagePrivateAccess();
+        }
+        MethodResolutionResult resolutionResult =
+            isInterface.isUnknown()
+                ? appView.appInfo().unsafeResolveMethodDueToDexFormat(rewrittenMethod)
+                : appView.appInfo().resolveMethod(rewrittenMethod, isInterface.isTrue());
+        if (!resolutionResult.isSingleResolution()) {
+          return setFoundPackagePrivateAccess();
+        }
+        DexClassAndMethod resolvedMethod =
+            resolutionResult.asSingleResolution().getResolutionPair();
+        if (resolvedMethod.getHolder() != getContext().getHolder()
+            && !resolvedMethod.getAccessFlags().isPublic()) {
+          return setFoundPackagePrivateAccess();
+        }
+      }
+      return continueSearchForPackagePrivateAccess();
     }
 
     private boolean checkTypeReference(DexType type) {
-      DexType baseType = appView.graphLens().lookupType(type.toBaseType(appView.dexItemFactory()));
+      return internalCheckTypeReference(type, appView.graphLens());
+    }
+
+    private boolean checkRewrittenTypeReference(DexType type) {
+      return internalCheckTypeReference(type, GraphLens.getIdentityLens());
+    }
+
+    private boolean internalCheckTypeReference(DexType type, GraphLens graphLens) {
+      DexType baseType = graphLens.lookupType(type.toBaseType(appView.dexItemFactory()));
       if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
         DexClass clazz = appView.definitionFor(baseType);
-        if (clazz == null || !clazz.accessFlags.isPublic()) {
-          setResult(true);
-          return true;
+        if (clazz == null || !clazz.isPublic()) {
+          return setFoundPackagePrivateAccess();
         }
       }
-      return false;
+      return continueSearchForPackagePrivateAccess();
     }
 
     @Override
     public void registerInitClass(DexType clazz) {
-      checkTypeReference(clazz);
+      if (appView.initClassLens().isFinal()) {
+        // The InitClass lens is always rewritten up until the most recent graph lens, so first map
+        // the class type to the most recent graph lens.
+        DexType rewrittenType = appView.graphLens().lookupType(clazz);
+        DexField initClassField = appView.initClassLens().getInitClassField(rewrittenType);
+        checkRewrittenFieldReference(initClassField);
+      } else {
+        checkTypeReference(clazz);
+      }
     }
 
     @Override
     public void registerInvokeVirtual(DexMethod method) {
-      MethodLookupResult lookup =
-          appView.graphLens().lookupMethod(method, getContext().getReference(), VIRTUAL);
-      checkMethodReference(lookup.getReference(), OptionalBool.FALSE);
+      MethodLookupResult lookup = appView.graphLens().lookupInvokeVirtual(method, getContext());
+      checkRewrittenMethodReference(lookup.getReference(), OptionalBool.FALSE);
     }
 
     @Override
     public void registerInvokeDirect(DexMethod method) {
-      MethodLookupResult lookup =
-          appView.graphLens().lookupMethod(method, getContext().getReference(), DIRECT);
-      checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
+      MethodLookupResult lookup = appView.graphLens().lookupInvokeDirect(method, getContext());
+      checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
     }
 
     @Override
     public void registerInvokeStatic(DexMethod method) {
-      MethodLookupResult lookup =
-          appView.graphLens().lookupMethod(method, getContext().getReference(), Type.STATIC);
-      checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
+      MethodLookupResult lookup = appView.graphLens().lookupInvokeStatic(method, getContext());
+      checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
     }
 
     @Override
     public void registerInvokeInterface(DexMethod method) {
-      MethodLookupResult lookup =
-          appView.graphLens().lookupMethod(method, getContext().getReference(), Type.INTERFACE);
-      checkMethodReference(lookup.getReference(), OptionalBool.TRUE);
+      MethodLookupResult lookup = appView.graphLens().lookupInvokeInterface(method, getContext());
+      checkRewrittenMethodReference(lookup.getReference(), OptionalBool.TRUE);
     }
 
     @Override
     public void registerInvokeSuper(DexMethod method) {
-      MethodLookupResult lookup =
-          appView.graphLens().lookupMethod(method, getContext().getReference(), Type.SUPER);
-      checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
+      MethodLookupResult lookup = appView.graphLens().lookupInvokeSuper(method, getContext());
+      checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
     }
 
     @Override
     public void registerInstanceFieldWrite(DexField field) {
-      checkFieldReference(appView.graphLens().lookupField(field));
+      checkFieldReference(field);
     }
 
     @Override
     public void registerInstanceFieldRead(DexField field) {
-      checkFieldReference(appView.graphLens().lookupField(field));
+      checkFieldReference(field);
     }
 
     @Override
@@ -2227,12 +2266,12 @@
 
     @Override
     public void registerStaticFieldRead(DexField field) {
-      checkFieldReference(appView.graphLens().lookupField(field));
+      checkFieldReference(field);
     }
 
     @Override
     public void registerStaticFieldWrite(DexField field) {
-      checkFieldReference(appView.graphLens().lookupField(field));
+      checkFieldReference(field);
     }
 
     @Override
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InitClassToPackagePrivateFieldWithCrossPackageMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InitClassToPackagePrivateFieldWithCrossPackageMergingTest.java
index ed2130d..af9f880 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InitClassToPackagePrivateFieldWithCrossPackageMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InitClassToPackagePrivateFieldWithCrossPackageMergingTest.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
 import org.junit.Test;
@@ -47,16 +47,13 @@
         .addProgramClassFileData(getProgramClassFileData())
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
-            inspector ->
-                inspector.assertMergedInto(
-                    Reference.classFromClass(A.class),
-                    Reference.classFromDescriptor(NEW_B_DESCRIPTOR)))
+            HorizontallyMergedClassesInspector::assertNoClassesMerged)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .run(parameters.getRuntime(), Main.class)
-        .assertFailureWithErrorThatThrows(IllegalAccessError.class);
+        .assertSuccessWithOutputLines("Hello, world!");
   }
 
   private List<byte[]> getProgramClassFileData() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java b/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
index e17ac14..0370c29 100644
--- a/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
@@ -29,7 +28,8 @@
 
 /**
  * Simple test that compiles framework.jar with D8 a number of times with various number of threads
- * available to the compiler. This test also tests the hidden marker inserted into classes.dex.
+ * available to the compiler. This test also tests that no hidden marker is inserted into
+ * classes.dex.
  */
 @RunWith(Parameterized.class)
 public class D8FrameworkDexPassthroughMarkerTest {
@@ -62,15 +62,10 @@
     Marker marker = new Marker(Tool.D8)
         .setVersion("1.0.0")
         .setMinApi(minApi);
-    InternalOptions options = new InternalOptions();
-    DexString markerDexString = options.itemFactory.createString(marker.toString());
-    Marker selfie = Marker.parse(markerDexString);
-    assert marker.equals(selfie);
     AndroidApp app = ToolHelper.runD8(command, opts -> opts.setMarker(marker));
-    DexApplication dexApp = new ApplicationReader(app, options, Timing.empty()).read();
+    DexApplication dexApp =
+        new ApplicationReader(app, new InternalOptions(), Timing.empty()).read();
     Collection<Marker> markers = dexApp.dexItemFactory.extractMarkers();
-    assertEquals(1, markers.size());
-    Marker readMarker = markers.iterator().next();
-    assertEquals(marker, readMarker);
+    assertEquals(0, markers.size());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
index dbeb66b..c78212b 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
@@ -40,6 +40,7 @@
 import java.nio.file.Paths;
 import java.util.concurrent.ExecutionException;
 import org.junit.After;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -191,6 +192,7 @@
    * Validates that when all protos are kept and the proto optmization is enabled, the generated
    * proto schemas are identical to the proto schemas in the input.
    */
+  @Ignore
   @Test
   public void testProtoRewriting() throws Exception {
     assumeTrue(shouldRunSlowTests());
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 907ca27..483f18b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.kotlin.TestKotlinClass.AccessorKind;
 import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
 import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -219,7 +220,9 @@
     runTest(PROPERTIES_PACKAGE_NAME, mainClass)
         .inspect(
             inspector -> {
-              if (allowAccessModification) {
+              if (allowAccessModification
+                  || (testParameters.isDexRuntime()
+                      && testParameters.getApiLevel().isGreaterThan(AndroidApiLevel.B))) {
                 checkClassIsRemoved(inspector, testedClass.getOuterClassName());
                 return;
               }
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index 59ba99b..527e568 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
 import com.android.tools.r8.naming.MemberNaming;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -509,7 +510,9 @@
             inspector -> {
               checkClassIsRemoved(inspector, testedClass.getClassName());
 
-              if (allowAccessModification) {
+              if (allowAccessModification
+                  || (testParameters.isDexRuntime()
+                      && testParameters.getApiLevel().isGreaterThan(AndroidApiLevel.B))) {
                 checkClassIsRemoved(inspector, testedClass.getOuterClassName());
                 return;
               }
diff --git a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
index 1a8d281..2188ba6 100644
--- a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
@@ -5,17 +5,17 @@
 
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.debug.DebugTestBase;
+import com.android.tools.r8.debug.DebugTestConfig;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class LIRRoundtripTest extends TestBase {
+public class LIRRoundtripTest extends DebugTestBase {
 
   static class TestClass {
     public static void main(String[] args) {
@@ -64,9 +64,8 @@
         .assertSuccessWithOutputLines("Hello, world!");
   }
 
-  // TODO(b/225838009): Support debug local info.
-  @Test(expected = CompilationFailedException.class)
-  public void testRoundtripDebug() throws Exception {
+  @Test
+  public void testRoundtripDebug() throws Throwable {
     testForD8(parameters.getBackend())
         .debug()
         .setMinApi(AndroidApiLevel.B)
@@ -77,6 +76,17 @@
               o.testing.roundtripThroughLIR = true;
             })
         .run(parameters.getRuntime(), TestClass.class)
-        .assertSuccessWithOutputLines("Hello, world!");
+        .assertSuccessWithOutputLines("Hello, world!")
+        .debugger(this::runDebugger);
+  }
+
+  private void runDebugger(DebugTestConfig config) throws Throwable {
+    runDebugTest(
+        config,
+        TestClass.class,
+        breakOnException(typeName(TestClass.class), "main", true, true),
+        run(),
+        checkLocals("args", "message"),
+        run());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index c62aa08..d60500f 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -74,6 +74,7 @@
 import com.android.tools.r8.retrace.stacktraces.SuppressedStackTrace;
 import com.android.tools.r8.retrace.stacktraces.SyntheticLambdaMethodStackTrace;
 import com.android.tools.r8.retrace.stacktraces.SyntheticLambdaMethodWithInliningStackTrace;
+import com.android.tools.r8.retrace.stacktraces.TrailingWhitespaceStackTrace;
 import com.android.tools.r8.retrace.stacktraces.UnicodeInFileNameStackTrace;
 import com.android.tools.r8.retrace.stacktraces.UnknownSourceStackTrace;
 import com.android.tools.r8.retrace.stacktraces.VerboseUnknownStackTrace;
@@ -439,6 +440,11 @@
             DiagnosticsMatcher.diagnosticMessage(containsString("99.0")));
   }
 
+  @Test
+  public void testTrailingWhitespaceStackTrace() throws Exception {
+    runRetraceTest(new TrailingWhitespaceStackTrace());
+  }
+
   private void inspectRetraceTest(
       StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) {
     inspection.accept(
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/TrailingWhitespaceStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/TrailingWhitespaceStackTrace.java
new file mode 100644
index 0000000..bfc7476
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/TrailingWhitespaceStackTrace.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2022, 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.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class TrailingWhitespaceStackTrace implements StackTraceForTest {
+
+  private static final String NO_BREAKING_SPACE_STRING = new String(new char[] {160});
+
+  @Override
+  public List<String> obfuscatedStackTrace() {
+    return Arrays.asList(
+        "Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat a.b.main(Main.dummy:1)" + NO_BREAKING_SPACE_STRING);
+  }
+
+  @Override
+  public List<String> retracedStackTrace() {
+    return Arrays.asList(
+        "Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:7)"
+            + NO_BREAKING_SPACE_STRING);
+  }
+
+  @Override
+  public List<String> retraceVerboseStackTrace() {
+    return Arrays.asList(
+        "Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java:7)"
+            + NO_BREAKING_SPACE_STRING);
+  }
+
+  @Override
+  public String mapping() {
+    return StringUtils.lines(
+        "com.android.tools.r8.naming.retrace.Main -> a.b:",
+        "    1:1:void main(java.lang.String[]):7:7 -> main");
+  }
+
+  @Override
+  public int expectedWarnings() {
+    return 0;
+  }
+}
diff --git a/tools/startup/adb_utils.py b/tools/startup/adb_utils.py
index a376018..3900ffc 100755
--- a/tools/startup/adb_utils.py
+++ b/tools/startup/adb_utils.py
@@ -85,10 +85,23 @@
   return cmd
 
 def capture_app_profile_data(app_id, device_id=None):
-  cmd = create_adb_cmd(
-      'shell killall -s SIGUSR1 %s' % app_id, device_id)
-  subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
-  time.sleep(5)
+  ps_cmd = create_adb_cmd('shell ps -o NAME', device_id)
+  stdout = subprocess.check_output(ps_cmd).decode('utf-8').strip()
+  killed_any = False
+  for process_name in stdout.splitlines():
+    if process_name.startswith(app_id):
+      print('Flushing profile for process %s' % process_name)
+      killall_cmd = create_adb_cmd(
+          'shell killall -s SIGUSR1 %s' % process_name, device_id)
+      killall_result = subprocess.run(killall_cmd, capture_output=True)
+      stdout = killall_result.stdout.decode('utf-8')
+      stderr = killall_result.stderr.decode('utf-8')
+      if killall_result.returncode == 0:
+        killed_any = True
+      else:
+        print('Error: stdout: %s, stderr: %s' % (stdout, stderr))
+      time.sleep(5)
+  assert killed_any, 'Expected to find at least one process'
 
 def check_app_has_profile_data(app_id, device_id=None):
   profile_path = get_profile_path(app_id)