Report total size of Composable methods in benchmarks

Change-Id: I25d81f3e9f4b91715fddb869ac112ce2b2f24d57
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
index b04a407..b73738e 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.benchmarks;
 
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -12,7 +11,6 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
@@ -26,20 +24,6 @@
       throw new BenchmarkConfigError(
           "Inconsistent single/group benchmark setup: " + benchmark + " and " + other);
     }
-    Set<String> subNames =
-        Sets.union(benchmark.getSubBenchmarks().keySet(), other.getSubBenchmarks().keySet());
-    for (String subName : subNames) {
-      if (!Objects.equals(
-          benchmark.getSubBenchmarks().get(subName), other.getSubBenchmarks().get(subName))) {
-        throw new BenchmarkConfigError(
-            "Inconsistent metrics for sub-benchmark "
-                + subName
-                + " in benchmarks: "
-                + benchmark
-                + " and "
-                + other);
-      }
-    }
     if (!benchmark.getSuite().equals(other.getSuite())) {
       throw new BenchmarkConfigError(
           "Inconsistent suite for benchmarks: " + benchmark + " and " + other);
@@ -182,6 +166,11 @@
       return this;
     }
 
+    public Builder measureComposableCodeSize() {
+      metrics.add(BenchmarkMetric.ComposableCodeSize);
+      return this;
+    }
+
     public Builder measureWarmup() {
       measureWarmup = true;
       return this;
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsCollection.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsCollection.java
index da3f3d2..8fc8508 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsCollection.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsCollection.java
@@ -24,22 +24,25 @@
   @Override
   public void addRuntimeResult(long result) {
     throw new BenchmarkConfigError(
-        "Unexpected attempt to add a runtime result to a the root of a benchmark with"
-            + " sub-benchmarks");
+        "Unexpected attempt to add a result to a the root of a benchmark with sub-benchmarks");
   }
 
   @Override
   public void addCodeSizeResult(long result) {
     throw new BenchmarkConfigError(
-        "Unexpected attempt to add a runtime result to a the root of a benchmark with"
-            + " sub-benchmarks");
+        "Unexpected attempt to add a result to a the root of a benchmark with sub-benchmarks");
+  }
+
+  @Override
+  public void addComposableCodeSizeResult(long result) {
+    throw new BenchmarkConfigError(
+        "Unexpected attempt to add a result to a the root of a benchmark with sub-benchmarks");
   }
 
   @Override
   public void addResourceSizeResult(long result) {
     throw new BenchmarkConfigError(
-        "Unexpected attempt to add a runtime result to a the root of a benchmark with"
-            + " sub-benchmarks");
+        "Unexpected attempt to add a result to a the root of a benchmark with sub-benchmarks");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java
index e15dffb..ad6b1b3 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java
@@ -9,13 +9,15 @@
 import it.unimi.dsi.fastutil.longs.LongList;
 import java.io.PrintStream;
 import java.util.Set;
+import java.util.function.LongConsumer;
 
 public class BenchmarkResultsSingle implements BenchmarkResults {
 
-  private String name;
+  private final String name;
   private final Set<BenchmarkMetric> metrics;
   private final LongList runtimeResults = new LongArrayList();
   private final LongList codeSizeResults = new LongArrayList();
+  private final LongList composableCodeSizeResults = new LongArrayList();
 
   public BenchmarkResultsSingle(String name, Set<BenchmarkMetric> metrics) {
     this.name = name;
@@ -30,6 +32,10 @@
     return codeSizeResults;
   }
 
+  public LongList getComposableCodeSizeResults() {
+    return composableCodeSizeResults;
+  }
+
   public LongList getRuntimeResults() {
     return runtimeResults;
   }
@@ -47,6 +53,15 @@
   }
 
   @Override
+  public void addComposableCodeSizeResult(long result) {
+    verifyMetric(
+        BenchmarkMetric.ComposableCodeSize,
+        metrics.contains(BenchmarkMetric.ComposableCodeSize),
+        true);
+    composableCodeSizeResults.add(result);
+  }
+
+  @Override
   public void addResourceSizeResult(long result) {
     addCodeSizeResult(result);
   }
@@ -78,6 +93,10 @@
         BenchmarkMetric.CodeSize,
         metrics.contains(BenchmarkMetric.CodeSize),
         !codeSizeResults.isEmpty());
+    verifyMetric(
+        BenchmarkMetric.ComposableCodeSize,
+        metrics.contains(BenchmarkMetric.ComposableCodeSize),
+        !composableCodeSizeResults.isEmpty());
   }
 
   private void printRunTime(long duration) {
@@ -89,6 +108,11 @@
     System.out.println(BenchmarkResults.prettyMetric(name, BenchmarkMetric.CodeSize, "" + bytes));
   }
 
