Merge commit 'f7b4811592c1cb2f8d5cfb6a27b7f7f231147dfc' into dev-release
diff --git a/build.gradle b/build.gradle
index 3c96101..d3005a8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -301,6 +301,9 @@
 def r8DesugaredPath = "$buildDir/libs/r8desugared.jar"
 def r8LibGeneratedKeepRulesPath = "$buildDir/generated/keep.txt"
 def r8LibTestPath = "$buildDir/classes/r8libtest"
+def java11ClassFiles = "$buildDir/classes/java/mainJava11"
+def r8RetracePath = "$buildDir/libs/r8retrace.jar"
+def r8RetraceExludeDepsPath = "$buildDir/libs/r8retrace-exclude-deps.jar"
 
 def osString = OperatingSystem.current().isLinux() ? "linux" :
         OperatingSystem.current().isMacOsX() ? "mac" : "windows"
@@ -1117,6 +1120,29 @@
     outputs.file r8DesugaredPath
 }
 
+task R8Retrace {
+    dependsOn R8Lib
+    dependsOn r8LibCreateTask(
+            "Retrace",
+            ["src/main/keep_retrace.txt"],
+            R8Lib,
+            r8RetracePath,
+    ).dependsOn(R8Lib)
+    outputs.file r8RetracePath
+}
+
+task R8RetraceNoDeps {
+    dependsOn R8LibNoDeps
+    dependsOn r8LibCreateTask(
+            "RetraceNoDeps",
+            ["src/main/keep_retrace.txt"],
+            R8LibNoDeps,
+            r8RetraceExludeDepsPath,
+            repackageDeps.outputs.files
+    ).dependsOn(R8LibNoDeps)
+    outputs.file r8RetraceExludeDepsPath
+}
+
 task sourceJar(type: Jar, dependsOn: classes) {
     classifier = 'src'
     from sourceSets.main.allSource
@@ -1717,7 +1743,7 @@
       maxParallelForks = processors.intdiv(3) ?: 1
       // On low cpu count machines (bots) we under subscribe, so increase the count.
       if (processors == 8) {
-        maxParallelForks = 4
+        maxParallelForks = 3
       }
     }
     println("NOTE: Max parallel forks " + maxParallelForks)
diff --git a/src/main/java/com/android/tools/r8/KeepForRetraceApi.java b/src/main/java/com/android/tools/r8/KeepForRetraceApi.java
new file mode 100644
index 0000000..5ed253c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/KeepForRetraceApi.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, 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;
+
+@KeepForRetraceApi
+public @interface KeepForRetraceApi {}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index df4cefd..9a4afa3 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -88,10 +88,12 @@
       // of the program.
       return appView.options().minApiLevel;
     }
-    return reference.apply(
-        androidApiLevelDatabase::getTypeApiLevel,
-        androidApiLevelDatabase::getFieldApiLevel,
-        androidApiLevelDatabase::getMethodApiLevel);
+    return reference
+        .apply(
+            androidApiLevelDatabase::getTypeApiLevel,
+            androidApiLevelDatabase::getFieldApiLevel,
+            androidApiLevelDatabase::getMethodApiLevel)
+        .max(appView.options().minApiLevel);
   }
 
   private boolean isReferenceToJavaLangObject(DexReference reference) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java b/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java
index 14ac116..9140e87 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java
@@ -35,4 +35,23 @@
   }
 
   public abstract ArrayAccess withMemberType(MemberType newMemberType);
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public boolean instructionInstanceCanThrow() {
+    // TODO(b/203731608): Add parameters to the method and use abstract value in R8.
+    if (index().isConstant() && !array().isPhi() && array().definition.isNewArrayEmpty()) {
+      Value newArraySizeValue = array().definition.asNewArrayEmpty().size();
+      if (newArraySizeValue.isConstant()) {
+        int newArraySize = newArraySizeValue.getConstInstruction().asConstNumber().getIntValue();
+        int index = index().getConstInstruction().asConstNumber().getIntValue();
+        return newArraySize <= 0 || index < 0 || newArraySize <= index;
+      }
+    }
+    return true;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index 845cb26..9aff428 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.conversion.TypeConstraintResolver;
@@ -135,12 +136,6 @@
   }
 
   @Override
-  public boolean instructionTypeCanThrow() {
-    // TODO: Determine if the array index out-of-bounds exception cannot happen.
-    return true;
-  }
-
-  @Override
   public boolean isArrayGet() {
     return true;
   }
@@ -272,4 +267,20 @@
   public ArrayAccess withMemberType(MemberType newMemberType) {
     return new ArrayGet(newMemberType, outValue(), array(), index());
   }
+
+  @Override
+  public boolean instructionMayHaveSideEffects(
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
+    // TODO(b/203731608): Move to instructionInstanceCanThrow and remove the method.
+    if (array().isPhi() || !index().isConstant()) {
+      return true;
+    }
+    AbstractValue abstractValue = array().getAliasedValue().getAbstractValue(appView, context);
+    if (!abstractValue.isKnownLengthArrayValue()) {
+      return true;
+    }
+    int newArraySize = abstractValue.asKnownLengthArrayValue().getLength();
+    int index = index().getConstInstruction().asConstNumber().getIntValue();
+    return newArraySize <= 0 || index < 0 || newArraySize <= index;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index 9dae2a8..4108175 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -115,24 +115,6 @@
   }
 
   @Override
