Merge "Insert cast-instruction from inlining in entry block if possible"
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 9b90df2..9828bed 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -97,7 +97,7 @@
 
   public abstract DexMethod getRenamedMethodSignature(DexMethod originalMethod);
 
-  public final DexEncodedMethod mapDexEncodedMethod(
+  public DexEncodedMethod mapDexEncodedMethod(
       AppInfo appInfo, DexEncodedMethod originalEncodedMethod) {
     DexMethod newMethod = getRenamedMethodSignature(originalEncodedMethod.method);
     if (newMethod != originalEncodedMethod.method) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 05553c1..5c4d581 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -733,7 +733,10 @@
       return;
     }
     assert local != null;
-    assert local == getOutgoingLocal(register);
+    assert local == getOutgoingLocal(register) :
+        "local-start mismatch: " + local + " != " + getOutgoingLocal(register)
+            + " at " + currentInstructionOffset
+            + " for source\n" + source.toString();
     // TODO(b/111251032): Here we lookup a value with type based on debug info. That's just wrong!
     ValueType valueType = ValueType.fromDexType(local.type);
     Value incomingValue = readRegisterIgnoreLocal(register, valueType, local);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java
new file mode 100644
index 0000000..a8616fb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+class InterfaceMethodDesugaringLense extends NestedGraphLense {
+  private final Map<DexEncodedMethod, DexEncodedMethod> methodsWithMovedCode;
+
+  InterfaceMethodDesugaringLense(
+      BiMap<DexMethod, DexMethod> methodMapping,
+      Map<DexEncodedMethod, DexEncodedMethod> methodsWithMovedCode,
+      GraphLense previous, DexItemFactory factory) {
+    super(
+        ImmutableMap.of(),
+        methodMapping,
+        ImmutableMap.of(),
+        ImmutableBiMap.of(),
+        methodMapping.inverse(),
+        previous,
+        factory);
+    this.methodsWithMovedCode = methodsWithMovedCode;
+  }
+
+  @Override
+  public DexEncodedMethod mapDexEncodedMethod(AppInfo appInfo, DexEncodedMethod original) {
+    return super.mapDexEncodedMethod(
+        appInfo, methodsWithMovedCode.getOrDefault(original, original));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 76a80d4..3883f4d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -368,6 +368,12 @@
         processor.process(clazz.asProgramClass());
       }
     }
+    if (converter.enableWholeProgramOptimizations &&
+        (!processor.methodsWithMovedCode.isEmpty() || !processor.movedMethods.isEmpty())) {
+      converter.setGraphLense(
+          new InterfaceMethodDesugaringLense(processor.movedMethods,
+              processor.methodsWithMovedCode, converter.getGraphLense(), factory));
+    }
     return processor.companionClasses;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index c538e86..e5900d9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -19,6 +19,8 @@
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -40,6 +42,9 @@
   // All created companion classes indexed by interface classes.
   final Map<DexProgramClass, DexProgramClass> companionClasses = new IdentityHashMap<>();
 
+  final BiMap<DexMethod, DexMethod> movedMethods = HashBiMap.create();
+  final Map<DexEncodedMethod, DexEncodedMethod> methodsWithMovedCode = new IdentityHashMap<>();
+
   InterfaceProcessor(InterfaceMethodRewriter rewriter) {
     this.rewriter = rewriter;
   }
@@ -74,12 +79,15 @@
         assert (dexCode.getDebugInfo() == null)
             || (companionMethod.getArity() == dexCode.getDebugInfo().parameters.length);
 
-        companionMethods.add(new DexEncodedMethod(companionMethod,
-            newFlags, virtual.annotations, virtual.parameterAnnotationsList, code));
-
         // Make the method abstract.
         virtual.accessFlags.setAbstract();
-        virtual.removeCode();
+        virtual.removeCode(); // Remove code first to void ownership.
+
+        DexEncodedMethod implMethod = new DexEncodedMethod(
+            companionMethod, newFlags, virtual.annotations, virtual.parameterAnnotationsList, code);
+        companionMethods.add(implMethod);
+
+        methodsWithMovedCode.put(virtual, implMethod);
       }
 
       // Remove bridge methods.