+  private void printComposableCodeSize(long bytes) {
+    System.out.println(
+        BenchmarkResults.prettyMetric(name, BenchmarkMetric.ComposableCodeSize, "" + bytes));
+  }
+
   @Override
   public void printResults(ResultMode mode, boolean failOnCodeSizeDifferences) {
     verifyConfigAndResults();
@@ -97,6 +121,13 @@
       long result = mode == ResultMode.SUM ? sum : sum / runtimeResults.size();
       printRunTime(result);
     }
+    printCodeSizeResults(codeSizeResults, failOnCodeSizeDifferences, this::printCodeSize);
+    printCodeSizeResults(
+        composableCodeSizeResults, failOnCodeSizeDifferences, this::printComposableCodeSize);
+  }
+
+  private static void printCodeSizeResults(
+      LongList codeSizeResults, boolean failOnCodeSizeDifferences, LongConsumer printer) {
     if (!codeSizeResults.isEmpty()) {
       long size = codeSizeResults.getLong(0);
       if (failOnCodeSizeDifferences) {
@@ -107,7 +138,7 @@
           }
         }
       }
-      printCodeSize(size);
+      printer.accept(size);
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingleAdapter.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingleAdapter.java
index 55936d7..68d6fb8 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingleAdapter.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingleAdapter.java
@@ -22,6 +22,10 @@
         (codeSizeResult, iteration) -> {
           JsonObject resultObject = new JsonObject();
           resultObject.addProperty("code_size", codeSizeResult);
+          if (!result.getComposableCodeSizeResults().isEmpty()) {
+            resultObject.addProperty(
+                "composable_code_size", result.getComposableCodeSizeResults().getLong(iteration));
+          }
           resultObject.addProperty("runtime", result.getRuntimeResults().getLong(iteration));
           resultsArray.add(resultObject);
         });
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsWarmup.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsWarmup.java
index 1a4e33c..19c1de8 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsWarmup.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsWarmup.java
@@ -13,6 +13,7 @@
   private final String name;
   private final LongList runtimeResults = new LongArrayList();
   private long codeSizeResult = -1;
+  private long composableCodeSizeResult = -1;
   private long resourceSizeResult = -1;
 
   public BenchmarkResultsWarmup(String name) {
@@ -36,6 +37,20 @@
   }
 
   @Override
+  public void addComposableCodeSizeResult(long result) {
+    if (composableCodeSizeResult == -1) {
+      composableCodeSizeResult = result;
+    }
+    if (composableCodeSizeResult != result) {
+      throw new RuntimeException(
+          "Unexpected Composable code size difference: "
+              + result
+              + " and "
+              + composableCodeSizeResult);
+    }
+  }
+
+  @Override
   public void addResourceSizeResult(long result) {
     if (resourceSizeResult == -1) {
       resourceSizeResult = result;
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
index 9454d6e..1f087c1 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
@@ -20,7 +20,12 @@
 import com.android.tools.r8.benchmarks.BenchmarkTarget;
 import com.android.tools.r8.dump.CompilerDump;
 import com.android.tools.r8.dump.DumpOptions;
+import com.android.tools.r8.keepanno.annotations.AnnotationPattern;
+import com.android.tools.r8.keepanno.annotations.KeepEdge;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
 import java.io.IOException;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -31,6 +36,18 @@
 
 public class AppDumpBenchmarkBuilder {
 
+  @KeepEdge(
+      consequences =
+          @KeepTarget(
+              kind = KeepItemKind.ONLY_METHODS,
+              methodAnnotatedByClassName = "androidx.compose.runtime.Composable",
+              constraints = {},
+              constrainAnnotations =
+                  @AnnotationPattern(
+                      name = "androidx.compose.runtime.Composable",
+                      retention = RetentionPolicy.CLASS)))
+  static class KeepComposableAnnotations {}
+
   public static AppDumpBenchmarkBuilder builder() {
     return new AppDumpBenchmarkBuilder();
   }
@@ -99,6 +116,7 @@
         .addDependency(dumpDependency)
         .measureRunTime()
         .measureCodeSize()
+        .measureComposableCodeSize()
         .setTimeout(10, TimeUnit.MINUTES)
         .build();
   }
@@ -158,6 +176,10 @@
     return name + "Code";
   }
 
+  private String nameForComposableCodePart() {
+    return name + "ComposableCode";
+  }
+
   private String nameForLibraryPart() {
     return name + "Library";
   }
@@ -210,6 +232,16 @@
                 options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces());
   }
 
