Reland "Enable pass through of bytecode for kotlin inline functions"

This reverts commit 3f9d7c48fa37653fa6ba74d82e92313ff42a492c.

Change-Id: I0579b95f4ce8f4dcf8a9657d61fb85615bdbc29f
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 802160f..9a0e76a 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -475,7 +475,11 @@
         timing.end();
       }
 
-      if (options.getProguardConfiguration().isOptimizing()) {
+      boolean isKotlinLibraryCompilationWithInlinePassThrough =
+          options.enableCfByteCodePassThrough && appView.hasCfByteCodePassThroughMethods();
+
+      if (!isKotlinLibraryCompilationWithInlinePassThrough
+          && options.getProguardConfiguration().isOptimizing()) {
         if (options.enableHorizontalClassMerging) {
           timing.begin("HorizontalStaticClassMerger");
           StaticClassMerger staticClassMerger =
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 656f3aa..65cf1bd 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.inspector.Inspector;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.kotlin.KotlinMetadataUtils;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.shaking.ProguardConfiguration;
@@ -933,6 +934,9 @@
     internal.desugaredLibraryConfiguration = libraryConfiguration;
     internal.synthesizedClassPrefix = synthesizedClassPrefix;
     internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
+    internal.enableCfByteCodePassThrough =
+        internal.isGeneratingClassFiles()
+            && KotlinMetadataUtils.isKeepingKotlinMetadataInRules(internal);
 
     if (!DETERMINISTIC_DEBUGGING) {
       assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 0b6547e..c3d4904 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -26,8 +26,10 @@
 import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableSet;
 import java.util.IdentityHashMap;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -67,6 +69,7 @@
   private HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses;
   private VerticallyMergedClasses verticallyMergedClasses;
   private EnumValueInfoMapCollection unboxedEnums = EnumValueInfoMapCollection.empty();
+  private Set<DexMethod> cfByteCodePassThrough = ImmutableSet.of();
 
   private Map<DexClass, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
 
@@ -372,6 +375,10 @@
     this.initializedClassesInInstanceMethods = initializedClassesInInstanceMethods;
   }
 
+  public void setCfByteCodePassThrough(Set<DexMethod> cfByteCodePassThrough) {
+    this.cfByteCodePassThrough = cfByteCodePassThrough;
+  }
+
   public <U> U withInitializedClassesInInstanceMethods(
       Function<InitializedClassesInInstanceMethods, U> fn, U defaultValue) {
     if (initializedClassesInInstanceMethods != null) {
@@ -463,7 +470,17 @@
   }
 
   public boolean isCfByteCodePassThrough(DexEncodedMethod method) {
+    if (!options.isGeneratingClassFiles()) {
+      return false;
+    }
+    if (cfByteCodePassThrough.contains(method.method)) {
+      return true;
+    }
     return options.testing.cfByteCodePassThrough != null
-        && options.testing.cfByteCodePassThrough.test(method);
+        && options.testing.cfByteCodePassThrough.test(method.method);
+  }
+
+  public boolean hasCfByteCodePassThroughMethods() {
+    return !cfByteCodePassThrough.isEmpty();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index d17a4d2..d660501 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -988,7 +988,8 @@
   private static DebugParsingOptions getParsingOptions(
       JarApplicationReader application, boolean reachabilitySensitive) {
     int parsingOptions =
-        application.options.testing.readInputStackMaps
+        (application.options.enableCfByteCodePassThrough
+                || application.options.testing.readInputStackMaps)
             ? ClassReader.EXPAND_FRAMES
             : ClassReader.SKIP_FRAMES;
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index b5bf1c4..dd450d6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -66,13 +66,14 @@
       return;
     }
 
+    timing.begin("Enqueue methods for reprocessing");
+    enqueueMethodsForReprocessing(appInfo, executorService);
+    timing.end(); // Enqueue methods for reprocessing
+
     timing.begin("Clear reads from fields of interest");
     clearReadsFromFieldsOfInterest(appInfo);
     timing.end(); // Clear reads from fields of interest
 
-    timing.begin("Enqueue methods for reprocessing");
-    enqueueMethodsForReprocessing(appInfo, executorService);
-    timing.end(); // Enqueue methods for reprocessing
     timing.end(); // Trivial field accesses analysis
 
     fieldsOfInterest.forEach(OptimizationFeedbackSimple.getInstance()::markFieldAsDead);
@@ -164,6 +165,11 @@
     private boolean registerFieldAccess(DexField field, boolean isStatic) {
       DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
       if (encodedField != null) {
+        // We cannot remove references from pass through functions.
+        if (appView.isCfByteCodePassThrough(method.getDefinition())) {
+          fieldsOfInterest.remove(encodedField);
+          return true;
+        }
         if (encodedField.isStatic() == isStatic) {
           if (fieldsOfInterest.contains(encodedField)) {
             methodsToReprocess.add(method);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index f5c08f1..6bfdb1f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1217,6 +1217,16 @@
     assert code.verifyTypes(appView);
     assert code.isConsistentSSA();
 
+    if (appView.isCfByteCodePassThrough(method)) {
+      // If the code is pass trough, do not finalize by overwriting the existing code.
+      assert appView.enableWholeProgramOptimizations();
+      timing.begin("Collect optimization info");
+      collectOptimizationInfo(
+          method, code, ClassInitializerDefaultsResult.empty(), feedback, methodProcessor, timing);
+      timing.end();
+      return timing;
+    }
+
     assertionsRewriter.run(method, code, timing);
 
     if (serviceLoaderRewriter != null) {
@@ -1607,11 +1617,6 @@
       timing.end();
     }
 
-    if (appView.isCfByteCodePassThrough(method)) {
-      // If the code is pass trough, do not finalize by overwriting the existing code.
-      return timing;
-    }
-
     printMethod(code, "Optimized IR (SSA)", previous);
     timing.begin("Finalize IR");
     finalizeIR(code, feedback, timing);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index 2cb8fe4..6448696 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -23,6 +23,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Consumer;
 import kotlinx.metadata.KmClass;
 import kotlinx.metadata.KmConstructor;
 import kotlinx.metadata.KmType;
@@ -75,7 +76,8 @@
       KmClass kmClass,
       DexClass hostClass,
       DexDefinitionSupplier definitionSupplier,
-      Reporter reporter) {
+      Reporter reporter,
+      Consumer<DexEncodedMethod> keepByteCode) {
     Map<String, DexEncodedField> fieldMap = new HashMap<>();
     for (DexEncodedField field : hostClass.fields()) {
       fieldMap.put(toJvmFieldSignature(field.field).asString(), field);
@@ -101,7 +103,7 @@
     }
     KotlinDeclarationContainerInfo container =
         KotlinDeclarationContainerInfo.create(
-            kmClass, methodMap, fieldMap, definitionSupplier, reporter);
+            kmClass, methodMap, fieldMap, definitionSupplier, reporter, keepByteCode);
     setCompanionObject(kmClass, hostClass, reporter);
     return new KotlinClassInfo(
         kmClass.getFlags(),
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
index a6d1105..27d0425 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueArray;
@@ -18,6 +19,7 @@
 import com.android.tools.r8.utils.StringDiagnostic;
 import java.util.IdentityHashMap;
 import java.util.Map;
+import java.util.function.Consumer;
 import kotlinx.metadata.InconsistentKotlinMetadataException;
 import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -31,7 +33,8 @@
       DexClass clazz,
       DexDefinitionSupplier definitionSupplier,
       Reporter reporter,
-      boolean onlyProcessLambda) {
+      boolean onlyProcessLambda,
+      Consumer<DexEncodedMethod> keepByteCode) {
     DexAnnotation meta =
         clazz
             .annotations()
@@ -42,7 +45,8 @@
         if (onlyProcessLambda && kMetadata.getHeader().getKind() != KOTLIN_METADATA_KIND_LAMBDA) {
           return NO_KOTLIN_INFO;
         }
-        return createKotlinInfo(kotlin, clazz, kMetadata, definitionSupplier, reporter);
+        return createKotlinInfo(
+            kotlin, clazz, kMetadata, definitionSupplier, reporter, keepByteCode);
       } catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) {
         reporter.info(
             new StringDiagnostic(
@@ -100,14 +104,23 @@
       DexClass clazz,
       KotlinClassMetadata kMetadata,
       DexDefinitionSupplier definitionSupplier,
-      Reporter reporter) {
+      Reporter reporter,
+      Consumer<DexEncodedMethod> keepByteCode) {
     if (kMetadata instanceof KotlinClassMetadata.Class) {
       return KotlinClassInfo.create(
-          ((KotlinClassMetadata.Class) kMetadata).toKmClass(), clazz, definitionSupplier, reporter);
+          ((KotlinClassMetadata.Class) kMetadata).toKmClass(),
+          clazz,
+          definitionSupplier,
+          reporter,
+          keepByteCode);
     } else if (kMetadata instanceof KotlinClassMetadata.FileFacade) {
       // e.g., B.kt becomes class `BKt`
       return KotlinFileFacadeInfo.create(
-          (KotlinClassMetadata.FileFacade) kMetadata, clazz, definitionSupplier, reporter);
+          (KotlinClassMetadata.FileFacade) kMetadata,
+          clazz,
+          definitionSupplier,
+          reporter,
+          keepByteCode);
     } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassFacade) {
       // multi-file class with the same @JvmName.
       return KotlinMultiFileClassFacadeInfo.create(
@@ -115,7 +128,11 @@
     } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassPart) {
       // A single file, which is part of multi-file class.
       return KotlinMultiFileClassPartInfo.create(
-          (KotlinClassMetadata.MultiFileClassPart) kMetadata, clazz, definitionSupplier, reporter);
+          (KotlinClassMetadata.MultiFileClassPart) kMetadata,
+          clazz,
+          definitionSupplier,
+          reporter,
+          keepByteCode);
     } else if (kMetadata instanceof KotlinClassMetadata.SyntheticClass) {
       return KotlinSyntheticClassInfo.create(
           (KotlinClassMetadata.SyntheticClass) kMetadata,
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
index 6dc4d4a..5f7c77e 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -19,10 +19,12 @@
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Consumer;
 import kotlinx.metadata.KmDeclarationContainer;
 import kotlinx.metadata.KmFunction;
 import kotlinx.metadata.KmProperty;
 import kotlinx.metadata.KmTypeAlias;
+import kotlinx.metadata.internal.metadata.deserialization.Flags;
 import kotlinx.metadata.jvm.JvmExtensionsKt;
 import kotlinx.metadata.jvm.JvmMethodSignature;
 
@@ -50,7 +52,8 @@
       Map<String, DexEncodedMethod> methodSignatureMap,
       Map<String, DexEncodedField> fieldSignatureMap,
       DexDefinitionSupplier definitionSupplier,
-      Reporter reporter) {
+      Reporter reporter,
+      Consumer<DexEncodedMethod> keepByteCode) {
     ImmutableList.Builder<KotlinFunctionInfo> notBackedFunctions = ImmutableList.builder();
     for (KmFunction kmFunction : container.getFunctions()) {
       JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmFunction);
@@ -75,6 +78,7 @@
         }
         continue;
       }
+      keepIfInline(kmFunction.getFlags(), method, keepByteCode);
       method.setKotlinMemberInfo(kotlinFunctionInfo);
     }
 
@@ -97,6 +101,7 @@
             methodSignatureMap.get(propertyProcessor.getterSignature().asString());
         if (method != null) {
           hasBacking = true;
+          keepIfInline(kmProperty.getFlags(), method, keepByteCode);
           method.setKotlinMemberInfo(kotlinPropertyInfo);
         }
       }
@@ -105,6 +110,7 @@
             methodSignatureMap.get(propertyProcessor.setterSignature().asString());
         if (method != null) {
           hasBacking = true;
+          keepIfInline(kmProperty.getFlags(), method, keepByteCode);
           method.setKotlinMemberInfo(kotlinPropertyInfo);
         }
       }
@@ -118,6 +124,13 @@
         notBackedProperties.build());
   }
 
+  private static void keepIfInline(
+      int flags, DexEncodedMethod method, Consumer<DexEncodedMethod> keepByteCode) {
+    if (Flags.IS_INLINE.get(flags) || Flags.IS_CROSSINLINE.get(flags)) {
+      keepByteCode.accept(method);
+    }
+  }
+
   private static List<KotlinTypeAliasInfo> getTypeAliases(
       List<KmTypeAlias> aliases, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
     ImmutableList.Builder<KotlinTypeAliasInfo> builder = ImmutableList.builder();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
index ccd957a..50ec7b7 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
@@ -7,9 +7,11 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Reporter;
+import java.util.function.Consumer;
 import kotlinx.metadata.KmPackage;
 import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -28,9 +30,11 @@
       FileFacade kmFileFacade,
       DexClass clazz,
       DexDefinitionSupplier definitionSupplier,
-      Reporter reporter) {
+      Reporter reporter,
+      Consumer<DexEncodedMethod> keepByteCode) {
     return new KotlinFileFacadeInfo(
-        KotlinPackageInfo.create(kmFileFacade.toKmPackage(), clazz, definitionSupplier, reporter));
+        KotlinPackageInfo.create(
+            kmFileFacade.toKmPackage(), clazz, definitionSupplier, reporter, keepByteCode));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index 6d01587..c5ad4ce 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -6,13 +6,17 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
 import com.android.tools.r8.shaking.Enqueuer;
+import com.google.common.collect.Sets;
+import java.util.Set;
 
 public class KotlinMetadataEnqueuerExtension extends EnqueuerAnalysis {
 
   private final AppView<?> appView;
   private final DexDefinitionSupplier definitionSupplier;
+  private final Set<DexMethod> keepByteCodeFunctions = Sets.newIdentityHashSet();
 
   public KotlinMetadataEnqueuerExtension(
       AppView<?> appView, DexDefinitionSupplier definitionSupplier) {
@@ -35,7 +39,9 @@
                   clazz,
                   definitionSupplier,
                   appView.options().reporter,
-                  !keepMetadata || !enqueuer.isPinned(clazz.type)));
+                  !keepMetadata || !enqueuer.isPinned(clazz.type),
+                  method -> keepByteCodeFunctions.add(method.method)));
         });
