passes: improve degenerate Phi detection

It is possible that a group of mutually dependent Phis have a
constant value, like
	a = phi(null, b)
and in a different block
	b = phi(a, null)

The simple algorithm could not detect it.

The codegen preparation pass may reduce this to the constant,
causing problem if it is recorded in the stack map.

Improve degenerate Phi detection to handle this case.

Change-Id: Ie81a78796cfd0f9920411af18758343d886a55e3
Reviewed-on: https://go-review.googlesource.com/c/155497
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/passes/GoStatepoints.cpp b/passes/GoStatepoints.cpp
index ae506d3..041be22 100644
--- a/passes/GoStatepoints.cpp
+++ b/passes/GoStatepoints.cpp
@@ -1670,6 +1670,43 @@
   assert(ToZero.empty());
 }
 
+// Detect degenerate Phi.
+// Try harder to handle mutually dependent case, like
+//   a = phi(null, b)
+// (in a different block)
+//   b = phi(a, null)
+static Value *
+phiHasConstantValue(PHINode *Phi0) {
+  Value *V = Phi0->hasConstantValue();
+  if (V)
+    return V;
+
+  // Visit all the Phi inputs. Discover new Phis on the go, and visit them.
+  // Early exit if we see a non-constant or two different constants.
+  SmallSet<PHINode *, 4> Phis, Visited;
+  Phis.insert(Phi0);
+  while (!Phis.empty()) {
+    PHINode *Phi = *Phis.begin();
+    Visited.insert(Phi);
+    for (Value *Operand : Phi->incoming_values()) {
+      if (PHINode *P = dyn_cast<PHINode>(Operand)) {
+        if (!Visited.count(P))
+          Phis.insert(P);
+        continue;
+      }
+      if (isa<Constant>(Operand)) {
+        if (V && V != Operand)
+          return nullptr; // operands not same
+        V = Operand;
+      } else
+        return nullptr; // has non-constant input
+    }
+    Phis.erase(Phi);
+  }
+
+  return V;
+}
+
 static void
 fixBadPhiOperands(Function &F, SetVector<Value *> &BadLoads,
                   SetVector<Instruction *> &ToDel) {
@@ -1688,8 +1725,8 @@
     Changed = false;
     for (Instruction &I : instructions(F))
       if (auto *Phi = dyn_cast<PHINode>(&I))
-        if (Value *V = Phi->hasConstantValue())
-          if (!ToDel.count(Phi)) {
+        if (!ToDel.count(Phi))
+          if (Value *V = phiHasConstantValue(Phi)) {
             Phi->replaceAllUsesWith(V);
             ToDel.insert(Phi);
             Changed = true;