+  private static ThrowableConsumer<? super R8FullTestBuilder>
+      getKeepComposableAnnotationsConfiguration() {
+    return testBuilder ->
+        testBuilder
+            .addProgramClasses(KeepComposableAnnotations.class)
+            .addKeepClassAndMembersRules("androidx.compose.runtime.Composable")
+            .addKeepRuntimeVisibleAnnotations()
+            .enableExperimentalKeepAnnotations();
+  }
+
   private static BenchmarkMethod runR8(
       AppDumpBenchmarkBuilder builder, ThrowableConsumer<? super R8FullTestBuilder> configuration) {
     return internalRunR8(builder, false, configuration);
@@ -253,6 +285,12 @@
                           })
                       .apply(configuration)
                       .applyIf(
+                          environment
+                              .getConfig()
+                              .getMetrics()
+                              .contains(BenchmarkMetric.ComposableCodeSize),
+                          getKeepComposableAnnotationsConfiguration())
+                      .applyIf(
                           enableResourceShrinking,
                           b ->
                               b.enableOptimizedShrinking()
@@ -264,9 +302,14 @@
                                       results.getSubResults(builder.nameForRuntimePart()))
                                   .benchmarkCodeSize(
                                       results.getSubResults(builder.nameForCodePart()))
+                                  .benchmarkComposableCodeSize(
+                                      results.getSubResults(builder.nameForComposableCodePart()))
                                   .benchmarkResourceSize(
                                       results.getSubResults(builder.nameForResourcePart())),
-                          r -> r.benchmarkCompile(results).benchmarkCodeSize(results));
+                          r ->
+                              r.benchmarkCompile(results)
+                                  .benchmarkCodeSize(results)
+                                  .benchmarkComposableCodeSize(results));
                 });
   }
 
diff --git a/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java
index 172fb2b..7a4f1ba 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -12,6 +13,8 @@
 import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.ResourceTableInspector;
 import com.android.tools.r8.benchmarks.BenchmarkResults;
 import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.metadata.R8BuildMetadata;
 import com.android.tools.r8.profile.art.model.ExternalArtProfile;
 import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
@@ -19,13 +22,14 @@
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.IntBox;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThrowingBiConsumer;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import com.android.tools.r8.utils.ZipUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.Matchers;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import com.android.tools.r8.utils.graphinspector.GraphInspector;
 import java.io.IOException;
 import java.io.UncheckedIOException;
@@ -266,21 +270,18 @@
       throws IOException {
     assert getBackend() == runtime.getBackend();
     ClassSubject mainClassSubject = inspector().clazz(SplitRunner.class);
-    assertThat(
-        "Did you forget a keep rule for the main method?", mainClassSubject, Matchers.isPresent());
+    assertThat("Did you forget a keep rule for the main method?", mainClassSubject, isPresent());
     assertThat(
         "Did you forget a keep rule for the main method?",
         mainClassSubject.mainMethod(),
-        Matchers.isPresent());
+        isPresent());
     ClassSubject mainFeatureClassSubject = featureInspector(feature).clazz(mainFeatureClass);
     assertThat(
-        "Did you forget a keep rule for the run method?",
-        mainFeatureClassSubject,
-        Matchers.isPresent());
+        "Did you forget a keep rule for the run method?", mainFeatureClassSubject, isPresent());
     assertThat(
         "Did you forget a keep rule for the run method?",
         mainFeatureClassSubject.uniqueMethodWithOriginalName("run"),
-        Matchers.isPresent());
+        isPresent());
     String[] args = new String[2 + featureDependencies.length];
     args[0] = mainFeatureClassSubject.getFinalName();
     args[1] = feature.toString();
@@ -319,4 +320,28 @@
     results.addCodeSizeResult(applicationSizeWithFeatures);
     return self();
   }
+
+  @Override
+  public R8TestCompileResult benchmarkComposableCodeSize(BenchmarkResults results)
+      throws IOException {
+    if (!features.isEmpty()) {
+      throw new Unimplemented();
+    }
+    CodeInspector inspector = inspector();
+    ClassSubject annotationClassSubject = inspector.clazz("androidx.compose.runtime.Composable");
+    assertThat(annotationClassSubject, isPresent());
+    IntBox composableCodeSize = new IntBox();
+    for (FoundClassSubject classSubject : inspector.allClasses()) {
+      for (ProgramMethod method : classSubject.getDexProgramClass().directProgramMethods()) {
+        if (method
+            .getAnnotations()
+            .hasAnnotation(annotationClassSubject.getDexProgramClass().getType())) {
+          composableCodeSize.increment(
+              method.getDefinition().getCode().asDexCode().codeSizeInBytes());
+        }
+      }
+    }
+    results.addComposableCodeSizeResult(composableCodeSize.get());
+    return self();
+  }
 }
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBuilder.java b/src/test/testbase/java/com/android/tools/r8/TestBuilder.java
index a7c2cd8..5f9d323 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBuilder.java
@@ -50,7 +50,7 @@
     return self();
   }
 
