Merge "SCCP: No need to continue phi analysis when one operand is bottom"
diff --git a/build.gradle b/build.gradle
index 2d9e9b4..8237781 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1339,10 +1339,6 @@
 }
 
 task buildPreNJdwpTestsDex(type: Exec, dependsOn: "buildPreNJdwpTestsJar") {
-    onlyIf {
-        // TODO(b/76135355): Update dx.bat on Windows to something that can build this.
-        !OperatingSystem.current().isWindows()
-    }
     def inFile = buildPreNJdwpTestsJar.archivePath
     def outFile = new File(buildPreNJdwpTestsJar.destinationDir, buildPreNJdwpTestsJar.baseName + '-dex.jar')
     inputs.file inFile
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index f525565..3db9699 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -40,9 +40,9 @@
         new ApplicationReader(app, options, timing).read(executor).toDirect();
     AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
     RootSet mainDexRootSet =
-        new RootSetBuilder(application, appInfo, options.mainDexKeepRules, options).run(executor);
+        new RootSetBuilder(appInfo, application, options.mainDexKeepRules, options).run(executor);
     Enqueuer enqueuer = new Enqueuer(appInfo, options, true);
-    AppInfoWithLiveness mainDexAppInfo = enqueuer.traceMainDex(mainDexRootSet, timing);
+    AppInfoWithLiveness mainDexAppInfo = enqueuer.traceMainDex(mainDexRootSet, executor, timing);
     // LiveTypes is the result.
     Set<DexType> mainDexClasses =
         new MainDexListBuilder(new HashSet<>(mainDexAppInfo.liveTypes), application).run();
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d062d13..4f12ce1 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -282,13 +282,13 @@
             ProguardConfiguration.builder(application.dexItemFactory, options.reporter);
 
         rootSet = new RootSetBuilder(
-                    application, appInfo, options.proguardConfiguration.getRules(), options)
+                    appInfo, application, options.proguardConfiguration.getRules(), options)
                 .run(executorService);
         ProtoLiteExtension protoLiteExtension =
             options.forceProguardCompatibility ? null : new ProtoLiteExtension(appInfo);
         Enqueuer enqueuer = new Enqueuer(appInfo, options, options.forceProguardCompatibility,
             compatibility, protoLiteExtension);
