Merge commit 'b21566f8' into dev-release
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index dcd5329..244aa34 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -214,6 +214,9 @@
         "java.nio.file.ProviderNotFoundException",
         "java.nio.file.ReadOnlyFileSystemException"
       ],
+      "retarget_method": {
+        "java.nio.channels.FileChannel java.nio.channels.FileChannel#open(java.nio.file.Path, java.nio.file.OpenOption[])": "java.nio.channels.DesugarChannels"
+      },
       "retarget_method_with_emulated_dispatch": {
         "java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile"
       },
@@ -434,7 +437,6 @@
         "java.util.Date java.util.Date#from(java.time.Instant)": "java.util.DesugarDate",
         "java.util.GregorianCalendar java.util.GregorianCalendar#from(java.time.ZonedDateTime)": "java.util.DesugarGregorianCalendar",
         "java.util.TimeZone java.util.TimeZone#getTimeZone(java.lang.String)": "java.util.DesugarTimeZone",
-        "java.nio.channels.FileChannel java.nio.channels.FileChannel#open(java.nio.file.Path, java.nio.file.OpenOption[])": "java.nio.channels.DesugarChannels",
         "java.nio.channels.FileChannel java.nio.channels.FileChannel#open(java.nio.file.Path, java.util.Set, java.nio.file.attribute.FileAttribute[])": "java.nio.channels.DesugarChannels"
       }
     },
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index 911789d..4367a38 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -33,7 +33,8 @@
     D8,
     R8,
     L8,