+    appView.setCfByteCodePassThrough(keepByteCodeFunctions);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
index 79745af..f4cee6b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
@@ -188,10 +188,18 @@
       ProguardConfigurationRule rule, DexItemFactory factory) {
     if (rule.isProguardKeepRule()) {
       ProguardKeepRule proguardKeepRule = rule.asProguardKeepRule();
-      return proguardKeepRule.getType() == ProguardKeepRuleType.KEEP
-          && !proguardKeepRule.getModifiers().allowsShrinking
-          && !proguardKeepRule.getModifiers().allowsObfuscation
-          && proguardKeepRule.getClassNames().matches(factory.kotlinMetadataType);
+      // Check if rule is a keep
+      if (proguardKeepRule.getType() != ProguardKeepRuleType.KEEP
+          || proguardKeepRule.getModifiers().allowsShrinking
+          || proguardKeepRule.getModifiers().allowsObfuscation) {
+        return false;
+      }
+      // Only match if -keep class kotlin.Metadata is specified explicitly, since we translate
+      // -dont*** to a global rule.
+      if (!proguardKeepRule.getClassNames().hasWildcards()
+          && proguardKeepRule.getClassNames().matches(factory.kotlinMetadataType)) {
+        return true;
+      }
     }
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
index 6a06b7d..9a751ed 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
@@ -7,9 +7,11 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Reporter;
+import java.util.function.Consumer;
 import kotlinx.metadata.KmPackage;
 import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -30,10 +32,12 @@
       MultiFileClassPart classPart,
       DexClass clazz,
       DexDefinitionSupplier definitionSupplier,
-      Reporter reporter) {
+      Reporter reporter,
+      Consumer<DexEncodedMethod> keepByteCode) {
     return new KotlinMultiFileClassPartInfo(
         classPart.getFacadeClassName(),
-        KotlinPackageInfo.create(classPart.toKmPackage(), clazz, definitionSupplier, reporter));
+        KotlinPackageInfo.create(
+            classPart.toKmPackage(), clazz, definitionSupplier, reporter, keepByteCode));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
index 7a667e6..d98e109 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.utils.Reporter;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.function.Consumer;
 import kotlinx.metadata.KmPackage;
 import kotlinx.metadata.jvm.JvmExtensionsKt;
 
