Run test examples in CF-to-DEX mode

Add getFailingCompileCfToDex() and getFailingRunCfToDex() to mark tests
that currently fail when running with the CF frontend for testing and
the DEX backend because of b/109788783 (byte/boolean distinction),
b/109789539 (multianewarray), b/109789541 (synchronized methods).

* Add references to tracking bugs for missing implementations.

* CfMultiANewArray.buildIR(): Throw Unimplemented when running with the
  DEX backend.

* barray example: Add loads and stores to non-null arrays.

Bug: 109788783, 109789539, 109789541
Change-Id: If80113549b050be533f506bf9b3d7a9e6ec36b8d
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index 015b8a3..0eed4a6 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -52,6 +53,10 @@
 
   @Override
   public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
+    if (!builder.isGeneratingClassFiles()) {
+      // TODO(b/109789539): Implement this case (see JarSourceCode.buildPrelude()/buildPostlude()).
+      throw new Unimplemented("CfMultiANewArray to DEX backend");
+    }
     int[] dimensions = state.popReverse(this.dimensions);
     builder.addMultiNewArray(type, state.push(type).register, dimensions);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index ffa34d1..b8e0bd8 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -225,8 +225,11 @@
       ValueNumberGenerator generator,
       Position callerPosition,
       Origin origin) {
-    assert !options.isGeneratingDex() || !encodedMethod.accessFlags.isSynchronized()
-        : "Converting CfCode to IR not supported for DEX output of synchronized methods.";
+    // TODO(b/109789541): Implement CF->IR->DEX for synchronized methods.
+    if (options.isGeneratingDex() && encodedMethod.accessFlags.isSynchronized()) {
+      throw new Unimplemented(
+          "Converting CfCode to IR not supported for DEX output of synchronized methods.");
+    }
     CfSourceCode source = new CfSourceCode(this, encodedMethod, callerPosition, origin);
     IRBuilder builder =
         (generator == null)
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 454ed29..afc2969 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -533,7 +533,8 @@
           return MemberType.OBJECT;
         case Opcodes.BALOAD:
         case Opcodes.BASTORE:
-          return MemberType.BOOLEAN; // TODO: Distinguish byte and boolean.
+          // TODO(b/109788783): Distinguish byte and boolean.
+          return MemberType.BOOLEAN;
         case Opcodes.CALOAD:
         case Opcodes.CASTORE:
           return MemberType.CHAR;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index e68e3d5..ca79b0f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -324,7 +324,7 @@
     buildArgumentInstructions(builder);
     recordStateForTarget(0, state.getSnapshot());
     // TODO: addDebugLocalUninitialized + addDebugLocalStart for non-argument locals live at 0
-    // TODO: Generate method synchronization
+    // TODO(b/109789541): Generate method synchronization for DEX backend.
     inPrelude = false;
   }
 
@@ -353,7 +353,7 @@
 
   @Override
   public void buildPostlude(IRBuilder builder) {
-    // Since we're generating classfiles, we never need to synthesize monitor enter/exit.
+    // TODO(b/109789541): Generate method synchronization for DEX backend.
   }
 
   @Override