@@ -105,27 +113,29 @@
         newFlags.setPublic();
       }
 
+      DexMethod oldMethod = direct.method;
       if (isStaticMethod(direct)) {
         assert originalFlags.isPrivate() || originalFlags.isPublic()
             : "Static interface method " + direct.toSourceString() + " is expected to "
             + "either be public or private in " + iface.origin;
-        companionMethods.add(new DexEncodedMethod(
-            rewriter.staticAsMethodOfCompanionClass(direct.method), newFlags,
+        DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(oldMethod);
+        companionMethods.add(new DexEncodedMethod(companionMethod, newFlags,
             direct.annotations, direct.parameterAnnotationsList, direct.getCode()));
+        movedMethods.put(oldMethod, companionMethod);
 
       } else {
         if (originalFlags.isPrivate()) {
-          assert !rewriter.factory.isClassConstructor(direct.method)
+          assert !rewriter.factory.isClassConstructor(oldMethod)
               : "Unexpected private constructor " + direct.toSourceString()
               + " in " + iface.origin;
           newFlags.setStatic();
 
-          DexMethod companionMethod = rewriter.privateAsMethodOfCompanionClass(direct.method);
+          DexMethod companionMethod = rewriter.privateAsMethodOfCompanionClass(oldMethod);
 
           Code code = direct.getCode();
           if (code == null) {
             throw new CompilationError("Code is missing for private instance "
-                + "interface method: " + direct.method.toSourceString(), iface.origin);
+                + "interface method: " + oldMethod.toSourceString(), iface.origin);
           }
           DexCode dexCode = code.asDexCode();
           // TODO(ager): Should we give the new first parameter an actual name? Maybe 'this'?
@@ -135,11 +145,12 @@
 
           companionMethods.add(new DexEncodedMethod(companionMethod,
               newFlags, direct.annotations, direct.parameterAnnotationsList, code));
+          movedMethods.put(oldMethod, companionMethod);
 
         } else {
           // Since there are no interface constructors at this point,
           // this should only be class constructor.
-          assert rewriter.factory.isClassConstructor(direct.method);
+          assert rewriter.factory.isClassConstructor(oldMethod);
           remainingMethods.add(direct);
         }
       }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 80e407c..67fcb59 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -53,7 +53,6 @@
 import org.junit.rules.TemporaryFolder;
 
 public class TestBase {
-
   protected enum Backend {
     CF,
     DEX
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 2af67a4..8337f6e 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -64,6 +64,7 @@
 import java.util.concurrent.Executors;
 import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import org.junit.Assume;
 import org.junit.rules.TemporaryFolder;
@@ -758,8 +759,13 @@
   }
 
   public static List<Path> getClassFilesForTestDirectory(Path directory) throws IOException {
+    return getClassFilesForTestDirectory(directory, null);
+  }
+
+  public static List<Path> getClassFilesForTestDirectory(
+      Path directory, Predicate<Path> filter) throws IOException {
     return Files.walk(directory)
-        .filter(path -> path.toString().endsWith(".class"))
+        .filter(path -> path.toString().endsWith(".class") && (filter == null || filter.test(path)))
         .collect(Collectors.toList());
   }
 
diff --git a/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java b/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java
index c805c15..4cfb71a 100644
--- a/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java
@@ -4,8 +4,10 @@
 package com.android.tools.r8.debug;
 
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.DebuggeeState;
 import java.util.stream.Stream;
+import org.junit.Assume;
 import org.junit.Test;
 
 // See b/80385846
@@ -16,6 +18,8 @@
 
   @Test
   public void test() throws Exception {
+    Assume.assumeTrue("Older runtimes cause some kind of debug streaming issues",
+        ToolHelper.getDexVm().isNewerThan(DexVm.ART_5_1_1_HOST));
     DebugTestConfig cfConfig = new CfDebugTestConfig().addPaths(ToolHelper.getClassPathForTests());
     DebugTestConfig d8Config = new D8DebugTestConfig().compileAndAddClasses(temp, CLASS);
     new DebugStreamComparator()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
new file mode 100644
index 0000000..2e6fb4c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.outliner.b112247415;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+class TestClass {
+  interface Act {
+    default String get(StringBuilder builder, String arg) {
+      builder.append(arg).append(arg).append(arg);
+      return builder.toString();
+    }
+  }
+
+  public static void main(String[] args) {
+    System.out.println(get(new TestClass().toActOverridden(), new StringBuilder(), "a"));
+    System.out.println(get(new TestClass().toActDefault(), new StringBuilder(), "b"));
+  }
+
+  static String get(Act act, StringBuilder builder, String arg) {
+    act.get(builder, arg);
+    return builder.toString();
+  }
+
+  Act toActOverridden() {
+    return new Act() {
+      @Override
+      public String get(StringBuilder builder, String arg) {
+        builder.append(arg).append(arg).append(arg);
+        return builder.toString();
+      }
+    };
+  }
+
+  Act toActDefault() {
+    return new Act() {
+    };
+  }
+}
+
+@RunWith(VmTestRunner.class)
+public class B112247415 extends TestBase {
+
+  @Test
+  public void test() throws Exception {
+    String javaResult = runOnJava(TestClass.class);
+
+    R8Command.Builder builder = R8Command.builder();
+    builder.addProgramFiles(ToolHelper.getClassFilesForTestDirectory(
+        ToolHelper.getPackageDirectoryForTestPackage(TestClass.Act.class.getPackage()),
+        path -> path.getFileName().toString().startsWith("TestClass")));
+    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+    String config = keepMainProguardConfiguration(TestClass.class);
+    builder.addProguardConfiguration(ImmutableList.of(config), Origin.unknown());
+    AndroidApp app = ToolHelper.runR8(builder.build(), options -> {
+      // To trigger outliner, set # of expected outline candidate as threshold.
+      options.outline.threshold = 2;
+      options.enableInlining = false;
+      options.enableMinification = false;
+    });
+    ProcessResult result = runOnArtRaw(app, TestClass.class);
+    assertEquals(0, result.exitCode);
+    assertEquals(javaResult, result.stdout);
+
+    CodeInspector inspector = new CodeInspector(app);
+    for (FoundClassSubject clazz : inspector.allClasses()) {
+      clazz.getDexClass().forEachMethod(encodedMethod -> {
+        Code code = encodedMethod.getCode();
+        if (code != null && !encodedMethod.method.name.toString().startsWith("outline")) {
+          verifyAbsenceOfStringBuilderAppend(code.asDexCode().instructions);
+        }
+      });
+    }
+  }
+
+  private void verifyAbsenceOfStringBuilderAppend(Instruction[] instructions) {
+    for (Instruction instr : instructions) {
+      if (instr instanceof InvokeVirtual) {
+        InvokeVirtual invokeVirtual = (InvokeVirtual) instr;
+        DexMethod invokedMethod = invokeVirtual.getMethod();
+        if (invokedMethod.getHolder().getName().endsWith("StringBuilder")) {
+          assertNotEquals("append", invokedMethod.name.toString());
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 9cf0a85..ff762b7 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -9,7 +9,6 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.CompatProguardCommandBuilder;
-import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
@@ -42,24 +41,42 @@
 import com.android.tools.r8.utils.codeinspector.FieldSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
 import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
 import java.io.File;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
+@RunWith(Parameterized.class)
 public class ForceProguardCompatibilityTest extends TestBase {
+
+  private Backend backend;
+
+  @Parameterized.Parameters(name = "Backend: {0}")
+  public static Collection<Backend> data() {
+    return Arrays.asList(Backend.values());
+  }
+
+  public ForceProguardCompatibilityTest(Backend backend) {
+    this.backend = backend;
+  }
+
   private void test(Class mainClass, Class mentionedClass, boolean forceProguardCompatibility)
       throws Exception {
     String proguardConfig = keepMainProguardConfiguration(mainClass, true, false);
-    CodeInspector inspector = new CodeInspector(
-        compileWithR8(
-            ImmutableList.of(mainClass, mentionedClass),
-            proguardConfig,
-            options -> options.forceProguardCompatibility = forceProguardCompatibility));
+    CodeInspector inspector =
+        new CodeInspector(
+            compileWithR8(
+                readClasses(ImmutableList.of(mainClass, mentionedClass)),
+                proguardConfig,
+                options -> options.forceProguardCompatibility = forceProguardCompatibility,
+                backend));
     assertTrue(inspector.clazz(mainClass.getCanonicalName()).isPresent());
     ClassSubject clazz = inspector.clazz(getJavacGeneratedClassName(mentionedClass));
     assertTrue(clazz.isPresent());
@@ -101,7 +118,7 @@
           Origin.unknown());
     }
 
-    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
     CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
     assertTrue(inspector.clazz(mainClass.getCanonicalName()).isPresent());
     ClassSubject clazz = inspector.clazz(getJavacGeneratedClassName(mentionedClassWithAnnotations));
@@ -136,7 +153,7 @@
     Path proguardCompatibilityRules = temp.newFile().toPath();
     builder.setProguardCompatibilityRulesOutput(proguardCompatibilityRules);
 
-    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
     CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
     ClassSubject clazz = inspector.clazz(getJavacGeneratedClassName(testClass));
     assertTrue(clazz.isPresent());
@@ -193,7 +210,7 @@
         "-dontobfuscate");
     builder.addProguardConfiguration(proguardConfig, Origin.unknown());
 
-    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
     CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
     assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent());
     ClassSubject clazz = inspector.clazz(getJavacGeneratedClassName(instantiatedClass));
@@ -255,7 +272,7 @@
       builder.setProguardMapOutputPath(temp.newFile().toPath());
     }
 
-    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
     CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
     assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent());
     forNameClasses.forEach(clazz -> {
@@ -349,7 +366,7 @@
       builder.setProguardMapOutputPath(temp.newFile().toPath());
     }
 
-    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
     CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
     assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent());
     ClassSubject classSubject = inspector.clazz(getJavacGeneratedClassName(withMemberClass));
@@ -453,7 +470,7 @@
       builder.setProguardMapOutputPath(temp.newFile().toPath());
     }
 
-    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
     CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
     assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent());
     ClassSubject classSubject = inspector.clazz(getJavacGeneratedClassName(withVolatileFields));
@@ -568,7 +585,7 @@
     builder.setProguardCompatibilityRulesOutput(proguardCompatibilityRules);
 
     AndroidApp app;
-    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
     try {
       app = ToolHelper.runR8(builder.build(), o -> o.enableClassInlining = false);
     } catch (CompilationError e) {
@@ -577,7 +594,14 @@
     }
     CodeInspector inspector = new CodeInspector(app);
     assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent());
-    assertEquals(innerClasses || enclosingMethod ? "1" : "0", runOnArt(app, mainClass));
+    String result;
+    if (backend == Backend.DEX) {
+      result = runOnArt(app, mainClass);
+    } else {
+      assert backend == Backend.CF;
+      result = runOnJava(app, mainClass);
+    }
+    assertEquals(innerClasses || enclosingMethod ? "1" : "0", result);
 
     // Check the Proguard compatibility configuration generated.
     ProguardConfigurationParser parser =
@@ -623,8 +647,10 @@
         "-dontobfuscate"),
         Origin.unknown());
     builder.addProguardConfiguration(additionalKeepRules, Origin.unknown());
-    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
-    builder.setMinApiLevel(AndroidApiLevel.O.getLevel());
+    builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
+    if (backend == Backend.DEX) {
+      builder.setMinApiLevel(AndroidApiLevel.O.getLevel());
+    }
     Path proguardCompatibilityRules = temp.newFile().toPath();
     builder.setProguardCompatibilityRulesOutput(proguardCompatibilityRules);
     AndroidApp app = ToolHelper.runR8(builder.build(), o -> o.enableClassInlining = false);