Merge commit '199b06b7dde9f75e04515100cff84ff669b5e542' into dev-release
diff --git a/.gitignore b/.gitignore
index d1bacd1..052f0a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,6 +114,8 @@
 third_party/kotlin/kotlin-compiler-1.4.20
 third_party/kotlin/kotlin-compiler-1.5.0.tar.gz
 third_party/kotlin/kotlin-compiler-1.5.0
+third_party/kotlin/kotlin-compiler-1.6.0.tar.gz
+third_party/kotlin/kotlin-compiler-1.6.0
 third_party/kotlin/kotlin-compiler-dev.tar.gz
 third_party/kotlin/kotlin-compiler-dev
 third_party/kotlinx-coroutines-1.3.6.tar.gz
diff --git a/build.gradle b/build.gradle
index 007fe58..921a270 100644
--- a/build.gradle
+++ b/build.gradle
@@ -351,6 +351,7 @@
                 "kotlin/kotlin-compiler-1.3.72",
                 "kotlin/kotlin-compiler-1.4.20",
                 "kotlin/kotlin-compiler-1.5.0",
+                "kotlin/kotlin-compiler-1.6.0",
                 "kotlinx-coroutines-1.3.6",
                 "openjdk/openjdk-rt-1.8",
                 "openjdk/desugar_jdk_libs",
@@ -989,7 +990,7 @@
     return baseD8CommandLine(allArgs)
 }
 
-def r8LibCreateTask(name, pgConfs = [], r8Task, output, libs = []) {
+def r8LibCreateTask(name, pgConfs = [], r8Task, output, libs = [], classpath = []) {
     return tasks.create("r8Lib${name}", Exec) {
         inputs.files ([pgConfs, r8WithRelocatedDeps.outputs, r8Task.outputs])
         outputs.file output
@@ -1001,7 +1002,8 @@
                 "--r8jar", r8Task.outputs.files[0],
                 "--output", output]
                 + (pgConfs.collectMany { ["--pg-conf", it] })
-                + (libs.collectMany { ["--lib", it] }))
+                + (libs.collectMany { ["--lib", it] })
+                + (classpath.collectMany { ["--classpath", it] }))
         workingDir = projectDir
     }
 }
@@ -1100,6 +1102,7 @@
             ["src/main/keep.txt"],
             r8NoManifestWithoutDeps,
             r8LibExludeDepsPath,
+            [],
             repackageDeps.outputs.files
     ).dependsOn(repackageDeps)
     inputs.files ([r8NoManifestWithoutDeps.outputs, repackageDeps.outputs])
@@ -1136,6 +1139,7 @@
             ["src/main/keep_retrace.txt"],
             R8LibNoDeps,
             r8RetraceExludeDepsPath,
+            [],
             repackageDeps.outputs.files
     ).dependsOn(R8LibNoDeps)
     outputs.file r8RetraceExludeDepsPath
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index f4975b7..4623314 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -1095,6 +1095,35 @@
       }
     }
     builders {
+      name: "linux-kotlin_old"
+      swarming_host: "chrome-swarming.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.r8.ci"
+      recipe {
+        name: "rex"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+        cipd_version: "refs/heads/master"
+        properties_j: "builder_group:\"internal.client.r8\""
+        properties_j: "test_options:[\"--runtimes=dex-default:jdk11\",\"--kotlin-compiler-old\",\"--one_line_per_test\",\"--archive_failures\",\"--no-internal\",\"*kotlin*\"]"
+      }
+      priority: 26
+      execution_timeout_secs: 43200
+      expiration_secs: 126000
+      build_numbers: YES
+      service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+    }
+    builders {
       name: "linux-none"
       swarming_host: "chrome-swarming.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/global/generated/luci-milo.cfg b/infra/config/global/generated/luci-milo.cfg
index a48c9aa..6151ce6 100644
--- a/infra/config/global/generated/luci-milo.cfg
+++ b/infra/config/global/generated/luci-milo.cfg
@@ -106,6 +106,11 @@
     short_name: "kotlin_dev"
   }
   builders {
+    name: "buildbucket/luci.r8.ci/linux-kotlin_old"
+    category: "R8"
+    short_name: "kotlin_old"
+  }
+  builders {
     name: "buildbucket/luci.r8.ci/linux-d8_jctf"
     category: "jctf"
     short_name: "d8_jctf"
diff --git a/infra/config/global/generated/luci-notify.cfg b/infra/config/global/generated/luci-notify.cfg
index c6af3c9..e697c32 100644
--- a/infra/config/global/generated/luci-notify.cfg
+++ b/infra/config/global/generated/luci-notify.cfg
@@ -432,6 +432,18 @@
   }
   builders {
     bucket: "ci"
+    name: "linux-kotlin_old"
+    repository: "https://r8.googlesource.com/r8"
+  }
+}
+notifiers {
+  notifications {
+    on_failure: true
+    on_new_failure: true
+    notify_blamelist {}
+  }
+  builders {
+    bucket: "ci"
     name: "linux-none"
     repository: "https://r8.googlesource.com/r8"
   }
diff --git a/infra/config/global/generated/luci-scheduler.cfg b/infra/config/global/generated/luci-scheduler.cfg
index e9b307f..bf973f3 100644
--- a/infra/config/global/generated/luci-scheduler.cfg
+++ b/infra/config/global/generated/luci-scheduler.cfg
@@ -512,6 +512,20 @@
   }
 }
 job {
+  id: "linux-kotlin_old"
+  realm: "ci"
+  acl_sets: "ci"
+  triggering_policy {
+    kind: GREEDY_BATCHING
+    max_concurrent_invocations: 4
+  }
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.r8.ci"
+    builder: "linux-kotlin_old"
+  }
+}
+job {
   id: "linux-none"
   realm: "ci"
   acl_sets: "ci"
@@ -676,6 +690,7 @@
   triggers: "linux-jdk8"
   triggers: "linux-jdk9"
   triggers: "linux-kotlin_dev"
+  triggers: "linux-kotlin_old"
   triggers: "linux-none"
   triggers: "linux-r8cf_jctf"
   triggers: "linux-run-on-app-dump"
diff --git a/infra/config/global/generated/project.cfg b/infra/config/global/generated/project.cfg
index 1659d63..525fcd4 100644
--- a/infra/config/global/generated/project.cfg
+++ b/infra/config/global/generated/project.cfg
@@ -7,7 +7,7 @@
 name: "r8"
 access: "group:all"
 lucicfg {
-  version: "1.29.1"
+  version: "1.30.4"
   package_dir: ".."
   config_dir: "generated"
   entry_point: "main.star"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 238f87c..3aa3fe2 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -336,6 +336,17 @@
     }
 )
 
+r8_builder(
+    "linux-kotlin_old",
+    dimensions = get_dimensions(),
+    execution_timeout = time.hour * 12,
+    expiration_timeout = time.hour * 35,
+    properties = {
+      "builder_group" : "internal.client.r8",
+      "test_options" : ["--runtimes=dex-default:jdk11", "--kotlin-compiler-old", "--one_line_per_test", "--archive_failures", "--no-internal", "*kotlin*"]
+    }
+)
+
 def jctf():
   for release in ["", "_release"]:
     for tool in ["d8", "r8cf"]:
diff --git a/src/main/java/com/android/tools/r8/errors/InlinableStaticFinalFieldPreconditionDiagnostic.java b/src/main/java/com/android/tools/r8/errors/InlinableStaticFinalFieldPreconditionDiagnostic.java
new file mode 100644
index 0000000..45b40d2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/InlinableStaticFinalFieldPreconditionDiagnostic.java
@@ -0,0 +1,50 @@
+// 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.errors;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.shaking.ProguardIfRule;
+import com.android.tools.r8.utils.FieldReferenceUtils;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Collection;
+import java.util.List;
+
+@Keep
+public class InlinableStaticFinalFieldPreconditionDiagnostic implements Diagnostic {
+
+  private final ProguardIfRule rule;
+  private final Collection<FieldReference> fields;
+
+  public InlinableStaticFinalFieldPreconditionDiagnostic(
+      ProguardIfRule rule, List<DexField> fields) {
+    this.rule = rule;
+    this.fields = ListUtils.map(fields, DexField::asFieldReference);
+  }
+
+  @Override
+  public Origin getOrigin() {
+    return rule.getOrigin();
+  }
+
+  @Override
+  public Position getPosition() {
+    return rule.getPosition();
+  }
+
+  @Override
+  public String getDiagnosticMessage() {
+    return StringUtils.lines(
+            "Rule precondition matches static final fields javac has inlined.",
+            "Such rules are unsound as the shrinker cannot infer the inlining precisely.",
+            "Consider adding !static to the rule.",
+            "Matched fields are: ")
+        + StringUtils.joinLines(ListUtils.map(fields, FieldReferenceUtils::toSourceString));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 8c16e46..d49c43f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -572,9 +572,8 @@
       return null;
     }
     if (debugInfoForWriting == null) {
-      debugInfoForWriting = new DexDebugInfoForWriting(debugInfo);
+      debugInfoForWriting = DexDebugInfoForWriting.create(debugInfo);
     }
-
     return debugInfoForWriting;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index bb93147..e4994c0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.dex.DebugBytecodeWriter;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
-import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.utils.Int2StructuralItemArrayMap;
@@ -78,8 +77,20 @@
     internalAcceptHashing(visitor);
   }
 
-  public abstract void writeOn(
-      DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens);
+  public final void writeOn(
+      DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
+    assert isWritableEvent();
+    internalWriteOn(writer, mapping, graphLens);
+  }
+
+  boolean isWritableEvent() {
+    return false;
+  }
+
+  void internalWriteOn(
+      DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
+    throw new Unreachable();
+  }
 
   public abstract void accept(DexDebugEventVisitor visitor);
 
@@ -108,7 +119,12 @@
     public final int delta;
 
     @Override