diff --git a/src/test/examples/barray/BArray.java b/src/test/examples/barray/BArray.java
index 0ca6505..26ac5f8 100644
--- a/src/test/examples/barray/BArray.java
+++ b/src/test/examples/barray/BArray.java
@@ -6,20 +6,59 @@
 public class BArray {
 
   public static void main(String[] args) {
+    System.out.println("null boolean: " + readNullBooleanArray());
+    System.out.println("null byte: " + readNullByteArray());
+    System.out.println("boolean: " + readBooleanArray(writeBooleanArray(args)));
+    System.out.println("byte: " + readByteArray(writeByteArray(args)));
+  }
+
+  public static boolean readNullBooleanArray() {
     boolean[] boolArray = null;
+    try {
+      return boolArray[0] || boolArray[1];
+    } catch (Throwable e) {
+      return true;
+    }
+  }
+
+  public static byte readNullByteArray() {
     byte[] byteArray = null;
-    boolean bool;
-    byte bits;
     try {
-      bool = boolArray[0] || boolArray[1];
+      return byteArray[0];
     } catch (Throwable e) {
-      bool = true;
+      return 42;
     }
+  }
+
+  public static boolean[] writeBooleanArray(String[] args) {
+    boolean[] array = new boolean[args.length];
+    for (int i = 0; i < args.length; i++) {
+      array[i] = args[i].length() == 42;
+    }
+    return array;
+  }
+
+  public static byte[] writeByteArray(String[] args) {
+    byte[] array = new byte[args.length];
+    for (int i = 0; i < args.length; i++) {
+      array[i] = (byte) args[i].length();
+    }
+    return array;
+  }
+
+  public static boolean readBooleanArray(boolean[] boolArray) {
     try {
-      bits = byteArray[0];
+      return boolArray[0] || boolArray[1];
     } catch (Throwable e) {
-      bits = 42;
+      return true;
     }
-    System.out.println("bits " + bits + " and bool " + bool);
+  }
+
+  public static byte readByteArray(byte[] byteArray) {
+    try {
+      return byteArray[0];
+    } catch (Throwable e) {
+      return 42;
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
index fe5ae72..4afd0c2 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
@@ -148,6 +148,11 @@
     if (output == Output.CF && getFailingCompileCf().contains(mainClass)) {
       thrown.expect(Throwable.class);
     }
+    if (frontend == Frontend.CF
+        && output == Output.DEX
+        && getFailingCompileCfToDex().contains(mainClass)) {
+      thrown.expect(Throwable.class);
+    }
     OutputMode outputMode = output == Output.CF ? OutputMode.ClassFile : OutputMode.DexIndexed;
     switch (compiler) {
       case D8: {
@@ -215,6 +220,12 @@
       thrown = ExpectedException.none();
     }
 
+    if (frontend == Frontend.CF
+        && output == Output.DEX
+        && getFailingRunCfToDex().contains(mainClass)) {
+      thrown.expect(Throwable.class);
+    }
+
     if (output == Output.CF) {
       ToolHelper.ProcessResult result = ToolHelper.runJava(generated, mainClass);
       if (result.exitCode != 0) {
@@ -257,6 +268,11 @@
 
   protected abstract Map<String, TestCondition> getFailingRunCf();
 
+  protected abstract Set<String> getFailingCompileCfToDex();
+
+  // TODO(mathiasr): Add CompilerSet for CfToDex so we can fold this into getFailingRun().
+  protected abstract Set<String> getFailingRunCfToDex();
+
   protected abstract Set<String> getFailingCompileCf();
 
   protected abstract Set<String> getFailingOutputCf();
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
index 97c8624..dc94a71 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
@@ -49,6 +49,16 @@
   }
 
   @Override
+  protected Set<String> getFailingCompileCfToDex() {
+    return Collections.emptySet();
+  }
+
+  @Override
+  protected Set<String> getFailingRunCfToDex() {
+    return Collections.emptySet();
+  }
+
+  @Override
   protected Set<String> getFailingCompileCf() {
     return Collections.emptySet();
   }
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index d8b886c..4ee928b 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -109,6 +109,14 @@
               test,
               Frontend.CF,
               Output.CF));
+      fullTestList.add(
+          makeTest(
+              Input.JAVAC_ALL,
+              CompilerUnderTest.R8,
+              CompilationMode.RELEASE,
+              test,
+              Frontend.CF,
+              Output.DEX));
     }
     return fullTestList;
   }
@@ -143,6 +151,29 @@
   }
 
   @Override
+  protected Set<String> getFailingCompileCfToDex() {
+    return new ImmutableSet.Builder<String>()
+        // TODO(b/109788783): Implement byte/boolean distinction for array load/store.
+        .add("arrayaccess.ArrayAccess")
+        .add("barray.BArray")
+        .add("filledarray.FilledArray")
+        .build();
+  }
+
+  @Override
+  protected Set<String> getFailingRunCfToDex() {
+    return new ImmutableSet.Builder<String>()
+        // TODO(b/109789541): Implement method synchronization for DEX backend.
+        .add("sync.Sync")
+        // TODO(b/109789539): Implement CfMultiANewArray.buildIR() for DEX backend.
+        .add("newarray.NewArray")
+        .add("trycatch.TryCatch")
+        .add("regress_70737019.Test")
+        .add("regress_72361252.Test")
+        .build();
+  }
+
+  @Override
   protected Set<String> getFailingCompileCf() {
     return new ImmutableSet.Builder<String>()
         .build();