@@ -35,7 +36,8 @@
       KmPackage kmPackage,
       DexClass clazz,
       DexDefinitionSupplier definitionSupplier,
-      Reporter reporter) {
+      Reporter reporter,
+      Consumer<DexEncodedMethod> keepByteCode) {
     Map<String, DexEncodedField> fieldMap = new HashMap<>();
     for (DexEncodedField field : clazz.fields()) {
       fieldMap.put(toJvmFieldSignature(field.field).asString(), field);
@@ -47,7 +49,7 @@
     return new KotlinPackageInfo(
         JvmExtensionsKt.getModuleName(kmPackage),
         KotlinDeclarationContainerInfo.create(
-            kmPackage, methodMap, fieldMap, definitionSupplier, reporter));
+            kmPackage, methodMap, fieldMap, definitionSupplier, reporter, keepByteCode));
   }
 
   public void rewrite(
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
index 0b02b70..d00bc9a 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -87,6 +87,10 @@
     return Collections::emptyIterator;
   }
 
+  public boolean hasWildcards() {
+    return getWildcards().iterator().hasNext();
+  }
+
   static Iterable<ProguardWildcard> getWildcardsOrEmpty(ProguardClassNameList nameList) {
     return nameList == null ? Collections::emptyIterator : nameList.getWildcards();
   }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index e846e56..b48c81c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -303,6 +303,9 @@
   // fused together by play store when shipped for pre-L devices.
   public boolean ignoreMainDexMissingClasses = false;
 
