Merge commit '7f5bdd94cd22d02c72aa1119f56a11e328c07cf2' into dev-release
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 79d8121..d0cc5af 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -67,10 +67,10 @@
 import com.android.tools.r8.optimize.MemberRebindingAnalysis;
 import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
 import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
+import com.android.tools.r8.optimize.RedundantBridgeRemover;
 import com.android.tools.r8.optimize.bridgehoisting.BridgeHoisting;
 import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
 import com.android.tools.r8.optimize.proto.ProtoNormalizer;
-import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemover;
 import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index a664d9d..97ceb6e 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -747,7 +747,7 @@
                   dataResourceConsumer.accept(adapted, options.reporter);
                 } else {
                   options.reporter.warning(
-                      new StringDiagnostic("Resource '" + file.getName() + "' already exists."));
+                      new StringDiagnostic("Resource '" + adapted.getName() + "' already exists."));
                 }
                 options.reporter.failIfPendingErrors();
               }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexCompareHelper.java b/src/main/java/com/android/tools/r8/dex/code/DexCompareHelper.java
index 93c2501..6af977e 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexCompareHelper.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexCompareHelper.java
@@ -11,7 +11,7 @@
   static final int DEX_ITEM_CONST_STRING_COMPARE_ID;
   static final int DEX_RECORD_FIELD_VALUES_COMPARE_ID;
 
-  private static int HIGHEST_DEX_OPCODE = 0xFF;
+  private static final int HIGHEST_DEX_OPCODE = 0xFF;
 
   static {
     int lastId = HIGHEST_DEX_OPCODE;
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 f9a5279..f30986c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -475,9 +475,9 @@
     return isInstanceInitializer() || willBeInlinedIntoInstanceInitializer(dexItemFactory);
   }
 
