Remove catch handler deduplication from LinearScanRegisterAllocator

- the optimizations is mostly subsumed by the improvement suffix sharing and identical basic block sharing.
- the code size regression is because this optimizations was not considering a bug in Android N to P devices.

Change-Id: I2170237e6b512e9f9f457474edf538340a5b6720
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index fac67df..7bd8312 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -373,7 +373,6 @@
     insertRangeInvokeMoves();
     insertInitializedThisMove();
     ImmutableList<BasicBlock> blocks = computeLivenessInformation();
-    dedupCatchHandlerBlocks();
     timing.end();
     timing.begin("Allocate");
     performAllocation();
@@ -3491,77 +3490,6 @@
     code.blocks.forEach(BasicBlock::clearUserInfo);
   }
 
-  private void dedupCatchHandlerBlocks() {
-    List<BasicBlock> candidateBlocks = new ArrayList<>();
-    for (BasicBlock block : code.getBlocks()) {
-      if (block.hasUniquePredecessor()
-          && block.getUniquePredecessor().hasCatchSuccessor(block)
-          && liveAtEntrySets.get(block).isEmpty()
-          && block.size() <= 2) {
-        candidateBlocks.add(block);
-      }
-    }
-    if (candidateBlocks.isEmpty()) {
-      return;
-    }
-    Set<BasicBlock> removedBlocks = Sets.newIdentityHashSet();
-    for (BasicBlock candidateBlock : candidateBlocks) {
-      assert !removedBlocks.contains(candidateBlock);
-      BasicBlock equivalentBlock = null;
-      for (BasicBlock block : candidateBlocks) {
-        if (block == candidateBlock || removedBlocks.contains(block)) {
-          continue;
-        }
-        if (isEquivalentCatchHandlers(candidateBlock, block)) {
-          equivalentBlock = block;
-          break;
-        }
-      }
-      if (equivalentBlock == null) {
-        continue;
-      }
-      assert !candidateBlock.hasCatchHandlers();
-      removedBlocks.add(candidateBlock);
-      for (BasicBlock tryBlock : candidateBlock.getPredecessors()) {
-        tryBlock.replaceSuccessor(candidateBlock, equivalentBlock);
-        if (!equivalentBlock.getPredecessors().contains(tryBlock)) {
-          equivalentBlock.getMutablePredecessors().add(tryBlock);
-        }
-      }
-      for (BasicBlock successor : candidateBlock.getSuccessors()) {
-        int index = successor.getPredecessors().indexOf(candidateBlock);
-        successor.getMutablePredecessors().remove(index);
-        for (Phi phi : successor.getPhis()) {
-          phi.removeOperand(index);
-        }
-      }
-    }
-    code.removeBlocks(removedBlocks);
-  }
-
-  // TODO(b/153139043): Generalize this. Maybe use BasicBlock subsumption.
-  private boolean isEquivalentCatchHandlers(BasicBlock block, BasicBlock other) {
-    assert liveAtEntrySets.get(block).isEmpty();
-    assert liveAtEntrySets.get(other).isEmpty();
-    if (block.size() != other.size() || block.size() > 2) {
-      return false;
-    }
-    if (block.size() == 2) {
-      if (!block.entry().isMoveException() || !other.entry().isMoveException()) {
-        return false;
-      }
-    }
-    if (block.exit().isGoto()
-        && other.exit().isGoto()
-        && block.getUniqueNormalSuccessor() == other.getUniqueNormalSuccessor()) {
-      return true;
-    }
-    if (block.exit().isReturn() && other.exit().isReturn()) {
-      return true;
-    }
-    return false;
-  }
-
   // Rewrites casts on the form "lhs = (T) rhs" into "(T) rhs" and replaces the uses of lhs by rhs.
   // This transformation helps to ensure that we do not insert unnecessary moves in bridge methods
   // with an invoke-range instruction, since all the arguments to the invoke-range instruction will
diff --git a/src/test/java8/regress/com/android/tools/r8/regress/b120164595/B120164595.java b/src/test/java8/regress/com/android/tools/r8/regress/b120164595/B120164595.java
index e63bcbb..9d6a405 100644
--- a/src/test/java8/regress/com/android/tools/r8/regress/b120164595/B120164595.java
+++ b/src/test/java8/regress/com/android/tools/r8/regress/b120164595/B120164595.java
@@ -4,16 +4,26 @@
 
 package com.android.tools.r8.regress.b120164595;
 
+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.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestCompileResult;
-import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import java.io.IOException;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
 
 /**
  * Regression test for art issue with multi catch-handlers.
@@ -43,34 +53,60 @@
   }
 }
 
+@RunWith(Parameterized.class)
 public class B120164595 extends TestBase {
-  @Test
-  public void testD8()
-      throws IOException, CompilationFailedException {
-    TestCompileResult d8Result = testForD8().addProgramClasses(TestClass.class).compile();
-    checkArt(d8Result);
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
   }
 
   @Test
-  public void testR8()
-      throws IOException, CompilationFailedException {
-    TestCompileResult r8Result = testForR8(Backend.DEX)
-        .addProgramClasses(TestClass.class)
-        .addKeepClassAndMembersRules(TestClass.class)
-        .compile();
-    checkArt(r8Result);
+  public void testD8() throws Exception {
+    TestCompileResult<?, ?> d8Result =
+        testForD8(parameters.getBackend())
+            .addProgramClasses(TestClass.class)
+            .setMinApi(parameters)
+            .compile();
+    checkResult(d8Result);
   }
 
-  private void checkArt(TestCompileResult result) throws IOException {
-    ProcessResult artResult =
-        runOnArtRaw(
-            result.app,
-            TestClass.class.getCanonicalName(),
-            builder -> {
-              builder.appendArtOption("-Xusejit:true");
-            },
-            DexVm.fromVersion(DexVm.Version.last()));
-    assertEquals(0, artResult.exitCode);
-    assertFalse(artResult.stderr.contains("Expected NullPointerException"));
+  @Test
+  public void testR8() throws Exception {
+    TestCompileResult<?, ?> r8Result =
+        testForR8(parameters.getBackend())
+            .addProgramClasses(TestClass.class)
+            .addKeepClassAndMembersRules(TestClass.class)
+            .setMinApi(parameters)
+            .compile();
+    checkResult(r8Result);
+  }
+
+  private void checkResult(TestCompileResult<?, ?> result) throws Exception {
+    result.inspect(
+        inspector -> {
+          ClassSubject classSubject = inspector.clazz(TestClass.class);
+          assertThat(classSubject, isPresent());
+          MethodSubject methodSubject = classSubject.uniqueMethodWithOriginalName("toBeOptimized");
+          assertThat(methodSubject, isPresent());
+          DexCode code = methodSubject.getMethod().getCode().asDexCode();
+          assertEquals(1, code.getHandlers().length);
+          TryHandler handler = code.getHandlers()[0];
+          assertEquals(2, handler.pairs.length);
+          if (parameters.getApiLevel().isLessThan(AndroidApiLevel.Q)) {
+            assertNotEquals(handler.pairs[0].addr, handler.pairs[1].addr);
+          } else {
+            assertEquals(handler.pairs[0].addr, handler.pairs[1].addr);
+          }
+        });
+    result
+        .applyIf(
+            parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0),
+            r -> r.addVmArguments("-Xusejit:true"))
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithEmptyOutput();
   }
 }