-    public void writeOn(
+    boolean isWritableEvent() {
+      return true;
+    }
+
+    @Override
+    public void internalWriteOn(
         DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
       writer.putByte(Constants.DBG_ADVANCE_PC);
       writer.putUleb128(delta);
@@ -158,7 +174,12 @@
     }
 
     @Override
-    public void writeOn(
+    boolean isWritableEvent() {
+      return true;
+    }
+
+    @Override
+    public void internalWriteOn(
         DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
       writer.putByte(Constants.DBG_SET_PROLOGUE_END);
     }
@@ -203,7 +224,12 @@
     }
 
     @Override
-    public void writeOn(
+    boolean isWritableEvent() {
+      return true;
+    }
+
+    @Override
+    public void internalWriteOn(
         DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
       writer.putByte(Constants.DBG_SET_EPILOGUE_BEGIN);
     }
@@ -249,7 +275,12 @@
     }
 
     @Override
-    public void writeOn(
+    boolean isWritableEvent() {
+      return true;
+    }
+
+    @Override
+    public void internalWriteOn(
         DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
       writer.putByte(Constants.DBG_ADVANCE_LINE);
       writer.putSleb128(delta);
@@ -317,7 +348,12 @@
     }
 
     @Override
-    public void writeOn(
+    boolean isWritableEvent() {
+      return true;
+    }
+
+    @Override
+    public void internalWriteOn(
         DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
       writer.putByte(signature == null
           ? Constants.DBG_START_LOCAL
@@ -388,7 +424,12 @@
     }
 
     @Override
-    public void writeOn(
+    boolean isWritableEvent() {
+      return true;
+    }
+
+    @Override
+    public void internalWriteOn(
         DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
       writer.putByte(Constants.DBG_END_LOCAL);
       writer.putUleb128(registerNum);
@@ -435,7 +476,12 @@
     }
 
     @Override
-    public void writeOn(
+    boolean isWritableEvent() {
+      return true;
+    }
+
+    @Override
+    public void internalWriteOn(
         DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
       writer.putByte(Constants.DBG_RESTART_LOCAL);
       writer.putUleb128(registerNum);
@@ -489,9 +535,9 @@
     }
 
     @Override
-    public void writeOn(
-        DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
-      throw new InternalCompilerError("Unused/unsupported SetFile event should never be written");
+    boolean isWritableEvent() {
+      // Even though this is a DEX specified event it is unsupported and should never be written.
+      return false;
     }
 
     @Override
@@ -547,12 +593,6 @@
     }
 
     @Override
-    public void writeOn(
-        DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
-      // CallerPosition will not be written.
-    }
-
-    @Override
     public void accept(DexDebugEventVisitor visitor) {
       visitor.visit(this);
     }
@@ -626,12 +666,6 @@
     }
 
     @Override
-    public void writeOn(
-        DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
-      // Will not be written
-    }
-
-    @Override
     public void accept(DexDebugEventVisitor visitor) {
       visitor.visit(this);
     }
@@ -663,12 +697,6 @@
     }
 
     @Override
-    public void writeOn(
-        DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
-      // CallerPosition will not be written.
-    }
-
-    @Override
     public void accept(DexDebugEventVisitor visitor) {
       visitor.visit(this);
     }
@@ -729,7 +757,12 @@
     }
 
     @Override
-    public void writeOn(
+    boolean isWritableEvent() {
+      return true;
+    }
+
+    @Override
+    public void internalWriteOn(
         DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
       writer.putByte(value);
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java
index 0777b85..ddb4efc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java
@@ -4,20 +4,36 @@
 
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame;
-import java.util.Arrays;
-
 /**
  * Wraps DexDebugInfo to make comparison and hashcode not consider
  * the SetInlineFrames
  */
 public class DexDebugInfoForWriting extends DexDebugInfo {
 
-  public DexDebugInfoForWriting(DexDebugInfo dexDebugInfo) {
-    super(dexDebugInfo.startLine, dexDebugInfo.parameters,
-        Arrays.stream(dexDebugInfo.events)
-            .filter(d -> !(d instanceof SetInlineFrame))
-            .toArray(DexDebugEvent[]::new));
+  private DexDebugInfoForWriting(int startLine, DexString[] parameters, DexDebugEvent[] events) {
+    super(startLine, parameters, events);
   }
 
+  public static DexDebugInfoForWriting create(DexDebugInfo debugInfo) {
+    assert debugInfo != null;
+    int nonWritableEvents = 0;
+    for (DexDebugEvent event : debugInfo.events) {
+      if (!event.isWritableEvent()) {
+        nonWritableEvents++;
+      }
+    }
+    DexDebugEvent[] writableEvents;
+    if (nonWritableEvents == 0) {
+      writableEvents = debugInfo.events;
+    } else {
+      writableEvents = new DexDebugEvent[debugInfo.events.length - nonWritableEvents];
+      int i = 0;
+      for (DexDebugEvent event : debugInfo.events) {
+        if (event.isWritableEvent()) {
+          writableEvents[i++] = event;
+        }
+      }
+    }
+    return new DexDebugInfoForWriting(debugInfo.startLine, debugInfo.parameters, writableEvents);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 44a5132..3ae4b1f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -43,6 +43,11 @@
   private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance();
   private KotlinFieldLevelInfo kotlinMemberInfo = getNoKotlinInfo();
 
+  // Mark indicating if this field has been identified as potentially inlined by javac.
+  // This is to ensure consistent tracing in the second round of tree shaking. Remove this field
+  // once conditional rules are represented by rule-instances rather than reevaluating rule-schemas.
+  private boolean isInlinableByJavaC = false;
+
   private static void specify(StructuralSpecification<DexEncodedField, ?> spec) {
     spec.withItem(DexEncodedField::getReference)
         .withItem(DexEncodedField::getAccessFlags)
@@ -358,6 +363,14 @@
     return new Builder(true);
   }
 
+  public void markAsInlinableByJavaC() {
+    isInlinableByJavaC = true;
+  }
+
+  public boolean isInlinableByJavaC() {
+    return isInlinableByJavaC;
+  }
+
   public static class Builder {
 
     private DexField field;
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index a8709ec..a68f038 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -47,7 +47,6 @@
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
-import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -55,12 +54,10 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.ir.optimize.NestUtils;
-import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
 import com.android.tools.r8.ir.optimize.info.MutableMethodOptimizationInfo;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
 import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
@@ -150,7 +147,6 @@
           ComputedApiLevel.notSet(),
           ComputedApiLevel.notSet(),
           null,
-          CallSiteOptimizationInfo.top(),
           DefaultMethodOptimizationInfo.getInstance(),
           false);
   public static final Int2ReferenceMap<DebugLocalInfo> NO_PARAMETER_INFO =
@@ -164,7 +160,6 @@
   //   we need to maintain a set of states with (potentially different) contexts.
   private CompilationState compilationState = CompilationState.NOT_PROCESSED;
   private MethodOptimizationInfo optimizationInfo;
-  private CallSiteOptimizationInfo callSiteOptimizationInfo;
   private CfVersion classFileVersion;
   /** The apiLevelForCode describes the api level needed for knowing all references in the code */
   private ComputedApiLevel apiLevelForCode;
@@ -244,7 +239,6 @@
       ComputedApiLevel apiLevelForDefinition,
       ComputedApiLevel apiLevelForCode,
       CfVersion classFileVersion,
-      CallSiteOptimizationInfo callSiteOptimizationInfo,
       MethodOptimizationInfo optimizationInfo,
       boolean deprecated) {
     super(method, annotations, d8R8Synthesized, apiLevelForDefinition);
@@ -255,7 +249,6 @@
     this.code = code;
     this.classFileVersion = classFileVersion;
     this.apiLevelForCode = apiLevelForCode;
-    this.callSiteOptimizationInfo = requireNonNull(callSiteOptimizationInfo);
     this.optimizationInfo = requireNonNull(optimizationInfo);
     assert accessFlags != null;
     assert code == null || !shouldNotHaveCode();
@@ -1298,27 +1291,6 @@
     optimizationInfo = info;
   }
 
-  public synchronized void abandonCallSiteOptimizationInfo() {
-    checkIfObsolete();
-    callSiteOptimizationInfo = CallSiteOptimizationInfo.abandoned();
-  }
-
-  public synchronized CallSiteOptimizationInfo getCallSiteOptimizationInfo() {
-    checkIfObsolete();
-    return callSiteOptimizationInfo;
-  }
-
-  public synchronized void joinCallSiteOptimizationInfo(
-      CallSiteOptimizationInfo other, AppView<?> appView) {
-    checkIfObsolete();
-    callSiteOptimizationInfo = callSiteOptimizationInfo.join(other, appView, this);
-  }
-
-  public synchronized void setCallSiteOptimizationInfo(
-      CallSiteOptimizationInfo callSiteOptimizationInfo) {
-    this.callSiteOptimizationInfo = callSiteOptimizationInfo;
-  }
-
   public void copyMetadata(AppView<?> appView, DexEncodedMethod from) {
     checkIfObsolete();
     if (from.hasClassFileVersion()) {
@@ -1369,9 +1341,6 @@
     private OptionalBool isLibraryMethodOverride = OptionalBool.UNKNOWN;
     private ParameterAnnotationsList parameterAnnotations = ParameterAnnotationsList.empty();
     private CompilationState compilationState = CompilationState.NOT_PROCESSED;
-    // TODO(b/190154391): We should set this to top, but the old call site optimization requires
-    //  this to be bottom.
-    private CallSiteOptimizationInfo callSiteOptimizationInfo = CallSiteOptimizationInfo.bottom();
     private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.getInstance();
     private KotlinMethodLevelInfo kotlinInfo = getNoKotlinInfo();
     private CfVersion classFileVersion = null;
@@ -1401,7 +1370,6 @@
       code = from.getCode();
       apiLevelForDefinition = from.getApiLevelForDefinition();
       apiLevelForCode = from.getApiLevelForCode();
-      callSiteOptimizationInfo = from.getCallSiteOptimizationInfo();
       optimizationInfo =
           from.getOptimizationInfo().isMutableOptimizationInfo()
               ? from.getOptimizationInfo().asMutableMethodOptimizationInfo().mutableCopy()
@@ -1440,31 +1408,10 @@
       return this;
     }
 
-    public Builder fixupCallSiteOptimizationInfo(MethodOptimizationInfoFixer fixer) {
-      if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo()) {
-        callSiteOptimizationInfo =
-            fixer.fixupCallSiteOptimizationInfo(
-                callSiteOptimizationInfo.asConcreteCallSiteOptimizationInfo());
-      }
-      return this;
-    }
-
     public Builder fixupOptimizationInfo(
         AppView<AppInfoWithLiveness> appView, MethodOptimizationInfoFixer fixer) {
-      return fixupCallSiteOptimizationInfo(fixer)
-          .modifyOptimizationInfo(
-              (newMethod, optimizationInfo) -> optimizationInfo.fixup(appView, fixer));
-    }
-
-    public Builder setSimpleInliningConstraint(
-        DexProgramClass holder, SimpleInliningConstraint simpleInliningConstraint) {
-      return addBuildConsumer(
-          newMethod ->
-              OptimizationFeedbackSimple.getInstance()
-                  .setSimpleInliningConstraint(
-                      // The method has not yet been installed so we cannot use
-                      // asProgramMethod(appView).
-                      new ProgramMethod(holder, newMethod), simpleInliningConstraint));
+      return modifyOptimizationInfo(
+          (newMethod, optimizationInfo) -> optimizationInfo.fixup(appView, fixer));
     }
 
     public Builder addBuildConsumer(Consumer<DexEncodedMethod> consumer) {
@@ -1669,7 +1616,6 @@
               apiLevelForDefinition,
               apiLevelForCode,
               classFileVersion,
-              callSiteOptimizationInfo,
               optimizationInfo,
               deprecated);
       result.setKotlinMemberInfo(kotlinInfo);
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index d72df8c..4879d46 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -883,6 +883,7 @@
           boxedShortType,
           boxedVoidType,
           enumType,
+          javaLangSystemType,
           npeType,
           objectType,
           stringBufferType,
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 900e79c..2bbb51b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -8,8 +8,6 @@
 
 import com.android.tools.r8.ProgramResource;
 import com.android.tools.r8.ProgramResource.Kind;
-import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
-import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
@@ -822,21 +820,4 @@
     return checksumSupplier;
   }
 
-  public ComputedApiLevel getApiReferenceLevel(
-      AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
-    // The api level of a class is the max level of it's members, super class and interfaces.
-    return getMembersApiReferenceLevel(
-        apiLevelCompute.computeApiLevelForDefinition(
-            allImmediateSupertypes(), apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
-  }
-
-  public ComputedApiLevel getMembersApiReferenceLevel(ComputedApiLevel memberLevel) {
-    for (DexEncodedMember<?, ?> member : members()) {
-      memberLevel = memberLevel.max(member.getApiLevel());
-      if (memberLevel.isUnknownApiLevel()) {
-        return memberLevel;
-      }
-    }
-    return memberLevel;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 9236d1f..a6c1c5c 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -118,40 +118,64 @@
    */
   public static class FieldLookupResult extends MemberLookupResult<DexField> {
 
-    private final DexType castType;
+    private final DexType readCastType;
+    private final DexType writeCastType;
 
-    private FieldLookupResult(DexField reference, DexField reboundReference, DexType castType) {
+    private FieldLookupResult(
+        DexField reference,
+        DexField reboundReference,
+        DexType readCastType,
+        DexType writeCastType) {
       super(reference, reboundReference);
-      this.castType = castType;
+      this.readCastType = readCastType;
+      this.writeCastType = writeCastType;
     }
 
     public static Builder builder(GraphLens lens) {
       return new Builder(lens);
     }
 
-    public boolean hasCastType() {
-      return castType != null;
+    public boolean hasReadCastType() {
+      return readCastType != null;
     }
 
-    public DexType getCastType() {
-      return castType;
+    public DexType getReadCastType() {
+      return readCastType;
     }
 
-    public DexType getRewrittenCastType(Function<DexType, DexType> fn) {
-      return hasCastType() ? fn.apply(castType) : null;
+    public DexType getRewrittenReadCastType(Function<DexType, DexType> fn) {
+      return hasReadCastType() ? fn.apply(readCastType) : null;
+    }
+
+    public boolean hasWriteCastType() {
+      return writeCastType != null;
+    }
+
+    public DexType getWriteCastType() {
+      return writeCastType;
+    }
+
+    public DexType getRewrittenWriteCastType(Function<DexType, DexType> fn) {
+      return hasWriteCastType() ? fn.apply(writeCastType) : null;
     }
 
     public static class Builder extends MemberLookupResult.Builder<DexField, Builder> {
 
-      private DexType castType;
+      private DexType readCastType;
+      private DexType writeCastType;
       private GraphLens lens;
 
       private Builder(GraphLens lens) {
         this.lens = lens;
       }
 
-      public Builder setCastType(DexType castType) {
-        this.castType = castType;
+      public Builder setReadCastType(DexType readCastType) {
+        this.readCastType = readCastType;
+        return this;
+      }
+
+      public Builder setWriteCastType(DexType writeCastType) {
+        this.writeCastType = writeCastType;
         return this;
       }
 
@@ -162,7 +186,7 @@
 
       public FieldLookupResult build() {
         // TODO(b/168282032): All non-identity graph lenses should set the rebound reference.
-        return new FieldLookupResult(reference, reboundReference, castType);
+        return new FieldLookupResult(reference, reboundReference, readCastType, writeCastType);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
index 62ce9d8..e5b3462 100644
--- a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
@@ -163,7 +163,9 @@
       return FieldLookupResult.builder(this)
           .setReboundReference(rewrittenReboundReference)
           .setReference(rewrittenNonReboundReference)
-          .setCastType(previous.getRewrittenCastType(this::internalDescribeLookupClassType))
+          .setReadCastType(previous.getRewrittenReadCastType(this::internalDescribeLookupClassType))
+          .setWriteCastType(
+              previous.getRewrittenWriteCastType(this::internalDescribeLookupClassType))
           .build();
     } else {
       // TODO(b/168282032): We should always have the rebound reference, so this should become
@@ -171,7 +173,9 @@
       DexField rewrittenReference = previous.getRewrittenReference(fieldMap);
       return FieldLookupResult.builder(this)
           .setReference(rewrittenReference)
-          .setCastType(previous.getRewrittenCastType(this::internalDescribeLookupClassType))
+          .setReadCastType(previous.getRewrittenReadCastType(this::internalDescribeLookupClassType))
+          .setWriteCastType(
+              previous.getRewrittenWriteCastType(this::internalDescribeLookupClassType))
           .build();
     }
   }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java b/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java
index 1243e98..01b6d96 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java
@@ -4,9 +4,9 @@
 
 package com.android.tools.r8.graph.analysis;
 
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 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.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
@@ -16,8 +16,6 @@
 import com.android.tools.r8.shaking.KeepInfo.Joiner;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableSet;
-import java.util.Set;
 
 /**
  * In Dalvik it is a verification error to read and use a field of type Missing[].
@@ -37,27 +35,11 @@
 
   private final DexItemFactory dexItemFactory;
   private final Enqueuer enqueuer;
-  private final Set<DexType> knownToBePresentOnDalvik;
 
   public GetArrayOfMissingTypeVerifyErrorWorkaround(
       AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
     this.dexItemFactory = appView.dexItemFactory();
     this.enqueuer = enqueuer;
-    this.knownToBePresentOnDalvik =
-        ImmutableSet.<DexType>builder()
-            .add(dexItemFactory.boxedBooleanType)
-            .add(dexItemFactory.boxedByteType)
-            .add(dexItemFactory.boxedCharType)
-            .add(dexItemFactory.boxedDoubleType)
-            .add(dexItemFactory.boxedFloatType)
-            .add(dexItemFactory.boxedIntType)
-            .add(dexItemFactory.boxedLongType)
-            .add(dexItemFactory.boxedShortType)
-            .add(dexItemFactory.classType)
-            .add(dexItemFactory.objectType)
-            .add(dexItemFactory.enumType)
-            .add(dexItemFactory.stringType)
-            .build();
   }
 
   public static void register(
@@ -77,7 +59,7 @@
   @Override
   public void traceInstanceFieldRead(
       DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
-    if (isUnsafeToUseFieldOnDalvik(field, context)) {
+    if (isUnsafeToUseFieldOnDalvik(field)) {
       enqueuer.getKeepInfo().joinMethod(context, Joiner::disallowOptimization);
     }
   }
@@ -85,12 +67,12 @@
   @Override
   public void traceStaticFieldRead(
       DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
-    if (isUnsafeToUseFieldOnDalvik(field, context)) {
+    if (isUnsafeToUseFieldOnDalvik(field)) {
       enqueuer.getKeepInfo().joinMethod(context, Joiner::disallowOptimization);
     }
   }
 
-  private boolean isUnsafeToUseFieldOnDalvik(DexField field, ProgramMethod context) {
+  private boolean isUnsafeToUseFieldOnDalvik(DexField field) {
     DexType fieldType = field.getType();
     if (!fieldType.isArrayType()) {
       return false;
@@ -99,13 +81,15 @@
     if (!baseType.isClassType()) {
       return false;
     }
-    if (knownToBePresentOnDalvik.contains(baseType)) {
-      return false;
-    }
-    // TODO(b/206891715): Use the API database to determine if the given type is introduced in API
-    //  level L or later.
-    DexClass baseClass = enqueuer.definitionFor(baseType, context);
-    return baseClass != null && baseClass.isLibraryClass();
+    ComputedApiLevel baseTypeApiLevel =
+        enqueuer
+            .getApiLevelCompute()
+            .computeApiLevelForLibraryReference(baseType, ComputedApiLevel.unknown());
+    return !baseTypeApiLevel.isKnownApiLevel()
+        || baseTypeApiLevel
+            .asKnownApiLevel()
+            .getApiLevel()
+            .isGreaterThanOrEqualTo(AndroidApiLevel.L);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 03a044d..937a1fc 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -77,10 +77,11 @@
     return FieldLookupResult.builder(this)
         .setReference(lookup.getReference())
         .setReboundReference(lookup.getReboundReference())
-        .setCastType(
+        .setReadCastType(
             lookup.getReference().getType() != previous.getReference().getType()
                 ? lookupType(previous.getReference().getType())
                 : null)
+        .setWriteCastType(previous.getRewrittenWriteCastType(this::internalDescribeLookupClassType))
         .build();
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
index 1f953f4..6a083f0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.horizontalclassmerging.policies;
 
+import static com.android.tools.r8.utils.AndroidApiLevelUtils.getApiReferenceLevelForMerging;
+
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
 import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.graph.AppView;
@@ -37,6 +39,6 @@
   @Override
   public ComputedApiLevel getMergeKey(DexProgramClass clazz) {
     assert enableApiCallerIdentification;
-    return clazz.getApiReferenceLevel(appView, apiLevelCompute);
+    return getApiReferenceLevelForMerging(appView, apiLevelCompute, clazz);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldPut.java b/src/main/java/com/android/tools/r8/ir/code/FieldPut.java
index b45feb0..991a263 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldPut.java
@@ -10,10 +10,14 @@
 
   DexField getField();
 
+  Position getPosition();
+
   int getValueIndex();
 
   Value value();
 
+  void setValue(Value value);
+
   FieldInstruction asFieldInstruction();
 
   boolean isInstancePut();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 2a5d60e..95086dc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -80,6 +80,11 @@
   }
 
   @Override
+  public void setValue(Value value) {
+    replaceValue(1, value);
+  }
+
+  @Override
   public void buildDex(DexBuilder builder) {
     com.android.tools.r8.code.Instruction instruction;
     int valueRegister = builder.allocatedRegister(value(), getNumber());
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 8cbeb6d..339f16f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -58,6 +58,11 @@
   }
 
   @Override
+  public void setValue(Value value) {
+    replaceValue(0, value);
+  }
+
+  @Override
   public void buildDex(DexBuilder builder) {
     com.android.tools.r8.code.Instruction instruction;
     int src = builder.allocatedRegister(value(), getNumber());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 40cb9e9..c3fd49f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -753,7 +753,8 @@
     // types, those could be still less precise at one single call site, where specific arguments
     // will be passed during (double) inlining. Instead of adding assumptions and removing invalid
     // ones, it's better not to insert assumptions for inlinee in the beginning.
-    CallSiteOptimizationInfo callSiteOptimizationInfo = getMethod().getCallSiteOptimizationInfo();
+    CallSiteOptimizationInfo callSiteOptimizationInfo =
+        getMethod().getOptimizationInfo().getArgumentInfos();
     if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo() && method == context) {
       // TODO(b/190154391): Consider pruning all argument information from the optimization info
       //  after the second optimization pass. That way we save memory and can assert here that
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 269dc23..dafd559 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -68,6 +68,7 @@
 import com.android.tools.r8.ir.code.ConstClass;
 import com.android.tools.r8.ir.code.ConstMethodHandle;
 import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.FieldPut;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.InitClass;
 import com.android.tools.r8.ir.code.InstanceGet;
@@ -104,6 +105,7 @@
 import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiFunction;
@@ -347,13 +349,14 @@
                   SingleNumberValue numberValue = parameter.getValue(appView);
 
                   // Try to find an existing constant instruction, otherwise generate a new one.
+                  InstructionListIterator finalIterator = iterator;
                   Value value =
                       parameterMap
                           .computeIfAbsent(numberValue, ignore -> new IdentityHashMap<>())
                           .computeIfAbsent(
                               type,
                               ignore -> {
-                                iterator.previous();
+                                finalIterator.previous();
                                 Instruction instruction =
                                     numberValue.createMaterializingInstruction(
                                         appView,
@@ -363,8 +366,8 @@
                                 assert !instruction.instructionTypeCanThrow();
                                 instruction.setPosition(
                                     options.debug ? invoke.getPosition() : Position.none());
-                                iterator.add(instruction);
-                                iterator.next();
+                                finalIterator.add(instruction);
+                                finalIterator.next();
                                 return instruction.outValue();
                               });
 
@@ -448,15 +451,15 @@
                     new InstanceGet(newOutValue, instanceGet.object(), rewrittenField));
               }
               if (newOutValue != null) {
-                if (lookup.hasCastType() && newOutValue.hasNonDebugUsers()) {
+                if (lookup.hasReadCastType() && newOutValue.hasNonDebugUsers()) {
                   TypeElement castType =
                       TypeElement.fromDexType(
-                          lookup.getCastType(), newOutValue.getType().nullability(), appView);
+                          lookup.getReadCastType(), newOutValue.getType().nullability(), appView);
                   Value castOutValue = code.createValue(castType);
                   newOutValue.replaceUsers(castOutValue);
                   CheckCast checkCast =
                       SafeCheckCast.builder()
-                          .setCastType(lookup.getCastType())
+                          .setCastType(lookup.getReadCastType())
                           .setObject(newOutValue)
                           .setOutValue(castOutValue)
                           .setPosition(instanceGet)
@@ -476,6 +479,8 @@
               InstancePut instancePut = current.asInstancePut();
               DexField field = instancePut.getField();
               FieldLookupResult lookup = graphLens.lookupFieldResult(field);
+              insertCastForFieldAssignmentIfNeeded(code, blocks, iterator, instancePut, lookup);
+
               DexField rewrittenField = rewriteFieldReference(lookup, method);
               DexMethod replacementMethod =
                   graphLens.lookupPutFieldForMethod(rewrittenField, method.getReference());
@@ -517,15 +522,15 @@
                 iterator.replaceCurrentInstruction(new StaticGet(newOutValue, rewrittenField));
               }
               if (newOutValue != null) {
-                if (lookup.hasCastType() && newOutValue.hasNonDebugUsers()) {
+                if (lookup.hasReadCastType() && newOutValue.hasNonDebugUsers()) {
                   TypeElement castType =
                       TypeElement.fromDexType(
-                          lookup.getCastType(), newOutValue.getType().nullability(), appView);
+                          lookup.getReadCastType(), newOutValue.getType().nullability(), appView);
                   Value castOutValue = code.createValue(castType);
                   newOutValue.replaceUsers(castOutValue);
                   CheckCast checkCast =
                       SafeCheckCast.builder()
-                          .setCastType(lookup.getCastType())
+                          .setCastType(lookup.getReadCastType())
                           .setObject(newOutValue)
                           .setOutValue(castOutValue)
                           .setPosition(staticGet)
@@ -545,6 +550,8 @@
               StaticPut staticPut = current.asStaticPut();
               DexField field = staticPut.getField();
               FieldLookupResult lookup = graphLens.lookupFieldResult(field);
+              insertCastForFieldAssignmentIfNeeded(code, blocks, iterator, staticPut, lookup);
+
               DexField actualField = rewriteFieldReference(lookup, method);
               DexMethod replacementMethod =
                   graphLens.lookupPutFieldForMethod(actualField, method.getReference());
@@ -745,6 +752,30 @@
     assert code.hasNoMergedClasses(appView);
   }
 
+  private void insertCastForFieldAssignmentIfNeeded(
+      IRCode code,
+      ListIterator<BasicBlock> blocks,
+      InstructionListIterator iterator,
+      FieldPut fieldPut,
+      FieldLookupResult lookup) {
+    if (lookup.hasWriteCastType()) {
+      iterator.previous();
+      CheckCast checkCast =
+          SafeCheckCast.builder()
+              .setObject(fieldPut.value())
+              .setFreshOutValue(code, lookup.getWriteCastType().toTypeElement(appView))
+              .setCastType(lookup.getWriteCastType())
+              .setPosition(fieldPut.getPosition())
+              .build();
+      iterator.add(checkCast);
+      iterator =
+          iterator.splitCopyCatchHandlers(code, blocks, appView.options()).listIterator(code);
+      fieldPut.setValue(checkCast.outValue());
+      Instruction next = iterator.next();
+      assert next == fieldPut;
+    }
+  }
+
   private DexField rewriteFieldReference(FieldLookupResult lookup, ProgramMethod context) {
     if (lookup.hasReboundReference()) {
       DexClass holder = appView.definitionFor(lookup.getReboundReference().getHolderType());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
deleted file mode 100644
index d39f509..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ /dev/null
@@ -1,440 +0,0 @@
-// Copyright (c) 2019, 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.ir.optimize;
-
-import static com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo.abandoned;
-import static com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo.top;
-import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeCustom;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
-import com.android.tools.r8.ir.conversion.PostMethodProcessor;
-import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
-import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
-import com.android.tools.r8.logging.Log;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.ForEachable;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.CallSiteOptimizationOptions;
-import com.android.tools.r8.utils.LazyBox;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
-import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.collect.Sets;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.function.Consumer;
-
-public class CallSiteOptimizationInfoPropagator {
-
-  // TODO(b/139246447): should we revisit new targets over and over again?
-  //   Maybe piggy-back on MethodProcessor's wave/batch processing?
-  // For now, before revisiting methods with more precise argument info, we switch the mode.
-  // Then, revisiting a target at a certain level will not improve call site information of
-  // callees in lower levels.
-  public enum Mode {
-    // Set until the end of the 1st round of IR processing. CallSiteOptimizationInfo will be updated
-    // in this mode only.
-    COLLECT,
-    // Set once the all methods are processed. IRBuilder will add other instructions that reflect
-    // collected CallSiteOptimizationInfo.
-    REVISIT;
-
-    public boolean isRevisit() {
-      return this == REVISIT;
-    }
-  }
-
-  private final AppView<AppInfoWithLiveness> appView;
-  private final InternalOptions options;
-  private final CallSiteOptimizationOptions optimizationOptions;
-  private Mode mode = Mode.COLLECT;
-
-  private ProgramMethodSet revisitedMethodsForTesting = null;
-
-  public CallSiteOptimizationInfoPropagator(AppView<AppInfoWithLiveness> appView) {
-    assert appView.enableWholeProgramOptimizations();
-    this.appView = appView;
-    this.options = appView.options();
-    this.optimizationOptions = appView.options().callSiteOptimizationOptions();
-    if (Log.isLoggingEnabledFor(CallSiteOptimizationInfoPropagator.class)) {
-      revisitedMethodsForTesting = ProgramMethodSet.create();
-    }
-  }
-
-  public Mode getMode() {
-    return mode;
-  }
-
-  public void logResults() {
-    assert Log.ENABLED;
-    if (revisitedMethodsForTesting != null) {
-      Log.info(getClass(), "# of methods to revisit: %s", revisitedMethodsForTesting.size());
-      for (ProgramMethod m : revisitedMethodsForTesting) {
-        Log.info(
-            getClass(),
-            "%s: %s",
-            m.toSourceString(),
-            m.getDefinition().getCallSiteOptimizationInfo().toString());
-      }
-    }
-  }
-
-  public void collectCallSiteOptimizationInfo(IRCode code, Timing timing) {
-    // TODO(b/139246447): we could collect call site optimization during REVISIT mode as well,
-    //   but that may require a separate copy of CallSiteOptimizationInfo.
-    if (mode != Mode.COLLECT) {
-      return;
-    }
-
-    ProgramMethod context = code.context();
-    for (Instruction instruction : code.instructions()) {
-      if (instruction.isInvokeMethod()) {
-        collectCallSiteOptimizationInfoForInvokeMethod(
-            instruction.asInvokeMethod(), context, timing);
-      } else if (instruction.isInvokeCustom()) {
-        collectCallSiteOptimizationInfoForInvokeCustom(instruction.asInvokeCustom());
-      }
-    }
-  }
-
-  private void collectCallSiteOptimizationInfoForInvokeMethod(
-      InvokeMethod invoke, ProgramMethod context, Timing timing) {
-    DexMethod invokedMethod = invoke.getInvokedMethod();
-    SingleResolutionResult resolutionResult =
-        appView
-            .appInfo()
-            .resolveMethod(invokedMethod, invoke.getInterfaceBit())
-            .asSingleResolution();
-    if (resolutionResult == null) {
-      return;
-    }
-    // For virtual and interface calls, proceed on valid results only (since it's enforced).
-    if (invoke.isInvokeMethodWithDynamicDispatch() && !resolutionResult.isVirtualTarget()) {
-      return;
-    }
-    ProgramMethod resolutionTarget =
-        resolutionResult.asSingleResolution().getResolutionPair().asProgramMethod();
-    if (resolutionTarget == null || isMaybeClasspathOrLibraryMethodOverride(resolutionTarget)) {
-      return;
-    }
-    propagateArgumentsToDispatchTargets(invoke, resolutionResult, context, timing);
-  }
-
-  private void collectCallSiteOptimizationInfoForInvokeCustom(InvokeCustom invoke) {
-    // If the bootstrap method is program declared it will be called. The call is with runtime
-    // provided arguments so ensure that the call-site info is TOP.
-    DexMethodHandle bootstrapMethod = invoke.getCallSite().bootstrapMethod;
-    SingleResolutionResult resolution =
-        appView
-            .appInfo()
-            .resolveMethod(bootstrapMethod.asMethod(), bootstrapMethod.isInterface)
-            .asSingleResolution();
-    if (resolution != null && resolution.getResolvedHolder().isProgramClass()) {
-      resolution.getResolvedMethod().joinCallSiteOptimizationInfo(top(), appView);
-    }
-  }
-
-  private boolean isMaybeClasspathOrLibraryMethodOverride(ProgramMethod target) {
-    // If the method overrides a library method, it is unsure how the method would be invoked by
-    // that library.
-    return target.getDefinition().isLibraryMethodOverride().isPossiblyTrue();
-  }
-
-  // Propagate information about the arguments to all possible dispatch targets of the invoke.
-  private void propagateArgumentsToDispatchTargets(
-      InvokeMethod invoke,
-      SingleResolutionResult resolutionResult,
-      ProgramMethod context,
-      Timing timing) {
-    if (invoke.arguments().isEmpty()) {
-      // Nothing to propagate.
-      return;
-    }
-
-    if (invoke.arguments().size()
-        != invoke.getInvokedMethod().getArity()
-            + BooleanUtils.intValue(invoke.isInvokeMethodWithReceiver())) {
-      // Verification error.
-      assert false;
-      return;
-    }
-
-    if (resolutionResult.getResolvedMethod().getCallSiteOptimizationInfo().isAbandoned()) {
-      // We stopped tracking the arguments to all possible dispatch targets.
-      assert verifyAllProgramDispatchTargetsHaveBeenAbandoned(invoke, context);
-      return;
-    }
-
-    timing.begin("Lookup possible dispatch targets");
-    ProgramMethodSet targets = invoke.lookupProgramDispatchTargets(appView, context);
-    timing.end();
-
-    assert invoke.isInvokeMethodWithDynamicDispatch()
-        // For other invocation types, the size of targets should be at most one.
-        || targets == null
-        || targets.size() <= 1;
-
-    if (targets == null || targets.isEmpty()) {
-      return;
-    }
-
-    if (targets.size() > optimizationOptions.getMaxNumberOfDispatchTargetsBeforeAbandoning()) {
-      // If the number of targets exceed the threshold, abandon call site optimization for all
-      // targets.
-      abandonCallSitePropagation(invoke, resolutionResult, targets, context);
-      return;
-    }
-
-    timing.begin("Record arguments");
-    // Lazily computed piece of information that needs to be propagated to all dispatch targets.
-    LazyBox<CallSiteOptimizationInfo> callSiteOptimizationInfo =
-        new LazyBox<>(() -> computeCallSiteOptimizationInfoFromArguments(invoke, context, timing));
-    for (ProgramMethod target : targets) {
-      CallSiteOptimizationInfo newCallSiteOptimizationInfo =
-          propagateArgumentsToDispatchTarget(target, callSiteOptimizationInfo, timing);
-
-      // If one of the targets is abandoned or ends up being abandoned, then abandon call site
-      // optimization for all targets.
-      if (newCallSiteOptimizationInfo.isAbandoned()) {
-        abandonCallSitePropagation(invoke, resolutionResult, targets, context);
-        break;
-      }
-    }
-    timing.end();
-  }
-
-  private CallSiteOptimizationInfo propagateArgumentsToDispatchTarget(
-      ProgramMethod target,
-      LazyBox<CallSiteOptimizationInfo> lazyCallSiteOptimizationInfo,
-      Timing timing) {
-    CallSiteOptimizationInfo existingCallSiteOptimizationInfo =
-        target.getDefinition().getCallSiteOptimizationInfo();
-    if (existingCallSiteOptimizationInfo.isAbandoned()
-        || existingCallSiteOptimizationInfo.isTop()) {
-      return existingCallSiteOptimizationInfo;
-    }
-    if (!appView.appInfo().mayPropagateArgumentsTo(target)) {
-      return top();
-    }
-    timing.begin("Join argument info");
-    CallSiteOptimizationInfo callSiteOptimizationInfo =
-        lazyCallSiteOptimizationInfo.computeIfAbsent();
-    target
-        .getDefinition()
-        .joinCallSiteOptimizationInfo(
-            callSiteOptimizationInfo.hasUsefulOptimizationInfo(appView, target)
-                ? callSiteOptimizationInfo
-                : top(),
-            appView);
-    timing.end();
-    return target.getDefinition().getCallSiteOptimizationInfo();
-  }
-
-  private void abandonCallSitePropagation(
-      InvokeMethod invoke,
-      SingleResolutionResult resolutionResult,
-      ProgramMethodSet targets,
-      ProgramMethod context) {
-    if (invoke.isInvokeMethodWithDynamicDispatch()) {
-      // When there is a dynamic dispatch, we may have used dynamic type information to reduce the
-      // set of possible dispatch targets. However, it is an invariant that a method is marked as
-      // abandoned if-and-only-if that method and all of its overrides have been marked as
-      // abandoned. Therefore, we need to find all the overrides of the targeted method and mark
-      // them as abandoned, which we accomplish by performing a lookup without any dynamic type
-      // information.
-      InvokeMethodWithReceiver invokeMethodWithReceiver = invoke.asInvokeMethodWithReceiver();
-      if (invokeMethodWithReceiver.hasRefinedReceiverUpperBoundType(appView)
-          || invokeMethodWithReceiver.hasRefinedReceiverLowerBoundType(appView)) {
-        LookupResult lookupResult =
-            resolutionResult.lookupVirtualDispatchTargets(context.getHolder(), appView.appInfo());
-        // This should always succeed since we already looked up `targets` successfully.
-        assert lookupResult.isLookupResultSuccess();
-        abandonCallSitePropagation(
-            consumer ->
-                lookupResult.forEach(
-                    methodTarget -> {
-                      if (methodTarget.isProgramMethod()) {
-                        consumer.accept(methodTarget.asProgramMethod());
-                      } else {
-                        // This may happen if an interface method in the program is implemented
-                        // by a method in the classpath or library.
-                        assert invoke.isInvokeInterface();
-                      }
-                    },
-                    emptyConsumer()));
-        return;
-      }
-    }
-    abandonCallSitePropagation(targets::forEach);
-  }
-
-  private void abandonCallSitePropagation(ForEachable<ProgramMethod> methods) {
-    if (InternalOptions.assertionsEnabled()) {
-      synchronized (this) {
-        methods.forEach(method -> method.getDefinition().abandonCallSiteOptimizationInfo());
-      }
-    } else {
-      methods.forEach(method -> method.getDefinition().abandonCallSiteOptimizationInfo());
-    }
-  }
-
-  public void abandonCallSitePropagationForLambdaImplementationMethods(
-      ExecutorService executorService, Timing timing) throws ExecutionException {
-    if (appView.options().isGeneratingClassFiles()) {
-      timing.begin("Call site optimization: abandon lambda implementation methods");
-      ForEachable<ProgramMethod> lambdaImplementationMethods =
-          consumer ->
-              appView
-                  .appInfo()
-                  .forEachMethod(
-                      method -> {
-                        if (appView
-                            .appInfo()
-                            .isMethodTargetedByInvokeDynamic(method.getReference())) {
-                          consumer.accept(method);
-                        }
-                      });
-      ThreadUtils.processItems(
-          lambdaImplementationMethods,
-          this::abandonCallSitePropagationForMethodAndOverrides,
-          executorService);
-      timing.end();
-    }
-  }
-
-  public void abandonCallSitePropagationForPinnedMethodsAndOverrides(
-      ExecutorService executorService, Timing timing) throws ExecutionException {
-    timing.begin("Call site optimization: abandon pinned methods");
-    ThreadUtils.processItems(
-        this::forEachPinnedNonPrivateVirtualMethod,
-        this::abandonCallSitePropagationForMethodAndOverrides,
-        executorService);
-    timing.end();
-  }
-
-  private void forEachPinnedNonPrivateVirtualMethod(Consumer<ProgramMethod> consumer) {
-    for (DexProgramClass clazz : appView.appInfo().classes()) {
-      for (ProgramMethod virtualProgramMethod : clazz.virtualProgramMethods()) {
-        if (virtualProgramMethod.getDefinition().isNonPrivateVirtualMethod()
-            && appView
-                .getKeepInfo()
-                .isPinned(virtualProgramMethod.getReference(), appView, options)) {
-          consumer.accept(virtualProgramMethod);
-        }
-      }
-    }
-  }
-
-  private void abandonCallSitePropagationForMethodAndOverrides(ProgramMethod method) {
-    Set<ProgramMethod> abandonSet = Sets.newIdentityHashSet();
-    if (method.getDefinition().isNonPrivateVirtualMethod()) {
-      SingleResolutionResult resolutionResult =
-          new SingleResolutionResult(
-              method.getHolder(), method.getHolder(), method.getDefinition());
-      resolutionResult
-          .lookupVirtualDispatchTargets(method.getHolder(), appView.appInfo())
-          .forEach(
-              methodTarget -> {
-                if (methodTarget.isProgramMethod()) {
-                  abandonSet.add(methodTarget.asProgramMethod());
-                }
-              },
-              lambdaTarget -> {
-                if (lambdaTarget.getImplementationMethod().isProgramMethod()) {
-                  abandonSet.add(lambdaTarget.getImplementationMethod().asProgramMethod());
-                }
-              });
-    } else {
-      abandonSet.add(method);
-    }
-    abandonCallSitePropagation(abandonSet::forEach);
-  }
-
-  private CallSiteOptimizationInfo computeCallSiteOptimizationInfoFromArguments(
-      InvokeMethod invoke, ProgramMethod context, Timing timing) {
-    timing.begin("Compute argument info");
-    CallSiteOptimizationInfo callSiteOptimizationInfo =
-        ConcreteCallSiteOptimizationInfo.fromArguments(
-            appView, invoke.getInvokedMethod(), invoke.arguments(), context);
-    if (callSiteOptimizationInfo.isTop()) {
-      // If we are propagating unknown information to all call sites, then mark them as abandoned
-      // such that we bail out before looking up the possible dispatch targets if we see any future
-      // invokes to these methods.
-      callSiteOptimizationInfo = abandoned();
-    }
-    timing.end();
-    return callSiteOptimizationInfo;
-  }
-
-  public void enqueueMethodsForReprocessing(
-      PostMethodProcessor.Builder postMethodProcessorBuilder) {
-    postMethodProcessorBuilder
-        .getMethodsToReprocessBuilder()
-        .rewrittenWithLens(appView.graphLens())
-        .merge(methodsToRevisit());
-  }
-
-  private LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToRevisit() {
-    mode = Mode.REVISIT;
-    GraphLens currentGraphLens = appView.graphLens();
-    LongLivedProgramMethodSetBuilder<ProgramMethodSet> builder =
-        LongLivedProgramMethodSetBuilder.createForIdentitySet(currentGraphLens);
-    for (DexProgramClass clazz : appView.appInfo().classes()) {
-      clazz.forEachProgramMethodMatching(
-          definition -> {
-            assert !definition.isObsolete();
-            if (definition.shouldNotHaveCode()
-                || !definition.hasCode()
-                || definition.getCode().isEmptyVoidMethod()) {
-              return false;
-            }
-            // TODO(b/139246447): Assert no BOTTOM left.
-            CallSiteOptimizationInfo callSiteOptimizationInfo =
-                definition.getCallSiteOptimizationInfo();
-            return callSiteOptimizationInfo.hasUsefulOptimizationInfo(appView, definition);
-          },
-          method -> {
-            builder.add(method, currentGraphLens);
-            appView.options().testing.callSiteOptimizationInfoInspector.accept(method);
-          });
-    }
-    if (revisitedMethodsForTesting != null) {
-      revisitedMethodsForTesting.addAll(builder.build(appView));
-    }
-    return builder;
-  }
-
-  private synchronized boolean verifyAllProgramDispatchTargetsHaveBeenAbandoned(
-      InvokeMethod invoke, ProgramMethod context) {
-    ProgramMethodSet targets = invoke.lookupProgramDispatchTargets(appView, context);
-    if (targets != null) {
-      for (ProgramMethod target : targets) {
-        assert target.getDefinition().getCallSiteOptimizationInfo().isAbandoned()
-            : "Expected method `"
-                + target.toSourceString()
-                + "` to be marked as abandoned (called from `"
-                + invoke.toString()
-                + "` in `"
-                + context.toSourceString()
-                + "`)";
-      }
-    }
-    return true;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/AbandonedCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/AbandonedCallSiteOptimizationInfo.java
deleted file mode 100644
index a1dece6..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/AbandonedCallSiteOptimizationInfo.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2019, 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.ir.optimize.info;
-
-/**
- * Used to represent that nothing is known about the argument values of a given method and all of
- * its overrides.
- */
-public class AbandonedCallSiteOptimizationInfo extends CallSiteOptimizationInfo {
-
-  private static final AbandonedCallSiteOptimizationInfo INSTANCE =
-      new AbandonedCallSiteOptimizationInfo();
-
-  private AbandonedCallSiteOptimizationInfo() {}
-
-  static AbandonedCallSiteOptimizationInfo getInstance() {
-    return INSTANCE;
-  }
-
-  @Override
-  public boolean isAbandoned() {
-    return true;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/BottomCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/BottomCallSiteOptimizationInfo.java
deleted file mode 100644
index 14d9dc0..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/BottomCallSiteOptimizationInfo.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2019, 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.ir.optimize.info;
-
-// Nothing is known about arguments at call sites.
-public class BottomCallSiteOptimizationInfo extends CallSiteOptimizationInfo {
-
-  private static final BottomCallSiteOptimizationInfo INSTANCE =
-      new BottomCallSiteOptimizationInfo();
-
-  private BottomCallSiteOptimizationInfo() {}
-
-  static BottomCallSiteOptimizationInfo getInstance() {
-    return INSTANCE;
-  }
-
-  @Override
-  public boolean isBottom() {
-    return true;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
index ad274e9..3a9c9c0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
@@ -5,36 +5,18 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
-import com.android.tools.r8.ir.optimize.CallSiteOptimizationInfoPropagator;
 
 // A flat lattice structure:
 //   BOTTOM, TOP, and a lattice element that holds accumulated argument info.
 public abstract class CallSiteOptimizationInfo {
 
-  public static AbandonedCallSiteOptimizationInfo abandoned() {
-    return AbandonedCallSiteOptimizationInfo.getInstance();
-  }
-
-  public static BottomCallSiteOptimizationInfo bottom() {
-    return BottomCallSiteOptimizationInfo.getInstance();
-  }
-
   public static TopCallSiteOptimizationInfo top() {
     return TopCallSiteOptimizationInfo.getInstance();
   }
 
-  public boolean isAbandoned() {
-    return false;
-  }
-
-  public boolean isBottom() {
-    return false;
-  }
-
   public boolean isTop() {
     return false;
   }
@@ -49,13 +31,10 @@
 
   public CallSiteOptimizationInfo join(
       CallSiteOptimizationInfo other, AppView<?> appView, DexEncodedMethod method) {
-    if (isAbandoned() || other.isAbandoned()) {
-      return abandoned();
-    }
-    if (isBottom() || other.isTop()) {
+    if (other.isTop()) {
       return other;
     }
-    if (isTop() || other.isBottom()) {
+    if (isTop()) {
       return this;
     }
     assert isConcreteCallSiteOptimizationInfo() && other.isConcreteCallSiteOptimizationInfo();
@@ -63,19 +42,10 @@
         .join(other.asConcreteCallSiteOptimizationInfo(), appView, method);
   }
 
-  /**
-   * {@link CallSiteOptimizationInfoPropagator} will reprocess the call target if its collected call
-   * site optimization info has something useful that can trigger more optimizations. For example,
-   * if a certain argument is guaranteed to be definitely not null for all call sites, null-check on
-   * that argument can be simplified during the reprocessing of the method.
-   */
   public boolean hasUsefulOptimizationInfo(AppView<?> appView, DexEncodedMethod method) {
     return false;
   }
 
-  public final boolean hasUsefulOptimizationInfo(AppView<?> appView, ProgramMethod method) {
-    return hasUsefulOptimizationInfo(appView, method.getDefinition());
-  }
 
   // The index exactly matches with in values of invocation, i.e., even including receiver.
   public TypeElement getDynamicUpperBoundType(int argIndex) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 24d3ad4..7bc6c1c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -59,6 +59,11 @@
   }
 
   @Override
+  public CallSiteOptimizationInfo getArgumentInfos() {
+    return CallSiteOptimizationInfo.top();
+  }
+
+  @Override
   public ClassInlinerMethodConstraint getClassInlinerMethodConstraint() {
     return ClassInlinerMethodConstraint.alwaysFalse();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 08ef5d9..3de6a7a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -32,6 +32,8 @@
 
   public abstract boolean classInitializerMayBePostponed();
 
+  public abstract CallSiteOptimizationInfo getArgumentInfos();
+
   public abstract ClassInlinerMethodConstraint getClassInlinerMethodConstraint();
 
   public abstract EnumUnboxerMethodClassification getEnumUnboxerMethodClassification();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 06a25cf..7bda54d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -35,6 +35,7 @@
 public class MutableMethodOptimizationInfo extends MethodOptimizationInfo
     implements MutableOptimizationInfo {
 
+  private CallSiteOptimizationInfo argumentInfos = CallSiteOptimizationInfo.top();
   private Set<DexType> initializedClassesOnNormalExit =
       DefaultMethodOptimizationInfo.UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT;
   private int returnedArgument = DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT;
@@ -144,6 +145,7 @@
   // Copy constructor used to create a mutable copy. Do not forget to copy from template when a new
   // field is added.
   private MutableMethodOptimizationInfo(MutableMethodOptimizationInfo template) {
+    argumentInfos = template.argumentInfos;
     flags = template.flags;
     initializedClassesOnNormalExit = template.initializedClassesOnNormalExit;
     returnedArgument = template.returnedArgument;
@@ -162,7 +164,8 @@
 
   public MutableMethodOptimizationInfo fixup(
       AppView<AppInfoWithLiveness> appView, MethodOptimizationInfoFixer fixer) {
-    return fixupBridgeInfo(fixer)
+    return fixupArgumentInfos(fixer)
+        .fixupBridgeInfo(fixer)
         .fixupClassInlinerMethodConstraint(appView, fixer)
         .fixupEnumUnboxerMethodClassification(fixer)
         .fixupInstanceInitializerInfo(appView, fixer)
@@ -257,6 +260,23 @@
   }
 
   @Override
+  public CallSiteOptimizationInfo getArgumentInfos() {
+    return argumentInfos;
+  }
+
+  public MutableMethodOptimizationInfo fixupArgumentInfos(MethodOptimizationInfoFixer fixer) {
+    if (argumentInfos.isConcreteCallSiteOptimizationInfo()) {
+      argumentInfos =
+          fixer.fixupCallSiteOptimizationInfo(argumentInfos.asConcreteCallSiteOptimizationInfo());
+    }
+    return this;
+  }
+
+  void setArgumentInfos(CallSiteOptimizationInfo argumentInfos) {
+    this.argumentInfos = argumentInfos;
+  }
+
+  @Override
   public ClassInlinerMethodConstraint getClassInlinerMethodConstraint() {
     return classInlinerConstraint;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 77615e2..40a6cdd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -154,6 +154,10 @@
     // Ignored.
   }
 
+  public void setArgumentInfos(ProgramMethod method, CallSiteOptimizationInfo argumentInfos) {
+    method.getDefinition().getMutableOptimizationInfo().setArgumentInfos(argumentInfos);
+  }
+
   @Override
   public void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo) {
     method.getMutableOptimizationInfo().setBridgeInfo(bridgeInfo);
diff --git a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
index baa2951..9cd5455 100644
--- a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
@@ -31,7 +31,7 @@
     if (options.isMinifying()) {
       String renaming = getRenameSourceFileAttribute(options);
       if (renaming != null) {
-        return rewriteTo(renaming, isDefault(renaming, options));
+        return rewriteTo(renaming, isDefaultOrEmpty(renaming, options));
       }
     }
     return null;
@@ -40,7 +40,7 @@
   private static SourceFileProvider computeNonCompatProvider(InternalOptions options) {
     String renaming = getRenameSourceFileAttribute(options);
     if (renaming != null) {
-      return rewriteTo(renaming, isDefault(renaming, options));
+      return rewriteTo(renaming, isDefaultOrEmpty(renaming, options));
     }
     if (options.isMinifying() || options.isOptimizing()) {
       return rewriteToDefaultSourceFile(options.dexItemFactory());
@@ -52,8 +52,9 @@
     return options.getProguardConfiguration().getRenameSourceFileAttribute();
   }
 
-  public static boolean isDefault(String sourceFile, InternalOptions options) {
-    return options.dexItemFactory().defaultSourceFileAttributeString.equals(sourceFile);
+  public static boolean isDefaultOrEmpty(String sourceFile, InternalOptions options) {
+    return sourceFile.isEmpty()
+        || options.dexItemFactory().defaultSourceFileAttributeString.equals(sourceFile);
   }
 
   private static SourceFileProvider rewriteToDefaultSourceFile(DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
index e63eb5e..4f56738 100644
--- a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
@@ -43,7 +43,7 @@
 
   @Override
   protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
-    assert !previous.hasCastType();
+    assert !previous.hasReadCastType();
     assert !previous.hasReboundReference();
     return FieldLookupResult.builder(this)
         .setReference(previous.getReference())
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 0e6b99f..8d8962e 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -56,7 +56,7 @@
 
   @Override
   protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
-    assert !previous.hasCastType();
+    assert !previous.hasReadCastType();
     assert !previous.hasReboundReference();
     return FieldLookupResult.builder(this)
         .setReference(previous.getReference())
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index 8861f6c..62bf247 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -96,7 +96,7 @@
 
   @Override
   protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
-    assert !previous.hasCastType();
+    assert !previous.hasReadCastType();
     assert !previous.hasReboundReference();
     return FieldLookupResult.builder(this)
         .setReference(previous.getReference())
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index efa1985..772a404 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -79,7 +79,7 @@
           DexEncodedMethod::hasCode,
           method -> {
             CallSiteOptimizationInfo callSiteOptimizationInfo =
-                method.getDefinition().getCallSiteOptimizationInfo();
+                method.getOptimizationInfo().getArgumentInfos();
             if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo()
                 && !appView.appInfo().isNeverReprocessMethod(method)) {
               methodsToReprocessBuilder.add(method, currentGraphLens);
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
index 04afa1c..81df33e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.optimize.argumentpropagation;
 
+import static com.android.tools.r8.ir.optimize.info.OptimizationFeedback.getSimpleFeedback;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethodSignature;
@@ -227,9 +228,9 @@
     }
 
     ConcreteMonomorphicMethodState finalMethodState = widenedMethodState.asMonomorphic();
-    method
-        .getDefinition()
-        .setCallSiteOptimizationInfo(
+    getSimpleFeedback()
+        .setArgumentInfos(
+            method,
             ConcreteCallSiteOptimizationInfo.fromMethodState(appView, method, finalMethodState));
 
     // Strengthen the return value of the method if the method is known to return one of the
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 51120e1..8e7380d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -412,8 +412,7 @@
           // OK, this parameter can be removed.
           continue;
         }
-        CallSiteOptimizationInfo optimizationInfo =
-            method.getDefinition().getCallSiteOptimizationInfo();
+        CallSiteOptimizationInfo optimizationInfo = method.getOptimizationInfo().getArgumentInfos();
         if (optimizationInfo.isConcreteCallSiteOptimizationInfo()) {
           ConcreteCallSiteOptimizationInfo concreteOptimizationInfo =
               optimizationInfo.asConcreteCallSiteOptimizationInfo();
@@ -620,7 +619,7 @@
     private ArgumentInfoCollection computeParameterChangesForMethod(
         ProgramMethod method, IntPredicate removableParameterIndices) {
       ConcreteCallSiteOptimizationInfo optimizationInfo =
-          method.getDefinition().getCallSiteOptimizationInfo().asConcreteCallSiteOptimizationInfo();
+          method.getOptimizationInfo().getArgumentInfos().asConcreteCallSiteOptimizationInfo();
       if (optimizationInfo == null) {
         return ArgumentInfoCollection.empty();
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index 227cbf7..a5b539b 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -220,14 +220,6 @@
   }
 
   private void setMaxApiReferenceLevel(DexReference reference) {
-    if (reference.isDexMember()) {
-      maxApiReferenceLevel =
-          maxApiReferenceLevel.max(
-              apiLevelCompute.computeApiLevelForDefinition(
-                  reference.asDexMember(),
-                  appView.dexItemFactory(),
-                  apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
-    }
     maxApiReferenceLevel =
         maxApiReferenceLevel.max(
             apiLevelCompute.computeApiLevelForLibraryReference(
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 9f30d38..2121adc 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -517,6 +517,10 @@
     return appView.appInfo();
   }
 
+  public AndroidApiLevelCompute getApiLevelCompute() {
+    return apiLevelCompute;
+  }
+
   public Mode getMode() {
     return mode;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index d5c8c68..7292248 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -71,15 +71,17 @@
             ifRules.entrySet().iterator();
         while (it.hasNext()) {
           Map.Entry<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> ifRuleEntry = it.next();
-          ProguardIfRule ifRule = ifRuleEntry.getKey().get();
+          ProguardIfRule ifRuleKey = ifRuleEntry.getKey().get();
+          Set<ProguardIfRule> ifRulesInEquivalence = ifRuleEntry.getValue();
           ProguardIfRuleEvaluationData ifRuleEvaluationData =
               appView.options().testing.proguardIfRuleEvaluationData;
+          List<ProguardIfRule> toRemove = new ArrayList<>();
 
           // Depending on which types that trigger the -if rule, the application of the subsequent
           // -keep rule may vary (due to back references). So, we need to try all pairs of -if
           // rule and live types.
           for (DexProgramClass clazz :
-              ifRule.relevantCandidatesForRule(
+              ifRuleKey.relevantCandidatesForRule(
                   appView, subtypingInfo, appView.appInfo().classes())) {
             if (!isEffectivelyLive(clazz)) {
               continue;
@@ -89,21 +91,21 @@
             if (appView.options().testing.measureProguardIfRuleEvaluations) {
               ifRuleEvaluationData.numberOfProguardIfRuleClassEvaluations++;
             }
-            if (evaluateClassForIfRule(ifRule, clazz)) {
+            if (evaluateClassForIfRule(ifRuleKey, clazz)) {
               // When matching an if rule against a type, the if-rule are filled with the current
               // capture of wildcards. Propagate this down to member rules with same class part
               // equivalence.
-              ifRuleEntry
-                  .getValue()
-                  .removeIf(
-                      memberRule -> {
-                        registerClassCapture(memberRule, clazz, clazz);
-                        if (appView.options().testing.measureProguardIfRuleEvaluations) {
-                          ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
-                        }
-                        return evaluateIfRuleMembersAndMaterialize(memberRule, clazz, clazz)
-                            && canRemoveSubsequentKeepRule(memberRule);
-                      });
+              ifRulesInEquivalence.forEach(
+                  ifRule -> {
+                    registerClassCapture(ifRule, clazz, clazz);
+                    if (appView.options().testing.measureProguardIfRuleEvaluations) {
+                      ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
+                    }
+                    boolean matched = evaluateIfRuleMembersAndMaterialize(ifRule, clazz, clazz);
+                    if (matched && canRemoveSubsequentKeepRule(ifRule)) {
+                      toRemove.add(ifRule);
+                    }
+                  });
             }
 
             // Check if one of the types that have been merged into `clazz` satisfies the if-rule.
@@ -123,25 +125,26 @@
                 if (appView.options().testing.measureProguardIfRuleEvaluations) {
                   ifRuleEvaluationData.numberOfProguardIfRuleClassEvaluations++;
                 }
-                if (evaluateClassForIfRule(ifRule, sourceClass)) {
-                  ifRuleEntry
-                      .getValue()
-                      .removeIf(
-                          memberRule -> {
-                            registerClassCapture(memberRule, sourceClass, clazz);
-                            if (appView.options().testing.measureProguardIfRuleEvaluations) {
-                              ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
-                            }
-                            return evaluateIfRuleMembersAndMaterialize(
-                                    memberRule, sourceClass, clazz)
-                                && canRemoveSubsequentKeepRule(memberRule);
-                          });
+                if (evaluateClassForIfRule(ifRuleKey, sourceClass)) {
+                  ifRulesInEquivalence.forEach(
+                      ifRule -> {
+                        registerClassCapture(ifRule, sourceClass, clazz);
+                        if (appView.options().testing.measureProguardIfRuleEvaluations) {
+                          ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
+                        }
+                        if (evaluateIfRuleMembersAndMaterialize(ifRule, sourceClass, clazz)
+                            && canRemoveSubsequentKeepRule(ifRule)) {
+                          toRemove.add(ifRule);
+                        }
+                      });
                 }
               }
             }
           }
-          if (ifRuleEntry.getValue().isEmpty()) {
+          if (ifRulesInEquivalence.size() == toRemove.size()) {
             it.remove();
+          } else if (!toRemove.isEmpty()) {
+            ifRulesInEquivalence.removeAll(toRemove);
           }
         }
         ThreadUtils.awaitFutures(futures);
@@ -153,10 +156,7 @@
   }
 
   private boolean canRemoveSubsequentKeepRule(ProguardIfRule rule) {
-    // We cannot remove an if-rule if there is a kept graph consumer, otherwise we would not record
-    // all edges.
-    return Iterables.isEmpty(rule.subsequentRule.getWildcards())
-        && appView.options().keptGraphConsumer == null;
+    return Iterables.isEmpty(rule.subsequentRule.getWildcards());
   }
 
   /**
@@ -227,18 +227,27 @@
       return true;
     }
 
+    List<DexClassAndField> fieldsInlinedByJavaC = new ArrayList<>();
     Set<DexDefinition> filteredMembers = Sets.newIdentityHashSet();
     Iterables.addAll(
         filteredMembers,
         targetClass.fields(
-            f ->
-                // Fields referenced only by -keep may not be referenced, we therefore have to
-                // filter on both live and referenced.
-                (enqueuer.isFieldLive(f)
-                        || enqueuer.isFieldReferenced(f)
-                        || f.getOptimizationInfo().valueHasBeenPropagated())
-                    && appView.graphLens().getOriginalFieldSignature(f.getReference()).holder
-                        == sourceClass.type));
+            f -> {
+              // Fields that are javac inlined are unsound as predicates for conditional rules.
+              // Ignore any such field members and record it for possible reporting later.
+              if (isFieldInlinedByJavaC(f)) {
+                f.markAsInlinableByJavaC();
+                fieldsInlinedByJavaC.add(DexClassAndField.create(targetClass, f));
+                return false;
+              }
+              // Fields referenced only by -keep may not be referenced, we therefore have to
+              // filter on both live and referenced.
+              return (enqueuer.isFieldLive(f)
+                      || enqueuer.isFieldReferenced(f)
+                      || f.getOptimizationInfo().valueHasBeenPropagated())
+                  && (appView.graphLens().getOriginalFieldSignature(f.getReference()).holder
+                      == sourceClass.type);
+            }));
     Iterables.addAll(
         filteredMembers,
         targetClass.methods(
@@ -249,6 +258,21 @@
                     && appView.graphLens().getOriginalMethodSignature(m.getReference()).holder
                         == sourceClass.type));
 
+    // Check if the rule could hypothetically have matched a javac inlined field.
+    // If so mark the rule. Reporting happens only if the rule is otherwise unused.
+    if (!fieldsInlinedByJavaC.isEmpty()) {
+      for (ProguardMemberRule memberRule : memberKeepRules) {
+        if (!memberRule.getRuleType().includesFields()) {
+          continue;
+        }
+        for (DexClassAndField field : fieldsInlinedByJavaC) {
+          if (rootSetBuilder.sideEffectFreeIsRuleSatisfiedByField(memberRule, field)) {
+            rule.addInlinableFieldMatchingPrecondition(field.getReference());
+          }
+        }
+      }
+    }
+
     // If the number of member rules to hold is more than live members, we can't make it.
     if (filteredMembers.size() < memberKeepRules.size()) {
       return false;
@@ -258,6 +282,7 @@
     // -keep rule may vary (due to back references). So, we need to try literally all
     // combinations of live members. But, we can at least limit the number of elements per
     // combination as the size of member rules to satisfy.
+    // TODO(b/206086945): Consider ways of reducing the size of this set computation.
     for (Set<DexDefinition> combination :
         Sets.combinations(filteredMembers, memberKeepRules.size())) {
       Collection<DexClassAndField> fieldsInCombination =
@@ -287,6 +312,27 @@
     return false;
   }
 
+  private boolean isFieldInlinedByJavaC(DexEncodedField field) {
+    if (enqueuer.getMode().isFinalTreeShaking()) {
+      // Ignore any field value in the final tree shaking pass so it remains consistent with the
+      // initial pass.
+      return field.isInlinableByJavaC();
+    }
+    if (!field.isStatic() || !field.isFinal()) {
+      return false;
+    }
+    if (!field.hasExplicitStaticValue()) {
+      return false;
+    }
+    if (field.getType().isPrimitiveType()) {
+      return true;
+    }
+    if (field.getType() != appView.dexItemFactory().stringType) {
+      return false;
+    }
+    return field.getStaticValue().isDexValueString();
+  }
+
   private void materializeIfRule(ProguardIfRule rule, Set<DexReference> preconditions) {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     ProguardIfRule materializedRule = rule.materialize(dexItemFactory, preconditions);
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
index 0c54589..140b86b 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
@@ -24,8 +24,11 @@
     return bottom().joiner();
   }
 
+  private final boolean allowFieldTypeStrengthening;
+
   private KeepFieldInfo(Builder builder) {
     super(builder);
+    this.allowFieldTypeStrengthening = builder.isFieldTypeStrengtheningAllowed();
   }
 
   // This builder is not private as there are known instances where it is safe to modify keep info
@@ -35,6 +38,14 @@
     return new Builder(this);
   }
 
+  public boolean isFieldTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
+    return internalIsFieldTypeStrengtheningAllowed();
+  }
+
+  boolean internalIsFieldTypeStrengtheningAllowed() {
+    return allowFieldTypeStrengthening;
+  }
+
   public Joiner joiner() {
     assert !isTop();
     return new Joiner(this);
@@ -52,12 +63,32 @@
 
   public static class Builder extends KeepInfo.Builder<Builder, KeepFieldInfo> {
 
+    private boolean allowFieldTypeStrengthening;
+
     private Builder() {
       super();
     }
 
     private Builder(KeepFieldInfo original) {
       super(original);
+      allowFieldTypeStrengthening = original.internalIsFieldTypeStrengtheningAllowed();
+    }
+
+    public boolean isFieldTypeStrengtheningAllowed() {
+      return allowFieldTypeStrengthening;
+    }
+
+    public Builder setAllowFieldTypeStrengthening(boolean allowFieldTypeStrengthening) {
+      this.allowFieldTypeStrengthening = allowFieldTypeStrengthening;
+      return self();
+    }
+
+    public Builder allowFieldTypeStrengthening() {
+      return setAllowFieldTypeStrengthening(true);
+    }
+
+    public Builder disallowFieldTypeStrengthening() {
+      return setAllowFieldTypeStrengthening(false);
     }
 
     @Override
@@ -92,6 +123,11 @@
       super(info.builder());
     }
 
+    public Joiner disallowFieldTypeStrengthening() {
+      builder.disallowFieldTypeStrengthening();
+      return self();
+    }
+
     @Override
     public Joiner asFieldJoiner() {
       return this;
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index 12265aa..598f6a7 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -239,7 +239,7 @@
 
     abstract boolean isEqualTo(K other);
 
-    private K original;
+    protected K original;
     private boolean allowAccessModification;
     private boolean allowAnnotationRemoval;
     private boolean allowMinification;
@@ -439,14 +439,14 @@
 
     abstract J self();
 
-    final Builder<B, K> builder;
+    final B builder;
 
     // The set of rules that have contributed to this joiner. These are only needed for the
     // interpretation of keep rules into keep info, and is therefore not stored in the keep info
     // builder above.
     final Set<ProguardKeepRuleBase> rules = Sets.newIdentityHashSet();
 
-    Joiner(Builder<B, K> builder) {
+    Joiner(B builder) {
       this.builder = builder;
     }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/NoFieldTypeStrengtheningRule.java b/src/main/java/com/android/tools/r8/shaking/NoFieldTypeStrengtheningRule.java
new file mode 100644
index 0000000..80cf0db
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoFieldTypeStrengtheningRule.java
@@ -0,0 +1,84 @@
+// 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoFieldTypeStrengtheningRule extends ProguardConfigurationRule {
+
+  public static final String RULE_NAME = "nofieldtypestrengthening";
+
+  public static class Builder
+      extends ProguardConfigurationRule.Builder<NoFieldTypeStrengtheningRule, Builder> {
+
+    private Builder() {
+      super();
+    }
+
+    @Override
+    public NoFieldTypeStrengtheningRule.Builder self() {
+      return this;
+    }
+
+    @Override
+    public NoFieldTypeStrengtheningRule build() {
+      return new NoFieldTypeStrengtheningRule(
+          origin,
+          getPosition(),
+          source,
+          buildClassAnnotations(),
+          classAccessFlags,
+          negatedClassAccessFlags,
+          classTypeNegated,
+          classType,
+          classNames,
+          buildInheritanceAnnotations(),
+          inheritanceClassName,
+          inheritanceIsExtends,
+          memberRules);
+    }
+  }
+
+  private NoFieldTypeStrengtheningRule(
+      Origin origin,
+      Position position,
+      String source,
+      List<ProguardTypeMatcher> classAnnotations,
+      ProguardAccessFlags classAccessFlags,
+      ProguardAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      ProguardClassNameList classNames,
+      List<ProguardTypeMatcher> inheritanceAnnotations,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      List<ProguardMemberRule> memberRules) {
+    super(
+        origin,
+        position,
+        source,
+        classAnnotations,
+        classAccessFlags,
+        negatedClassAccessFlags,
+        classTypeNegated,
+        classType,
+        classNames,
+        inheritanceAnnotations,
+        inheritanceClassName,
+        inheritanceIsExtends,
+        memberRules);
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  String typeString() {
+    return RULE_NAME;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index f86a251..b92040b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -480,6 +480,11 @@
           configurationBuilder.addRule(rule);
           return true;
         }
+        if (acceptString(NoFieldTypeStrengtheningRule.RULE_NAME)) {
+          ProguardConfigurationRule rule = parseNoFieldTypeStrengtheningRule(optionStart);
+          configurationBuilder.addRule(rule);
+          return true;
+        }
         if (acceptString(NoUnusedInterfaceRemovalRule.RULE_NAME)) {
           ProguardConfigurationRule rule = parseNoUnusedInterfaceRemovalRule(optionStart);
           configurationBuilder.addRule(rule);
@@ -779,6 +784,17 @@
       return keepRuleBuilder.build();
     }
 
+    private NoFieldTypeStrengtheningRule parseNoFieldTypeStrengtheningRule(Position start)
+        throws ProguardRuleParserException {
+      NoFieldTypeStrengtheningRule.Builder keepRuleBuilder =
+          NoFieldTypeStrengtheningRule.builder().setOrigin(origin).setStart(start);
+      parseClassSpec(keepRuleBuilder, false);
+      Position end = getPosition();
+      keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+      keepRuleBuilder.setEnd(end);
+      return keepRuleBuilder.build();
+    }
+
     private NoUnusedInterfaceRemovalRule parseNoUnusedInterfaceRemovalRule(Position start)
         throws ProguardRuleParserException {
       NoUnusedInterfaceRemovalRule.Builder keepRuleBuilder =
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index 8313325..f61b4ee 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.origin.Origin;
@@ -10,7 +11,9 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
 public class ProguardIfRule extends ProguardKeepRuleBase {
@@ -34,6 +37,8 @@
   private final Set<DexReference> preconditions;
   final ProguardKeepRule subsequentRule;
 
+  private Map<DexField, DexField> inlinableFieldsInPrecondition = new ConcurrentHashMap<>();
+
   public Set<DexReference> getPreconditions() {
     return preconditions;
   }
@@ -42,6 +47,16 @@
     return subsequentRule;
   }
 
+  public void addInlinableFieldMatchingPrecondition(DexField field) {
+    inlinableFieldsInPrecondition.put(field, field);
+  }
+
+  public Set<DexField> getAndClearInlinableFieldsMatchingPrecondition() {
+    Set<DexField> fields = inlinableFieldsInPrecondition.keySet();
+    inlinableFieldsInPrecondition = null;
+    return fields;
+  }
+
   public static class Builder extends ProguardKeepRuleBase.Builder<ProguardIfRule, Builder> {
 
     ProguardKeepRule subsequentRule = null;
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 03cff10..3644e3d 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -10,6 +10,7 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.AssumeNoSideEffectsRuleForObjectMembersDiagnostic;
+import com.android.tools.r8.errors.InlinableStaticFinalFieldPreconditionDiagnostic;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -262,6 +263,8 @@
         markMatchingOverriddenMethods(
             appView.appInfo(), clazz, memberKeepRules, rule, null, true, ifRule);
         markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
+      } else if (rule instanceof NoFieldTypeStrengtheningRule) {
+        markMatchingFields(clazz, memberKeepRules, rule, null, ifRule);
       } else if (rule instanceof InlineRule
           || rule instanceof ConstantArgumentRule
           || rule instanceof UnusedArgumentRule
@@ -957,6 +960,10 @@
       return false;
     }
 
+    boolean sideEffectFreeIsRuleSatisfiedByField(ProguardMemberRule rule, DexClassAndField field) {
+      return rule.matches(field, appView, ignore -> {}, dexStringCache);
+    }
+
     static AnnotationMatchResult containsAllAnnotations(
         List<ProguardTypeMatcher> annotationMatchers, DexClass clazz) {
       return containsAllAnnotations(
@@ -1220,6 +1227,13 @@
             throw new Unreachable();
         }
         context.markAsUsed();
+      } else if (context instanceof NoFieldTypeStrengtheningRule) {
+        assert item.isProgramField();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asFieldJoiner()
+            .disallowFieldTypeStrengthening();
+        context.markAsUsed();
       } else if (context instanceof NoUnusedInterfaceRemovalRule) {
         noUnusedInterfaceRemoval.add(item.asClass().type);
         context.markAsUsed();
@@ -1675,17 +1689,26 @@
 
     public void checkAllRulesAreUsed(InternalOptions options) {
       List<ProguardConfigurationRule> rules = options.getProguardConfiguration().getRules();
-      if (rules != null) {
-        for (ProguardConfigurationRule rule : rules) {
-          if (!rule.isUsed()) {
-            String message =
-                "Proguard configuration rule does not match anything: `" + rule.toString() + "`";
-            StringDiagnostic diagnostic = new StringDiagnostic(message, rule.getOrigin());
-            if (options.testing.reportUnusedProguardConfigurationRules) {
-              options.reporter.info(diagnostic);
-            }
+      if (rules == null) {
+        return;
+      }
+      for (ProguardConfigurationRule rule : rules) {
+        if (rule.isProguardIfRule()) {
+          ProguardIfRule ifRule = rule.asProguardIfRule();
+          Set<DexField> unorderedFields = ifRule.getAndClearInlinableFieldsMatchingPrecondition();
+          if (!unorderedFields.isEmpty()) {
+            List<DexField> fields = new ArrayList<>(unorderedFields);
+            fields.sort(DexField::compareTo);
+            options.reporter.warning(
+                new InlinableStaticFinalFieldPreconditionDiagnostic(ifRule, fields));
+            continue;
           }
         }
+        if (!rule.isUsed() && options.testing.reportUnusedProguardConfigurationRules) {
+          String message = "Proguard configuration rule does not match anything: `" + rule + "`";
+          StringDiagnostic diagnostic = new StringDiagnostic(message, rule.getOrigin());
+          options.reporter.info(diagnostic);
+        }
       }
     }
 
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 9396f50..9822a54 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.ir.code.Invoke.Type.DIRECT;
 import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
+import static com.android.tools.r8.utils.AndroidApiLevelUtils.getApiReferenceLevelForMerging;
 
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
 import com.android.tools.r8.androidapi.ComputedApiLevel;
@@ -531,8 +532,10 @@
     // Only merge if api reference level of source class is equal to target class. The check is
     // somewhat expensive.
     if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
-      ComputedApiLevel sourceApiLevel = sourceClass.getApiReferenceLevel(appView, apiLevelCompute);
-      ComputedApiLevel targetApiLevel = targetClass.getApiReferenceLevel(appView, apiLevelCompute);
+      ComputedApiLevel sourceApiLevel =
+          getApiReferenceLevelForMerging(appView, apiLevelCompute, sourceClass);
+      ComputedApiLevel targetApiLevel =
+          getApiReferenceLevelForMerging(appView, apiLevelCompute, targetClass);
       if (!sourceApiLevel.equals(targetApiLevel)) {
         if (Log.ENABLED) {
           AbortReason.API_REFERENCE_LEVEL.printLogMessageForClass(sourceClass);
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 73cf09f..089ccce 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -6,6 +6,9 @@
 
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
 import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.LibraryMethod;
 import com.android.tools.r8.graph.ProgramMethod;
 
@@ -19,12 +22,38 @@
     if (caller.getHolderType() == inlinee.getHolderType()) {
       return true;
     }
+    // For inlining we only measure if the code has invokes into the library.
     return caller
         .getDefinition()
-        .getApiLevel()
+        .getApiLevelForCode()
         .isGreaterThanOrEqualTo(inlinee.getDefinition().getApiLevelForCode());
   }
 
+  public static ComputedApiLevel getApiReferenceLevelForMerging(
+      AppView<?> appView, AndroidApiLevelCompute apiLevelCompute, DexProgramClass clazz) {
+    // The api level of a class is the max level of it's members, super class and interfaces.
+    return getMembersApiReferenceLevelForMerging(
+        clazz,
+        apiLevelCompute.computeApiLevelForDefinition(
+            clazz.allImmediateSupertypes(), apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
+  }
+
+  private static ComputedApiLevel getMembersApiReferenceLevelForMerging(
+      DexProgramClass clazz, ComputedApiLevel memberLevel) {
+    // Based on b/138781768#comment57 there is almost no penalty for having an unknown reference
+    // as long as we are not invoking or accessing a field on it. Therefore we can disregard static
+    // types of fields and only consider method code api levels.
+    for (DexEncodedMethod method : clazz.methods()) {
+      if (method.hasCode()) {
+        memberLevel = memberLevel.max(method.getApiLevelForCode());
+      }
+      if (memberLevel.isUnknownApiLevel()) {
+        return memberLevel;
+      }
+    }
+    return memberLevel;
+  }
+
   public static boolean isApiSafeForMemberRebinding(
       LibraryMethod method,
       AndroidApiLevelCompute androidApiLevelCompute,
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 1cbbc21..de70549 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -735,6 +735,9 @@
 
   public LineNumberOptimization lineNumberOptimization = LineNumberOptimization.ON;
 
+  // TODO(b/207765416): Enable and remove this once fixed.
+  public boolean enablePcBasedMappingFile = false;
+
   public CallSiteOptimizationOptions callSiteOptimizationOptions() {
     return callSiteOptimizationOptions;
   }
@@ -1863,7 +1866,9 @@
   }
 
   public boolean canUseDexPcAsDebugInformation() {
-    return lineNumberOptimization == LineNumberOptimization.ON && hasMinApi(AndroidApiLevel.O);
+    return enablePcBasedMappingFile
+        && lineNumberOptimization == LineNumberOptimization.ON
+        && hasMinApi(AndroidApiLevel.O);
   }
 
   public boolean isInterfaceMethodDesugaringEnabled() {
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index 94ceeb4..3cd4941 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -61,10 +61,11 @@
     KOTLINC_1_3_72("kotlin-compiler-1.3.72"),
     KOTLINC_1_4_20("kotlin-compiler-1.4.20"),
     KOTLINC_1_5_0("kotlin-compiler-1.5.0"),
+    KOTLINC_1_6_0("kotlin-compiler-1.6.0"),
     KOTLIN_DEV("kotlin-compiler-dev");
 
     public static final KotlinCompilerVersion MIN_SUPPORTED_VERSION = KOTLINC_1_4_20;
-    public static final KotlinCompilerVersion MAX_SUPPORTED_VERSION = KOTLINC_1_5_0;
+    public static final KotlinCompilerVersion MAX_SUPPORTED_VERSION = KOTLINC_1_6_0;
 
     private final String folder;
 
@@ -79,6 +80,15 @@
     public KotlinCompiler getCompiler() {
       return new KotlinCompiler(this);
     }
+
+    public static List<KotlinCompilerVersion> getSupported() {
+      return Arrays.stream(KotlinCompilerVersion.values())
+          .filter(
+              compiler ->
+                  compiler.isGreaterThanOrEqualTo(MIN_SUPPORTED_VERSION)
+                      && compiler.isLessThanOrEqualTo(MAX_SUPPORTED_VERSION))
+          .collect(Collectors.toList());
+    }
   }
 
   public static final class KotlinCompiler {
diff --git a/src/test/java/com/android/tools/r8/KotlinTestParameters.java b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
index 7996dc4..2ac3f87 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestParameters.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import static org.junit.Assume.assumeTrue;
+
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
@@ -69,6 +71,8 @@
     private Predicate<KotlinTargetVersion> targetVersionFilter = t -> false;
     private boolean withDevCompiler =
         System.getProperty("com.android.tools.r8.kotlincompilerdev") != null;
+    private boolean withOldCompilers =
+        System.getProperty("com.android.tools.r8.kotlincompilerold") != null;
 
     private Builder() {}
 
@@ -101,6 +105,11 @@
       return this;
     }
 
+    public Builder withOldCompilersIfSet() {
+      assumeTrue(withOldCompilers);
+      return this;
+    }
+
     public Builder withAllTargetVersions() {
       withTargetVersionFilter(t -> t != KotlinTargetVersion.NONE);
       return this;
@@ -126,10 +135,16 @@
       List<KotlinCompilerVersion> compilerVersions;
       if (withDevCompiler) {
         compilerVersions = ImmutableList.of(KotlinCompilerVersion.KOTLIN_DEV);
-      } else {
+      } else if (withOldCompilers) {
         compilerVersions =
             Arrays.stream(KotlinCompilerVersion.values())
-                .filter(c -> c != KotlinCompilerVersion.KOTLIN_DEV && compilerFilter.test(c))
+                .filter(c -> c.isLessThan(KotlinCompilerVersion.MIN_SUPPORTED_VERSION))
+                .collect(Collectors.toList());
+
+      } else {
+        compilerVersions =
+            KotlinCompilerVersion.getSupported().stream()
+                .filter(c -> compilerFilter.test(c))
                 .collect(Collectors.toList());
       }
       for (KotlinCompilerVersion kotlinVersion : compilerVersions) {
diff --git a/src/test/java/com/android/tools/r8/NoFieldTypeStrengthening.java b/src/test/java/com/android/tools/r8/NoFieldTypeStrengthening.java
new file mode 100644
index 0000000..af7d5bd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoFieldTypeStrengthening.java
@@ -0,0 +1,11 @@
+// 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.FIELD})
+public @interface NoFieldTypeStrengthening {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 6c8ac51..92270ae 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.CollectingGraphConsumer;
+import com.android.tools.r8.shaking.NoFieldTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoHorizontalClassMergingRule;
 import com.android.tools.r8.shaking.NoUnusedInterfaceRemovalRule;
 import com.android.tools.r8.shaking.NoVerticalClassMergingRule;
@@ -27,6 +28,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Pair;
 import java.io.IOException;
+import java.lang.annotation.Annotation;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -429,6 +431,11 @@
         .addInternalKeepRules("-neverclassinline @com.android.tools.r8.NeverClassInline class *");
   }
 
+  T addInternalMatchAnnotationOnFieldRule(String name, Class<? extends Annotation> annotation) {
+    return addInternalKeepRules(
+        "-" + name + " class * { @" + annotation.getTypeName() + " <fields>; }");
+  }
+
   T addInternalMatchInterfaceRule(String name, Class<?> matchInterface) {
     return addInternalKeepRules("-" + name + " @" + matchInterface.getTypeName() + " class *");
   }
@@ -489,6 +496,12 @@
     return addOptionsModification(options -> options.testing.allowInliningOfSynthetics = false);
   }
 
+  public T enableNoFieldTypeStrengtheningAnnotations() {
+    return addNoFieldTypeStrengtheningAnnotation()
+        .addInternalMatchAnnotationOnFieldRule(
+            NoFieldTypeStrengtheningRule.RULE_NAME, NoFieldTypeStrengthening.class);
+  }
+
   public T enableNoUnusedInterfaceRemovalAnnotations() {
     return addNoUnusedInterfaceRemovalAnnotations()
         .addInternalMatchInterfaceRule(
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index 04eb3c8..c53ec8c 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -88,15 +88,15 @@
   public abstract TestDiagnosticMessages assertErrorsCount(int count);
 
   public final TestDiagnosticMessages assertNoInfos() {
-    return assertInfosCount(0);
+    return assertInfosMatch(Collections.emptyList());
   }
 
   public final TestDiagnosticMessages assertNoWarnings() {
-    return assertWarningsCount(0);
+    return assertWarningsMatch(Collections.emptyList());
   }
 
   public final TestDiagnosticMessages assertNoErrors() {
-    return assertErrorsCount(0);
+    return assertErrorsMatch(Collections.emptyList());
   }
 
   // Match exact.
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index acd96aa..7fc1ecc 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -119,6 +119,24 @@
     return addKeepRules("-dontshrink");
   }
 
+  public T addDontNote(Class<?>... classes) {
+    for (Class<?> clazz : classes) {
+      addDontNote(clazz.getTypeName());
+    }
+    return self();
+  }
+
+  public T addDontNote(Collection<String> classes) {
+    for (String clazz : classes) {
+      addKeepRules("-dontnote " + clazz);
+    }
+    return self();
+  }
+
+  public T addDontNote(String... classes) {
+    return addDontNote(Arrays.asList(classes));
+  }
+
   public T addDontWarn(Class<?>... classes) {
     for (Class<?> clazz : classes) {
       addDontWarn(clazz.getTypeName());
@@ -440,6 +458,10 @@
     return addTestingAnnotation(NeverSingleCallerInline.class);
   }
 
+  public final T addNoFieldTypeStrengtheningAnnotation() {
+    return addTestingAnnotation(NoFieldTypeStrengthening.class);
+  }
+
   public final T addNoHorizontalClassMergingAnnotations() {
     return addTestingAnnotation(NoHorizontalClassMerging.class);
   }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
index 7c4226c..c41c5e2 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
@@ -44,14 +44,7 @@
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .addHorizontallyMergedClassesInspector(
-            inspector -> {
-              if (parameters.isDexRuntime()
-                  && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
-                inspector.assertClassesMerged(A.class, B.class);
-              } else {
-                inspector.assertNoClassesMerged();
-              }
-            })
+            inspector -> inspector.assertClassesMerged(A.class, B.class))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .apply(ApiModelingTestHelper::disableCheckAllApiReferencesAreNotUnknown)
         .apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldTypeReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldTypeReferenceTest.java
index 149c93e..302cca5 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldTypeReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldTypeReferenceTest.java
@@ -53,9 +53,9 @@
                   if (Reference.methodFromMethod(readApiField).equals(method)
                       || Reference.methodFromMethod(setApiField).equals(method)) {
                     if (parameters.isCfRuntime()) {
-                      assertEquals(AndroidApiLevel.L_MR1, apiLevel);
+                      assertEquals(AndroidApiLevel.B, apiLevel);
                     } else {
-                      assertEquals(AndroidApiLevel.L_MR1.max(parameters.getApiLevel()), apiLevel);
+                      assertEquals(AndroidApiLevel.B.max(parameters.getApiLevel()), apiLevel);
                     }
                   }
                 }))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMethodTypeReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMethodTypeReferenceTest.java
index d3d9572..c4308ed 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMethodTypeReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMethodTypeReferenceTest.java
@@ -53,9 +53,9 @@
                   if (Reference.methodFromMethod(readApi).equals(method)
                       || Reference.methodFromMethod(setApi).equals(method)) {
                     if (parameters.isCfRuntime()) {
-                      assertEquals(AndroidApiLevel.L_MR1, apiLevel);
+                      assertEquals(AndroidApiLevel.B, apiLevel);
                     } else {
-                      assertEquals(AndroidApiLevel.L_MR1.max(parameters.getApiLevel()), apiLevel);
+                      assertEquals(AndroidApiLevel.B.max(parameters.getApiLevel()), apiLevel);
                     }
                   }
                 }))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
new file mode 100644
index 0000000..ef4737d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
@@ -0,0 +1,94 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.reflect.Method;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelTypeReferenceInvokeTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    Method apiMethod = LibraryClass.class.getDeclaredMethod("apiMethod");
+    Method apiCaller = ApiHelper.class.getDeclaredMethod("apiCaller", LibraryClass.class);
+    Method apiCallerCaller = Main.class.getDeclaredMethod("typeReference", Object.class);
+    boolean libraryClassOnBoot =
+        parameters.isDexRuntime()
+            && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.M);
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class, ApiHelper.class)
+        .addLibraryClasses(LibraryClass.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .apply(setMockApiLevelForClass(LibraryClass.class, AndroidApiLevel.M))
+        .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.M))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .addAndroidBuildVersion()
+        .compile()
+        .inspect(
+            verifyThat(parameters, apiCaller)
+                .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.M))
+        .addRunClasspathClasses(LibraryClass.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLinesIf(
+            libraryClassOnBoot, "LibraryClass::apiMethod", "Hello World")
+        .assertSuccessWithOutputLinesIf(!libraryClassOnBoot, "Hello World");
+  }
+
+  public static class LibraryClass {
+
+    public void apiMethod() {
+      System.out.println("LibraryClass::apiMethod");
+    }
+  }
+
+  public static class ApiHelper {
+
+    public static void apiCaller(LibraryClass libraryClass) {
+      if (AndroidBuildVersion.VERSION >= 23) {
+        libraryClass.apiMethod();
+      }
+    }
+  }
+
+  public static class Main {
+
+    @NeverInline
+    public static void typeReference(Object object) {
+      if (object instanceof LibraryClass) {
+        ApiHelper.apiCaller((LibraryClass) object);
+      }
+    }
+
+    public static void main(String[] args) {
+      typeReference(args.length == 0 ? new LibraryClass() : "Foo");
+      System.out.println("Hello World");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 5232896..59d408f 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command.NopCommand;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.naming.ClassNameMapper;
@@ -390,6 +391,11 @@
     return stepInto(DEFAULT_FILTER);
   }
 
+  protected final JUnit3Wrapper.Command applyIf(
+      boolean condition, Supplier<JUnit3Wrapper.Command> action) {
+    return condition ? action.get() : new NopCommand();
+  }
+
   protected final JUnit3Wrapper.Command stepInto(StepFilter stepFilter) {
     return step(StepKind.INTO, stepFilter);
   }
@@ -1763,6 +1769,14 @@
         }
       }
 
+      class NopCommand implements Command {
+
+        @Override
+        public void perform(JUnit3Wrapper testBase) {
+          // Intentionally empty - it is a nop.
+        }
+      }
+
       // Break on exceptions thrown in className.methodName.
       class BreakOnExceptionCommand implements Command {
         private static final int ALL_EXCEPTIONS = 0;
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
index d205f26..4d44f78 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
@@ -6,6 +6,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
@@ -335,6 +336,10 @@
         checkLocals(left_mangledLvName, right_mangledLvName),
         // Enter "foo"
         stepInto(),
+        // TODO(b/207743106): Remove when resolved.
+        applyIf(
+            kotlinParameters.isNewerThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_6_0),
+            this::stepInto),
         checkMethod(DEBUGGEE_CLASS, "foo"),
         checkLine(SOURCE_FILE, 34),
         stepOut(),
@@ -384,6 +389,10 @@
         checkNoLocal(inlinee2_lambda2_inlineScope),
         // Enter the call to "foo"
         stepInto(),
+        // TODO(b/207743106): Remove when resolved.
+        applyIf(
+            kotlinParameters.isNewerThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_6_0),
+            this::stepInto),
         checkMethod(DEBUGGEE_CLASS, "foo"),
         checkLine(SOURCE_FILE, 34),
         run());
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
index f93bfb4..ebba822 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -14,6 +14,8 @@
 import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersBuilder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -53,6 +55,7 @@
     KotlinCompiler compiler = kotlinTestParameters.getCompiler();
     testForR8(parameters.getBackend())
         .addProgramFiles(compiler.getKotlinStdlibJar(), compiler.getKotlinAnnotationJar())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
         .addKeepAllAttributes()
         .allowDiagnosticWarningMessages()
         .noMinification()
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
index 37d2b6e..f194b99 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
@@ -70,6 +70,7 @@
         .addKeepAttributeLineNumberTable()
         .addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
         .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
         .run(parameters.getRuntime(), MAIN)
         .assertFailureWithErrorThatMatches(containsString(EXPECTED))
         .inspectOriginalStackTrace(
diff --git a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
index 30505ab..14c8504 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
@@ -62,6 +62,7 @@
         .addProgramClasses(MAIN)
         .setMinApi(parameters.getApiLevel())
         .internalEnableMappingOutput()
+        .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
         .run(parameters.getRuntime(), MAIN)
         // For a debug build we always expect the output to have actual line information.
         .inspectFailure(this::checkHasLineNumberInfo)
@@ -75,6 +76,7 @@
         .addProgramClasses(MAIN)
         .setMinApi(parameters.getApiLevel())
         .internalEnableMappingOutput()
+        .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
         // TODO(b/191038746): Enable LineNumberOptimization for release builds for DEX PC Output.
         .applyIf(
             apiLevelSupportsPcOutput(),
@@ -113,6 +115,7 @@
         .release()
         .addProgramClasses(MAIN)
         .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
         .run(parameters.getRuntime(), MAIN)
         // If compiling without a map output actual debug info should also be retained. Otherwise
         // there would not be any way to obtain the actual lines.
@@ -128,6 +131,7 @@
         .addKeepMainRule(MAIN)
         .addKeepAttributeLineNumberTable()
         .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
         .run(parameters.getRuntime(), MAIN)
         .inspectOriginalStackTrace(
             (stackTrace, inspector) -> {
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java
index 64ed426..f83d077 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java
@@ -99,6 +99,30 @@
   }
 
   @Test
+  public void testManuallySetEmptySourceFile() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .addKeepAttributeSourceFile()
+        .addKeepAttributeLineNumberTable()
+        .addKeepRules("-renamesourcefileattribute")
+        .enableInliningAnnotations()
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(NullPointerException.class)
+        .inspectStackTrace(
+            (stackTrace, inspector) -> {
+              assertThat(stackTrace, isSame(expectedStackTrace));
+              ClassSubject mainSubject = inspector.clazz(Main.class);
+              assertThat(mainSubject, isPresent());
+              assertThat(mainSubject.uniqueMethodWithName("inlinee"), not(isPresent()));
+              assertThat(
+                  mainSubject.uniqueMethodWithName("shouldRemoveLineNumberForInline"),
+                  notIf(hasLineNumberTable(), parameters.isDexRuntime()));
+            });
+  }
+
+  @Test
   public void testNonDefaultSourceFile() throws Exception {
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleInlineTest.java b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleInlineTest.java
index bc0d012..351b1e8 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleInlineTest.java
@@ -60,6 +60,7 @@
         .addKeepAttributeSourceFile()
         .addKeepAttributeLineNumberTable()
         .enableInliningAnnotations()
+        .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
         .run(parameters.getRuntime(), Main.class)
         .assertFailureWithErrorThatThrows(NullPointerException.class)
         .inspectStackTrace(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
index f2c3fad..d07e819 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
@@ -66,7 +66,7 @@
     assert method.getReference().name.toString().equals("m")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     if (method.getHolderType().toSourceString().endsWith("$C")) {
       assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
     } else {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
index cedf279..eb665f8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
@@ -61,7 +61,7 @@
     assert method.getReference().name.toString().equals("m")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     if (method.getHolderType().toSourceString().endsWith("$C")) {
       assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
     } else {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java
index 0a68ca4..ee612f8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java
@@ -57,7 +57,7 @@
     assert method.getReference().name.toString().equals("test")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
     assert callSiteOptimizationInfo.getAbstractArgumentValue(1).isUnknown();
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java
index 8636291..fed41d3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java
@@ -63,7 +63,7 @@
     assert method.getReference().name.toString().equals("m")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
     assert callSiteOptimizationInfo.getAbstractArgumentValue(1).isUnknown();
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
index 7ed853e..042ca95 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
@@ -63,7 +63,7 @@
     assert method.getReference().name.toString().equals("m")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
     AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(1);
     assert abstractValue.isSingleStringValue()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java
index ac071d2..4c1b255 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java
@@ -55,7 +55,7 @@
     assert method.getReference().name.toString().equals("test")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull();
     assert callSiteOptimizationInfo.getAbstractArgumentValue(0).isUnknown();
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java
index 10df35d..ba515db5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java
@@ -60,7 +60,7 @@
     assert methodName.equals("m") || methodName.equals("test")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     if (methodName.equals("m")) {
       assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
       assert callSiteOptimizationInfo.getAbstractArgumentValue(1).isUnknown();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
index 6eef61d..bbe45f0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
@@ -59,7 +59,7 @@
     assert method.getReference().name.toString().equals("m")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
     AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(1);
     if (method.getHolderType().toSourceString().endsWith("$A")) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
index 148d988..86a89ed 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
@@ -57,7 +57,7 @@
     assert method.getReference().name.toString().equals("test")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
     assert upperBoundType.isDefinitelyNotNull();
     assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
index 6bb75b3..5b7fa84 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
@@ -60,7 +60,7 @@
     assert methodName.equals("<init>") || methodName.equals("test")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     TypeElement upperBoundType;
     if (methodName.equals("test")) {
       upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
index e77cbaa..4c72b52 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
@@ -63,7 +63,7 @@
     assert method.getReference().name.toString().equals("m")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
     assert upperBoundType.isDefinitelyNotNull();
     assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
index 045742d..0c4b99a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
@@ -65,7 +65,7 @@
     assert method.getReference().name.toString().equals("m")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
     assert upperBoundType.isDefinitelyNotNull();
     if (method.getHolderType().toSourceString().endsWith("$A")) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
index 2362fd6..0b46131 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
@@ -55,7 +55,7 @@
     assert method.getReference().name.toString().equals("test")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(0);
     assert upperBoundType.isDefinitelyNotNull();
     assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
index 027d715..dae8e30 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
@@ -58,7 +58,7 @@
     assert methodName.equals("<init>") || methodName.equals("test")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     // `arg` for `test` or the receiver of `Base#<init>`.
     // TODO(b/139246447): should avoid visiting <init>, which is trivial, default init!
     // For testing purpose, `Base` is not merged and kept. The system correctly caught that, when
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
index 2b666d2..1952e51 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
@@ -60,7 +60,7 @@
     assert methodName.equals("m") || methodName.equals("test")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     if (methodName.equals("m")) {
       TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
       assert upperBoundType.isDefinitelyNotNull();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
index 56fdde5..e863304 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
@@ -60,7 +60,7 @@
     assert methodName.equals("<init>") || methodName.equals("m")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     TypeElement upperBoundType;
     if (methodName.equals("m")) {
       upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
index 8a88c46..dc8820b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
@@ -56,7 +56,7 @@
     assert method.getReference().name.toString().equals("test")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
index 7624d2a..f989c9f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
@@ -63,7 +63,7 @@
     assert method.getReference().name.toString().equals("m")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
     assert upperBoundType.isNullable();
     assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
index 904e53c..0c38b8f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
@@ -62,7 +62,7 @@
     assert method.getReference().name.toString().equals("m")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
index c226571..d386c2f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
@@ -54,7 +54,7 @@
     assert method.getReference().name.toString().equals("test")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull();
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
index 500f34a..e473e4a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
@@ -60,7 +60,7 @@
     assert methodName.equals("m") || methodName.equals("test")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     if (methodName.equals("m")) {
       TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
       assert upperBoundType.isNullable();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
index c294462..0ee8eca 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
@@ -59,7 +59,7 @@
     assert method.getReference().name.toString().equals("m")
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
-        method.getDefinition().getCallSiteOptimizationInfo();
+        method.getOptimizationInfo().getArgumentInfos();
     TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
     assert upperBoundType.isClassType()
         && upperBoundType.asClassType().getClassType().toSourceString().endsWith("$A");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/InstanceOfToNullCheckRewritingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/InstanceOfToNullCheckRewritingTest.java
new file mode 100644
index 0000000..d51649b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/InstanceOfToNullCheckRewritingTest.java
@@ -0,0 +1,58 @@
+// 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.ir.optimize.instanceofremoval;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InstanceOfToNullCheckRewritingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .enableNoVerticalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(inspector -> assertThat(inspector.clazz(I.class), isPresent()))
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("true", "false");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      A nonNullA = System.currentTimeMillis() >= 0 ? new A() : null;
+      A nullA = System.currentTimeMillis() < 0 ? new A() : null;
+      System.out.println(nonNullA instanceof I);
+      System.out.println(nullA instanceof I);
+    }
+  }
+
+  @NoVerticalClassMerging
+  interface I {}
+
+  static class A implements I {}
+}
diff --git a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
index 5c45b49..0b10725 100644
--- a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
+++ b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
@@ -3,8 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.jsr45;
 
-import static com.android.tools.r8.ToolHelper.getDefaultAndroidJar;
-
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
@@ -69,7 +67,7 @@
         R8Command.builder()
             .addProgramFiles(inputPath)
             .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
-            .addLibraryFiles(getDefaultAndroidJar())
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
             .setOutput(outputPath, OutputMode.DexIndexed)
             .addProguardConfigurationFiles(keepRulesPath)
             .build());
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
index 6169da8..5f6030f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
@@ -9,7 +9,6 @@
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -43,10 +42,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters()
-            .withCompiler(KotlinCompilerVersion.KOTLINC_1_3_72)
-            .withAllTargetVersions()
-            .build(),
+        getKotlinTestParameters().withOldCompilersIfSet().withAllTargetVersions().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
index db73684..e14d7a6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -8,7 +8,9 @@
 import com.android.tools.r8.KotlinTestBase;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.util.Collection;
 import org.junit.Test;
@@ -34,6 +36,7 @@
   private void test(Collection<String> rules) throws Exception {
     testForR8(parameters.getBackend())
         .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
         .addKeepRules(rules)
         .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
         .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
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 67c908e..b3af78a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.kotlin;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_6_0;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -370,22 +371,31 @@
               }
 
               ClassSubject classSubject = checkClassIsKept(inspector, testedClass.getClassName());
+
+              // For kotlin 1.6 we completely remove the field and accessors. We are unable to
+              // remove the entire class because we are not reprocessing TestMain.main.
               String propertyName = "property";
+              if (kotlinParameters.isNewerThanOrEqualTo(KOTLINC_1_6_0)) {
+                FieldSubject field = classSubject.field(JAVA_LANG_STRING, propertyName);
+                assertFalse(field.isPresent());
+                return;
+              }
+
               FieldSubject fieldSubject =
                   checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
               assertFalse(fieldSubject.getField().accessFlags.isStatic());
+              assertTrue(fieldSubject.getField().accessFlags.isPrivate());
 
               AccessorKind accessorKind =
-                  kotlinc.getCompilerVersion() == KOTLINC_1_5_0
+                  kotlinc.getCompilerVersion().isGreaterThanOrEqualTo(KOTLINC_1_5_0)
                       ? AccessorKind.FROM_INNER
                       : AccessorKind.FROM_LAMBDA;
               MemberNaming.MethodSignature getterAccessor =
                   testedClass.getGetterAccessorForProperty(propertyName, accessorKind);
               MemberNaming.MethodSignature setterAccessor =
                   testedClass.getSetterAccessorForProperty(propertyName, accessorKind);
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-                checkMethodIsKept(classSubject, getterAccessor);
-                checkMethodIsKept(classSubject, setterAccessor);
+              checkMethodIsKept(classSubject, getterAccessor);
+              checkMethodIsKept(classSubject, setterAccessor);
             });
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java
index de3e23d..f9381cb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java
@@ -44,10 +44,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters()
-            .withCompiler(KotlinCompilerVersion.KOTLINC_1_3_72)
-            .withAllTargetVersions()
-            .build());
+        getKotlinTestParameters().withOldCompilersIfSet().withAllTargetVersions().build());
   }
 
   public MetadataFirstToLatestTest(
@@ -59,7 +56,7 @@
   @Test
   public void smokeTest() throws Exception {
     runTest(
-        KotlinCompilerVersion.KOTLINC_1_5_0,
+        KotlinCompilerVersion.MAX_SUPPORTED_VERSION,
         libJars.getForConfiguration(kotlinc, targetVersion),
         kotlinc.getKotlinStdlibJar());
   }
@@ -93,7 +90,7 @@
         assertThrows(
             AssertionError.class,
             () -> {
-              runTest(KotlinCompilerVersion.KOTLINC_1_3_72, libJar, stdLibJar);
+              runTest(kotlinParameters.getCompiler().getCompilerVersion(), libJar, stdLibJar);
             });
     assertThat(
         assertionError.getMessage(),
@@ -120,7 +117,7 @@
             .assertAllWarningMessagesMatch(
                 equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
             .writeToZip();
-    runTest(KotlinCompilerVersion.KOTLINC_1_5_0, libJar, stdLibJar);
+    runTest(KotlinCompilerVersion.MAX_SUPPORTED_VERSION, libJar, stdLibJar);
   }
 
   private void runTest(KotlinCompilerVersion kotlinCompilerVersion, Path libJar, Path stdLibJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index 11e391d..a29d2ec 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -142,7 +142,9 @@
     KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
     assertThat(age, isPresent());
     assertThat(age, not(isExtensionProperty()));
-    assertEquals(kotlinc.is(KOTLINC_1_5_0) ? "b:I" : "a:I", age.fieldSignature().asString());
+    assertEquals(
+        kotlinParameters.isNewerThanOrEqualTo(KOTLINC_1_5_0) ? "b:I" : "a:I",
+        age.fieldSignature().asString());
     assertEquals("getAge()I", age.getterSignature().asString());
     assertEquals("setAge(I)V", age.setterSignature().asString());
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
index 833ba26..b7b222f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -162,7 +162,7 @@
             .compileRaw();
 
     assertNotEquals(0, kotlinTestCompileResult.exitCode);
-    if (kotlinc.is(KotlinCompilerVersion.KOTLINC_1_5_0)) {
+    if (kotlinParameters.isNewerThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_5_0)) {
       assertThat(
           kotlinTestCompileResult.stderr,
           containsString(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
index 84139d9..1cd5dea 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
@@ -8,7 +8,6 @@
 import static org.junit.Assert.fail;
 import static org.objectweb.asm.Opcodes.ASM7;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
@@ -43,7 +42,7 @@
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
         getKotlinTestParameters()
-            .withCompiler(KotlinCompilerVersion.KOTLINC_1_3_72)
+            .withOldCompilersIfSet()
             .withTargetVersion(KotlinTargetVersion.JAVA_8)
             .build());
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
index 1ad87d5..1cc87e4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.kotlin.metadata.KotlinMetadataTestBase;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FileUtils;
 import java.io.File;
@@ -59,6 +60,7 @@
         .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
         .addProgramFiles(kotlinc.getKotlinStdlibJar())
         .addProgramFiles(kotlinc.getKotlinReflectJar())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
         .run(parameters.getRuntime(), PKG + ".SimpleReflectKt")
         .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
   }
@@ -90,6 +92,7 @@
         .addProgramFiles(kotlinc.getKotlinStdlibJar())
         .addProgramFiles(kotlinc.getKotlinReflectJar())
         .addProgramFiles(kotlinc.getKotlinAnnotationJar())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
         .setMinApi(parameters.getApiLevel())
         .addKeepAllClassesRule()
         .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
index 5da69a2..98ead74 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
@@ -91,6 +91,7 @@
     R8TestCompileResult libraryResult =
         testForR8(parameters.getBackend())
             .addProgramClasses(LIBRARY_CLASSES)
+            .addOptionsModification(options -> options.enableRedundantFieldLoadElimination = false)
             .addKeepMainRule(LibraryMain.class)
             .setMinApi(parameters.getApiLevel())
             .compile();
@@ -107,8 +108,8 @@
         .addProgramClasses(PROGRAM_CLASSES)
         .addApplyMapping(libraryResult.getProguardMap())
         .addLibraryClasses(LIBRARY_CLASSES)
+        .addDefaultRuntimeLibrary(parameters)
         .addTestingAnnotationsAsLibraryClasses()
-        .addLibraryFiles(runtimeJar(parameters.getBackend()))
         .setMinApi(parameters.getApiLevel())
         .compile()
         .addRunClasspathFiles(libraryResult.writeToZip())
diff --git a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
index cc7900e..3dc67f0 100644
--- a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
@@ -38,6 +38,7 @@
         .addKeepMainRule(InlineInto.class)
         .addKeepRules("-keepparameternames")
         .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
         .run(parameters.getRuntime(), InlineInto.class)
         .assertSuccessWithOutputLines("42foo")
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
index c8a47ca..4f028fb 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
@@ -52,6 +52,7 @@
         .addMemberValuePropagationAnnotations()
         .apply(this::configureRepackaging)
         .enableMemberValuePropagationAnnotations(enableMemberValuePropagationAnnotations)
+        .addOptionsModification(options -> options.enableRedundantFieldLoadElimination = false)
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndJumboStringTest.java b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndJumboStringTest.java
new file mode 100644
index 0000000..1eb5db5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndJumboStringTest.java
@@ -0,0 +1,21 @@
+// 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.retrace;
+
+public class StackTraceWithPcAndJumboStringTest {
+
+  public static void foo() {
+    if ((System.nanoTime() > 0 ? "SomeString" : "AndSomeOtherString").length() == 10) {
+      throw new RuntimeException("MoreStrings" + System.nanoTime() + "That Other String");
+    }
+  }
+
+  public static void bar() {
+    foo();
+  }
+
+  public static void main(String[] args) {
+    bar();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndJumboStringTestRunner.java b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndJumboStringTestRunner.java
new file mode 100644
index 0000000..652de07
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndJumboStringTestRunner.java
@@ -0,0 +1,104 @@
+// 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.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StackTraceWithPcAndJumboStringTestRunner extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public StackTraceWithPcAndJumboStringTestRunner(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private Class<?> getTestClass() {
+    return StackTraceWithPcAndJumboStringTest.class;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(getTestClass())
+        .run(parameters.getRuntime(), getTestClass())
+        .assertFailureWithErrorThatThrows(RuntimeException.class)
+        .inspectStackTrace(stacktrace -> assertThat(stacktrace, isSame(getExpectedStackTrace())));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(getTestClass())
+        .noTreeShaking()
+        .addKeepAttributeLineNumberTable()
+        .addKeepMainRule(getTestClass())
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(
+            o -> {
+              o.enablePcBasedMappingFile = true;
+              o.testing.forceJumboStringProcessing = true;
+            })
+        .run(parameters.getRuntime(), getTestClass())
+        .assertFailureWithErrorThatThrows(RuntimeException.class)
+        .inspectStackTrace(
+            stacktrace -> {
+              if (isApiLevelWithPcSupport()) {
+                // TODO(b/207765416): Remove this when PC support works with jumbo string rewriting.
+                assertThat(stacktrace, not(isSame(getExpectedStackTrace())));
+              } else {
+                assertThat(stacktrace, isSame(getExpectedStackTrace()));
+              }
+            });
+  }
+
+  private boolean isApiLevelWithPcSupport() {
+    return parameters.isDexRuntime()
+        && parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport());
+  }
+
+  private StackTrace getExpectedStackTrace() {
+    String className = getTestClass().getName();
+    String sourceFile = getTestClass().getSimpleName() + ".java";
+    return StackTrace.builder()
+        .add(
+            StackTraceLine.builder()
+                .setClassName(className)
+                .setFileName(sourceFile)
+                .setMethodName("foo")
+                .setLineNumber(10)
+                .build())
+        .add(
+            StackTraceLine.builder()
+                .setClassName(className)
+                .setFileName(sourceFile)
+                .setMethodName("bar")
+                .setLineNumber(15)
+                .build())
+        .add(
+            StackTraceLine.builder()
+                .setClassName(className)
+                .setFileName(sourceFile)
+                .setMethodName("main")
+                .setLineNumber(19)
+                .build())
+        .build();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
index 5e17250..d36489a 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -12,13 +12,12 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersBuilder;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.StringReader;
@@ -40,6 +39,7 @@
 public class TreeShakingSpecificTest extends TestBase {
 
   private Backend backend;
+  private AndroidApiLevel minApi = AndroidApiLevel.LATEST;
 
   @Parameters(name = "Backend: {1}")
   public static List<Object[]> data() {
@@ -63,18 +63,6 @@
     return Files.readAllBytes(Paths.get(EXAMPLES_BUILD_DIR, test, "classes.dex"));
   }
 
-  private void finishBuild(R8Command.Builder builder, Path out, String test) throws IOException {
-    Path input;
-    if (backend == Backend.DEX) {
-      builder.setOutput(out, OutputMode.DexIndexed);
-      input = Paths.get(EXAMPLES_BUILD_DIR, test, "classes.dex");
-    } else {
-      builder.setOutput(out, OutputMode.ClassFile);
-      input = Paths.get(EXAMPLES_BUILD_DIR, test + ".jar");
-    }
-    ToolHelper.getAppBuilder(builder).addProgramFiles(input);
-  }
-
   @Test
   public void testIgnoreWarnings() throws Exception {
     // Generate R8 processed version without library option.
@@ -86,6 +74,7 @@
             builder -> builder.addProgramDexFileData(getProgramDexFileData(test)))
         .addKeepRuleFiles(Paths.get(EXAMPLES_DIR, test, "keep-rules.txt"))
         .addIgnoreWarnings()
+        .setMinApi(minApi)
         .compile();
   }
 
@@ -101,6 +90,7 @@
         .addLibraryFiles()
         .addKeepRuleFiles(Paths.get(EXAMPLES_DIR, test, "keep-rules.txt"))
         .allowDiagnosticErrorMessages()
+        .setMinApi(minApi)
         .compileWithExpectedDiagnostics(
             diagnostics -> {
               diagnostics
@@ -131,6 +121,8 @@
             builder -> builder.addProgramDexFileData(getProgramDexFileData(test)))
         .addKeepRuleFiles(Paths.get(EXAMPLES_DIR, test, "keep-rules.txt"))
         .addOptionsModification(options -> options.inlinerOptions().enableInlining = false)
+        .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
+        .setMinApi(minApi)
         .compile()
         .inspectProguardMap(
             proguardMap -> {
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
index 82bcb21..6413bb8 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
@@ -119,6 +119,7 @@
         .addKeepRules(keepRules)
         .enableSideEffectAnnotations()
         .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
         .run(parameters.getRuntime(), CLASS)
         .inspector()
         .clazz(CLASS)
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnStaticFinalFieldTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnStaticFinalFieldTest.java
new file mode 100644
index 0000000..e368a4f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnStaticFinalFieldTest.java
@@ -0,0 +1,149 @@
+// 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.shaking.ifrule;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.InlinableStaticFinalFieldPreconditionDiagnostic;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class IfOnStaticFinalFieldTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public IfOnStaticFinalFieldTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addInnerClasses(getClass())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("A", "B");
+  }
+
+  @Test
+  public void testRuleMatchingOnlyStaticFinalFieldsYieldWarning() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepRules(
+            "-if class " + StaticFinalField.class.getTypeName() + " { int f; }",
+            "-keep class " + A.class.getTypeName())
+        .addKeepRules(
+            "-if class " + StaticNonFinalField.class.getTypeName() + " { int f; }",
+            "-keep class " + B.class.getTypeName())
+        .setMinApi(parameters.getApiLevel())
+        .allowDiagnosticMessages()
+        .compileWithExpectedDiagnostics(
+            diagnostics ->
+                diagnostics
+                    .assertOnlyWarnings()
+                    .assertWarningsMatch(
+                        allOf(
+                            diagnosticType(InlinableStaticFinalFieldPreconditionDiagnostic.class),
+                            diagnosticMessage(containsString("StaticFinalField.f")))))
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("ClassNotFoundException: A", "B");
+  }
+
+  @Test
+  public void testRuleMatchingMoreHasWarning() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepRules("-if class * { int f; } -keep class " + B.class.getTypeName())
+        .setMinApi(parameters.getApiLevel())
+        .allowDiagnosticMessages()
+        .compileWithExpectedDiagnostics(
+            diagnostics ->
+                diagnostics
+                    .assertWarningsMatch(
+                        allOf(
+                            diagnosticType(InlinableStaticFinalFieldPreconditionDiagnostic.class),
+                            diagnosticMessage(containsString("StaticFinalField.f"))))
+                    .assertOnlyWarnings())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("ClassNotFoundException: A", "B");
+  }
+
+  static class StaticFinalField {
+    static final int f = 1;
+  }
+
+  static class StaticNonFinalField {
+    static int f = 2;
+  }
+
+  public static class A {
+
+    @Override
+    public String toString() {
+      return "A";
+    }
+  }
+
+  public static class B {
+
+    @Override
+    public String toString() {
+      return "B";
+    }
+  }
+
+  static class TestClass {
+
+    // Simulate construction of an object (View) from a field value (R-value).
+    static List<Object> getObjects(int... keys) {
+      List<Object> objects = new ArrayList<>();
+      for (int key : keys) {
+        String name;
+        if (key == 1) {
+          name = "A";
+        } else if (key == 2) {
+          name = "B";
+        } else {
+          continue;
+        }
+        Class<TestClass> clazz = TestClass.class;
+        String prefix = clazz.getName().substring(0, clazz.getName().indexOf('$') + 1);
+        try {
+          objects.add(clazz.forName(prefix + name).getConstructor().newInstance());
+        } catch (Exception e) {
+          objects.add(e.getClass().getSimpleName() + ": " + name);
+        }
+      }
+      return objects;
+    }
+
+    public static void main(String[] args) throws Exception {
+      if (System.nanoTime() < 0) {
+        // Force evaluation of the conditional rule for the final field type.
+        System.out.println(StaticFinalField.class.getTypeName());
+        System.out.println(new StaticFinalField());
+      }
+      // Two field references, one of which is inlined by javac.
+      for (Object object : getObjects(StaticFinalField.f, StaticNonFinalField.f)) {
+        System.out.println(object.toString());
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfSimilarClassSpecificationBundlingTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfSimilarClassSpecificationBundlingTest.java
index 6fdcdf9..70f8903 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfSimilarClassSpecificationBundlingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfSimilarClassSpecificationBundlingTest.java
@@ -81,7 +81,7 @@
       throws IOException, CompilationFailedException, ExecutionException {
     runTest(
         8,
-        12,
+        18,
         "-if class **$R* { int keepA; }",
         "-keep class " + A.class.getTypeName() + " { void a(); }",
         "-if class **$R* { int keepB; }",
@@ -115,7 +115,7 @@
       throws IOException, CompilationFailedException, ExecutionException {
     runTest(
         8,
-        12,
+        18,
         "-if class **$R*,**$X { int keepA; }",
         "-keep class " + A.class.getTypeName() + " { void a(); }",
         "-if class **$R*,**$X { int keepB; }",
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/StaticFinalFieldInliningSource.java b/src/test/java/com/android/tools/r8/shaking/ifrule/StaticFinalFieldInliningSource.java
new file mode 100644
index 0000000..f67d414
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/StaticFinalFieldInliningSource.java
@@ -0,0 +1,55 @@
+// 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.shaking.ifrule;
+
+public class StaticFinalFieldInliningSource {
+
+  public static final Object unusedField = "Unused Field";
+
+  public static final Object nullObject = null;
+  public static final Object constObject = "const";
+  public static final String nullString = null;
+  public static final String constString = "const";
+  public static final int nonConstInt = System.nanoTime() < 0 ? 42 : 21;
+  public static final int constInt = 42;
+  public static final double constDouble = 42.5;
+
+  public static Object getNullObject() {
+    return nullObject;
+  }
+
+  public static Object getConstObject() {
+    return constObject;
+  }
+
+  public static String getNullString() {
+    return nullString;
+  }
+
+  public static String getConstString() {
+    return constString;
+  }
+
+  public static int getNonConstInt() {
+    return nonConstInt;
+  }
+
+  public static int getConstInt() {
+    return constInt;
+  }
+
+  public static double getConstDouble() {
+    return constDouble;
+  }
+
+  public static void main(String[] args) {
+    System.out.println(getNullObject());
+    System.out.println(getConstObject());
+    System.out.println(getNullString());
+    System.out.println(getConstString());
+    System.out.println(getNonConstInt());
+    System.out.println(getConstInt());
+    System.out.println(getConstDouble());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/StaticFinalFieldInliningTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/StaticFinalFieldInliningTest.java
new file mode 100644
index 0000000..7a0d692
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/StaticFinalFieldInliningTest.java
@@ -0,0 +1,131 @@
+// 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.shaking.ifrule;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.JavaCompilerTool;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.InlinableStaticFinalFieldPreconditionDiagnostic;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StaticFinalFieldInliningTest extends TestBase {
+
+  static final String EXPECTED =
+      StringUtils.lines("null", "const", "null", "const", "21", "42", "42.5");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withCfRuntimes().build();
+  }
+
+  public StaticFinalFieldInliningTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testJavaCompilers() throws Exception {
+    Path output =
+        JavaCompilerTool.create(parameters.getRuntime().asCf(), temp)
+            .addSourceFiles(
+                ToolHelper.getSourceFileForTestClass(StaticFinalFieldInliningSource.class))
+            .compile();
+
+    testForJvm()
+        .addProgramFiles(output)
+        .run(parameters.getRuntime(), StaticFinalFieldInliningSource.class)
+        .assertSuccessWithOutput(EXPECTED);
+
+    CodeInspector inspector = new CodeInspector(output);
+    assertHasStaticGet(true, "getNullObject", inspector);
+    assertHasStaticGet(true, "getConstObject", inspector);
+    assertHasStaticGet(true, "getNullString", inspector);
+    assertHasStaticGet(false, "getConstString", inspector);
+    assertHasStaticGet(true, "getNonConstInt", inspector);
+    assertHasStaticGet(false, "getConstInt", inspector);
+    assertHasStaticGet(false, "getConstDouble", inspector);
+  }
+
+  private static void assertHasStaticGet(
+      boolean expected, String methodName, CodeInspector inspector) {
+    MethodSubject method =
+        inspector.clazz(StaticFinalFieldInliningSource.class).uniqueMethodWithName(methodName);
+    assertEquals(
+        method.getMethod().codeToString(),
+        expected,
+        method.streamInstructions().anyMatch(InstructionSubject::isStaticGet));
+  }
+
+  private static String makeIfRule(String fieldName) {
+    return "-if class * { *** " + fieldName + "; } -keep class <1> { *** unusedField; }";
+  }
+
+  @Test
+  public void testNoWarningsOnNonInlinedFields() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(StaticFinalFieldInliningSource.class)
+        .addKeepMainRule(StaticFinalFieldInliningSource.class)
+        .addKeepRules(makeIfRule("nullObject"))
+        .addKeepRules(makeIfRule("constObject"))
+        .addKeepRules(makeIfRule("nullString"))
+        .addKeepRules(makeIfRule("nonConstInt"))
+        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoWarnings);
+  }
+
+  @Test
+  public void testWarningsOnMultipleMatches() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(StaticFinalFieldInliningSource.class)
+        .addKeepMainRule(StaticFinalFieldInliningSource.class)
+        // This rule matches both constObject (non-inlined) and constString/Int/Double (inlined).
+        .addKeepRules(makeIfRule("const*"))
+        .allowDiagnosticWarningMessages()
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertWarningsMatch(
+                  allOf(
+                      diagnosticType(InlinableStaticFinalFieldPreconditionDiagnostic.class),
+                      diagnosticMessage(containsString("constString")),
+                      diagnosticMessage(containsString("constInt")),
+                      diagnosticMessage(containsString("constDouble")),
+                      not(diagnosticMessage(containsString("constObject")))));
+            });
+  }
+
+  @Test
+  public void testWarningsOnInlinedFields() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(StaticFinalFieldInliningSource.class)
+        .addKeepMainRule(StaticFinalFieldInliningSource.class)
+        .addKeepRules(makeIfRule("constString"))
+        .addKeepRules(makeIfRule("constInt"))
+        .addKeepRules(makeIfRule("constDouble"))
+        .allowDiagnosticWarningMessages()
+        .compileWithExpectedDiagnostics(
+            diagnostics ->
+                diagnostics.assertWarningsMatch(
+                    diagnosticMessage(containsString("constString")),
+                    diagnosticMessage(containsString("constInt")),
+                    diagnosticMessage(containsString("constDouble"))));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keep/ExtendsMissingClassDirectTest.java b/src/test/java/com/android/tools/r8/shaking/keep/ExtendsMissingClassDirectTest.java
new file mode 100644
index 0000000..a7c5297
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keep/ExtendsMissingClassDirectTest.java
@@ -0,0 +1,70 @@
+// 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.shaking.keep;
+
+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.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ExtendsMissingClassDirectTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    run(testForR8(Backend.CF).allowUnusedProguardConfigurationRules());
+  }
+
+  @Test
+  public void testProguard() throws Exception {
+    run(
+        testForProguard(ProguardVersion.V7_0_0)
+            .addDontWarn(MissingClass.class)
+            .addDontWarn(ExtendsMissingClassDirectTest.class));
+  }
+
+  private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+    builder
+        .addProgramClasses(Main.class, A.class)
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+        .addKeepMainRule(Main.class)
+        .addKeepRules("-keep class ** extends " + MissingClass.class.getTypeName())
+        .compile()
+        .inspect(
+            inspector ->
+                assertEquals(
+                    ImmutableList.of(inspector.clazz(Main.class)), inspector.allClasses()));
+  }
+
+  static class MissingClass {}
+
+  static class A extends MissingClass {}
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keep/ExtendsMissingClassTransitiveTest.java b/src/test/java/com/android/tools/r8/shaking/keep/ExtendsMissingClassTransitiveTest.java
new file mode 100644
index 0000000..2f7bcd4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keep/ExtendsMissingClassTransitiveTest.java
@@ -0,0 +1,72 @@
+// 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.shaking.keep;
+
+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.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ExtendsMissingClassTransitiveTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    run(testForR8(Backend.CF).allowUnusedProguardConfigurationRules());
+  }
+
+  @Test
+  public void testProguard() throws Exception {
+    run(
+        testForProguard(ProguardVersion.V7_0_0)
+            .addDontWarn(MissingClass.class)
+            .addDontWarn(ExtendsMissingClassTransitiveTest.class));
+  }
+
+  private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+    builder
+        .addProgramClasses(Main.class, A.class, B.class)
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+        .addKeepMainRule(Main.class)
+        .addKeepRules("-keep class ** extends " + MissingClass.class.getTypeName())
+        .compile()
+        .inspect(
+            inspector ->
+                assertEquals(
+                    ImmutableList.of(inspector.clazz(Main.class)), inspector.allClasses()));
+  }
+
+  static class MissingClass {}
+
+  static class A extends MissingClass {}
+
+  static class B extends A {}
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keep/ImplementsMissingClassDirectTest.java b/src/test/java/com/android/tools/r8/shaking/keep/ImplementsMissingClassDirectTest.java
new file mode 100644
index 0000000..a4724aa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keep/ImplementsMissingClassDirectTest.java
@@ -0,0 +1,70 @@
+// 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.shaking.keep;
+
+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.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ImplementsMissingClassDirectTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    run(testForR8(Backend.CF).allowUnusedProguardConfigurationRules());
+  }
+
+  @Test
+  public void testProguard() throws Exception {
+    run(
+        testForProguard(ProguardVersion.V7_0_0)
+            .addDontWarn(MissingInterface.class)
+            .addDontWarn(ImplementsMissingClassDirectTest.class));
+  }
+
+  private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+    builder
+        .addProgramClasses(Main.class, A.class)
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+        .addKeepMainRule(Main.class)
+        .addKeepRules("-keep class ** implements " + MissingInterface.class.getTypeName())
+        .compile()
+        .inspect(
+            inspector ->
+                assertEquals(
+                    ImmutableList.of(inspector.clazz(Main.class)), inspector.allClasses()));
+  }
+
+  interface MissingInterface {}
+
+  static class A implements MissingInterface {}
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keep/ImplementsMissingClassTransitiveTest.java b/src/test/java/com/android/tools/r8/shaking/keep/ImplementsMissingClassTransitiveTest.java
new file mode 100644
index 0000000..a087d90
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keep/ImplementsMissingClassTransitiveTest.java
@@ -0,0 +1,72 @@
+// 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.shaking.keep;
+
+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.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ImplementsMissingClassTransitiveTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    run(testForR8(Backend.CF).allowUnusedProguardConfigurationRules());
+  }
+
+  @Test
+  public void testProguard() throws Exception {
+    run(
+        testForProguard(ProguardVersion.V7_0_0)
+            .addDontWarn(MissingInterface.class)
+            .addDontWarn(ImplementsMissingClassTransitiveTest.class));
+  }
+
+  private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+    builder
+        .addProgramClasses(Main.class, A.class, B.class)
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+        .addKeepMainRule(Main.class)
+        .addKeepRules("-keep class ** implements " + MissingInterface.class.getTypeName())
+        .compile()
+        .inspect(
+            inspector ->
+                assertEquals(
+                    ImmutableList.of(inspector.clazz(Main.class)), inspector.allClasses()));
+  }
+
+  interface MissingInterface {}
+
+  static class A implements MissingInterface {}
+
+  static class B extends A {}
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
index 2851643..e7bb456 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
@@ -138,46 +138,51 @@
   }
 
   private void noInterfaceKept(CodeInspector inspector) {
-    // Indirectly assert that method is inlined into x, y and z.
-    assertEquals(1, countInstructionInX(inspector, InstructionSubject::isInvokeInterface));
-    assertEquals(1, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
-    assertEquals(1, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
+    // Indirectly assert that method is inlined into x, y and z and that redundant field loads
+    // remove invokes.
+    assertEquals(0, countInstructionInX(inspector, InstructionSubject::isInvokeInterface));
+    assertEquals(0, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
+    assertEquals(0, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
   }
 
   @Test
   public void testNoInterfaceKept() throws Exception {
-    runTest(ImmutableList.of(),
+    runTest(
+        ImmutableList.of(),
         this::noInterfaceKept,
-        "TestClass 1\nTestClass 1\nTestClass 1\nProxy\nEXCEPTION\n");
+        "TestClass 1\nTestClass 1\nTestClass 1\nEXCEPTION\n");
   }
 
   private void baseInterfaceKept(CodeInspector inspector) {
     // Indirectly assert that method is not inlined into x.
     assertEquals(3, countInstructionInX(inspector, InstructionSubject::isInvokeInterface));
-    // Indirectly assert that method is inlined into y and z.
-    assertEquals(1, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
-    assertEquals(1, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
-    assertEquals(1, countInstructionInZSubClass(inspector, InstructionSubject::isInvokeVirtual));
+    // Indirectly assert that method is inlined into y and z and that redundant field loads
+    // remove invokes.
+    assertEquals(0, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
+    assertEquals(0, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
+    assertEquals(0, countInstructionInZSubClass(inspector, InstructionSubject::isInvokeVirtual));
   }
 
   @Test
   public void testBaseInterfaceKept() throws Exception {
-    runTest(ImmutableList.of(
-        "-keep interface " + BaseInterface.class.getCanonicalName() + " {",
-        "  <methods>;",
-        "}"),
+    runTest(
+        ImmutableList.of(
+            "-keep interface " + BaseInterface.class.getCanonicalName() + " {",
+            "  <methods>;",
+            "}"),
         this::baseInterfaceKept,
-        "TestClass 1\nTestClass 1\nTestClass 1\nProxy\nProxy\nProxy\n" +
-        "TestClass 2\nTestClass 2\nTestClass 2\nProxy\nEXCEPTION\n");
+        "TestClass 1\nTestClass 1\nTestClass 1\nProxy\nProxy\nProxy\n"
+            + "TestClass 2\nTestClass 2\nTestClass 2\nEXCEPTION\n");
   }
 
   private void subInterfaceKept(CodeInspector inspector) {
     // Indirectly assert that method is not inlined into x or y.
     assertEquals(3, countInstructionInX(inspector, InstructionSubject::isInvokeInterface));
     assertEquals(3, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
-    // Indirectly assert that method is inlined into z.
-    assertEquals(1, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
-    assertEquals(1, countInstructionInZSubClass(inspector, InstructionSubject::isInvokeVirtual));
+    // Indirectly assert that method is inlined into x, y and z and that redundant field loads
+    // remove invokes.
+    assertEquals(0, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
+    assertEquals(0, countInstructionInZSubClass(inspector, InstructionSubject::isInvokeVirtual));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/workaround/ArrayFieldGetWithMissingBaseTypeTest.java b/src/test/java/com/android/tools/r8/workaround/ArrayFieldGetWithMissingBaseTypeTest.java
index aba5532..b28721d 100644
--- a/src/test/java/com/android/tools/r8/workaround/ArrayFieldGetWithMissingBaseTypeTest.java
+++ b/src/test/java/com/android/tools/r8/workaround/ArrayFieldGetWithMissingBaseTypeTest.java
@@ -51,7 +51,6 @@
                                 UsedDuringLaunch.class, NotUsedDuringLaunch.class))
                     .assertNoOtherClassesMerged())
         .enableInliningAnnotations()
-        .addOptionsModification(o -> o.apiModelingOptions().disableApiCallerIdentification())
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/workaround/MethodReturnWithMissingBaseTypeTest.java b/src/test/java/com/android/tools/r8/workaround/MethodReturnWithMissingBaseTypeTest.java
new file mode 100644
index 0000000..87a601b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/workaround/MethodReturnWithMissingBaseTypeTest.java
@@ -0,0 +1,110 @@
+// 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.workaround;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import java.util.function.Consumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MethodReturnWithMissingBaseTypeTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection parameters() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .applyIf(
+            parameters.isDexRuntime(),
+            testBuilder -> testBuilder.addLibraryFiles(ToolHelper.getMostRecentAndroidJar()))
+        .addKeepMainRule(Main.class)
+        .addKeepClassAndMembersRules(Utils.class)
+        .addKeepRules(
+            "-keep class " + Main.class.getTypeName() + " { void notUsedDuringLaunch(); }")
+        .addHorizontallyMergedClassesInspector(
+            inspector ->
+                inspector
+                    .assertIsCompleteMergeGroup(UsedDuringLaunch.class, NotUsedDuringLaunch.class)
+                    .assertNoOtherClassesMerged())
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      new UsedDuringLaunch().usedDuringLaunch();
+    }
+
+    // @Keep
+    static void notUsedDuringLaunch() {
+      Consumer<?> emptyConsumer = Utils.getEmptyConsumer();
+      new UsedDuringLaunch().onlyUsedOnHighApiLevels(emptyConsumer);
+      new NotUsedDuringLaunch().useOfConsumerArray();
+    }
+  }
+
+  @NeverClassInline
+  static class UsedDuringLaunch {
+
+    @NeverInline
+    void usedDuringLaunch() {
+      System.out.println("Hello world!");
+    }
+
+    @NeverInline
+    void onlyUsedOnHighApiLevels(Consumer<?> c) {
+      System.out.println(c);
+    }
+  }
+
+  @NeverClassInline
+  static class NotUsedDuringLaunch {
+
+    @NeverInline
+    void useOfConsumerArray() {
+      Utils.accept(Utils.getEmptyConsumers());
+    }
+  }
+
+  // @Keep
+  static class Utils {
+
+    // @Keep
+    static void accept(Consumer<?>[] array) {
+      System.out.println(array.length);
+    }
+
+    // @Keep
+    public static Consumer<?> getEmptyConsumer() {
+      return ignore -> {};
+    }
+
+    // @Keep
+    public static Consumer<?>[] getEmptyConsumers() {
+      return new Consumer<?>[] {getEmptyConsumer()};
+    }
+  }
+}
diff --git a/third_party/kotlin/kotlin-compiler-1.6.0.tar.gz.sha1 b/third_party/kotlin/kotlin-compiler-1.6.0.tar.gz.sha1
new file mode 100644
index 0000000..0a1ac8f
--- /dev/null
+++ b/third_party/kotlin/kotlin-compiler-1.6.0.tar.gz.sha1
@@ -0,0 +1 @@
+bd6cc2fbbc946a62268f76157aa4643b021b9b00
\ No newline at end of file
diff --git a/tools/compiledump.py b/tools/compiledump.py
index dbb4a18..98c9489 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -55,7 +55,7 @@
     '--r8-flags', '--r8_flags',
     help='Additional option(s) for the compiler.')
   parser.add_argument(
-    '-override',
+    '--override',
     help='Do not override any extracted dump in temp-dir',
     default=False,
     action='store_true')
diff --git a/tools/create_r8lib.py b/tools/create_r8lib.py
index 329ae8e..c8fcac3 100755
--- a/tools/create_r8lib.py
+++ b/tools/create_r8lib.py
@@ -38,6 +38,10 @@
       '--lib',
       action='append',
       help='Additional libraries (JDK 1.8 rt.jar already included)')
+  parser.add_argument(
+      '--classpath',
+      action='append',
+      help='Dependencies to add to classpath')
   return parser.parse_args()
 
 def get_r8_version(r8jar):
@@ -86,6 +90,9 @@
   if args.lib:
     for lib in args.lib:
       cmd.extend(['--lib', lib])
+  if args.classpath:
+    for cp in args.classpath:
+      cmd.extend(['--classpath', cp])
   print(' '.join(cmd))
   subprocess.check_call(cmd)
 
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index f71a314..6c25a4d 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -945,6 +945,9 @@
   result.add_argument('--shrinker',
                       help='The shrinkers to use (by default, all are run)',
                       action='append')
+  result.add_argument('--temp',
+                      help='A directory to use for temporaries and outputs.',
+                      default=None)
   result.add_argument('--version',
                       default='main',
                       help='The version of R8 to use (e.g., 1.4.51)')
@@ -1087,6 +1090,8 @@
     return 0
 
   with utils.TempDir() as temp_dir:
+    if options.temp:
+      temp_dir = options.temp
     if options.hash:
       # Download r8-<hash>.jar from
       # https://storage.googleapis.com/r8-releases/raw/.
diff --git a/tools/test.py b/tools/test.py
index 7235b6e..f5ce159 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -196,6 +196,9 @@
                     help='Specify to download a kotlin dev compiler and run '
                          'tests with that',
                     default=False, action='store_true')
+  result.add_option('--kotlin-compiler-old',
+                    help='Specify to run tests on older kotlin compilers',
+                    default=False, action='store_true')
   return result.parse_args()
 
 def archive_failures(options):
@@ -283,6 +286,8 @@
     gradle_args.append('-Pprint_full_stacktraces')
   if options.print_obfuscated_stacktraces:
     gradle_args.append('-Pprint_obfuscated_stacktraces')
+  if options.kotlin_compiler_old:
+    gradle_args.append('-Dcom.android.tools.r8.kotlincompilerold=1')
   if options.kotlin_compiler_dev:
     gradle_args.append('-Dcom.android.tools.r8.kotlincompilerdev=1')
     download_kotlin_dev.download_newest()