-  public T applyIf(boolean value, ThrowableConsumer<T> consumer) {
+  public T applyIf(boolean value, ThrowableConsumer<? super T> consumer) {
     T self = self();
     if (value) {
       consumer.acceptWithRuntimeException(self);
diff --git a/src/test/testbase/java/com/android/tools/r8/TestCompileResult.java b/src/test/testbase/java/com/android/tools/r8/TestCompileResult.java
index 8f32eae..1f50960 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestCompileResult.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.benchmarks.BenchmarkResults;
 import com.android.tools.r8.debug.DebugTestConfig;
+import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -721,4 +722,9 @@
     results.addCodeSizeResult(app.applicationSize());
     return self();
   }
+
+  public CR benchmarkComposableCodeSize(BenchmarkResults results)
+      throws IOException, ResourceException {
+    throw new Unimplemented();
+  }
 }
diff --git a/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkMetric.java b/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkMetric.java
index 62bc0ba..abf93ce 100644
--- a/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkMetric.java
+++ b/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkMetric.java
@@ -6,6 +6,7 @@
 public enum BenchmarkMetric {
   RunTimeRaw,
   CodeSize,
+  ComposableCodeSize,
   StartupTime;
 
   public String getDartType() {
diff --git a/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkResults.java b/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
index 9dcfa97..a925368 100644
--- a/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
+++ b/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
@@ -13,6 +13,8 @@
   // Append a code size result. This is always assumed to be identical if called multiple times.
   void addCodeSizeResult(long result);
 
+  void addComposableCodeSizeResult(long result);
+
   // Append a resource size result. This is always assumed to be identical if called multiple times.
   void addResourceSizeResult(long result);
 
diff --git a/tools/run_benchmark.py b/tools/run_benchmark.py
index 6bf0882..f01535f 100755
--- a/tools/run_benchmark.py
+++ b/tools/run_benchmark.py
@@ -18,7 +18,9 @@
     utils.GRADLE_TASK_TESTBASE_WITH_APPLY_MAPPING_JAR,
     utils.GRADLE_TASK_TEST_DEPS_JAR
 ]
-GOLEM_BUILD_TARGETS = [utils.GRADLE_TASK_R8LIB] + GOLEM_BUILD_TARGETS_TESTS
+GOLEM_BUILD_TARGETS = [
+    utils.GRADLE_TASK_R8LIB, utils.GRADLE_TASK_KEEP_ANNO_JAR
+] + GOLEM_BUILD_TARGETS_TESTS
 
 
 def get_golem_resource_path(benchmark):
@@ -46,6 +48,13 @@
         required=True,
         # These should 1:1 with benchmarks/BenchmarkTarget.java
         choices=['d8', 'r8-full', 'r8-force', 'r8-compat'])
+    result.add_argument(
+        '--debug-agent',
+        '--debug_agent',
+        help=
+        'Enable Java debug agent and suspend compilation (default disabled)',
+        default=False,
+        action='store_true')
     result.add_argument('--nolib',
                         '--no-lib',
                         '--no-r8lib',
@@ -119,7 +128,8 @@
             utils.GRADLE_TASK_TEST_JAR, utils.GRADLE_TASK_TEST_DEPS_JAR,
             utils.GRADLE_TASK_TEST_UNZIP_TESTBASE
         ]
-        buildTargets = [utils.GRADLE_TASK_R8] + testBuildTargets
+        buildTargets = [utils.GRADLE_TASK_R8, utils.GRADLE_TASK_KEEP_ANNO_JAR
+                       ] + testBuildTargets
         r8jar = utils.R8_JAR
         testjars = [
             utils.R8_TESTS_JAR, utils.R8_TESTS_DEPS_JAR, utils.R8_TESTBASE_JAR
@@ -157,7 +167,8 @@
     cmd = [
         jdk.GetJavaExecutable(jdkhome), '-Xms8g', '-Xmx8g',
         '-XX:+TieredCompilation', '-XX:TieredStopAtLevel=4',
-        '-DBENCHMARK_IGNORE_CODE_SIZE_DIFFERENCES'
+        '-DBENCHMARK_IGNORE_CODE_SIZE_DIFFERENCES',
+        f'-DBUILD_PROP_KEEPANNO_RUNTIME_PATH={utils.REPO_ROOT}/d8_r8/keepanno/build/classes/java/main'
     ]
     if options.enable_assertions:
         cmd.append('-ea')
@@ -175,6 +186,10 @@
     if options.output:
         cmd.append(f'-DBENCHMARK_OUTPUT={options.output}')
     cmd.extend(['-cp', ':'.join([r8jar] + testjars)])
+    if options.debug_agent:
+        cmd.append(
+            '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005'
+        )
     cmd.extend([
         'com.android.tools.r8.benchmarks.BenchmarkMainEntryRunner',
         options.benchmark,