-    Relocator;
+    Relocator,
+    TraceReferences;
 
     public static Tool[] valuesR8andD8() {
       return new Tool[] {Tool.D8, Tool.R8};
diff --git a/src/main/java/com/android/tools/r8/dump/DumpOptions.java b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
index 27dddcc..ef3a443 100644
--- a/src/main/java/com/android/tools/r8/dump/DumpOptions.java
+++ b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
@@ -46,6 +46,7 @@
   private static final String ENABLE_MISSING_LIBRARY_API_MODELING =
       "enable-missing-library-api-modeling";
   private static final String ANDROID_PLATFORM_BUILD = "android-platform-build";
+  private static final String TRACE_REFERENCES_CONSUMER = "trace_references_consumer";
 
   private final Tool tool;
   private final CompilationMode compilationMode;
@@ -70,6 +71,9 @@
 
   private final Map<String, String> systemProperties;
 
+  // TraceReferences only.
+  private final String traceReferencesConsumer;
+
   // Reporting only.
   private final boolean dumpInputToFile;
 
@@ -93,7 +97,8 @@
       boolean enableMissingLibraryApiModeling,
       boolean isAndroidPlatformBuild,
       Map<String, String> systemProperties,
-      boolean dumpInputToFile) {
+      boolean dumpInputToFile,
+      String traceReferencesConsumer) {
     this.tool = tool;
     this.compilationMode = compilationMode;
     this.minApi = minAPI;
@@ -114,6 +119,7 @@
     this.isAndroidPlatformBuild = isAndroidPlatformBuild;
     this.systemProperties = systemProperties;
     this.dumpInputToFile = dumpInputToFile;
+    this.traceReferencesConsumer = traceReferencesConsumer;
   }
 
   public String getBuildPropertiesFileContent() {
@@ -126,29 +132,33 @@
   public Map<String, String> getBuildProperties() {
     Map<String, String> buildProperties = new LinkedHashMap<>();
     addDumpEntry(buildProperties, TOOL_KEY, tool.name());
-    // We keep the following values for backward compatibility.
-    addDumpEntry(
-        buildProperties,
-        MODE_KEY,
-        compilationMode == CompilationMode.DEBUG ? DEBUG_MODE_VALUE : RELEASE_MODE_VALUE);
-    addDumpEntry(buildProperties, MIN_API_KEY, minApi);
-    addDumpEntry(
-        buildProperties, OPTIMIZE_MULTIDEX_FOR_LINEAR_ALLOC_KEY, optimizeMultidexForLinearAlloc);
     if (threadCount != ThreadUtils.NOT_SPECIFIED) {
       addDumpEntry(buildProperties, THREAD_COUNT_KEY, threadCount);
     }
-    addDumpEntry(buildProperties, DESUGAR_STATE_KEY, desugarState);
-    addDumpEntry(
-        buildProperties, ENABLE_MISSING_LIBRARY_API_MODELING, enableMissingLibraryApiModeling);
-    if (isAndroidPlatformBuild) {
-      addDumpEntry(buildProperties, ANDROID_PLATFORM_BUILD, isAndroidPlatformBuild);
+    if (tool != Tool.TraceReferences) {
+      // We keep the following values for backward compatibility.
+      addDumpEntry(
+          buildProperties,
+          MODE_KEY,
+          compilationMode == CompilationMode.DEBUG ? DEBUG_MODE_VALUE : RELEASE_MODE_VALUE);
+      addDumpEntry(buildProperties, MIN_API_KEY, minApi);
+      addDumpEntry(
+          buildProperties, OPTIMIZE_MULTIDEX_FOR_LINEAR_ALLOC_KEY, optimizeMultidexForLinearAlloc);
+      addDumpEntry(buildProperties, DESUGAR_STATE_KEY, desugarState);
+      addDumpEntry(
+          buildProperties, ENABLE_MISSING_LIBRARY_API_MODELING, enableMissingLibraryApiModeling);
+      if (isAndroidPlatformBuild) {
+        addDumpEntry(buildProperties, ANDROID_PLATFORM_BUILD, isAndroidPlatformBuild);
+      }
+      addOptionalDumpEntry(buildProperties, INTERMEDIATE_KEY, intermediate);
+      addOptionalDumpEntry(buildProperties, INCLUDE_DATA_RESOURCES_KEY, includeDataResources);
+      addOptionalDumpEntry(buildProperties, TREE_SHAKING_KEY, treeShaking);
+      addOptionalDumpEntry(
+          buildProperties, FORCE_PROGUARD_COMPATIBILITY_KEY, forceProguardCompatibility);
+    } else {
+      addDumpEntry(buildProperties, TRACE_REFERENCES_CONSUMER, traceReferencesConsumer);
     }
-    addOptionalDumpEntry(buildProperties, INTERMEDIATE_KEY, intermediate);
-    addOptionalDumpEntry(buildProperties, INCLUDE_DATA_RESOURCES_KEY, includeDataResources);
-    addOptionalDumpEntry(buildProperties, TREE_SHAKING_KEY, treeShaking);
     addOptionalDumpEntry(buildProperties, MINIFICATION_KEY, minification);
-    addOptionalDumpEntry(
-        buildProperties, FORCE_PROGUARD_COMPATIBILITY_KEY, forceProguardCompatibility);
     ArrayList<String> sortedKeys = new ArrayList<>(systemProperties.keySet());
     sortedKeys.sort(String::compareTo);
     sortedKeys.forEach(
@@ -212,6 +222,9 @@
       case FORCE_PROGUARD_COMPATIBILITY_KEY:
         builder.setForceProguardCompatibility(Boolean.parseBoolean(value));
         return;
+      case TRACE_REFERENCES_CONSUMER:
+        builder.setTraceReferencesConsumer(value);
+        return;
       default:
         if (key.startsWith(SYSTEM_PROPERTY_PREFIX)) {
           builder.setSystemProperty(key.substring(SYSTEM_PROPERTY_PREFIX.length()), value);
@@ -311,6 +324,8 @@
     private boolean enableMissingLibraryApiModeling = false;
     private boolean isAndroidPlatformBuild = false;
 
+    private String traceReferencesConsumer = null;
+
     private Map<String, String> systemProperties = new HashMap<>();
 
     // Reporting only.
@@ -323,6 +338,11 @@
       return this;
     }
 
+    public Builder setTraceReferencesConsumer(String traceReferencesConsumer) {
+      this.traceReferencesConsumer = traceReferencesConsumer;
+      return this;
+    }
+
     public Builder setCompilationMode(CompilationMode compilationMode) {
       this.compilationMode = compilationMode;
       return this;
@@ -456,7 +476,8 @@
           enableMissingLibraryApiModeling,
           isAndroidPlatformBuild,
           systemProperties,
-          dumpInputToFile);
+          dumpInputToFile,
+          traceReferencesConsumer);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 4b70a1c..369e60c 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -275,8 +275,8 @@
   public boolean isSubtype(DexType subtype, DexType supertype) {
     assert subtype != null;
     assert supertype != null;
-    assert subtype.isClassType() : "subtype not a class: " + subtype;
-    assert supertype.isClassType() : "supertype not a class: " + supertype;
+    assert subtype.isClassType();
+    assert supertype.isClassType();
     return subtype == supertype || isStrictSubtypeOf(subtype, supertype);
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
index 0d3c55a..a3e5972 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
@@ -91,6 +91,10 @@
     // application writer. We therefore simulate that we are in D8, to allow building IR for each of
     // the class initializers without applying the unapplied code rewritings, to avoid that we apply
     // the lens more than once to the same piece of code.
+
+    // Since we are now running in D8 mode clear type elements cache.
+    appView.dexItemFactory().clearTypeElementsCache();
+
     AppView<AppInfo> appViewForConversion =
         AppView.createForD8(
             AppInfo.createInitialAppInfo(
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 2a543a8..30c3b67 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
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
-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.ir.analysis.type.DynamicTypeWithUpperBound;
@@ -30,7 +29,6 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeNewArray;
 import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.Value;
@@ -39,7 +37,6 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Timing;
 import java.util.IdentityHashMap;
-import java.util.List;
 import java.util.Map;
 
 public class StaticFieldValueAnalysis extends FieldValueAnalysis {
@@ -213,7 +210,7 @@
     if (value.isPhi()) {
       return null;
     }
-    if (value.definition.isNewArrayEmptyOrInvokeNewArray()) {
+    if (value.definition.isNewArrayEmpty()) {
       return computeSingleEnumFieldValueForValuesArray(value);
     }
     if (value.definition.isNewInstance()) {
@@ -223,7 +220,7 @@
   }
 
   private SingleFieldValue computeSingleEnumFieldValueForValuesArray(Value value) {
-    if (!value.definition.isNewArrayEmptyOrInvokeNewArray()) {
+    if (!value.definition.isNewArrayEmpty()) {
       return null;
     }
     AbstractValue valuesValue = computedValues.get(value);
@@ -244,38 +241,26 @@
   }
 
   private SingleFieldValue internalComputeSingleEnumFieldValueForValuesArray(Value value) {
-    NewArrayEmpty newArrayEmpty = value.definition.asNewArrayEmpty();
-    InvokeNewArray invokeNewArray = value.definition.asInvokeNewArray();
-    assert newArrayEmpty != null || invokeNewArray != null;
+    assert value.isDefinedByInstructionSatisfying(Instruction::isNewArrayEmpty);
 
-    DexType arrayType = newArrayEmpty != null ? newArrayEmpty.type : invokeNewArray.getArrayType();
-    if (arrayType.toBaseType(appView.dexItemFactory()) != context.getHolder().type) {
+    NewArrayEmpty newArrayEmpty = value.definition.asNewArrayEmpty();
+    if (newArrayEmpty.type.toBaseType(appView.dexItemFactory()) != context.getHolder().type) {
       return null;
     }
     if (value.hasDebugUsers() || value.hasPhiUsers()) {
       return null;
     }
+    if (!newArrayEmpty.size().isConstNumber()) {
+      return null;
+    }
 
-    int valuesSize = newArrayEmpty != null ? newArrayEmpty.sizeIfConst() : invokeNewArray.size();
-    if (valuesSize < 1) {
-      // Array is empty or non-const size.
+    int valuesSize = newArrayEmpty.size().getConstInstruction().asConstNumber().getIntValue();
+    if (valuesSize == 0) {
+      // No need to compute the state of an empty array.
       return null;
     }
 
     ObjectState[] valuesState = new ObjectState[valuesSize];
-
-    if (invokeNewArray != null) {
-      // Populate array values from filled-new-array values.
-      List<Value> inValues = invokeNewArray.inValues();
-      for (int i = 0; i < valuesSize; ++i) {
-        if (!updateEnumValueState(valuesState, i, inValues.get(i))) {
-          return null;
-        }
-      }
-    }
-
-    // Populate / update array values from aput-object instructions, and find the static-put
-    // instruction.
     DexEncodedField valuesField = null;
     for (Instruction user : value.aliasedUsers()) {
       switch (user.opcode()) {
@@ -291,9 +276,18 @@
           if (index < 0 || index >= valuesSize) {
             return null;
           }
-          if (!updateEnumValueState(valuesState, index, arrayPut.value())) {
+          ObjectState objectState = computeEnumInstanceObjectState(arrayPut.value());
+          if (objectState == null || objectState.isEmpty()) {
+            // We need the state of all fields for the analysis to be valuable.
             return null;
           }
+          if (!valuesArrayIndexMatchesOrdinal(index, objectState)) {
+            return null;
+          }
+          if (valuesState[index] != null) {
+            return null;
+          }
+          valuesState[index] = objectState;
           break;
 
         case ASSUME:
@@ -334,34 +328,24 @@
         .createSingleFieldValue(valuesField.getReference(), new EnumValuesObjectState(valuesState));
   }
 
-  private boolean updateEnumValueState(ObjectState[] valuesState, int index, Value value) {
+  private ObjectState computeEnumInstanceObjectState(Value value) {
     Value root = value.getAliasedValue();
     if (root.isPhi()) {
-      return false;
+      return ObjectState.empty();
     }
     Instruction definition = root.getDefinition();
+    if (definition.isNewInstance()) {
+      return computeObjectState(definition.outValue());
+    }
     if (definition.isStaticGet()) {
       // Enums with many instance rely on staticGets to set the $VALUES data instead of directly
       // keeping the values in registers, due to the max capacity of the redundant field load
       // elimination. The capacity has already been increased, so that this case is extremely
       // uncommon (very large enums).
       // TODO(b/169050248): We could consider analysing these to answer the object state here.
-      return false;
+      return ObjectState.empty();
     }
-    ObjectState objectState =
-        definition.isNewInstance() ? computeObjectState(definition.outValue()) : null;
-    if (objectState == null || objectState.isEmpty()) {
-      // We need the state of all fields for the analysis to be valuable.
-      return false;
-    }
-    if (!valuesArrayIndexMatchesOrdinal(index, objectState)) {
-      return false;
-    }
-    if (valuesState[index] != null) {
-      return false;
-    }
-    valuesState[index] = objectState;
-    return true;
+    return ObjectState.empty();
   }
 
   private boolean valuesArrayIndexMatchesOrdinal(int ordinal, ObjectState objectState) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index b4b2359..74a3fb5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -33,7 +33,6 @@
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
-import com.android.tools.r8.ir.code.InvokeNewArray;
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.NewInstance;
@@ -47,7 +46,6 @@
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.Sets;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -318,37 +316,17 @@
     Value sizeValue =
         instructionIterator.insertConstIntInstruction(code, appView.options(), objects.size());
     Value newObjectsValue = code.createValue(objectArrayType);
+    instructionIterator.add(
+        new NewArrayEmpty(newObjectsValue, sizeValue, appView.dexItemFactory().objectArrayType));
 
     // Populate the `objects` array.
-    var rewriteOptions = appView.options().rewriteArrayOptions();
-    if (rewriteOptions.canUseFilledNewArrayOfObjects()
-        && objects.size() < rewriteOptions.maxRangeInputs) {
-      List<Value> arrayValues = new ArrayList<>(objects.size());
-      for (int i = 0; i < objects.size(); i++) {
-        Instruction materializingInstruction = objects.get(i).buildIR(appView, code);
-        instructionIterator.add(materializingInstruction);
-        arrayValues.add(materializingInstruction.outValue());
-      }
+    for (int i = 0; i < objects.size(); i++) {
+      Value indexValue = instructionIterator.insertConstIntInstruction(code, appView.options(), i);
+      Instruction materializingInstruction = objects.get(i).buildIR(appView, code);
+      instructionIterator.add(materializingInstruction);
       instructionIterator.add(
-          new InvokeNewArray(
-              appView.dexItemFactory().objectArrayType, newObjectsValue, arrayValues));
-    } else {
-      instructionIterator.add(
-          new NewArrayEmpty(newObjectsValue, sizeValue, appView.dexItemFactory().objectArrayType));
-
-      // Populate the `objects` array.
-      for (int i = 0; i < objects.size(); i++) {
-        Value indexValue =
-            instructionIterator.insertConstIntInstruction(code, appView.options(), i);
-        Instruction materializingInstruction = objects.get(i).buildIR(appView, code);
-        instructionIterator.add(materializingInstruction);
-        instructionIterator.add(
-            new ArrayPut(
-                MemberType.OBJECT,
-                newObjectsValue,
-                indexValue,
-                materializingInstruction.outValue()));
-      }
+          new ArrayPut(
+              MemberType.OBJECT, newObjectsValue, indexValue, materializingInstruction.outValue()));
     }
 
     // Pass the newly created `objects` array to RawMessageInfo.<init>(...) or
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
index c25c456..fbe3ff5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
@@ -31,7 +31,6 @@
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionIterator;
 import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeNewArray;
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.StaticGet;
@@ -303,30 +302,18 @@
    */
   private static ThrowingIterator<Value, InvalidRawMessageInfoException> createObjectIterator(
       Value objectsValue) throws InvalidRawMessageInfoException {
-    if (objectsValue.isPhi()) {
+    if (objectsValue.isPhi() || !objectsValue.definition.isNewArrayEmpty()) {
       throw new InvalidRawMessageInfoException();
     }
 
     NewArrayEmpty newArrayEmpty = objectsValue.definition.asNewArrayEmpty();
-    InvokeNewArray invokeNewArray = objectsValue.definition.asInvokeNewArray();
+    int expectedArraySize = objectsValue.uniqueUsers().size() - 1;
 
-    if (newArrayEmpty == null && invokeNewArray == null) {
-      throw new InvalidRawMessageInfoException();
-    }
-    // Verify that the array is used in only one spot.
-    if (invokeNewArray != null) {
-      if (objectsValue.uniqueUsers().size() != 1) {
-        throw new InvalidRawMessageInfoException();
-      }
-      return ThrowingIterator.fromIterator(invokeNewArray.inValues().iterator());
-    }
-
+    // Verify that the size is correct.
     Value sizeValue = newArrayEmpty.size().getAliasedValue();
-    if (sizeValue.isPhi() || !sizeValue.definition.isConstNumber()) {
-      throw new InvalidRawMessageInfoException();
-    }
-    int arraySize = sizeValue.definition.asConstNumber().getIntValue();
-    if (arraySize != objectsValue.uniqueUsers().size() - 1) {
+    if (sizeValue.isPhi()
+        || !sizeValue.definition.isConstNumber()
+        || sizeValue.definition.asConstNumber().getIntValue() != expectedArraySize) {
       throw new InvalidRawMessageInfoException();
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeUtils.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeUtils.java
index 99f92eb..e06cdbee 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeUtils.java
@@ -73,25 +73,20 @@
    * <p>Use with caution!
    */
   public static void removeArrayAndTransitiveInputsIfNotUsed(IRCode code, Instruction definition) {
+    Deque<InstructionOrPhi> worklist = new ArrayDeque<>();
     if (definition.isConstNumber()) {
       // No need to explicitly remove `null`, it will be removed by ordinary dead code elimination
       // anyway.
       assert definition.asConstNumber().isZero();
       return;
     }
-    Value arrayValue = definition.outValue();
-    if (arrayValue.hasPhiUsers() || arrayValue.hasDebugUsers()) {
-      return;
-    }
-    if (!definition.isNewArrayEmptyOrInvokeNewArray()) {
-      assert false;
-      return;
-    }
-    Deque<InstructionOrPhi> worklist = new ArrayDeque<>();
-    InvokeNewArray invokeNewArray = definition.asInvokeNewArray();
-    if (invokeNewArray != null) {
-      worklist.add(definition);
-    } else if (definition.isNewArrayEmpty()) {
+
+    if (definition.isNewArrayEmpty()) {
+      Value arrayValue = definition.outValue();
+      if (arrayValue.hasPhiUsers() || arrayValue.hasDebugUsers()) {
+        return;
+      }
+
       for (Instruction user : arrayValue.uniqueUsers()) {
         // If we encounter an Assume instruction here, we also need to consider indirect users.
         assert !user.isAssume();
@@ -100,10 +95,11 @@
         }
         worklist.add(user);
       }
-    } else {
-      assert false;
+      internalRemoveInstructionAndTransitiveInputsIfNotUsed(code, worklist);
+      return;
     }
-    internalRemoveInstructionAndTransitiveInputsIfNotUsed(code, worklist);
+
+    assert false;
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 8892da0..c99ee11 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -1038,10 +1038,6 @@
     return false;
   }
 
-  public boolean isNewArrayEmptyOrInvokeNewArray() {
-    return isNewArrayEmpty() || isInvokeNewArray();
-  }
-
   public NewArrayEmpty asNewArrayEmpty() {
     return null;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index f0715b5..a2e815f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -221,9 +221,4 @@
   void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
     registry.registerTypeReference(type);
   }
-
-  // Returns the number of elements in the array.
-  public int size() {
-    return inValues.size();
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index a829247..c6d527f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -49,6 +49,10 @@
     return super.toString() + " " + type.toString();
   }
 
+  public Value dest() {
+    return outValue;
+  }
+
   public Value size() {
     return inValues.get(0);
   }
@@ -56,7 +60,7 @@
   @Override
   public void buildDex(DexBuilder builder) {
     int size = builder.allocatedRegister(size(), getNumber());
-    int dest = builder.allocatedRegister(outValue, getNumber());
+    int dest = builder.allocatedRegister(dest(), getNumber());
     builder.add(this, new DexNewArray(dest, size, type));
   }
 
@@ -167,10 +171,4 @@
   void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
     registry.registerTypeReference(type);
   }
-
-  // Returns the size of the array if it is known, -1 otherwise.
-  public int sizeIfConst() {
-    Value size = size();
-    return size.isConstNumber() ? size.getConstInstruction().asConstNumber().getIntValue() : -1;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index f0ca38f..23f0de9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -16,6 +16,7 @@
 
 import com.android.tools.r8.algorithms.scc.SCC;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AccessControl;
@@ -54,6 +55,7 @@
 import com.android.tools.r8.ir.code.CatchHandlers.CatchHandler;
 import com.android.tools.r8.ir.code.CheckCast;
 import com.android.tools.r8.ir.code.ConstClass;
+import com.android.tools.r8.ir.code.ConstInstruction;
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.DebugLocalWrite;
@@ -82,7 +84,6 @@
 import com.android.tools.r8.ir.code.InvokeNewArray;
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.LinearFlowInstructionListIterator;
 import com.android.tools.r8.ir.code.Move;
 import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.NewArrayFilledData;
@@ -137,7 +138,6 @@
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collection;
 import java.util.Collections;
@@ -162,6 +162,7 @@
     FALSE
   }
 
+  private static final int MAX_FILL_ARRAY_SIZE = 8 * Constants.KILOBYTE;
   // This constant was determined by experimentation.
   private static final int STOP_SHARED_CONSTANT_THRESHOLD = 50;
 
@@ -2100,21 +2101,16 @@
     }
   }
 
-  private short[] computeArrayFilledData(Value[] values, int size, int elementSize) {
-    for (Value v : values) {
-      if (!v.isConstant()) {
-        return null;
-      }
+  private short[] computeArrayFilledData(ConstInstruction[] values, int size, int elementSize) {
+    if (values == null) {
+      return null;
     }
     if (elementSize == 1) {
       short[] result = new short[(size + 1) / 2];
       for (int i = 0; i < size; i += 2) {
-        short value =
-            (short) (values[i].getConstInstruction().asConstNumber().getIntValue() & 0xFF);
+        short value = (short) (values[i].asConstNumber().getIntValue() & 0xFF);
         if (i + 1 < size) {
-          value |=
-              (short)
-                  ((values[i + 1].getConstInstruction().asConstNumber().getIntValue() & 0xFF) << 8);
+          value |= (short) ((values[i + 1].asConstNumber().getIntValue() & 0xFF) << 8);
         }
         result[i / 2] = value;
       }
@@ -2124,7 +2120,7 @@
     int shortsPerConstant = elementSize / 2;
     short[] result = new short[size * shortsPerConstant];
     for (int i = 0; i < size; i++) {
-      long value = values[i].getConstInstruction().asConstNumber().getRawValue();
+      long value = values[i].asConstNumber().getRawValue();
       for (int part = 0; part < shortsPerConstant; part++) {
         result[i * shortsPerConstant + part] = (short) ((value >> (16 * part)) & 0xFFFFL);
       }
@@ -2132,45 +2128,40 @@
     return result;
   }
 
-  private Value[] computeArrayValues(LinearFlowInstructionListIterator it, int size) {
-    NewArrayEmpty newArrayEmpty = it.next().asNewArrayEmpty();
-    assert newArrayEmpty != null;
-    Value arrayValue = newArrayEmpty.outValue();
-
-    // aput-object allows any object for arrays of interfaces, but new-filled-array fails to verify
-    // if types require a cast.
-    // TODO(b/246971330): Check if adding a checked-cast would have the same observable result. E.g.
-    // if aput-object throws a ClassCastException if given an object that does not implement the
-    // desired interface, then we could add check-cast instructions for arguments we're not sure
-    // about.
-    DexType elementType = newArrayEmpty.type.toDimensionMinusOneType(dexItemFactory);
-    boolean needsTypeCheck =
-        !elementType.isPrimitiveType() && elementType != dexItemFactory.objectType;
-
-    Value[] values = new Value[size];
+  private ConstInstruction[] computeConstantArrayValues(
+      NewArrayEmpty newArray, BasicBlock block, int size) {
+    if (size > MAX_FILL_ARRAY_SIZE) {
+      return null;
+    }
+    ConstInstruction[] values = new ConstInstruction[size];
     int remaining = size;
-    Set<Instruction> users = newArrayEmpty.outValue().uniqueUsers();
+    Set<Instruction> users = newArray.outValue().uniqueUsers();
+    Set<BasicBlock> visitedBlocks = Sets.newIdentityHashSet();
+    // We allow the array instantiations to cross block boundaries as long as it hasn't encountered
+    // an instruction instance that can throw an exception.
+    InstructionIterator it = block.iterator();
+    it.nextUntil(i -> i == newArray);
+    do {
+      visitedBlocks.add(block);
       while (it.hasNext()) {
         Instruction instruction = it.next();
-      // If we encounter an instruction that can throw an exception we need to bail out of the
-      // optimization so that we do not transform half-initialized arrays into fully initialized
-      // arrays on exceptional edges. If the block has no handlers it is not observable so
-      // we perform the rewriting.
-      // TODO(b/246971330): Allow simplification when all users of the array are in the same
-      // try/catch.
-      if (instruction.getBlock().hasCatchHandlers() && instruction.instructionInstanceCanThrow()) {
+        // If we encounter an instruction that can throw an exception we need to bail out of the
+        // optimization so that we do not transform half-initialized arrays into fully initialized
+        // arrays on exceptional edges. If the block has no handlers it is not observable so
+        // we perform the rewriting.
+        if (block.hasCatchHandlers() && instruction.instructionInstanceCanThrow()) {
           return null;
         }
         if (!users.contains(instruction)) {
           continue;
         }
-      ArrayPut arrayPut = instruction.asArrayPut();
-      // If the initialization sequence is broken by another use we cannot use a fill-array-data
-      // instruction.
-      if (arrayPut == null || arrayPut.array() != arrayValue) {
+        // If the initialization sequence is broken by another use we cannot use a
+        // fill-array-data instruction.
+        if (!instruction.isArrayPut()) {
           return null;
         }
-      if (!arrayPut.index().isConstNumber()) {
+        ArrayPut arrayPut = instruction.asArrayPut();
+        if (!(arrayPut.value().isConstant() && arrayPut.index().isConstNumber())) {
           return null;
         }
         int index = arrayPut.index().getConstInstruction().asConstNumber().getIntValue();
@@ -2180,38 +2171,40 @@
         if (values[index] != null) {
           return null;
         }
-      Value value = arrayPut.value();
-      if (needsTypeCheck && !value.isAlwaysNull(appView)) {
-        DexType valueDexType = value.getType().asReferenceType().toDexType(dexItemFactory);
-        if (elementType.isArrayType()) {
-          if (elementType != valueDexType) {
-            return null;
-          }
-        } else if (valueDexType.isArrayType()) {
-          // isSubtype asserts for this case.
-          return null;
-        } else if (valueDexType.isNullValueType()) {
-          // Assume instructions can cause value.isAlwaysNull() == false while the DexType is
-          // null.
-          // TODO(b/246971330): Figure out how to write a test in SimplifyArrayConstructionTest
-          // that hits this case.
-        } else {
-          // TODO(b/246971330): When in d8 mode, we might still be able to see if this is true for
-          // library types (which this helper does not do).
-          if (appView.isSubtype(valueDexType, elementType).isPossiblyFalse()) {
-            return null;
-          }
-        }
-      }
+        ConstInstruction value = arrayPut.value().getConstInstruction();
         values[index] = value;
         --remaining;
         if (remaining == 0) {
           return values;
         }
       }
+      BasicBlock nextBlock = block.exit().isGoto() ? block.exit().asGoto().getTarget() : null;
+      block = nextBlock != null && !visitedBlocks.contains(nextBlock) ? nextBlock : null;
+      it = block != null ? block.iterator() : null;
+    } while (it != null);
     return null;
   }
 
+  private boolean allowNewFilledArrayConstruction(Instruction instruction) {
+    if (!(instruction instanceof NewArrayEmpty)) {
+      return false;
+    }
+    NewArrayEmpty newArray = instruction.asNewArrayEmpty();
+    if (!newArray.size().isConstant()) {
+      return false;
+    }
+    assert newArray.size().isConstNumber();
+    int size = newArray.size().getConstInstruction().asConstNumber().getIntValue();
+    if (size < 1) {
+      return false;
+    }
+    if (newArray.type.isPrimitiveArrayType()) {
+      return true;
+    }
+    return newArray.type == dexItemFactory.stringArrayType
+        && options.canUseFilledNewArrayOfObjects();
+  }
+
   /**
    * Replace new-array followed by stores of constants to all entries with new-array
    * and fill-array-data / filled-new-array.
@@ -2220,11 +2213,6 @@
     if (options.isGeneratingClassFiles()) {
       return;
     }
-    InternalOptions.RewriteArrayOptions rewriteOptions = options.rewriteArrayOptions();
-    boolean canUseForStrings = rewriteOptions.canUseFilledNewArrayOfStrings();
-    boolean canUseForObjects = rewriteOptions.canUseFilledNewArrayOfObjects();
-    boolean canUseForArrays = rewriteOptions.canUseFilledNewArrayOfArrays();
-
     for (BasicBlock block : code.blocks) {
       // Map from the array value to the number of array put instruction to remove for that value.
       Map<Value, Instruction> instructionToInsertForArray = new HashMap<>();
@@ -2233,56 +2221,31 @@
       InstructionListIterator it = block.listIterator(code);
       while (it.hasNext()) {
         Instruction instruction = it.next();
-        NewArrayEmpty newArrayEmpty = instruction.asNewArrayEmpty();
-        if (newArrayEmpty == null || !newArrayEmpty.size().isConstant()) {
+        if (instruction.getLocalInfo() != null || !allowNewFilledArrayConstruction(instruction)) {
           continue;
         }
-        if (instruction.getLocalInfo() != null) {
-          continue;
-        }
-        int size = newArrayEmpty.size().getConstInstruction().asConstNumber().getIntValue();
-        if (size < 1 || size > rewriteOptions.maxFillArrayDataInputs) {
-          continue;
-        }
-        DexType arrayType = newArrayEmpty.type;
-        if (!arrayType.isPrimitiveArrayType()) {
-          if (arrayType == dexItemFactory.stringArrayType) {
-            if (!canUseForStrings) {
-              continue;
-            }
-          } else if (!canUseForObjects) {
-            continue;
-          } else if (!canUseForArrays && arrayType.getNumberOfLeadingSquareBrackets() > 1) {
-            continue;
-          }
-        }
-
-        Value[] values =
-            computeArrayValues(
-                new LinearFlowInstructionListIterator(code, block, it.previousIndex()), size);
+        NewArrayEmpty newArray = instruction.asNewArrayEmpty();
+        int size = newArray.size().getConstInstruction().asConstNumber().getIntValue();
+        ConstInstruction[] values = computeConstantArrayValues(newArray, block, size);
         if (values == null) {
           continue;
         }
-        // filled-new-array is implemented only for int[] and Object[].
-        // For int[], using filled-new-array is usually smaller than filled-array-data.
-        // filled-new-array supports up to 5 registers before it's filled-new-array/range.
-        if (!arrayType.isPrimitiveArrayType()
-            || (arrayType == dexItemFactory.intArrayType && size <= 5)) {
+        if (newArray.type == dexItemFactory.stringArrayType) {
           // Don't replace with filled-new-array if it requires more than 200 consecutive registers.
-          if (size > rewriteOptions.maxRangeInputs) {
+          if (size > 200) {
             continue;
           }
-          // block.hasCatchHandlers() is fine here since new-array-filled replaces new-array-empty
-          // and computeArrayValues already checks that no throwing instructions exist between the
-          // original new-array-empty and the final aput-object (where the new-array-filled will be
-          // positioned).
-          Value invokeValue =
-              code.createValue(newArrayEmpty.getOutType(), newArrayEmpty.getLocalInfo());
-          InvokeNewArray invoke = new InvokeNewArray(arrayType, invokeValue, Arrays.asList(values));
-          for (Value value : newArrayEmpty.inValues()) {
-            value.removeUser(newArrayEmpty);
+          List<Value> stringValues = new ArrayList<>(size);
+          for (ConstInstruction value : values) {
+            stringValues.add(value.outValue());
           }
-          newArrayEmpty.outValue().replaceUsers(invokeValue);
+          Value invokeValue = code.createValue(newArray.getOutType(), newArray.getLocalInfo());
+          InvokeNewArray invoke =
+              new InvokeNewArray(dexItemFactory.stringArrayType, invokeValue, stringValues);
+          for (Value value : newArray.inValues()) {
+            value.removeUser(newArray);
+          }
+          newArray.outValue().replaceUsers(invokeValue);
           it.removeOrReplaceByDebugLocalRead();
           instructionToInsertForArray.put(invokeValue, invoke);
           storesToRemoveForArray.put(invokeValue, size);
@@ -2292,33 +2255,26 @@
           if (size == 1) {
             continue;
           }
-          // TODO(b/246971330): Allow simplification when all users of the array are in the same
-          // try/catch.
-          if (block.hasCatchHandlers()) {
-            // NewArrayFilledData can throw, so creating one as done below would add a second
-            // throwing instruction to the same block (the first one being NewArrayEmpty).
-            continue;
-          }
-          int elementSize = arrayType.elementSizeForPrimitiveArrayType();
+          int elementSize = newArray.type.elementSizeForPrimitiveArrayType();
           short[] contents = computeArrayFilledData(values, size, elementSize);
           if (contents == null) {
             continue;
           }
-          int arraySize = newArrayEmpty.size().getConstInstruction().asConstNumber().getIntValue();
-          // fill-array-data requires the new-array-empty instruction to remain, as it does not
-          // itself create an array.
+          if (block.hasCatchHandlers()) {
+            continue;
+          }
+          int arraySize = newArray.size().getConstInstruction().asConstNumber().getIntValue();
           NewArrayFilledData fillArray =
-              new NewArrayFilledData(newArrayEmpty.outValue(), elementSize, arraySize, contents);
-          fillArray.setPosition(newArrayEmpty.getPosition());
+              new NewArrayFilledData(newArray.outValue(), elementSize, arraySize, contents);
+          fillArray.setPosition(newArray.getPosition());
           it.add(fillArray);
-          storesToRemoveForArray.put(newArrayEmpty.outValue(), size);
+          storesToRemoveForArray.put(newArray.outValue(), size);
         }
       }
       // Second pass: remove all the array put instructions for the array for which we have
       // inserted a fill array data instruction instead.
       if (!storesToRemoveForArray.isEmpty()) {
         Set<BasicBlock> visitedBlocks = Sets.newIdentityHashSet();
-        int numInstructionsInserted = 0;
         do {
           visitedBlocks.add(block);
           it = block.listIterator(code);
@@ -2340,7 +2296,6 @@
                     // last removed put at which point we are now adding the construction.
                     construction.setPosition(instruction.getPosition());
                     it.add(construction);
-                    numInstructionsInserted += 1;
                   }
                 }
               }
@@ -2349,7 +2304,6 @@
           BasicBlock nextBlock = block.exit().isGoto() ? block.exit().asGoto().getTarget() : null;
           block = nextBlock != null && !visitedBlocks.contains(nextBlock) ? nextBlock : null;
         } while (block != null);
-        assert numInstructionsInserted == instructionToInsertForArray.size();
       }
     }
     assert code.isConsistentSSA(appView);
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 6fb553c..a649af6 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
@@ -18,7 +18,6 @@
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_CUSTOM;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_NEW_ARRAY;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
@@ -69,7 +68,6 @@
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InvokeCustom;
 import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeNewArray;
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeVirtual;
 import com.android.tools.r8.ir.code.MemberType;
@@ -1062,10 +1060,6 @@
             instruction.asInstancePut(), code, context, enumClass, enumValue);
       case INVOKE_DIRECT:
       case INVOKE_INTERFACE:
-        return analyzeInvokeUser(instruction.asInvokeMethod(), code, context, enumClass, enumValue);
-      case INVOKE_NEW_ARRAY:
-        return analyzeInvokeNewArrayUser(
-            instruction.asInvokeNewArray(), code, context, enumClass, enumValue);
       case INVOKE_STATIC:
       case INVOKE_SUPER:
       case INVOKE_VIRTUAL:
@@ -1136,40 +1130,6 @@
     return Reason.INVALID_ARRAY_PUT;
   }
 
-  private Reason analyzeInvokeNewArrayUser(
-      InvokeNewArray invokeNewArray,
-      IRCode code,
-      ProgramMethod context,
-      DexProgramClass enumClass,
-      Value enumValue) {
-    // MyEnum[] array = new MyEnum[] { MyEnum.A }; is valid.
-    // We need to prove that the value to put in and the array have correct types.
-    TypeElement arrayType = invokeNewArray.getOutType();
-    assert arrayType.isArrayType();
-
-    ClassTypeElement arrayBaseType = arrayType.asArrayType().getBaseType().asClassType();
-    if (arrayBaseType == null) {
-      assert false;
-      return Reason.INVALID_INVOKE_NEW_ARRAY;
-    }
-    if (arrayBaseType.getClassType() != enumClass.type) {
-      return Reason.INVALID_INVOKE_NEW_ARRAY;
-    }
-
-    for (Value value : invokeNewArray.inValues()) {
-      TypeElement valueBaseType = value.getType();
-      if (valueBaseType.isArrayType()) {
-        assert valueBaseType.asArrayType().getBaseType().isClassType();
-        assert valueBaseType.asArrayType().getNesting() == arrayType.asArrayType().getNesting() - 1;
-        valueBaseType = valueBaseType.asArrayType().getBaseType();
-      }
-      if (!arrayBaseType.equalUpToNullability(valueBaseType)) {
-        return Reason.INVALID_INVOKE_NEW_ARRAY;
-      }
-    }
-    return Reason.ELIGIBLE;
-  }
-
   private Reason analyzeCheckCastUser(
       CheckCast checkCast,
       IRCode code,
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 7cb0290..3e17a0a 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
@@ -32,8 +32,6 @@
       new StringReason("IMPLICIT_UP_CAST_IN_RETURN");
   public static final Reason INVALID_FIELD_PUT = new StringReason("INVALID_FIELD_PUT");
   public static final Reason INVALID_ARRAY_PUT = new StringReason("INVALID_ARRAY_PUT");
-  public static final Reason INVALID_INVOKE_NEW_ARRAY =
-      new StringReason("INVALID_INVOKE_NEW_ARRAY");
   public static final Reason TYPE_MISMATCH_FIELD_PUT = new StringReason("TYPE_MISMATCH_FIELD_PUT");
   public static final Reason INVALID_IF_TYPES = new StringReason("INVALID_IF_TYPES");
   public static final Reason ASSIGNMENT_OUTSIDE_INIT = new StringReason("ASSIGNMENT_OUTSIDE_INIT");
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
index 6e14907..3e4078b 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
@@ -27,7 +27,6 @@
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionIterator;
 import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeNewArray;
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeVirtual;
 import com.android.tools.r8.ir.code.NewArrayEmpty;
@@ -425,16 +424,12 @@
 
   // Perform a conservative evaluation of an array content of dex type values from its construction
   // until its use at a given instruction.
-  private static DexTypeList evaluateTypeArrayContentFromConstructionToUse(
-      NewArrayEmpty newArray, List<CheckCast> aliases, Instruction user, DexItemFactory factory) {
-    int size = newArray.sizeIfConst();
-    if (size < 0) {
-      return null;
-    } else if (size == 0) {
-      // TODO: We should likely still scan to ensure no ArrayPut instructions exist.
-      return DexTypeList.empty();
-    }
-
+  private static DexType[] evaluateTypeArrayContentFromConstructionToUse(
+      NewArrayEmpty newArray,
+      List<CheckCast> aliases,
+      int size,
+      Instruction user,
+      DexItemFactory factory) {
     DexType[] values = new DexType[size];
     int remaining = size;
     Set<Instruction> users = Sets.newIdentityHashSet();
@@ -458,7 +453,7 @@
         if (instruction == user) {
           // Return the array content if all elements are known when hitting the user for which
           // the content was requested.
-          return remaining == 0 ? new DexTypeList(values) : null;
+          return remaining == 0 ? values : null;
         }
         // Any other kinds of use besides array-put mean that the array escapes and its content
         // could be altered.
@@ -503,21 +498,6 @@
     return null;
   }
 
-  private static DexTypeList evaluateTypeArrayContent(
-      InvokeNewArray newArray, DexItemFactory factory) {
-    List<Value> arrayValues = newArray.inValues();
-    int size = arrayValues.size();
-    DexType[] values = new DexType[size];
-    for (int i = 0; i < size; ++i) {
-      DexType type = getTypeFromConstClassOrBoxedPrimitive(arrayValues.get(i), factory);
-      if (type == null) {
-        return null;
-      }
-      values[i] = type;
-    }
-    return new DexTypeList(values);
-  }
-
   /**
    * Visits all {@link ArrayPut}'s with the given {@param classListValue} as array and {@link Class}
    * as value. Then collects all corresponding {@link DexType}s so as to determine reflective cases.
@@ -571,13 +551,30 @@
     }
 
     // Make sure this Value refers to a new array.
-    if (classListValue.definition.isNewArrayEmpty()) {
-      return evaluateTypeArrayContentFromConstructionToUse(
-          classListValue.definition.asNewArrayEmpty(), aliases, invoke, factory);
-    } else if (classListValue.definition.isInvokeNewArray()) {
-      return evaluateTypeArrayContent(classListValue.definition.asInvokeNewArray(), factory);
-    } else {
+    if (!classListValue.definition.isNewArrayEmpty()
+        || !classListValue.definition.asNewArrayEmpty().size().isConstant()) {
       return null;
     }
+
+    int size =
+        classListValue
+            .definition
+            .asNewArrayEmpty()
+            .size()
+            .getConstInstruction()
+            .asConstNumber()
+            .getIntValue();
+    if (size == 0) {
+      return DexTypeList.empty();
+    }
+
+    DexType[] arrayContent =
+        evaluateTypeArrayContentFromConstructionToUse(
+            classListValue.definition.asNewArrayEmpty(), aliases, size, invoke, factory);
+
+    if (arrayContent == null) {
+      return null;
+    }
+    return new DexTypeList(arrayContent);
   }
 }
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 93d387e..213819d 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -104,9 +104,7 @@
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionIterator;
 import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeNewArray;
 import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
@@ -5081,43 +5079,25 @@
       return;
     }
     Value parametersValue = constructorDefinition.inValues().get(1);
-    if (parametersValue.isPhi()) {
+    if (parametersValue.isPhi() || !parametersValue.definition.isNewArrayEmpty()) {
       // Give up, we can't tell which constructor is being invoked.
       return;
     }
-    NewArrayEmpty newArrayEmpty = parametersValue.definition.asNewArrayEmpty();
-    InvokeNewArray invokeNewArray = parametersValue.definition.asInvokeNewArray();
-    int parametersSize =
-        newArrayEmpty != null
-            ? newArrayEmpty.sizeIfConst()
-            : invokeNewArray != null ? invokeNewArray.size() : -1;
-    if (parametersSize < 0) {
+
+    Value parametersSizeValue = parametersValue.definition.asNewArrayEmpty().size();
+    if (parametersSizeValue.isPhi() || !parametersSizeValue.definition.isConstNumber()) {
+      // Give up, we can't tell which constructor is being invoked.
       return;
     }
 
     ProgramMethod initializer = null;
 
+    int parametersSize = parametersSizeValue.definition.asConstNumber().getIntValue();
     if (parametersSize == 0) {
       initializer = clazz.getProgramDefaultInitializer();
     } else {
       DexType[] parameterTypes = new DexType[parametersSize];
       int missingIndices = parametersSize;
-
-      if (newArrayEmpty != null) {
-        missingIndices = parametersSize;
-      } else {
-        missingIndices = 0;
-        List<Value> values = invokeNewArray.inValues();
-        for (int i = 0; i < parametersSize; ++i) {
-          DexType type =
-              ConstantValueUtils.getDexTypeRepresentedByValueForTracing(values.get(i), appView);
-          if (type == null) {
-            return;
-          }
-          parameterTypes[i] = type;
-        }
-      }
-
       for (Instruction user : parametersValue.uniqueUsers()) {
         if (user.isArrayPut()) {
           ArrayPut arrayPutInstruction = user.asArrayPut();
@@ -5179,31 +5159,20 @@
     }
 
     Value interfacesValue = invoke.arguments().get(1);
-    if (interfacesValue.isPhi()) {
+    if (interfacesValue.isPhi() || !interfacesValue.definition.isNewArrayEmpty()) {
       // Give up, we can't tell which interfaces the proxy implements.
       return;
     }
 
-    InvokeNewArray invokeNewArray = interfacesValue.definition.asInvokeNewArray();
-    NewArrayEmpty newArrayEmpty = interfacesValue.definition.asNewArrayEmpty();
-    List<Value> values;
-    if (invokeNewArray != null) {
-      values = invokeNewArray.inValues();
-    } else if (newArrayEmpty != null) {
-      values = new ArrayList<>(interfacesValue.uniqueUsers().size());
-      for (Instruction user : interfacesValue.uniqueUsers()) {
-        ArrayPut arrayPut = user.asArrayPut();
-        if (arrayPut != null) {
-          values.add(arrayPut.value());
-        }
-      }
-    } else {
-      return;
-    }
-
     WorkList<DexProgramClass> worklist = WorkList.newIdentityWorkList();
-    for (Value value : values) {
-      DexType type = ConstantValueUtils.getDexTypeRepresentedByValueForTracing(value, appView);
+    for (Instruction user : interfacesValue.uniqueUsers()) {
+      if (!user.isArrayPut()) {
+        continue;
+      }
+
+      ArrayPut arrayPut = user.asArrayPut();
+      DexType type =
+          ConstantValueUtils.getDexTypeRepresentedByValueForTracing(arrayPut.value(), appView);
       if (type == null || !type.isClassType()) {
         continue;
       }
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
index 1344816..416a9a4 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
@@ -28,10 +28,7 @@
 public class TraceReferences {
 
   public static void run(TraceReferencesCommand command) throws CompilationFailedException {
-    InternalOptions options = new InternalOptions();
-    options.loadAllClassDefinitions = true;
-    ExceptionUtils.withCompilationHandler(
-        command.getReporter(), () -> runInternal(command, options));
+    runForTesting(command, command.getInternalOptions());
   }
 
   private static void forEachDescriptor(ProgramResourceProvider provider, Consumer<String> consumer)
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
index 9aff1d0..2635b95 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
@@ -18,12 +18,15 @@
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.dump.DumpOptions;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.utils.ArchiveResourceProvider;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableList;
@@ -396,4 +399,21 @@
   TraceReferencesConsumer getConsumer() {
     return consumer;
   }
+
+  InternalOptions getInternalOptions() {
+    InternalOptions options = new InternalOptions();
+    options.loadAllClassDefinitions = true;
+    TraceReferencesConsumer consumer = getConsumer();
+    DumpOptions.Builder builder =
+        DumpOptions.builder(Tool.TraceReferences)
+            .readCurrentSystemProperties()
+            // The behavior of TraceReferences greatly differs depending if we have a CheckConsumer
+            // or a KeepRules consumer. We log the consumer type and obfuscation if relevant.
+            .setTraceReferencesConsumer(consumer.getClass().getName());
+    if (consumer instanceof TraceReferencesKeepRules) {
+      builder.setMinification(((TraceReferencesKeepRules) consumer).allowObfuscation());
+    }
+    options.dumpOptions = builder.build();
+    return options;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesKeepRules.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesKeepRules.java
index f648da4..52ea13c 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesKeepRules.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesKeepRules.java
@@ -40,6 +40,10 @@
     this.allowObfuscation = allowObfuscation;
   }
 
+  public boolean allowObfuscation() {
+    return allowObfuscation;
+  }
+
   /**
    * Builder for constructing a {@link TraceReferencesKeepRules].
    *
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 09641c3..b726531 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -816,7 +816,6 @@
 
   public boolean debug = false;
 
-  private final RewriteArrayOptions rewriteArrayOptions = new RewriteArrayOptions();
   private final CallSiteOptimizationOptions callSiteOptimizationOptions =
       new CallSiteOptimizationOptions();
   private final CfCodeAnalysisOptions cfCodeAnalysisOptions = new CfCodeAnalysisOptions();
@@ -851,10 +850,6 @@
 
   public LineNumberOptimization lineNumberOptimization = LineNumberOptimization.ON;
 
-  public RewriteArrayOptions rewriteArrayOptions() {
-    return rewriteArrayOptions;
-  }
-
   public CallSiteOptimizationOptions callSiteOptimizationOptions() {
     return callSiteOptimizationOptions;
   }
@@ -1388,42 +1383,6 @@
         System.getProperty("com.android.tools.r8.lambdaClassFieldsNotFinal") == null;
   }
 
-  public class RewriteArrayOptions {
-    // Arbitrary limit of number of inputs to new-filled-array/range.
-    // The technical limit is 255 (Constants.U8BIT_MAX).
-    public int maxRangeInputs = 200;
-    // Arbitrary limit of number of inputs to fill-array-data.
-    public int maxFillArrayDataInputs = 8 * 1024;
-
-    // Dalvik x86-atom backend had a bug that made it crash on filled-new-array instructions for
-    // arrays of objects. This is unfortunate, since this never hits arm devices, but we have
-    // to disallow filled-new-array of objects for dalvik until kitkat. The buggy code was
-    // removed during the jelly-bean release cycle and is not there from kitkat.
-    //
-    // Buggy code that accidentally call code that only works on primitives arrays.
-    //
-    // https://android.googlesource.com/platform/dalvik/+/ics-mr0/vm/mterp/out/InterpAsm-x86-atom.S#25106
-    public boolean canUseFilledNewArrayOfStrings() {
-      assert isGeneratingDex();
-      return hasFeaturePresentFrom(AndroidApiLevel.K);
-    }
-
-    // When adding support for emitting new-filled-array for non-String types, ART 6.0.1 had issues.
-    // https://ci.chromium.org/ui/p/r8/builders/ci/linux-android-6.0.1/6507/overview
-    // It somehow had a new-array-filled return null.
-    public boolean canUseFilledNewArrayOfObjects() {
-      assert isGeneratingDex();
-      return hasFeaturePresentFrom(AndroidApiLevel.N);
-    }
-
-    // Dalvik doesn't handle new-filled-array with arrays as values. It fails with:
-    // W(629880) VFY: [Ljava/lang/Integer; is not instance of Ljava/lang/Integer;  (dalvikvm)
-    public boolean canUseFilledNewArrayOfArrays() {
-      assert isGeneratingDex();
-      return hasFeaturePresentFrom(AndroidApiLevel.L);
-    }
-  }
-
   public class CallSiteOptimizationOptions {
 
     private boolean enabled = true;
@@ -2431,6 +2390,19 @@
     return hasFeaturePresentFrom(AndroidApiLevel.J);
   }
 
+  // Dalvik x86-atom backend had a bug that made it crash on filled-new-array instructions for
+  // arrays of objects. This is unfortunate, since this never hits arm devices, but we have
+  // to disallow filled-new-array of objects for dalvik until kitkat. The buggy code was
+  // removed during the jelly-bean release cycle and is not there from kitkat.
+  //
+  // Buggy code that accidentally call code that only works on primitives arrays.
+  //
+  // https://android.googlesource.com/platform/dalvik/+/ics-mr0/vm/mterp/out/InterpAsm-x86-atom.S#25106
+  public boolean canUseFilledNewArrayOfObjects() {
+    assert isGeneratingDex();
+    return hasFeaturePresentFrom(AndroidApiLevel.K);
+  }
+
   // Art had a bug (b/68761724) for Android N and O in the arm32 interpreter
   // where an aget-wide instruction using the same register for the array
   // and the first register of the result could lead to the wrong exception
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingIterator.java b/src/main/java/com/android/tools/r8/utils/ThrowingIterator.java
index 7d41ae1..50d23c9 100644
--- a/src/main/java/com/android/tools/r8/utils/ThrowingIterator.java
+++ b/src/main/java/com/android/tools/r8/utils/ThrowingIterator.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.utils;
 
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
 
@@ -33,18 +32,4 @@
     }
     return result;
   }
-
-  public static <T, E extends Exception> ThrowingIterator<T, E> fromIterator(Iterator<T> it) {
-    return new ThrowingIterator<>() {
-      @Override
-      public boolean hasNext() {
-        return it.hasNext();
-      }
-
-      @Override
-      public T next() throws E {
-        return it.next();
-      }
-    };
-  }
 }
diff --git a/src/test/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
index e1ab7a6..8b56639 100644
--- a/src/test/java/com/android/tools/r8/JvmTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
@@ -107,7 +107,8 @@
           throw new RuntimeException(e);
         }
       } else {
-        throw new Unimplemented("No support for adding file: " + file);
+        assert Files.isDirectory(file);
+        classpath.add(file);
       }
     }
     return self();
diff --git a/src/test/java/com/android/tools/r8/compatproguard/GetMembersTest.java b/src/test/java/com/android/tools/r8/compatproguard/GetMembersTest.java
index b00d78b..812eb63 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/GetMembersTest.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/GetMembersTest.java
@@ -10,9 +10,7 @@
 import com.android.tools.r8.dex.code.DexConst4;
 import com.android.tools.r8.dex.code.DexConstClass;
 import com.android.tools.r8.dex.code.DexConstString;
-import com.android.tools.r8.dex.code.DexFilledNewArray;
 import com.android.tools.r8.dex.code.DexInvokeVirtual;
-import com.android.tools.r8.dex.code.DexMoveResultObject;
 import com.android.tools.r8.dex.code.DexNewArray;
 import com.android.tools.r8.dex.code.DexReturnVoid;
 import com.android.tools.r8.graph.DexCode;
@@ -95,29 +93,16 @@
     assertTrue(method.isPresent());
 
     DexCode code = method.getMethod().getCode().asDexCode();
-
-    // Accept either array construction style (differs based on minSdkVersion).
-    if (code.instructions[1] instanceof DexFilledNewArray) {
-      assertTrue(code.instructions[0] instanceof DexConstClass);
-      assertTrue(code.instructions[1] instanceof DexFilledNewArray);
-      assertTrue(code.instructions[2] instanceof DexMoveResultObject);
-      assertTrue(code.instructions[3] instanceof DexConstClass);
-      assertTrue(code.instructions[4] instanceof DexConstString);
-      assertNotEquals("foo", code.instructions[4].asConstString().getString().toString());
-      assertTrue(code.instructions[5] instanceof DexInvokeVirtual);
-      assertTrue(code.instructions[6] instanceof DexReturnVoid);
-    } else {
-      assertTrue(code.instructions[0] instanceof DexConst4);
-      assertTrue(code.instructions[1] instanceof DexNewArray);
-      assertTrue(code.instructions[2] instanceof DexConst4);
-      assertTrue(code.instructions[3] instanceof DexConstClass);
-      assertTrue(code.instructions[4] instanceof DexAputObject);
-      assertTrue(code.instructions[5] instanceof DexConstClass);
-      assertTrue(code.instructions[6] instanceof DexConstString);
-      assertNotEquals("foo", code.instructions[6].asConstString().getString().toString());
-      assertTrue(code.instructions[7] instanceof DexInvokeVirtual);
-      assertTrue(code.instructions[8] instanceof DexReturnVoid);
-    }
+    assertTrue(code.instructions[0] instanceof DexConst4);
+    assertTrue(code.instructions[1] instanceof DexNewArray);
+    assertTrue(code.instructions[2] instanceof DexConst4);
+    assertTrue(code.instructions[3] instanceof DexConstClass);
+    assertTrue(code.instructions[4] instanceof DexAputObject);
+    assertTrue(code.instructions[5] instanceof DexConstClass);
+    assertTrue(code.instructions[6] instanceof DexConstString);
+    assertNotEquals("foo", code.instructions[6].asConstString().getString().toString());
+    assertTrue(code.instructions[7] instanceof DexInvokeVirtual);
+    assertTrue(code.instructions[8] instanceof DexReturnVoid);
   }
 
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
index fb9ca6a..5ae7b6c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
@@ -18,6 +18,7 @@
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.channels.SeekableByteChannel;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.LinkOption;
@@ -35,6 +36,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Stream;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -43,51 +45,47 @@
 @RunWith(Parameterized.class)
 public class FilesTest extends DesugaredLibraryTestBase {
 
+  private static final String END_EXPECTED_RESULT =
+      StringUtils.lines("tmp", "/", "true", "This", "is", "fun!");
   private static final String EXPECTED_RESULT_DESUGARING_FILE_SYSTEM =
       StringUtils.lines(
-          "bytes written: 11",
-          "String written: Hello World",
-          "bytes read: 11",
-          "String read: Hello World",
-          "bytes read: 11",
-          "String read: Hello World",
-          "null",
-          "true",
-          "unsupported",
-          "j$.nio.file.attribute",
-          "tmp",
-          "/",
-          "true");
+              "bytes written: 11",
+              "String written: Hello World",
+              "bytes read: 11",
+              "String read: Hello World",
+              "bytes read: 11",
+              "String read: Hello World",
+              "null",
+              "true",
+              "unsupported",
+              "j$.nio.file.attribute")
+          + END_EXPECTED_RESULT;
   private static final String EXPECTED_RESULT_PLATFORM_FILE_SYSTEM_DESUGARING =
       StringUtils.lines(
-          "bytes written: 11",
-          "String written: Hello World",
-          "bytes read: 11",
-          "String read: Hello World",
-          "bytes read: 11",
-          "String read: Hello World",
-          "true",
-          "true",
-          "true",
-          "j$.nio.file.attribute",
-          "tmp",
-          "/",
-          "true");
+              "bytes written: 11",
+              "String written: Hello World",
+              "bytes read: 11",
+              "String read: Hello World",
+              "bytes read: 11",
+              "String read: Hello World",
+              "true",
+              "true",
+              "true",
+              "j$.nio.file.attribute")
+          + END_EXPECTED_RESULT;
   private static final String EXPECTED_RESULT_PLATFORM_FILE_SYSTEM =
       StringUtils.lines(
-          "bytes written: 11",
-          "String written: Hello World",
-          "bytes read: 11",
-          "String read: Hello World",
-          "bytes read: 11",
-          "String read: Hello World",
-          "true",
-          "true",
-          "true",
-          "java.nio.file.attribute",
-          "tmp",
-          "/",
-          "true");
+              "bytes written: 11",
+              "String written: Hello World",
+              "bytes read: 11",
+              "String read: Hello World",
+              "bytes read: 11",
+              "String read: Hello World",
+              "true",
+              "true",
+              "true",
+              "java.nio.file.attribute")
+          + END_EXPECTED_RESULT;
 
   private final TestParameters parameters;
   private final LibraryDesugaringSpecification libraryDesugaringSpecification;
@@ -145,6 +143,7 @@
       Files.setAttribute(path, "basic:lastModifiedTime", FileTime.from(Instant.EPOCH));
       fspMethodsWithGeneric(path);
       pathGeneric();
+      lines(path);
     }
 
     private static void pathGeneric() throws IOException {
@@ -192,6 +191,12 @@
       }
     }
 
+    private static void lines(Path path) throws IOException {
+      Files.write(path, "This\nis\nfun!".getBytes(StandardCharsets.UTF_8));
+      Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8);
+      lines.forEach(System.out::println);
+    }
+
     private static void readWriteThroughFilesAPI(Path path) throws IOException {
       try (SeekableByteChannel channel =
           Files.newByteChannel(path, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
index 7141ad08..1e43777 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
@@ -53,7 +53,8 @@
         .addProgramClasses(I.class, A.class)
         .addProgramClassFileData(getTransformedMain())
         .addKeepMainRule(Main.class)
-        .addKeepMethodRules(Reference.methodFromMethod(Main.class.getDeclaredMethod("get")))
+        // Keep get() to prevent that we optimize it into having static return type A.
+        .addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
         .addOptionsModification(
             options ->
                 options
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmptyTest.java
similarity index 95%
rename from src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
rename to src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmptyTest.java
index 7a7aec4..d8a78c9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmptyTest.java
@@ -22,7 +22,7 @@
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class MetadataRewriteDoNotEmitValuesIfEmpty extends KotlinMetadataTestBase {
+public class MetadataRewriteDoNotEmitValuesIfEmptyTest extends KotlinMetadataTestBase {
 
   private final Set<String> nullableFieldKeys = Sets.newHashSet("pn", "xs", "xi");
 
@@ -35,7 +35,7 @@
 
   private final TestParameters parameters;
 
-  public MetadataRewriteDoNotEmitValuesIfEmpty(
+  public MetadataRewriteDoNotEmitValuesIfEmptyTest(
       TestParameters parameters, KotlinTestParameters kotlinParameters) {
     super(kotlinParameters);
     this.parameters = parameters;
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java
new file mode 100644
index 0000000..939eb30
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** This is a regression test for b/259389417. */
+@RunWith(Parameterized.class)
+public class MetadataRewriteEnumTest extends KotlinMetadataTestBase {
+
+  private final String[] EXPECTED =
+      new String[] {"UP", "RIGHT", "DOWN", "LEFT", "UP", "RIGHT", "DOWN", "LEFT"};
+  private final String DIRECTION_TYPE_NAME = PKG + ".enum_lib.Direction";
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}, {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
+  }
+
+  public MetadataRewriteEnumTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
+    this.parameters = parameters;
+  }
+
+  private static final KotlinCompileMemoizer jarMap =
+      getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/enum_lib", "lib"));
+
+  @Test
+  public void smokeTest() throws Exception {
+    Path libJar = jarMap.getForConfiguration(kotlinc, targetVersion);
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/enum_app", "main"))
+            .compile();
+    testForRuntime(parameters)
+        .addProgramFiles(
+            libJar, output, kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar())
+        .run(parameters.getRuntime(), PKG + ".enum_app.MainKt")
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    R8TestCompileResult r8libResult =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(jarMap.getForConfiguration(kotlinc, targetVersion))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar())
+            .addKeepKotlinMetadata()
+            .addKeepEnumsRule()
+            .addKeepClassRules(DIRECTION_TYPE_NAME)
+            .addKeepClassAndMembersRulesWithAllowObfuscation(DIRECTION_TYPE_NAME)
+            .compile()
+            .inspect(
+                inspector -> {
+                  ClassSubject direction = inspector.clazz(DIRECTION_TYPE_NAME);
+                  assertThat(direction, isPresentAndNotRenamed());
+                  direction.allFields().forEach(field -> assertTrue(field.isRenamed()));
+                });
+    Path libJar = r8libResult.writeToZip();
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/enum_app", "main"))
+            .compile();
+    Path path =
+        testForR8(parameters.getBackend())
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
+            .addProgramFiles(output)
+            .addKeepAllClassesRule()
+            .addApplyMapping(r8libResult.getProguardMap())
+            .compile()
+            .writeToZip();
+    // TODO(b/259389417): We should rename enum values in metadata.
+    testForRuntime(parameters)
+        .addProgramFiles(libJar, kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), path)
+        .run(parameters.getRuntime(), PKG + ".enum_app.MainKt")
+        .assertFailureWithErrorThatThrows(NoSuchFieldError.class);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/enum_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/enum_app/main.kt
new file mode 100644
index 0000000..ff23216
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/enum_app/main.kt
@@ -0,0 +1,16 @@
+// Copyright (c) 2020, 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.kotlin.metadata.enum_app
+
+import com.android.tools.r8.kotlin.metadata.enum_lib.Direction
+
+fun main() {
+  println(Direction.UP)
+  println(Direction.RIGHT)
+  println(Direction.DOWN)
+  println(Direction.LEFT)
+
+  Direction::class.java.enumConstants.forEach { println(it) }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/enum_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/enum_lib/lib.kt
new file mode 100644
index 0000000..0423b6d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/enum_lib/lib.kt
@@ -0,0 +1,9 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.enum_lib
+
+enum class Direction {
+  UP, RIGHT, DOWN, LEFT
+}
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
index c544c9e..83ea780 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
@@ -16,12 +16,10 @@
 import com.android.tools.r8.dex.code.DexConst4;
 import com.android.tools.r8.dex.code.DexConstClass;
 import com.android.tools.r8.dex.code.DexConstString;
-import com.android.tools.r8.dex.code.DexFilledNewArray;
 import com.android.tools.r8.dex.code.DexInvokeDirect;
 import com.android.tools.r8.dex.code.DexInvokeStatic;
 import com.android.tools.r8.dex.code.DexInvokeVirtual;
 import com.android.tools.r8.dex.code.DexIputObject;
-import com.android.tools.r8.dex.code.DexMoveResultObject;
 import com.android.tools.r8.dex.code.DexNewArray;
 import com.android.tools.r8.dex.code.DexReturnVoid;
 import com.android.tools.r8.dex.code.DexSgetObject;
@@ -641,8 +639,7 @@
             + "}",
         "-keep class " + CLASS_NAME,
         "-keep class R { *; }");
-    CodeInspector inspector =
-        compileWithR8(builder, testBuilder -> testBuilder.addKeepRules(pgConfigs)).inspector();
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -650,33 +647,19 @@
     assertNotNull(method);
 
     DexCode code = method.getCode().asDexCode();
-    // Accept either array construction style (differs based on minSdkVersion).
-    if (code.instructions[2].getClass() == DexFilledNewArray.class) {
-      checkInstructions(
-          code,
-          ImmutableList.of(
-              DexInvokeDirect.class,
-              DexConstClass.class,
-              DexFilledNewArray.class,
-              DexMoveResultObject.class,
-              DexConstString.class,
-              DexInvokeStatic.class,
-              DexReturnVoid.class));
-    } else {
-      checkInstructions(
-          code,
-          ImmutableList.of(
-              DexInvokeDirect.class,
-              DexConst4.class,
-              DexNewArray.class,
-              DexConst4.class,
-              DexConstClass.class,
-              DexAputObject.class,
-              DexConstString.class,
-              DexInvokeStatic.class,
-              DexReturnVoid.class));
-    }
-    DexConstString constString = (DexConstString) code.instructions[code.instructions.length - 3];
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            DexInvokeDirect.class,
+            DexConst4.class,
+            DexNewArray.class,
+            DexConst4.class,
+            DexConstClass.class,
+            DexAputObject.class,
+            DexConstString.class,
+            DexInvokeStatic.class,
+            DexReturnVoid.class));
+    DexConstString constString = (DexConstString) code.instructions[6];
     assertEquals("foo", constString.getString().toString());
   }
 
@@ -717,8 +700,7 @@
             + "}",
         "-keep class " + CLASS_NAME,
         "-keep,allowobfuscation class R { *; }");
-    CodeInspector inspector =
-        compileWithR8(builder, testBuilder -> testBuilder.addKeepRules(pgConfigs)).inspector();
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -726,33 +708,19 @@
     assertNotNull(method);
 
     DexCode code = method.getCode().asDexCode();
-    // Accept either array construction style (differs based on minSdkVersion).
-    if (code.instructions[2].getClass() == DexFilledNewArray.class) {
-      checkInstructions(
-          code,
-          ImmutableList.of(
-              DexInvokeDirect.class,
-              DexConstClass.class,
-              DexFilledNewArray.class,
-              DexMoveResultObject.class,
-              DexConstString.class,
-              DexInvokeStatic.class,
-              DexReturnVoid.class));
-    } else {
-      checkInstructions(
-          code,
-          ImmutableList.of(
-              DexInvokeDirect.class,
-              DexConst4.class,
-              DexNewArray.class,
-              DexConst4.class,
-              DexConstClass.class,
-              DexAputObject.class,
-              DexConstString.class,
-              DexInvokeStatic.class,
-              DexReturnVoid.class));
-    }
-    DexConstString constString = (DexConstString) code.instructions[code.instructions.length - 3];
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            DexInvokeDirect.class,
+            DexConst4.class,
+            DexNewArray.class,
+            DexConst4.class,
+            DexConstClass.class,
+            DexAputObject.class,
+            DexConstString.class,
+            DexInvokeStatic.class,
+            DexReturnVoid.class));
+    DexConstString constString = (DexConstString) code.instructions[6];
     assertNotEquals("foo", constString.getString().toString());
   }
 
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayWithDataLengthRewriteTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayWithDataLengthRewriteTest.java
index 513dc73..4a76a6a 100644
--- a/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayWithDataLengthRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayWithDataLengthRewriteTest.java
@@ -6,7 +6,6 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.graph.AppView;
@@ -16,7 +15,6 @@
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -35,7 +33,7 @@
     this.parameters = parameters;
   }
 
-  private static final String[] expectedOutput = {"3", "2"};
+  private static final String[] expectedOutput = {"3"};
 
   @Test
   public void d8() throws Exception {
@@ -46,7 +44,7 @@
         .addOptionsModification(opt -> opt.testing.irModifier = this::transformArray)
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines(expectedOutput)
-        .inspect(i -> inspect(i, true));
+        .inspect(this::assertNoArrayLength);
   }
 
   @Test
@@ -56,52 +54,32 @@
         .addProgramClasses(Main.class)
         .addOptionsModification(opt -> opt.testing.irModifier = this::transformArray)
         .addKeepMainRule(Main.class)
-        .enableInliningAnnotations()
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines(expectedOutput)
-        .inspect(i -> inspect(i, false));
+        .inspect(this::assertNoArrayLength);
   }
 
   private void transformArray(IRCode irCode, AppView<?> appView) {
+    if (irCode.context().getReference().getName().toString().contains("main")) {
       new CodeRewriter(appView).simplifyArrayConstruction(irCode);
-    String name = irCode.context().getReference().getName().toString();
-    if (name.contains("filledArrayData")) {
       assertTrue(irCode.streamInstructions().anyMatch(Instruction::isNewArrayFilledData));
-    } else if (name.contains("filledNewArray")) {
-      assertTrue(irCode.streamInstructions().anyMatch(Instruction::isInvokeNewArray));
     }
   }
 
-  private void inspect(CodeInspector inspector, boolean d8) {
+  private void assertNoArrayLength(CodeInspector inspector) {
     ClassSubject mainClass = inspector.clazz(Main.class);
     assertTrue(mainClass.isPresent());
-    MethodSubject filledArrayData = mainClass.uniqueMethodWithOriginalName("filledArrayData");
-    assertTrue(filledArrayData.streamInstructions().noneMatch(InstructionSubject::isArrayLength));
-    if (!d8) {
-      MethodSubject filledNewArray = mainClass.uniqueMethodWithOriginalName("filledNewArray");
-      assertTrue(filledNewArray.streamInstructions().noneMatch(InstructionSubject::isArrayLength));
-    }
+    assertTrue(
+        mainClass.mainMethod().streamInstructions().noneMatch(InstructionSubject::isArrayLength));
   }
 
   public static final class Main {
-    @NeverInline
-    public static void filledArrayData() {
-      short[] values = new short[3];
-      values[0] = 5;
-      values[1] = 6;
-      values[2] = 1;
-      System.out.println(values.length);
-    }
-
-    @NeverInline
-    public static void filledNewArray() {
-      int[] values = new int[] {7, 8};
-      System.out.println(values.length);
-    }
-
     public static void main(String[] args) {
-      filledArrayData();
-      filledNewArray();
+      int[] ints = new int[3];
+      ints[0] = 5;
+      ints[1] = 6;
+      ints[2] = 1;
+      System.out.println(ints.length);
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java
deleted file mode 100644
index 939753f..0000000
--- a/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java
+++ /dev/null
@@ -1,774 +0,0 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.rewrite.arrays;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.Keep;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.dex.code.DexFillArrayData;
-import com.android.tools.r8.dex.code.DexFilledNewArray;
-import com.android.tools.r8.dex.code.DexFilledNewArrayRange;
-import com.android.tools.r8.dex.code.DexNewArray;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.transformers.ClassFileTransformer;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.beust.jcommander.internal.Lists;
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Predicate;
-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 SimplifyArrayConstructionTest extends TestBase {
-  @Parameters(name = "{0}, mode = {1}")
-  public static Iterable<?> data() {
-    return buildParameters(
-        getTestParameters().withDefaultCfRuntime().withDexRuntimesAndAllApiLevels().build(),
-        CompilationMode.values());
-  }
-
-  private final TestParameters parameters;
-  private final CompilationMode compilationMode;
-
-  public SimplifyArrayConstructionTest(TestParameters parameters, CompilationMode compilationMode) {
-    this.parameters = parameters;
-    this.compilationMode = compilationMode;
-  }
-
-  private static final Class<?>[] DEX_ARRAY_INSTRUCTIONS = {
-    DexNewArray.class, DexFilledNewArray.class, DexFilledNewArrayRange.class, DexFillArrayData.class
-  };
-
-  private static final String[] EXPECTED_OUTPUT = {
-    "[a]",
-    "[a, 1, null]",
-    "[1, null]",
-    "[1, null, 2]",
-    "[1, null, 2]",
-    "[1]",
-    "[1, 2]",
-    "[1, 2, 3, 4, 5]",
-    "[1]",
-    "[a, 1, null, d, e, f]",
-    "[1, null, 3, null, null, 6]",
-    "[1, 2, 3, 4, 5, 6]",
-    "[true, false]",
-    "[1, 2]",
-    "[1, 2]",
-    "[1, 2]",
-    "[1.0, 2.0]",
-    "[1.0, 2.0]",
-    "[]",
-    "[]",
-    "[true]",
-    "[1]",
-    "[1]",
-    "[1]",
-    "[1.0]",
-    "[1.0]",
-    "[0, 1]",
-    "[1, null]",
-    "[a]",
-    "[0, 1]",
-    "200",
-    "[0, 1, 2, 3, 4]",
-    "[4, 0, 0, 0, 0]",
-    "[4, 1, 2, 3, 4]",
-    "[0, 1, 2]",
-    "[0]",
-    "[0, 1, 2]",
-    "[1, 2, 3]",
-    "[1, 2, 3, 4, 5, 6]",
-    "[0]",
-    "[null, null]",
-  };
-
-  private static final byte[] TRANSFORMED_MAIN = transformedMain();
-
-  @Test
-  public void testRuntime() throws Exception {
-    assumeFalse(compilationMode == CompilationMode.DEBUG);
-    testForRuntime(
-            parameters.getRuntime(),
-            d8TestBuilder ->
-                d8TestBuilder.setMinApi(parameters.getApiLevel()).setMode(compilationMode))
-        .addProgramClassFileData(TRANSFORMED_MAIN)
-        .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(EXPECTED_OUTPUT)
-        .inspect(inspector -> inspect(inspector, false));
-  }
-
-  @Test
-  public void testR8() throws Exception {
-    testForR8(parameters.getBackend())
-        .setMinApi(parameters.getApiLevel())
-        .addOptionsModification(
-            options ->
-                options
-                    .getOpenClosedInterfacesOptions()
-                    .suppressSingleOpenInterface(Reference.classFromClass(Serializable.class)))
-        .setMode(compilationMode)
-        .addProgramClassFileData(TRANSFORMED_MAIN)
-        .addKeepMainRule(Main.class)
-        .enableInliningAnnotations()
-        .addKeepAnnotation()
-        .addKeepRules("-keepclassmembers class * { @com.android.tools.r8.Keep *; }")
-        .addKeepRules("-assumenosideeffects class * { *** assumedNullField return null; }")
-        .addKeepRules("-assumenosideeffects class * { *** assumedNonNullField return _NONNULL_; }")
-        .addDontObfuscate()
-        .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(EXPECTED_OUTPUT)
-        .inspect(inspector -> inspect(inspector, true));
-  }
-
-  private static byte[] transformedMain() {
-    try {
-      return transformer(Main.class)
-          .transformMethodInsnInMethod(
-              "interfaceArrayWithRawObject",
-              (opcode, owner, name, descriptor, isInterface, visitor) -> {
-                if (name.equals("getObjectThatImplementsSerializable")) {
-                  visitor.visitMethodInsn(opcode, owner, name, "()Ljava/lang/Object;", isInterface);
-                } else {
-                  visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
-                }
-              })
-          .setReturnType(
-              ClassFileTransformer.MethodPredicate.onName("getObjectThatImplementsSerializable"),
-              Object.class.getTypeName())
-          .transform();
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
-  }
-
-  private void inspect(CodeInspector inspector, boolean isR8) {
-    if (parameters.isCfRuntime()) {
-      return;
-    }
-    ClassSubject mainClass = inspector.clazz(Main.class);
-    assertTrue(mainClass.isPresent());
-
-    MethodSubject stringArrays = mainClass.uniqueMethodWithOriginalName("stringArrays");
-    MethodSubject referenceArraysNoCasts =
-        mainClass.uniqueMethodWithOriginalName("referenceArraysNoCasts");
-    MethodSubject referenceArraysWithSubclasses =
-        mainClass.uniqueMethodWithOriginalName("referenceArraysWithSubclasses");
-    MethodSubject interfaceArrayWithRawObject =
-        mainClass.uniqueMethodWithOriginalName("interfaceArrayWithRawObject");
-
-    MethodSubject phiFilledNewArray = mainClass.uniqueMethodWithOriginalName("phiFilledNewArray");
-    MethodSubject intsThatUseFilledNewArray =
-        mainClass.uniqueMethodWithOriginalName("intsThatUseFilledNewArray");
-    MethodSubject twoDimensionalArrays =
-        mainClass.uniqueMethodWithOriginalName("twoDimensionalArrays");
-    MethodSubject objectArraysFilledNewArrayRange =
-        mainClass.uniqueMethodWithOriginalName("objectArraysFilledNewArrayRange");
-    MethodSubject arraysThatUseFilledData =
-        mainClass.uniqueMethodWithOriginalName("arraysThatUseFilledData");
-    MethodSubject arraysThatUseNewArrayEmpty =
-        mainClass.uniqueMethodWithOriginalName("arraysThatUseNewArrayEmpty");
-    MethodSubject reversedArray = mainClass.uniqueMethodWithOriginalName("reversedArray");
-    MethodSubject arrayWithCorrectCountButIncompleteCoverage =
-        mainClass.uniqueMethodWithOriginalName("arrayWithCorrectCountButIncompleteCoverage");
-    MethodSubject arrayWithExtraInitialPuts =
-        mainClass.uniqueMethodWithOriginalName("arrayWithExtraInitialPuts");
-    MethodSubject catchHandlerThrowing =
-        mainClass.uniqueMethodWithOriginalName("catchHandlerThrowing");
-    MethodSubject catchHandlerNonThrowingFilledNewArray =
-        mainClass.uniqueMethodWithOriginalName("catchHandlerNonThrowingFilledNewArray");
-    MethodSubject catchHandlerNonThrowingFillArrayData =
-        mainClass.uniqueMethodWithOriginalName("catchHandlerNonThrowingFillArrayData");
-    MethodSubject assumedValues = mainClass.uniqueMethodWithOriginalName("assumedValues");
-
-    assertArrayTypes(arraysThatUseNewArrayEmpty, DexNewArray.class);
-    assertArrayTypes(intsThatUseFilledNewArray, DexFilledNewArray.class);
-    assertFilledArrayData(arraysThatUseFilledData);
-
-    if (compilationMode == CompilationMode.DEBUG) {
-      // The explicit assignments can't be collapsed without breaking the debugger's ability to
-      // visit each line.
-      assertArrayTypes(reversedArray, DexNewArray.class);
-    } else {
-      assertArrayTypes(reversedArray, DexFilledNewArray.class);
-    }
-
-    // Cannot use filled-new-array of String before K.
-    if (parameters.getApiLevel().isLessThan(AndroidApiLevel.K)) {
-      assertArrayTypes(stringArrays, DexNewArray.class);
-    } else {
-      assertArrayTypes(stringArrays, DexFilledNewArray.class);
-    }
-    // Cannot use filled-new-array of Object before L.
-    if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
-      assertArrayTypes(referenceArraysNoCasts, DexNewArray.class);
-      assertArrayTypes(referenceArraysWithSubclasses, DexNewArray.class);
-      assertArrayTypes(phiFilledNewArray, DexNewArray.class);
-      assertArrayTypes(objectArraysFilledNewArrayRange, DexNewArray.class);
-      assertArrayTypes(twoDimensionalArrays, DexNewArray.class);
-      assertArrayTypes(assumedValues, DexNewArray.class);
-    } else {
-      assertArrayTypes(referenceArraysNoCasts, DexFilledNewArray.class);
-      // TODO(b/246971330): Add support for arrays with subtypes.
-      if (isR8) {
-        assertArrayTypes(referenceArraysWithSubclasses, DexFilledNewArray.class);
-      } else {
-        assertArrayTypes(referenceArraysWithSubclasses, DexNewArray.class);
-      }
-
-      // TODO(b/246971330): Add support for arrays whose values have conditionals.
-      // assertArrayTypes(phiFilledNewArray, DexFilledNewArray.class);
-
-      assertArrayTypes(objectArraysFilledNewArrayRange, DexFilledNewArrayRange.class);
-
-      if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L)) {
-        assertArrayTypes(twoDimensionalArrays, DexFilledNewArray.class);
-      } else {
-        // No need to assert this case. If it's wrong, Dalvik verify errors cause test failures.
-      }
-
-      assertArrayTypes(assumedValues, DexFilledNewArray.class);
-    }
-    // filled-new-array fails verification when types of parameters are not subclasses (aput-object
-    // does not).
-    assertArrayTypes(interfaceArrayWithRawObject, DexNewArray.class);
-
-    // These could be optimized to use InvokeNewArray, but they seem like rare code patterns so we
-    // haven't bothered.
-    assertArrayTypes(arrayWithExtraInitialPuts, DexNewArray.class);
-    assertArrayTypes(arrayWithCorrectCountButIncompleteCoverage, DexNewArray.class);
-
-    assertArrayTypes(catchHandlerThrowing, DexNewArray.class);
-    assertArrayTypes(catchHandlerNonThrowingFillArrayData, DexNewArray.class);
-    assertArrayTypes(catchHandlerNonThrowingFilledNewArray, DexFilledNewArray.class);
-  }
-
-  private static Predicate<InstructionSubject> isInstruction(List<Class<?>> allowlist) {
-    return (ins) -> allowlist.contains(ins.asDexInstruction().getInstruction().getClass());
-  }
-
-  private static Predicate<InstructionSubject> isInstruction(Class<?> clazz) {
-    return isInstruction(Arrays.asList(clazz));
-  }
-
-  private static void assertArrayTypes(MethodSubject method, Class<?> allowedArrayInst) {
-    assertTrue(method.isPresent());
-    List<Class<?>> disallowedClasses = Lists.newArrayList(DEX_ARRAY_INSTRUCTIONS);
-    disallowedClasses.remove(allowedArrayInst);
-
-    assertTrue(method.streamInstructions().anyMatch(isInstruction(allowedArrayInst)));
-    assertTrue(method.streamInstructions().noneMatch(isInstruction(disallowedClasses)));
-  }
-
-  private static void assertFilledArrayData(MethodSubject method) {
-    assertTrue(method.isPresent());
-    List<Class<?>> disallowedClasses = Lists.newArrayList(DEX_ARRAY_INSTRUCTIONS);
-    disallowedClasses.remove(DexFillArrayData.class);
-    disallowedClasses.remove(DexNewArray.class);
-
-    assertTrue(method.streamInstructions().noneMatch(isInstruction(disallowedClasses)));
-    assertTrue(method.streamInstructions().noneMatch(InstructionSubject::isArrayPut));
-    long numNewArray = method.streamInstructions().filter(InstructionSubject::isNewArray).count();
-    long numFillArray =
-        method.streamInstructions().filter(isInstruction(DexFillArrayData.class)).count();
-    assertEquals(numNewArray, numFillArray);
-  }
-
-  public static final class Main {
-    static final String assumedNonNullField = null;
-    static final String assumedNullField = null;
-
-    public static void main(String[] args) {
-      stringArrays();
-      referenceArraysNoCasts();
-      referenceArraysWithSubclasses();
-      interfaceArrayWithRawObject();
-      phiFilledNewArray();
-      intsThatUseFilledNewArray();
-      twoDimensionalArrays();
-      objectArraysFilledNewArrayRange();
-      arraysThatUseFilledData();
-      arraysThatUseNewArrayEmpty();
-      reversedArray();
-      arrayWithCorrectCountButIncompleteCoverage();
-      arrayWithExtraInitialPuts();
-      catchHandlerThrowing();
-      catchHandlerNonThrowingFilledNewArray();
-      catchHandlerNonThrowingFillArrayData();
-      arrayIntoAnotherArray();
-      assumedValues();
-    }
-
-    @NeverInline
-    private static void stringArrays() {
-      // Test exact class, no null.
-      String[] stringArr = {"a"};
-      System.out.println(Arrays.toString(stringArr));
-    }
-
-    @NeverInline
-    private static void referenceArraysNoCasts() {
-      // Tests that no type info is needed when array type is Object[].
-      Object[] objectArr = {"a", 1, null};
-      System.out.println(Arrays.toString(objectArr));
-      // Test that interface arrays work when we have the exact interface already.
-      Serializable[] interfaceArr = {getSerializable(1), null};
-      System.out.println(Arrays.toString(interfaceArr));
-    }
-
-    @Keep
-    private static Serializable getSerializable(Integer value) {
-      return value;
-    }
-
-    @NeverInline
-    private static void referenceArraysWithSubclasses() {
-      Serializable[] interfaceArr = {1, null, 2};
-      System.out.println(Arrays.toString(interfaceArr));
-      Number[] objArray = {1, null, 2};
-      System.out.println(Arrays.toString(objArray));
-    }
-
-    @NeverInline
-    private static void interfaceArrayWithRawObject() {
-      // Interfaces can use filled-new-array only when we know types implement the interface.
-      Serializable[] arr = new Serializable[1];
-      // Transformed from `I get()` to `Object get()`.
-      arr[0] = getObjectThatImplementsSerializable();
-      System.out.println(Arrays.toString(arr));
-    }
-
-    @Keep
-    private static /*Object*/ Serializable getObjectThatImplementsSerializable() {
-      return 1;
-    }
-
-    @NeverInline
-    private static void reversedArray() {
-      int[] arr = new int[5];
-      arr[4] = 4;
-      arr[3] = 3;
-      arr[2] = 2;
-      arr[1] = 1;
-      arr[0] = 0;
-      System.out.println(Arrays.toString(arr));
-    }
-
-    @NeverInline
-    private static void arrayWithCorrectCountButIncompleteCoverage() {
-      int[] arr = new int[5];
-      arr[0] = 0;
-      arr[0] = 1;
-      arr[0] = 2;
-      arr[0] = 3;
-      arr[0] = 4;
-      System.out.println(Arrays.toString(arr));
-    }
-
-    @NeverInline
-    private static void arrayWithExtraInitialPuts() {
-      int[] arr = new int[5];
-      arr[0] = 0;
-      arr[0] = 1;
-      arr[0] = 2;
-      arr[0] = 3;
-      arr[0] = 4;
-      arr[1] = 1;
-      arr[2] = 2;
-      arr[3] = 3;
-      arr[4] = 4;
-      System.out.println(Arrays.toString(arr));
-    }
-
-    @NeverInline
-    private static void catchHandlerNonThrowingFilledNewArray() {
-      try {
-        int[] arr1 = {1, 2, 3};
-        System.currentTimeMillis();
-        System.out.println(Arrays.toString(arr1));
-      } catch (Throwable t) {
-        throw new RuntimeException(t);
-      }
-    }
-
-    @NeverInline
-    private static void catchHandlerNonThrowingFillArrayData() {
-      try {
-        int[] arr = {1, 2, 3, 4, 5, 6};
-        System.currentTimeMillis();
-        System.out.println(Arrays.toString(arr));
-      } catch (Throwable t) {
-        throw new RuntimeException(t);
-      }
-    }
-
-    @NeverInline
-    private static void catchHandlerThrowing() {
-      int[] arr1 = new int[3];
-      arr1[0] = 0;
-      arr1[1] = 1;
-      // Since the array is used in only one spot, and that spot is not within the try/catch, it
-      // should be safe to use filled-new-array, but we don't.
-      try {
-        System.currentTimeMillis();
-        arr1[2] = 2;
-      } catch (Throwable t) {
-        throw new RuntimeException(t);
-      }
-      System.out.println(Arrays.toString(arr1));
-
-      try {
-        // Test filled-new-array with a throwing instruction before the last array-put.
-        int[] arr2 = new int[1];
-        System.currentTimeMillis();
-        arr2[0] = 0;
-        System.out.println(Arrays.toString(arr2));
-
-        // Test filled-array-data with a throwing instruction before the last array-put.
-        short[] arr3 = new short[3];
-        arr3[0] = 0;
-        arr3[1] = 1;
-        System.currentTimeMillis();
-        arr3[2] = 2;
-        System.out.println(Arrays.toString(arr3));
-      } catch (Throwable t) {
-        throw new RuntimeException(t);
-      }
-    }
-
-    @NeverInline
-    private static void arrayIntoAnotherArray() {
-      // Tests that our computeValues() method does not assume all array-put-object users have
-      // the array as the target.
-      int[] intArr = new int[1];
-      Object[] objArr = new Object[2];
-      objArr[0] = intArr;
-      System.out.println(Arrays.toString((int[]) objArr[0]));
-    }
-
-    @NeverInline
-    private static void assumedValues() {
-      Object[] arr = {assumedNonNullField, assumedNullField};
-      System.out.println(Arrays.toString(arr));
-    }
-
-    @NeverInline
-    private static void phiFilledNewArray() {
-      // The presence of ? should not affect use of filled-new-array.
-      Integer[] phiArray = {1, System.nanoTime() > 0 ? 2 : 3};
-      System.out.println(Arrays.toString(phiArray));
-    }
-
-    @NeverInline
-    private static void intsThatUseFilledNewArray() {
-      // Up to 5 ints uses filled-new-array rather than filled-array-data.
-      int[] intArr = {1, 2, 3, 4, 5};
-      System.out.println(Arrays.toString(intArr));
-    }
-
-    @NeverInline
-    private static void twoDimensionalArrays() {
-      Integer[][] twoDimensions = {new Integer[] {1}, null};
-      System.out.println(Arrays.toString(Arrays.asList(twoDimensions).get(0)));
-    }
-
-    @NeverInline
-    private static void objectArraysFilledNewArrayRange() {
-      // 6 or more elements use /range variant.
-      Object[] objectArr = {"a", 1, null, "d", "e", "f"};
-      System.out.println(Arrays.toString(objectArr));
-      Serializable[] interfaceArr = {
-        getSerializable(1), null, getSerializable(3), null, null, getSerializable(6)
-      };
-      System.out.println(Arrays.toString(interfaceArr));
-    }
-
-    @NeverInline
-    private static void arraysThatUseFilledData() {
-      // For int[], <= 5 elements should use NewArrayFilledData (otherwise NewFilledArray is used).
-      int[] intArr = {1, 2, 3, 4, 5, 6};
-      // For other primitives, > 1 element leads to fill-array-data.
-      System.out.println(Arrays.toString(intArr));
-      boolean[] boolArr = {true, false};
-      System.out.println(Arrays.toString(boolArr));
-      byte[] byteArr = {1, 2};
-      System.out.println(Arrays.toString(byteArr));
-      char[] charArr = {'1', '2'};
-      System.out.println(Arrays.toString(charArr));
-      long[] longArr = {1, 2};
-      System.out.println(Arrays.toString(longArr));
-      float[] floatArr = {1, 2};
-      System.out.println(Arrays.toString(floatArr));
-      double[] doubleArr = {1, 2};
-      System.out.println(Arrays.toString(doubleArr));
-    }
-
-    @NeverInline
-    private static void arraysThatUseNewArrayEmpty() {
-      // int/object of size zero should not use filled-new-array.
-      int[] intArr = {};
-      System.out.println(Arrays.toString(intArr));
-      String[] strArr = {};
-      System.out.println(Arrays.toString(strArr));
-
-      // Other primitives with size <= 1 should not use filled-array-data.
-      boolean[] boolArr = {true};
-      System.out.println(Arrays.toString(boolArr));
-      byte[] byteArr = {1};
-      System.out.println(Arrays.toString(byteArr));
-      char[] charArr = {'1'};
-      System.out.println(Arrays.toString(charArr));
-      long[] longArr = {1};
-      System.out.println(Arrays.toString(longArr));
-      float[] floatArr = {1};
-      System.out.println(Arrays.toString(floatArr));
-      double[] doubleArr = {1};
-      System.out.println(Arrays.toString(doubleArr));
-
-      // Array used before all members are set.
-      int[] readArray = new int[2];
-      readArray[0] = (int) (System.currentTimeMillis() / System.nanoTime());
-      readArray[1] = readArray[0] + 1;
-      System.out.println(Arrays.toString(readArray));
-
-      // Array does not have all elements set (we could make this work, but likely this is rare).
-      Integer[] partialArray = new Integer[2];
-      partialArray[0] = 1;
-      System.out.println(Arrays.toString(partialArray));
-
-      // Non-constant array size.
-      int trickyZero = (int) (System.currentTimeMillis() / System.nanoTime());
-      Object[] nonConstSize = new Object[trickyZero + 1];
-      nonConstSize[0] = "a";
-      System.out.println(Arrays.toString(nonConstSize));
-
-      // Non-constant index.
-      Object[] nonConstIndex = new Object[2];
-      nonConstIndex[trickyZero] = 0;
-      nonConstIndex[trickyZero + 1] = 1;
-      System.out.println(Arrays.toString(nonConstIndex));
-
-      // Exceeds our (arbitrary) size limit for /range.
-      String[] bigArr = new String[201];
-      bigArr[0] = "0";
-      bigArr[1] = "1";
-      bigArr[2] = "2";
-      bigArr[3] = "3";
-      bigArr[4] = "4";
-      bigArr[5] = "5";
-      bigArr[6] = "6";
-      bigArr[7] = "7";
-      bigArr[8] = "8";
-      bigArr[9] = "9";
-      bigArr[10] = "10";
-      bigArr[11] = "11";
-      bigArr[12] = "12";
-      bigArr[13] = "13";
-      bigArr[14] = "14";
-      bigArr[15] = "15";
-      bigArr[16] = "16";
-      bigArr[17] = "17";
-      bigArr[18] = "18";
-      bigArr[19] = "19";
-      bigArr[20] = "20";
-      bigArr[21] = "21";
-      bigArr[22] = "22";
-      bigArr[23] = "23";
-      bigArr[24] = "24";
-      bigArr[25] = "25";
-      bigArr[26] = "26";
-      bigArr[27] = "27";
-      bigArr[28] = "28";
-      bigArr[29] = "29";
-      bigArr[30] = "30";
-      bigArr[31] = "31";
-      bigArr[32] = "32";
-      bigArr[33] = "33";
-      bigArr[34] = "34";
-      bigArr[35] = "35";
-      bigArr[36] = "36";
-      bigArr[37] = "37";
-      bigArr[38] = "38";
-      bigArr[39] = "39";
-      bigArr[40] = "40";
-      bigArr[41] = "41";
-      bigArr[42] = "42";
-      bigArr[43] = "43";
-      bigArr[44] = "44";
-      bigArr[45] = "45";
-      bigArr[46] = "46";
-      bigArr[47] = "47";
-      bigArr[48] = "48";
-      bigArr[49] = "49";
-      bigArr[50] = "50";
-      bigArr[51] = "51";
-      bigArr[52] = "52";
-      bigArr[53] = "53";
-      bigArr[54] = "54";
-      bigArr[55] = "55";
-      bigArr[56] = "56";
-      bigArr[57] = "57";
-      bigArr[58] = "58";
-      bigArr[59] = "59";
-      bigArr[60] = "60";
-      bigArr[61] = "61";
-      bigArr[62] = "62";
-      bigArr[63] = "63";
-      bigArr[64] = "64";
-      bigArr[65] = "65";
-      bigArr[66] = "66";
-      bigArr[67] = "67";
-      bigArr[68] = "68";
-      bigArr[69] = "69";
-      bigArr[70] = "70";
-      bigArr[71] = "71";
-      bigArr[72] = "72";
-      bigArr[73] = "73";
-      bigArr[74] = "74";
-      bigArr[75] = "75";
-      bigArr[76] = "76";
-      bigArr[77] = "77";
-      bigArr[78] = "78";
-      bigArr[79] = "79";
-      bigArr[80] = "80";
-      bigArr[81] = "81";
-      bigArr[82] = "82";
-      bigArr[83] = "83";
-      bigArr[84] = "84";
-      bigArr[85] = "85";
-      bigArr[86] = "86";
-      bigArr[87] = "87";
-      bigArr[88] = "88";
-      bigArr[89] = "89";
-      bigArr[90] = "90";
-      bigArr[91] = "91";
-      bigArr[92] = "92";
-      bigArr[93] = "93";
-      bigArr[94] = "94";
-      bigArr[95] = "95";
-      bigArr[96] = "96";
-      bigArr[97] = "97";
-      bigArr[98] = "98";
-      bigArr[99] = "99";
-      bigArr[100] = "100";
-      bigArr[101] = "101";
-      bigArr[102] = "102";
-      bigArr[103] = "103";
-      bigArr[104] = "104";
-      bigArr[105] = "105";
-      bigArr[106] = "106";
-      bigArr[107] = "107";
-      bigArr[108] = "108";
-      bigArr[109] = "109";
-      bigArr[110] = "110";
-      bigArr[111] = "111";
-      bigArr[112] = "112";
-      bigArr[113] = "113";
-      bigArr[114] = "114";
-      bigArr[115] = "115";
-      bigArr[116] = "116";
-      bigArr[117] = "117";
-      bigArr[118] = "118";
-      bigArr[119] = "119";
-      bigArr[120] = "120";
-      bigArr[121] = "121";
-      bigArr[122] = "122";
-      bigArr[123] = "123";
-      bigArr[124] = "124";
-      bigArr[125] = "125";
-      bigArr[126] = "126";
-      bigArr[127] = "127";
-      bigArr[128] = "128";
-      bigArr[129] = "129";
-      bigArr[130] = "130";
-      bigArr[131] = "131";
-      bigArr[132] = "132";
-      bigArr[133] = "133";
-      bigArr[134] = "134";
-      bigArr[135] = "135";
-      bigArr[136] = "136";
-      bigArr[137] = "137";
-      bigArr[138] = "138";
-      bigArr[139] = "139";
-      bigArr[140] = "140";
-      bigArr[141] = "141";
-      bigArr[142] = "142";
-      bigArr[143] = "143";
-      bigArr[144] = "144";
-      bigArr[145] = "145";
-      bigArr[146] = "146";
-      bigArr[147] = "147";
-      bigArr[148] = "148";
-      bigArr[149] = "149";
-      bigArr[150] = "150";
-      bigArr[151] = "151";
-      bigArr[152] = "152";
-      bigArr[153] = "153";
-      bigArr[154] = "154";
-      bigArr[155] = "155";
-      bigArr[156] = "156";
-      bigArr[157] = "157";
-      bigArr[158] = "158";
-      bigArr[159] = "159";
-      bigArr[160] = "160";
-      bigArr[161] = "161";
-      bigArr[162] = "162";
-      bigArr[163] = "163";
-      bigArr[164] = "164";
-      bigArr[165] = "165";
-      bigArr[166] = "166";
-      bigArr[167] = "167";
-      bigArr[168] = "168";
-      bigArr[169] = "169";
-      bigArr[170] = "170";
-      bigArr[171] = "171";
-      bigArr[172] = "172";
-      bigArr[173] = "173";
-      bigArr[174] = "174";
-      bigArr[175] = "175";
-      bigArr[176] = "176";
-      bigArr[177] = "177";
-      bigArr[178] = "178";
-      bigArr[179] = "179";
-      bigArr[180] = "180";
-      bigArr[181] = "181";
-      bigArr[182] = "182";
-      bigArr[183] = "183";
-      bigArr[184] = "184";
-      bigArr[185] = "185";
-      bigArr[186] = "186";
-      bigArr[187] = "187";
-      bigArr[188] = "188";
-      bigArr[189] = "189";
-      bigArr[190] = "190";
-      bigArr[191] = "191";
-      bigArr[192] = "192";
-      bigArr[193] = "193";
-      bigArr[194] = "194";
-      bigArr[195] = "195";
-      bigArr[196] = "196";
-      bigArr[197] = "197";
-      bigArr[198] = "198";
-      bigArr[199] = "199";
-      bigArr[200] = "200";
-      System.out.println(Arrays.asList(bigArr).get(200));
-    }
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceDumpInputsTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceDumpInputsTest.java
new file mode 100644
index 0000000..963c2e7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceDumpInputsTest.java
@@ -0,0 +1,123 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.tracereferences;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.StringConsumer.FileConsumer;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.DumpInputFlags;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ZipUtils;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TraceReferenceDumpInputsTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withSystemRuntime().build();
+  }
+
+  public TraceReferenceDumpInputsTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testDumpToDirectory() throws Exception {
+    Path dumpDir = temp.newFolder().toPath();
+    TraceReferencesKeepRules keepRulesConsumer =
+        TraceReferencesKeepRules.builder()
+            .setAllowObfuscation(true)
+            .setOutputConsumer(new FileConsumer(temp.newFile().toPath()))
+            .build();
+    TraceReferencesCommand command =
+        TraceReferencesCommand.builder()
+            .setConsumer(keepRulesConsumer)
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+            .addTargetFiles(ToolHelper.getClassFileForTestClass(TestClass.class))
+            .addSourceFiles(ToolHelper.getClassFileForTestClass(OtherTestClass.class))
+            .build();
+    InternalOptions internalOptions = command.getInternalOptions();
+    internalOptions.setDumpInputFlags(DumpInputFlags.dumpToDirectory(dumpDir));
+    TraceReferences.runForTesting(command, internalOptions);
+    verifyDumpDirectory(dumpDir);
+  }
+
+  private void verifyDumpDirectory(Path dumpDir) throws IOException {
+    assertTrue(Files.isDirectory(dumpDir));
+    List<Path> paths = Files.walk(dumpDir, 1).collect(Collectors.toList());
+    boolean hasVerified = false;
+    for (Path path : paths) {
+      if (!path.equals(dumpDir)) {
+        verifyDump(path);
+        hasVerified = true;
+      }
+    }
+    assertTrue(hasVerified);
+  }
+
+  private void verifyDump(Path dumpFile) throws IOException {
+    assertTrue(Files.exists(dumpFile));
+    Path unzipped = temp.newFolder().toPath();
+    ZipUtils.unzip(dumpFile.toString(), unzipped.toFile());
+    assertTrue(Files.exists(unzipped.resolve("r8-version")));
+    assertTrue(Files.exists(unzipped.resolve("build.properties")));
+    assertTrue(Files.exists(unzipped.resolve("program.jar")));
+    assertTrue(Files.exists(unzipped.resolve("library.jar")));
+    assertTrue(Files.exists(unzipped.resolve("classpath.jar")));
+    contains(unzipped, "program.jar", OtherTestClass.class);
+    contains(unzipped, "classpath.jar", TestClass.class);
+    checkProperties(unzipped.resolve("build.properties"));
+  }
+
+  private void checkProperties(Path properties) throws IOException {
+    List<String> lines = Files.readAllLines(properties);
+    assertEquals(4, lines.size());
+    assertEquals("tool=TraceReferences", lines.get(0));
+    assertEquals(
+        "trace_references_consumer=com.android.tools.r8.tracereferences.TraceReferencesKeepRules",
+        lines.get(2));
+    assertEquals("minification=true", lines.get(3));
+  }
+
+  private void contains(Path unzipped, String jar, Class<?> clazz) throws IOException {
+    Set<String> entries = new HashSet<>();
+    ZipUtils.iter(unzipped.resolve(jar).toString(), (entry, input) -> entries.add(entry.getName()));
+    assertTrue(
+        entries.contains(
+            DescriptorUtils.getClassFileName(
+                DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()))));
+  }
+
+  static class TestClass {
+    public static void main(String[] args) {
+      System.out.println("Hello, world");
+    }
+  }
+
+  static class OtherTestClass {
+    public static void main(String[] args) {
+      TestClass.main(args);
+    }
+  }
+}