+  // Boolean value indicating that byte code pass through may be enabled.
+  public boolean enableCfByteCodePassThrough = false;
+
   // Hidden marker for classes.dex
   private boolean hasMarker = false;
   private Marker marker;
@@ -1186,7 +1189,7 @@
 
     public Consumer<ProgramMethod> callSiteOptimizationInfoInspector = null;
 
-    public Predicate<DexEncodedMethod> cfByteCodePassThrough = null;
+    public Predicate<DexMethod> cfByteCodePassThrough = null;
   }
 
   @VisibleForTesting
diff --git a/src/test/java/com/android/tools/r8/code/PassThroughTest.java b/src/test/java/com/android/tools/r8/code/PassThroughTest.java
index b1a9f2f..f8e4752 100644
--- a/src/test/java/com/android/tools/r8/code/PassThroughTest.java
+++ b/src/test/java/com/android/tools/r8/code/PassThroughTest.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.CfFrontendExamplesTest;
 import com.android.tools.r8.ClassFileResourceProvider;
 import com.android.tools.r8.DirectoryClassFileProvider;
+import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestShrinkerBuilder;
@@ -21,6 +22,7 @@
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.List;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -30,7 +32,7 @@
 @RunWith(Parameterized.class)
 public class PassThroughTest extends TestBase {
 
-  private final String EXPECTED = StringUtils.lines("0", "foo", "0");
+  private final String EXPECTED = StringUtils.lines("0", "foo", "0", "foo", "foo");
 
   private final TestParameters parameters;
   private final boolean keepDebug;
@@ -56,7 +58,8 @@
     // Check that reading the same input is actual matches.
     ClassFileResourceProvider original =
         DirectoryClassFileProvider.fromDirectory(ToolHelper.getClassPathForTests());
-    verifyInstructionsForMainMatchingExpectation(original, true, true);
+    verifyInstructionsForMethodMatchingExpectation(original, "main", true, true);
+    verifyInstructionsForMethodMatchingExpectation(original, "exceptionTest", true, true);
   }
 
   @Test
