Remove kotlin inline functions in D8

Bug: b/370968906
Change-Id: Ie95b7b4e6f9d277ea3ffe8b7cc9baa47b2315c1d
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
index 93b681a..9528327 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
@@ -33,6 +33,7 @@
 import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
 import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.kotlin.KotlinInlineMethodRemover;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.threading.ThreadingModule;
@@ -57,6 +58,7 @@
   public void convert(AppView<AppInfo> appView, ExecutorService executorService)
       throws ExecutionException {
     LambdaDeserializationMethodRemover.run(appView);
+    new KotlinInlineMethodRemover(appView).run(executorService);
     workaroundAbstractMethodOnNonAbstractClassVerificationBug(executorService);
     DexApplication application = appView.appInfo().app();
     ProfileCollectionAdditions profileCollectionAdditions =
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInlineMethodRemover.java b/src/main/java/com/android/tools/r8/kotlin/KotlinInlineMethodRemover.java
new file mode 100644
index 0000000..595e6a9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinInlineMethodRemover.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2024, 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 com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DefaultUseRegistryWithResult;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.Sets;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
+
+public class KotlinInlineMethodRemover {
+
+  private final AppView<AppInfo> appView;
+  private final DexType kotlinInlineMarkerType;
+  private final DexType kotlinMetadataType;
+
+  public KotlinInlineMethodRemover(AppView<AppInfo> appView) {
+    this.appView = appView;
+    this.kotlinInlineMarkerType =
+        appView.dexItemFactory().createType("Lkotlin/jvm/internal/InlineMarker;");
+    this.kotlinMetadataType = appView.dexItemFactory().kotlinMetadataType;
+  }
+
+  public void run(ExecutorService executorService) throws ExecutionException {
+    if (appView.options().isGeneratingDex()) {
+      ThreadUtils.processItems(
+          this::forEachKotlinClass,
+          this::processClass,
+          appView.options().getThreadingModule(),
+          executorService);
+    }
+  }
+
+  private void forEachKotlinClass(Consumer<DexProgramClass> consumer) {
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
+      if (clazz.annotations().hasAnnotation(kotlinMetadataType)) {
+        consumer.accept(clazz);
+      } else {
+        assert verifyNoKotlinInlineFunctions(clazz);
+      }
+    }
+  }
+
+  private void processClass(DexProgramClass clazz) {
+    Set<DexEncodedMethod> methodsToRemove = null;
+    for (ProgramMethod method : clazz.programMethods()) {
+      if (isKotlinInlineFunction(method)) {
+        assert method.getHolder().annotations().hasAnnotation(kotlinMetadataType);
+        if (methodsToRemove == null) {
+          methodsToRemove = Sets.newIdentityHashSet();
+        }
+        methodsToRemove.add(method.getDefinition());
+      }
+    }
+    if (methodsToRemove != null) {
+      clazz.getMethodCollection().removeMethods(methodsToRemove);
+    }
+  }
+
+  private boolean isKotlinInlineFunction(ProgramMethod method) {
+    return method.registerCodeReferencesWithResult(
+        new DefaultUseRegistryWithResult<>(appView, method, false) {
+
+          @Override
+          public void registerInvokeStatic(DexMethod method) {
+            if (method.getHolderType().isIdenticalTo(kotlinInlineMarkerType)) {
+              setResult(true);
+            }
+          }
+
+          @Override
+          public void registerInvokeSpecial(DexMethod method) {
+            // Intentionally empty. Override the default use registry behavior that attempts to
+            // convert this invoke-special to its corresponding dex invoke type. This is only
+            // possible after desugaring.
+          }
+        });
+  }
+
+  private boolean verifyNoKotlinInlineFunctions(DexProgramClass clazz) {
+    for (ProgramMethod method : clazz.programMethods()) {
+      assert !isKotlinInlineFunction(method);
+    }
+    return true;
+  }
+}