-  public boolean instructionTypeCanThrow() {
-    return true;
-  }
-
-  @Override
-  public boolean instructionInstanceCanThrow() {
-    if (index().isConstant() && !array().isPhi() && array().definition.isNewArrayEmpty()) {
-      Value newArraySizeValue = array().definition.asNewArrayEmpty().size();
-      if (newArraySizeValue.isConstant()) {
-        int newArraySize = newArraySizeValue.getConstInstruction().asConstNumber().getIntValue();
-        int index = index().getConstInstruction().asConstNumber().getIntValue();
-        return newArraySize <= 0 || index < 0 || newArraySize <= index;
-      }
-    }
-    return true;
-  }
-
-  @Override
   public boolean instructionMayHaveSideEffects(
       AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     // In debug mode, ArrayPut has a side-effect on the locals.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java
index ed388b8..f64b05b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java
@@ -110,10 +110,10 @@
                 182,
                 options.itemFactory.createMethod(
                     options.itemFactory.stringType,
-                    options.itemFactory.createProto(options.itemFactory.booleanType),
-                    options.itemFactory.createString("isEmpty")),
+                    options.itemFactory.createProto(options.itemFactory.intType),
+                    options.itemFactory.createString("length")),
                 false),
-            new CfIf(If.Type.EQ, ValueType.INT, label1),
+            new CfIf(If.Type.NE, ValueType.INT, label1),
             new CfConstNumber(0, ValueType.INT),
             new CfNewArray(options.itemFactory.createType("[Ljava/lang/String;")),
             new CfGoto(label2),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 0b7b139..8007b17 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -127,8 +127,7 @@
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
     // Do not inline if the inlinee is greater than the api caller level.
     // TODO(b/188498051): We should not force inline lower api method calls.