@@ -64,14 +67,16 @@
     Path outputJar = temp.newFile("output.jar").toPath();
     testForR8(parameters.getBackend())
         .addProgramClasses(Main.class)
-        .addKeepMainRule(Main.class)
+        .addKeepAllClassesRule()
+        .enableInliningAnnotations()
         .ifTrue(keepDebug, TestShrinkerBuilder::addKeepAllAttributes)
         .compile()
         .writeToZip(outputJar)
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutput(EXPECTED);
-    verifyInstructionsForMainMatchingExpectation(
-        new ArchiveClassFileProvider(outputJar), keepDebug, false);
+    ArchiveClassFileProvider actual = new ArchiveClassFileProvider(outputJar);
+    verifyInstructionsForMethodMatchingExpectation(actual, "main", keepDebug, false);
+    verifyInstructionsForMethodMatchingExpectation(actual, "exceptionTest", keepDebug, false);
   }
 
   @Test
@@ -79,22 +84,25 @@
     Path outputJar = temp.newFile("output.jar").toPath();
     testForR8(parameters.getBackend())
         .addProgramClasses(Main.class)
-        .addKeepMainRule(Main.class)
+        .addKeepAllClassesRule()
+        .enableInliningAnnotations()
         .ifTrue(keepDebug, TestShrinkerBuilder::addKeepAllAttributes)
         .addOptionsModification(
             internalOptions ->
                 internalOptions.testing.cfByteCodePassThrough =
-                    method -> method.method.name.toString().equals("main"))
+                    method -> !method.name.toString().equals("<init>"))
         .compile()
         .writeToZip(outputJar)
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutput(EXPECTED);
-    verifyInstructionsForMainMatchingExpectation(
-        new ArchiveClassFileProvider(outputJar), keepDebug, true);
+    ArchiveClassFileProvider actual = new ArchiveClassFileProvider(outputJar);
+    verifyInstructionsForMethodMatchingExpectation(actual, "main", keepDebug, true);
+    verifyInstructionsForMethodMatchingExpectation(actual, "exceptionTest", keepDebug, true);
   }
 
