StringBuilderAppendOptimizer as CodeRewriterPass

Bug: b/284304606
Change-Id: Idbea2ef38aa7609578d27a0d554b97734562df6e
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 6007ea9..8522b13 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
@@ -753,9 +753,7 @@
     new ArrayConstructionSimplifier(appView).run(code, timing);
     new MoveResultRewriter(appView).run(code, timing);
     if (options.enableStringConcatenationOptimization && !isDebugMode) {
-      timing.begin("Rewrite string concat");
-      StringBuilderAppendOptimizer.run(appView, code);
-      timing.end();
+      new StringBuilderAppendOptimizer(appView).run(code, timing);
     }
     timing.begin("Propagate sparse conditionals");
     new SparseConditionalConstantPropagation(appView, code).run();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
index 2798fb0..2ff8ec9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
@@ -16,6 +16,7 @@
 import static com.android.tools.r8.ir.optimize.string.StringBuilderNode.createToStringNode;
 import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
 
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult.SuccessfulDataflowAnalysisResult;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.IntraProceduralDataflowAnalysisOptions;
@@ -30,6 +31,8 @@
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.Phi;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.passes.CodeRewriterPass;
+import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
 import com.android.tools.r8.ir.optimize.string.StringBuilderNode.AppendNode;
 import com.android.tools.r8.ir.optimize.string.StringBuilderNode.ImplicitToStringNode;
 import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitNode;
@@ -75,29 +78,33 @@
  *
  * <p>Finally, based on all optimizations, the IR is updated to reflect the optimizations.
  */
-public class StringBuilderAppendOptimizer {
+public class StringBuilderAppendOptimizer extends CodeRewriterPass<AppInfo> {
 
-  private final AppView<?> appView;
   private final StringBuilderOracle oracle;
-  private final IRCode code;
-
   private static final int NUMBER_OF_MUNCHING_PASSES = 3;
 
-  private StringBuilderAppendOptimizer(AppView<?> appView, IRCode code) {
-    this.appView = appView;
-    this.code = code;
+  public StringBuilderAppendOptimizer(AppView<?> appView) {
+    super(appView);
     oracle = new DefaultStringBuilderOracle(appView.dexItemFactory());
   }
 
-  public static void run(AppView<?> appView, IRCode code) {
-    new StringBuilderAppendOptimizer(appView, code).run();
+  @Override
+  protected String getTimingId() {
+    return "StringBuilderAppendOptimizer";
   }
 
-  private void run() {
-    Map<Value, StringBuilderNode> stringBuilderGraphs = computeStringBuilderGraphs();
-    Map<Instruction, StringBuilderAction> actions = optimizeOnGraphs(stringBuilderGraphs);
+  @Override
+  protected boolean shouldRewriteCode(IRCode code) {
+    return code.metadata().mayHaveNewInstance()
+        || code.metadata().mayHaveInvokeMethodWithReceiver();
+  }
+
+  @Override
+  protected CodeRewriterResult rewriteCode(IRCode code) {
+    Map<Value, StringBuilderNode> stringBuilderGraphs = computeStringBuilderGraphs(code);
+    Map<Instruction, StringBuilderAction> actions = optimizeOnGraphs(code, stringBuilderGraphs);
     if (actions.isEmpty()) {
-      return;
+      return CodeRewriterResult.NO_CHANGE;
     }
     InstructionListIterator it = code.instructionListIterator();
     while (it.hasNext()) {
@@ -108,6 +115,7 @@
       }
     }
     code.removeAllDeadAndTrivialPhis();
+    return CodeRewriterResult.HAS_CHANGED;
   }
 
   private static class StringBuilderGraphState {
@@ -137,7 +145,7 @@
    * answer, for a given instruction, is a string builder value escaping and all escaped values
    * at the instruction.
    */
-  private Map<Value, StringBuilderNode> computeStringBuilderGraphs() {
+  private Map<Value, StringBuilderNode> computeStringBuilderGraphs(IRCode code) {
     StringBuilderEscapeTransferFunction transferFunction =
         new StringBuilderEscapeTransferFunction(oracle);
     IntraproceduralDataflowAnalysis<StringBuilderEscapeState> analysis =
@@ -516,7 +524,7 @@
    * the munching and care about performance.
    */
   private Map<Instruction, StringBuilderAction> optimizeOnGraphs(
-      Map<Value, StringBuilderNode> stringBuilderGraphs) {
+      IRCode code, Map<Value, StringBuilderNode> stringBuilderGraphs) {
     Map<Instruction, StringBuilderAction> actions = new IdentityHashMap<>();
     // Build state to allow munching over the string builder graphs.
     Map<StringBuilderNode, NewInstanceNode> newInstances = new IdentityHashMap<>();