-    if (reason != Reason.FORCE
-        && isApiSafeForInlining(method, singleTarget, appView.options()).isPossiblyFalse()) {
+    if (reason != Reason.FORCE && !isApiSafeForInlining(method, singleTarget, appView.options())) {
       whyAreYouNotInliningReporter.reportInlineeHigherApiCall();
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 5f05d8d..228c259 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -880,7 +880,7 @@
       return null;
     }
     // Check the api level is allowed to be inlined.
-    if (isApiSafeForInlining(method, singleTarget, appView.options()).isPossiblyFalse()) {
+    if (!isApiSafeForInlining(method, singleTarget, appView.options())) {
       return null;
     }
     // Check that the entire constructor chain can be inlined into the current context.
@@ -910,7 +910,7 @@
         return null;
       }
       // Check the api level is allowed to be inlined.
-      if (isApiSafeForInlining(method, encodedParent, appView.options()).isPossiblyFalse()) {
+      if (!isApiSafeForInlining(method, encodedParent, appView.options())) {
         return null;
       }
       parent =
diff --git a/src/main/java/com/android/tools/r8/references/ArrayReference.java b/src/main/java/com/android/tools/r8/references/ArrayReference.java
index 919bbd7..6699f2b 100644
--- a/src/main/java/com/android/tools/r8/references/ArrayReference.java
+++ b/src/main/java/com/android/tools/r8/references/ArrayReference.java
@@ -4,12 +4,14 @@
 package com.android.tools.r8.references;
 
 import com.android.tools.r8.Keep;
+import com.android.tools.r8.KeepForRetraceApi;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.DescriptorUtils;
 import java.util.Objects;
 
 /** Reference to an array type. */
 @Keep
+@KeepForRetraceApi
 public final class ArrayReference implements TypeReference {
 
   private final int dimensions;
diff --git a/src/main/java/com/android/tools/r8/references/ClassReference.java b/src/main/java/com/android/tools/r8/references/ClassReference.java
index 3e30319..392b4a4 100644
--- a/src/main/java/com/android/tools/r8/references/ClassReference.java
+++ b/src/main/java/com/android/tools/r8/references/ClassReference.java
@@ -4,10 +4,12 @@
 package com.android.tools.r8.references;
 
 import com.android.tools.r8.Keep;
+import com.android.tools.r8.KeepForRetraceApi;
 import com.android.tools.r8.utils.DescriptorUtils;
 
 /** Reference to a class type or interface type. */
 @Keep
+@KeepForRetraceApi
 public final class ClassReference implements TypeReference {
 
   private final String descriptor;
diff --git a/src/main/java/com/android/tools/r8/references/FieldReference.java b/src/main/java/com/android/tools/r8/references/FieldReference.java
index d4d74fd..dab6fd8 100644
--- a/src/main/java/com/android/tools/r8/references/FieldReference.java
+++ b/src/main/java/com/android/tools/r8/references/FieldReference.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.references;
 
 import com.android.tools.r8.Keep;
+import com.android.tools.r8.KeepForRetraceApi;
 import java.util.Objects;
 
 /**
@@ -13,6 +14,7 @@
  * type of the field.
  */
 @Keep
+@KeepForRetraceApi
 public final class FieldReference {
   private final ClassReference holderClass;
   private final String fieldName;
diff --git a/src/main/java/com/android/tools/r8/references/MethodReference.java b/src/main/java/com/android/tools/r8/references/MethodReference.java
index fc8f183..e0314b8 100644
--- a/src/main/java/com/android/tools/r8/references/MethodReference.java
+++ b/src/main/java/com/android/tools/r8/references/MethodReference.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.references;
 
 import com.android.tools.r8.Keep;
+import com.android.tools.r8.KeepForRetraceApi;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
@@ -17,6 +18,7 @@
  * full list of formal parameters.
  */
 @Keep
+@KeepForRetraceApi
 public final class MethodReference {
   private final ClassReference holderClass;
   private final String methodName;
diff --git a/src/main/java/com/android/tools/r8/references/PackageReference.java b/src/main/java/com/android/tools/r8/references/PackageReference.java
index b2474fe..b959738 100644
--- a/src/main/java/com/android/tools/r8/references/PackageReference.java
+++ b/src/main/java/com/android/tools/r8/references/PackageReference.java
@@ -5,11 +5,13 @@
 package com.android.tools.r8.references;
 
 import com.android.tools.r8.Keep;
+import com.android.tools.r8.KeepForRetraceApi;
 import com.android.tools.r8.utils.DescriptorUtils;
 import java.util.Objects;
 
 /** Reference to a package. */
 @Keep
+@KeepForRetraceApi
 public class PackageReference {
 
   private final String packageName;
diff --git a/src/main/java/com/android/tools/r8/references/PrimitiveReference.java b/src/main/java/com/android/tools/r8/references/PrimitiveReference.java
index eb4ae72..f7fe50a 100644
--- a/src/main/java/com/android/tools/r8/references/PrimitiveReference.java
+++ b/src/main/java/com/android/tools/r8/references/PrimitiveReference.java
@@ -4,9 +4,11 @@
 package com.android.tools.r8.references;
 
 import com.android.tools.r8.Keep;
+import com.android.tools.r8.KeepForRetraceApi;
 import com.android.tools.r8.errors.Unreachable;
 
 @Keep
+@KeepForRetraceApi
 public abstract class PrimitiveReference implements TypeReference {
 
   static final PrimitiveReference BOOL =
diff --git a/src/main/java/com/android/tools/r8/references/Reference.java b/src/main/java/com/android/tools/r8/references/Reference.java
index e6a829b..75dfb4c 100644
--- a/src/main/java/com/android/tools/r8/references/Reference.java
+++ b/src/main/java/com/android/tools/r8/references/Reference.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.references;
 
 import com.android.tools.r8.Keep;
+import com.android.tools.r8.KeepForRetraceApi;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
 import java.lang.reflect.Constructor;
@@ -22,6 +23,7 @@
  * <p>No guarantees are made on identity and all references must be compared by {@code equals}.
  */
 @Keep
+@KeepForRetraceApi
 public final class Reference {
 
   public static PrimitiveReference BOOL = PrimitiveReference.BOOL;
diff --git a/src/main/java/com/android/tools/r8/references/TypeReference.java b/src/main/java/com/android/tools/r8/references/TypeReference.java
index bfef3f6..36099b9 100644
--- a/src/main/java/com/android/tools/r8/references/TypeReference.java
+++ b/src/main/java/com/android/tools/r8/references/TypeReference.java
@@ -4,9 +4,11 @@
 package com.android.tools.r8.references;
 
 import com.android.tools.r8.Keep;
+import com.android.tools.r8.KeepForRetraceApi;
 import com.android.tools.r8.utils.DescriptorUtils;
 
 @Keep
+@KeepForRetraceApi
 public interface TypeReference {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
index 96c4a2b..73ee4de 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -8,18 +8,17 @@
 
 public class AndroidApiLevelUtils {
 
-  public static OptionalBool isApiSafeForInlining(
+  public static boolean isApiSafeForInlining(
       ProgramMethod caller, ProgramMethod inlinee, InternalOptions options) {
     if (!options.apiModelingOptions().enableApiCallerIdentification) {
-      return OptionalBool.TRUE;
+      return true;
     }
     if (caller.getHolderType() == inlinee.getHolderType()) {
-      return OptionalBool.TRUE;
+      return true;
     }
-    return OptionalBool.of(
-        caller
-            .getDefinition()
-            .getApiLevel()
-            .isGreaterThanOrEqualTo(inlinee.getDefinition().getApiLevelForCode()));
+    return caller
+        .getDefinition()
+        .getApiLevel()
+        .isGreaterThanOrEqualTo(inlinee.getDefinition().getApiLevelForCode());
   }
 }
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 0fdc11c..88c96ef 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1473,7 +1473,7 @@
     public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
     public BiConsumer<MethodReference, AndroidApiLevel> tracedMethodApiLevelCallback = null;
 
-    public boolean enableApiCallerIdentification = false;
+    public boolean enableApiCallerIdentification = true;
     public boolean checkAllApiReferencesAreSet = true;
     public boolean useHashingDatabase = true;
 
diff --git a/src/main/keep.txt b/src/main/keep.txt
index e03ef61..e376924 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -2,9 +2,13 @@
 # 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.
 
+# TODO(b/204058761): Remove when we can use test proguard options.
 -keep @com.android.tools.r8.Keep class * { public *; }
 -keep @com.android.tools.r8.KeepForSubclassing class * { public *; protected *; }
 
+# Keep all things that can be reached from the retrace api and keep the annotation
+-keep @com.android.tools.r8.KeepForRetraceApi class * { public *; }
+
 -keep public class com.android.tools.r8.D8 { public static void main(java.lang.String[]); }
 -keep public class com.android.tools.r8.R8 { public static void main(java.lang.String[]); }
 -keep public class com.android.tools.r8.ExtractMarker { public static void main(java.lang.String[]); }
@@ -18,7 +22,7 @@
 -keep public class com.android.tools.r8.Version { public static java.lang.String getPreReleaseString(); }
 -keep public class com.android.tools.r8.Version { public static boolean isDevelopmentVersion(); }
 
--keepattributes SourceFile, LineNumberTable, InnerClasses, EnclosingMethod, Exceptions, Signature
+-keepattributes SourceFile, LineNumberTable, InnerClasses, EnclosingMethod, Exceptions, Signature, RuntimeInvisibleAnnotations
 
 -keepparameternames
 -keeppackagenames com.android.tools.r8
diff --git a/src/main/keep_retrace.txt b/src/main/keep_retrace.txt
new file mode 100644
index 0000000..e8d050a
--- /dev/null
+++ b/src/main/keep_retrace.txt
@@ -0,0 +1,19 @@
+# Copyright (c) 2021, 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.
+
+# The retrace api is separated out without repackaging which is why this broad
+# rule is used.
+-keep public class com.android.tools.r8.retrace.* {
+     public <methods>;
+     public <fields>;
+ }
+-keepattributes SourceFile, LineNumberTable, InnerClasses, EnclosingMethod, Exceptions, Signature
+-keepparameternames
+
+# This is run on r8lib so keep everything in lib that is traced. That way
+# we only need a single mapping file
+-keep,allowshrinking class * { *; }
+
+# Keep all things that can be reached from the retrace api
+-keep @com.android.tools.r8.KeepForRetraceApi class * { public *; }
diff --git a/src/test/java/com/android/tools/r8/ProguardVersion.java b/src/test/java/com/android/tools/r8/ProguardVersion.java
index 20560fc..3be26d9 100644
--- a/src/test/java/com/android/tools/r8/ProguardVersion.java
+++ b/src/test/java/com/android/tools/r8/ProguardVersion.java
@@ -25,16 +25,25 @@
   }
 
   public Path getProguardScript() {
+    return isWindows()
+        ? getScriptDirectory().resolve("proguard.bat")
+        : getScriptDirectory().resolve("proguard.sh");
+  }
+
+  public Path getRetraceScript() {
+    return isWindows()
+        ? getScriptDirectory().resolve("retrace.bat")
+        : getScriptDirectory().resolve("retrace.sh");
+  }
+
+  private Path getScriptDirectory() {
     Path scriptDirectory = Paths.get(ToolHelper.THIRD_PARTY_DIR).resolve("proguard");
     if (this == V7_0_0) {
-      scriptDirectory = scriptDirectory.resolve("proguard-" + version);
+      scriptDirectory = scriptDirectory.resolve("proguard-" + version).resolve("bin");
     } else {
-      scriptDirectory = scriptDirectory.resolve("proguard" + version);
+      scriptDirectory = scriptDirectory.resolve("proguard" + version).resolve("bin");
     }
-    if (isWindows()) {
-      return scriptDirectory.resolve("bin/proguard.bat");
-    }
-    return scriptDirectory.resolve("bin/proguard.sh");
+    return scriptDirectory;
   }
 
   public String getVersion() {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 8f3b1e4..033c3f4 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -155,8 +155,6 @@
   public static final Path JACOCO_CLI = JACOCO_ROOT.resolve(Paths.get("lib", "jacococli.jar"));
   public static final String PROGUARD_SETTINGS_FOR_INTERNAL_APPS = "third_party/proguardsettings/";
 
-  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 RETRACE_MAPS_DIR = Paths.get(THIRD_PARTY_DIR, "r8mappings");
 
   public static final long BOT_MAX_HEAP_SIZE = 7908360192L;
@@ -177,6 +175,7 @@
   public static final Path R8LIB_EXCLUDE_DEPS_MAP =
       Paths.get(LIBS_DIR, "r8lib-exclude-deps.jar.map");
   public static final Path DEPS = Paths.get(LIBS_DIR, "deps_all.jar");
+  public static final Path R8_RETRACE_JAR = Paths.get(LIBS_DIR, "r8retrace.jar");
 
   public static final Path DESUGAR_LIB_CONVERSIONS =
       Paths.get(LIBS_DIR, "library_desugar_conversions.zip");
@@ -777,13 +776,6 @@
     return new Backend[]{Backend.DEX};
   }
 
-  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";
@@ -2021,17 +2013,19 @@
     return runProguard(getProguard6Script(), inJar, outJar, configs, map);
   }
 
-  public static ProcessResult runRetraceRaw(Path map, Path stackTrace) throws IOException {
+  public static ProcessResult runRetraceRaw(Path retracePath, Path map, Path stackTrace)
+      throws IOException {
     List<String> command = new ArrayList<>();
-    command.add(getRetraceScript());
+    command.add(retracePath.toString());
     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);
+  public static String runRetrace(ProguardVersion pgRetracer, Path map, Path stackTrace)
+      throws IOException {
+    ProcessResult result = runRetraceRaw(pgRetracer.getRetraceScript(), map, stackTrace);
     if (result.exitCode != 0) {
       fail("Retrace failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
     }
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 46ecc50..699bb50 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.classmerging.vertical;
 
 import static com.android.tools.r8.smali.SmaliBuilder.buildCode;
+import static com.android.tools.r8.utils.AndroidApiLevel.K;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -315,10 +316,13 @@
           CF_DIR.resolve("FieldCollisionTest$A.class"),
           CF_DIR.resolve("FieldCollisionTest$B.class")
         };
-    Set<String> preservedClassNames =
-        ImmutableSet.of(
-            "classmerging.FieldCollisionTest",
-            "classmerging.FieldCollisionTest$B");
+    ImmutableSet.Builder<String> preservedNamesBuilder = ImmutableSet.builder();
+    preservedNamesBuilder.add("classmerging.FieldCollisionTest");
+    preservedNamesBuilder.add("classmerging.FieldCollisionTest$B");
+    if (parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(K)) {
+      preservedNamesBuilder.add("classmerging.FieldCollisionTest$A");
+    }
+    Set<String> preservedClassNames = preservedNamesBuilder.build();
     runTest(
         testForR8(parameters.getBackend())
             .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
index 00575cd..bc389c9 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
@@ -12,10 +12,12 @@
 import static junit.framework.TestCase.assertTrue;
 
 import com.android.tools.r8.Jdk9TestUtils;
+import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ThrowableConsumer;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.NestMemberClassAttribute;
 import com.android.tools.r8.utils.StringUtils;
@@ -51,33 +53,72 @@
 
   @Test
   public void testClassMergingNestMemberRemoval() throws Exception {
-    testNestAttributesCorrect(MERGING_OUTER_CLASS, MERGING_OUTER_CLASS, MERGING_EXPECTED_RESULT, 3);
+    testNestAttributesCorrect(
+        MERGING_OUTER_CLASS,
+        MERGING_OUTER_CLASS,
+        MERGING_EXPECTED_RESULT,
+        3,
+        ThrowableConsumer.empty());
   }
 
   @Test
   public void testClassMergingNestHostRemoval() throws Exception {
     testNestAttributesCorrect(
-        MERGING_OUTER_CLASS + "$MiddleOuter", MERGING_OUTER_CLASS, MERGING_EXPECTED_RESULT, 2);
+        MERGING_OUTER_CLASS + "$MiddleOuter",
+        MERGING_OUTER_CLASS,
+        MERGING_EXPECTED_RESULT,
+        2,
+        builder -> {
+          builder.addOptionsModification(
+              internalOptions -> {
+                // The test makes an invoke to StringConcatFactory which is not known to DEX and
+                // we therefore fail to merge the classes.
+                internalOptions.apiModelingOptions().enableApiCallerIdentification = false;
+              });
+        });
   }
 
   @Test
   public void testTreePruningNestMemberRemoval() throws Exception {
-    testNestAttributesCorrect(PRUNING_OUTER_CLASS, PRUNING_OUTER_CLASS, PRUNING_EXPECTED_RESULT, 2);
+    testNestAttributesCorrect(
+        PRUNING_OUTER_CLASS,
+        PRUNING_OUTER_CLASS,
+        PRUNING_EXPECTED_RESULT,
+        2,
+        ThrowableConsumer.empty());
   }
 
   @Test
   public void testTreePruningNestHostRemoval() throws Exception {
     testNestAttributesCorrect(
-        PRUNING_OUTER_CLASS + "$Pruned", PRUNING_OUTER_CLASS, PRUNING_EXPECTED_RESULT, 1);
+        PRUNING_OUTER_CLASS + "$Pruned",
+        PRUNING_OUTER_CLASS,
+        PRUNING_EXPECTED_RESULT,
+        1,
+        ThrowableConsumer.empty());
   }
 
   public void testNestAttributesCorrect(
-      String mainClassName, String outerNestName, String expectedResult, int expectedNumClassesLeft)
+      String mainClassName,
+      String outerNestName,
+      String expectedResult,
+      int expectedNumClassesLeft,
+      ThrowableConsumer<R8FullTestBuilder> testBuilderConsumer)
       throws Exception {
     testNestAttributesCorrect(
-        mainClassName, outerNestName, expectedResult, true, expectedNumClassesLeft);
+        mainClassName,
+        outerNestName,
+        expectedResult,
+        true,
+        expectedNumClassesLeft,
+        testBuilderConsumer);
     testNestAttributesCorrect(
-        mainClassName, outerNestName, expectedResult, false, expectedNumClassesLeft);
+        mainClassName,
+        outerNestName,
+        expectedResult,
+        false,
+        expectedNumClassesLeft,
+        testBuilderConsumer);
   }
 
   public void testNestAttributesCorrect(
@@ -85,7 +126,8 @@
       String outerNestName,
       String expectedResult,
       boolean minification,
-      int expectedNumClassesLeft)
+      int expectedNumClassesLeft,
+      ThrowableConsumer<R8FullTestBuilder> testBuilderConsumer)
       throws Exception {
     String actualMainClassName = PACKAGE_NAME + mainClassName;
     testForR8(parameters.getBackend())
@@ -101,6 +143,7 @@
         .applyIf(parameters.isCfRuntime(), Jdk9TestUtils.addJdk9LibraryFiles(temp))
         .addKeepPackageNamesRule("nesthostexample")
         .addInliningAnnotations()
+        .apply(testBuilderConsumer)
         .compile()
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordMethods.java b/src/test/java/com/android/tools/r8/desugar/records/RecordMethods.java
index a3df3b5..f00657c 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordMethods.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordMethods.java
@@ -13,7 +13,7 @@
   public static String toString(
       Object[] recordFieldsValues, Class<?> recordClass, String fieldNames) {
     // Example: "Person[name=Jane Doe, age=42]"
-    String[] fieldNamesSplit = fieldNames.isEmpty() ? new String[0] : fieldNames.split(";");
+    String[] fieldNamesSplit = fieldNames.length() == 0 ? new String[0] : fieldNames.split(";");
     StringBuilder builder = new StringBuilder();
     builder.append(recordClass.getSimpleName()).append("[");
     for (int i = 0; i < fieldNamesSplit.length; i++) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java
index 693b9bf..26bbf63 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.NeverPropagateValue;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -56,9 +57,9 @@
 
   private void verifyClassInliningRemovesCandidate(CodeInspector inspector) {
     ClassSubject candidateClassSubject = inspector.clazz(Candidate.class);
-    if (enableClassInlining) {
-      // TODO(b/188388130): String.empty was added in AndroidApiLevel.G, so if this fails after
-      //  enabling api modeling then check for api level.
+    if (enableClassInlining
+        && (parameters.isDexRuntime()
+            && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.G))) {
       assertThat(candidateClassSubject, not(isPresent()));
     } else {
       assertThat(candidateClassSubject, isPresent());
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/CatchAllRangeWithNoLineNumberTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/CatchAllRangeWithNoLineNumberTest.java
new file mode 100644
index 0000000..34da9a6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/CatchAllRangeWithNoLineNumberTest.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2021, 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.retraceproguard;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetraceOptions;
+import com.android.tools.r8.retrace.StringRetrace;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+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 CatchAllRangeWithNoLineNumberTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final ProguardVersion proguardVersion;
+
+  @Parameters(name = "{0}, {1}")
+  public static List<Object[]> data() {
+    return buildParameters(getTestParameters().withNoneRuntime().build(), ProguardVersion.values());
+  }
+
+  public CatchAllRangeWithNoLineNumberTest(
+      TestParameters parameters, ProguardVersion proguardVersion) {
+    this.parameters = parameters;
+    this.proguardVersion = proguardVersion;
+  }
+
+  private final String[] stackTrace =
+      new String[] {
+        "\tat a.a(SourceFile)",
+        "\tat b.a(SourceFile)",
+        "\tat c.a(SourceFile)",
+        "\tat a.a(SourceFile:1)",
+        "\tat a.a(SourceFile:0)"
+      };
+
+  private final String mapping =
+      StringUtils.joinLines(
+          "foo.bar.Baz -> a:",
+          "  0:65535:void foo():33:33 -> a",
+          "foo.bar.Qux -> b:",
+          "  1:65535:void foo():33:33 -> a",
+          "foo.bar.Quux -> c:",
+          "  void foo():33:33 -> a");
+
+  private final String retraced5_2_1 =
+      StringUtils.lines(
+          "foo.bar.Baz.a(Baz.java)",
+          "foo.bar.Qux.a(Qux.java)",
+          "foo.bar.Quux.a(Quux.java)",
+          "foo.bar.Baz.foo(Baz.java:33)",
+          "foo.bar.Baz.a(Baz.java:0)");
+
+  private final String retraced6_0_1 =
+      StringUtils.lines(
+          "foo.bar.Baz.a(Baz.java)",
+          "foo.bar.Qux.a(Qux.java)",
+          "foo.bar.Quux.a(Quux.java)",
+          "foo.bar.Baz.foo(Baz.java:33)",
+          "foo.bar.Baz.a(Baz.java:0)");
+
+  private final String retraced7_0_0 =
+      StringUtils.lines(
+          "foo.bar.Baz.foo(Baz.java)",
+          "foo.bar.Qux.foo(Qux.java)",
+          "foo.bar.Quux.a(Quux.java)",
+          "foo.bar.Baz.foo(Baz.java:33)",
+          "foo.bar.Baz.foo(Baz.java:33)");
+
+  private final String retracedR8 =
+      StringUtils.lines(
+          "\tat foo.bar.Baz.foo(Baz.java:33)",
+          "\tat foo.bar.Qux.foo(Qux.java:33)",
+          "\tat foo.bar.Quux.foo(Quux.java:33)",
+          "\tat foo.bar.Baz.foo(Baz.java:33)",
+          "\tat foo.bar.Baz.foo(Baz.java:33)");
+
+  @Test
+  public void testCatchAllRange() throws IOException {
+    StackTrace actualStackTrace = StackTrace.extractFromJvm(StringUtils.lines(stackTrace));
+    StackTrace retraced =
+        actualStackTrace.retrace(proguardVersion, mapping, temp.newFolder().toPath());
+    assertEquals(getExpected(), retraced.toString());
+  }
+
+  @Test
+  public void testCatchAllRangeR8() {
+    List<String> retrace =
+        StringRetrace.create(
+                RetraceOptions.builder()
+                    .setProguardMapProducer(ProguardMapProducer.fromString(mapping))
+                    .build())
+            .retrace(Arrays.asList(stackTrace));
+    assertEquals(retracedR8, StringUtils.lines(retrace));
+  }
+
+  private String getExpected() {
+    switch (proguardVersion) {
+      case V5_2_1:
+        return retraced5_2_1;
+      case V6_0_1:
+        return retraced6_0_1;
+      default:
+        assertEquals(ProguardVersion.V7_0_0, proguardVersion);
+        return retraced7_0_0;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/StackTrace.java
index 92b78b6..8869cbc 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/StackTrace.java
@@ -8,6 +8,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.ProguardVersion;
 import com.android.tools.r8.SingleTestRunResult;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm;
@@ -219,6 +220,11 @@
   }
 
   public StackTrace retrace(String map, Path tempFolder) throws IOException {
+    return retrace(ProguardVersion.getLatest(), map, tempFolder);
+  }
+
+  public StackTrace retrace(ProguardVersion proguardVersion, String map, Path tempFolder)
+      throws IOException {
     Path mapFile = tempFolder.resolve("map");
     Path stackTraceFile = tempFolder.resolve("stackTrace");
     FileUtils.writeTextFile(mapFile, map);
@@ -227,7 +233,8 @@
         stackTraceLines.stream().map(line -> line.originalLine).collect(Collectors.toList()));
     // Keep the original stderr in the retraced stacktrace.
     return new StackTrace(
-        internalExtractFromJvm(ToolHelper.runRetrace(mapFile, stackTraceFile)), originalStderr);
+        internalExtractFromJvm(ToolHelper.runRetrace(proguardVersion, mapFile, stackTraceFile)),
+        originalStderr);
   }
 
   public StackTrace filter(Predicate<StackTraceLine> filter) {
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
index 3ad54fb..5905930 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
@@ -75,15 +75,8 @@
     return height;
   }
 
-  private boolean filterSynthesizedMethodWhenLineNumberAvailable(
-      StackTraceLine retracedStackTraceLine) {
-    return retracedStackTraceLine.lineNumber > 0;
-  }
-
   private boolean filterSynthesizedMethod(StackTraceLine retracedStackTraceLine) {
-    return haveSeenLines.add(retracedStackTraceLine)
-        && (retracedStackTraceLine.className.contains("ResourceWrapper")
-            || retracedStackTraceLine.className.contains("MainApp"));
+    return retracedStackTraceLine.lineNumber > 0;
   }
 
   @Test
@@ -95,7 +88,7 @@
           StackTrace reprocessedStackTrace =
               mode == CompilationMode.DEBUG
                   ? retracedStackTrace
-                  : retracedStackTrace.filter(this::filterSynthesizedMethodWhenLineNumberAvailable);
+                  : retracedStackTrace.filter(this::filterSynthesizedMethod);
           assertThat(
               reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod),
               isSameExceptForFileName(
@@ -114,7 +107,7 @@
           StackTrace reprocessedStackTrace =
               mode == CompilationMode.DEBUG
                   ? retracedStackTrace
-                  : retracedStackTrace.filter(this::filterSynthesizedMethodWhenLineNumberAvailable);
+                  : retracedStackTrace.filter(this::filterSynthesizedMethod);
           assertThat(
               reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod),
               isSameExceptForFileName(
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
index 3fb600a..d348b54 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
@@ -303,7 +303,7 @@
       command.add(ToolHelper.getSystemJavaExecutable());
       command.add("-ea");
       command.add("-cp");
-      command.add(ToolHelper.R8LIB_JAR.toString());
+      command.add(ToolHelper.R8_RETRACE_JAR.toString());
       command.add("com.android.tools.r8.retrace.Retrace");
       command.addAll(args);
       ProcessBuilder builder = new ProcessBuilder(command);
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 17ea86d..16aa702 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -408,7 +408,7 @@
       command.add(testParameters.getRuntime().asCf().getJavaExecutable().toString());
       command.add("-ea");
       command.add("-cp");
-      command.add(ToolHelper.R8LIB_JAR.toString());
+      command.add(ToolHelper.R8_RETRACE_JAR.toString());
       if (allowExperimentalMapping) {
         command.add("-Dcom.android.tools.r8.experimentalmapping");
       }
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
index 4deb785..fb48ff4 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
@@ -56,7 +56,7 @@
 
   @Override
   public Path getTargetJar() {
-    return ToolHelper.isTestingR8Lib() ? ToolHelper.R8LIB_JAR : ToolHelper.R8_JAR;
+    return ToolHelper.isTestingR8Lib() ? ToolHelper.R8_RETRACE_JAR : ToolHelper.R8_JAR;
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
index b8de479..9afdd92 100644
--- a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
@@ -301,6 +301,35 @@
             });
   }
 
+  @Test
+  public void testPositiveWithNegatedR8Compat() throws Exception {
+    testPositiveWithNegated(testForR8Compat(parameters.getBackend()));
+  }
+
+  @Test
+  public void testPositiveWithNegatedR8Full() throws Exception {
+    testPositiveWithNegated(testForR8(parameters.getBackend()));
+  }
+
+  @Test
+  public void testPositiveWithNegatedProguard() throws Exception {
+    testPositiveWithNegated(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"));
+  }
+
+  public void testPositiveWithNegated(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder)
+      throws Exception {
+    run(testBuilder.addKeepRules("-keep class **Bar,!**$Foo* { *; }"))
+        .inspect(
+            inspector -> {
+              assertThat(inspector.clazz(BarBar.class), isPresent());
+              assertThat(inspector.clazz(FooBar.class), isPresent());
+              assertThat(inspector.clazz(A.class), isPresent());
+              assertThat(inspector.clazz(B.class), isPresent());
+              assertThat(inspector.clazz(C.class), isPresent());
+              assertThat(inspector.clazz(D.class), isPresent());
+            });
+  }
+
   private TestCompileResult<?, ?> run(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder)
       throws Exception {
     return testBuilder
diff --git a/tools/archive.py b/tools/archive.py
index 5207e5c..24e8e0a 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -145,6 +145,8 @@
     utils.D8,
     utils.R8LIB,
     utils.R8LIB_NO_DEPS,
+    utils.R8RETRACE,
+    utils.R8RETRACE_NO_DEPS,
     utils.LIBRARY_DESUGAR_CONVERSIONS,
     '-Pno_internal'
   ])
@@ -191,6 +193,8 @@
       utils.R8_FULL_EXCLUDE_DEPS_JAR,
       utils.R8LIB_EXCLUDE_DEPS_JAR,
       utils.R8LIB_EXCLUDE_DEPS_JAR + '.map',
+      utils.R8RETRACE_JAR,
+      utils.R8RETRACE_EXCLUDE_DEPS_JAR,
       utils.MAVEN_ZIP,
       utils.MAVEN_ZIP_LIB,
       utils.DESUGAR_CONFIGURATION,
diff --git a/tools/chrome_data.py b/tools/chrome_data.py
index 0d4329d..ebae542 100644
--- a/tools/chrome_data.py
+++ b/tools/chrome_data.py
@@ -248,6 +248,7 @@
         'inputs': [os.path.join(V180917_BASE, path) for path in INPUT_JARS],
         'pgconf': [os.path.join(V180917_BASE, path) for path in PG_CONFS],
         'libraries': [os.path.join(V180917_BASE, path) for path in LIBRARIES],
+        'min-api': '21',
     },
   },
   '200430': {
diff --git a/tools/r8_release.py b/tools/r8_release.py
index a0ed999..f9d14f9 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -363,6 +363,7 @@
       g4_open('src.jar')
       g4_open('lib.jar')
       g4_open('lib.jar.map')
+      g4_open('retrace_lib.jar')
       g4_open('desugar_jdk_libs_configuration.jar')
       download_file(options.version, 'r8-full-exclude-deps.jar', 'full.jar')
       download_file(options.version, 'r8-src.jar', 'src.jar')
@@ -371,6 +372,7 @@
           options.version, 'r8lib-exclude-deps.jar.map', 'lib.jar.map')
       download_file(options.version, 'desugar_jdk_libs_configuration.jar',
                     'desugar_jdk_libs_configuration.jar')
+      download_file(options.version, 'r8retrace-exclude-deps.jar', 'retrace_lib.jar')
       g4_open('METADATA')
       sed(r'[1-9]\.[0-9]{1,2}\.[0-9]{1,3}-dev',
           options.version,
diff --git a/tools/test.py b/tools/test.py
index 5da5b71..4a5d79c 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -321,6 +321,7 @@
     # Force gradle to build a version of r8lib without dependencies for
     # BootstrapCurrentEqualityTest.
     gradle_args.append('R8LibNoDeps')
+    gradle_args.append('R8Retrace')
   if options.r8lib_no_deps:
     gradle_args.append('-Pr8lib_no_deps')
   if options.worktree:
diff --git a/tools/utils.py b/tools/utils.py
index 40c5c7a..5d574ac 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -41,6 +41,8 @@
 R8 = 'r8'
 R8LIB = 'r8lib'
 R8LIB_NO_DEPS = 'r8LibNoDeps'
+R8RETRACE = 'R8Retrace'
+R8RETRACE_NO_DEPS = 'R8RetraceNoDeps'
 R8_SRC = 'sourceJar'
 LIBRARY_DESUGAR_CONVERSIONS = 'buildLibraryDesugarConversions'
 
@@ -52,6 +54,8 @@
 R8_SRC_JAR = os.path.join(LIBS, 'r8-src.jar')
 R8LIB_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8lib-exclude-deps.jar')
 R8_FULL_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8-full-exclude-deps.jar')
+R8RETRACE_JAR = os.path.join(LIBS, 'r8retrace.jar')
+R8RETRACE_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8retrace-exclude-deps.jar')
 MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
 MAVEN_ZIP_LIB = os.path.join(LIBS, 'r8lib.zip')
 LIBRARY_DESUGAR_CONVERSIONS_ZIP = os.path.join(LIBS, 'library_desugar_conversions.zip')