-  public boolean isDefaultInstanceInitializer() {
+  public boolean isDefaultInitializer() {
     checkIfObsolete();
-    return isInstanceInitializer() && getParameters().isEmpty();
+    return isInstanceInitializer() && getReference().proto.parameters.isEmpty();
   }
 
   public boolean isClassInitializer() {
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 2dfb637..13c5ab8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -602,9 +602,6 @@
   public final DexType javaUtilSetType = createStaticallyKnownType("Ljava/util/Set;");
 
   public final DexType androidAppActivity = createStaticallyKnownType("Landroid/app/Activity;");
-  public final DexType androidAppFragment = createStaticallyKnownType("Landroid/app/Fragment;");
-  public final DexType androidAppZygotePreload =
-      createStaticallyKnownType("Landroid/app/ZygotePreload;");
   public final DexType androidOsBuildType = createStaticallyKnownType("Landroid/os/Build;");
   public final DexType androidOsBuildVersionType =
       createStaticallyKnownType("Landroid/os/Build$VERSION;");
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index e5d9dfe..47b1c27 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -25,12 +25,12 @@
 
   public static final FieldAccessInfoImpl MISSING_FIELD_ACCESS_INFO = new FieldAccessInfoImpl(null);
 
-  public static int FLAG_IS_READ_FROM_ANNOTATION = 1 << 0;
-  public static int FLAG_IS_READ_FROM_METHOD_HANDLE = 1 << 1;
-  public static int FLAG_IS_WRITTEN_FROM_METHOD_HANDLE = 1 << 2;
-  public static int FLAG_HAS_REFLECTIVE_READ = 1 << 3;
-  public static int FLAG_HAS_REFLECTIVE_WRITE = 1 << 4;
-  public static int FLAG_IS_READ_FROM_RECORD_INVOKE_DYNAMIC = 1 << 5;
+  public static final int FLAG_IS_READ_FROM_ANNOTATION = 1 << 0;
+  public static final int FLAG_IS_READ_FROM_METHOD_HANDLE = 1 << 1;
+  public static final int FLAG_IS_WRITTEN_FROM_METHOD_HANDLE = 1 << 2;
+  public static final int FLAG_HAS_REFLECTIVE_READ = 1 << 3;
+  public static final int FLAG_HAS_REFLECTIVE_WRITE = 1 << 4;
+  public static final int FLAG_IS_READ_FROM_RECORD_INVOKE_DYNAMIC = 1 << 5;
 
   // A direct reference to the definition of the field.
   private DexField field;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index d871616..637026f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.code.FieldInstruction;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -69,6 +70,7 @@
       IRCode code,
       BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder,
       OptimizationFeedback feedback,
+      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
       MethodProcessor methodProcessor) {
     if (!methodProcessor.isPrimaryMethodProcessor()) {
       return;
@@ -85,7 +87,8 @@
             appView.appInfo().resolveField(fieldInstruction.getField()).getProgramField();
         if (field != null) {
           if (fieldAssignmentTracker != null) {
-            fieldAssignmentTracker.recordFieldAccess(fieldInstruction, field, code.context());
+            fieldAssignmentTracker.recordFieldAccess(
+                fieldInstruction, field, fieldReadBeforeWriteAnalysis);
           }
           if (fieldBitAccessAnalysis != null) {
             fieldBitAccessAnalysis.recordFieldAccess(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index 0db5825..dc90a85 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerUtils;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcreteArrayTypeFieldState;
 import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcreteClassTypeFieldState;
 import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcretePrimitiveTypeFieldState;
@@ -34,6 +35,8 @@
 import com.android.tools.r8.ir.analysis.value.SingleValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.FieldPut;
+import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.Value;
@@ -179,16 +182,23 @@
         });
   }
 
-  void recordFieldAccess(FieldInstruction instruction, ProgramField field, ProgramMethod context) {
+  void recordFieldAccess(
+      FieldInstruction instruction,
+      ProgramField field,
+      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
     if (instruction.isFieldPut()) {
-      recordFieldPut(field, instruction.value(), context);
+      recordFieldPut(instruction.asFieldPut(), field, fieldReadBeforeWriteAnalysis);
     }
   }
 
-  private void recordFieldPut(ProgramField field, Value value, ProgramMethod context) {
+  private void recordFieldPut(
+      FieldPut fieldPut,
+      ProgramField field,
+      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
     // For now only attempt to prove that fields are definitely null. In order to prove a single
     // value for fields that are not definitely null, we need to prove that the given field is never
     // read before it is written.
+    Value value = fieldPut.value();
     AbstractValue abstractValue =
         value.isZero() ? abstractValueFactory.createZeroValue() : AbstractValue.unknown();
     fieldStates.compute(
@@ -203,12 +213,8 @@
               return ConcretePrimitiveTypeFieldState.create(abstractValue);
             }
             assert fieldType.isClassType();
-            DynamicType dynamicType =
-                WideningUtils.widenDynamicNonReceiverType(
-                    appView,
-                    value.getDynamicType(appView).withNullability(Nullability.maybeNull()),
-                    field.getType());
-            return ConcreteClassTypeFieldState.create(abstractValue, dynamicType);
+            return ConcreteClassTypeFieldState.create(
+                abstractValue, getDynamicType(fieldPut, field, fieldReadBeforeWriteAnalysis));
           }
 
           if (fieldState.isUnknown()) {
@@ -231,10 +237,31 @@
 
           ConcreteClassTypeFieldState classFieldState = fieldState.asClass();
           return classFieldState.mutableJoin(
-              appView, abstractValue, value.getDynamicType(appView), field);
+              appView,
+              abstractValue,
+              getDynamicType(fieldPut, field, fieldReadBeforeWriteAnalysis),
+              field);
         });
   }
 
+  private DynamicType getDynamicType(
+      FieldPut fieldPut,
+      ProgramField field,
+      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
+    DynamicTypeWithUpperBound dynamicType = fieldPut.value().getDynamicType(appView);
+    if (fieldPut.isInstancePut()) {
+      InstancePut instancePut = fieldPut.asInstancePut();
+      if (fieldReadBeforeWriteAnalysis.isInstanceFieldMaybeReadBeforeInstruction(
+          instancePut.object(), field.getDefinition(), instancePut)) {
+        dynamicType = dynamicType.withNullability(Nullability.maybeNull());
+      }
+    } else if (fieldReadBeforeWriteAnalysis.isStaticFieldMaybeReadBeforeInstruction(
+        field.getDefinition(), fieldPut.asStaticPut())) {
+      dynamicType = dynamicType.withNullability(Nullability.maybeNull());
+    }
+    return WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, field.getType());
+  }
+
   void recordAllocationSite(NewInstance instruction, DexProgramClass clazz, ProgramMethod context) {
     Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass =
         abstractInstanceFieldValues.get(clazz);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java
new file mode 100644
index 0000000..fb6bae0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2023, 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.analysis.fieldaccess.readbeforewrite;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+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.Value;
+
+public abstract class FieldReadBeforeWriteAnalysis {
+
+  public static FieldReadBeforeWriteAnalysis create(
+      AppView<?> appView, IRCode code, ProgramMethod context) {
+    if (appView.hasLiveness()) {
+      return new FieldReadBeforeWriteAnalysisImpl(appView.withLiveness(), code, context);
+    }
+    return trivial();
+  }
+
+  public static TrivialFieldReadBeforeWriteAnalysis trivial() {
+    return new TrivialFieldReadBeforeWriteAnalysis();
+  }
+
+  public abstract boolean isInstanceFieldMaybeReadBeforeInstruction(
+      Value receiver, DexEncodedField field, Instruction instruction);
+
+  public abstract boolean isStaticFieldMaybeReadBeforeInstruction(
+      DexEncodedField field, Instruction instruction);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java
new file mode 100644
index 0000000..7e7ac89
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java
@@ -0,0 +1,209 @@
+// Copyright (c) 2023, 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.analysis.fieldaccess.readbeforewrite;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.KnownFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DequeUtils;
+import java.util.Deque;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+class FieldReadBeforeWriteAnalysisImpl extends FieldReadBeforeWriteAnalysis {
+
+  private final AppView<AppInfoWithLiveness> appView;
+  private final IRCode code;
+  private final ProgramMethod context;
+
+  private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
+
+  public FieldReadBeforeWriteAnalysisImpl(
+      AppView<AppInfoWithLiveness> appView, IRCode code, ProgramMethod context) {
+    this.appView = appView;
+    this.code = code;
+    this.context = context;
+  }
+
+  @Override
+  public boolean isInstanceFieldMaybeReadBeforeInstruction(
+      Value receiver, DexEncodedField field, Instruction instruction) {
+    if (!code.context().getDefinition().isInstanceInitializer()
+        || receiver.getAliasedValue() != code.getThis()) {
+      return true;
+    }
+    return isFieldMaybeReadBeforeInstructionInInitializer(field, instruction);
+  }
+
+  @Override
+  public boolean isStaticFieldMaybeReadBeforeInstruction(
+      DexEncodedField field, Instruction instruction) {
+    if (!code.context().getDefinition().isClassInitializer()
+        || field.getHolderType() != code.context().getHolderType()) {
+      return true;
+    }
+    return isFieldMaybeReadBeforeInstructionInInitializer(field, instruction);
+  }
+
+  public boolean isFieldMaybeReadBeforeInstructionInInitializer(
+      DexEncodedField field, Instruction instruction) {
+    BasicBlock block = instruction.getBlock();
+
+    // First check if the field may be read in any of the (transitive) predecessor blocks.
+    if (fieldMaybeReadBeforeBlock(field, block)) {
+      return true;
+    }
+
+    // Then check if any of the instructions that precede the given instruction in the current block
+    // may read the field.
+    InstructionIterator instructionIterator = block.iterator();
+    while (instructionIterator.hasNext()) {
+      Instruction current = instructionIterator.next();
+      if (current == instruction) {
+        break;
+      }
+      if (current.readSet(appView, context).contains(field)) {
+        return true;
+      }
+    }
+
+    // Otherwise, the field is not read prior to the given instruction.
+    return false;
+  }
+
+  private boolean fieldMaybeReadBeforeBlock(DexEncodedField field, BasicBlock block) {
+    for (BasicBlock predecessor : block.getPredecessors()) {
+      if (fieldMaybeReadBeforeBlockInclusive(field, predecessor)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean fieldMaybeReadBeforeBlockInclusive(DexEncodedField field, BasicBlock block) {
+    return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(field);
+  }
+
+  private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
+    if (fieldsMaybeReadBeforeBlockInclusiveCache == null) {
+      fieldsMaybeReadBeforeBlockInclusiveCache = createFieldsMaybeReadBeforeBlockInclusive();
+    }
+    return fieldsMaybeReadBeforeBlockInclusiveCache;
+  }
+
+  /**
+   * Eagerly creates a mapping from each block to the set of fields that may be read in that block
+   * and its transitive predecessors.
+   */
+  private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() {
+    Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>();
+    Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock());
+    while (!worklist.isEmpty()) {
+      BasicBlock block = worklist.removeFirst();
+      boolean seenBefore = result.containsKey(block);
+      AbstractFieldSet readSet =
+          result.computeIfAbsent(block, ignore -> EmptyFieldSet.getInstance());
+      if (readSet.isTop()) {
+        // We already have unknown information for this block.
+        continue;
+      }
+
+      assert readSet.isKnownFieldSet();
+      KnownFieldSet knownReadSet = readSet.asKnownFieldSet();
+      int oldSize = seenBefore ? knownReadSet.size() : -1;
+
+      // Everything that is read in the predecessor blocks should also be included in the read set
+      // for the current block, so here we join the information from the predecessor blocks into the
+      // current read set.
+      boolean blockOrPredecessorMaybeReadAnyField = false;
+      for (BasicBlock predecessor : block.getPredecessors()) {
+        AbstractFieldSet predecessorReadSet =
+            result.getOrDefault(predecessor, EmptyFieldSet.getInstance());
+        if (predecessorReadSet.isBottom()) {
+          continue;
+        }
+        if (predecessorReadSet.isTop()) {
+          blockOrPredecessorMaybeReadAnyField = true;
+          break;
+        }
+        assert predecessorReadSet.isConcreteFieldSet();
+        if (!knownReadSet.isConcreteFieldSet()) {
+          knownReadSet = new ConcreteMutableFieldSet();
+        }
+        knownReadSet.asConcreteFieldSet().addAll(predecessorReadSet.asConcreteFieldSet());
+      }
+
+      if (!blockOrPredecessorMaybeReadAnyField) {
+        // Finally, we update the read set with the fields that are read by the instructions in the
+        // current block. This can be skipped if the block has already been processed.
+        if (seenBefore) {
+          assert verifyFieldSetContainsAllFieldReadsInBlock(knownReadSet, block, context);
+        } else {
+          for (Instruction instruction : block.getInstructions()) {
+            AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
+            if (instructionReadSet.isBottom()) {
+              continue;
+            }
+            if (instructionReadSet.isTop()) {
+              blockOrPredecessorMaybeReadAnyField = true;
+              break;
+            }
+            if (!knownReadSet.isConcreteFieldSet()) {
+              knownReadSet = new ConcreteMutableFieldSet();
+            }
+            knownReadSet.asConcreteFieldSet().addAll(instructionReadSet.asConcreteFieldSet());
+          }
+        }
+      }
+
+      boolean changed = false;
+      if (blockOrPredecessorMaybeReadAnyField) {
+        // Record that this block reads all fields.
+        result.put(block, UnknownFieldSet.getInstance());
+        changed = true;
+      } else {
+        if (knownReadSet != readSet) {
+          result.put(block, knownReadSet.asConcreteFieldSet());
+        }
+        if (knownReadSet.size() != oldSize) {
+          assert knownReadSet.size() > oldSize;
+          changed = true;
+        }
+      }
+
+      if (changed) {
+        // Rerun the analysis for all successors because the state of the current block changed.
+        worklist.addAll(block.getSuccessors());
+      }
+    }
+    return result;
+  }
+
+  private boolean verifyFieldSetContainsAllFieldReadsInBlock(
+      KnownFieldSet readSet, BasicBlock block, ProgramMethod context) {
+    for (Instruction instruction : block.getInstructions()) {
+      AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
+      assert !instructionReadSet.isTop();
+      if (instructionReadSet.isBottom()) {
+        continue;
+      }
+      for (DexEncodedField field : instructionReadSet.asConcreteFieldSet().getFields()) {
+        assert readSet.contains(field);
+      }
+    }
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java
new file mode 100644
index 0000000..9cd76dc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2023, 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.analysis.fieldaccess.readbeforewrite;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+
+class TrivialFieldReadBeforeWriteAnalysis extends FieldReadBeforeWriteAnalysis {
+
+  @Override
+  public boolean isInstanceFieldMaybeReadBeforeInstruction(
+      Value receiver, DexEncodedField field, Instruction instruction) {
+    return true;
+  }
+
+  @Override
+  public boolean isStaticFieldMaybeReadBeforeInstruction(
+      DexEncodedField field, Instruction instruction) {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
index b18d02a..854d046 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -39,7 +39,7 @@
     return this;
   }
 
-  Set<DexEncodedField> getFields() {
+  public Set<DexEncodedField> getFields() {
     if (InternalOptions.assertionsEnabled()) {
       return Collections.unmodifiableSet(fields);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index 9c09ef2..50c075e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -10,13 +10,14 @@
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.DominatorTree;
 import com.android.tools.r8.ir.code.DominatorTree.Assumption;
 import com.android.tools.r8.ir.code.FieldInstruction;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionIterator;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
@@ -24,14 +25,11 @@
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
 import com.android.tools.r8.ir.optimize.info.field.UnknownInstanceFieldInitializationInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.DequeUtils;
 import com.android.tools.r8.utils.ListUtils;
 import java.util.ArrayList;
-import java.util.Deque;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 
 public abstract class FieldValueAnalysis {
 
@@ -53,16 +51,20 @@
   final OptimizationFeedback feedback;
 
   private DominatorTree dominatorTree;
-  private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
+  private FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis;
 
   final Map<DexEncodedField, List<FieldInitializationInfo>> putsPerField = new IdentityHashMap<>();
 
   FieldValueAnalysis(
-      AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
+      AppView<AppInfoWithLiveness> appView,
+      IRCode code,
+      OptimizationFeedback feedback,
+      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
     this.appView = appView;
     this.code = code;
-    this.feedback = feedback;
     this.context = code.context();
+    this.feedback = feedback;
+    this.fieldReadBeforeWriteAnalysis = fieldReadBeforeWriteAnalysis;
   }
 
   DominatorTree getOrCreateDominatorTree() {
@@ -72,13 +74,6 @@
     return dominatorTree;
   }
 
-  private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
-    if (fieldsMaybeReadBeforeBlockInclusiveCache == null) {
-      fieldsMaybeReadBeforeBlockInclusiveCache = createFieldsMaybeReadBeforeBlockInclusive();
-    }
-    return fieldsMaybeReadBeforeBlockInclusiveCache;
-  }
-
   boolean isInstanceFieldValueAnalysis() {
     return false;
   }
@@ -95,9 +90,9 @@
     return null;
   }
 
-  abstract boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field);
+  abstract boolean isSubjectToOptimizationIgnoringPinning(ProgramField field);
 
-  abstract boolean isSubjectToOptimization(DexEncodedField field);
+  abstract boolean isSubjectToOptimization(ProgramField field);
 
   void recordFieldPut(DexEncodedField field, Instruction instruction) {
     recordFieldPut(field, instruction, UnknownInstanceFieldInitializationInfo.getInstance());
@@ -127,13 +122,12 @@
           DexField field = fieldPut.getField();
           ProgramField programField = appInfo.resolveField(field).getProgramField();
           if (programField != null) {
-            DexEncodedField encodedField = programField.getDefinition();
-            if (isSubjectToOptimization(encodedField)) {
-              recordFieldPut(encodedField, fieldPut);
+            if (isSubjectToOptimization(programField)) {
+              recordFieldPut(programField.getDefinition(), fieldPut);
             } else if (isStaticFieldValueAnalysis()
                 && programField.getHolder().isEnum()
-                && isSubjectToOptimizationIgnoringPinning(encodedField)) {
-              recordFieldPut(encodedField, fieldPut);
+                && isSubjectToOptimizationIgnoringPinning(programField)) {
+              recordFieldPut(programField.getDefinition(), fieldPut);
             }
           }
         } else if (isInstanceFieldValueAnalysis()
@@ -144,186 +138,54 @@
       }
     }
 
+    boolean checkDominance = !isStraightLineCode;
     List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
-    for (Entry<DexEncodedField, List<FieldInitializationInfo>> entry : putsPerField.entrySet()) {
-      DexEncodedField field = entry.getKey();
-      List<FieldInitializationInfo> fieldPuts = entry.getValue();
-      if (fieldPuts.size() > 1) {
-        continue;
-      }
-      FieldInitializationInfo info = ListUtils.first(fieldPuts);
-      Instruction instruction = info.instruction;
-      if (instruction.isInvokeDirect()) {
-        asInstanceFieldValueAnalysis()
-            .recordInstanceFieldIsInitializedWithInfo(field, info.instanceFieldInitializationInfo);
-        continue;
-      }
-      FieldInstruction fieldPut = instruction.asFieldInstruction();
-      if (!isStraightLineCode) {
-        if (!getOrCreateDominatorTree().dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
-          continue;
-        }
-      }
-      boolean priorReadsWillReadSameValue =
-          !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero();
-      if (!priorReadsWillReadSameValue && fieldMaybeReadBeforeInstruction(field, fieldPut)) {
-        // TODO(b/172528424): Generalize to InstanceFieldValueAnalysis.
-        if (isStaticFieldValueAnalysis()) {
-          // At this point the value read in the field can be only the default static value, if read
-          // prior to the put, or the value put, if read after the put. We still want to record it
-          // because the default static value is typically null/0, so code present after a null/0
-          // check can take advantage of the optimization.
-          DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field);
-          asStaticFieldValueAnalysis()
-              .updateFieldOptimizationInfoWith2Values(field, fieldPut.value(), valueBeforePut);
-        }
-        continue;
-      }
-      updateFieldOptimizationInfo(field, fieldPut, fieldPut.value());
-    }
-  }
-
-  private boolean fieldMaybeReadBeforeInstruction(
-      DexEncodedField encodedField, Instruction instruction) {
-    BasicBlock block = instruction.getBlock();
-
-    // First check if the field may be read in any of the (transitive) predecessor blocks.
-    if (fieldMaybeReadBeforeBlock(encodedField, block)) {
-      return true;
-    }
-
-    // Then check if any of the instructions that precede the given instruction in the current block
-    // may read the field.
-    InstructionIterator instructionIterator = block.iterator();
-    while (instructionIterator.hasNext()) {
-      Instruction current = instructionIterator.next();
-      if (current == instruction) {
-        break;
-      }
-      if (current.readSet(appView, context).contains(encodedField)) {
-        return true;
-      }
-    }
-
-    // Otherwise, the field is not read prior to the given instruction.
-    return false;
-  }
-
-  private boolean fieldMaybeReadBeforeBlock(DexEncodedField encodedField, BasicBlock block) {
-    for (BasicBlock predecessor : block.getPredecessors()) {
-      if (fieldMaybeReadBeforeBlockInclusive(encodedField, predecessor)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private boolean fieldMaybeReadBeforeBlockInclusive(
-      DexEncodedField encodedField, BasicBlock block) {
-    return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(encodedField);
-  }
-
-  /**
-   * Eagerly creates a mapping from each block to the set of fields that may be read in that block
-   * and its transitive predecessors.
-   */
-  private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() {
-    Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>();
-    Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock());
-    while (!worklist.isEmpty()) {
-      BasicBlock block = worklist.removeFirst();
-      boolean seenBefore = result.containsKey(block);
-      AbstractFieldSet readSet =
-          result.computeIfAbsent(block, ignore -> EmptyFieldSet.getInstance());
-      if (readSet.isTop()) {
-        // We already have unknown information for this block.
-        continue;
-      }
-
-      assert readSet.isKnownFieldSet();
-      KnownFieldSet knownReadSet = readSet.asKnownFieldSet();
-      int oldSize = seenBefore ? knownReadSet.size() : -1;
-
-      // Everything that is read in the predecessor blocks should also be included in the read set
-      // for the current block, so here we join the information from the predecessor blocks into the
-      // current read set.
-      boolean blockOrPredecessorMaybeReadAnyField = false;
-      for (BasicBlock predecessor : block.getPredecessors()) {
-        AbstractFieldSet predecessorReadSet =
-            result.getOrDefault(predecessor, EmptyFieldSet.getInstance());
-        if (predecessorReadSet.isBottom()) {
-          continue;
-        }
-        if (predecessorReadSet.isTop()) {
-          blockOrPredecessorMaybeReadAnyField = true;
-          break;
-        }
-        assert predecessorReadSet.isConcreteFieldSet();
-        if (!knownReadSet.isConcreteFieldSet()) {
-          knownReadSet = new ConcreteMutableFieldSet();
-        }
-        knownReadSet.asConcreteFieldSet().addAll(predecessorReadSet.asConcreteFieldSet());
-      }
-
-      if (!blockOrPredecessorMaybeReadAnyField) {
-        // Finally, we update the read set with the fields that are read by the instructions in the
-        // current block. This can be skipped if the block has already been processed.
-        if (seenBefore) {
-          assert verifyFieldSetContainsAllFieldReadsInBlock(knownReadSet, block, context);
-        } else {
-          for (Instruction instruction : block.getInstructions()) {
-            AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
-            if (instructionReadSet.isBottom()) {
-              continue;
-            }
-            if (instructionReadSet.isTop()) {
-              blockOrPredecessorMaybeReadAnyField = true;
-              break;
-            }
-            if (!knownReadSet.isConcreteFieldSet()) {
-              knownReadSet = new ConcreteMutableFieldSet();
-            }
-            knownReadSet.asConcreteFieldSet().addAll(instructionReadSet.asConcreteFieldSet());
+    putsPerField.forEach(
+        (field, fieldPuts) -> {
+          if (fieldPuts.size() > 1) {
+            return;
           }
-        }
-      }
-
-      boolean changed = false;
-      if (blockOrPredecessorMaybeReadAnyField) {
-        // Record that this block reads all fields.
-        result.put(block, UnknownFieldSet.getInstance());
-        changed = true;
-      } else {
-        if (knownReadSet != readSet) {
-          result.put(block, knownReadSet.asConcreteFieldSet());
-        }
-        if (knownReadSet.size() != oldSize) {
-          assert knownReadSet.size() > oldSize;
-          changed = true;
-        }
-      }
-
-      if (changed) {
-        // Rerun the analysis for all successors because the state of the current block changed.
-        worklist.addAll(block.getSuccessors());
-      }
-    }
-    return result;
-  }
-
-  private boolean verifyFieldSetContainsAllFieldReadsInBlock(
-      KnownFieldSet readSet, BasicBlock block, ProgramMethod context) {
-    for (Instruction instruction : block.getInstructions()) {
-      AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
-      assert !instructionReadSet.isTop();
-      if (instructionReadSet.isBottom()) {
-        continue;
-      }
-      for (DexEncodedField field : instructionReadSet.asConcreteFieldSet().getFields()) {
-        assert readSet.contains(field);
-      }
-    }
-    return true;
+          FieldInitializationInfo info = ListUtils.first(fieldPuts);
+          Instruction instruction = info.instruction;
+          if (instruction.isInvokeDirect()) {
+            asInstanceFieldValueAnalysis()
+                .recordInstanceFieldIsInitializedWithInfo(
+                    field, info.instanceFieldInitializationInfo);
+            return;
+          }
+          FieldInstruction fieldPut = instruction.asFieldInstruction();
+          if (checkDominance
+              && !getOrCreateDominatorTree()
+                  .dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
+            return;
+          }
+          boolean priorReadsWillReadSameValue =
+              !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero();
+          if (!priorReadsWillReadSameValue) {
+            if (fieldPut.isInstancePut()) {
+              InstancePut instancePut = fieldPut.asInstancePut();
+              if (fieldReadBeforeWriteAnalysis.isInstanceFieldMaybeReadBeforeInstruction(
+                  instancePut.object(), field, instancePut)) {
+                return;
+              }
+            } else {
+              if (fieldReadBeforeWriteAnalysis.isStaticFieldMaybeReadBeforeInstruction(
+                  field, fieldPut)) {
+                // TODO(b/172528424): Generalize to InstanceFieldValueAnalysis.
+                // At this point the value read in the field can be only the default static value,
+                // if read prior to the put, or the value put, if read after the put. We still want
+                // to record it because the default static value is typically null/0, so code
+                // present after a null/0 check can take advantage of the optimization.
+                DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field);
+                asStaticFieldValueAnalysis()
+                    .updateFieldOptimizationInfoWith2Values(
+                        field, fieldPut.value(), valueBeforePut);
+                return;
+              }
+            }
+          }
+          updateFieldOptimizationInfo(field, fieldPut, fieldPut.value());
+        });
   }
 
   abstract void updateFieldOptimizationInfo(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
index c4061ec..9ae014f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -11,7 +11,9 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -50,9 +52,10 @@
       AppView<AppInfoWithLiveness> appView,
       IRCode code,
       OptimizationFeedback feedback,
+      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
       DexClassAndMethod parentConstructor,
       InvokeDirect parentConstructorCall) {
-    super(appView, code, feedback);
+    super(appView, code, feedback, fieldReadBeforeWriteAnalysis);
     this.factory = appView.instanceFieldInitializationInfoFactory();
     this.parentConstructor = parentConstructor;
     this.parentConstructorCall = parentConstructorCall;
@@ -67,10 +70,11 @@
       IRCode code,
       ClassInitializerDefaultsResult classInitializerDefaultsResult,
       OptimizationFeedback feedback,
+      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
       Timing timing) {
     timing.begin("Analyze instance initializer");
     InstanceFieldInitializationInfoCollection result =
-        run(appView, code, classInitializerDefaultsResult, feedback);
+        run(appView, code, classInitializerDefaultsResult, feedback, fieldReadBeforeWriteAnalysis);
     timing.end();
     return result;
   }
@@ -79,7 +83,8 @@
       AppView<?> appView,
       IRCode code,
       ClassInitializerDefaultsResult classInitializerDefaultsResult,
-      OptimizationFeedback feedback) {
+      OptimizationFeedback feedback,
+      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
     assert appView.appInfo().hasLiveness();
     assert appView.enableWholeProgramOptimizations();
     assert code.context().getDefinition().isInstanceInitializer();
@@ -101,6 +106,7 @@
             appView.withLiveness(),
             code,
             feedback,
+            fieldReadBeforeWriteAnalysis,
             parentConstructor,
             parentConstructorCall);
     analysis.computeFieldOptimizationInfo(classInitializerDefaultsResult);
@@ -119,12 +125,12 @@
   }
 
   @Override
-  boolean isSubjectToOptimization(DexEncodedField field) {
-    return !field.isStatic() && field.getHolderType() == context.getHolderType();
+  boolean isSubjectToOptimization(ProgramField field) {
+    return !field.getAccessFlags().isStatic() && field.getHolderType() == context.getHolderType();
   }
 
   @Override
-  boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) {
+  boolean isSubjectToOptimizationIgnoringPinning(ProgramField field) {
     throw new Unreachable("Used by static analysis only.");
   }
 
@@ -203,7 +209,7 @@
     if (abstractValue.isSingleValue()) {
       return abstractValue.asSingleValue();
     }
-    DexType fieldType = field.type();
+    DexType fieldType = field.getType();
     if (fieldType.isClassType()) {
       ClassTypeElement dynamicLowerBoundType = value.getDynamicLowerBoundType(appView);
       TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
@@ -229,7 +235,7 @@
 
   private boolean fieldNeverWrittenBetweenInstancePutAndMethodExit(
       DexEncodedField field, InstancePut instancePut) {
-    if (field.isFinal()) {
+    if (field.getAccessFlags().isFinal()) {
       return true;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index 4df6b75..5f8381d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -15,6 +15,8 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueNull;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -49,8 +51,11 @@
   private final Map<Value, AbstractValue> computedValues = new IdentityHashMap<>();
 
   private StaticFieldValueAnalysis(
-      AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
-    super(appView, code, feedback);
+      AppView<AppInfoWithLiveness> appView,
+      IRCode code,
+      OptimizationFeedback feedback,
+      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
+    super(appView, code, feedback, fieldReadBeforeWriteAnalysis);
     builder = StaticFieldValues.builder(code.context().getHolder());
   }
 
@@ -59,13 +64,15 @@
       IRCode code,
       ClassInitializerDefaultsResult classInitializerDefaultsResult,
       OptimizationFeedback feedback,
+      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
       Timing timing) {
     assert appView.appInfo().hasLiveness();
     assert appView.enableWholeProgramOptimizations();
     assert code.context().getDefinition().isClassInitializer();
     timing.begin("Analyze class initializer");
     StaticFieldValues result =
-        new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback)
+        new StaticFieldValueAnalysis(
+                appView.withLiveness(), code, feedback, fieldReadBeforeWriteAnalysis)
             .analyze(classInitializerDefaultsResult);
     timing.end();
     return result;
@@ -117,19 +124,22 @@
   }
 
   @Override
-  boolean isSubjectToOptimization(DexEncodedField field) {
-    return field.isStatic()
-        && field.getHolderType() == context.getHolderType()
-        && appView.appInfo().isFieldOnlyWrittenInMethod(field, context.getDefinition());
-  }
-
-  @Override
-  boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) {
-    return field.isStatic()
+  boolean isSubjectToOptimization(ProgramField field) {
+    return field.getAccessFlags().isStatic()
         && field.getHolderType() == context.getHolderType()
         && appView
             .appInfo()
-            .isFieldOnlyWrittenInMethodIgnoringPinning(field, context.getDefinition());
+            .isFieldOnlyWrittenInMethod(field.getDefinition(), context.getDefinition());
+  }
+
+  @Override
+  boolean isSubjectToOptimizationIgnoringPinning(ProgramField field) {
+    return field.getAccessFlags().isStatic()
+        && field.getHolderType() == context.getHolderType()
+        && appView
+            .appInfo()
+            .isFieldOnlyWrittenInMethodIgnoringPinning(
+                field.getDefinition(), context.getDefinition());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index fc8d1f2..bc02268 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
 import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
 import com.android.tools.r8.ir.analysis.fieldaccess.FieldAccessAnalysis;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.InstanceFieldValueAnalysis;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValueAnalysis;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
@@ -979,10 +980,16 @@
       timing.end();
     }
 
+    FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis =
+        FieldReadBeforeWriteAnalysis.create(appView, code, method);
     if (fieldAccessAnalysis != null) {
       timing.begin("Analyze field accesses");
       fieldAccessAnalysis.recordFieldAccesses(
-          code, bytecodeMetadataProviderBuilder, feedback, methodProcessor);
+          code,
+          bytecodeMetadataProviderBuilder,
+          feedback,
+          fieldReadBeforeWriteAnalysis,
+          methodProcessor);
       if (classInitializerDefaultsResult != null) {
         fieldAccessAnalysis.acceptClassInitializerDefaultsResult(classInitializerDefaultsResult);
       }
@@ -999,11 +1006,21 @@
       if (method.getDefinition().isClassInitializer()) {
         staticFieldValues =
             StaticFieldValueAnalysis.run(
-                appView, code, classInitializerDefaultsResult, feedback, timing);
+                appView,
+                code,
+                classInitializerDefaultsResult,
+                feedback,
+                fieldReadBeforeWriteAnalysis,
+                timing);
       } else {
         instanceFieldInitializationInfos =
             InstanceFieldValueAnalysis.run(
-                appView, code, classInitializerDefaultsResult, feedback, timing);
+                appView,
+                code,
+                classInitializerDefaultsResult,
+                feedback,
+                fieldReadBeforeWriteAnalysis,
+                timing);
       }
     }
     enumUnboxer.recordEnumState(method.getHolder(), staticFieldValues);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
index c2e13af..125b53c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
@@ -86,7 +86,7 @@
           continue;
         }
 
-        if (method.getDefinition().isDefaultInstanceInitializer()
+        if (method.getDefinition().isDefaultInitializer()
             && appView.hasProguardCompatibilityActions()
             && appView.getProguardCompatibilityActions().isCompatInstantiated(method.getHolder())) {
           continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 5f9e4f2..c147322 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -96,6 +96,7 @@
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingExactDynamicEnumTypeForEnumWithSubtypesReason;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingInstanceFieldValueForEnumInstanceReason;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingObjectStateForEnumInstanceReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnboxedValueNonComparable;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedInstanceFieldValueForEnumInstanceReason;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedLibraryInvokeReason;
 import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
@@ -1369,6 +1370,24 @@
     return reason;
   }
 
+  private Reason comparableAsUnboxedValues(InvokeMethod invoke) {
+    assert invoke.inValues().size() == 2;
+    TypeElement type1 = invoke.getFirstArgument().getType();
+    TypeElement type2 = invoke.getLastArgument().getType();
+    DexProgramClass candidate1 = getEnumUnboxingCandidateOrNull(type1);
+    DexProgramClass candidate2 = getEnumUnboxingCandidateOrNull(type2);
+    assert candidate1 != null || candidate2 != null;
+    if (type1.isNullType() || type2.isNullType()) {
+      // Comparing an unboxed enum to null is always allowed.
+      return Reason.ELIGIBLE;
+    }
+    if (candidate1 == candidate2) {
+      // Comparing two unboxed enum values is valid only if they come from the same enum.
+      return Reason.ELIGIBLE;
+    }
+    return new UnboxedValueNonComparable(invoke.getInvokedMethod(), type1, type2);
+  }
+
   private Reason analyzeLibraryInvoke(
       InvokeMethod invoke,
       IRCode code,
@@ -1382,13 +1401,9 @@
       // TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
       if (singleTargetReference == factory.enumMembers.compareTo
           || singleTargetReference == factory.enumMembers.compareToWithObject) {
-        DexProgramClass otherEnumClass =
-            getEnumUnboxingCandidateOrNull(invoke.getLastArgument().getType());
-        if (otherEnumClass == enumClass || invoke.getLastArgument().getType().isNullType()) {
-          return Reason.ELIGIBLE;
-        }
+        return comparableAsUnboxedValues(invoke);
       } else if (singleTargetReference == factory.enumMembers.equals) {
-        return Reason.ELIGIBLE;
+        return comparableAsUnboxedValues(invoke);
       } else if (singleTargetReference == factory.enumMembers.nameMethod
           || singleTargetReference == factory.enumMembers.toString) {
         assert invoke.asInvokeMethodWithReceiver().getReceiver() == enumValue;
@@ -1415,6 +1430,17 @@
         // This is a hidden null check.
         return Reason.ELIGIBLE;
       }
+      if (singleTargetReference == factory.objectMembers.toString) {
+        assert invoke.asInvokeMethodWithReceiver().getReceiver() == enumValue;
+        addRequiredNameData(enumClass);
+        return Reason.ELIGIBLE;
+      }
+      if (singleTargetReference == factory.objectMembers.hashCode) {
+        return Reason.ELIGIBLE;
+      }
+      if (singleTargetReference == factory.objectMembers.equals) {
+        return comparableAsUnboxedValues(invoke);
+      }
       return new UnsupportedLibraryInvokeReason(singleTargetReference);
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
index 26c35b2..bfd215d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.google.common.collect.ImmutableList;
 
 public abstract class Reason {
@@ -268,6 +269,43 @@
     }
   }
 
+  public static class UnboxedValueNonComparable extends Reason {
+
+    private final DexMethod invokedMethod;
+    private final TypeElement type1;
+    private final TypeElement type2;
+
+    public UnboxedValueNonComparable(
+        DexMethod invokedMethod, TypeElement type1, TypeElement type2) {
+      this.invokedMethod = invokedMethod;
+      this.type1 = type1;
+      this.type2 = type2;
+    }
+
+    @Override
+    public Object getKind() {
+      return ImmutableList.of(getClass(), invokedMethod);
+    }
+
+    private static String typeInformation(TypeElement type) {
+      if (type.isClassType()) {
+        return type.asClassType().getClassType().toSourceString();
+      }
+      return type.toString();
+    }
+
+    @Override
+    public String toString() {
+      return "NonComparableElements("
+          + invokedMethod.toSourceString()
+          + " - "
+          + typeInformation(type1)
+          + " vs "
+          + typeInformation(type2)
+          + ")";
+    }
+  }
+
   public static class UnsupportedStaticFieldReason extends Reason {
 
     private final DexField field;
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 f518d5e..8b7bd6b 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
@@ -28,16 +28,16 @@
   public static final DefaultMethodOptimizationInfo DEFAULT_INSTANCE =
       new DefaultMethodOptimizationInfo();
 
-  static Set<DexType> UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT = ImmutableSet.of();
-  static int UNKNOWN_RETURNED_ARGUMENT = -1;
-  static boolean UNKNOWN_NEVER_RETURNS_NORMALLY = false;
-  static AbstractValue UNKNOWN_ABSTRACT_RETURN_VALUE = UnknownValue.getInstance();
-  static boolean UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT = false;
-  static boolean UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS = false;
-  static boolean UNKNOWN_MAY_HAVE_SIDE_EFFECTS = true;
-  static boolean UNKNOWN_RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS = false;
-  static BitSet NO_NULL_PARAMETER_OR_THROW_FACTS = null;
-  static BitSet NO_NULL_PARAMETER_ON_NORMAL_EXITS_FACTS = null;
+  static final Set<DexType> UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT = ImmutableSet.of();
+  static final int UNKNOWN_RETURNED_ARGUMENT = -1;
+  static final boolean UNKNOWN_NEVER_RETURNS_NORMALLY = false;
+  static final AbstractValue UNKNOWN_ABSTRACT_RETURN_VALUE = UnknownValue.getInstance();
+  static final boolean UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT = false;
+  static final boolean UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS = false;
+  static final boolean UNKNOWN_MAY_HAVE_SIDE_EFFECTS = true;
+  static final boolean UNKNOWN_RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS = false;
+  static final BitSet NO_NULL_PARAMETER_OR_THROW_FACTS = null;
+  static final BitSet NO_NULL_PARAMETER_ON_NORMAL_EXITS_FACTS = null;
 
   protected DefaultMethodOptimizationInfo() {}
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index f8e784e..c92342f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -420,9 +420,10 @@
                 }
                 builder.setParent(invokedMethod);
               } else {
-                builder
-                    .markAllFieldsAsRead()
-                    .setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
+                builder.markAllFieldsAsRead();
+                if (invoke.instructionMayHaveSideEffects(appView, context)) {
+                  builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
+                }
                 for (Value inValue : invoke.inValues()) {
                   if (couldBeReceiverValue(inValue, receiver, aliasesThroughAssumeAndCheckCasts)) {
                     builder.setReceiverMayEscapeOutsideConstructorChain();
@@ -453,9 +454,10 @@
           case INVOKE_VIRTUAL:
             {
               InvokeMethod invoke = instruction.asInvokeMethod();
-              builder
-                  .markAllFieldsAsRead()
-                  .setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
+              builder.markAllFieldsAsRead();
+              if (invoke.instructionMayHaveSideEffects(appView, context)) {
+                builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
+              }
               for (Value argument : invoke.arguments()) {
                 if (couldBeReceiverValue(argument, receiver, aliasesThroughAssumeAndCheckCasts)) {
                   builder.setReceiverMayEscapeOutsideConstructorChain();
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index c3fc071..1d92c11 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -17,7 +17,7 @@
 
 public class ProguardMapSupplier {
 
-  public static int PG_MAP_ID_LENGTH = 7;
+  public static final int PG_MAP_ID_LENGTH = 7;
 
   // Hash of the Proguard map (excluding the header up to and including the hash marker).
   public static class ProguardMapId {
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
similarity index 72%
rename from src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
rename to src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
index c050859..763c40f 100644
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
@@ -1,10 +1,9 @@
 // Copyright (c) 2017, 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.optimize.redundantbridgeremoval;
+package com.android.tools.r8.optimize;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
@@ -16,35 +15,23 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.optimize.InvokeSingleTargetExtractor;
 import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
-import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
+import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.KeepMethodInfo;
-import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.collect.Iterables;
-import java.util.Collections;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.function.Consumer;
 
 public class RedundantBridgeRemover {
 
   private final AppView<AppInfoWithLiveness> appView;
-  private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions;
-
-  private final InvokedReflectivelyFromPlatformAnalysis invokedReflectivelyFromPlatformAnalysis =
-      new InvokedReflectivelyFromPlatformAnalysis();
 
   public RedundantBridgeRemover(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
-    this.redundantBridgeRemovalOptions =
-        appView.options().getRedundantBridgeRemovalOptions().ensureInitialized();
   }
 
   private DexClassAndMethod getTargetForRedundantBridge(ProgramMethod method) {
@@ -65,9 +52,6 @@
     if (!isTargetingSuperMethod(method, targetExtractor.getKind(), target)) {
       return null;
     }
-    if (invokedReflectivelyFromPlatformAnalysis.isMaybeInvokedReflectivelyFromPlatform(method)) {
-      return null;
-    }
     // This is a visibility forward, so check for the direct target.
     DexClassAndMethod targetMethod =
         appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(target).getResolutionPair();
@@ -252,88 +236,4 @@
         });
     appView.pruneItems(prunedItemsBuilder.build(), executorService);
   }
-
-  class InvokedReflectivelyFromPlatformAnalysis {
-
-    // Maps each class to a boolean indicating if the class inherits from android.app.Fragment or
-    // android.app.ZygotePreload.
-    private final Map<DexClass, Boolean> cache = new ConcurrentHashMap<>();
-
-    boolean isMaybeInvokedReflectivelyFromPlatform(ProgramMethod method) {
-      return method.getDefinition().isDefaultInstanceInitializer()
-          && !method.getHolder().isAbstract()
-          && computeIsPlatformReflectingOnDefaultConstructor(method.getHolder());
-    }
-
-    private boolean computeIsPlatformReflectingOnDefaultConstructor(DexProgramClass clazz) {
-      Boolean cacheResult = cache.get(clazz);
-      if (cacheResult != null) {
-        return cacheResult;
-      }
-      WorkList.<WorklistItem>newIdentityWorkList(new NotProcessedWorklistItem(clazz))
-          .process(WorklistItem::accept);
-      assert cache.containsKey(clazz);
-      return cache.get(clazz);
-    }
-
-    abstract class WorklistItem implements Consumer<WorkList<WorklistItem>> {
-
-      protected final DexClass clazz;
-
-      WorklistItem(DexClass clazz) {
-        this.clazz = clazz;
-      }
-
-      Iterable<DexClass> getImmediateSupertypes() {
-        return IterableUtils.flatMap(
-            clazz.allImmediateSupertypes(),
-            supertype -> {
-              DexClass definition = appView.definitionFor(supertype);
-              return definition != null
-                  ? Collections.singletonList(definition)
-                  : Collections.emptyList();
-            });
-      }
-    }
-
-    class NotProcessedWorklistItem extends WorklistItem {
-
-      NotProcessedWorklistItem(DexClass clazz) {
-        super(clazz);
-      }
-
-      @Override
-      public void accept(WorkList<WorklistItem> worklist) {
-        // Enqueue a worklist item to process the current class after processing its super classes.
-        worklist.addFirstIgnoringSeenSet(new ProcessedWorklistItem(clazz));
-        // Enqueue all superclasses for processing.
-        for (DexClass supertype : getImmediateSupertypes()) {
-          if (!cache.containsKey(supertype)) {
-            worklist.addFirstIgnoringSeenSet(new NotProcessedWorklistItem(supertype));
-          }
-        }
-      }
-    }
-
-    class ProcessedWorklistItem extends WorklistItem {
-
-      ProcessedWorklistItem(DexClass clazz) {
-        super(clazz);
-      }
-
-      @Override
-      public void accept(WorkList<WorklistItem> worklist) {
-        cache.put(
-            clazz,
-            Iterables.any(
-                getImmediateSupertypes(),
-                supertype ->
-                    cache.get(supertype)
-                        || (supertype.isLibraryClass()
-                            && redundantBridgeRemovalOptions
-                                .isPlatformReflectingOnDefaultConstructorInSubclasses(
-                                    supertype.asLibraryClass()))));
-      }
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
deleted file mode 100644
index a902764..0000000
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2023, 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.optimize.redundantbridgeremoval;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexLibraryClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.SetUtils;
-import java.util.Collections;
-import java.util.Set;
-
-public class RedundantBridgeRemovalOptions {
-
-  private final InternalOptions options;
-
-  private Set<DexType> noConstructorShrinkingHierarchies;
-
-  public RedundantBridgeRemovalOptions(InternalOptions options) {
-    this.options = options;
-  }
-
-  public void clearNoConstructorShrinkingHierarchiesForTesting() {
-    noConstructorShrinkingHierarchies = Collections.emptySet();
-  }
-
-  public RedundantBridgeRemovalOptions ensureInitialized() {
-    if (noConstructorShrinkingHierarchies == null) {
-      DexItemFactory dexItemFactory = options.dexItemFactory();
-      noConstructorShrinkingHierarchies =
-          SetUtils.newIdentityHashSet(
-              dexItemFactory.androidAppFragment, dexItemFactory.androidAppZygotePreload);
-    }
-    return this;
-  }
-
-  public boolean isPlatformReflectingOnDefaultConstructorInSubclasses(DexLibraryClass clazz) {
-    return noConstructorShrinkingHierarchies.contains(clazz.getType());
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 39d8035..07a5bb2 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -954,14 +954,6 @@
     return staticInitializer != null && isFieldOnlyWrittenInMethod(field, staticInitializer);
   }
 
-  public boolean mayPropagateArgumentsTo(ProgramMethod method) {
-    DexMethod reference = method.getReference();
-    return method.getDefinition().hasCode()
-        && !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
-        && !neverReprocess.contains(reference)
-        && !keepInfo.getMethodInfo(method).isPinned(options());
-  }
-
   public boolean mayPropagateValueFor(
       AppView<AppInfoWithLiveness> appView, DexClassAndMember<?, ?> member) {
     assert checkIfObsolete();
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 ca51281..86cdb5a 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1624,9 +1624,9 @@
 
   static class FieldAccessMetadata {
 
-    private static int DEFERRED_MASK = 1;
-    private static int FROM_METHOD_HANDLE_MASK = 2;
-    private static int FROM_RECORD_METHOD_HANDLE_MASK = 4;
+    private static final int DEFERRED_MASK = 1;
+    private static final int FROM_METHOD_HANDLE_MASK = 2;
+    private static final int FROM_RECORD_METHOD_HANDLE_MASK = 4;
 
     static FieldAccessMetadata DEFAULT = new FieldAccessMetadata(0);
     static FieldAccessMetadata FROM_METHOD_HANDLE =
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 7286734..722a412 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -74,7 +74,6 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.MapVersion;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
-import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalOptions;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
 import com.android.tools.r8.profile.art.ArtProfileOptions;
@@ -890,8 +889,6 @@
   private final OpenClosedInterfacesOptions openClosedInterfacesOptions =
       new OpenClosedInterfacesOptions();
   private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions();
-  private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions =
-      new RedundantBridgeRemovalOptions(this);
   private final KotlinOptimizationOptions kotlinOptimizationOptions =
       new KotlinOptimizationOptions();
   private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
@@ -960,10 +957,6 @@
     return cfCodeAnalysisOptions;
   }
 
-  public RedundantBridgeRemovalOptions getRedundantBridgeRemovalOptions() {
-    return redundantBridgeRemovalOptions;
-  }
-
   public DumpInputFlags getDumpInputFlags() {
     return dumpInputFlags;
   }
@@ -1677,6 +1670,7 @@
     private boolean enable =
         !Version.isDevelopmentVersion()
             || System.getProperty("com.android.tools.r8.disableHorizontalClassMerging") == null;
+    private boolean enableInitial = true;
     // TODO(b/205611444): Enable by default.
     private boolean enableClassInitializerDeadlockDetection = true;
     private boolean enableInterfaceMerging =
@@ -1692,6 +1686,10 @@
       enable = false;
     }
 
+    public void disableInitialRoundOfClassMerging() {
+      enableInitial = false;
+    }
+
     public void disableSyntheticMerging() {
       enableSyntheticMerging = false;
     }
@@ -1729,7 +1727,7 @@
         return false;
       }
       if (mode.isInitial()) {
-        return inlinerOptions.enableInlining && isShrinking();
+        return enableInitial && inlinerOptions.enableInlining && isShrinking();
       }
       assert mode.isFinal();
       return true;
@@ -2064,7 +2062,7 @@
       }
     }
 
-    public static int NO_LIMIT = -1;
+    public static final int NO_LIMIT = -1;
 
     public ArgumentPropagatorEventConsumer argumentPropagatorEventConsumer =
         ArgumentPropagatorEventConsumer.emptyConsumer();
@@ -2919,7 +2917,8 @@
   }
 
   public boolean canHaveNonReboundConstructorInvoke() {
-    return isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
+    // TODO(b/246679983): Turned off while diagnosing b/246679983.
+    return false && isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
   }
 
   // b/238399429 Some art 6 vms have issues with multiple monitors in the same method
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index 206a8ab..c22f43e 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -58,7 +58,8 @@
    * runtime error.
    */
   public boolean canHaveNonReboundConstructorInvoke() {
-    return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
+    // TODO(b/246679983): Turned off while diagnosing b/246679983.
+    return false && isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
   }
 
   public boolean canUseDefaultAndStaticInterfaceMethods() {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
new file mode 100644
index 0000000..70f55b4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2023, 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.classmerging.horizontal;
+
+import static org.junit.Assert.assertTrue;
+
+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.utils.AndroidApiLevel;
+import org.junit.Assume;
+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 HorizontalClassMergingAfterConstructorShrinkingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDexRuntimes()
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.L)
+        .build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    // TODO(b/276385221): Disabled constructor shrinking for now
+    Assume.assumeTrue(parameters.canHaveNonReboundConstructorInvoke());
+    assertTrue(parameters.canHaveNonReboundConstructorInvoke());
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.horizontalClassMergerOptions().disableInitialRoundOfClassMerging())
+        .addHorizontallyMergedClassesInspector(
+            inspector ->
+                inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters)
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/276385221): Should not trigger A.<init>.
+        .assertSuccessWithOutputLines("Ouch!", "B");
+  }
+
+  static class Main {
+
+    static {
+      new B().setFieldOnB().printFieldOnB();
+    }
+
+    public static void main(String[] args) {
+      if (System.currentTimeMillis() < 0) {
+        new A().setFieldOnA().printFieldOnA();
+      }
+    }
+  }
+
+  @NeverClassInline
+  static class A {
+
+    Object field;
+
+    A() {
+      System.out.println("Ouch!");
+    }
+
+    @NeverInline
+    A setFieldOnA() {
+      field = "A";
+      return this;
+    }
+
+    @NeverInline
+    void printFieldOnA() {
+      System.out.println(field);
+    }
+  }
+
+  @NeverClassInline
+  static class B {
+
+    Object field;
+
+    // Removed by constructor shrinking.
+    B() {}
+
+    @NeverInline
+    B setFieldOnB() {
+      field = "B";
+      return this;
+    }
+
+    @NeverInline
+    void printFieldOnB() {
+      System.out.println(field);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
index e8d7572..b7bb268 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
@@ -13,14 +13,12 @@
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
 import com.google.common.collect.ImmutableSet;
 import java.util.List;
-import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -49,9 +47,6 @@
 
   @Test
   public void test() throws Throwable {
-    Assume.assumeTrue(
-        "b/273921056",
-        parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
     R8TestCompileResult compileResult =
         testForR8(parameters.getBackend())
             .addInnerClasses(getClass())
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 22529e5..3d2be09 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -223,9 +223,6 @@
   // This test has a cycle in the call graph consisting of the methods A.<init> and B.<init>.
   @Test
   public void testCallGraphCycle() throws Throwable {
-    Assume.assumeTrue(
-        "b/273921056",
-        parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
     String main = "classmerging.CallGraphCycleTest";
     Path[] programFiles =
         new Path[] {
@@ -319,9 +316,6 @@
 
   @Test
   public void testFieldCollision() throws Throwable {
-    Assume.assumeTrue(
-        "b/273921056",
-        parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
     String main = "classmerging.FieldCollisionTest";
     Path[] programFiles =
         new Path[] {
@@ -429,9 +423,6 @@
 
   @Test
   public void testPinnedParameterTypes() throws Throwable {
-    Assume.assumeTrue(
-        "b/273921056",
-        parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
     String main = "classmerging.PinnedParameterTypesTest";
     Path[] programFiles =
         new Path[] {
@@ -457,9 +448,6 @@
 
   @Test
   public void testPinnedArrayParameterTypes() throws Throwable {
-    Assume.assumeTrue(
-        "b/273921056",
-        parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
     String main = "classmerging.PinnedArrayParameterTypesTest";
     Path[] programFiles =
         new Path[] {
@@ -853,9 +841,6 @@
   //   }
   @Test
   public void testSuperCallToMergedClassIsRewritten() throws Throwable {
-    Assume.assumeTrue(
-        "b/273921056",
-        parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
     assumeTrue(parameters.isDexRuntime()); // Due to smali input.
     assumeFalse(parameters.getRuntime().asDex().getVm().getVersion() == Version.V5_1_1);
     assumeFalse(parameters.getRuntime().asDex().getVm().getVersion() == Version.V6_0_1);
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
index 6676e2c..265bb19 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -36,7 +36,6 @@
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 import java.util.stream.Collectors;
-import org.junit.Assume;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
@@ -143,10 +142,6 @@
   public void testContinuousSingleStep() throws Throwable {
     DebugTestConfig config = compiledJars.apply(jarPath);
     assert config != null;
-    Assume.assumeTrue(
-        "b/273921056",
-        config.isCfRuntime()
-            || !config.getRuntime().asDex().getVersion().isEqualTo(Version.V14_0_0));
     runContinuousTest(mainClass, config, MAIN_METHOD_NAME);
   }
 
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
index a2254be..35178f8 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
-import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
 import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.FrameInspector;
 import java.util.ArrayList;
@@ -15,7 +14,6 @@
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.Tag;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
 import org.junit.Assert;
-import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -279,10 +277,6 @@
 
   @Test
   public void testLocals_MoreThan16() throws Throwable {
-    Assume.assumeTrue(
-        "b/273921056",
-        config.isCfRuntime()
-            || !config.getRuntime().asDex().getVersion().isEqualTo(Version.V14_0_0));
     final int minIndex = 1;
     final int maxIndex = 16;
     Map<String, Value> arrayLocals = new HashMap<>();
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/CallToOtherEnumCompareToMethodNegativeUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/CallToOtherEnumCompareToMethodNegativeUnboxingTest.java
new file mode 100644
index 0000000..f190a34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/CallToOtherEnumCompareToMethodNegativeUnboxingTest.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2023, 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.enumunboxing;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.codeinspector.EnumUnboxingInspector;
+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 CallToOtherEnumCompareToMethodNegativeUnboxingTest extends EnumUnboxingTestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addInnerClasses(getClass())
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            parameters.isCfRuntime()
+                || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0),
+            runResult -> runResult.assertFailureWithErrorThatThrows(ClassCastException.class),
+            runResult -> runResult.assertSuccessWithOutputLines("0"));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addKeepClassRules(Foo.class)
+        .addEnumUnboxingInspector(EnumUnboxingInspector::assertNoEnumsUnboxed)
+        .setMinApi(parameters)
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            parameters.isCfRuntime()
+                || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0),
+            runResult -> runResult.assertFailureWithErrorThatThrows(ClassCastException.class),
+            runResult -> runResult.assertSuccessWithOutputLines("0"));
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      Enum<?> fooEnum = Foo.A;
+      Enum<Bar> fooEnumInDisguise = (Enum<Bar>) fooEnum;
+      System.out.println(fooEnumInDisguise.compareTo(Bar.B));
+    }
+  }
+
+  enum Foo {
+    A
+  }
+
+  enum Bar {
+    B
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/InvalidEqualsEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/InvalidEqualsEnumUnboxingTest.java
new file mode 100644
index 0000000..2ed2d40
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/InvalidEqualsEnumUnboxingTest.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2023, 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.enumunboxing;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.EnumUnboxingInspector;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InvalidEqualsEnumUnboxingTest extends EnumUnboxingTestBase {
+
+  private final TestParameters parameters;
+  private final boolean enumValueOptimization;
+  private final EnumKeepRules enumKeepRules;
+
+  @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+  public static List<Object[]> data() {
+    return enumUnboxingTestParameters();
+  }
+
+  public InvalidEqualsEnumUnboxingTest(
+      TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
+    this.parameters = parameters;
+    this.enumValueOptimization = enumValueOptimization;
+    this.enumKeepRules = enumKeepRules;
+  }
+
+  @Test
+  public void testEnumUnboxing() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(EnumEquals.class)
+        .addEnumUnboxingInspector(EnumUnboxingInspector::assertNoEnumsUnboxed)
+        .enableNeverClassInliningAnnotations()
+        .addKeepRules(enumKeepRules.getKeepRules())
+        .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), EnumEquals.class)
+        .assertSuccessWithOutputLines("false", "false");
+  }
+
+  static class EnumEquals {
+
+    @NeverClassInline
+    enum MyEnumEquals {
+      A,
+      B
+    }
+
+    @NeverClassInline
+    enum MyEnumEquals1 {
+      A,
+      B
+    }
+
+    @NeverClassInline
+    enum MyEnumEquals2 {
+      A,
+      B
+    }
+
+    public static void main(String[] args) {
+      Object guineaPig = new Object();
+      System.out.println(MyEnumEquals.A.equals(guineaPig));
+      System.out.println(MyEnumEquals1.A.equals(MyEnumEquals2.A));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index 2cdeffd..bb382f8 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.MethodProcessorWithWave;
@@ -96,7 +97,11 @@
         method -> {
           IRCode code = method.buildIR(appView);
           fieldAccessAnalysis.recordFieldAccesses(
-              code, BytecodeMetadataProvider.builder(), feedback, new PrimaryMethodProcessorMock());
+              code,
+              BytecodeMetadataProvider.builder(),
+              feedback,
+              FieldReadBeforeWriteAnalysis.trivial(),
+              new PrimaryMethodProcessorMock());
         });
 
     int bitsReadInBitField = feedback.bitsReadPerField.getInt(uniqueFieldByName(clazz, "bitField"));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java
new file mode 100644
index 0000000..972cc79
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2023, 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.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+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 NonNullInstanceFieldPropagationTest 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)
+        .setMinApi(parameters)
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject mainClassSubject = inspector.clazz(Main.class);
+              assertThat(mainClassSubject, isPresent());
+              assertEquals(1, inspector.allClasses().size());
+              assertEquals(1, mainClassSubject.allMethods().size());
+              assertEquals(0, mainClassSubject.allFields().size());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithEmptyOutput();
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      Main p = new Main();
+      p.mThreadChecker.assertOnValidThread();
+    }
+
+    public final ThreadChecker mThreadChecker = new ThreadChecker();
+  }
+
+  static class ThreadChecker {
+    public void assertOnValidThread() {
+      if (System.currentTimeMillis() == 0) {
+        throw new RuntimeException();
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
index a7c2581..d843a4c 100644
--- a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -72,6 +72,10 @@
     return dexVm.getVersion().isNewerThanOrEqual(Version.V4_4_4);
   }
 
+  static boolean isAndroidK(DexVm dexVm, Tool tool) {
+    return dexVm.getVersion().isEqualTo(Version.V4_4_4);
+  }
+
   static boolean isAndroidLOrAbove(DexVm dexVm, Tool tool) {
     return dexVm.getVersion().isNewerThan(Version.V4_4_4);
   }
@@ -126,11 +130,13 @@
           .put("ClassObjectReference.ReflectedType002Test", RunJdwpTests::isAndroidLOrAbove)
           .put("ClassType.InvokeMethodTest", RunJdwpTests::isAndroidLOrAbove)
           .put("ClassType.InvokeMethod002Test", RunJdwpTests::isAndroidLOrAbove)
-          .put("ClassType.InvokeMethodAfterMultipleThreadSuspensionTest",
+          .put(
+              "ClassType.InvokeMethodAfterMultipleThreadSuspensionTest",
               RunJdwpTests::isAndroidNOrAbove)
           .put("ClassType.InvokeMethodWithSuspensionTest", RunJdwpTests::isAndroidMOrAbove)
           .put("ClassType.NewInstanceTest", RunJdwpTests::isAndroidLOrAbove)
-          .put("ClassType.NewInstanceAfterMultipleThreadSuspensionTest",
+          .put(
+              "ClassType.NewInstanceAfterMultipleThreadSuspensionTest",
               RunJdwpTests::isAndroidNOrAbove)
           .put("ClassType.NewInstanceStringTest", RunJdwpTests::isAndroidOOrAbove)
           .put("ClassType.NewInstanceTagTest", RunJdwpTests::isAndroidNOrAbove)
@@ -164,7 +170,9 @@
           .put("Method.IsObsoleteTest", RunJdwpTests::isAndroidNOrAbove)
           .put("Method.LineTableTest", or(RunJdwpTests::isAndroidKOrAbove, RunJdwpTests::isJava))
           .put("Method.VariableTableTest", RunJdwpTests::isAndroidOOrAbove)
-          .put("Method.VariableTableWithGenericTest", RunJdwpTests::isAndroidOOrAbove)
+          .put(
+              "Method.VariableTableWithGenericTest",
+              or(RunJdwpTests::isAndroidOOrAbove, RunJdwpTests::isAndroidK))
           .put("MultiSession.AttachConnectorTest", RunJdwpTests::isAndroidLOrAbove)
           .put("MultiSession.BreakpointTest", RunJdwpTests::isAndroidLOrAbove)
           .put("MultiSession.ClassObjectIDTest", RunJdwpTests::isAndroidLOrAbove)
@@ -183,7 +191,8 @@
           .put("ObjectReference.GetValues002Test", RunJdwpTests::isAndroidLOrAbove)
           .put("ObjectReference.InvokeMethodDefaultTest", RunJdwpTests::isAndroidNOrAbove)
           .put("ObjectReference.InvokeMethodDefault002Test", RunJdwpTests::isAndroidNOrAbove)
-          .put("ObjectReference.InvokeMethodAfterMultipleThreadSuspensionTest",
+          .put(
+              "ObjectReference.InvokeMethodAfterMultipleThreadSuspensionTest",
               RunJdwpTests::isAndroidNOrAbove)
           .put("ObjectReference.InvokeMethodWithSuspensionTest", RunJdwpTests::isAndroidMOrAbove)
           .put("ObjectReference.IsCollectedTest", RunJdwpTests::isAndroidLOrAbove)
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
index 92404f0..8078ed6 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -101,6 +101,7 @@
         "  void <init>();",
         "}",
         "-neverclassinline class *",
+        "-nohorizontalclassmerging class adaptresourcefilenames.B",
         "-nohorizontalclassmerging class adaptresourcefilenames.pkg.C",
         "-nohorizontalclassmerging class adaptresourcefilenames.pkg.innerpkg.D");
   }
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
index c83d81d..2d38860 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
@@ -75,22 +75,15 @@
   }
 
   private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    inspect(profileInspector, inspector, false, false);
+    inspect(profileInspector, inspector, false);
   }
 
   private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    inspect(
-        profileInspector,
-        inspector,
-        parameters.canHaveNonReboundConstructorInvoke(),
-        parameters.isCfRuntime());
+    inspect(profileInspector, inspector, parameters.isCfRuntime());
   }
 
   public void inspect(
-      ArtProfileInspector profileInspector,
-      CodeInspector inspector,
-      boolean canHaveNonReboundConstructorInvoke,
-      boolean canUseLambdas) {
+      ArtProfileInspector profileInspector, CodeInspector inspector, boolean canUseLambdas) {
     ClassSubject mainClassSubject = inspector.clazz(Main.class);
     assertThat(mainClassSubject, isPresent());
 
@@ -103,26 +96,21 @@
     assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
 
     MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
-    assertThat(
-        lambdaInitializerSubject,
-        notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
+    assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
 
     MethodSubject lambdaMainMethodSubject =
         lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
     assertThat(lambdaMainMethodSubject, notIf(isPresent(), canUseLambdas));
 
     if (canUseLambdas) {
-      profileInspector.assertContainsMethodRule(mainMethodSubject);
+      profileInspector.assertContainsMethodRule(mainMethodSubject).assertContainsNoOtherRules();
     } else {
       profileInspector
           .assertContainsClassRules(lambdaClassSubject)
-          .assertContainsMethodRules(mainMethodSubject, lambdaMainMethodSubject)
-          .applyIf(
-              !canHaveNonReboundConstructorInvoke,
-              i -> i.assertContainsMethodRule(lambdaInitializerSubject));
+          .assertContainsMethodRules(
+              mainMethodSubject, lambdaInitializerSubject, lambdaMainMethodSubject)
+          .assertContainsNoOtherRules();
     }
-
-    profileInspector.assertContainsNoOtherRules();
   }
 
   static class Main {
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
index 577518d..7d9e9ab 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
@@ -104,22 +104,17 @@
 
   private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector)
       throws Exception {
-    inspect(profileInspector, inspector, false, parameters.canUseNestBasedAccessesWhenDesugaring());
+    inspect(profileInspector, inspector, parameters.canUseNestBasedAccessesWhenDesugaring());
   }
 
   private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector)
       throws Exception {
-    inspect(
-        profileInspector,
-        inspector,
-        parameters.canHaveNonReboundConstructorInvoke(),
-        parameters.canUseNestBasedAccesses());
+    inspect(profileInspector, inspector, parameters.canUseNestBasedAccesses());
   }
 
   private void inspect(
       ArtProfileInspector profileInspector,
       CodeInspector inspector,
-      boolean canHaveNonReboundConstructorInvoke,
       boolean canUseNestBasedAccesses)
       throws Exception {
     ClassSubject nestMemberClassSubject = inspector.clazz(NestMember.class);
@@ -133,7 +128,7 @@
         syntheticConstructorArgumentClassSubject, notIf(isPresent(), canUseNestBasedAccesses));
 
     MethodSubject instanceInitializer = nestMemberClassSubject.init();
-    assertThat(instanceInitializer, notIf(isPresent(), canHaveNonReboundConstructorInvoke));
+    assertThat(instanceInitializer, isPresent());
 
     MethodSubject instanceInitializerWithSyntheticArgumentSubject =
         syntheticConstructorArgumentClassSubject.isPresent()
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index 6d03704..5f1886d 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -149,7 +149,6 @@
         profileInspector,
         inspector,
         SyntheticItemsTestUtils.syntheticRecordTagClass(),
-        false,
         parameters.canUseNestBasedAccessesWhenDesugaring(),
         parameters.canUseRecordsWhenDesugaring());
   }
@@ -159,7 +158,6 @@
         profileInspector,
         inspector,
         RECORD_REFERENCE,
-        parameters.canHaveNonReboundConstructorInvoke(),
         parameters.canUseNestBasedAccesses(),
         parameters.canUseRecords());
   }
@@ -168,7 +166,6 @@
       ArtProfileInspector profileInspector,
       CodeInspector inspector,
       ClassReference recordClassReference,
-      boolean canHaveNonReboundConstructorInvoke,
       boolean canUseNestBasedAccesses,
       boolean canUseRecords) {
     ClassSubject mainClassSubject = inspector.clazz(MAIN_REFERENCE);
@@ -180,14 +177,11 @@
     ClassSubject recordTagClassSubject = inspector.clazz(recordClassReference);
     assertThat(recordTagClassSubject, notIf(isPresent(), canUseRecords));
     if (!canUseRecords) {
-      assertEquals(
-          canHaveNonReboundConstructorInvoke ? 0 : 1, recordTagClassSubject.allMethods().size());
+      assertEquals(1, recordTagClassSubject.allMethods().size());
     }
 
     MethodSubject recordTagInstanceInitializerSubject = recordTagClassSubject.init();
-    assertThat(
-        recordTagInstanceInitializerSubject,
-        notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseRecords));
+    assertThat(recordTagInstanceInitializerSubject, notIf(isPresent(), canUseRecords));
 
     ClassSubject personRecordClassSubject = inspector.clazz(PERSON_REFERENCE);
     assertThat(personRecordClassSubject, isPresent());
@@ -296,13 +290,11 @@
                         hashCodeHelperClassSubject,
                         toStringHelperClassSubject)
                     .assertContainsMethodRules(
+                        recordTagInstanceInitializerSubject,
                         equalsHelperMethodSubject,
                         getFieldsAsObjectsMethodSubject,
                         hashCodeHelperMethodSubject,
-                        toStringHelperMethodSubject)
-                    .applyIf(
-                        !canHaveNonReboundConstructorInvoke,
-                        j -> j.assertContainsMethodRule(recordTagInstanceInitializerSubject)))
+                        toStringHelperMethodSubject))
         .assertContainsNoOtherRules();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
index ef6b3a3..385f94b 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
@@ -68,7 +68,6 @@
     public void inspect(
         ArtProfileInspector profileInspector,
         CodeInspector inspector,
-        boolean canHaveNonReboundConstructorInvoke,
         boolean canUseLambdas,
         boolean canAccessModifyLambdaImplementationMethods) {
       ClassSubject mainClassSubject = inspector.clazz(Main.class);
@@ -107,9 +106,7 @@
       assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
 
       MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
-      assertThat(
-          lambdaInitializerSubject,
-          notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
+      assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
 
       MethodSubject lambdaMainMethodSubject =
           lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -122,9 +119,7 @@
 
       MethodSubject otherLambdaInitializerSubject =
           otherLambdaClassSubject.uniqueInstanceInitializer();
-      assertThat(
-          otherLambdaInitializerSubject,
-          notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
+      assertThat(otherLambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
 
       MethodSubject otherLambdaMainMethodSubject =
           otherLambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -154,12 +149,8 @@
             // interface method implementation does not need to be included in the profile.
             profileInspector
                 .assertContainsClassRules(lambdaClassSubject, otherLambdaClassSubject)
-                .assertContainsMethodRules(mainMethodSubject)
-                .applyIf(
-                    !canHaveNonReboundConstructorInvoke,
-                    i ->
-                        i.assertContainsMethodRules(
-                            lambdaInitializerSubject, otherLambdaInitializerSubject))
+                .assertContainsMethodRules(
+                    mainMethodSubject, lambdaInitializerSubject, otherLambdaInitializerSubject)
                 .assertContainsNoOtherRules();
             break;
           case IMPLEMENTATION_METHOD:
@@ -230,16 +221,11 @@
   }
 
   private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    artProfileInputOutput.inspect(profileInspector, inspector, false, false, true);
+    artProfileInputOutput.inspect(profileInspector, inspector, false, true);
   }
 
   private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    artProfileInputOutput.inspect(
-        profileInspector,
-        inspector,
-        parameters.canHaveNonReboundConstructorInvoke(),
-        parameters.isCfRuntime(),
-        false);
+    artProfileInputOutput.inspect(profileInspector, inspector, parameters.isCfRuntime(), false);
   }
 
   static class Main {
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java b/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
index 31d557e..babd7a7 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
@@ -59,7 +59,10 @@
     Bar value = new Bar();
 
     public static void main(String[] args) {
-      new Foo();
+      Foo foo = new Foo();
+      if (System.currentTimeMillis() < 0) {
+        System.out.println(foo);
+      }
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
index 7dacf64..534bbb9 100644
--- a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
@@ -5,21 +5,14 @@
 package com.android.tools.r8.shaking.constructor;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assume.assumeFalse;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.apimodel.ApiModelingTestHelper;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -29,168 +22,48 @@
 @RunWith(Parameterized.class)
 public class ForwardingConstructorUsedFromPlatformShakingOnDexTest extends TestBase {
 
-  private static final String APPLICATION_INFO_DESCRIPTOR = "Landroid/content/pm/ApplicationInfo;";
-  private static final String FRAGMENT_DESCRIPTOR = "Landroid/app/Fragment;";
-  private static final String ZYGOTE_PRELOAD_DESCRIPTOR = "Landroid/app/ZygotePreload;";
-
-  private static final String EXPECTED_OUTPUT =
-      StringUtils.lines(
-          "Fragment.onCreate()", "MyFragment.onCreate()", "MyZygotePreload.doPreload()");
-
-  private static List<byte[]> transformedProgramClassFileData;
-  private static List<byte[]> transformedLibraryClassFileData;
-
   @Parameter(0)
-  public boolean enableModeling;
-
-  @Parameter(1)
   public TestParameters parameters;
 
-  @Parameters(name = "{1}, modeling: {0}")
-  public static List<Object[]> data() {
-    return buildParameters(
-        BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
-  }
-
-  @BeforeClass
-  public static void setup() throws Exception {
-    transformedProgramClassFileData = getTransformedProgramClasses();
-    transformedLibraryClassFileData = getTransformedLibraryClasses();
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   @Test
-  public void testRuntime() throws Exception {
-    assumeFalse(enableModeling);
-    testForRuntime(parameters)
-        .addProgramClassFileData(transformedProgramClassFileData)
-        .addProgramClassFileData(transformedLibraryClassFileData)
-        .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutput(EXPECTED_OUTPUT);
-  }
-
-  @Test
-  public void testR8() throws Exception {
+  public void test() throws Exception {
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(transformedProgramClassFileData)
-        .addLibraryClassFileData(transformedLibraryClassFileData)
+        .addProgramClasses(Main.class, MyFragment.class)
+        .addLibraryClasses(Fragment.class)
         .addLibraryFiles(parameters.getDefaultRuntimeLibrary())
         .addKeepMainRule(Main.class)
-        .applyIf(
-            !enableModeling,
-            testBuilder ->
-                testBuilder.addOptionsModification(
-                    options ->
-                        options
-                            .getRedundantBridgeRemovalOptions()
-                            .clearNoConstructorShrinkingHierarchiesForTesting()))
-        // Since Fragment is first defined in API 11.
-        .apply(ApiModelingTestHelper::disableStubbingOfClasses)
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters)
         .compile()
         .inspect(this::inspect)
-        .addRunClasspathClassFileData(transformedLibraryClassFileData)
+        .addBootClasspathClasses(Fragment.class)
         .run(parameters.getRuntime(), Main.class)
-        .applyIf(
-            enableModeling || !parameters.canHaveNonReboundConstructorInvoke(),
-            runResult -> runResult.assertSuccessWithOutput(EXPECTED_OUTPUT),
-            runResult -> runResult.assertFailureWithErrorThatThrows(NoSuchMethodException.class));
-  }
-
-  private static List<byte[]> getTransformedProgramClasses() throws Exception {
-    return ImmutableList.of(
-        transformer(Main.class)
-            .replaceClassDescriptorInMethodInstructions(
-                descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
-            .replaceClassDescriptorInMethodInstructions(
-                descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
-            .transform(),
-        transformer(MyFragment.class)
-            .replaceClassDescriptorInMethodInstructions(
-                descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
-            .setSuper(FRAGMENT_DESCRIPTOR)
-            .transform(),
-        transformer(MyZygotePreload.class)
-            .replaceClassDescriptorInMembers(
-                descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
-            .setImplementsClassDescriptors(ZYGOTE_PRELOAD_DESCRIPTOR)
-            .transform());
-  }
-
-  private static List<byte[]> getTransformedLibraryClasses() throws Exception {
-    return ImmutableList.of(
-        transformer(ApplicationInfo.class)
-            .setClassDescriptor(APPLICATION_INFO_DESCRIPTOR)
-            .transform(),
-        transformer(Fragment.class).setClassDescriptor(FRAGMENT_DESCRIPTOR).transform(),
-        transformer(Platform.class)
-            .replaceClassDescriptorInMembers(descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
-            .replaceClassDescriptorInMethodInstructions(
-                descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
-            .replaceClassDescriptorInMembers(
-                descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
-            .replaceClassDescriptorInMethodInstructions(
-                descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
-            .replaceClassDescriptorInMethodInstructions(
-                descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
-            .transform(),
-        transformer(ZygotePreload.class)
-            .replaceClassDescriptorInMembers(
-                descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
-            .setClassDescriptor(ZYGOTE_PRELOAD_DESCRIPTOR)
-            .transform());
+        .assertSuccessWithOutputLines("Instantiating");
   }
 
   private void inspect(CodeInspector inspector) {
     ClassSubject myFragmentClassSubject = inspector.clazz(MyFragment.class);
     assertThat(myFragmentClassSubject, isPresent());
-    assertThat(
-        myFragmentClassSubject.init(),
-        onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
-
-    ClassSubject myZygotePreloadClassSubject = inspector.clazz(MyZygotePreload.class);
-    assertThat(myZygotePreloadClassSubject, isPresent());
-    assertThat(
-        myZygotePreloadClassSubject.init(),
-        onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
+    assertThat(myFragmentClassSubject.init(), isPresent());
   }
 
-  // Library classes.
-
-  public abstract static class ApplicationInfo {}
-
   public abstract static class Fragment {
 
-    public void onCreate() {
-      System.out.println("Fragment.onCreate()");
+    public Fragment newInstance() throws Exception {
+      System.out.println("Instantiating");
+      return getClass().getDeclaredConstructor().newInstance();
     }
   }
 
-  public interface ZygotePreload {
-
-    void doPreload(ApplicationInfo applicationInfo);
-  }
-
-  public static class Platform {
-
-    public static void accept(Fragment fragment) throws Exception {
-      Fragment newFragment = fragment.getClass().getDeclaredConstructor().newInstance();
-      newFragment.onCreate();
-    }
-
-    public static void accept(ZygotePreload runnable) throws Exception {
-      ZygotePreload newZygotePreload = runnable.getClass().getDeclaredConstructor().newInstance();
-      newZygotePreload.doPreload(null);
-    }
-  }
-
-  // Program classes.
-
   public static class Main {
 
     public static void main(String[] args) throws Exception {
-      Platform.accept(new MyFragment());
-      Platform.accept(new MyZygotePreload());
+      new MyFragment().newInstance();
     }
   }
 
@@ -198,21 +71,5 @@
   public static class MyFragment extends Fragment {
 
     public MyFragment() {}
-
-    @Override
-    public void onCreate() {
-      super.onCreate();
-      System.out.println("MyFragment.onCreate()");
-    }
-  }
-
-  public static class MyZygotePreload implements ZygotePreload {
-
-    public MyZygotePreload() {}
-
-    @Override
-    public void doPreload(ApplicationInfo applicationInfo) {
-      System.out.println("MyZygotePreload.doPreload()");
-    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
index c075406..3cbb42d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
@@ -22,6 +22,11 @@
     this.unboxedEnums = unboxedEnums;
   }
 
+  public EnumUnboxingInspector assertNoEnumsUnboxed() {
+    assertTrue(unboxedEnums.isEmpty());
+    return this;
+  }
+
   public EnumUnboxingInspector assertUnboxed(String typeName) {
     assertTrue(
         unboxedEnums.isUnboxedEnum(
diff --git a/third_party/jdwp-tests.tar.gz.sha1 b/third_party/jdwp-tests.tar.gz.sha1
index d3eeb60..0da9451 100644
--- a/third_party/jdwp-tests.tar.gz.sha1
+++ b/third_party/jdwp-tests.tar.gz.sha1
@@ -1 +1 @@
-db49b6093abaa165e422734a755cdcecf36c03db
\ No newline at end of file
+86eeaa4cfe57d20b03af02310acac89892db3bd3
\ No newline at end of file
diff --git a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
index c845f51..f548507 100644
--- a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
+++ b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
@@ -1 +1 @@
-e01c698069f2b52bd80864b70adfeaf0c27e5f4f
\ No newline at end of file
+99369b53116d6ab88384f57930b203cd7de55c0a
\ No newline at end of file