Add utils to build and extend CodeOptimization.

Bug: 140766440
Change-Id: I3cddf8ac1d3542806b117cde8f728072bd676a2d
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java b/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java
index 5d73cd5..8a40db4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java
@@ -6,9 +6,14 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.function.Consumer;
 
 /**
- * An abstraction of {@link IRCode}-level optimization.
+ * An abstraction of {@link IRCode}-level optimization, which may retrieve info from
+ * {@link AppView}; update {@link OptimizationFeedback}; or utilize {@link MethodProcessor}.
  */
 public interface CodeOptimization {
 
@@ -17,9 +22,61 @@
   //  rewriting every affected optimization.
   // Note that a code optimization can be a collection of other code optimizations.
   // In that way, IRConverter will serve as the default full processing of all optimizations.
-  void optimize(
-      AppView<?> appView,
-      IRCode code,
-      OptimizationFeedback feedback,
-      MethodProcessor methodProcessor);
+  void optimize(IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor);
+
+  static CodeOptimization from(Consumer<IRCode> consumer) {
+    return (code, feedback, methodProcessor) -> {
+      consumer.accept(code);
+    };
+  }
+
+  static CodeOptimization sequence(CodeOptimization... codeOptimizations) {
+    return sequence(Arrays.asList(codeOptimizations));
+  }
+
+  static CodeOptimization sequence(Collection<CodeOptimization> codeOptimizations) {
+    return (code, feedback, methodProcessor) -> {
+      for (CodeOptimization codeOptimization : codeOptimizations) {
+        codeOptimization.optimize(code, feedback, methodProcessor);
+      }
+    };
+  }
+
+  /**
+   * Builder for {@link CodeOptimization}.
+   *
+   * Users can append either {@link CodeOptimization}, or simply {@link IRCode} consumer.
+   *
+   * Note that the order of everything that is appended through the builder matters.
+   */
+  public static class Builder {
+    private ImmutableList.Builder<CodeOptimization> processingQueue;
+
+    private Builder() {
+      processingQueue = ImmutableList.builder();
+    }
+
+    public static Builder builder() {
+      return new Builder();
+    }
+
+    public Builder addIRCodeConsumer(Consumer<IRCode> consumer) {
+      processingQueue.add(from(consumer));
+      return this;
+    }
+
+    public Builder addCodeOptimization(CodeOptimization optimization) {
+      processingQueue.add(optimization);
+      return this;
+    }
+
+    public CodeOptimization build() {
+      return (code, feedback, methodProcessor) -> {
+        processingQueue
+            .build()
+            .forEach(codeOptimization ->
+                codeOptimization.optimize(code, feedback, methodProcessor));
+      };
+    }
+  }
 }
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 00d2f79..b3a6d66 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
@@ -1092,12 +1092,11 @@
       feedback.markProcessed(method, ConstraintWithTarget.NEVER);
       return;
     }
-    optimize(appView, code, feedback, methodProcessor);
+    optimize(code, feedback, methodProcessor);
   }
 
   // TODO(b/140766440): Convert all sub steps an implementer of CodeOptimization
   private void optimize(
-      AppView<?> appView,
       IRCode code,
       OptimizationFeedback feedback,
       MethodProcessor methodProcessor) {
@@ -1218,7 +1217,7 @@
       stringOptimizer.computeTrivialOperationsOnConstString(code);
       stringOptimizer.removeTrivialConversions(code);
       if (libraryMethodOptimizer != null) {
-        libraryMethodOptimizer.optimize(appView, code, feedback, methodProcessor);
+        libraryMethodOptimizer.optimize(code, feedback, methodProcessor);
       }
       assert code.isConsistentSSA();
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index 9ecc62d..7e0c9d5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -153,7 +153,7 @@
     }
     // TODO(b/140768815): Reprocessing may trigger more methods to revisit. Update waves on-the-fly.
     for (CodeOptimization codeOptimization : codeOptimizations) {
-      codeOptimization.optimize(appView, code, feedback, this);
+      codeOptimization.optimize(code, feedback, this);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
index 0a92679..9c5a8d1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
@@ -24,10 +24,13 @@
 
 public class LibraryMethodOptimizer implements CodeOptimization {
 
+  private final AppView<?> appView;
+
   private final Map<DexType, LibraryMethodModelCollection> libraryMethodModelCollections =
       new IdentityHashMap<>();
 
   public LibraryMethodOptimizer(AppView<? extends AppInfoWithSubtyping> appView) {
+    this.appView = appView;
     register(new BooleanMethodOptimizer(appView));
   }
 
@@ -39,7 +42,6 @@
 
   @Override
   public void optimize(
-      AppView<?> appView,
       IRCode code,
       OptimizationFeedback feedback,
       MethodProcessor methodProcessor) {