Test limiting exception range before throwing instruction

Bug: 122445224
Change-Id: I1c841a41efef5142ab87ef8ff8b93fe66fea3151
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 6e5e5d7..4dcc839 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -149,7 +149,9 @@
     computeInitializers();
     typeVerificationHelper = new TypeVerificationHelper(code, factory, appInfo);
     typeVerificationHelper.computeVerificationTypes();
-    splitExceptionalBlocks();
+    if (!options.testing.noSplittingExceptionalEdges) {
+      splitExceptionalBlocks();
+    }
     rewriter.converter.deadCodeRemover.run(code);
     rewriteNots();
     LoadStoreHelper loadStoreHelper = new LoadStoreHelper(code, typeVerificationHelper, appInfo);
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 e96acfb..b1ea3f9 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
@@ -879,6 +879,10 @@
     printC1VisualizerHeader(method);
     String previous = printMethod(code, "Initial IR (SSA)", null);
 
+    if (options.testing.irModifier != null) {
+      options.testing.irModifier.accept(code);
+    }
+
     if (options.canHaveArtStringNewInitBug()) {
       CodeRewriter.ensureDirectStringNewToInit(code);
     }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 4921c79..f34e059 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.ProguardConfiguration;
@@ -532,6 +533,8 @@
     public boolean noLocalsTableOnInput = false;
     public boolean forceNameReflectionOptimization = false;
     public boolean disallowLoadStoreOptimization = false;
+    public Consumer<IRCode> irModifier = null;
+    public boolean noSplittingExceptionalEdges = false;
   }
 
   private boolean hasMinApi(AndroidApiLevel level) {
diff --git a/src/test/java/com/android/tools/r8/cf/TryRangeTestLimitRange.java b/src/test/java/com/android/tools/r8/cf/TryRangeTestLimitRange.java
new file mode 100644
index 0000000..5e97ab0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/TryRangeTestLimitRange.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.cf;
+
+import com.android.tools.r8.NeverInline;
+
+public class TryRangeTestLimitRange {
+
+  @NeverInline
+  public static float doSomething(int x) throws Exception {
+    if (x == 42) {
+      throw new Exception("is 42");
+    } else {
+      return 1;
+    }
+  }
+
+  public static void main(String[] args) {
+    int x = args.length;
+    int y = x + 1;
+    try {
+      doSomething(y);
+    } catch (Exception ex) {
+      System.out.println(x + ": " + y);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
index 98542ff..e4c7098 100644
--- a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
@@ -4,8 +4,14 @@
 
 package com.android.tools.r8.cf;
 
+import static org.junit.Assert.assertTrue;
+
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import java.util.ListIterator;
 import org.junit.Test;
 
 /**
@@ -18,7 +24,7 @@
 public class TryRangeTestRunner extends TestBase {
 
   @Test
-  public void test() throws Exception {
+  public void testRegisterAllocationLimitTrailingRange() throws Exception {
     testForR8(Backend.CF)
         .addProgramClasses(TryRangeTest.class)
         .addKeepMainRule(TryRangeTest.class)
@@ -26,8 +32,55 @@
         .minification(false)
         .noTreeShaking()
         .enableInliningAnnotations()
-        .addOptionsModification(o -> o.testing.disallowLoadStoreOptimization = true)
+        .addOptionsModification(
+            o -> {
+              o.testing.disallowLoadStoreOptimization = true;
+            })
         .run(TryRangeTest.class)
         .assertSuccess();
   }
+
+  @Test
+  public void testRegisterAllocationLimitLeadingRange() throws Exception {
+    testForR8(Backend.CF)
+        .addProgramClasses(TryRangeTestLimitRange.class)
+        .addKeepMainRule(TryRangeTestLimitRange.class)
+        .setMode(CompilationMode.RELEASE)
+        .minification(false)
+        .noTreeShaking()
+        .enableInliningAnnotations()
+        .addOptionsModification(
+            o -> {
+              o.testing.disallowLoadStoreOptimization = true;
+              o.testing.irModifier = this::processIR;
+              // TODO(mkroghj) Remove this option entirely when splittingExceptionalEdges is moved.
+              o.testing.noSplittingExceptionalEdges = true;
+            })
+        .run(TryRangeTestLimitRange.class)
+        .assertFailure();
+  }
+
+  private void processIR(IRCode code) {
+    if (!code.method.qualifiedName().equals(TryRangeTestLimitRange.class.getName() + ".main")) {
+      return;
+    }
+    BasicBlock entryBlock = code.blocks.get(0);
+    BasicBlock tryBlock = code.blocks.get(1);
+    assertTrue(tryBlock.hasCatchHandlers());
+    ListIterator<Instruction> it = entryBlock.getInstructions().listIterator();
+    Instruction constNumber = it.next();
+    while (!constNumber.isConstNumber()) {
+      constNumber = it.next();
+    }
+    it.remove();
+    Instruction add = it.next();
+    while (!add.isAdd()) {
+      add = it.next();
+    }
+    it.remove();
+    constNumber.setBlock(tryBlock);
+    add.setBlock(tryBlock);
+    tryBlock.getInstructions().add(0, add);
+    tryBlock.getInstructions().add(0, constNumber);
+  }
 }