Make ThrowCatchOptimizer a CodeRewriterPass

Bug: b/284304606
Change-Id: I231eb42accbb2310b3eec0bd6c096d0b54117b74
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 8941176..327c74c 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
@@ -760,9 +760,7 @@
       new StringBuilderAppendOptimizer(appView).run(code, timing);
     }
     new SparseConditionalConstantPropagation(appView, code).run(code, timing);
-    timing.begin("Rewrite always throwing instructions");
-    new ThrowCatchOptimizer(appView).optimizeAlwaysThrowingInstructions(code);
-    timing.end();
+    new ThrowCatchOptimizer(appView, isDebugMode).run(code, timing);
     timing.begin("Simplify control flow");
     if (new BranchSimplifier(appView).simplifyBranches(code)) {
       new TrivialCheckCastAndInstanceOfRemover(appView)
@@ -788,13 +786,6 @@
       invertConditionalsForTesting(code);
     }
 
-    if (!isDebugMode) {
-      timing.begin("Rewrite throw NPE");
-      new ThrowCatchOptimizer(appView).rewriteThrowNullPointerException(code);
-      timing.end();
-      previous = printMethod(code, "IR after rewrite throw null (SSA)", previous);
-    }
-
     timing.begin("Optimize class initializers");
     ClassInitializerDefaultsResult classInitializerDefaultsResult =
         classInitializerDefaultsOptimization.optimize(code, feedback);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java
index f34a6bd..f5a6841 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java
@@ -5,10 +5,10 @@
 package com.android.tools.r8.ir.conversion.passes;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -32,6 +32,7 @@
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Throw;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
@@ -40,18 +41,41 @@
 import java.util.ListIterator;
 import java.util.Set;
 
-public class ThrowCatchOptimizer {
+public class ThrowCatchOptimizer extends CodeRewriterPass<AppInfo> {
 
-  private final AppView<?> appView;
-  private final DexItemFactory dexItemFactory;
+  private final boolean rewriteThrowNull;
+
+  public ThrowCatchOptimizer(AppView<?> appView, boolean isDebug) {
+    super(appView);
+    this.rewriteThrowNull = !isDebug;
+  }
 
   public ThrowCatchOptimizer(AppView<?> appView) {
-    this.appView = appView;
-    this.dexItemFactory = appView.dexItemFactory();
+    super(appView);
+    this.rewriteThrowNull = false;
+  }
+
+  @Override
+  protected String getTimingId() {
+    return "ThrowCatchOptimizer";
+  }
+
+  @Override
+  protected boolean shouldRewriteCode(IRCode code) {
+    return true;
+  }
+
+  @Override
+  protected CodeRewriterResult rewriteCode(IRCode code) {
+    optimizeAlwaysThrowingInstructions(code);
+    if (rewriteThrowNull) {
+      rewriteThrowNullPointerException(code);
+    }
+    return CodeRewriterResult.NONE;
   }
 
   // Rewrite 'throw new NullPointerException()' to 'throw null'.
-  public void rewriteThrowNullPointerException(IRCode code) {
+  private void rewriteThrowNullPointerException(IRCode code) {
     boolean shouldRemoveUnreachableBlocks = false;
     for (BasicBlock block : code.blocks) {
       InstructionListIterator it = block.listIterator(code);
@@ -64,7 +88,7 @@
           if (appView
               .dexItemFactory()
               .objectsMethods
-              .isRequireNonNullMethod(code.method().getReference())) {
+              .isRequireNonNullMethod(code.context().getReference())) {
             continue;
           }
 
@@ -192,7 +216,7 @@
   // Find all instructions that always throw, split the block after each such instruction and follow
   // it with a block throwing a null value (which should result in NPE). Note that this throw is not
   // expected to be ever reached, but is intended to satisfy verifier.
-  public void optimizeAlwaysThrowingInstructions(IRCode code) {
+  private void optimizeAlwaysThrowingInstructions(IRCode code) {
     Set<Value> affectedValues = Sets.newIdentityHashSet();
     Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet();
     ListIterator<BasicBlock> blockIterator = code.listIterator();
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
index 1c0d3de..f7cf0b5 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
@@ -270,7 +270,7 @@
     rewriter.rewriteCode(ir, initializedClassesWithContexts, prunedFields);
 
     // Run dead code elimination.
-    new ThrowCatchOptimizer(appView).optimizeAlwaysThrowingInstructions(ir);
+    new ThrowCatchOptimizer(appView).run(ir, Timing.empty());
     rewriter.getDeadCodeRemover().run(ir, Timing.empty());
 
     // Finalize to class files or dex.