-        appInfo = enqueuer.traceApplication(rootSet, timing);
+        appInfo = enqueuer.traceApplication(rootSet, executorService, timing);
         if (options.proguardConfiguration.isPrintSeeds()) {
           ByteArrayOutputStream bytes = new ByteArrayOutputStream();
           PrintStream out = new PrintStream(bytes);
@@ -396,9 +396,10 @@
         Enqueuer enqueuer = new Enqueuer(appInfo, options, true);
         // Lets find classes which may have code executed before secondary dex files installation.
         RootSet mainDexRootSet =
-            new RootSetBuilder(application, appInfo, options.mainDexKeepRules, options)
+            new RootSetBuilder(appInfo, application, options.mainDexKeepRules, options)
                 .run(executorService);
-        AppInfoWithLiveness mainDexAppInfo = enqueuer.traceMainDex(mainDexRootSet, timing);
+        AppInfoWithLiveness mainDexAppInfo =
+            enqueuer.traceMainDex(mainDexRootSet, executorService, timing);
 
         // LiveTypes is the result.
         Set<DexType> mainDexBaseClasses = new HashSet<>(mainDexAppInfo.liveTypes);
@@ -416,7 +417,7 @@
         timing.begin("Post optimization code stripping");
         try {
           Enqueuer enqueuer = new Enqueuer(appInfo, options, options.forceProguardCompatibility);
-          appInfo = enqueuer.traceApplication(rootSet, timing);
+          appInfo = enqueuer.traceApplication(rootSet, executorService, timing);
           if (options.enableTreeShaking) {
             TreePruner pruner = new TreePruner(application, appInfo.withLiveness(), options);
             application = pruner.run();
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 6f062cf..5a0d2c4 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "v1.2.4-dev";
+  public static final String LABEL = "v1.2.6-dev";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 39d916a..181ebac 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -16,6 +16,9 @@
 import com.android.tools.r8.cf.code.CfConstString;
 import com.android.tools.r8.cf.code.CfFieldInstruction;
 import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.Uninitialized;
+import com.android.tools.r8.cf.code.CfFrame.UninitializedNew;
+import com.android.tools.r8.cf.code.CfFrame.UninitializedThis;
 import com.android.tools.r8.cf.code.CfGoto;
 import com.android.tools.r8.cf.code.CfIf;
 import com.android.tools.r8.cf.code.CfIfCmp;
@@ -240,7 +243,17 @@
     StringBuilder builder = new StringBuilder("frame: [");
     String separator = "";
     for (Entry<DexType> entry : frame.getLocals().int2ReferenceEntrySet()) {
-      builder.append(separator).append(entry.getIntKey()).append(':').append(entry.getValue());
+      builder.append(separator).append(entry.getIntKey()).append(':');
+      Uninitialized allocator = frame.getAllocators().get(entry.getIntKey());
+      if (allocator == null) {
+        builder.append(entry.getValue());
+      } else if (allocator instanceof UninitializedThis) {
+        builder.append("uninitialized this");
+      } else {
+        builder
+            .append("uninitialized ")
+            .append(getLabel(((UninitializedNew) allocator).getLabel()));
+      }
       separator = ", ";
     }
     builder.append("] ");
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index e30d8ce..130362f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -16,10 +16,13 @@
 
   private final int opcode;
   private final DexField field;
+  private final DexField declaringField;
 
-  public CfFieldInstruction(int opcode, DexField field) {
+  public CfFieldInstruction(int opcode, DexField field, DexField declaringField) {
     this.opcode = opcode;
     this.field = field;
+    this.declaringField = declaringField;
+    assert field.type == declaringField.type;
   }
 
   public DexField getField() {
@@ -33,7 +36,7 @@
   @Override
   public void write(MethodVisitor visitor, NamingLens lens) {
     String owner = lens.lookupInternalName(field.getHolder());
-    String name = lens.lookupName(field).toString();
+    String name = lens.lookupName(declaringField).toString();
     String desc = lens.lookupDescriptor(field.type).toString();
     visitor.visitFieldInsn(opcode, owner, name, desc);
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index 2818e44..b5e759c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -17,11 +17,44 @@
 
 public class CfFrame extends CfInstruction {
 
+  public abstract static class Uninitialized {
+    abstract Object getAsmLabel();
+  }
+
+  public static class UninitializedNew extends Uninitialized {
+    private final CfLabel label;
+
+    public UninitializedNew(CfLabel label) {
+      this.label = label;
+    }
+
+    @Override
+    Object getAsmLabel() {
+      return label.getLabel();
+    }
+
+    public CfLabel getLabel() {
+      return label;
+    }
+  }
+
+  public static class UninitializedThis extends Uninitialized {
+    @Override
+    Object getAsmLabel() {
+      return Opcodes.UNINITIALIZED_THIS;
+    }
+  }
+
   private final Int2ReferenceSortedMap<DexType> locals;
+  private final Int2ReferenceSortedMap<Uninitialized> allocators;
   private final List<DexType> stack;
 
-  public CfFrame(Int2ReferenceSortedMap<DexType> locals, List<DexType> stack) {
+  public CfFrame(
+      Int2ReferenceSortedMap<DexType> locals,
+      Int2ReferenceSortedMap<Uninitialized> allocators,
+      List<DexType> stack) {
     this.locals = locals;
+    this.allocators = allocators;
     this.stack = stack;
   }
 
@@ -29,6 +62,10 @@
     return locals;
   }
 
+  public Int2ReferenceSortedMap<Uninitialized> getAllocators() {
+    return allocators;
+  }
+
   public List<DexType> getStack() {
     return stack;
   }
@@ -88,7 +125,8 @@
     int localIndex = 0;
     for (int i = 0; i <= maxRegister; i++) {
       DexType type = locals.get(i);
-      Object typeOpcode = getType(type, lens);
+      Uninitialized allocator = allocators.get(i);
+      Object typeOpcode = allocator == null ? getType(type, lens) : allocator.getAsmLabel();
       localsTypes[localIndex++] = typeOpcode;
       if (type != null && isWide(type)) {
         i++;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index ca8f7cb..f830427 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -4,9 +4,11 @@
 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.errors.Unreachable;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueDouble;
@@ -41,11 +43,16 @@
       bsmArgs[i] = decodeBootstrapArgument(bootstrapArgs.get(i), lens);
     }
     Handle bsmHandle = bootstrapMethod.toAsmHandle(lens);
+    DexString methodName;
+    if (lens.isIdentityLens()) {
+      methodName = callSite.methodName;
+    } else if (!callSite.interfaceMethods.isEmpty()) {
+      methodName = lens.lookupName(callSite.interfaceMethods.get(0));
+    } else {
+      throw new Unimplemented("Minification of non-lambda InvokeDynamic not supported");
+    }
     visitor.visitInvokeDynamicInsn(
-        callSite.methodName.toString(),
-        callSite.methodProto.toDescriptorString(),
-        bsmHandle,
-        bsmArgs);
+        methodName.toString(), callSite.methodProto.toDescriptorString(lens), bsmHandle, bsmArgs);
   }
 
   private Object decodeBootstrapArgument(DexValue dexValue, NamingLens lens) {
@@ -60,9 +67,9 @@
     } else if (dexValue instanceof DexValueString) {
       return ((DexValueString) dexValue).getValue();
     } else if (dexValue instanceof DexValueType) {
-      return Type.getType(((DexValueType) dexValue).value.toDescriptorString());
+      return Type.getType(lens.lookupDescriptor(((DexValueType) dexValue).value).toString());
     } else if (dexValue instanceof DexValueMethodType) {
-      return Type.getMethodType(((DexValueMethodType) dexValue).value.toDescriptorString());
+      return Type.getMethodType(((DexValueMethodType) dexValue).value.toDescriptorString(lens));
     } else if (dexValue instanceof DexValueMethodHandle) {
       return ((DexValueMethodHandle) dexValue).value.toAsmHandle(lens);
     } else {
diff --git a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
index 45a62ef..e3753a7 100644
--- a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
+++ b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException;
 import com.android.tools.r8.utils.OptionsParsing;
 import com.android.tools.r8.utils.OptionsParsing.ParseContext;
+import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -27,40 +28,80 @@
   private static final boolean PRINT_ARGS = false;
 
   public static class Options {
-    List<String> inputArchives = new ArrayList<>();
-    List<String> featureJars = new ArrayList<>();
-    String splitBaseName = DEFAULT_OUTPUT_ARCHIVE_FILENAME;
-    String featureSplitMapping;
-    String proguardMap;
+    private List<String> inputArchives = new ArrayList<>();
+    private List<String> featureJars = new ArrayList<>();
+    private String splitBaseName = DEFAULT_OUTPUT_ARCHIVE_FILENAME;
+    private String featureSplitMapping;
+    private String proguardMap;
+
+    public String getSplitBaseName() {
+      return splitBaseName;
+    }
+
+    public void setSplitBaseName(String splitBaseName) {
+      this.splitBaseName = splitBaseName;
+    }
+
+    public String getFeatureSplitMapping() {
+      return featureSplitMapping;
+    }
+
+    public void setFeatureSplitMapping(String featureSplitMapping) {
+      this.featureSplitMapping = featureSplitMapping;
+    }
+
+    public String getProguardMap() {
+      return proguardMap;
+    }
+
+    public void setProguardMap(String proguardMap) {
+      this.proguardMap = proguardMap;
+    }
+
+    public void addInputArchive(String inputArchive) {
+      inputArchives.add(inputArchive);
+    }
+
+    public void addFeatureJar(String featureJar) {
+      featureJars.add(featureJar);
+    }
+
+    public ImmutableList<String> getInputArchives() {
+      return ImmutableList.copyOf(inputArchives);
+    }
+
+    public ImmutableList<String> getFeatureJars() {
+      return ImmutableList.copyOf(featureJars);
+    }
   }
 
   private static Options parseArguments(String[] args) throws IOException {
     Options options = new Options();
     ParseContext context = new ParseContext(args);
     while (context.head() != null) {
-      List<String> input = OptionsParsing.tryParseMulti(context, "--input");
-      if (input != null) {
-        options.inputArchives.addAll(input);
+      List<String> inputs = OptionsParsing.tryParseMulti(context, "--input");
+      if (inputs != null) {
+        inputs.stream().forEach(options::addInputArchive);
         continue;
       }
       List<String> featureJars = OptionsParsing.tryParseMulti(context, "--feature-jar");
       if (featureJars != null) {
-        options.featureJars.addAll(featureJars);
+        featureJars.stream().forEach(options::addFeatureJar);
         continue;
       }
       String output = OptionsParsing.tryParseSingle(context, "--output", "-o");
       if (output != null) {
-        options.splitBaseName = output;
+        options.setSplitBaseName(output);
         continue;
       }
       String proguardMap = OptionsParsing.tryParseSingle(context, "--proguard-map", null);
       if (proguardMap != null) {
-        options.proguardMap = proguardMap;
+        options.setProguardMap(proguardMap);
         continue;
       }
       String featureSplit = OptionsParsing.tryParseSingle(context, "--feature-splits", null);
       if (featureSplit != null) {
-        options.featureSplitMapping = featureSplit;
+        options.setFeatureSplitMapping(featureSplit);
         continue;
       }
       throw new RuntimeException(String.format("Unknown options: '%s'.", context.head()));
@@ -70,8 +111,8 @@
 
   private static FeatureClassMapping createFeatureClassMapping(Options options)
       throws IOException, FeatureMappingException, ResourceException {
-    if (options.featureSplitMapping != null) {
-      return FeatureClassMapping.fromSpecification(Paths.get(options.featureSplitMapping));
+    if (options.getFeatureSplitMapping() != null) {
+      return FeatureClassMapping.fromSpecification(Paths.get(options.getFeatureSplitMapping()));
     }
     assert !options.featureJars.isEmpty();
     return FeatureClassMapping.fromJarFiles(options.featureJars);
@@ -87,13 +128,13 @@
   public static void run(Options options)
       throws IOException, FeatureMappingException, ResourceException, CompilationException,
       ExecutionException, CompilationFailedException {
-    if (options.inputArchives.isEmpty()) {
+    if (options.getInputArchives().isEmpty()) {
       throw new RuntimeException("Need at least one --input");
     }
-    if (options.featureSplitMapping == null && options.featureJars.isEmpty()) {
+    if (options.getFeatureSplitMapping() == null && options.getFeatureJars().isEmpty()) {
       throw new RuntimeException("You must supply a feature split mapping or feature jars");
     }
-    if (options.featureSplitMapping != null && !options.featureJars.isEmpty()) {
+    if (options.getFeatureSplitMapping() != null && !options.getFeatureJars().isEmpty()) {
       throw new RuntimeException("You can't supply both a feature split mapping and feature jars");
     }
 
@@ -108,7 +149,7 @@
     FeatureClassMapping featureClassMapping = createFeatureClassMapping(options);
 
     DexSplitterHelper.run(
-        builder.build(), featureClassMapping, options.splitBaseName, options.proguardMap);
+        builder.build(), featureClassMapping, options.getSplitBaseName(), options.getProguardMap());
   }
 
   public static void main(String[] args) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index 0c32750..ac954f8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -29,19 +29,27 @@
   public final DexMethodHandle bootstrapMethod;
   public final List<DexValue> bootstrapArgs;
 
+  public final List<DexMethod> interfaceMethods;
+
   private DexEncodedArray encodedArray = null;
 
-  DexCallSite(DexString methodName, DexProto methodProto,
-      DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
+  DexCallSite(
+      DexString methodName,
+      DexProto methodProto,
+      DexMethodHandle bootstrapMethod,
+      List<DexValue> bootstrapArgs,
+      List<DexMethod> interfaceMethods) {
     assert methodName != null;
     assert methodProto != null;
     assert bootstrapMethod != null;
     assert bootstrapArgs != null;
+    assert interfaceMethods != null;
 
     this.methodName = methodName;
     this.methodProto = methodProto;
     this.bootstrapMethod = bootstrapMethod;
     this.bootstrapArgs = bootstrapArgs;
+    this.interfaceMethods = interfaceMethods;
   }
 
   public static DexCallSite fromAsmInvokeDynamic(
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 9d3c7a9..5fe9134 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -15,7 +15,9 @@
 import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame;
 import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd;
 import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexValue.DexValueMethodType;
 import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.naming.NamingLens;
 import com.google.common.base.Strings;
@@ -25,6 +27,7 @@
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.List;
@@ -227,6 +230,33 @@
   public final DexType annotationSynthesizedClassMap =
       createType("Lcom/android/tools/r8/annotations/SynthesizedClassMap;");
 
+  private static final String METAFACTORY_METHOD_NAME = "metafactory";
+  private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
+
+  public final DexType metafactoryType = createType("Ljava/lang/invoke/LambdaMetafactory;");
+  public final DexType callSiteType = createType("Ljava/lang/invoke/CallSite;");
+  public final DexType lookupType = createType("Ljava/lang/invoke/MethodHandles$Lookup;");
+  public final DexType serializableType = createType("Ljava/io/Serializable;");
+
+  public final DexMethod metafactoryMethod =
+      createMethod(
+          metafactoryType,
+          createProto(
+              callSiteType,
+              lookupType,
+              stringType,
+              methodTypeType,
+              methodTypeType,
+              methodHandleType,
+              methodTypeType),
+          createString(METAFACTORY_METHOD_NAME));
+
+  public final DexMethod metafactoryAltMethod =
+      createMethod(
+          metafactoryType,
+          createProto(callSiteType, lookupType, stringType, methodTypeType, objectArrayType),
+          createString(METAFACTORY_ALT_METHOD_NAME));
+
   private boolean skipNameValidationForTesting = false;
 
   public void setSkipNameValidationForTesting(boolean skipNameValidationForTesting) {
@@ -544,10 +574,46 @@
       DexString methodName, DexProto methodProto,
       DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
     assert !sorted;
-    DexCallSite callSite = new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs);
+    List<DexMethod> interfaceMethods =
+        getCallSiteInterfaceMethods(methodName, methodProto, bootstrapMethod, bootstrapArgs);
+    DexCallSite callSite =
+        new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs, interfaceMethods);
     return canonicalize(callSites, callSite);
   }
 
+  private List<DexMethod> getCallSiteInterfaceMethods(
+      DexString methodName,
+      DexProto methodProto,
+      DexMethodHandle bootstrapMethodHandle,
+      List<DexValue> bootstrapArgs) {
+    // TODO(mathiasr): Unify this with LambdaDescriptor.infer().
+    if (!bootstrapMethodHandle.type.isInvokeStatic()) {
+      return Collections.emptyList();
+    }
+    DexMethod bootstrapMethod = bootstrapMethodHandle.asMethod();
+    if (bootstrapMethod != metafactoryMethod && bootstrapMethod != metafactoryAltMethod) {
+      return Collections.emptyList();
+    }
+    DexType interfaceType = methodProto.returnType;
+    assert bootstrapMethod == metafactoryAltMethod || bootstrapArgs.size() == 3;
+    // Signature of main functional interface method.
+    // In Java docs, this argument is named 'samMethodType'.
+    DexValueMethodType funcErasedSignature = (DexValueMethodType) bootstrapArgs.get(0);
+    DexMethod mainMethod = createMethod(interfaceType, funcErasedSignature.value, methodName);
+    if (bootstrapMethod == metafactoryAltMethod) {
+      List<DexMethod> result = new ArrayList<>();
+      result.add(mainMethod);
+      LambdaDescriptor.extractAltMetafactory(
+          this,
+          bootstrapArgs,
+          type -> result.add(createMethod(type, funcErasedSignature.value, methodName)),
+          bridge -> {});
+      return result;
+    } else {
+      return Collections.singletonList(mainMethod);
+    }
+  }
+
   public DexMethod createMethod(DexString clazzDescriptor, DexString name,
       DexString returnTypeDescriptor,
       DexString[] parameterDescriptors) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index c0c2e27..9c3e9a5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -71,6 +71,14 @@
     return false;
   }
 
+  /**
+   * Returns true if the other method has the same name and prototype (including signature and
+   * return type), false otherwise.
+   */
+  public boolean hasSameProtoAndName(DexMethod other) {
+    return name == other.name && proto == other.proto;
+  }
+
   @Override
   public int compareTo(DexMethod other) {
     return sortedCompareTo(other.getSortedIndex());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index a7bce9d..660a713 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -153,7 +153,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfFieldInstruction(Opcodes.GETFIELD, field));
+    builder.add(new CfFieldInstruction(Opcodes.GETFIELD, field, builder.resolveField(field)));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index ec92b67..9591978 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -136,7 +136,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfFieldInstruction(Opcodes.PUTFIELD, field));
+    builder.add(new CfFieldInstruction(Opcodes.PUTFIELD, field, builder.resolveField(field)));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 47141bc..6db3b8d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -131,7 +131,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfFieldInstruction(Opcodes.GETSTATIC, field));
+    builder.add(new CfFieldInstruction(Opcodes.GETSTATIC, field, builder.resolveField(field)));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 0eca18d..0c78208 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -130,7 +130,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfFieldInstruction(Opcodes.PUTSTATIC, field));
+    builder.add(new CfFieldInstruction(Opcodes.PUTSTATIC, field, builder.resolveField(field)));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 933f66d..8dd9162 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -7,6 +7,9 @@
 import com.android.tools.r8.cf.LoadStoreHelper;
 import com.android.tools.r8.cf.TypeVerificationHelper;
 import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.Uninitialized;
+import com.android.tools.r8.cf.code.CfFrame.UninitializedNew;
+import com.android.tools.r8.cf.code.CfFrame.UninitializedThis;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfLabel;
 import com.android.tools.r8.cf.code.CfPosition;
@@ -16,7 +19,9 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
 import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.Argument;
@@ -26,8 +31,10 @@
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionIterator;
 import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.JumpInstruction;
 import com.android.tools.r8.ir.code.Load;
+import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.StackValue;
 import com.android.tools.r8.ir.code.Store;
@@ -40,9 +47,11 @@
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -73,6 +82,12 @@
   private final Int2ReferenceMap<LocalVariableInfo> openLocalVariables =
       new Int2ReferenceOpenHashMap<>();
 
+  private AppInfoWithSubtyping appInfo;
+
+  private Map<NewInstance, List<InvokeDirect>> initializers;
+  private List<InvokeDirect> thisInitializers;
+  private Map<NewInstance, CfLabel> newInstanceLabels;
+
   // Internal abstraction of the stack values and height.
   private static class Stack {
     int maxHeight = 0;
@@ -106,6 +121,7 @@
 
   public CfCode build(
       CodeRewriter rewriter, InternalOptions options, AppInfoWithSubtyping appInfo) {
+    computeInitializers();
     types = new TypeVerificationHelper(code, factory, appInfo).computeVerificationTypes();
     splitExceptionalBlocks();
     LoadStoreHelper loadStoreHelper = new LoadStoreHelper(code, types);
@@ -119,10 +135,49 @@
     int instructionTableCount =
         DexBuilder.instructionNumberToIndex(code.numberRemainingInstructions());
     DexBuilder.removeRedundantDebugPositions(code, instructionTableCount);
+    this.appInfo = appInfo;
     CfCode code = buildCfCode();
     return code;
   }
 
+  public DexField resolveField(DexField field) {
+    DexEncodedField resolvedField = appInfo.resolveFieldOn(field.clazz, field);
+    return resolvedField == null ? field : resolvedField.field;
+  }
+
+  private void computeInitializers() {
+    assert initializers == null;
+    assert thisInitializers == null;
+    initializers = new HashMap<>();
+    for (BasicBlock block : code.blocks) {
+      for (Instruction insn : block.getInstructions()) {
+        if (insn.isNewInstance()) {
+          initializers.put(insn.asNewInstance(), computeInitializers(insn.outValue()));
+        } else if (insn.isArgument() && method.isInstanceInitializer()) {
+          if (insn.outValue().isThis()) {
+            // By JVM8 §4.10.1.9 (invokespecial), a this() or super() call in a constructor
+            // changes the type of `this` from uninitializedThis
+            // to the type of the class of the <init> method.
+            thisInitializers = computeInitializers(insn.outValue());
+          }
+        }
+      }
+    }
+    assert !(method.isInstanceInitializer() && thisInitializers == null);
+  }
+
+  private List<InvokeDirect> computeInitializers(Value value) {
+    List<InvokeDirect> initializers = new ArrayList<>();
+    for (Instruction user : value.uniqueUsers()) {
+      if (user instanceof InvokeDirect
+          && user.inValues().get(0) == value
+          && user.asInvokeDirect().getInvokedMethod().name == factory.constructorMethodName) {
+        initializers.add(user.asInvokeDirect());
+      }
+    }
+    return initializers;
+  }
+
   // Split all blocks with throwing instructions and exceptional edges such that any non-throwing
   // instructions that might define values prior to the throwing exception are excluded from the
   // try-catch range. Failure to do so will result in code that does not verify on the JVM.
@@ -193,6 +248,7 @@
     List<CfTryCatch> tryCatchRanges = new ArrayList<>();
     labels = new HashMap<>(code.blocks.size());
     emittedLabels = new HashSet<>(code.blocks.size());
+    newInstanceLabels = new HashMap<>(initializers.size());
     instructions = new ArrayList<>();
     ListIterator<BasicBlock> blockIterator = code.listIterator();
     BasicBlock block = blockIterator.next();
@@ -201,6 +257,7 @@
     BasicBlock pendingFrame = null;
     boolean previousFallthrough = false;
     do {
+      assert stack.isEmpty();
       CatchHandlers<BasicBlock> handlers = block.getCatchHandlers();
       if (!tryCatchHandlers.equals(handlers)) {
         if (!tryCatchHandlers.isEmpty()) {
@@ -220,7 +277,6 @@
       // If previousBlock is fallthrough, then it is counted in getPredecessors().size(), but
       // we only want to set a pendingFrame if we have a predecessor which is not previousBlock.
       if (block.getPredecessors().size() > (previousFallthrough ? 1 : 0)) {
-        assert stack.isEmpty();
         pendingFrame = block;
         emitLabel(getLabel(block));
       }
@@ -308,6 +364,9 @@
           pendingLocalChanges = true;
         }
       } else {
+        if (instruction.isNewInstance()) {
+          newInstanceLabels.put(instruction.asNewInstance(), ensureLabel());
+        }
         updatePositionAndLocals(instruction);
         instruction.buildCf(this);
       }
@@ -390,9 +449,11 @@
 
     Collection<Value> locals = registerAllocator.getLocalsAtBlockEntry(block);
     Int2ReferenceSortedMap<DexType> mapping = new Int2ReferenceAVLTreeMap<>();
+    Int2ReferenceSortedMap<Uninitialized> allocators = new Int2ReferenceAVLTreeMap<>();
 
     for (Value local : locals) {
       DexType type;
+      Uninitialized allocator = null;
       switch (local.outType()) {
         case INT:
           type = factory.intType;
@@ -408,14 +469,64 @@
           break;
         case OBJECT:
           type = types.get(local);
+          allocator = findAllocator(block, local);
           break;
         default:
           throw new Unreachable(
               "Unexpected local type: " + local.outType() + " for local: " + local);
       }
       mapping.put(getLocalRegister(local), type);
+      if (allocator != null) {
+        allocators.put(getLocalRegister(local), allocator);
+      }
     }
-    instructions.add(new CfFrame(mapping, stackTypes));
+    instructions.add(new CfFrame(mapping, allocators, stackTypes));
+  }
+
+  private Uninitialized findAllocator(BasicBlock liveBlock, Value value) {
+    Instruction definition = value.definition;
+    while (definition != null && (definition.isStore() || definition.isLoad())) {
+      definition = definition.inValues().get(0).definition;
+    }
+    if (definition == null) {
+      return null;
+    }
+    Uninitialized res;
+    if (definition.isNewInstance()) {
+      res = new UninitializedNew(newInstanceLabels.get(definition.asNewInstance()));
+    } else if (definition.isArgument()
+        && method.isInstanceInitializer()
+        && definition.outValue().isThis()) {
+      res = new UninitializedThis();
+    } else {
+      return null;
+    }
+    BasicBlock definitionBlock = definition.getBlock();
+    Set<BasicBlock> visited = new HashSet<>();
+    Deque<BasicBlock> toVisit = new ArrayDeque<>();
+    List<InvokeDirect> valueInitializers =
+        definition.isArgument() ? thisInitializers : initializers.get(definition.asNewInstance());
+    for (InvokeDirect initializer : valueInitializers) {
+      BasicBlock initializerBlock = initializer.getBlock();
+      if (initializerBlock == liveBlock) {
+        return res;
+      }
+      if (initializerBlock != definitionBlock && visited.add(initializerBlock)) {
+        toVisit.addLast(initializerBlock);
+      }
+    }
+    while (!toVisit.isEmpty()) {
+      BasicBlock block = toVisit.removeLast();
+      for (BasicBlock predecessor : block.getPredecessors()) {
+        if (predecessor == liveBlock) {
+          return res;
+        }
+        if (predecessor != definitionBlock && visited.add(predecessor)) {
+          toVisit.addLast(predecessor);
+        }
+      }
+    }
+    return null;
   }
 
   private void emitLabel(CfLabel label) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 228f594..9c6bb86 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -172,7 +172,9 @@
   }
 
   @Override
-  public void buildInstruction(IRBuilder builder, int instructionIndex) throws ApiLevelException {
+  public void buildInstruction(
+      IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
+      throws ApiLevelException {
     updateCurrentCatchHandlers(instructionIndex);
     updateDebugPosition(instructionIndex, builder);
     currentDexInstruction = code.instructions[instructionIndex];
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 5bea64e..13d66c2 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
@@ -504,7 +504,7 @@
           addToWorklist(info.block, i);
           break;
         }
-        source.buildInstruction(this, i);
+        source.buildInstruction(this, i, i == item.firstInstructionIndex);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 2298bc6..7f89bdc 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -489,7 +489,9 @@
   }
 
   @Override
-  public void buildInstruction(IRBuilder builder, int instructionIndex) throws ApiLevelException {
+  public void buildInstruction(
+      IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
+      throws ApiLevelException {
     if (instructionIndex == EXCEPTIONAL_SYNC_EXIT_OFFSET) {
       buildExceptionalPostlude(builder);
       return;
@@ -499,8 +501,17 @@
     assert verifyExceptionEdgesAreRecorded(insn);
 
     // If a new block is starting here, we restore the computed JarState.
-    if (builder.getCFG().containsKey(instructionIndex) || instructionIndex == 0) {
+    // current position needs to be compute only for the first instruction of a block, thereafter
+    // current position will be updated by LineNumberNode into this block.
+    if (firstBlockInstruction || instructionIndex == 0) {
       state.restoreState(instructionIndex);
+      // Don't include line changes when processing a label. Doing so will end up emitting local
+      // writes after the line has changed and thus causing locals to become visible too late.
+      currentPosition =
+          getDebugPositionAtOffset(
+              ((instructionIndex > 0) && (insn instanceof LabelNode))
+                  ? instructionIndex - 1
+                  : instructionIndex);
     }
 
     String preInstructionState;
@@ -508,14 +519,6 @@
       preInstructionState = state.toString();
     }
 
-    // Don't include line changes when processing a label. Doing so will end up emitting local
-    // writes after the line has changed and thus causing locals to become visible too late.
-    currentPosition =
-        getDebugPositionAtOffset(
-            ((instructionIndex > 0) && (insn instanceof LabelNode))
-                ? instructionIndex - 1
-                : instructionIndex);
-
     build(insn, builder);
 
     if (Log.ENABLED && !(insn instanceof LineNumberNode)) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
index 13d1551..be1c670 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
@@ -47,7 +47,9 @@
 
   // Delegates for IR building.
   void buildPrelude(IRBuilder builder);
-  void buildInstruction(IRBuilder builder, int instructionIndex) throws ApiLevelException;
+
+  void buildInstruction(IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
+      throws ApiLevelException;
   void buildPostlude(IRBuilder builder);
 
   // Helper to resolve switch payloads and build switch instructions (dex code only).
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index c752fb5..2152364 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -32,8 +32,6 @@
   private final Set<DexClass> processedClasses = Sets.newIdentityHashSet();
   // Maps already created methods into default methods they were generated based on.
   private final Map<DexEncodedMethod, DexEncodedMethod> createdMethods = new IdentityHashMap<>();
-  // Caches default interface method info for already processed interfaces.
-  private final Map<DexType, DefaultMethodsHelper.Collection> cache = new IdentityHashMap<>();
 
   ClassProcessor(InterfaceMethodRewriter rewriter) {
     this.rewriter = rewriter;
@@ -67,7 +65,7 @@
     if (superClass != null && superType != rewriter.factory.objectType) {
       if (superClass.isInterface()) {
         throw new CompilationError("Interface `" + superClass.toSourceString()
-        + "` used as super class of `" + clazz.toSourceString() + "`.");
+            + "` used as super class of `" + clazz.toSourceString() + "`.");
       }
       process(superClass);
     }
@@ -136,7 +134,7 @@
     // the future.
     while (current.type != rewriter.factory.objectType) {
       for (DexType type : current.interfaces.values) {
-        helper.merge(getOrCreateInterfaceInfo(clazz, current, type));
+        helper.merge(rewriter.getOrCreateInterfaceInfo(clazz, current, type));
       }
 
       accumulatedVirtualMethods.addAll(Arrays.asList(clazz.virtualMethods()));
@@ -168,8 +166,8 @@
         } else {
           String message = "Default method desugaring of `" + clazz.toSourceString() + "` failed";
           if (current == clazz) {
-            message += " because its super class `" + clazz.superType.toSourceString()
-            + "` is missing";
+            message += " because its super class `" +
+                clazz.superType.toSourceString() + "` is missing";
           } else {
             message +=
                 " because it's hierarchy is incomplete. The class `"
@@ -244,62 +242,4 @@
       }
     }
   }
-
-  private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(
-      DexClass classToDesugar,
-      DexClass implementing,
-      DexType iface) {
-    DefaultMethodsHelper.Collection collection = cache.get(iface);
-    if (collection != null) {
-      return collection;
-    }
-    collection = createInterfaceInfo(classToDesugar, implementing, iface);
-    cache.put(iface, collection);
-    return collection;
-  }
-
-  private DefaultMethodsHelper.Collection createInterfaceInfo(
-      DexClass classToDesugar,
-      DexClass implementing,
-      DexType iface) {
-    DefaultMethodsHelper helper = new DefaultMethodsHelper();
-    DexClass definedInterface = rewriter.findDefinitionFor(iface);
-    if (definedInterface == null) {
-      rewriter.warnMissingInterface(classToDesugar, implementing, iface);
-      return helper.wrapInCollection();
-    }
-
-    if (!definedInterface.isInterface()) {
-      throw new CompilationError(
-          "Type " + iface.toSourceString() + " is referenced as an interface of `"
-          + implementing.toString() + "`.");
-    }
-
-    if (definedInterface.isLibraryClass()) {
-      // NOTE: We intentionally ignore all candidates coming from android.jar
-      // since it is only possible in case v24+ version of android.jar is provided.
-      // WARNING: This may result in incorrect code if something else than Android bootclasspath
-      // classes are given as libraries!
-      return helper.wrapInCollection();
-    }
-
-    // Merge information from all superinterfaces.
-    for (DexType superinterface : definedInterface.interfaces.values) {
-      helper.merge(getOrCreateInterfaceInfo(classToDesugar, definedInterface, superinterface));
-    }
-
-    // Hide by virtual methods of this interface.
-    for (DexEncodedMethod virtual : definedInterface.virtualMethods()) {
-      helper.hideMatches(virtual.method);
-    }
-
-    // Add all default methods of this interface.
-    for (DexEncodedMethod encoded : definedInterface.virtualMethods()) {
-      if (rewriter.isDefaultMethod(encoded)) {
-        helper.addDefaultMethod(encoded);
-      }
-    }
-
-    return helper.wrapInCollection();
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
index df65e7e..fb051aa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
@@ -40,6 +40,22 @@
       this.live = live;
       this.hidden = hidden;
     }
+
+    // If there is just one live method having specified
+    // signature return it, otherwise return null.
+    DexMethod getSingleCandidate(DexMethod method) {
+      DexMethod candidate = null;
+      for (DexEncodedMethod encodedMethod : live) {
+        DexMethod current = encodedMethod.method;
+        if (current.proto == method.proto && current.name == method.name) {
+          if (candidate != null) {
+            return null;
+          }
+          candidate = current;
+        }
+      }
+      return candidate;
+    }
   }
 
   final void merge(Collection collection) {
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 9379a83..9b8b8e2 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
@@ -26,6 +26,7 @@
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeSuper;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.desugar.DefaultMethodsHelper.Collection;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -33,6 +34,7 @@
 import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 //
 // Default and static interface method desugaring rewriter (note that lambda
@@ -75,6 +77,9 @@
   // to this collection since it is only filled in ClassProcessor running synchronously.
   private final Set<DexEncodedMethod> forwardingMethods = Sets.newIdentityHashSet();
 
+  // Caches default interface method info for already processed interfaces.
+  private final Map<DexType, DefaultMethodsHelper.Collection> cache = new ConcurrentHashMap<>();
+
   /**
    * A set of dexitems we have reported missing to dedupe warnings.
    */
@@ -166,8 +171,10 @@
             // of android.jar is provided.
             // WARNING: This may result in incorrect code on older platforms!
             // Retarget call to an appropriate method of companion class.
+            DexMethod amendedMethod = amendDefaultMethod(
+                findDefinitionFor(encodedMethod.method.holder), method);
             instructions.replaceCurrentInstruction(
-                new InvokeStatic(defaultAsMethodOfCompanionClass(method),
+                new InvokeStatic(defaultAsMethodOfCompanionClass(amendedMethod),
                     invokeSuper.outValue(), invokeSuper.arguments()));
           }
           continue;
@@ -294,6 +301,15 @@
         factory.createString(prefix + method.name.toString()));
   }
 
+  // It is possible that referenced method actually points to an interface which does
+  // not define this default methods, but inherits it. We are making our best effort
+  // to find an appropriate method, but still use the original one in case we fail.
+  private DexMethod amendDefaultMethod(DexClass classToDesugar, DexMethod method) {
+    DexMethod singleCandidate = getOrCreateInterfaceInfo(
+        classToDesugar, classToDesugar, method.holder).getSingleCandidate(method);
+    return singleCandidate != null ? singleCandidate : method;
+  }
+
   // Represent a default interface method as a method of companion class.
   final DexMethod defaultAsMethodOfCompanionClass(DexMethod method) {
     return instanceAsMethodOfCompanionClass(method, DEFAULT_METHOD_PREFIX);
@@ -329,6 +345,14 @@
     for (DexEncodedMethod method : forwardingMethods) {
       converter.optimizeSynthesizedMethod(method);
     }
+
+    // Cached data is not needed any more.
+    clear();
+  }
+
+  private void clear() {
+    this.cache.clear();
+    this.forwardingMethods.clear();
   }
 
   private static boolean shouldProcess(
@@ -426,4 +450,62 @@
     DexClass clazz = converter.appInfo.definitionFor(holder);
     return clazz == null ? Origin.unknown() : clazz.getOrigin();
   }
+
+  final DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(
+      DexClass classToDesugar,
+      DexClass implementing,
+      DexType iface) {
+    DefaultMethodsHelper.Collection collection = cache.get(iface);
+    if (collection != null) {
+      return collection;
+    }
+    collection = createInterfaceInfo(classToDesugar, implementing, iface);
+    Collection existing = cache.putIfAbsent(iface, collection);
+    return existing != null ? existing : collection;
+  }
+
+  private DefaultMethodsHelper.Collection createInterfaceInfo(
+      DexClass classToDesugar,
+      DexClass implementing,
+      DexType iface) {
+    DefaultMethodsHelper helper = new DefaultMethodsHelper();
+    DexClass definedInterface = findDefinitionFor(iface);
+    if (definedInterface == null) {
+      warnMissingInterface(classToDesugar, implementing, iface);
+      return helper.wrapInCollection();
+    }
+
+    if (!definedInterface.isInterface()) {
+      throw new CompilationError(
+          "Type " + iface.toSourceString() + " is referenced as an interface from `"
+              + implementing.toString() + "`.");
+    }
+
+    if (definedInterface.isLibraryClass()) {
+      // NOTE: We intentionally ignore all candidates coming from android.jar
+      // since it is only possible in case v24+ version of android.jar is provided.
+      // WARNING: This may result in incorrect code if something else than Android bootclasspath
+      // classes are given as libraries!
+      return helper.wrapInCollection();
+    }
+
+    // Merge information from all superinterfaces.
+    for (DexType superinterface : definedInterface.interfaces.values) {
+      helper.merge(getOrCreateInterfaceInfo(classToDesugar, definedInterface, superinterface));
+    }
+
+    // Hide by virtual methods of this interface.
+    for (DexEncodedMethod virtual : definedInterface.virtualMethods()) {
+      helper.hideMatches(virtual.method);
+    }
+
+    // Add all default methods of this interface.
+    for (DexEncodedMethod encoded : definedInterface.virtualMethods()) {
+      if (isDefaultMethod(encoded)) {
+        helper.addDefaultMethod(encoded);
+      }
+    }
+
+    return helper.wrapInCollection();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index 5d52523..49a797a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -21,9 +21,10 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Consumer;
 
 // Represents the lambda descriptor inferred from calls site.
-final class LambdaDescriptor {
+public final class LambdaDescriptor {
   private static final int LAMBDA_ALT_SERIALIZABLE = 1;
   private static final int LAMBDA_ALT_HAS_EXTRA_INTERFACES = 2;
   private static final int LAMBDA_ALT_HAS_BRIDGES = 4;
@@ -227,8 +228,8 @@
     }
 
     DexMethod bootstrapMethod = callSite.bootstrapMethod.asMethod();
-    boolean isMetafactoryMethod = bootstrapMethod == rewriter.metafactoryMethod;
-    boolean isAltMetafactoryMethod = bootstrapMethod == rewriter.metafactoryAltMethod;
+    boolean isMetafactoryMethod = bootstrapMethod == rewriter.factory.metafactoryMethod;
+    boolean isAltMetafactoryMethod = bootstrapMethod == rewriter.factory.metafactoryAltMethod;
     if (!isMetafactoryMethod && !isAltMetafactoryMethod) {
       // It is not a lambda, thus no need to manage this call site.
       return LambdaDescriptor.MATCH_FAILED;
@@ -240,18 +241,18 @@
 
     // Signature of main functional interface method.
     DexValue.DexValueMethodType funcErasedSignature =
-        getBootstrapArgument(callSite, 0, DexValue.DexValueMethodType.class);
+        getBootstrapArgument(callSite.bootstrapArgs, 0, DexValue.DexValueMethodType.class);
 
     // Method handle of the implementation method.
     DexMethodHandle lambdaImplMethodHandle =
-        getBootstrapArgument(callSite, 1, DexValue.DexValueMethodHandle.class).value;
+        getBootstrapArgument(callSite.bootstrapArgs, 1, DexValue.DexValueMethodHandle.class).value;
     // Even though there are some limitations on which method handle kinds are
     // allowed for lambda impl-methods, there is no way to detect unsupported
     // handle kinds after they are transformed into DEX method handle.
 
     // Signature to be enforced on main method.
     DexValue.DexValueMethodType funcEnforcedSignature =
-        getBootstrapArgument(callSite, 2, DexValue.DexValueMethodType.class);
+        getBootstrapArgument(callSite.bootstrapArgs, 2, DexValue.DexValueMethodType.class);
     if (!isEnforcedSignatureValid(
         rewriter, funcEnforcedSignature.value, funcErasedSignature.value)) {
       throw new Unreachable(
@@ -277,67 +278,70 @@
             "Unexpected number of metafactory method arguments in " + callSite.toString());
       }
     } else {
-      extractExtraLambdaInfo(rewriter, callSite, match);
+      extractAltMetafactory(
+          rewriter.factory,
+          callSite.bootstrapArgs,
+          interfaceType -> {
+            if (!match.interfaces.contains(interfaceType)) {
+              match.interfaces.add(interfaceType);
+            }
+          },
+          match.bridges::add);
     }
 
     return match;
   }
 
-  private static void extractExtraLambdaInfo(
-      LambdaRewriter rewriter, DexCallSite callSite, LambdaDescriptor match) {
+  public static void extractAltMetafactory(
+      DexItemFactory dexItemFactory,
+      List<DexValue> bootstrapArgs,
+      Consumer<DexType> interfaceConsumer,
+      Consumer<DexProto> bridgeConsumer) {
     int argIndex = 3;
-    int flagsArg = getBootstrapArgument(
-        callSite, argIndex++, DexValue.DexValueInt.class).value;
+    int flagsArg =
+        getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueInt.class).value;
     assert (flagsArg & ~LAMBDA_ALT_MASK) == 0;
 
     // Load extra interfaces if any.
     if ((flagsArg & LAMBDA_ALT_HAS_EXTRA_INTERFACES) != 0) {
-      int count = getBootstrapArgument(
-          callSite, argIndex++, DexValue.DexValueInt.class).value;
+      int count = getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueInt.class).value;
       for (int i = 0; i < count; i++) {
-        DexType type = getBootstrapArgument(
-            callSite, argIndex++, DexValue.DexValueType.class).value;
-        if (!match.interfaces.contains(type)) {
-          match.interfaces.add(type);
-        }
+        DexType interfaceType =
+            getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueType.class).value;
+        interfaceConsumer.accept(interfaceType);
       }
     }
 
     // If the lambda is serializable, add it.
     if ((flagsArg & LAMBDA_ALT_SERIALIZABLE) != 0) {
-      if (!match.interfaces.contains(rewriter.serializableType)) {
-        match.interfaces.add(rewriter.serializableType);
-      }
+      interfaceConsumer.accept(dexItemFactory.serializableType);
     }
 
     // Load bridges if any.
     if ((flagsArg & LAMBDA_ALT_HAS_BRIDGES) != 0) {
-      int count = getBootstrapArgument(
-          callSite, argIndex++, DexValue.DexValueInt.class).value;
+      int count = getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueInt.class).value;
       for (int i = 0; i < count; i++) {
-        DexProto bridgeProto = getBootstrapArgument(
-            callSite, argIndex++, DexValue.DexValueMethodType.class).value;
-        match.bridges.add(bridgeProto);
+        DexProto bridgeProto =
+            getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueMethodType.class)
+                .value;
+        bridgeConsumer.accept(bridgeProto);
       }
     }
 
-    if (callSite.bootstrapArgs.size() != argIndex) {
-      throw new Unreachable(
-          "Unexpected number of metafactory method arguments in " + callSite.toString());
+    if (bootstrapArgs.size() != argIndex) {
+      throw new Unreachable("Unexpected number of metafactory method arguments in DexCallSite");
     }
   }
 
   @SuppressWarnings("unchecked")
-  private static <T> T getBootstrapArgument(DexCallSite callSite, int i, Class<T> clazz) {
-    List<DexValue> bootstrapArgs = callSite.bootstrapArgs;
+  private static <T> T getBootstrapArgument(List<DexValue> bootstrapArgs, int i, Class<T> clazz) {
     if (bootstrapArgs.size() < i) {
-      throw new Unreachable("Expected to find at least "
-          + i + " bootstrap arguments in " + callSite.toString());
+      throw new Unreachable(
+          "Expected to find at least " + i + " bootstrap arguments in DexCallSite");
     }
     DexValue value = bootstrapArgs.get(i);
     if (!clazz.isAssignableFrom(value.getClass())) {
-      throw new Unreachable("Unexpected type of "
-          + "bootstrap arguments #" + i + " in " + callSite.toString());
+      throw new Unreachable("Unexpected type of bootstrap arguments #" + i + " in DexCallSite");
     }
     return (T) value;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index aad7961..82792df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -41,16 +41,8 @@
  * lambda class generation, and instruction patching.
  */
 public class LambdaRewriter {
-  private static final String METAFACTORY_TYPE_DESCR = "Ljava/lang/invoke/LambdaMetafactory;";
-  private static final String CALLSITE_TYPE_DESCR = "Ljava/lang/invoke/CallSite;";
-  private static final String LOOKUP_TYPE_DESCR = "Ljava/lang/invoke/MethodHandles$Lookup;";
-  private static final String METHODTYPE_TYPE_DESCR = "Ljava/lang/invoke/MethodType;";
-  private static final String METHODHANDLE_TYPE_DESCR = "Ljava/lang/invoke/MethodHandle;";
-  private static final String SERIALIZABLE_TYPE_DESCR = "Ljava/io/Serializable;";
   private static final String SERIALIZED_LAMBDA_TYPE_DESCR = "Ljava/lang/invoke/SerializedLambda;";
 
-  private static final String METAFACTORY_METHOD_NAME = "metafactory";
-  private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
   private static final String DESERIALIZE_LAMBDA_METHOD_NAME = "$deserializeLambda$";
 
   // Public for testing.
@@ -62,12 +54,8 @@
   final AppInfo appInfo;
   final DexItemFactory factory;
 
-  final DexMethod metafactoryMethod;
   final DexMethod objectInitMethod;
 
-  final DexMethod metafactoryAltMethod;
-  final DexType serializableType;
-
   final DexString constructorName;
   final DexString classConstructorName;
   final DexString instanceFieldName;
@@ -97,28 +85,11 @@
     this.factory = converter.appInfo.dexItemFactory;
     this.appInfo = converter.appInfo;
 
-    DexType metafactoryType = factory.createType(METAFACTORY_TYPE_DESCR);
-    DexType callSiteType = factory.createType(CALLSITE_TYPE_DESCR);
-    DexType lookupType = factory.createType(LOOKUP_TYPE_DESCR);
-    DexType methodTypeType = factory.createType(METHODTYPE_TYPE_DESCR);
-    DexType methodHandleType = factory.createType(METHODHANDLE_TYPE_DESCR);
-
-    this.metafactoryMethod = factory.createMethod(metafactoryType,
-        factory.createProto(callSiteType, lookupType, factory.stringType, methodTypeType,
-            methodTypeType, methodHandleType, methodTypeType),
-        factory.createString(METAFACTORY_METHOD_NAME));
-
-    this.metafactoryAltMethod = factory.createMethod(metafactoryType,
-        factory.createProto(callSiteType, lookupType, factory.stringType, methodTypeType,
-            factory.objectArrayType),
-        factory.createString(METAFACTORY_ALT_METHOD_NAME));
-
     this.constructorName = factory.createString(Constants.INSTANCE_INITIALIZER_NAME);
     DexProto initProto = factory.createProto(factory.voidType);
     this.objectInitMethod = factory.createMethod(factory.objectType, initProto, constructorName);
     this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
     this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
-    this.serializableType = factory.createType(SERIALIZABLE_TYPE_DESCR);
 
     this.deserializeLambdaMethodName = factory.createString(DESERIALIZE_LAMBDA_METHOD_NAME);
     this.deserializeLambdaMethodProto = factory.createProto(
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 3feb59a..bed4df3 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
@@ -2023,7 +2023,8 @@
               simplifyIfWithKnownCondition(code, block, theIf, cond, color);
             }
           }
-        } else if (theIf.isZeroTest() && !inValues.get(0).isConstNumber()) {
+        } else if (theIf.isZeroTest() && !inValues.get(0).isConstNumber()
+            && (theIf.getType() == Type.EQ || theIf.getType() == Type.NE)) {
           if (inValues.get(0).isNeverNull()) {
             simplifyIfWithKnownCondition(code, block, theIf, 1, color);
           } else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index a5ac28c..678ddae 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -55,7 +55,10 @@
 
   private final Set<DexMethod> blackList = Sets.newIdentityHashSet();
 
-  public Inliner(AppInfoWithLiveness appInfo, GraphLense graphLense, InternalOptions options) {
+  public Inliner(
+      AppInfoWithLiveness appInfo,
+      GraphLense graphLense,
+      InternalOptions options) {
     this.appInfo = appInfo;
     this.graphLense = graphLense;
     this.options = options;
@@ -202,7 +205,7 @@
         }
         return NEVER;
       } else {
-      /* package-private */
+        /* package-private */
         return targetHolder.isSamePackage(contextHolder) ? PACKAGE : NEVER;
       }
     }
@@ -269,9 +272,6 @@
       if (!target.isProcessed()) {
         new LensCodeRewriter(graphLense, appInfo).rewrite(code, target);
       }
-      if (options.enableNonNullTracking) {
-        new NonNullTracker().addNonNull(code);
-      }
       return code;
     }
   }
@@ -443,7 +443,17 @@
               iterator.previous();
               instruction_allowance -= numberOfInstructions(inlinee);
               if (instruction_allowance >= 0 || result.ignoreInstructionBudget()) {
-                iterator.inlineInvoke(code, inlinee, blockIterator, blocksToRemove, downcast);
+                BasicBlock invokeSuccessor =
+                    iterator.inlineInvoke(code, inlinee, blockIterator, blocksToRemove, downcast);
+                if (options.enableNonNullTracking) {
+                  // Move the cursor back to where the inlinee blocks are added.
+                  blockIterator = code.blocks.listIterator(code.blocks.indexOf(block));
+                  // Kick off the tracker to add non-null IRs only to the inlinee blocks.
+                  new NonNullTracker()
+                      .addNonNullInPart(code, blockIterator, inlinee.blocks::contains);
+                  // Move the cursor forward to where the inlinee blocks end.
+                  blockIterator = code.blocks.listIterator(code.blocks.indexOf(invokeSuccessor));
+                }
                 // Update type env for inlined blocks.
                 typeEnvironment.analyzeBlocks(inlinee.topologicallySortedBlocks());
                 // TODO(b/69964136): need a test where refined env in inlinee affects the caller.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index 4eae3d2..9aff28e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.Sets;
 import java.util.ListIterator;
 import java.util.Set;
+import java.util.function.Predicate;
 
 public class NonNullTracker {
 
@@ -56,9 +57,16 @@
   }
 
   public void addNonNull(IRCode code) {
-    ListIterator<BasicBlock> blocks = code.blocks.listIterator();
-    while (blocks.hasNext()) {
-      BasicBlock block = blocks.next();
+    addNonNullInPart(code, code.blocks.listIterator(), b -> true);
+  }
+
+  public void addNonNullInPart(
+      IRCode code, ListIterator<BasicBlock> blockIterator, Predicate<BasicBlock> blockTester) {
+    while (blockIterator.hasNext()) {
+      BasicBlock block = blockIterator.next();
+      if (!blockTester.test(block)) {
+        continue;
+      }
       // Add non-null after instructions that implicitly indicate receiver/array is not null.
       InstructionListIterator iterator = block.listIterator();
       while (iterator.hasNext()) {
@@ -90,7 +98,7 @@
         // A: ...y // blockWithNonNullInstruction
         //
         BasicBlock blockWithNonNullInstruction =
-            block.hasCatchHandlers() ? iterator.split(code, blocks) : block;
+            block.hasCatchHandlers() ? iterator.split(code, blockIterator) : block;
         // Next, add non-null fake IR, e.g.,
         // ...x
         // invoke(rcv, ...)
@@ -191,7 +199,7 @@
                 }
               }
               // Avoid adding a non-null for the value without meaningful users.
-              if (!dominatedUsers.isEmpty() && !dominatedPhiUsers.isEmpty()) {
+              if (!dominatedUsers.isEmpty() || !dominatedPhiUsers.isEmpty()) {
                 Value nonNullValue = code.createValue(
                     knownToBeNonNullValue.outType(), knownToBeNonNullValue.getLocalInfo());
                 NonNull nonNull = new NonNull(nonNullValue, knownToBeNonNullValue, theIf);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index dba6183..126b127 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -859,7 +859,8 @@
     }
 
     @Override
-    public void buildInstruction(IRBuilder builder, int instructionIndex) {
+    public void buildInstruction(
+        IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
       if (instructionIndex == outline.templateInstructions.size()) {
         if (outline.returnType == dexItemFactory.voidType) {
           builder.addReturn();
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
index 5d03dfd..5a6cc2d 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -188,7 +188,8 @@
   }
 
   @Override
-  public final void buildInstruction(IRBuilder builder, int instructionIndex)
+  public final void buildInstruction(
+      IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
       throws ApiLevelException {
     constructors.get(instructionIndex).accept(builder);
   }
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index c8be00e..a7b3f77 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -276,7 +276,7 @@
       }
       AnnotationVisitor v =
           visitor.visit(
-              dexAnnotation.annotation.type.toDescriptorString(),
+              namingLens.lookupDescriptor(dexAnnotation.annotation.type).toString(),
               dexAnnotation.visibility == DexAnnotation.VISIBILITY_RUNTIME);
       if (v != null) {
         writeAnnotation(v, dexAnnotation.annotation);
@@ -295,7 +295,8 @@
     if (value instanceof DexValueAnnotation) {
       DexValueAnnotation valueAnnotation = (DexValueAnnotation) value;
       AnnotationVisitor innerVisitor =
-          visitor.visitAnnotation(name, valueAnnotation.value.type.toDescriptorString());
+          visitor.visitAnnotation(
+              name, namingLens.lookupDescriptor(valueAnnotation.value.type).toString());
       if (innerVisitor != null) {
         writeAnnotation(innerVisitor, valueAnnotation.value);
         innerVisitor.visitEnd();
@@ -311,7 +312,8 @@
       }
     } else if (value instanceof DexValueEnum) {
       DexValueEnum en = (DexValueEnum) value;
-      visitor.visitEnum(name, en.value.type.toDescriptorString(), en.value.name.toString());
+      visitor.visitEnum(
+          name, namingLens.lookupDescriptor(en.value.type).toString(), en.value.name.toString());
     } else if (value instanceof DexValueField) {
       throw new Unreachable("writeAnnotationElement of DexValueField");
     } else if (value instanceof DexValueMethod) {
@@ -325,7 +327,7 @@
       visitor.visit(name, str.getValue().toString());
     } else if (value instanceof DexValueType) {
       DexValueType ty = (DexValueType) value;
-      visitor.visit(name, Type.getType(ty.value.toDescriptorString()));
+      visitor.visit(name, Type.getType(namingLens.lookupDescriptor(ty.value).toString()));
     } else if (value instanceof UnknownDexValue) {
       throw new Unreachable("writeAnnotationElement of UnknownDexValue");
     } else {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index ce37763..25a84b6 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.MethodJavaSignatureEquivalence;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.base.Equivalence;
@@ -88,13 +87,10 @@
  */
 class MethodNameMinifier extends MemberNameMinifier<DexMethod, DexProto> {
 
-  private final Equivalence<DexMethod> equivalence;
+  private final Equivalence<DexMethod> equivalence = MethodSignatureEquivalence.get();
 
   MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
     super(appInfo, rootSet, options);
-    equivalence = overloadAggressively
-        ? MethodSignatureEquivalence.get()
-        : MethodJavaSignatureEquivalence.get();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index de171a3..3372a20 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -11,8 +11,9 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.BiFunction;
 import java.util.function.Function;
@@ -124,7 +125,7 @@
       // Rebind to the lowest library class or program class.
       if (target != null && target.method != method) {
         DexClass targetClass = appInfo.definitionFor(target.method.holder);
-        // If the targetclass is not public but the targeted method is, we might run into
+        // If the target class is not public but the targeted method is, we might run into
         // visibility problems when rebinding.
         if (!targetClass.accessFlags.isPublic() && target.accessFlags.isPublic()) {
           // If the original class is public and this method is public, it might have been called
@@ -178,27 +179,37 @@
     return null;
   }
 
-  private void computeFieldRebinding(Set<DexField> fields,
+  private void computeFieldRebinding(Map<DexField, Set<DexEncodedMethod>> fields,
       BiFunction<DexType, DexField, DexEncodedField> lookup,
       BiFunction<DexClass, DexField, DexEncodedField> lookupTargetOnClass) {
-    for (DexField field : fields) {
+    for (Map.Entry<DexField, Set<DexEncodedMethod>> entry : fields.entrySet()) {
+      DexField field = entry.getKey();
       field = lense.lookupField(field, null);
       DexEncodedField target = lookup.apply(field.getHolder(), field);
       // Rebind to the lowest library class or program class. Do not rebind accesses to fields that
-      // are not public, as this might lead to access violation errors.
-      if (target != null && target.field != field && isVisibleFromOtherClasses(target)) {
+      // are not visible from the access context.
+      Set<DexEncodedMethod> contexts = entry.getValue();
+      if (target != null && target.field != field && contexts.stream().allMatch(context ->
+          isVisibleFromOriginalContext(context.method.getHolder(), target))) {
         builder.map(field, validTargetFor(target.field, field, lookupTargetOnClass));
       }
     }
   }
 
-  private boolean isVisibleFromOtherClasses(DexEncodedField field) {
-    // If the field is not public, the visibility on the class can not be a further constraint.
-    if (!field.accessFlags.isPublic()) {
-      return true;
+  private boolean isVisibleFromOriginalContext(DexType context, DexEncodedField field) {
+    DexType holderType = field.field.getHolder();
+    DexClass holder = appInfo.definitionFor(holderType);
+    if (holder == null) {
+      return false;
     }
-    // If the field is public, then a non-public holder class will further constrain visibility.
-    return appInfo.definitionFor(field.field.getHolder()).accessFlags.isPublic();
+    Constraint classVisibility =
+        Constraint.deriveConstraint(context, holderType, holder.accessFlags, appInfo);
+    if (classVisibility == Constraint.NEVER) {
+      return false;
+    }
+    Constraint fieldVisibility =
+        Constraint.deriveConstraint(context, holderType, field.accessFlags, appInfo);
+    return fieldVisibility != Constraint.NEVER;
   }
 
   public GraphLense run() {
@@ -213,10 +224,15 @@
     // Likewise static invokes.
     computeMethodRebinding(appInfo.staticInvokes, this::anyLookup);
 
-    computeFieldRebinding(Sets.union(appInfo.staticFieldReads, appInfo.staticFieldWrites),
+    computeFieldRebinding(appInfo.staticFieldReads,
         appInfo::resolveFieldOn, DexClass::lookupField);
-    computeFieldRebinding(Sets.union(appInfo.instanceFieldReads, appInfo.instanceFieldWrites),
+    computeFieldRebinding(appInfo.staticFieldWrites,
         appInfo::resolveFieldOn, DexClass::lookupField);
+    computeFieldRebinding(appInfo.instanceFieldReads,
+        appInfo::resolveFieldOn, DexClass::lookupField);
+    computeFieldRebinding(appInfo.instanceFieldWrites,
+        appInfo::resolveFieldOn, DexClass::lookupField);
+
     return builder.build(appInfo.dexItemFactory, lense);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
index c815cf8..5847a80 100644
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -33,8 +33,9 @@
       method.getCode().registerCodeReferences(targetExtractor);
       DexMethod target = targetExtractor.getTarget();
       InvokeKind kind = targetExtractor.getKind();
-      if (target != null &&
-          target.proto == method.method.proto) {
+      // javac-generated visibility forward bridge method has same descriptor (name, signature and
+      // return type).
+      if (target != null && target.hasSameProtoAndName(method.method)) {
         assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor();
         if (kind == InvokeKind.SUPER) {
           // This is a visibility forward, so check for the direct target.
diff --git a/src/main/java/com/android/tools/r8/shaking/DexStringCache.java b/src/main/java/com/android/tools/r8/shaking/DexStringCache.java
new file mode 100644
index 0000000..377dacd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/DexStringCache.java
@@ -0,0 +1,15 @@
+// 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.shaking;
+
+import com.android.tools.r8.graph.DexString;
+import java.util.concurrent.ConcurrentHashMap;
+
+final class DexStringCache {
+  private final ConcurrentHashMap<DexString, String> stringCache = new ConcurrentHashMap<>();
+
+  public String lookupString(DexString name) {
+    return stringCache.computeIfAbsent(name, DexString::toString);
+  }
+}
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 0b43509..cd04989 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -41,6 +41,7 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.shaking.RootSetBuilder.ConsequentRootSet;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.shaking.protolite.ProtoLiteExtension;
 import com.android.tools.r8.utils.InternalOptions;
@@ -70,6 +71,8 @@
 import java.util.Queue;
 import java.util.Set;
 import java.util.SortedSet;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
 import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -99,10 +102,14 @@
       Maps.newIdentityHashMap();
   private final Map<DexType, Set<DexMethod>> directInvokes = Maps.newIdentityHashMap();
   private final Map<DexType, Set<DexMethod>> staticInvokes = Maps.newIdentityHashMap();
-  private final Map<DexType, Set<DexField>> instanceFieldsWritten = Maps.newIdentityHashMap();
-  private final Map<DexType, Set<DexField>> instanceFieldsRead = Maps.newIdentityHashMap();
-  private final Map<DexType, Set<DexField>> staticFieldsRead = Maps.newIdentityHashMap();
-  private final Map<DexType, Set<DexField>> staticFieldsWritten = Maps.newIdentityHashMap();
+  private final Map<DexType, Set<TargetWithContext<DexField>>> instanceFieldsWritten =
+      Maps.newIdentityHashMap();
+  private final Map<DexType, Set<TargetWithContext<DexField>>> instanceFieldsRead =
+      Maps.newIdentityHashMap();
+  private final Map<DexType, Set<TargetWithContext<DexField>>> staticFieldsRead =
+      Maps.newIdentityHashMap();
+  private final Map<DexType, Set<TargetWithContext<DexField>>> staticFieldsWritten =
+      Maps.newIdentityHashMap();
 
   private final ProtoLiteExtension protoLiteExtension;
   private final Set<DexField> protoLiteFields = Sets.newIdentityHashSet();
@@ -128,7 +135,7 @@
       .newIdentityHashMap();
 
   /**
-   * Set of types that are mentioned in the program. We at least need an empty abstract classitem
+   * Set of types that are mentioned in the program. We at least need an empty abstract class item
    * for these.
    */
   private final Set<DexType> liveTypes = Sets.newIdentityHashSet();
@@ -361,7 +368,7 @@
 
     @Override
     public boolean registerInstanceFieldWrite(DexField field) {
-      if (!registerItemWithTarget(instanceFieldsWritten, field)) {
+      if (!registerItemWithTargetAndContext(instanceFieldsWritten, field, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -374,7 +381,7 @@
 
     @Override
     public boolean registerInstanceFieldRead(DexField field) {
-      if (!registerItemWithTarget(instanceFieldsRead, field)) {
+      if (!registerItemWithTargetAndContext(instanceFieldsRead, field, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -392,7 +399,7 @@
 
     @Override
     public boolean registerStaticFieldRead(DexField field) {
-      if (!registerItemWithTarget(staticFieldsRead, field)) {
+      if (!registerItemWithTargetAndContext(staticFieldsRead, field, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -404,7 +411,7 @@
 
     @Override
     public boolean registerStaticFieldWrite(DexField field) {
-      if (!registerItemWithTarget(staticFieldsWritten, field)) {
+      if (!registerItemWithTargetAndContext(staticFieldsWritten, field, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -712,12 +719,12 @@
     assert clazz.accessFlags.isInterface();
     SetWithReason<DexEncodedMethod> reachableMethods = reachableVirtualMethods.get(iface);
     if (reachableMethods != null) {
-      seen = seen.newNestedScope();
-      transitionNonAbstractMethodsToLiveAndShadow(reachableMethods.getItems(), instantiatedType,
-          seen);
-      for (DexType subInterface : clazz.interfaces.values) {
-        transitionDefaultMethodsForInstantiatedClass(subInterface, instantiatedType, seen);
-      }
+      transitionNonAbstractMethodsToLiveAndShadow(
+          reachableMethods.getItems(), instantiatedType, seen.newNestedScope());
+    }
+    seen = seen.newNestedScope();
+    for (DexType subInterface : clazz.interfaces.values) {
+      transitionDefaultMethodsForInstantiatedClass(subInterface, instantiatedType, seen);
     }
   }
 
@@ -1027,30 +1034,36 @@
         reachability, instantiatedTypes.getReasons());
   }
 
-  public AppInfoWithLiveness traceMainDex(RootSet rootSet, Timing timing) {
+  public AppInfoWithLiveness traceMainDex(
+      RootSet rootSet, ExecutorService executorService, Timing timing) throws ExecutionException {
     this.tracingMainDex = true;
     this.rootSet = rootSet;
     // Translate the result of root-set computation into enqueuer actions.
     enqueueRootItems(rootSet.noShrinking);
-    AppInfoWithLiveness appInfo = trace(timing);
+    AppInfoWithLiveness appInfo = trace(executorService, timing);
     options.reporter.failIfPendingErrors();
     return appInfo;
   }
 
-  public AppInfoWithLiveness traceApplication(RootSet rootSet, Timing timing) {
+  public AppInfoWithLiveness traceApplication(
+      RootSet rootSet, ExecutorService executorService, Timing timing) throws ExecutionException {
     this.rootSet = rootSet;
     // Translate the result of root-set computation into enqueuer actions.
     enqueueRootItems(rootSet.noShrinking);
     appInfo.libraryClasses().forEach(this::markAllLibraryVirtualMethodsReachable);
-    AppInfoWithLiveness result = trace(timing);
+    AppInfoWithLiveness result = trace(executorService, timing);
     options.reporter.failIfPendingErrors();
     return result;
   }
 
-  private AppInfoWithLiveness trace(Timing timing) {
+  private AppInfoWithLiveness trace(
+      ExecutorService executorService, Timing timing) throws ExecutionException {
     timing.begin("Grow the tree.");
     try {
       while (true) {
+        long numOfLiveItems = (long) liveTypes.size();
+        numOfLiveItems += (long) liveMethods.items.size();
+        numOfLiveItems += (long) liveFields.items.size();
         while (!workList.isEmpty()) {
           Action action = workList.poll();
           switch (action.kind) {
@@ -1083,6 +1096,24 @@
               throw new IllegalArgumentException(action.kind.toString());
           }
         }
+
+        // Continue fix-point processing if -if rules are enabled by items that newly became live.
+        long numOfLiveItemsAfterProcessing = (long) liveTypes.size();
+        numOfLiveItemsAfterProcessing += (long) liveMethods.items.size();
+        numOfLiveItemsAfterProcessing += (long) liveFields.items.size();
+        if (numOfLiveItemsAfterProcessing > numOfLiveItems) {
+          RootSetBuilder consequentSetBuilder =
+              new RootSetBuilder(appInfo, rootSet.ifRules, options);
+          ConsequentRootSet consequentRootSet = consequentSetBuilder.runForIfRules(
+              executorService, liveTypes, liveMethods.getItems(), liveFields.getItems());
+          enqueueRootItems(consequentRootSet.noShrinking);
+          rootSet.noOptimization.addAll(consequentRootSet.noOptimization);
+          rootSet.noObfuscation.addAll(consequentRootSet.noObfuscation);
+          if (!workList.isEmpty()) {
+            continue;
+          }
+        }
+
         // Continue fix-point processing while there are additional work items to ensure
         // Proguard compatibility.
         if (proguardCompatibilityWorkList.isEmpty()
@@ -1208,34 +1239,41 @@
     }
   }
 
-  private Set<DexField> collectFields(Map<DexType, Set<DexField>> map) {
-    return map.values().stream().flatMap(Collection::stream)
-        .collect(Collectors.toCollection(Sets::newIdentityHashSet));
+  private Map<DexField, Set<DexEncodedMethod>> collectFields(
+      Map<DexType, Set<TargetWithContext<DexField>>> map) {
+    Map<DexField, Set<DexEncodedMethod>> result = new IdentityHashMap<>();
+    for (Map.Entry<DexType, Set<TargetWithContext<DexField>>> entry : map.entrySet()) {
+      for (TargetWithContext<DexField> fieldWithContext : entry.getValue()) {
+        DexField field = fieldWithContext.getTarget();
+        DexEncodedMethod context = fieldWithContext.getContext();
+        result.computeIfAbsent(field, k -> Sets.newIdentityHashSet())
+            .add(context);
+      }
+    }
+    return result;
   }
 
-  SortedSet<DexField> collectInstanceFieldsRead() {
-    return ImmutableSortedSet.copyOf(
-        PresortedComparable<DexField>::slowCompareTo, collectFields(instanceFieldsRead));
+  Map<DexField, Set<DexEncodedMethod>> collectInstanceFieldsRead() {
+    return Collections.unmodifiableMap(collectFields(instanceFieldsRead));
   }
 
-  SortedSet<DexField> collectInstanceFieldsWritten() {
-    return ImmutableSortedSet.copyOf(
-        PresortedComparable<DexField>::slowCompareTo, collectFields(instanceFieldsWritten));
+  Map<DexField, Set<DexEncodedMethod>> collectInstanceFieldsWritten() {
+    return Collections.unmodifiableMap(collectFields(instanceFieldsWritten));
   }
 
-  SortedSet<DexField> collectStaticFieldsRead() {
-    return ImmutableSortedSet.copyOf(
-        PresortedComparable<DexField>::slowCompareTo, collectFields(staticFieldsRead));
+  Map<DexField, Set<DexEncodedMethod>> collectStaticFieldsRead() {
+    return Collections.unmodifiableMap(collectFields(staticFieldsRead));
   }
 
-  SortedSet<DexField> collectStaticFieldsWritten() {
-    return ImmutableSortedSet.copyOf(
-        PresortedComparable<DexField>::slowCompareTo, collectFields(staticFieldsWritten));
+  Map<DexField, Set<DexEncodedMethod>> collectStaticFieldsWritten() {
+    return Collections.unmodifiableMap(collectFields(staticFieldsWritten));
   }
 
-  private Set<DexField> collectReachedFields(Map<DexType, Set<DexField>> map,
-      Function<DexField, DexField> lookup) {
-    return map.values().stream().flatMap(set -> set.stream().map(lookup).filter(Objects::nonNull))
+  private Set<DexField> collectReachedFields(
+      Set<DexField> set, Function<DexField, DexField> lookup) {
+    return set.stream()
+        .map(lookup)
+        .filter(Objects::nonNull)
         .collect(Collectors.toCollection(Sets::newIdentityHashSet));
   }
 
@@ -1249,16 +1287,11 @@
     return target == null ? null : target.field;
   }
 
-  SortedSet<DexField> collectFieldsRead() {
+  SortedSet<DexField> mergeFieldAccesses(Set<DexField> instanceFields, Set<DexField> staticFields) {
     return ImmutableSortedSet.copyOf(PresortedComparable<DexField>::slowCompareTo,
-        Sets.union(collectReachedFields(instanceFieldsRead, this::tryLookupInstanceField),
-            collectReachedFields(staticFieldsRead, this::tryLookupStaticField)));
-  }
-
-  SortedSet<DexField> collectFieldsWritten() {
-    return ImmutableSortedSet.copyOf(PresortedComparable<DexField>::slowCompareTo,
-        Sets.union(collectReachedFields(instanceFieldsWritten, this::tryLookupInstanceField),
-            collectReachedFields(staticFieldsWritten, this::tryLookupStaticField)));
+        Sets.union(
+            collectReachedFields(instanceFields, this::tryLookupInstanceField),
+            collectReachedFields(staticFields, this::tryLookupStaticField)));
   }
 
   private void markClassAsInstantiatedWithCompatRule(DexClass clazz) {
@@ -1429,21 +1462,21 @@
      */
     public final SortedSet<DexField> fieldsWritten;
     /**
-     * Set of all field ids used in instance field reads.
+     * Set of all field ids used in instance field reads, along with access context.
      */
-    public final SortedSet<DexField> instanceFieldReads;
+    public final Map<DexField, Set<DexEncodedMethod>> instanceFieldReads;
     /**
-     * Set of all field ids used in instance field writes.
+     * Set of all field ids used in instance field writes, along with access context.
      */
-    public final SortedSet<DexField> instanceFieldWrites;
+    public final Map<DexField, Set<DexEncodedMethod>> instanceFieldWrites;
     /**
-     * Set of all field ids used in static static field reads.
+     * Set of all field ids used in static static field reads, along with access context.
      */
-    public final SortedSet<DexField> staticFieldReads;
+    public final Map<DexField, Set<DexEncodedMethod>> staticFieldReads;
     /**
-     * Set of all field ids used in static field writes.
+     * Set of all field ids used in static field writes, along with access context.
      */
-    public final SortedSet<DexField> staticFieldWrites;
+    public final Map<DexField, Set<DexEncodedMethod>> staticFieldWrites;
     /**
      * Set of all methods referenced in virtual invokes;
      */
@@ -1515,8 +1548,10 @@
       this.instanceFieldWrites = enqueuer.collectInstanceFieldsWritten();
       this.staticFieldReads = enqueuer.collectStaticFieldsRead();
       this.staticFieldWrites = enqueuer.collectStaticFieldsWritten();
-      this.fieldsRead = enqueuer.collectFieldsRead();
-      this.fieldsWritten = enqueuer.collectFieldsWritten();
+      this.fieldsRead = enqueuer.mergeFieldAccesses(
+          instanceFieldReads.keySet(), staticFieldReads.keySet());
+      this.fieldsWritten = enqueuer.mergeFieldAccesses(
+          instanceFieldWrites.keySet(), staticFieldWrites.keySet());
       this.pinnedItems = rewritePinnedItemsToDescriptors(enqueuer.pinnedItems);
       this.virtualInvokes = joinInvokedMethods(enqueuer.virtualInvokes);
       this.interfaceInvokes = joinInvokedMethods(enqueuer.interfaceInvokes);
@@ -1532,8 +1567,8 @@
       this.prunedTypes = Collections.emptySet();
       this.switchMaps = Collections.emptyMap();
       this.ordinalsMaps = Collections.emptyMap();
-      assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
-      assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
+      assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
+      assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
     }
 
     private AppInfoWithLiveness(AppInfoWithLiveness previous, DexApplication application,
@@ -1566,8 +1601,8 @@
       this.prunedTypes = mergeSets(previous.prunedTypes, removedClasses);
       this.switchMaps = previous.switchMaps;
       this.ordinalsMaps = previous.ordinalsMaps;
-      assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
-      assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
+      assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
+      assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
     }
 
     private AppInfoWithLiveness(AppInfoWithLiveness previous,
@@ -1579,10 +1614,14 @@
       this.targetedMethods = rewriteItems(previous.targetedMethods, lense::lookupMethod);
       this.liveMethods = rewriteItems(previous.liveMethods, lense::lookupMethod);
       this.liveFields = rewriteItems(previous.liveFields, lense::lookupField);
-      this.instanceFieldReads = rewriteItems(previous.instanceFieldReads, lense::lookupField);
-      this.instanceFieldWrites = rewriteItems(previous.instanceFieldWrites, lense::lookupField);
-      this.staticFieldReads = rewriteItems(previous.staticFieldReads, lense::lookupField);
-      this.staticFieldWrites = rewriteItems(previous.staticFieldWrites, lense::lookupField);
+      this.instanceFieldReads =
+          rewriteKeysWhileMergingValues(previous.instanceFieldReads, lense::lookupField);
+      this.instanceFieldWrites =
+          rewriteKeysWhileMergingValues(previous.instanceFieldWrites, lense::lookupField);
+      this.staticFieldReads =
+          rewriteKeysWhileMergingValues(previous.staticFieldReads, lense::lookupField);
+      this.staticFieldWrites =
+          rewriteKeysWhileMergingValues(previous.staticFieldWrites, lense::lookupField);
       this.fieldsRead = rewriteItems(previous.fieldsRead, lense::lookupField);
       this.fieldsWritten = rewriteItems(previous.fieldsWritten, lense::lookupField);
       this.pinnedItems = rewriteMixedItems(previous.pinnedItems, lense);
@@ -1608,8 +1647,8 @@
       this.ordinalsMaps = rewriteKeys(previous.ordinalsMaps, lense::lookupType);
       this.protoLiteFields = previous.protoLiteFields;
       // Sanity check sets after rewriting.
-      assert Sets.intersection(instanceFieldReads, staticFieldReads).isEmpty();
-      assert Sets.intersection(instanceFieldWrites, staticFieldWrites).isEmpty();
+      assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
+      assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
     }
 
     public AppInfoWithLiveness(AppInfoWithLiveness previous,
@@ -1744,6 +1783,18 @@
       return builder.build();
     }
 
+    private static <T extends PresortedComparable<T>, S> Map<T, Set<S>>
+        rewriteKeysWhileMergingValues(
+            Map<T, Set<S>> original, BiFunction<T, DexEncodedMethod, T> rewrite) {
+      Map<T, Set<S>> result = new IdentityHashMap<>();
+      for (T item : original.keySet()) {
+        T rewrittenKey = rewrite.apply(item, null);
+        result.computeIfAbsent(rewrittenKey, k -> Sets.newIdentityHashSet())
+            .addAll(original.get(item));
+      }
+      return Collections.unmodifiableMap(result);
+    }
+
     private static ImmutableSet<DexItem> rewriteMixedItems(
         Set<DexItem> original, GraphLense lense) {
       ImmutableSet.Builder<DexItem> builder = ImmutableSet.builder();
@@ -2095,6 +2146,10 @@
       return target;
     }
 
+    public DexEncodedMethod getContext() {
+      return context;
+    }
+
     @Override
     public int hashCode() {
       return target.hashCode() * 31 + context.hashCode();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 35c8e47..c1af9f4 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -336,8 +336,6 @@
         configurationBuilder.addRule(parseIdentifierNameStringRule());
       } else if (acceptString("if")) {
         configurationBuilder.addRule(parseIfRule(optionStart));
-        // TODO(b/73708139): remove warning once we add support -if <class_spec>
-        warnIgnoringOptions("if", optionStart);
       } else {
         String unknownOption = acceptString();
         reporter.error(new StringDiagnostic(
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index 6facec1..9659954 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -5,21 +5,19 @@
 
 import java.util.Set;
 
-public class ProguardIfRule extends ProguardConfigurationRule {
+public class ProguardIfRule extends ProguardKeepRule {
 
   final ProguardKeepRule subsequentRule;
 
-  public static class Builder extends ProguardConfigurationRule.Builder {
+  public static class Builder extends ProguardKeepRule.Builder {
 
     ProguardKeepRule subsequentRule = null;
 
-    private Builder() {
-    }
-
     public void setSubsequentRule(ProguardKeepRule rule) {
       subsequentRule = rule;
     }
 
+    @Override
     public ProguardIfRule build() {
       assert subsequentRule != null : "Option -if without a subsequent rule.";
       return new ProguardIfRule(classAnnotation, classAccessFlags,
@@ -37,7 +35,8 @@
       Set<ProguardMemberRule> memberRules,
       ProguardKeepRule subsequentRule) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
-        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules,
+        ProguardKeepRuleType.CONDITIONAL, ProguardKeepRuleModifiers.builder().build());
     this.subsequentRule = subsequentRule;
   }
 
@@ -46,6 +45,23 @@
   }
 
   @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof ProguardIfRule)) {
+      return false;
+    }
+    ProguardIfRule other = (ProguardIfRule) o;
+    if (subsequentRule != other.subsequentRule) {
+      return false;
+    }
+    return super.equals(o);
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() * 3 + subsequentRule.hashCode();
+  }
+
+  @Override
   String typeString() {
     return "if";
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
index 9b1bc0b..d3d6705 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -14,7 +14,7 @@
     private final ProguardKeepRuleModifiers.Builder modifiersBuilder
         = ProguardKeepRuleModifiers.builder();
 
-    private Builder() {}
+    protected Builder() {}
 
     public void setType(ProguardKeepRuleType type) {
       this.type = type;
@@ -34,7 +34,7 @@
   private final ProguardKeepRuleType type;
   private final ProguardKeepRuleModifiers modifiers;
 
-  private ProguardKeepRule(
+  protected ProguardKeepRule(
       ProguardTypeMatcher classAnnotation,
       ProguardAccessFlags classAccessFlags,
       ProguardAccessFlags negatedClassAccessFlags,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleType.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleType.java
index 4af299c..2018cbf 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleType.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleType.java
@@ -8,7 +8,8 @@
 public enum ProguardKeepRuleType {
   KEEP,
   KEEP_CLASS_MEMBERS,
-  KEEP_CLASSES_WITH_MEMBERS;
+  KEEP_CLASSES_WITH_MEMBERS,
+  CONDITIONAL;
 
   @Override
   public String toString() {
@@ -19,6 +20,8 @@
         return "keepclassmembers";
       case KEEP_CLASSES_WITH_MEMBERS:
         return "keepclasseswithmembers";
+      case CONDITIONAL:
+        return "if";
       default:
         throw new Unreachable("Unknown ProguardKeepRuleType.");
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
index db7dca8..5e1293f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -156,7 +156,7 @@
     return type;
   }
 
-  public boolean matches(DexEncodedField field, RootSetBuilder builder) {
+  public boolean matches(DexEncodedField field, DexStringCache stringCache) {
     switch (getRuleType()) {
       case ALL:
       case ALL_FIELDS:
@@ -169,7 +169,7 @@
         return RootSetBuilder.containsAnnotation(annotation, field.annotations);
       case FIELD:
         // Name check.
-        String name = builder.lookupString(field.field.name);
+        String name = stringCache.lookupString(field.field.name);
         if (!getName().matches(name)) {
           break;
         }
@@ -196,7 +196,7 @@
     return false;
   }
 
-  public boolean matches(DexEncodedMethod method, RootSetBuilder builder) {
+  public boolean matches(DexEncodedMethod method, DexStringCache stringCache) {
     switch (getRuleType()) {
       case ALL_METHODS:
         if (method.isClassInitializer()) {
@@ -220,7 +220,7 @@
       case CONSTRUCTOR:
       case INIT:
         // Name check.
-        String name = builder.lookupString(method.method.name);
+        String name = stringCache.lookupString(method.method.name);
         if (!getName().matches(name)) {
           break;
         }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 9c0dcdd..3912b1c 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.logging.Log;
@@ -40,17 +39,17 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 public class RootSetBuilder {
 
-  private final DirectMappedDexApplication application;
   private final AppInfo appInfo;
-  private final List<ProguardConfigurationRule> rules;
+  private final DirectMappedDexApplication application;
+  private final Collection<ProguardConfigurationRule> rules;
   private final Map<DexItem, ProguardKeepRule> noShrinking = new IdentityHashMap<>();
   private final Set<DexItem> noOptimization = Sets.newIdentityHashSet();
   private final Set<DexItem> noObfuscation = Sets.newIdentityHashSet();
@@ -67,18 +66,37 @@
   private final Set<DexItem> identifierNameStrings = Sets.newIdentityHashSet();
   private final InternalOptions options;
 
-  public RootSetBuilder(DexApplication application, AppInfo appInfo,
-      List<ProguardConfigurationRule> rules, InternalOptions options) {
-    this.application = application.asDirect();
+  private final DexStringCache dexStringCache = new DexStringCache();
+  private final Set<ProguardIfRule> ifRules = Sets.newIdentityHashSet();
+
+  public RootSetBuilder(
+      AppInfo appInfo,
+      DexApplication application,
+      List<ProguardConfigurationRule> rules,
+      InternalOptions options) {
     this.appInfo = appInfo;
-    this.rules = rules;
+    this.application = application.asDirect();
+    this.rules = rules == null ? null : Collections.unmodifiableCollection(rules);
     this.options = options;
   }
 
-  private boolean anySuperTypeMatches(DexType type, ProguardTypeMatcher name,
+  RootSetBuilder(
+      AppInfo appInfo,
+      Set<ProguardIfRule> ifRules,
+      InternalOptions options) {
+    this.appInfo = appInfo;
+    this.application = appInfo.app.asDirect();
+    this.rules = Collections.unmodifiableCollection(ifRules);
+    this.options = options;
+  }
+
+  private boolean anySuperTypeMatches(
+      DexType type,
+      Function<DexType, DexClass> definitionFor,
+      ProguardTypeMatcher name,
       ProguardTypeMatcher annotation) {
     while (type != null) {
-      DexClass clazz = application.definitionFor(type);
+      DexClass clazz = definitionFor.apply(type);
       if (clazz == null) {
         // TODO(herhut): Warn about broken supertype chain?
         return false;
@@ -91,36 +109,42 @@
     return false;
   }
 
-  private boolean anyImplementedInterfaceMatches(DexClass clazz,
-      ProguardTypeMatcher className, ProguardTypeMatcher annotation) {
+  private boolean anyImplementedInterfaceMatches(
+      DexClass clazz,
+      Function<DexType, DexClass> definitionFor,
+      ProguardTypeMatcher className,
+      ProguardTypeMatcher annotation) {
     if (clazz == null) {
       return false;
     }
     for (DexType iface : clazz.interfaces.values) {
-      DexClass ifaceClass = application.definitionFor(iface);
+      DexClass ifaceClass = definitionFor.apply(iface);
       if (ifaceClass == null) {
         // TODO(herhut): Warn about broken supertype chain?
         return false;
       }
       // TODO(herhut): Maybe it would be better to do this breadth first.
       if ((className.matches(iface) && containsAnnotation(annotation, ifaceClass.annotations))
-          || anyImplementedInterfaceMatches(ifaceClass, className, annotation)) {
+          || anyImplementedInterfaceMatches(ifaceClass, definitionFor, className, annotation)) {
         return true;
       }
     }
     if (clazz.superType == null) {
       return false;
     }
-    DexClass superClass = application.definitionFor(clazz.superType);
+    DexClass superClass = definitionFor.apply(clazz.superType);
     if (superClass == null) {
       // TODO(herhut): Warn about broken supertype chain?
       return false;
     }
-    return anyImplementedInterfaceMatches(superClass, className, annotation);
+    return anyImplementedInterfaceMatches(superClass, definitionFor, className, annotation);
   }
 
   // Process a class with the keep rule.
-  private void process(DexClass clazz, ProguardConfigurationRule rule) {
+  private void process(
+      DexClass clazz,
+      ProguardConfigurationRule rule,
+      ProguardIfRule ifRule) {
     if (rule.getClassType().matches(clazz) == rule.getClassTypeNegated()) {
       return;
     }
@@ -140,12 +164,18 @@
     // TODO(herhut): One day make this do what it says.
     if (rule.hasInheritanceClassName()) {
       boolean extendsExpected =
-          anySuperTypeMatches(clazz.superType, rule.getInheritanceClassName(),
+          anySuperTypeMatches(
+              clazz.superType,
+              application::definitionFor,
+              rule.getInheritanceClassName(),
               rule.getInheritanceAnnotation());
       boolean implementsExpected = false;
       if (!extendsExpected) {
         implementsExpected =
-            anyImplementedInterfaceMatches(clazz, rule.getInheritanceClassName(),
+            anyImplementedInterfaceMatches(
+                clazz,
+                application::definitionFor,
+                rule.getInheritanceClassName(),
                 rule.getInheritanceAnnotation());
       }
       if (!extendsExpected && !implementsExpected) {
@@ -167,16 +197,15 @@
       }
     }
 
-    if (rule instanceof ProguardIfRule) {
-      // TODO(b/73708139): add support -if <class_spec>
-      // Check if -if part matches or subsequent -keep part matches.
-    } else if (rule.getClassNames().matches(clazz.type)) {
+    if (rule.getClassNames().matches(clazz.type)) {
       Collection<ProguardMemberRule> memberKeepRules = rule.getMemberRules();
       if (rule instanceof ProguardKeepRule) {
         switch (((ProguardKeepRule) rule).getType()) {
           case KEEP_CLASS_MEMBERS: {
-            markMatchingVisibleMethods(clazz, memberKeepRules, rule, clazz.type);
-            markMatchingFields(clazz, memberKeepRules, rule, clazz.type);
+            // If we're handling -if consequent part, that means precondition already met.
+            DexType precondition = ifRule != null ? null : clazz.type;
+            markMatchingVisibleMethods(clazz, memberKeepRules, rule, precondition);
+            markMatchingFields(clazz, memberKeepRules, rule, precondition);
             break;
           }
           case KEEP_CLASSES_WITH_MEMBERS: {
@@ -191,6 +220,9 @@
             markMatchingFields(clazz, memberKeepRules, rule, null);
             break;
           }
+          case CONDITIONAL:
+            assert rule instanceof ProguardIfRule;
+            throw new Unreachable("-if rule will be evaluated separately, not here.");
         }
       } else if (rule instanceof ProguardCheckDiscardRule) {
         if (memberKeepRules.isEmpty()) {
@@ -220,6 +252,36 @@
     }
   }
 
+  private void runPerRule(
+      ExecutorService executorService,
+      List<Future<?>> futures,
+      ProguardConfigurationRule rule,
+      ProguardIfRule ifRule) {
+    List<DexType> specifics = rule.getClassNames().asSpecificDexTypes();
+    if (specifics != null) {
+      // This keep rule only lists specific type matches.
+      // This means there is no need to iterate over all classes.
+      for (DexType type : specifics) {
+        DexClass clazz = application.definitionFor(type);
+        // Ignore keep rule iff it does not reference a class in the app.
+        if (clazz != null) {
+          process(clazz, rule, ifRule);
+        }
+      }
+    } else {
+      futures.add(executorService.submit(() -> {
+        for (DexProgramClass clazz : application.classes()) {
+          process(clazz, rule, ifRule);
+        }
+        if (rule.applyToLibraryClasses()) {
+          for (DexLibraryClass clazz : application.libraryClasses()) {
+            process(clazz, rule, ifRule);
+          }
+        }
+      }));
+    }
+  }
+
   public RootSet run(ExecutorService executorService) throws ExecutionException {
     application.timing.begin("Build root set...");
     try {
@@ -227,28 +289,11 @@
       // Mark all the things explicitly listed in keep rules.
       if (rules != null) {
         for (ProguardConfigurationRule rule : rules) {
-          List<DexType> specifics = rule.getClassNames().asSpecificDexTypes();
-          if (specifics != null) {
-            // This keep rule only lists specific type matches.
-            // This means there is no need to iterate over all classes.
-            for (DexType type : specifics) {
-              DexClass clazz = application.definitionFor(type);
-              // Ignore keep rule iff it does not reference a class in the app.
-              if (clazz != null) {
-                process(clazz, rule);
-              }
-            }
+          if (rule instanceof ProguardIfRule) {
+            ProguardIfRule ifRule = (ProguardIfRule) rule;
+            ifRules.add(ifRule);
           } else {
-            futures.add(executorService.submit(() -> {
-              for (DexProgramClass clazz : application.classes()) {
-                process(clazz, rule);
-              }
-              if (rule.applyToLibraryClasses()) {
-                for (DexLibraryClass clazz : application.libraryClasses()) {
-                  process(clazz, rule);
-                }
-              }
-            }));
+            runPerRule(executorService, futures, rule, null);
           }
         }
         ThreadUtils.awaitFutures(futures);
@@ -267,33 +312,65 @@
         noSideEffects,
         assumedValues,
         dependentNoShrinking,
-        identifierNameStrings);
+        identifierNameStrings,
+        ifRules);
   }
 
-  private void markMatchingVisibleMethods(DexClass clazz,
-      Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
+  ConsequentRootSet runForIfRules(
+      ExecutorService executorService,
+      Set<DexType> liveTypes,
+      Set<DexEncodedMethod> liveMethods,
+      Set<DexEncodedField> liveFields) throws ExecutionException {
+    application.timing.begin("Find consequent items for -if rules...");
+    try {
+      List<Future<?>> futures = new ArrayList<>();
+      if (rules != null) {
+        for (ProguardConfigurationRule rule : rules) {
+          assert rule instanceof ProguardIfRule;
+          ProguardIfRule ifRule = (ProguardIfRule) rule;
+          if (ruleSatisfiedWithLiveItems(
+              ifRule, appInfo::definitionFor, liveTypes, liveMethods, liveFields)) {
+            runPerRule(executorService, futures, ifRule.subsequentRule, ifRule);
+          }
+        }
+        ThreadUtils.awaitFutures(futures);
+      }
+    } finally {
+      application.timing.end();
+    }
+    return new ConsequentRootSet(noShrinking, noOptimization, noObfuscation);
+  }
+
+  private void markMatchingVisibleMethods(
+      DexClass clazz,
+      Collection<ProguardMemberRule> memberKeepRules,
+      ProguardConfigurationRule rule,
       DexType onlyIfClassKept) {
     Set<Wrapper<DexMethod>> methodsMarked = new HashSet<>();
     Arrays.stream(clazz.directMethods()).forEach(method ->
-        markMethod(method, memberKeepRules, rule, methodsMarked, onlyIfClassKept));
+        markMethod(method, memberKeepRules, methodsMarked, rule, onlyIfClassKept));
     while (clazz != null) {
       Arrays.stream(clazz.virtualMethods()).forEach(method ->
-          markMethod(method, memberKeepRules, rule, methodsMarked, onlyIfClassKept));
+          markMethod(method, memberKeepRules, methodsMarked, rule, onlyIfClassKept));
       clazz = clazz.superType == null ? null : application.definitionFor(clazz.superType);
     }
   }
 
-  private void markMatchingMethods(DexClass clazz,
-      Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
+  private void markMatchingMethods(
+      DexClass clazz,
+      Collection<ProguardMemberRule> memberKeepRules,
+      ProguardConfigurationRule rule,
       DexType onlyIfClassKept) {
     Arrays.stream(clazz.directMethods()).forEach(method ->
-        markMethod(method, memberKeepRules, rule, null, onlyIfClassKept));
+        markMethod(method, memberKeepRules, null, rule, onlyIfClassKept));
     Arrays.stream(clazz.virtualMethods()).forEach(method ->
-        markMethod(method, memberKeepRules, rule, null, onlyIfClassKept));
+        markMethod(method, memberKeepRules, null, rule, onlyIfClassKept));
   }
 
-  private void markMatchingFields(DexClass clazz,
-      Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
+  private void markMatchingFields(
+      DexClass clazz,
+      Collection<ProguardMemberRule> memberKeepRules,
+      ProguardConfigurationRule rule,
       DexType onlyIfClassKept) {
     clazz.forEachField(field -> markField(field, memberKeepRules, rule, onlyIfClassKept));
   }
@@ -341,6 +418,79 @@
     out.close();
   }
 
+  private boolean satisfyInheritanceRule(
+      DexType type,
+      Function<DexType, DexClass> definitionFor,
+      ProguardConfigurationRule rule) {
+    DexClass clazz = definitionFor.apply(type);
+    if (clazz == null) {
+      return false;
+    }
+    return
+        anySuperTypeMatches(
+            clazz.superType,
+            definitionFor,
+            rule.getInheritanceClassName(),
+            rule.getInheritanceAnnotation())
+        || anyImplementedInterfaceMatches(
+            clazz,
+            definitionFor,
+            rule.getInheritanceClassName(),
+            rule.getInheritanceAnnotation());
+  }
+
+  private boolean ruleSatisfiedWithLiveItems(
+      ProguardIfRule ifRule,
+      Function<DexType, DexClass> definitionFor,
+      Set<DexType> liveTypes,
+      Set<DexEncodedMethod> liveMethods,
+      Set<DexEncodedField> liveFields) {
+    DexType matchedType = null;
+    Function<DexType, DexClass> definitionForWithLiveTypes = type -> {
+      DexClass clazz = definitionFor.apply(type);
+      if (clazz != null && liveTypes.contains(clazz.type)) {
+        return clazz;
+      }
+      return null;
+    };
+    for (DexType liveType : liveTypes) {
+      if (ifRule.hasInheritanceClassName()) {
+        if (!satisfyInheritanceRule(liveType, definitionForWithLiveTypes, ifRule)) {
+          // Try another live type since the current one doesn't satisfy the inheritance rule.
+          continue;
+        }
+      }
+      if (ifRule.getClassNames().matches(liveType)) {
+        matchedType = liveType;
+        final DexType filterType = matchedType;
+        Collection<ProguardMemberRule> memberKeepRules = ifRule.getMemberRules();
+        Set<DexEncodedMethod> filteredMethods =
+            liveMethods.stream().filter(method -> method.method.getHolder() == filterType)
+                .collect(Collectors.toSet());
+        Set<DexEncodedField> filteredFields =
+            liveFields.stream().filter(field -> field.field.getHolder() == filterType)
+                .collect(Collectors.toSet());
+        boolean notSatisfied = false;
+        for (ProguardMemberRule rule : memberKeepRules) {
+          if (!ruleSatisfiedByMethods(rule, filteredMethods)
+            && !ruleSatisfiedByFields(rule, filteredFields)) {
+            notSatisfied = true;
+            break;
+          }
+        }
+        // Try another live type if the current live type does not satisfy any member rule.
+        if (notSatisfied) {
+          continue;
+        }
+        // TODO(b/73800755): we may need to return the matched live type for back reference.
+        // Maybe, collect all matched live types.
+        return true;
+      }
+    }
+    // No live types satisfy the given -if rule.
+    return false;
+  }
+
   private boolean allRulesSatisfied(Collection<ProguardMemberRule> memberKeepRules,
       DexClass clazz) {
     for (ProguardMemberRule rule : memberKeepRules) {
@@ -362,10 +512,36 @@
         || ruleSatisfiedByFields(rule, clazz.instanceFields());
   }
 
+  private boolean ruleSatisfiedByMethods(
+      ProguardMemberRule rule,
+      Iterable<DexEncodedMethod> methods) {
+    if (rule.getRuleType().includesMethods()) {
+      for (DexEncodedMethod method : methods) {
+        if (rule.matches(method, dexStringCache)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
   private boolean ruleSatisfiedByMethods(ProguardMemberRule rule, DexEncodedMethod[] methods) {
     if (rule.getRuleType().includesMethods()) {
       for (DexEncodedMethod method : methods) {
-        if (rule.matches(method, this)) {
+        if (rule.matches(method, dexStringCache)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  private boolean ruleSatisfiedByFields(
+      ProguardMemberRule rule,
+      Iterable<DexEncodedField> fields) {
+    if (rule.getRuleType().includesFields()) {
+      for (DexEncodedField field : fields) {
+        if (rule.matches(field, dexStringCache)) {
           return true;
         }
       }
@@ -376,7 +552,7 @@
   private boolean ruleSatisfiedByFields(ProguardMemberRule rule, DexEncodedField[] fields) {
     if (rule.getRuleType().includesFields()) {
       for (DexEncodedField field : fields) {
-        if (rule.matches(field, this)) {
+        if (rule.matches(field, dexStringCache)) {
           return true;
         }
       }
@@ -400,21 +576,18 @@
     return false;
   }
 
-  private final ConcurrentHashMap<DexString, String> stringCache = new ConcurrentHashMap<>();
-
-  public String lookupString(DexString name) {
-    return stringCache.computeIfAbsent(name, DexString::toString);
-  }
-
-  private void markMethod(DexEncodedMethod method, Collection<ProguardMemberRule> rules,
-      ProguardConfigurationRule context, Set<Wrapper<DexMethod>> methodsMarked,
-      DexType onlyIfClassKept) {
+  private void markMethod(
+      DexEncodedMethod method,
+      Collection<ProguardMemberRule> rules,
+      Set<Wrapper<DexMethod>> methodsMarked,
+      ProguardConfigurationRule context,
+      DexItem precondition) {
     if ((methodsMarked != null)
         && methodsMarked.contains(MethodSignatureEquivalence.get().wrap(method.method))) {
       return;
     }
     for (ProguardMemberRule rule : rules) {
-      if (rule.matches(method, this)) {
+      if (rule.matches(method, dexStringCache)) {
         if (Log.ENABLED) {
           Log.verbose(getClass(), "Marking method `%s` due to `%s { %s }`.", method, context,
               rule);
@@ -422,20 +595,23 @@
         if (methodsMarked != null) {
           methodsMarked.add(MethodSignatureEquivalence.get().wrap(method.method));
         }
-        addItemToSets(method, context, rule, onlyIfClassKept);
+        addItemToSets(method, context, rule, precondition);
       }
     }
   }
 
-  private void markField(DexEncodedField field, Collection<ProguardMemberRule> rules,
-      ProguardConfigurationRule context, DexType onlyIfClassKept) {
+  private void markField(
+      DexEncodedField field,
+      Collection<ProguardMemberRule> rules,
+      ProguardConfigurationRule context,
+      DexItem precondition) {
     for (ProguardMemberRule rule : rules) {
-      if (rule.matches(field, this)) {
+      if (rule.matches(field, dexStringCache)) {
         if (Log.ENABLED) {
           Log.verbose(getClass(), "Marking field `%s` due to `%s { %s }`.", field, context,
               rule);
         }
-        addItemToSets(field, context, rule, onlyIfClassKept);
+        addItemToSets(field, context, rule, precondition);
       }
     }
   }
@@ -480,14 +656,17 @@
     }
   }
 
-  private synchronized void addItemToSets(DexItem item, ProguardConfigurationRule context,
-      ProguardMemberRule rule, DexType onlyIfClassKept) {
+  private synchronized void addItemToSets(
+      DexItem item,
+      ProguardConfigurationRule context,
+      ProguardMemberRule rule,
+      DexItem precondition) {
     if (context instanceof ProguardKeepRule) {
       ProguardKeepRule keepRule = (ProguardKeepRule) context;
       ProguardKeepRuleModifiers modifiers = keepRule.getModifiers();
       if (!modifiers.allowsShrinking) {
-        if (onlyIfClassKept != null) {
-          dependentNoShrinking.computeIfAbsent(onlyIfClassKept, x -> new IdentityHashMap<>())
+        if (precondition != null) {
+          dependentNoShrinking.computeIfAbsent(precondition, x -> new IdentityHashMap<>())
               .put(item, keepRule);
         } else {
           noShrinking.put(item, keepRule);
@@ -540,6 +719,7 @@
     public final Map<DexItem, ProguardMemberRule> assumedValues;
     private final Map<DexItem, Map<DexItem, ProguardKeepRule>> dependentNoShrinking;
     public final Set<DexItem> identifierNameStrings;
+    public final Set<ProguardIfRule> ifRules;
 
     private boolean isTypeEncodedMethodOrEncodedField(DexItem item) {
       assert item instanceof DexType
@@ -573,10 +753,11 @@
         Map<DexItem, ProguardMemberRule> noSideEffects,
         Map<DexItem, ProguardMemberRule> assumedValues,
         Map<DexItem, Map<DexItem, ProguardKeepRule>> dependentNoShrinking,
-        Set<DexItem> identifierNameStrings) {
+        Set<DexItem> identifierNameStrings,
+        Set<ProguardIfRule> ifRules) {
       this.noShrinking = Collections.unmodifiableMap(noShrinking);
-      this.noOptimization = Collections.unmodifiableSet(noOptimization);
-      this.noObfuscation = Collections.unmodifiableSet(noObfuscation);
+      this.noOptimization = noOptimization;
+      this.noObfuscation = noObfuscation;
       this.reasonAsked = Collections.unmodifiableSet(reasonAsked);
       this.keepPackageName = Collections.unmodifiableSet(keepPackageName);
       this.checkDiscarded = Collections.unmodifiableSet(checkDiscarded);
@@ -585,6 +766,7 @@
       this.assumedValues = Collections.unmodifiableMap(assumedValues);
       this.dependentNoShrinking = dependentNoShrinking;
       this.identifierNameStrings = Collections.unmodifiableSet(identifierNameStrings);
+      this.ifRules = Collections.unmodifiableSet(ifRules);
       assert legalNoObfuscationItems(noObfuscation);
       assert legalDependentNoShrinkingItems(dependentNoShrinking);
     }
@@ -629,6 +811,7 @@
       builder.append("\nassumedValues: " + assumedValues.size());
       builder.append("\ndependentNoShrinking: " + dependentNoShrinking.size());
       builder.append("\nidentifierNameStrings: " + identifierNameStrings.size());
+      builder.append("\nifRules: " + ifRules.size());
 
       builder.append("\n\nNo Shrinking:");
       noShrinking.keySet().stream()
@@ -639,4 +822,20 @@
       return builder.toString();
     }
   }
+
+  // A partial RootSet that becomes live due to the enabled -if rule.
+  static class ConsequentRootSet {
+    final Map<DexItem, ProguardKeepRule> noShrinking;
+    final Set<DexItem> noOptimization;
+    final Set<DexItem> noObfuscation;
+
+    private ConsequentRootSet(
+        Map<DexItem, ProguardKeepRule> noShrinking,
+        Set<DexItem> noOptimization,
+        Set<DexItem> noObfuscation) {
+      this.noShrinking = Collections.unmodifiableMap(noShrinking);
+      this.noOptimization = Collections.unmodifiableSet(noOptimization);
+      this.noObfuscation = Collections.unmodifiableSet(noObfuscation);
+    }
+  }
 }
diff --git a/src/test/examples/regress_76025099/Logger.java b/src/test/examples/regress_76025099/Logger.java
new file mode 100644
index 0000000..d6e9f95
--- /dev/null
+++ b/src/test/examples/regress_76025099/Logger.java
@@ -0,0 +1,8 @@
+// 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 regress_76025099;
+
+public interface Logger {
+  String getName();
+}
diff --git a/src/test/examples/regress_76025099/Main.java b/src/test/examples/regress_76025099/Main.java
new file mode 100644
index 0000000..659bdbd
--- /dev/null
+++ b/src/test/examples/regress_76025099/Main.java
@@ -0,0 +1,13 @@
+// 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 regress_76025099;
+
+import regress_76025099.impl.Factory;
+
+public class Main {
+  public static void main(String[] args) {
+    Logger l = Factory.getImpl(Main.class.getCanonicalName());
+    System.out.println(l.getName());
+  }
+}
diff --git a/src/test/examples/regress_76025099/helper/AbstractBase.java b/src/test/examples/regress_76025099/helper/AbstractBase.java
new file mode 100644
index 0000000..046e12f
--- /dev/null
+++ b/src/test/examples/regress_76025099/helper/AbstractBase.java
@@ -0,0 +1,15 @@
+// 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 regress_76025099.helper;
+
+import regress_76025099.Logger;
+
+abstract class AbstractBase implements Logger {
+  protected String name;
+
+  @Override
+  public String getName() {
+    return name;
+  }
+}
diff --git a/src/test/examples/regress_76025099/helper/AbstractSub.java b/src/test/examples/regress_76025099/helper/AbstractSub.java
new file mode 100644
index 0000000..9240e4a
--- /dev/null
+++ b/src/test/examples/regress_76025099/helper/AbstractSub.java
@@ -0,0 +1,7 @@
+// 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 regress_76025099.helper;
+
+public abstract class AbstractSub extends AbstractBase {
+}
diff --git a/src/test/examples/regress_76025099/impl/Factory.java b/src/test/examples/regress_76025099/impl/Factory.java
new file mode 100644
index 0000000..0cc1cc9
--- /dev/null
+++ b/src/test/examples/regress_76025099/impl/Factory.java
@@ -0,0 +1,10 @@
+// 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 regress_76025099.impl;
+
+public class Factory {
+  public static Impl getImpl(String name) {
+    return new Impl(name);
+  }
+}
diff --git a/src/test/examples/regress_76025099/impl/Impl.java b/src/test/examples/regress_76025099/impl/Impl.java
new file mode 100644
index 0000000..74eefcf
--- /dev/null
+++ b/src/test/examples/regress_76025099/impl/Impl.java
@@ -0,0 +1,12 @@
+// 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 regress_76025099.impl;
+
+import regress_76025099.helper.AbstractSub;
+
+public class Impl extends AbstractSub {
+  Impl(String name) {
+    this.name = name;
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 4d7240e..923398d 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -550,24 +550,6 @@
   // Tests where the output of R8 fails when run with Art.
   private static final Multimap<String, TestCondition> failingRunWithArt =
       new ImmutableListMultimap.Builder<String, TestCondition>()
-          // This test relies on specific field access patterns, which we rewrite.
-          .put("064-field-access",
-              TestCondition.match(
-                  TestCondition.R8DEX_NOT_AFTER_D8_COMPILER,
-                  TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
-          .put("064-field-access",
-              TestCondition.match(
-                  TestCondition.R8DEX_COMPILER,
-                  TestCondition.runtimes(
-                      DexVm.Version.DEFAULT, DexVm.Version.V7_0_0, DexVm.Version.V6_0_1,
-                      DexVm.Version.V5_1_1)))
-          .put("064-field-access",
-              TestCondition.match(
-                  TestCondition.tools(DexTool.NONE),
-                  TestCondition.D8_AFTER_R8CF_COMPILER,
-                  TestCondition.runtimes(
-                      DexVm.Version.DEFAULT, DexVm.Version.V7_0_0, DexVm.Version.V6_0_1,
-                      DexVm.Version.V5_1_1)))
           // The growth limit test fails after processing by R8 because R8 will eliminate an
           // "unneeded" const store. The following reflective call to the VM's GC will then see the
           // large array as still live and the subsequent allocations will fail to reach the desired
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index d7d8b99..a29d673 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -398,6 +398,13 @@
         + (obfuscate ? "-printmapping\n" : "-dontobfuscate\n");
   }
 
+  public static String keepMainProguardConfiguration(
+      String clazz, boolean allowaccessmodification, boolean obfuscate) {
+    return keepMainProguardConfiguration(clazz)
+        + (allowaccessmodification ? "-allowaccessmodification\n" : "")
+        + (obfuscate ? "-printmapping\n" : "-dontobfuscate\n");
+  }
+
   /**
    * Run application on the specified version of Art with the specified main class.
    */
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
index 6e3c114..35a7613 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
@@ -4,14 +4,26 @@
 
 package com.android.tools.r8.bridgeremoval;
 
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.bridgeremoval.bridgestoremove.Main;
 import com.android.tools.r8.bridgeremoval.bridgestoremove.Outer;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
 import java.lang.reflect.Method;
+import java.nio.file.Path;
+import java.util.Collections;
 import java.util.List;
 import org.junit.Test;
 
@@ -40,4 +52,81 @@
   public void testWithoutObfuscation() throws Exception {
     run(false);
   }
+
+  @Test
+  public void regressionTest_b76383728_WithObfuscation() throws Exception {
+    runRegressionTest_b76383728(true);
+  }
+
+  @Test
+  public void regressionTest_b76383728_WithoutObfuscation() throws Exception {
+    runRegressionTest_b76383728(false);
+  }
+
+  /**
+   * Regression test for b76383728 to make sure we correctly identify and remove real visibility
+   * forward bridge methods synthesized by javac.
+   */
+  private void runRegressionTest_b76383728(boolean obfuscate) throws Exception {
+    JasminBuilder jasminBuilder = new JasminBuilder();
+
+    ClassBuilder superClass = jasminBuilder.addClass("SuperClass");
+    superClass.addDefaultConstructor();
+    superClass.addVirtualMethod("method", Collections.emptyList(), "Ljava/lang/String;",
+        ".limit stack 1",
+        "ldc \"Hello World\"",
+        "areturn");
+
+    // Generate a subclass with a method with same
+    ClassBuilder subclass = jasminBuilder.addClass("SubClass", superClass.name);
+    subclass.addBridgeMethod("getMethod", Collections.emptyList(), "Ljava/lang/String;",
+        ".limit stack 1",
+        "aload_0",
+        "invokespecial " + superClass.name + "/method()Ljava/lang/String;",
+        "areturn");
+
+    ClassBuilder mainClass = jasminBuilder.addClass("Main");
+    mainClass.addMainMethod(
+        ".limit stack 3",
+        ".limit locals 2",
+        "getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "new " + subclass.name,
+        "dup",
+        "invokespecial " + subclass.name + "/<init>()V",
+        "invokevirtual " + subclass.name + "/getMethod()Ljava/lang/String;",
+        "invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+        "return"
+    );
+
+    final String mainClassName = mainClass.name;
+
+    String proguardConfig = keepMainProguardConfiguration(mainClass.name, true, obfuscate);
+
+    // Run input program on java.
+    Path outputDirectory = temp.newFolder().toPath();
+    jasminBuilder.writeClassFiles(outputDirectory);
+    ProcessResult javaResult = ToolHelper.runJava(outputDirectory, mainClassName);
+    assertEquals(0, javaResult.exitCode);
+
+    AndroidApp optimizedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
+        // Disable inlining to avoid the (short) tested method from being inlined then removed.
+        internalOptions -> internalOptions.enableInlining = false);
+
+    // Run optimized (output) program on ART
+    String artResult = runOnArt(optimizedApp, mainClassName);
+    assertEquals(javaResult.stdout, artResult);
+
+    DexInspector inspector = new DexInspector(optimizedApp);
+
+    ClassSubject classSubject = inspector.clazz(superClass.name);
+    assertThat(classSubject, isPresent());
+    MethodSubject methodSubject = classSubject
+        .method("java.lang.String", "method", Collections.emptyList());
+    assertThat(methodSubject, isPresent());
+
+    classSubject = inspector.clazz(subclass.name);
+    assertThat(classSubject, isPresent());
+    methodSubject = classSubject.method("java.lang.String", "getMethod", Collections.emptyList());
+    assertThat(methodSubject, isPresent());
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/cf/BaseDefaultMethodTest.java b/src/test/java/com/android/tools/r8/cf/BaseDefaultMethodTest.java
new file mode 100644
index 0000000..a0d6122
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/BaseDefaultMethodTest.java
@@ -0,0 +1,32 @@
+// 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.cf;
+
+public class BaseDefaultMethodTest {
+
+  public static final Class[] CLASSES = {
+      BaseDefaultMethodTest.class,
+      Base.class,
+      Derived.class,
+      Impl.class,
+  };
+
+  interface Base {
+    default void bar() {}
+  }
+
+  interface Derived extends Base {}
+
+  static class Impl implements Derived {}
+
+  static Derived foo() {
+    return new Impl();
+  }
+
+  public static void main(String[] args) {
+    Derived d = foo();
+    d.bar();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/BaseDefaultMethodTestRunner.java b/src/test/java/com/android/tools/r8/cf/BaseDefaultMethodTestRunner.java
new file mode 100644
index 0000000..414752f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/BaseDefaultMethodTestRunner.java
@@ -0,0 +1,54 @@
+// 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.cf;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class BaseDefaultMethodTestRunner {
+  static final Class CLASS = BaseDefaultMethodTest.class;
+  static final Class[] CLASSES = BaseDefaultMethodTest.CLASSES;
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Test
+  public void test() throws Exception {
+    ProcessResult runInput =
+        ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
+    assertEquals(0, runInput.exitCode);
+    List<String> config =
+        Arrays.asList(
+            "-keep public class " + CLASS.getCanonicalName() + " {",
+            "  public static void main(...);",
+            "}");
+    Path out = temp.getRoot().toPath().resolve("out.jar");
+    Builder builder =
+        R8Command.builder()
+            .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+            .setProgramConsumer(new ArchiveConsumer(out))
+            .addProguardConfiguration(config, Origin.unknown());
+    for (Class<?> c : CLASSES) {
+      builder.addClassProgramData(ToolHelper.getClassAsBytes(c), Origin.unknown());
+    }
+    // TODO(b/75997473): Enable inlining when supported
+    ToolHelper.runR8(builder.build(), options -> options.enableInlining = false);
+    ProcessResult runOutput = ToolHelper.runJava(out, CLASS.getCanonicalName());
+    assertEquals(runInput.toString(), runOutput.toString());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/UninitializedInFrameDump.java b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameDump.java
new file mode 100644
index 0000000..bac896e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameDump.java
@@ -0,0 +1,179 @@
+// 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.cf;
+
+import java.util.*;
+import org.objectweb.asm.*;
+
+public class UninitializedInFrameDump implements Opcodes {
+
+  public static byte[] dump() throws Exception {
+
+    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+    FieldVisitor fv;
+    MethodVisitor mv;
+    AnnotationVisitor av0;
+
+    cw.visit(
+        V1_8,
+        ACC_PUBLIC + ACC_SUPER,
+        "com/android/tools/r8/cf/UninitializedInFrameTest",
+        null,
+        "java/lang/Object",
+        null);
+
+    // The constructor UninitializedInFrameTest(int i) has been modified
+    // to add a jump back to the entry block.
+    {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(I)V", null, null);
+      mv.visitCode();
+      Label l = new Label(); // Added
+      mv.visitLabel(l); // Added
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ILOAD, 1);
+      mv.visitInsn(ICONST_1);
+      mv.visitInsn(ISUB);
+      mv.visitInsn(DUP); // Added
+      mv.visitIntInsn(BIPUSH, 42);
+      Label l0 = new Label();
+      mv.visitJumpInsn(IF_ICMPLT, l0);
+      // mv.visitInsn(ICONST_1);
+      mv.visitVarInsn(ISTORE, 1); // Added
+      mv.visitInsn(POP); // Added
+      mv.visitJumpInsn(GOTO, l); // Added
+      Label l1 = new Label();
+      mv.visitJumpInsn(GOTO, l1);
+      mv.visitLabel(l0);
+      mv.visitInsn(POP); // Added
+      mv.visitInsn(ICONST_0);
+      mv.visitLabel(l1);
+      mv.visitMethodInsn(
+          INVOKESPECIAL,
+          "com/android/tools/r8/cf/UninitializedInFrameTest",
+          "<init>",
+          "(Z)V",
+          false);
+      mv.visitInsn(RETURN);
+      mv.visitMaxs(-1, -1);
+      // mv.visitMaxs(3, 2);
+      mv.visitEnd();
+    }
+    {
+      mv = cw.visitMethod(ACC_PRIVATE, "<init>", "(Z)V", null, null);
+      mv.visitCode();
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+      mv.visitInsn(RETURN);
+      mv.visitMaxs(-1, -1);
+      // mv.visitMaxs(1, 2);
+      mv.visitEnd();
+    }
+    {
+      mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+      mv.visitCode();
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitInsn(ARRAYLENGTH);
+      Label l0 = new Label();
+      mv.visitJumpInsn(IFEQ, l0);
+      mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
+      mv.visitInsn(DUP);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitInsn(ARRAYLENGTH);
+      mv.visitIntInsn(BIPUSH, 42);
+      Label l1 = new Label();
+      mv.visitJumpInsn(IF_ICMPNE, l1);
+      // mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
+      // mv.visitInsn(DUP);
+      mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(
+          INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "()V", false);
+      mv.visitMethodInsn(
+          INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/Throwable;)V", false);
+      mv.visitVarInsn(ASTORE, 1);
+      // At this point, stack is empty.
+      Label l2 = new Label();
+      mv.visitJumpInsn(GOTO, l2);
+      mv.visitLabel(l1);
+      // At this point, stack contains two copies of uninitialized RuntimeException.
+      // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+      // mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
+      // mv.visitInsn(DUP);
+      mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
+      mv.visitLdcInsn("You supplied ");
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL,
+          "java/lang/StringBuilder",
+          "append",
+          "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+          false);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitInsn(ARRAYLENGTH);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL,
+          "java/lang/StringBuilder",
+          "append",
+          "(I)Ljava/lang/StringBuilder;",
+          false);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitInsn(ARRAYLENGTH);
+      mv.visitInsn(ICONST_1);
+      Label l3 = new Label();
+      mv.visitJumpInsn(IF_ICMPNE, l3);
+      mv.visitLdcInsn(" arg");
+      Label l4 = new Label();
+      mv.visitJumpInsn(GOTO, l4);
+      mv.visitLabel(l3);
+      // At this point, stack contains two copies of uninitialized RuntimeException.
+      // Note that asmifier seems to produce incorrect labels for the uninitialized type.
+      // mv.visitFrame(Opcodes.F_FULL, 1, new Object[] {"[Ljava/lang/String;"}, 3, new Object[] {l1,
+      // l1, "java/lang/StringBuilder"});
+      mv.visitLdcInsn(" args");
+      mv.visitLabel(l4);
+      // At this point, stack contains two copies of uninitialized RuntimeException.
+      // mv.visitFrame(Opcodes.F_FULL, 1, new Object[] {"[Ljava/lang/String;"}, 4, new Object[] {l1,
+      // l1, "java/lang/StringBuilder", "java/lang/String"});
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL,
+          "java/lang/StringBuilder",
+          "append",
+          "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+          false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
+      mv.visitMethodInsn(
+          INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V", false);
+      mv.visitVarInsn(ASTORE, 1);
+      mv.visitLabel(l2);
+      // At this point, stack is empty, and local 1 contains an initialized RuntimeException.
+      // mv.visitFrame(Opcodes.F_APPEND,1, new Object[] {"java/lang/RuntimeException"}, 0, null);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitInsn(ARRAYLENGTH);
+      mv.visitInsn(ICONST_2);
+      mv.visitInsn(IREM);
+      Label l5 = new Label();
+      mv.visitJumpInsn(IFNE, l5);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false);
+      mv.visitLabel(l5);
+      // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitInsn(ATHROW);
+      mv.visitLabel(l0);
+      // mv.visitFrame(Opcodes.F_CHOP,1, null, 0, null);
+      mv.visitInsn(RETURN);
+      mv.visitMaxs(-1, -1);
+      // mv.visitMaxs(5, 2);
+      mv.visitEnd();
+    }
+    cw.visitEnd();
+
+    return cw.toByteArray();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTest.java b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTest.java
new file mode 100644
index 0000000..4479796
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTest.java
@@ -0,0 +1,54 @@
+// 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.cf;
+
+public class UninitializedInFrameTest {
+  int v;
+
+  public UninitializedInFrameTest(int i) {
+    // In UninitializedInFrameDump, this method is changed to:
+    //     while (i-1 >= 42) {i = i-1;} this(i-1 >= 42);
+    // ...which is invalid in Java source since this() must be the first statement.
+    // Put "i-1 >= 42" in the code here to aid the manual editing in UninitializedInFrameDump.
+    this(i - 1 >= 42);
+  }
+
+  public UninitializedInFrameTest(boolean b) {
+    v = b ? 42 : 0;
+    System.out.println(this);
+    // Add an InvokeDirect that has 'this' as argument to ensure we don't consider it to be
+    // an initialization for 'this'.
+    if (!b) {
+      throw new AssertionError(this);
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "Hello world! " + v;
+  }
+
+  public static void main(String[] args) {
+    try {
+      new UninitializedInFrameTest(true);
+      new UninitializedInFrameTest(45);
+    } catch (AssertionError e) {
+    }
+    if (args.length != 0) {
+      RuntimeException e;
+      if (args.length == 42) {
+        e = new RuntimeException(new IllegalArgumentException());
+      } else {
+        e =
+            new RuntimeException(
+                "You supplied " + args.length + (args.length == 1 ? " arg" : " args"));
+      }
+      if (args.length % 2 == 0) {
+        System.out.println(e);
+      }
+      throw e;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTestRunner.java b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTestRunner.java
new file mode 100644
index 0000000..f433a9d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTestRunner.java
@@ -0,0 +1,64 @@
+// 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.cf;
+
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class UninitializedInFrameTestRunner {
+  static final Class CLASS = UninitializedInFrameTest.class;
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Test
+  public void test() throws Exception {
+    test(ToolHelper.getClassAsBytes(CLASS));
+  }
+
+  @Test
+  public void testDump() throws Exception {
+    test(UninitializedInFrameDump.dump());
+  }
+
+  private void test(byte[] clazz) throws CompilationFailedException, IOException {
+    Path input = temp.getRoot().toPath().resolve("input.jar");
+    Path output = temp.getRoot().toPath().resolve("output.jar");
+
+    ArchiveConsumer inputConsumer = new ArchiveConsumer(input);
+    inputConsumer.accept(clazz, DescriptorUtils.javaTypeToDescriptor(CLASS.getName()), null);
+    inputConsumer.finished(null);
+    ProcessResult runInput = ToolHelper.runJava(input, CLASS.getCanonicalName());
+    if (runInput.exitCode != 0) {
+      System.out.println(runInput);
+    }
+    Assert.assertEquals(0, runInput.exitCode);
+
+    R8.run(
+        R8Command.builder()
+            .setMode(CompilationMode.DEBUG)
+            .addClassProgramData(clazz, Origin.unknown())
+            .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+            .setProgramConsumer(new ArchiveConsumer(output))
+            .build());
+    ProcessResult runOutput = ToolHelper.runJava(output, CLASS.getCanonicalName());
+    if (runOutput.exitCode != 0) {
+      System.out.println(runOutput);
+    }
+    Assert.assertEquals(runInput.toString(), runOutput.toString());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/MinificationTest.java b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
index 414fbc3..2f995db 100644
--- a/src/test/java/com/android/tools/r8/debug/MinificationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
@@ -185,11 +185,6 @@
       String innerClassName,
       MethodSignature innerMethod)
       throws Throwable {
-    if (ToolHelper.isWindows()) {
-      // TODO(b/76135355): Update dx.bat on Windows to something that can build
-      // jdwp-tests-preN-dex.jar.
-      return;
-    }
     Path proguardMap = config.getProguardMap();
     String mappingFile = proguardMap == null ? null : proguardMap.toString();
     DexInspector inspector = new DexInspector(config.getPaths(), mappingFile);
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
index e07f319..d2cf708 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
@@ -4,8 +4,7 @@
 
 package com.android.tools.r8.desugaring.interfacemethods;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.AsmTestBase;
 import com.android.tools.r8.ToolHelper;
@@ -33,7 +32,7 @@
         ToolHelper.getMinApiLevelForDexVm(),
         ToolHelper.getClassAsBytes(
             com.android.tools.r8.desugaring.interfacemethods.test0.TestMain.class),
-        introduceInvokeSpecial(ToolHelper.getClassAsBytes(
+        patchInterfaceWithDefaults(ToolHelper.getClassAsBytes(
             com.android.tools.r8.desugaring.interfacemethods.test0.InterfaceWithDefaults.class)));
   }
 
@@ -47,16 +46,33 @@
         ToolHelper.getMinApiLevelForDexVm(),
         ToolHelper.getClassAsBytes(
             com.android.tools.r8.desugaring.interfacemethods.test1.TestMain.class),
-        introduceInvokeSpecial(ToolHelper.getClassAsBytes(
+        patchInterfaceWithDefaults(ToolHelper.getClassAsBytes(
             com.android.tools.r8.desugaring.interfacemethods.test1.InterfaceWithDefaults.class)));
   }
 
-  private class MutableBoolean {
-    boolean value;
+  @Test
+  public void testInvokeSpecialToInheritedDefaultMethod() throws Exception {
+    ensureSameOutput(
+        com.android.tools.r8.desugaring.interfacemethods.test2.TestMain.class.getCanonicalName(),
+        ToolHelper.getMinApiLevelForDexVm(),
+        ToolHelper.getClassAsBytes(
+            com.android.tools.r8.desugaring.interfacemethods.test2.TestMain.class),
+        ToolHelper.getClassAsBytes(
+            com.android.tools.r8.desugaring.interfacemethods.test2.Test.class),
+        ToolHelper.getClassAsBytes(
+            com.android.tools.r8.desugaring.interfacemethods.test2.LeftTest.class),
+        ToolHelper.getClassAsBytes(
+            com.android.tools.r8.desugaring.interfacemethods.test2.RightTest.class),
+        ToolHelper.getClassAsBytes(
+            com.android.tools.r8.desugaring.interfacemethods.test2.Test2.class));
   }
 
-  private byte[] introduceInvokeSpecial(byte[] classBytes) throws IOException {
-    MutableBoolean patched = new MutableBoolean();
+  private static class MutableInteger {
+    int value;
+  }
+
+  private byte[] patchInterfaceWithDefaults(byte[] classBytes) throws IOException {
+    MutableInteger patched = new MutableInteger();
     try (InputStream input = new ByteArrayInputStream(classBytes)) {
       ClassReader cr = new ClassReader(input);
       ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
@@ -73,9 +89,9 @@
                   if (opcode == Opcodes.INVOKEINTERFACE &&
                       owner.endsWith("InterfaceWithDefaults") &&
                       name.equals("foo")) {
-                    assertFalse(patched.value);
+                    assertEquals(0, patched.value);
                     super.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, itf);
-                    patched.value = true;
+                    patched.value++;
 
                   } else {
                     super.visitMethodInsn(opcode, owner, name, desc, itf);
@@ -84,7 +100,7 @@
               };
             }
           }, 0);
-      assertTrue(patched.value);
+      assertEquals(1, patched.value);
       return cw.toByteArray();
     }
   }
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/LeftTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/LeftTest.java
new file mode 100644
index 0000000..4a5b2de
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/LeftTest.java
@@ -0,0 +1,8 @@
+// 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.desugaring.interfacemethods.test2;
+
+public interface LeftTest extends Test {
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/RightTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/RightTest.java
new file mode 100644
index 0000000..6c6ea7e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/RightTest.java
@@ -0,0 +1,8 @@
+// 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.desugaring.interfacemethods.test2;
+
+public interface RightTest extends Test {
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test.java
new file mode 100644
index 0000000..1bf3295
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test.java
@@ -0,0 +1,11 @@
+// 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.desugaring.interfacemethods.test2;
+
+public interface Test {
+  default String foo(String a) {
+    return "Test::foo(" + a + ")";
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test2.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test2.java
new file mode 100644
index 0000000..b076cde
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test2.java
@@ -0,0 +1,11 @@
+// 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.desugaring.interfacemethods.test2;
+
+public interface Test2 extends LeftTest, RightTest {
+  default String bar(String a) {
+    return "Test2::bar(" + LeftTest.super.foo(a) + " + " + RightTest.super.foo(a) + ")";
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/TestMain.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/TestMain.java
new file mode 100644
index 0000000..ca86963
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/TestMain.java
@@ -0,0 +1,19 @@
+// 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.desugaring.interfacemethods.test2;
+
+public class TestMain implements Test2 {
+  public static void main(String... args) {
+    TestMain m = new TestMain();
+    System.out.println(m.bar("first"));
+    System.out.println(m.foo("second"));
+    System.out.println(m.fooDelegate("third"));
+  }
+
+  private String fooDelegate(String a) {
+    return "TestMain::fooDelegate(" + Test2.super.foo(a) + ")";
+  }
+}
+
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
index 19a5ee2..cf87d46 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -89,9 +89,9 @@
 
     if (useOptions) {
       Options options = new Options();
-      options.inputArchives.add(inputZip.toString());
-      options.featureSplitMapping = splitSpec.toString();
-      options.splitBaseName = output.toString();
+      options.addInputArchive(inputZip.toString());
+      options.setFeatureSplitMapping(splitSpec.toString());
+      options.setSplitBaseName(output.toString());
       DexSplitter.run(options);
     } else {
       DexSplitter.main(
@@ -235,12 +235,12 @@
     featureStream.close();
     if (useOptions) {
       Options options = new Options();
-      options.inputArchives.add(inputZip.toString());
-      options.splitBaseName = output.toString();
+      options.addInputArchive(inputZip.toString());
+      options.setSplitBaseName(output.toString());
       if (explicitBase) {
-        options.featureJars.add(baseJar.toString());
+        options.addFeatureJar(baseJar.toString());
       }
-      options.featureJars.add(featureJar.toString());
+      options.addFeatureJar(featureJar.toString());
       DexSplitter.run(options);
     } else {
       List<String> args = Lists.newArrayList(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
index 646cfaf..b6109d4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
@@ -185,6 +185,6 @@
     buildAndTest(NonNullAfterNullCheck.class, bar, 1, this::checkInvokeGetsNonNullReceiver);
     MethodSignature baz =
         new MethodSignature("baz", "int", new String[]{"java.lang.String"});
-    buildAndTest(NonNullAfterNullCheck.class, baz, 1, this::checkInvokeGetsNullReceiver);
+    buildAndTest(NonNullAfterNullCheck.class, baz, 2, this::checkInvokeGetsNullReceiver);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
index 92e43d0..525e21b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
@@ -30,8 +30,8 @@
 
   private void buildAndTest(Class<?> testClass, List<MethodSignature> signatures) throws Exception {
     AndroidApp app = buildAndroidApp(ToolHelper.getClassAsBytes(testClass));
-    AndroidApp r8Result = compileWithR8(
-        app, keepMainProguardConfiguration(testClass), o -> o.enableInlining = false);
+    AndroidApp r8Result = compileWithR8(app,
+        "-keep class " + testClass.getCanonicalName() + " { *; }");
     DexInspector dexInspector = new DexInspector(r8Result);
     for (MethodSignature signature : signatures) {
       DexEncodedMethod method =
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java b/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java
new file mode 100644
index 0000000..440f4f3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java
@@ -0,0 +1,820 @@
+// 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.regalloc;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import java.util.List;
+import java.util.Map;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class B77240639 extends TestBase {
+  @Ignore("b/77240639")
+  @Test
+  public void test() throws Exception {
+    AndroidApp app = compileWithD8(readClasses(TestClass.class));
+    DexInspector inspector = new DexInspector(app);
+    ClassSubject clazz = inspector.clazz(TestClass.class);
+    assertThat(clazz, isPresent());
+  }
+}
+
+class TestClass {
+  private boolean b;
+  private boolean b_flag = false;
+  private Boolean ob;
+  private boolean ob_flag = false;
+  private List<Boolean> vb;
+  private boolean vb_flag = false;
+  private List<Boolean> vob;
+  private boolean vob_flag = false;
+  private Map<String, Boolean> db;
+  private boolean db_flag = false;
+  private Map<String, Boolean> dob;
+  private boolean dob_flag = false;
+  private int i;
+  private boolean i_flag = false;
+  private Integer oi;
+  private boolean oi_flag = false;
+  private List<Boolean> vi;
+  private boolean vi_flag = false;
+  private List<Boolean> voi;
+  private boolean voi_flag = false;
+  private Map<String, Boolean> di;
+  private boolean di_flag = false;
+  private Map<String, Boolean> doi;
+  private boolean doi_flag = false;
+  private int ui;
+  private boolean ui_flag = false;
+  private Integer oui;
+  private boolean oui_flag = false;
+  private List<Boolean> vui;
+  private boolean vui_flag = false;
+  private List<Boolean> voui;
+  private boolean voui_flag = false;
+  private Map<String, Boolean> dui;
+  private boolean dui_flag = false;
+  private Map<String, Boolean> doui;
+  private boolean doui_flag = false;
+  private long i64;
+  private boolean i64_flag = false;
+  private Long oi64;
+  private boolean oi64_flag = false;
+  private List<Boolean> vi64;
+  private boolean vi64_flag = false;
+  private List<Boolean> voi64;
+  private boolean voi64_flag = false;
+  private Map<String, Boolean> di64;
+  private boolean di64_flag = false;
+  private Map<String, Boolean> doi64;
+  private boolean doi64_flag = false;
+  private float fl;
+  private boolean fl_flag = false;
+  private Float ofl;
+  private boolean ofl_flag = false;
+  private List<Boolean> vfl;
+  private boolean vfl_flag = false;
+  private List<Boolean> vofl;
+  private boolean vofl_flag = false;
+  private Map<String, Boolean> dfl;
+  private boolean dfl_flag = false;
+  private Map<String, Boolean> dofl;
+  private boolean dofl_flag = false;
+  private double d;
+  private boolean d_flag = false;
+  private Double od;
+  private boolean od_flag = false;
+  private List<Boolean> vd;
+  private boolean vd_flag = false;
+  private List<Boolean> vod;
+  private boolean vod_flag = false;
+  private Map<String, Boolean> dd;
+  private boolean dd_flag = false;
+  private Map<String, Boolean> dod;
+  private boolean dod_flag = false;
+  private String s;
+  private boolean s_flag = false;
+  private String os;
+  private boolean os_flag = false;
+  private List<Boolean> vs;
+  private boolean vs_flag = false;
+  private List<Boolean> vos;
+  private boolean vos_flag = false;
+  private Map<String, Boolean> ds;
+  private boolean ds_flag = false;
+  private Map<String, Boolean> dos;
+  private boolean dos_flag = false;
+  private long ti;
+  private boolean ti_flag = false;
+  private Long oti;
+  private boolean oti_flag = false;
+  private List<Boolean> vti;
+  private boolean vti_flag = false;
+  private List<Boolean> voti;
+  private boolean voti_flag = false;
+  private Map<String, Boolean> dti;
+  private boolean dti_flag = false;
+  private Map<String, Boolean> doti;
+  private boolean doti_flag = false;
+  private long at;
+  private boolean at_flag = false;
+  private Long oat;
+  private boolean oat_flag = false;
+  private List<Boolean> vat;
+  private boolean vat_flag = false;
+  private List<Boolean> voat;
+  private boolean voat_flag = false;
+  private Map<String, Boolean> dat;
+  private boolean dat_flag = false;
+  private Map<String, Boolean> doat;
+  private boolean doat_flag = false;
+  private long rt;
+  private boolean rt_flag = false;
+  private Long ort;
+  private boolean ort_flag = false;
+  private List<Boolean> vrt;
+  private boolean vrt_flag = false;
+  private List<Boolean> vort;
+  private boolean vort_flag = false;
+  private Map<String, Boolean> drt;
+  private boolean drt_flag = false;
+  private Map<String, Boolean> dort;
+  private boolean dort_flag = false;
+  private byte[] by;
+  private boolean by_flag = false;
+  private byte[] oby;
+  private boolean oby_flag = false;
+  private int c;
+  private boolean c_flag = false;
+  private Integer oc;
+  private boolean oc_flag = false;
+  private List<Integer> vc;
+  private boolean vc_flag = false;
+  private List<Integer> voc;
+  private boolean voc_flag = false;
+  private Map<String, Integer> dc;
+  private boolean dc_flag = false;
+  private Map<String, Integer> doc;
+  private boolean doc_flag = false;
+  private Object p;
+  private boolean p_flag = false;
+  private Object op;
+  private boolean op_flag = false;
+  private List<Object> vp;
+  private boolean vp_flag = false;
+  private List<Object> vop;
+  private boolean vop_flag = false;
+  private Map<String, Object> dp;
+  private boolean dp_flag = false;
+  private Map<String, Object> dop;
+  private boolean dop_flag = false;
+  private Object e;
+  private boolean e_flag = false;
+  private Object oe;
+  private boolean oe_flag = false;
+  private List<Object> ve;
+  private boolean ve_flag = false;
+  private List<Object> voe;
+  private boolean voe_flag = false;
+  private Map<String, Object> de;
+  private boolean de_flag = false;
+  private Map<String, Object> doe;
+  private boolean doe_flag = false;
+  private int be;
+  private boolean be_flag = false;
+  private Integer obe;
+  private boolean obe_flag = false;
+  private List<Object> vbe;
+  private boolean vbe_flag = false;
+  private List<Object> vobe;
+  private boolean vobe_flag = false;
+  private Map<String, Object> dbe;
+  private boolean dbe_flag = false;
+  private Map<String, Object> dobe;
+  private boolean dobe_flag = false;
+  private Object ts;
+  private boolean ts_flag = false;
+  private Object ots;
+  private boolean ots_flag = false;
+  private List<Object> vts;
+  private boolean vts_flag = false;
+  private List<Object> vots;
+  private boolean vots_flag = false;
+  private Map<String, Object> dts;
+  private boolean dts_flag = false;
+  private Map<String, Object> dots;
+  private boolean dots_flag = false;
+  private Object lts;
+  private boolean lts_flag = false;
+  private Object olts;
+  private boolean olts_flag = false;
+  private List<Object> vlts;
+  private boolean vlts_flag = false;
+  private List<Object> volts;
+  private boolean volts_flag = false;
+  private Map<String, Object> dlts;
+  private boolean dlts_flag = false;
+  private Map<String, Object> dolts;
+  private boolean dolts_flag = false;
+  private Object opts;
+  private boolean opts_flag = false;
+  private Object oopts;
+  private boolean oopts_flag = false;
+  private List<Object> vopts;
+  private boolean vopts_flag = false;
+  private List<Object> voopts;
+  private boolean voopts_flag = false;
+  private Map<String, Object> dopts;
+  private boolean dopts_flag = false;
+  private Map<String, Object> doopts;
+  private boolean doopts_flag = false;
+  private Object nativeObject;
+
+  public TestClass() {}
+
+  public TestClass(
+      boolean b,
+      Boolean ob,
+      List<Boolean> vb,
+      List<Boolean> vob,
+      Map<String, Boolean> db,
+      Map<String, Boolean> dob,
+      int i,
+      Integer oi,
+      List<Boolean> vi,
+      List<Boolean> voi,
+      Map<String, Boolean> di,
+      Map<String, Boolean> doi,
+      int ui,
+      Integer oui,
+      List<Boolean> vui,
+      List<Boolean> voui,
+      Map<String, Boolean> dui,
+      Map<String, Boolean> doui,
+      long i64,
+      Long oi64,
+      List<Boolean> vi64,
+      List<Boolean> voi64,
+      Map<String, Boolean> di64,
+      Map<String, Boolean> doi64,
+      float fl,
+      Float ofl,
+      List<Boolean> vfl,
+      List<Boolean> vofl,
+      Map<String, Boolean> dfl,
+      Map<String, Boolean> dofl,
+      double d,
+      Double od,
+      List<Boolean> vd,
+      List<Boolean> vod,
+      Map<String, Boolean> dd,
+      Map<String, Boolean> dod,
+      String s,
+      String os,
+      List<Boolean> vs,
+      List<Boolean> vos,
+      Map<String, Boolean> ds,
+      Map<String, Boolean> dos,
+      long ti,
+      Long oti,
+      List<Boolean> vti,
+      List<Boolean> voti,
+      Map<String, Boolean> dti,
+      Map<String, Boolean> doti,
+      long at,
+      Long oat,
+      List<Boolean> vat,
+      List<Boolean> voat,
+      Map<String, Boolean> dat,
+      Map<String, Boolean> doat,
+      long rt,
+      Long ort,
+      List<Boolean> vrt,
+      List<Boolean> vort,
+      Map<String, Boolean> drt,
+      Map<String, Boolean> dort,
+      byte[] by,
+      byte[] oby,
+      int c,
+      Integer oc,
+      List<Integer> vc,
+      List<Integer> voc,
+      Map<String, Integer> dc,
+      Map<String, Integer> doc,
+      Object p,
+      Object op,
+      List<Object> vp,
+      List<Object> vop,
+      Map<String, Object> dp,
+      Map<String, Object> dop,
+      Object e,
+      Object oe,
+      List<Object> ve,
+      List<Object> voe,
+      Map<String, Object> de,
+      Map<String, Object> doe,
+      int be,
+      Integer obe,
+      List<Object> vbe,
+      List<Object> vobe,
+      Map<String, Object> dbe,
+      Map<String, Object> dobe,
+      Object ts,
+      Object ots,
+      List<Object> vts,
+      List<Object> vots,
+      Map<String, Object> dts,
+      Map<String, Object> dots,
+      Object lts,
+      Object olts,
+      List<Object> vlts,
+      List<Object> volts,
+      Map<String, Object> dlts,
+      Map<String, Object> dolts,
+      Object opts,
+      Object oopts,
+      List<Object> vopts,
+      List<Object> voopts,
+      Map<String, Object> dopts,
+      Map<String, Object> doopts) {
+    if (vb == null) {
+      throw new IllegalArgumentException("vb");
+    } else if (vob == null) {
+      throw new IllegalArgumentException("vob");
+    } else if (db == null) {
+      throw new IllegalArgumentException("db");
+    } else if (dob == null) {
+      throw new IllegalArgumentException("dob");
+    } else if (vi == null) {
+      throw new IllegalArgumentException("vi");
+    } else if (voi == null) {
+      throw new IllegalArgumentException("voi");
+    } else if (di == null) {
+      throw new IllegalArgumentException("di");
+    } else if (doi == null) {
+      throw new IllegalArgumentException("doi");
+    } else if (vui == null) {
+      throw new IllegalArgumentException("vui");
+    } else if (voui == null) {
+      throw new IllegalArgumentException("voui");
+    } else if (dui == null) {
+      throw new IllegalArgumentException("dui");
+    } else if (doui == null) {
+      throw new IllegalArgumentException("doui");
+    } else if (vi64 == null) {
+      throw new IllegalArgumentException("vi64");
+    } else if (voi64 == null) {
+      throw new IllegalArgumentException("voi64");
+    } else if (di64 == null) {
+      throw new IllegalArgumentException("di64");
+    } else if (doi64 == null) {
+      throw new IllegalArgumentException("doi64");
+    } else if (vfl == null) {
+      throw new IllegalArgumentException("vfl");
+    } else if (vofl == null) {
+      throw new IllegalArgumentException("vofl");
+    } else if (dfl == null) {
+      throw new IllegalArgumentException("dfl");
+    } else if (dofl == null) {
+      throw new IllegalArgumentException("dofl");
+    } else if (vd == null) {
+      throw new IllegalArgumentException("vd");
+    } else if (vod == null) {
+      throw new IllegalArgumentException("vod");
+    } else if (dd == null) {
+      throw new IllegalArgumentException("dd");
+    } else if (dod == null) {
+      throw new IllegalArgumentException("dod");
+    } else if (s == null) {
+      throw new IllegalArgumentException("s");
+    } else if (vs == null) {
+      throw new IllegalArgumentException("vs");
+    } else if (vos == null) {
+      throw new IllegalArgumentException("vos");
+    } else if (ds == null) {
+      throw new IllegalArgumentException("ds");
+    } else if (dos == null) {
+      throw new IllegalArgumentException("dos");
+    } else if (vti == null) {
+      throw new IllegalArgumentException("vti");
+    } else if (voti == null) {
+      throw new IllegalArgumentException("voti");
+    } else if (dti == null) {
+      throw new IllegalArgumentException("dti");
+    } else if (doti == null) {
+      throw new IllegalArgumentException("doti");
+    } else if (vat == null) {
+      throw new IllegalArgumentException("vat");
+    } else if (voat == null) {
+      throw new IllegalArgumentException("voat");
+    } else if (dat == null) {
+      throw new IllegalArgumentException("dat");
+    } else if (doat == null) {
+      throw new IllegalArgumentException("doat");
+    } else if (vrt == null) {
+      throw new IllegalArgumentException("vrt");
+    } else if (vort == null) {
+      throw new IllegalArgumentException("vort");
+    } else if (drt == null) {
+      throw new IllegalArgumentException("drt");
+    } else if (dort == null) {
+      throw new IllegalArgumentException("dort");
+    } else if (by == null) {
+      throw new IllegalArgumentException("by");
+    } else if (vc == null) {
+      throw new IllegalArgumentException("vc");
+    } else if (voc == null) {
+      throw new IllegalArgumentException("voc");
+    } else if (dc == null) {
+      throw new IllegalArgumentException("dc");
+    } else if (doc == null) {
+      throw new IllegalArgumentException("doc");
+    } else if (p == null) {
+      throw new IllegalArgumentException("p");
+    } else if (vp == null) {
+      throw new IllegalArgumentException("vp");
+    } else if (vop == null) {
+      throw new IllegalArgumentException("vop");
+    } else if (dp == null) {
+      throw new IllegalArgumentException("dp");
+    } else if (dop == null) {
+      throw new IllegalArgumentException("dop");
+    } else if (e == null) {
+      throw new IllegalArgumentException("e");
+    } else if (ve == null) {
+      throw new IllegalArgumentException("ve");
+    } else if (voe == null) {
+      throw new IllegalArgumentException("voe");
+    } else if (de == null) {
+      throw new IllegalArgumentException("de");
+    } else if (doe == null) {
+      throw new IllegalArgumentException("doe");
+    } else if (vbe == null) {
+      throw new IllegalArgumentException("vbe");
+    } else if (vobe == null) {
+      throw new IllegalArgumentException("vobe");
+    } else if (dbe == null) {
+      throw new IllegalArgumentException("dbe");
+    } else if (dobe == null) {
+      throw new IllegalArgumentException("dobe");
+    } else if (ts == null) {
+      throw new IllegalArgumentException("ts");
+    } else if (vts == null) {
+      throw new IllegalArgumentException("vts");
+    } else if (vots == null) {
+      throw new IllegalArgumentException("vots");
+    } else if (dts == null) {
+      throw new IllegalArgumentException("dts");
+    } else if (dots == null) {
+      throw new IllegalArgumentException("dots");
+    } else if (lts == null) {
+      throw new IllegalArgumentException("lts");
+    } else if (vlts == null) {
+      throw new IllegalArgumentException("vlts");
+    } else if (volts == null) {
+      throw new IllegalArgumentException("volts");
+    } else if (dlts == null) {
+      throw new IllegalArgumentException("dlts");
+    } else if (dolts == null) {
+      throw new IllegalArgumentException("dolts");
+    } else if (opts == null) {
+      throw new IllegalArgumentException("opts");
+    } else if (vopts == null) {
+      throw new IllegalArgumentException("vopts");
+    } else if (voopts == null) {
+      throw new IllegalArgumentException("voopts");
+    } else if (dopts == null) {
+      throw new IllegalArgumentException("dopts");
+    } else if (doopts == null) {
+      throw new IllegalArgumentException("doopts");
+    } else {
+      this.nativeObject =
+          this.init(
+              b, ob, vb, vob, db, dob, i, oi, vi, voi, di, doi, ui, oui, vui, voui, dui, doui, i64,
+              oi64, vi64, voi64, di64, doi64, fl, ofl, vfl, vofl, dfl, dofl, d, od, vd, vod, dd,
+              dod, s, os, vs, vos, ds, dos, ti, oti, vti, voti, dti, doti, at, oat, vat, voat, dat,
+              doat, rt, ort, vrt, vort, drt, dort, by, oby, c, oc, vc, voc, dc, doc, p, op, vp, vop,
+              dp, dop, e, oe, ve, voe, de, doe, be, obe, vbe, vobe, dbe, dobe, ts, ots, vts, vots,
+              dts, dots, lts, olts, vlts, volts, dlts, dolts, opts, oopts, vopts, voopts, dopts,
+              doopts);
+      this.b = b;
+      this.b_flag = true;
+      this.ob = ob;
+      this.ob_flag = true;
+      this.vb = vb;
+      this.vb_flag = true;
+      this.vob = vob;
+      this.vob_flag = true;
+      this.db = db;
+      this.db_flag = true;
+      this.dob = dob;
+      this.dob_flag = true;
+      this.i = i;
+      this.i_flag = true;
+      this.oi = oi;
+      this.oi_flag = true;
+      this.vi = vi;
+      this.vi_flag = true;
+      this.voi = voi;
+      this.voi_flag = true;
+      this.di = di;
+      this.di_flag = true;
+      this.doi = doi;
+      this.doi_flag = true;
+      this.ui = ui;
+      this.ui_flag = true;
+      this.oui = oui;
+      this.oui_flag = true;
+      this.vui = vui;
+      this.vui_flag = true;
+      this.voui = voui;
+      this.voui_flag = true;
+      this.dui = dui;
+      this.dui_flag = true;
+      this.doui = doui;
+      this.doui_flag = true;
+      this.i64 = i64;
+      this.i64_flag = true;
+      this.oi64 = oi64;
+      this.oi64_flag = true;
+      this.vi64 = vi64;
+      this.vi64_flag = true;
+      this.voi64 = voi64;
+      this.voi64_flag = true;
+      this.di64 = di64;
+      this.di64_flag = true;
+      this.doi64 = doi64;
+      this.doi64_flag = true;
+      this.fl = fl;
+      this.fl_flag = true;
+      this.ofl = ofl;
+      this.ofl_flag = true;
+      this.vfl = vfl;
+      this.vfl_flag = true;
+      this.vofl = vofl;
+      this.vofl_flag = true;
+      this.dfl = dfl;
+      this.dfl_flag = true;
+      this.dofl = dofl;
+      this.dofl_flag = true;
+      this.d = d;
+      this.d_flag = true;
+      this.od = od;
+      this.od_flag = true;
+      this.vd = vd;
+      this.vd_flag = true;
+      this.vod = vod;
+      this.vod_flag = true;
+      this.dd = dd;
+      this.dd_flag = true;
+      this.dod = dod;
+      this.dod_flag = true;
+      this.s = s;
+      this.s_flag = true;
+      this.os = os;
+      this.os_flag = true;
+      this.vs = vs;
+      this.vs_flag = true;
+      this.vos = vos;
+      this.vos_flag = true;
+      this.ds = ds;
+      this.ds_flag = true;
+      this.dos = dos;
+      this.dos_flag = true;
+      this.ti = ti;
+      this.ti_flag = true;
+      this.oti = oti;
+      this.oti_flag = true;
+      this.vti = vti;
+      this.vti_flag = true;
+      this.voti = voti;
+      this.voti_flag = true;
+      this.dti = dti;
+      this.dti_flag = true;
+      this.doti = doti;
+      this.doti_flag = true;
+      this.at = at;
+      this.at_flag = true;
+      this.oat = oat;
+      this.oat_flag = true;
+      this.vat = vat;
+      this.vat_flag = true;
+      this.voat = voat;
+      this.voat_flag = true;
+      this.dat = dat;
+      this.dat_flag = true;
+      this.doat = doat;
+      this.doat_flag = true;
+      this.rt = rt;
+      this.rt_flag = true;
+      this.ort = ort;
+      this.ort_flag = true;
+      this.vrt = vrt;
+      this.vrt_flag = true;
+      this.vort = vort;
+      this.vort_flag = true;
+      this.drt = drt;
+      this.drt_flag = true;
+      this.dort = dort;
+      this.dort_flag = true;
+      this.by = by;
+      this.by_flag = true;
+      this.oby = oby;
+      this.oby_flag = true;
+      this.c = c;
+      this.c_flag = true;
+      this.oc = oc;
+      this.oc_flag = true;
+      this.vc = vc;
+      this.vc_flag = true;
+      this.voc = voc;
+      this.voc_flag = true;
+      this.dc = dc;
+      this.dc_flag = true;
+      this.doc = doc;
+      this.doc_flag = true;
+      this.p = p;
+      this.p_flag = true;
+      this.op = op;
+      this.op_flag = true;
+      this.vp = vp;
+      this.vp_flag = true;
+      this.vop = vop;
+      this.vop_flag = true;
+      this.dp = dp;
+      this.dp_flag = true;
+      this.dop = dop;
+      this.dop_flag = true;
+      this.e = e;
+      this.e_flag = true;
+      this.oe = oe;
+      this.oe_flag = true;
+      this.ve = ve;
+      this.ve_flag = true;
+      this.voe = voe;
+      this.voe_flag = true;
+      this.de = de;
+      this.de_flag = true;
+      this.doe = doe;
+      this.doe_flag = true;
+      this.be = be;
+      this.be_flag = true;
+      this.obe = obe;
+      this.obe_flag = true;
+      this.vbe = vbe;
+      this.vbe_flag = true;
+      this.vobe = vobe;
+      this.vobe_flag = true;
+      this.dbe = dbe;
+      this.dbe_flag = true;
+      this.dobe = dobe;
+      this.dobe_flag = true;
+      this.ts = ts;
+      this.ts_flag = true;
+      this.ots = ots;
+      this.ots_flag = true;
+      this.vts = vts;
+      this.vts_flag = true;
+      this.vots = vots;
+      this.vots_flag = true;
+      this.dts = dts;
+      this.dts_flag = true;
+      this.dots = dots;
+      this.dots_flag = true;
+      this.lts = lts;
+      this.lts_flag = true;
+      this.olts = olts;
+      this.olts_flag = true;
+      this.vlts = vlts;
+      this.vlts_flag = true;
+      this.volts = volts;
+      this.volts_flag = true;
+      this.dlts = dlts;
+      this.dlts_flag = true;
+      this.dolts = dolts;
+      this.dolts_flag = true;
+      this.opts = opts;
+      this.opts_flag = true;
+      this.oopts = oopts;
+      this.oopts_flag = true;
+      this.vopts = vopts;
+      this.vopts_flag = true;
+      this.voopts = voopts;
+      this.voopts_flag = true;
+      this.dopts = dopts;
+      this.dopts_flag = true;
+      this.doopts = doopts;
+      this.doopts_flag = true;
+    }
+  }
+
+  private native Object init(
+      boolean v1,
+      Boolean v2,
+      List<Boolean> v3,
+      List<Boolean> v4,
+      Map<String, Boolean> v5,
+      Map<String, Boolean> v6,
+      int v7, Integer var8,
+      List<Boolean> v9,
+      List<Boolean> v10,
+      Map<String, Boolean> v11,
+      Map<String, Boolean> v12,
+      int v13,
+      Integer v14,
+      List<Boolean> v15,
+      List<Boolean> v16,
+      Map<String, Boolean> v17,
+      Map<String, Boolean> v18,
+      long v19, Long var21,
+      List<Boolean> v22,
+      List<Boolean> v23,
+      Map<String, Boolean> v24,
+      Map<String, Boolean> v25,
+      float v26,
+      Float v27,
+      List<Boolean> v28,
+      List<Boolean> v29,
+      Map<String, Boolean> v30,
+      Map<String, Boolean> v31,
+      double v32,
+      Double v34,
+      List<Boolean> v35,
+      List<Boolean> v36,
+      Map<String, Boolean> v37,
+      Map<String, Boolean> v38,
+      String v39,
+      String v40,
+      List<Boolean> v41,
+      List<Boolean> v42,
+      Map<String, Boolean> v43,
+      Map<String, Boolean> v44,
+      long v45, Long var47,
+      List<Boolean> v48,
+      List<Boolean> v49,
+      Map<String, Boolean> v50,
+      Map<String, Boolean> v51,
+      long v52,
+      Long v54,
+      List<Boolean> v55,
+      List<Boolean> v56,
+      Map<String, Boolean> v57,
+      Map<String, Boolean> v58,
+      long v59,
+      Long v61,
+      List<Boolean> v62,
+      List<Boolean> v63,
+      Map<String, Boolean> v64,
+      Map<String, Boolean> v65,
+      byte[] v66,
+      byte[] v67,
+      int v68,
+      Integer v69,
+      List<Integer> v70,
+      List<Integer> v71,
+      Map<String, Integer> v72,
+      Map<String, Integer> v73,
+      Object v74,
+      Object v75,
+      List<Object> v76,
+      List<Object> v77,
+      Map<String, Object> v78,
+      Map<String, Object> v79,
+      Object v80,
+      Object v81,
+      List<Object> v82,
+      List<Object> v83,
+      Map<String, Object> v84,
+      Map<String, Object> v85,
+      int v86,
+      Integer v87,
+      List<Object> v88,
+      List<Object> v89,
+      Map<String, Object> v90,
+      Map<String, Object> v91,
+      Object v92,
+      Object v93,
+      List<Object> v94,
+      List<Object> v95,
+      Map<String, Object> v96,
+      Map<String, Object> v97,
+      Object v98,
+      Object v99,
+      List<Object> v100,
+      List<Object> v101,
+      Map<String, Object> v102,
+      Map<String, Object> v103,
+      Object v104,
+      Object v105,
+      List<Object> v106,
+      List<Object> v107,
+      Map<String, Object> v108,
+      Map<String, Object> v109);
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
index 64d092b..ff3f81a 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -120,6 +120,15 @@
       return addMethod("public", name, argumentTypes, returnType, lines);
     }
 
+    public MethodSignature addBridgeMethod(
+        String name,
+        List<String> argumentTypes,
+        String returnType,
+        String... lines) {
+      makeInit = true;
+      return addMethod("public bridge", name, argumentTypes, returnType, lines);
+    }
+
     public MethodSignature addPrivateVirtualMethod(
         String name,
         List<String> argumentTypes,
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 3e46ff8..93f4249 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -47,7 +47,7 @@
   // invoke the tested method.
   private static final String JASMIN_MAIN_CLASS = "TestMain";
 
-  @Parameters(name = "{0}_{1}")
+  @Parameters(name = "allowAccessModification: {0} target: {1}")
   public static Collection<Object[]> data() {
     ImmutableList.Builder<Object[]> builder = new ImmutableList.Builder<>();
     for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
@@ -171,9 +171,16 @@
     return proguardRules.toString();
   }
 
+  protected String keepAllMembers(String className) {
+    return "-keep class " + className + " {" + System.lineSeparator()
+        + "  *;" + System.lineSeparator()
+        + "}";
+  }
+
   protected String keepClassMethod(String className, MethodSignature methodSignature) {
-    return "-keep class " + className + " {" + System.lineSeparator() +
-        methodSignature.toString() + ";" + System.lineSeparator() + "}";
+    return "-keep class " + className + " {" + System.lineSeparator()
+        + methodSignature.toString() + ";" + System.lineSeparator()
+        + "}";
   }
 
   protected void runTest(String folder, String mainClass, AndroidAppInspector inspector)
@@ -199,7 +206,7 @@
     // Build with R8
     AndroidApp.Builder builder = AndroidApp.builder();
     builder.addProgramFiles(classpath);
-    AndroidApp app = compileWithR8(builder.build(), proguardRules.toString());
+    AndroidApp app = compileWithR8(builder.build(), proguardRules);
 
     // Materialize file for execution.
     Path generatedDexFile = temp.getRoot().toPath().resolve("classes.jar");
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
new file mode 100644
index 0000000..ab6e949
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -0,0 +1,82 @@
+// 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.kotlin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.code.Format21t;
+import com.android.tools.r8.code.Format22t;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import org.junit.Test;
+
+public class SimplifyIfNotNullKotlinTest extends AbstractR8KotlinTestBase {
+  private static final String FOLDER = "non_null";
+  private static final String STRING = "java.lang.String";
+
+  private static boolean isIf(Instruction instruction) {
+    return instruction instanceof Format21t || instruction instanceof Format22t;
+  }
+
+  @Test
+  public void test_example1() throws Exception {
+    final TestKotlinClass ex1 = new TestKotlinClass("non_null.Example1Kt");
+    final MethodSignature testMethodSignature =
+        new MethodSignature("forMakeAndModel", "java.util.SortedMap",
+            ImmutableList.of("java.util.Collection", STRING, STRING, "java.lang.Integer"));
+
+    final String mainClassName = ex1.getClassName();
+    final String extraRules = keepAllMembers(mainClassName);
+    runTest(FOLDER, mainClassName, extraRules, app -> {
+      DexInspector dexInspector = new DexInspector(app);
+      ClassSubject clazz = checkClassExists(dexInspector, ex1.getClassName());
+
+      MethodSubject testMethod = checkMethodIsPresent(clazz, testMethodSignature);
+      DexCode dexCode = getDexCode(testMethod);
+      long count = Arrays.stream(dexCode.instructions)
+          .filter(SimplifyIfNotNullKotlinTest::isIf).count();
+      if (allowAccessModification) {
+        // Three null-check's from inlined checkParameterIsNotNull for receiver and two arguments.
+        assertEquals(5, count);
+      } else {
+        // One after Iterator#hasNext, and another in the filter predicate: sinceYear != null.
+        assertEquals(2, count);
+      }
+    });
+  }
+
+  @Test
+  public void test_example2() throws Exception {
+    final TestKotlinClass ex2 = new TestKotlinClass("non_null.Example2Kt");
+    final MethodSignature testMethodSignature =
+        new MethodSignature("aOrDefault", STRING, ImmutableList.of(STRING, STRING));
+
+    final String mainClassName = ex2.getClassName();
+    final String extraRules = keepAllMembers(mainClassName);
+    runTest(FOLDER, mainClassName, extraRules, app -> {
+      DexInspector dexInspector = new DexInspector(app);
+      ClassSubject clazz = checkClassExists(dexInspector, ex2.getClassName());
+
+      MethodSubject testMethod = checkMethodIsPresent(clazz, testMethodSignature);
+      DexCode dexCode = getDexCode(testMethod);
+      long count = Arrays.stream(dexCode.instructions)
+          .filter(SimplifyIfNotNullKotlinTest::isIf).count();
+      if (allowAccessModification) {
+        // TODO(b/76202537): 3 -> 2,
+        //   Yet another null-check from checkParameterIsNotNull should subsume another from ?:
+        assertEquals(3, count);
+      } else {
+        // One null-check from force inlined coalesce and another from ?:
+        assertEquals(2, count);
+      }
+    });
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 35df33e..a6d0177 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -707,7 +707,8 @@
     }
 
     @Override
-    public void buildInstruction(IRBuilder builder, int instructionIndex) {
+    public void buildInstruction(
+        IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
       assert instructionIndex == 0;
       builder.addReturn();
     }
diff --git a/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTest.java b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTest.java
new file mode 100644
index 0000000..d42800a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTest.java
@@ -0,0 +1,79 @@
+// 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.naming;
+
+public class InterfaceRenamingTest {
+
+  public static final Class[] CLASSES = {
+    InterfaceRenamingTest.class,
+    InterfaceA.class,
+    InterfaceB.class,
+    ImplementationA1.class,
+    ImplementationB1.class,
+    ImplementationA2.class,
+    ImplementationB2.class,
+  };
+
+  // Since the names and parameter lists of the two methods in the two interfaces are equal,
+  // non-aggressive minification gives the methods the same minified name.
+  // However, the return types are different, so looking up the renamed name by prototype
+  // only gives a result for one of the two interfaces.
+  interface InterfaceA {
+    Boolean interfaceMethod();
+  }
+
+  interface InterfaceB {
+    Integer interfaceMethod();
+  }
+
+  // Two implementations of each interface are required
+  // to avoid the Devirtualizer hiding the buggy behavior.
+  static class ImplementationA1 implements InterfaceA {
+    @Override
+    public Boolean interfaceMethod() {
+      System.out.println("interfaceMethod1");
+      return true;
+    }
+  }
+
+  static class ImplementationA2 implements InterfaceA {
+    @Override
+    public Boolean interfaceMethod() {
+      System.out.println("interfaceMethod1 dummy");
+      return false;
+    }
+  }
+
+  static class ImplementationB1 implements InterfaceB {
+    @Override
+    public Integer interfaceMethod() {
+      System.out.println("interfaceMethod2");
+      return 10;
+    }
+  }
+
+  static class ImplementationB2 implements InterfaceB {
+    @Override
+    public Integer interfaceMethod() {
+      System.out.println("interfaceMethod2 dummy");
+      return 20;
+    }
+  }
+
+  public static void main(String[] args) {
+    invokeA(new ImplementationA1());
+    invokeB(new ImplementationB1());
+    invokeA(new ImplementationA2());
+    invokeB(new ImplementationB2());
+  }
+
+  private static void invokeA(InterfaceA instance) {
+    System.out.println("invokeA: " + instance.interfaceMethod());
+  }
+
+  private static void invokeB(InterfaceB instance) {
+    System.out.println("invokeB: " + instance.interfaceMethod());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
new file mode 100644
index 0000000..6c054e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
@@ -0,0 +1,111 @@
+// 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.naming;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+
+public class InterfaceRenamingTestRunner extends TestBase {
+  static final Class CLASS = InterfaceRenamingTest.class;
+  static final Class[] CLASSES = InterfaceRenamingTest.CLASSES;
+
+  @Test
+  public void testCfNoMinify() throws Exception {
+    testCf(MinifyMode.NONE);
+  }
+
+  @Test
+  public void testCfMinify() throws Exception {
+    testCf(MinifyMode.JAVA);
+  }
+
+  @Test
+  public void testCfMinifyAggressive() throws Exception {
+    testCf(MinifyMode.AGGRESSIVE);
+  }
+
+  @Test
+  public void testDexNoMinify() throws Exception {
+    testDex(MinifyMode.NONE);
+  }
+
+  @Test
+  public void testDexMinify() throws Exception {
+    testDex(MinifyMode.JAVA);
+  }
+
+  @Test
+  public void testDexMinifyAggressive() throws Exception {
+    testDex(MinifyMode.AGGRESSIVE);
+  }
+
+  private void testCf(MinifyMode minify) throws Exception {
+    ProcessResult runInput =
+        ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
+    assertEquals(0, runInput.exitCode);
+    Path outCf = temp.getRoot().toPath().resolve("cf.zip");
+    build(new ClassFileConsumer.ArchiveConsumer(outCf), minify);
+    ProcessResult runCf = ToolHelper.runJava(outCf, CLASS.getCanonicalName());
+    assertEquals(runInput.toString(), runCf.toString());
+  }
+
+  private void testDex(MinifyMode minify) throws Exception {
+    ProcessResult runInput =
+        ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
+    assertEquals(0, runInput.exitCode);
+    Path outDex = temp.getRoot().toPath().resolve("dex.zip");
+    build(new DexIndexedConsumer.ArchiveConsumer(outDex), minify);
+    ProcessResult runDex =
+        ToolHelper.runArtNoVerificationErrorsRaw(outDex.toString(), CLASS.getCanonicalName());
+    assertEquals(runInput.stdout, runDex.stdout);
+    assertEquals(runInput.exitCode, runDex.exitCode);
+  }
+
+  private void build(ProgramConsumer consumer, MinifyMode minify) throws Exception {
+    List<String> config =
+        Arrays.asList(
+            "-keep public class " + CLASS.getCanonicalName() + " {",
+            "  public static void main(...);",
+            "}");
+
+    Builder builder =
+        ToolHelper.addProguardConfigurationConsumer(
+                R8Command.builder(),
+                pgConfig -> {
+                  pgConfig.setPrintMapping(true);
+                  pgConfig.setOverloadAggressively(minify == MinifyMode.AGGRESSIVE);
+                  if (!minify.isMinify()) {
+                    pgConfig.disableObfuscation();
+                  }
+                })
+            .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+            .setProgramConsumer(consumer)
+            .addProguardConfiguration(config, Origin.unknown());
+    for (Class<?> c : CLASSES) {
+      builder.addClassProgramData(ToolHelper.getClassAsBytes(c), Origin.unknown());
+    }
+    if (consumer instanceof ClassFileConsumer) {
+      // TODO(b/75997473): Enable inlining when supported by CF backend
+      ToolHelper.runR8(builder.build(), options -> options.enableInlining = false);
+    } else {
+      ToolHelper.runR8(builder.build());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index 5b1e7e3..b60ede7 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -30,6 +30,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
 import java.util.function.BiConsumer;
 import org.junit.Before;
 
@@ -74,10 +75,11 @@
       ClassAndMemberPublicizer.run(program, dexItemFactory);
     }
 
-    RootSet rootSet = new RootSetBuilder(program, appInfo, configuration.getRules(), options)
-        .run(ThreadUtils.getExecutorService(options));
+    ExecutorService executor = ThreadUtils.getExecutorService(1);
+    RootSet rootSet = new RootSetBuilder(appInfo, program, configuration.getRules(), options)
+        .run(executor);
     Enqueuer enqueuer = new Enqueuer(appInfo, options, options.forceProguardCompatibility);
-    appInfo = enqueuer.traceApplication(rootSet, timing);
+    appInfo = enqueuer.traceApplication(rootSet, executor, timing);
     return new Minifier(appInfo.withLiveness(), rootSet, options).run(timing);
   }
 
diff --git a/src/test/java/com/android/tools/r8/regress/B76025099.java b/src/test/java/com/android/tools/r8/regress/B76025099.java
new file mode 100644
index 0000000..767420b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/B76025099.java
@@ -0,0 +1,128 @@
+// 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.regress;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.OutputMode;
+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.InvokeDirect;
+import com.android.tools.r8.code.IputObject;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import regress_76025099.Main;
+import regress_76025099.impl.Impl;
+
+@RunWith(VmTestRunner.class)
+public class B76025099 extends TestBase {
+  private static final String PRG =
+      ToolHelper.EXAMPLES_BUILD_DIR + "regress_76025099" + FileUtils.JAR_EXTENSION;
+
+  private AndroidApp runR8(AndroidApp app) throws Exception {
+     R8Command command =
+        ToolHelper.addProguardConfigurationConsumer(
+            ToolHelper.prepareR8CommandBuilder(app),
+            pgConfig -> {
+              pgConfig.setPrintMapping(true);
+              pgConfig.setPrintMappingFile(map);
+            })
+        .addProguardConfigurationFiles(pgConfig)
+        .setOutput(tempRoot.toPath(), OutputMode.DexIndexed)
+        .build();
+    return ToolHelper.runR8(command, o -> {
+      o.enableMinification = false;
+    });
+  }
+
+  private File tempRoot;
+  private Path jarPath;
+  private AndroidApp originalApp;
+  private String mainName;
+  private Path pgConfig;
+  private Path map;
+
+  @Before
+  public void setUp() throws Exception {
+    tempRoot = temp.getRoot();
+    jarPath = Paths.get(PRG);
+    originalApp = readJar(jarPath);
+    mainName = Main.class.getCanonicalName();
+    pgConfig = File.createTempFile("keep-rules", ".config", tempRoot).toPath();
+    String config = keepMainProguardConfiguration(Main.class);
+    config += System.lineSeparator() + "-dontobfuscate";
+    Files.write(pgConfig, config.getBytes());
+    map = File.createTempFile("proguard", ".map", tempRoot).toPath();
+  }
+
+  @Test
+  public void testProguardAndD8() throws Exception {
+    if (!isRunProguard()) {
+      return;
+    }
+
+    ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
+    assertEquals(0, jvmOutput.exitCode);
+
+    Path proguarded =
+        File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, tempRoot).toPath();
+    ProcessResult proguardResult = ToolHelper.runProguardRaw(jarPath, proguarded, pgConfig, map);
+    assertEquals(0, proguardResult.exitCode);
+
+    AndroidApp processedApp = ToolHelper.runD8(readJar(proguarded));
+    verifyFieldAccess(processedApp, jvmOutput);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
+    assertEquals(0, jvmOutput.exitCode);
+
+    AndroidApp processedApp = runR8(originalApp);
+    verifyFieldAccess(processedApp, jvmOutput);
+  }
+
+  private void verifyFieldAccess(AndroidApp processedApp, ProcessResult jvmOutput)
+      throws Exception {
+    DexInspector inspector = new DexInspector(processedApp);
+    ClassSubject impl = inspector.clazz(Impl.class);
+    assertThat(impl, isPresent());
+    MethodSubject init = impl.init(ImmutableList.of("java.lang.String"));
+    assertThat(init, isPresent());
+    DexCode dexCode = init.getMethod().getCode().asDexCode();
+    checkInstructions(dexCode, ImmutableList.of(
+        InvokeDirect.class,
+        IputObject.class,
+        ReturnVoid.class
+    ));
+    IputObject iput = (IputObject) dexCode.instructions[1];
+    DexField fld = iput.getField();
+    assertEquals("name", fld.name.toString());
+    assertEquals("regress_76025099.impl.Impl", fld.getHolder().toSourceString());
+
+    ProcessResult artOutput = runOnArtRaw(processedApp, mainName);
+    assertEquals(0, artOutput.exitCode);
+    assertEquals(jvmOutput.stdout, artOutput.stdout);
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java
new file mode 100644
index 0000000..7733339
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java
@@ -0,0 +1,70 @@
+// 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.resolution;
+
+public class PublicFieldInnerClassTest {
+  public static final Class<?>[] CLASSES = {
+      PrivateBase.class,
+      PrivateSubclass.class,
+      PackageBase.class,
+      PackageSubclass.class,
+      ProtectedBase.class,
+      ProtectedSubclass.class,
+      PublicBase.class,
+      PublicSubclass.class,
+      PublicFieldInnerClassTest.class
+  };
+
+  private static class PrivateBase {
+    public int value;
+  }
+
+  private static class PrivateSubclass extends PrivateBase {
+  }
+
+  static class PackageBase {
+    public int value;
+  }
+
+  private static class PackageSubclass extends PackageBase {
+  }
+
+  protected static class ProtectedBase {
+    public int value;
+  }
+
+  private static class ProtectedSubclass extends ProtectedBase {
+  }
+
+  public static class PublicBase {
+    public int value;
+  }
+
+  private static class PublicSubclass extends PublicBase {
+  }
+
+  private static int getPrivate(PrivateSubclass instance) {
+    return instance.value;
+  }
+
+  private static int getPackage(PackageSubclass instance) {
+    return instance.value;
+  }
+
+  private static int getProtected(ProtectedSubclass instance) {
+    return instance.value;
+  }
+
+  private static int getPublic(PublicSubclass instance) {
+    return instance.value;
+  }
+
+  public static void main(String[] args) {
+    System.out.println(getPrivate(new PrivateSubclass()));
+    System.out.println(getPackage(new PackageSubclass()));
+    System.out.println(getProtected(new ProtectedSubclass()));
+    System.out.println(getPublic(new PublicSubclass()));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
new file mode 100644
index 0000000..6642352
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
@@ -0,0 +1,78 @@
+// 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.resolution;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+
+public class PublicFieldInnerClassTestRunner extends TestBase {
+  static final Class CLASS = PublicFieldInnerClassTest.class;
+  static final Class<?>[] CLASSES = PublicFieldInnerClassTest.CLASSES;
+
+  @Test
+  public void testCf() throws Exception {
+    ProcessResult runInput =
+        ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
+    assertEquals(0, runInput.exitCode);
+    Path outCf = temp.getRoot().toPath().resolve("cf.jar");
+    build(new ClassFileConsumer.ArchiveConsumer(outCf));
+    ProcessResult runCf = ToolHelper.runJava(outCf, CLASS.getCanonicalName());
+    assertEquals(runInput.toString(), runCf.toString());
+    assertEquals(
+        -1,
+        runCf.stderr.indexOf("java.lang.NoSuchFieldError"));
+  }
+
+  @Test
+  public void testDex() throws Exception {
+    ProcessResult runInput =
+        ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
+    assertEquals(0, runInput.exitCode);
+    Path outDex = temp.getRoot().toPath().resolve("dex.zip");
+    build(new DexIndexedConsumer.ArchiveConsumer(outDex));
+    ProcessResult runDex = ToolHelper.runArtNoVerificationErrorsRaw(
+        outDex.toString(), CLASS.getCanonicalName());
+    assertEquals(runInput.stdout, runDex.stdout);
+    assertEquals(runInput.exitCode, runDex.exitCode);
+    assertEquals(
+        -1,
+        runDex.stderr.indexOf("java.lang.NoSuchFieldError"));
+  }
+
+  private void build(ProgramConsumer consumer) throws Exception {
+    List<String> config = Arrays.asList(
+        "-keep public class " + CLASS.getCanonicalName() + " {",
+        "  public static void main(...);",
+        "}"
+    );
+    Builder builder = R8Command.builder()
+        .setMode(CompilationMode.DEBUG)
+        .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+        .setProgramConsumer(consumer)
+        .addProguardConfiguration(config, Origin.unknown());
+    for (Class<?> c : CLASSES) {
+      builder.addClassProgramData(ToolHelper.getClassAsBytes(c), Origin.unknown());
+    }
+    if (consumer instanceof ClassFileConsumer) {
+      // TODO(b/75997473): Enable inlining when supported by CF.
+      ToolHelper.runR8(builder.build(), options -> options.enableInlining = false);
+    } else {
+      ToolHelper.runR8(builder.build());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index c483267..1631a1f 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -42,11 +42,13 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.stream.Collectors;
 import org.junit.Assert;
@@ -103,11 +105,11 @@
     DexApplication application = new ApplicationReader(app, options, timing).read().toDirect();
     AppInfoWithSubtyping appInfoWithSubtyping = new AppInfoWithSubtyping(application);
 
-    RootSet rootSet = new RootSetBuilder(application, appInfoWithSubtyping,
-        buildKeepRuleForClass(Main.class, application.dexItemFactory), options).run(
-        Executors.newSingleThreadExecutor());
+    ExecutorService executor = Executors.newSingleThreadExecutor();
+    RootSet rootSet = new RootSetBuilder(appInfoWithSubtyping, application,
+        buildKeepRuleForClass(Main.class, application.dexItemFactory), options).run(executor);
     appInfo = new Enqueuer(appInfoWithSubtyping, options, options.forceProguardCompatibility)
-        .traceApplication(rootSet, timing);
+        .traceApplication(rootSet, executor, timing);
     // We do not run the tree pruner to ensure that the hierarchy is as designed and not modified
     // due to liveness.
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index f9e0422..5dc4ec6 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -1163,10 +1163,7 @@
     ProguardConfigurationParser parser =
         new ProguardConfigurationParser(new DexItemFactory(), reporter);
     parser.parse(proguardConfig);
-    assertEquals(3, handler.warnings.size());
-    for (int i = 0; i < 3; i++) {
-      assertTrue(handler.warnings.get(i).getDiagnosticMessage().contains("Ignoring option: -if"));
-    }
+    verifyParserEndsCleanly();
     ProguardConfiguration config = parser.getConfig();
     // Three -if rules and one independent -keepnames
     assertEquals(4, config.getRules().size());
@@ -1196,8 +1193,7 @@
     ProguardConfigurationParser parser =
         new ProguardConfigurationParser(new DexItemFactory(), reporter);
     parser.parse(proguardConfig);
-    checkDiagnostic(handler.warnings, proguardConfig, 1, 1,
-        "Ignoring", "-if");
+    verifyParserEndsCleanly();
     ProguardConfiguration config = parser.getConfig();
     assertEquals(1, config.getRules().size());
     ProguardIfRule if0 = (ProguardIfRule) config.getRules().get(0);
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
index 004c31f..3976f89 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
@@ -1,9 +1,12 @@
 // 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.shaking.forceproguardcompatibility;
 
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+
 import com.android.tools.r8.CompatProguardCommandBuilder;
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.R8Command;
@@ -12,6 +15,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.FileUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.File;
@@ -113,4 +117,20 @@
       throws Exception {
     return runProguard6AndD8(programClasses, keepMainProguardConfiguration(mainClass));
   }
+
+  protected void verifyClassesPresent(
+      DexInspector dexInspector, Class<?>... classesOfInterest) {
+    for (Class klass : classesOfInterest) {
+      ClassSubject c = dexInspector.clazz(klass);
+      assertThat(c, isPresent());
+    }
+  }
+
+  protected void verifyClassesAbsent(
+      DexInspector dexInspector, Class<?>... classesOfInterest) {
+    for (Class klass : classesOfInterest) {
+      ClassSubject c = dexInspector.clazz(klass);
+      assertThat(c, not(isPresent()));
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTest.java
new file mode 100644
index 0000000..080df2a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTest.java
@@ -0,0 +1,91 @@
+// 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.shaking.ifrule;
+
+import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
+import com.android.tools.r8.utils.DexInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class IfOnAnnotationTest extends ProguardCompatabilityTestBase {
+  private final static List<Class> CLASSES = ImmutableList.of(
+      UsedAnnotation.class, UnusedAnnotation.class,
+      UsedAnnotationDependent.class, UnusedAnnotationDependent.class,
+      AnnotationUser.class, MainUsesAnnotationUser.class);
+
+  private final Shrinker shrinker;
+
+  public IfOnAnnotationTest(Shrinker shrinker) {
+    this.shrinker = shrinker;
+  }
+
+  @Parameters(name = "shrinker: {0}")
+  public static Collection<Object> data() {
+    return ImmutableList.of(Shrinker.PROGUARD6, Shrinker.R8);
+  }
+
+  @Test
+  public void ifOnAnnotation_withoutNthWildcard() throws Exception {
+    List<String> config = ImmutableList.of(
+        "-keepattributes *Annotation*",
+        "-keep class **.Main* {",
+        "  public static void main(java.lang.String[]);",
+        "}",
+        // @UsedAnnotation <methods> -> UsedAnnotationDependent
+        "-if class **.*User {",
+        "  @**.UsedAnnotation <methods>;",
+        "}",
+        "-keep class **.UsedAnnotation*",
+        // @UnusedAnnotation <methods> -> UnusedAnnotationDependent
+        "-if class **.*User {",
+        "  @**.UnusedAnnotation <methods>;",
+        "}",
+        "-keep class **.UnusedAnnotation*"
+    );
+
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    verifyClassesAbsent(dexInspector,
+        UnusedAnnotation.class, UnusedAnnotationDependent.class);
+    verifyClassesPresent(dexInspector,
+        UsedAnnotation.class, UsedAnnotationDependent.class);
+  }
+
+  @Test
+  public void ifOnAnnotation_withNthWildcard() throws Exception {
+    // TODO(b/73800755): not implemented yet.
+    if (shrinker == Shrinker.R8) {
+      return;
+    }
+
+    List<String> config = ImmutableList.of(
+        "-keepattributes *Annotation*",
+        "-keep class **.Main* {",
+        "  public static void main(java.lang.String[]);",
+        "}",
+        // @UsedAnnotation <methods> -> UsedAnnotationDependent
+        "-if class **.*User {",
+        "  @<1>.Used<2> <methods>;",
+        "}",
+        "-keep class <1>.Used<2>*",
+        // @UnusedAnnotation <methods> -> UnusedAnnotationDependent
+        "-if class **.*User {",
+        "  @<1>.Unused<2> <methods>;",
+        "}",
+        "-keep class <1>.Unused<2>*"
+    );
+
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    verifyClassesAbsent(dexInspector,
+        UnusedAnnotation.class, UnusedAnnotationDependent.class);
+    verifyClassesPresent(dexInspector,
+        UsedAnnotation.class, UsedAnnotationDependent.class);
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTestClasses.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTestClasses.java
new file mode 100644
index 0000000..56fb4c1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTestClasses.java
@@ -0,0 +1,39 @@
+// 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.shaking.ifrule;
+
+@interface UsedAnnotation {
+  // Intentionally left empty.
+}
+
+@interface UnusedAnnotation {
+  // Intentionally left empty.
+}
+
+class UsedAnnotationDependent {
+  // Intentionally left empty.
+}
+
+class UnusedAnnotationDependent {
+  // Intentionally left empty.
+}
+
+class AnnotationUser {
+  private int intField;
+
+  @UsedAnnotation void live() {
+    System.out.println("live(" + intField++ + ")");
+  }
+
+  @UnusedAnnotation void dead() {
+    throw new AssertionError("Should be removed.");
+  }
+}
+
+class MainUsesAnnotationUser {
+  public static void main(String[] args) {
+    AnnotationUser user = new AnnotationUser();
+    user.live();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnClassTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnClassTest.java
index 5f3cae1..c26f43f 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnClassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnClassTest.java
@@ -78,7 +78,44 @@
   }
 
   @Test
-  public void ifThenKeep() throws Exception {
+  public void ifThenKeep_withoutNthWildcard() throws Exception {
+    List<String> config = ImmutableList.of(
+        "-if class **.Precondition",
+        "-keep,allowobfuscation class **.*User",
+        "-if class **.DependentUser",
+        "-keep,allowobfuscation class **.Dependent"
+    );
+
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    if (!keepPrecondition) {
+      // TODO(b/73708139): Proguard6 kept Dependent (w/o any members), which is not necessary.
+      if (shrinker == Shrinker.PROGUARD6) {
+        return;
+      }
+      assertEquals(1, dexInspector.allClasses().size());
+      return;
+    }
+
+    ClassSubject clazz = dexInspector.clazz(DependentUser.class);
+    assertThat(clazz, isRenamed());
+    // Members of DependentUser are not used anywhere.
+    MethodSubject m = clazz.method("void", "callFoo", ImmutableList.of());
+    assertThat(m, not(isPresent()));
+    FieldSubject f = clazz.field("int", "canBeShrinked");
+    assertThat(f, not(isPresent()));
+
+    // Although DependentUser#callFoo is shrinked, Dependent is kept via -if.
+    clazz = dexInspector.clazz(Dependent.class);
+    assertThat(clazz, isRenamed());
+    // But, its members are gone.
+    m = clazz.method("java.lang.String", "foo", ImmutableList.of());
+    assertThat(m, not(isPresent()));
+    f = clazz.field("int", "intField");
+    assertThat(f, not(isPresent()));
+  }
+
+  @Test
+  public void ifThenKeep_withNthWildcard() throws Exception {
     List<String> config = ImmutableList.of(
         "-if class **.Precondition",
         "-keep,allowobfuscation class **.*User",
@@ -96,7 +133,7 @@
       return;
     }
 
-    // TODO(b/73708139): not implemented yet.
+    // TODO(b/73800755): not implemented yet.
     if (shrinker == Shrinker.R8) {
       return;
     }
@@ -134,8 +171,43 @@
       return;
     }
 
-    // TODO(b/73708139): not implemented yet.
-    if (shrinker == Shrinker.R8) {
+    ClassSubject clazz = dexInspector.clazz(DependentUser.class);
+    assertThat(clazz, isRenamed());
+    MethodSubject m = clazz.method("void", "callFoo", ImmutableList.of());
+    assertThat(m, isRenamed());
+    FieldSubject f = clazz.field("int", "canBeShrinked");
+    assertThat(f, not(isPresent()));
+
+    // Dependent is kept due to DependentUser#callFoo, but renamed.
+    clazz = dexInspector.clazz(Dependent.class);
+    assertThat(clazz, isRenamed());
+    m = clazz.method("java.lang.String", "foo", ImmutableList.of());
+    assertThat(m, isRenamed());
+    f = clazz.field("int", "intField");
+    assertThat(f, isRenamed());
+  }
+
+  @Test
+  public void ifThenKeepClassMembers_withoutNthWildcard() throws Exception {
+    List<String> config = ImmutableList.of(
+        "-if class **.Precondition",
+        "-keepclassmembers,allowobfuscation class **.*User {",
+        "  static void callFoo(...);",
+        "}",
+        // If the member is kept, keep the enclosing class too.
+        "-if class **.*User {",
+        "  static void callFoo(...);",
+        "}",
+        "-keep,allowobfuscation class **.*User"
+    );
+
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    if (!keepPrecondition) {
+      // TODO(b/73708139): Proguard6 kept DependentUser (w/o any members), which is not necessary.
+      if (shrinker == Shrinker.PROGUARD6) {
+        return;
+      }
+      assertEquals(1, dexInspector.allClasses().size());
       return;
     }
 
@@ -156,15 +228,15 @@
   }
 
   @Test
-  public void ifThenKeepClassMembers() throws Exception {
+  public void ifThenKeepClassMembers_withNthWildcard() throws Exception {
     List<String> config = ImmutableList.of(
         "-if class **.Precondition",
         "-keepclassmembers,allowobfuscation class **.*User {",
         "  static void callFoo(...);",
         "}",
-        // If members are kept, keep the enclosing class too.
+        // If the member is kept, keep the enclosing class too.
         "-if class **.*User {",
-        "  <methods>;",
+        "  static void callFoo(...);",
         "}",
         "-keep,allowobfuscation class <1>.<2>User"
     );
@@ -179,7 +251,7 @@
       return;
     }
 
-    // TODO(b/73708139): not implemented yet.
+    // TODO(b/73800755): not implemented yet.
     if (shrinker == Shrinker.R8) {
       return;
     }
@@ -214,11 +286,6 @@
 
     DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
 
-    // TODO(b/73708139): not implemented yet.
-    if (shrinker == Shrinker.R8) {
-      return;
-    }
-
     ClassSubject clazz = dexInspector.clazz(Dependent.class);
     // Only class name is not renamed, if triggered.
     assertThat(clazz, keepPrecondition ? isNotRenamed() : isRenamed());
@@ -244,11 +311,6 @@
 
     DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
 
-    // TODO(b/73708139): not implemented yet.
-    if (shrinker == Shrinker.R8) {
-      return;
-    }
-
     ClassSubject clazz = dexInspector.clazz(Dependent.class);
     // Class name is not renamed, if triggered.
     assertThat(clazz, keepPrecondition ? isNotRenamed() : isRenamed());
@@ -275,11 +337,6 @@
 
     DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
 
-    // TODO(b/73708139): not implemented yet.
-    if (shrinker == Shrinker.R8) {
-      return;
-    }
-
     ClassSubject clazz = dexInspector.clazz(Dependent.class);
     assertThat(clazz, isRenamed());
     MethodSubject m = clazz.method("java.lang.String", "foo", ImmutableList.of());
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnFieldTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnFieldTest.java
index 07e94cd..2bf8a27 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnFieldTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnFieldTest.java
@@ -3,15 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking.ifrule;
 
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
 import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -27,7 +23,8 @@
       D.class, D1.class, D2.class,
       R.class, R1.class, R2.class,
       MainWithInner.InnerR.class, MainWithInner.InnerD.class,
-      MainUsesR.class, MainWithIf.class, MainWithInner.class);
+      I.class, Impl.class,
+      MainUsesR.class, MainWithIf.class, MainWithInner.class, MainUsesImpl.class);
 
   private final Shrinker shrinker;
 
@@ -59,24 +56,39 @@
     return super.runProguard6(programClasses, adaptConfiguration(proguardConfig));
   }
 
-  private void verifyClassesPresent(
-      DexInspector dexInspector, Class<?>... classesOfInterest) {
-    for (Class klass : classesOfInterest) {
-      ClassSubject c = dexInspector.clazz(klass);
-      assertThat(c, isPresent());
-    }
-  }
+  @Test
+  public void ifOnField_withoutNthWildcard() throws Exception {
+    List<String> config = ImmutableList.of(
+        "-keep class **.MainUsesR {",
+        "  public static void main(java.lang.String[]);",
+        "}",
+        // R.id1 -> D1
+        "-if class **.R {",
+        "  public static int id1;",
+        "}",
+        "-keep class **.D1",
+        // R.id2 -> D2
+        "-if class **.R {",
+        "  public static int id2;",
+        "}",
+        "-keep class **.D2",
+        // R.id1 && R.id2 -> D
+        "-if class **.R {",
+        "  public static int id1;",
+        "  public static int id2;",
+        "}",
+        "-keep class **.D"
+    );
 
-  private void verifyClassesAbsent(
-      DexInspector dexInspector, Class<?>... classesOfInterest) {
-    for (Class klass : classesOfInterest) {
-      ClassSubject c = dexInspector.clazz(klass);
-      assertThat(c, not(isPresent()));
-    }
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    verifyClassesAbsent(dexInspector,
+        R1.class, R2.class, D.class, D2.class);
+    verifyClassesPresent(dexInspector,
+        R.class, D1.class);
   }
 
   @Test
-  public void ifOnField() throws Exception {
+  public void ifOnField_withNthWildcard() throws Exception {
     // TODO(b/73800755): not implemented yet.
     if (shrinker == Shrinker.R8) {
       return;
@@ -87,13 +99,9 @@
         "  public static void main(java.lang.String[]);",
         "}",
         "-if class **.R {",
-        "  public static int id1;",
+        "  public static int id?;",
         "}",
-        "-keep class **.D1",
-        "-if class **.R {",
-        "  public static int id2;",
-        "}",
-        "-keep class **.D2"
+        "-keep class **.D<2>"
     );
 
     DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
@@ -101,26 +109,33 @@
         R1.class, R2.class, D.class, D2.class);
     verifyClassesPresent(dexInspector,
         R.class, D1.class);
-
-    config = ImmutableList.of(
-        "-keep class **.MainUsesR {",
-        "  public static void main(java.lang.String[]);",
-        "}",
-        "-if class **.R {",
-        "  public static int id?;",
-        "}",
-        "-keep class **.D<2>"
-    );
-
-    dexInspector = runShrinker(shrinker, CLASSES, config);
-    verifyClassesAbsent(dexInspector,
-        R1.class, R2.class, D.class, D2.class);
-    verifyClassesPresent(dexInspector,
-        R.class, D1.class);
   }
 
   @Test
-  public void ifOnFieldWithCapture() throws Exception {
+  public void ifOnFieldWithCapture_withoutNthWildcard() throws Exception {
+    List<String> config = ImmutableList.of(
+        "-keep class **.MainWithIf {",
+        "  public static void main(java.lang.String[]);",
+        "}",
+        "-if class **.R1 {",
+        "  public static int id*;",
+        "}",
+        "-keep class **.D1",
+        "-if class **.R2 {",
+        "  public static int id*;",
+        "}",
+        "-keep class **.D2"
+    );
+
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    verifyClassesAbsent(dexInspector,
+        R.class, D.class, R1.class, D1.class);
+    verifyClassesPresent(dexInspector,
+        R2.class, D2.class);
+  }
+
+  @Test
+  public void ifOnFieldWithCapture_withNthWildcard() throws Exception {
     // TODO(b/73800755): not implemented yet.
     if (shrinker == Shrinker.R8) {
       return;
@@ -131,8 +146,7 @@
         "  public static void main(java.lang.String[]);",
         "}",
         "-if class **.R* {",
-        "  public static int id1;",
-        "  public static int id2;",
+        "  public static int id*;",
         "}",
         "-keep class **.D<2>"
     );
@@ -145,7 +159,27 @@
   }
 
   @Test
-  public void ifOnFieldWithInner() throws Exception {
+  public void ifOnFieldWithInner_withoutNthWildcard() throws Exception {
+    List<String> config = ImmutableList.of(
+        "-keep class **.MainWithInner {",
+        "  public static void main(java.lang.String[]);",
+        "}",
+        "-if class **$*R {",
+        "  public static int id1;",
+        "  public static int id2;",
+        "}",
+        "-keep class **$*D"
+    );
+
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    verifyClassesAbsent(dexInspector,
+        R.class, D.class, R1.class, D1.class, R2.class, D2.class);
+    verifyClassesPresent(dexInspector,
+        MainWithInner.InnerR.class, MainWithInner.InnerD.class);
+  }
+
+  @Test
+  public void ifOnFieldWithInner_withNthWildcard() throws Exception {
     // TODO(b/73800755): not implemented yet.
     if (shrinker == Shrinker.R8) {
       return;
@@ -196,4 +230,53 @@
     }
   }
 
+  @Test
+  public void ifOnFieldInImplementer_withoutNthWildcard() throws Exception {
+    List<String> config = ImmutableList.of(
+        "-keep class **.MainUsesImpl {",
+        "  public static void main(java.lang.String[]);",
+        "}",
+        "-if class ** implements **.I {",
+        "  private <fields>;",
+        "}",
+        "-keep class **.D1",
+        "-if class ** implements **.I {",
+        "  public <fields>;",
+        "}",
+        "-keep class **.D2"
+    );
+
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    verifyClassesAbsent(dexInspector, D2.class);
+    verifyClassesPresent(dexInspector,
+        I.class, Impl.class, D1.class);
+  }
+
+  @Test
+  public void ifOnFieldInImplementer_withNthWildcard() throws Exception {
+    // TODO(b/73800755): not implemented yet.
+    if (shrinker == Shrinker.R8) {
+      return;
+    }
+
+    List<String> config = ImmutableList.of(
+        "-keep class **.MainUsesImpl {",
+        "  public static void main(java.lang.String[]);",
+        "}",
+        "-if class ** implements **.I {",
+        "  private <fields>;",
+        "}",
+        "-keep class <2>.D1",
+        "-if class ** implements **.I {",
+        "  public <fields>;",
+        "}",
+        "-keep class <2>.D2"
+    );
+
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    verifyClassesAbsent(dexInspector, D2.class);
+    verifyClassesPresent(dexInspector,
+        I.class, Impl.class, D1.class);
+  }
+
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnFieldTestClasses.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnFieldTestClasses.java
index 892b63e..bd8e5c2 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnFieldTestClasses.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnFieldTestClasses.java
@@ -27,6 +27,21 @@
   public static int id2 = 2;
 }
 
+interface I {
+  void ack();
+}
+
+class Impl implements I {
+  private int usedPrivateIntField;
+  private int unusedPrivateIntField;
+  public int unusedPublicIntField;
+  public String unusedPublicStringField;
+
+  public void ack() {
+    System.out.println("ack(" + usedPrivateIntField++ + ")");
+  }
+}
+
 class MainUsesR {
   public static void main(String[] args) {
     System.out.println(R.id1);
@@ -62,3 +77,9 @@
   }
 }
 
+class MainUsesImpl {
+  public static void main(String[] args) {
+    I instance = new Impl();
+    instance.ack();
+  }
+}
diff --git a/src/test/kotlinR8TestResources/non_null/example1.kt b/src/test/kotlinR8TestResources/non_null/example1.kt
new file mode 100644
index 0000000..02a3fcf
--- /dev/null
+++ b/src/test/kotlinR8TestResources/non_null/example1.kt
@@ -0,0 +1,29 @@
+// 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 non_null
+
+data class Car(
+        val make: String,
+        val model: String,
+        val year: Int,
+        val plateNumber: String)
+
+fun Collection<Car>.forMakeAndModel(
+        make: String, model: String, sinceYear: Int?
+) = this.asSequence()
+        .filter { it.make == make }
+        .filter { it.model == model }
+        .filter { sinceYear != null && it.year >= sinceYear }
+        .groupBy { it.year }
+        .toSortedMap()
+
+fun main(args: Array<String>) {
+    val leaf = Car("Nissan", "Leaf", 2015, "  LEAF  ")
+    val ms1 = Car("Tesla", "Model S", 2015, "  LGTM1 ")
+    val ms2 = Car("Tesla", "Model S", 2017, "  LGTM2 ")
+    val m3 = Car("Tesla", "Model 3", 2018, "  LGTM3 ")
+    val cars: List<Car> = mutableListOf(leaf, ms1, ms2, m3)
+    println(cars.forMakeAndModel("Tesla", "Model S", null))
+}
diff --git a/src/test/kotlinR8TestResources/non_null/example2.kt b/src/test/kotlinR8TestResources/non_null/example2.kt
new file mode 100644
index 0000000..e35b2e8
--- /dev/null
+++ b/src/test/kotlinR8TestResources/non_null/example2.kt
@@ -0,0 +1,14 @@
+// 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 non_null
+
+inline fun coalesce(a: String?, b: String?): String? = a ?: b
+fun aOrDefault(a: String?, default: String): String =
+        coalesce(a, default) ?: throw AssertionError()
+
+fun main(args: Array<String>) {
+    println(aOrDefault(null, "null"))
+    println(aOrDefault("null", "non-null"))
+}
\ No newline at end of file