Handle catch phis in code sinking.

When the user of an instruction we want to sink is a catch
phi, we should not look at predecessors (which don't map 1-1
for catch phis), but can only look at its dominator.

bug:37247890
Test: 647-sinking-catch
Change-Id: Ib64bd6f95d3ef45c394137e76819fa8d7d3d960a
diff --git a/compiler/optimizing/code_sinking.cc b/compiler/optimizing/code_sinking.cc
index dc3d378..0b4dcd3 100644
--- a/compiler/optimizing/code_sinking.cc
+++ b/compiler/optimizing/code_sinking.cc
@@ -161,9 +161,15 @@
   for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
     HInstruction* user = use.GetUser();
     if (!(filter && ShouldFilterUse(instruction, user, post_dominated))) {
-      finder.Update(user->IsPhi()
-          ? user->GetBlock()->GetPredecessors()[use.GetIndex()]
-          : user->GetBlock());
+      HBasicBlock* block = user->GetBlock();
+      if (user->IsPhi()) {
+        // Special case phis by taking the incoming block for regular ones,
+        // or the dominator for catch phis.
+        block = user->AsPhi()->IsCatchPhi()
+            ? block->GetDominator()
+            : block->GetPredecessors()[use.GetIndex()];
+      }
+      finder.Update(block);
     }
   }
   for (const HUseListNode<HEnvironment*>& use : instruction->GetEnvUses()) {
diff --git a/test/647-sinking-catch/expected.txt b/test/647-sinking-catch/expected.txt
new file mode 100644
index 0000000..b2cde18
--- /dev/null
+++ b/test/647-sinking-catch/expected.txt
@@ -0,0 +1 @@
+Three
diff --git a/test/647-sinking-catch/info.txt b/test/647-sinking-catch/info.txt
new file mode 100644
index 0000000..7a8c6a9
--- /dev/null
+++ b/test/647-sinking-catch/info.txt
@@ -0,0 +1,2 @@
+Regression test for the code sinking optimization, which used
+to incorrectly use catch phis.
diff --git a/test/647-sinking-catch/smali/TestCase.smali b/test/647-sinking-catch/smali/TestCase.smali
new file mode 100644
index 0000000..49a3060
--- /dev/null
+++ b/test/647-sinking-catch/smali/TestCase.smali
@@ -0,0 +1,35 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LTestCase;
+.super Ljava/lang/Object;
+
+.method public static foo()V
+  .registers 6
+  new-instance v0, Ljava/lang/Exception;
+  invoke-direct {v0}, Ljava/lang/Exception;-><init>()V
+  const-string v1, "Zero"
+  :try_start
+  const-string v1, "One"
+  const-string v1, "Two"
+  const-string v1, "Three"
+  throw v0
+  :try_end
+  .catchall {:try_start .. :try_end} :catch_all
+
+  :catch_all
+  sget-object v5, Ljava/lang/System;->out:Ljava/io/PrintStream;
+  invoke-virtual {v5, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+  throw v0
+.end method
diff --git a/test/647-sinking-catch/src/Main.java b/test/647-sinking-catch/src/Main.java
new file mode 100644
index 0000000..0e59056
--- /dev/null
+++ b/test/647-sinking-catch/src/Main.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class Main {
+
+  public static void testMethod(String method) throws Exception {
+    Class<?> c = Class.forName("TestCase");
+    Method m = c.getMethod(method);
+
+    Object[] arguments = new Object[] { };
+    try {
+      m.invoke(null, arguments);
+      throw new Error();
+    } catch (InvocationTargetException e) {
+      // expected
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+    testMethod("foo");
+  }
+}