-  private void verifyInstructionsForMainMatchingExpectation(
-      ClassFileResourceProvider actual, boolean checkDebug, boolean expectation) throws Exception {
+  private void verifyInstructionsForMethodMatchingExpectation(
+      ClassFileResourceProvider actual, String methodName, boolean checkDebug, boolean expectation)
+      throws Exception {
     ClassFileResourceProvider original =
         DirectoryClassFileProvider.fromDirectory(ToolHelper.getClassPathForTests());
     String descriptor = DescriptorUtils.javaTypeToDescriptor(Main.class.getTypeName());
@@ -103,43 +111,60 @@
     if (!Arrays.equals(expectedBytes, actualBytes)) {
       String expectedString = CfFrontendExamplesTest.asmToString(expectedBytes);
       String actualString = CfFrontendExamplesTest.asmToString(actualBytes);
-      verifyInstructionsForMainMatchingExpectation(
-          getMethodInstructions(expectedString),
-          getMethodInstructions(actualString),
+      verifyInstructionsForMethodMatchingExpectation(
+          getMethodInstructions(expectedString, methodName),
+          getMethodInstructions(actualString, methodName),
           checkDebug,
           expectation);
     }
   }
 
-  private String getMethodInstructions(String asm) {
+  private String getMethodInstructions(String asm, String methodName) {
     int methodIndexStart =
         asm.indexOf(
-            "methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, \"main\","
-                + " \"([Ljava/lang/String;)V\", null, null);");
-    int methodIndexEnd = asm.indexOf("}", methodIndexStart);
+            "methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, \""
+                + methodName
+                + "\",");
+    methodIndexStart = asm.indexOf("methodVisitor.visitCode();", methodIndexStart);
+    int methodIndexEnd = asm.indexOf("methodVisitor.visitEnd();", methodIndexStart);
     return asm.substring(methodIndexStart, methodIndexEnd);
   }
 
-  private void verifyInstructionsForMainMatchingExpectation(
+  private void verifyInstructionsForMethodMatchingExpectation(
       String originalInstructions,
       String actualInstructions,
       boolean checkDebug,
       boolean expectation) {
-    if (!checkDebug) {
-      originalInstructions =
-          StringUtils.splitLines(originalInstructions).stream()
-              .filter(this::isNotDebugInstruction)
-              .map(instr -> instr + "\n")
-              .collect(Collectors.joining());
+    if (checkDebug) {
+      // We may rewrite jump instructions, so filter those out.
+      originalInstructions = filter(originalInstructions, this::isNotLabelOrJumpInstruction);
+      actualInstructions = filter(actualInstructions, this::isNotLabelOrJumpInstruction);
+    } else {
+      originalInstructions = filter(originalInstructions, this::isNotDebugInstructionOrJump);
+      actualInstructions = filter(actualInstructions, this::isNotLabelOrJumpInstruction);
     }
     assertSame(expectation, actualInstructions.equals(originalInstructions));
   }
 
-  private boolean isNotDebugInstruction(String instruction) {
+  private String filter(String instructions, Predicate<String> predicate) {
+    return StringUtils.splitLines(instructions).stream()
+        .filter(predicate)
+        .map(instr -> instr + "\n")
+        .collect(Collectors.joining());
+  }
+
+  private boolean isNotDebugInstructionOrJump(String instruction) {
     return !(instruction.startsWith("methodVisitor.visitLocalVariable")
         || instruction.startsWith("methodVisitor.visitLabel")
         || instruction.startsWith("Label")
-        || instruction.startsWith("methodVisitor.visitLineNumber"));
+        || instruction.startsWith("methodVisitor.visitLineNumber")
+        || instruction.startsWith("methodVisitor.visitJumpInsn"));
+  }
+
+  private boolean isNotLabelOrJumpInstruction(String instruction) {
+    return !(instruction.startsWith("Label")
+        || instruction.startsWith("methodVisitor.visitJumpInsn")
+        || instruction.startsWith("methodVisitor.visitLabel"));
   }
 
   public static class Main {
@@ -155,6 +180,28 @@
       }
       System.out.println(foo);
       System.out.println(j);
+      System.out.println(phiTest(args.length > 0 ? args[0] : null));
+      System.out.println(exceptionTest(args.length > 0 ? args[0] : null));
+    }
+
+    @NeverInline
+    public static String phiTest(String arg) {
+      String result;
+      if (arg == null) {
+        result = "foo";
+      } else {
+        result = "bar";
+      }
+      return result;
+    }
+
+    @NeverInline
+    public static String exceptionTest(String arg) {
+      try {
+        return arg.toLowerCase();
+      } catch (NullPointerException ignored) {
+        return "foo";
+      }
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
index 90190c4..56d1f50 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -9,7 +9,6 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestParameters;
@@ -92,70 +91,6 @@
   }
 
   @Test
-  public void testMetadataInClasspathType_merged() throws Exception {
-    Path baseLibJar = baseLibJarMap.get(targetVersion);
-    Path libJar =
-        testForR8(parameters.getBackend())
-            .addClasspathFiles(baseLibJar, ToolHelper.getKotlinStdlibJar())
-            .addProgramFiles(extLibJarMap.get(targetVersion))
-            .addKeepKotlinMetadata()
-            // Keep the Extra class and its interface (which has the method).
-            .addKeepRules("-keep class **.Extra")
-            // Keep the ImplKt extension method which requires metadata
-            // to be called with Kotlin syntax from other kotlin code.
-            .addKeepRules("-keep class **.ImplKt { <methods>; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile()
-            .inspect(this::inspectMerged)
-            .writeToZip();
-
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
-            .addClasspathFiles(baseLibJar, libJar)
-            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/classpath_app", "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), PKG + ".classpath_app.MainKt")
-        .assertSuccessWithOutput(EXPECTED);
-  }
-
-  private void inspectMerged(CodeInspector inspector) {
-    String implClassName = PKG + ".classpath_lib_ext.Impl";
-    String implKtClassName = PKG + ".classpath_lib_ext.ImplKt";
-    String extraClassName = PKG + ".classpath_lib_ext.Extra";
-
-    assertThat(inspector.clazz(implClassName), not(isPresent()));
-
-    ClassSubject implKt = inspector.clazz(implKtClassName);
-    assertThat(implKt, isPresent());
-    assertThat(implKt, not(isRenamed()));
-    // API entry is kept, hence the presence of Metadata.
-    KmPackageSubject kmPackage = implKt.getKmPackage();
-    assertThat(kmPackage, isPresent());
-
-    KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("fooExt");
-    assertThat(kmFunction, isPresent());
-
-    ClassSubject extra = inspector.clazz(extraClassName);
-    assertThat(extra, isPresent());
-    assertThat(extra, not(isRenamed()));
-    // API entry is kept, hence the presence of Metadata.
-    KmClassSubject kmClass = extra.getKmClass();
-    assertThat(kmClass, isPresent());
-    List<ClassSubject> superTypes = kmClass.getSuperTypes();
-    assertTrue(superTypes.stream().noneMatch(
-        supertype -> supertype.getFinalDescriptor().contains("Impl")));
-    // The super types are changed and we should not keep any information about it in the metadata.
-    List<String> superTypeDescriptors = kmClass.getSuperTypeDescriptors();
-    assertEquals(1, superTypeDescriptors.size());
-    assertEquals(KT_ANY, superTypeDescriptors.get(0));
-  }
-
-  @Test
   public void testMetadataInClasspathType_renamed() throws Exception {
     Path baseLibJar = baseLibJarMap.get(targetVersion);
     Path libJar =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index 0a02782..8ba2119 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -244,12 +244,12 @@
     // #        setterFlags: 6,
     // #        setterParameter: null,
     // #        jvmFlags: 0,
-    // #        fieldSignature: name:Ljava/lang/String;,
+    // #        fieldSignature: c:Ljava/lang/String;,
     // #        getterSignature: getName()Ljava/lang/String;,
     // #        setterSignature: setName(Ljava/lang/String;)V,
-    assertEquals(name.fieldSignature().asString(), "name:Ljava/lang/String;");
-    assertEquals(name.getterSignature().asString(), "getName()Ljava/lang/String;");
-    assertEquals(name.setterSignature().asString(), "setName(Ljava/lang/String;)V");
+    assertEquals("c:Ljava/lang/String;", name.fieldSignature().asString());
+    assertEquals("getName()Ljava/lang/String;", name.getterSignature().asString());
+    assertEquals("setName(Ljava/lang/String;)V", name.setterSignature().asString());
 
     KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
     assertThat(familyName, not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
index c326ec4..431b2d7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -14,7 +14,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
@@ -184,14 +183,8 @@
 
   private void inspectInvalid(CodeInspector inspector) {
     String exprClassName = PKG + ".sealed_lib.Expr";
-    String numClassName = PKG + ".sealed_lib.Num";
     String libClassName = PKG + ".sealed_lib.LibKt";
 
-    // Without any specific keep rule and no instantiation point, it's not necessary to keep
-    // sub classes of Expr.
-    ClassSubject num = inspector.clazz(numClassName);
-    assertThat(num, not(isPresent()));
-
     ClassSubject expr = inspector.clazz(exprClassName);
     assertThat(expr, isPresent());
     assertThat(expr, not(isRenamed()));
@@ -199,8 +192,6 @@
     KmClassSubject kmClass = expr.getKmClass();
     assertThat(kmClass, isPresent());
 
-    assertTrue(kmClass.getSealedSubclassDescriptors().isEmpty());
-
     ClassSubject libKt = inspector.clazz(libClassName);
     assertThat(expr, isPresent());
     assertThat(expr, not(isRenamed()));
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
index 5a32bba..313adc9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
@@ -8,7 +8,6 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -16,7 +15,6 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -150,20 +148,16 @@
             .compile()
             .inspect(this::inspect)
             .writeToZip();
-
-    // TODO(b/152306391): Reified type-parameters are not flagged correctly.
-    ProcessResult mainResult =
+    Path mainJar =
         kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
             .addClasspathFiles(libJar)
             .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typeargument_app", "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compileRaw();
-    assertEquals(1, mainResult.exitCode);
-    assertThat(
-        mainResult.stderr,
-        containsString(
-            "org.jetbrains.kotlin.codegen.CompilationException: "
-                + "Back-end (JVM) Internal error: wrong bytecode generated"));
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(mainJar)
+        .run(parameters.getRuntime(), PKG + ".typeargument_app.MainKt")
+        .assertSuccessWithOutput(EXPECTED);
   }
 
   private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index da97837..004fc31 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -50,7 +50,7 @@
             .addProgramFiles(ToolHelper.getKotlinReflectJar())
             .addKeepMainRule(mainClassName)
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .addKeepRules("-keep class kotlin.Metadata")
+            .addKeepKotlinMetadata()
             .allowDiagnosticWarningMessages()
             .setMinApi(parameters.getApiLevel())
             .compile()
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
index bbcc8e2..239697e 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.rewrite.assertions;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
@@ -249,10 +250,9 @@
     String main = ClassWithAssertions.class.getCanonicalName();
     // When running on the JVM enable assertions. For Art this is not possible, and assertions
     // can only be activated at compile time.
-    assert parameters.getRuntime().isCf();
+    assertTrue(parameters.isCfRuntime());
     result.enableRuntimeAssertions();
     result
-        .disassemble()
         .run(parameters.getRuntime(), main, "0")
         .assertFailureWithOutput(StringUtils.lines("1"));
     // Assertion is not hit.
@@ -304,10 +304,10 @@
     assertTrue(clazz.isPresent());
     MethodSubject conditionMethod =
         clazz.method(new MethodSignature("condition", "boolean", new String[]{}));
-    assertTrue(!conditionMethod.isPresent());
+    assertFalse(conditionMethod.isPresent());
     MethodSubject clinit =
         clazz.method(new MethodSignature(Constants.CLASS_INITIALIZER_NAME, "void", new String[]{}));
-    assertTrue(!clinit.isPresent());
+    assertFalse(clinit.isPresent());
   }
 
   @Test