Stop using LIST nodes for PHI operand lists (dotnet/coreclr#20266)
authormikedn <onemihaid@hotmail.com>
Tue, 27 Aug 2019 21:28:43 +0000 (00:28 +0300)
committerCarol Eidt <carol.eidt@microsoft.com>
Tue, 27 Aug 2019 21:28:43 +0000 (14:28 -0700)
* Stop using LIST nodes for PHI operand lists

* Speed up PHI creation

Calling gtSetStmtInfo/fgSetStmtSeq every time an arg is added is rather expensive and not really needed. Costs are always 0 and the shape of the PHI tree is always the same.

Removal of GT_LIST nodes makes it easier to update the linear order incrementally when a GT_PHI_ARG node is added.

* Cleanup PHI value numbering

This replaces the manual traversal of PHI's list of uses with a range-based for loop and cleans up a bunch of convoluted code.

* Remove redundant AsPhi

* Remove list pointer union

* Update PHI equality check

* Add GenTreePhi comment

* Rename op to node

Commit migrated from https://github.com/dotnet/coreclr/commit/501c1e3629bbe89cbcec2ee8057ec111a88bdbc8

16 files changed:
src/coreclr/src/jit/assertionprop.cpp
src/coreclr/src/jit/compiler.cpp
src/coreclr/src/jit/compiler.h
src/coreclr/src/jit/compiler.hpp
src/coreclr/src/jit/flowgraph.cpp
src/coreclr/src/jit/gentree.cpp
src/coreclr/src/jit/gentree.h
src/coreclr/src/jit/gtlist.h
src/coreclr/src/jit/gtstructs.h
src/coreclr/src/jit/morph.cpp
src/coreclr/src/jit/optimizer.cpp
src/coreclr/src/jit/rangecheck.cpp
src/coreclr/src/jit/rationalize.cpp
src/coreclr/src/jit/ssabuilder.cpp
src/coreclr/src/jit/ssabuilder.h
src/coreclr/src/jit/valuenum.cpp

index 240499500c480a540587477bd33f081f61655ea3..e354248c5e19e51223ed406296211823ab011e87 100644 (file)
@@ -2021,13 +2021,11 @@ AssertionIndex Compiler::optAssertionGenPhiDefn(GenTree* tree)
         return NO_ASSERTION_INDEX;
     }
 
-    GenTree* phi = tree->gtOp.gtOp2;
-
     // Try to find if all phi arguments are known to be non-null.
     bool isNonNull = true;
-    for (GenTreeArgList* args = phi->gtOp.gtOp1->AsArgList(); args != nullptr; args = args->Rest())
+    for (GenTreePhi::Use& use : tree->AsOp()->gtGetOp2()->AsPhi()->Uses())
     {
-        if (!vnStore->IsKnownNonNull(args->Current()->gtVNPair.GetConservative()))
+        if (!vnStore->IsKnownNonNull(use.GetNode()->gtVNPair.GetConservative()))
         {
             isNonNull = false;
             break;
index caabcad8959f607f0695e13e08ba680f3a9837c7..f21249d7d01ebfed8f72dc8f4151a16d4dda0da5 100644 (file)
@@ -10590,20 +10590,17 @@ void cNodeIR(Compiler* comp, GenTree* tree)
     }
     else if (op == GT_PHI)
     {
-        if (tree->gtOp.gtOp1 != nullptr)
+        bool first = true;
+        for (GenTreePhi::Use& use : tree->AsPhi()->Uses())
         {
-            bool first = true;
-            for (GenTreeArgList* args = tree->gtOp.gtOp1->AsArgList(); args != nullptr; args = args->Rest())
+            child = use.GetNode();
+            if (!first)
             {
-                child = args->Current();
-                if (!first)
-                {
-                    chars += printf(",");
-                }
-                first = false;
-                chars += printf(" ");
-                chars += cOperandIR(comp, child);
+                chars += printf(",");
             }
+            first = false;
+            chars += printf(" ");
+            chars += cOperandIR(comp, child);
         }
     }
     else
index 3687c89fd9492579985fc8127cc107e29b568316..2cfa506272da23a9ccfd6adfe31251c0ac0d8b35 100644 (file)
@@ -9901,7 +9901,6 @@ public:
             case GT_NOP:
             case GT_RETURN:
             case GT_RETFILT:
-            case GT_PHI:
             case GT_RUNTIMELOOKUP:
             {
                 GenTreeUnOp* const unOp = node->AsUnOp();
@@ -9917,6 +9916,17 @@ public:
             }
 
             // Special nodes
+            case GT_PHI:
+                for (GenTreePhi::Use& use : node->AsPhi()->Uses())
+                {
+                    result = WalkTree(&use.NodeRef(), node);
+                    if (result == fgWalkResult::WALK_ABORT)
+                    {
+                        return result;
+                    }
+                }
+                break;
+
             case GT_CMPXCHG:
             {
                 GenTreeCmpXchg* const cmpXchg = node->AsCmpXchg();
index e3cdae434481ef2b6a4c7b3794c7f4ba5749cd8f..27e73821a198a34578a68e2bc12a1f3676abe851 100644 (file)
@@ -955,8 +955,7 @@ inline GenTree* Compiler::gtNewOperNode(genTreeOps oper, var_types type, GenTree
     assert((GenTree::OperKind(oper) & (GTK_UNOP | GTK_BINOP)) != 0);
     assert((GenTree::OperKind(oper) & GTK_EXOP) ==
            0); // Can't use this to construct any types that extend unary/binary operator.
-    assert(op1 != nullptr || oper == GT_PHI || oper == GT_RETFILT || oper == GT_NOP ||
-           (oper == GT_RETURN && type == TYP_VOID));
+    assert(op1 != nullptr || oper == GT_RETFILT || oper == GT_NOP || (oper == GT_RETURN && type == TYP_VOID));
 
     if (doSimplifications)
     {
@@ -4276,11 +4275,6 @@ void GenTree::VisitOperands(TVisitor visitor)
             return;
 
         // Variadic nodes
-        case GT_PHI:
-            assert(this->AsUnOp()->gtOp1 != nullptr);
-            this->AsUnOp()->gtOp1->VisitListOperands(visitor);
-            return;
-
         case GT_FIELD_LIST:
             VisitListOperands(visitor);
             return;
@@ -4313,6 +4307,16 @@ void GenTree::VisitOperands(TVisitor visitor)
 #endif // FEATURE_HW_INTRINSICS
 
         // Special nodes
+        case GT_PHI:
+            for (GenTreePhi::Use& use : AsPhi()->Uses())
+            {
+                if (visitor(use.GetNode()) == VisitResult::Abort)
+                {
+                    break;
+                }
+            }
+            return;
+
         case GT_CMPXCHG:
         {
             GenTreeCmpXchg* const cmpXchg = this->AsCmpXchg();
index 96260ab52661474350a6f7faf7126689575894ff..84d4373a6a2d9fe627923d5d329f0d2aa50b8846 100644 (file)
@@ -18799,6 +18799,13 @@ void Compiler::fgSetTreeSeqHelper(GenTree* tree, bool isLIR)
             fgSetTreeSeqHelper(tree->gtArrOffs.gtArrObj, isLIR);
             break;
 
+        case GT_PHI:
+            for (GenTreePhi::Use& use : tree->AsPhi()->Uses())
+            {
+                fgSetTreeSeqHelper(use.GetNode(), isLIR);
+            }
+            break;
+
         case GT_CMPXCHG:
             // Evaluate the trees left to right
             fgSetTreeSeqHelper(tree->gtCmpXchg.gtOpLocation, isLIR);
@@ -21403,6 +21410,14 @@ void Compiler::fgDebugCheckFlags(GenTree* tree)
                 chkFlags |= (bndsChk->gtArrLen->gtFlags & GTF_ALL_EFFECT);
                 break;
 
+            case GT_PHI:
+                for (GenTreePhi::Use& use : tree->AsPhi()->Uses())
+                {
+                    fgDebugCheckFlags(use.GetNode());
+                    chkFlags |= (use.GetNode()->gtFlags & GTF_ALL_EFFECT);
+                }
+                break;
+
             case GT_CMPXCHG:
 
                 chkFlags |= (GTF_GLOB_REF | GTF_ASG);
index d35debf9735e97188c6341067c226ed6e0b42913..c05de1062f197335e9cc913328284ac2376a036d 100644 (file)
@@ -1483,6 +1483,9 @@ AGAIN:
                     Compare(op1->gtArrOffs.gtIndex, op2->gtArrOffs.gtIndex) &&
                     Compare(op1->gtArrOffs.gtArrObj, op2->gtArrOffs.gtArrObj));
 
+        case GT_PHI:
+            return GenTreePhi::Equals(op1->AsPhi(), op2->AsPhi());
+
         case GT_CMPXCHG:
             return Compare(op1->gtCmpXchg.gtOpLocation, op2->gtCmpXchg.gtOpLocation) &&
                    Compare(op1->gtCmpXchg.gtOpValue, op2->gtCmpXchg.gtOpValue) &&
@@ -1700,6 +1703,16 @@ AGAIN:
             }
             break;
 
+        case GT_PHI:
+            for (GenTreePhi::Use& use : tree->AsPhi()->Uses())
+            {
+                if (gtHasRef(use.GetNode(), lclNum, defOnly))
+                {
+                    return true;
+                }
+            }
+            break;
+
         case GT_CMPXCHG:
             if (gtHasRef(tree->gtCmpXchg.gtOpLocation, lclNum, defOnly))
             {
@@ -2126,6 +2139,13 @@ AGAIN:
             }
             break;
 
+        case GT_PHI:
+            for (GenTreePhi::Use& use : tree->AsPhi()->Uses())
+            {
+                hash = genTreeHashAdd(hash, gtHashValue(use.GetNode()));
+            }
+            break;
+
         case GT_CMPXCHG:
             hash = genTreeHashAdd(hash, gtHashValue(tree->gtCmpXchg.gtOpLocation));
             hash = genTreeHashAdd(hash, gtHashValue(tree->gtCmpXchg.gtOpValue));
@@ -4251,6 +4271,23 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
             costSz += tree->gtArrOffs.gtArrObj->gtCostSz;
             break;
 
+        case GT_PHI:
+            for (GenTreePhi::Use& use : tree->AsPhi()->Uses())
+            {
+                lvl2 = gtSetEvalOrder(use.GetNode());
+                // PHI args should always have cost 0 and level 1
+                assert(lvl2 == 1);
+                assert(use.GetNode()->gtCostEx == 0);
+                assert(use.GetNode()->gtCostSz == 0);
+            }
+            // Give it a level of 2, just to be sure that it's greater than the LHS of
+            // the parent assignment and the PHI gets evaluated first in linear order.
+            // See also SsaBuilder::InsertPhi and SsaBuilder::AddPhiArg.
+            level  = 2;
+            costEx = 0;
+            costSz = 0;
+            break;
+
         case GT_CMPXCHG:
 
             level  = gtSetEvalOrder(tree->gtCmpXchg.gtOpLocation);
@@ -4547,6 +4584,16 @@ GenTree** GenTree::gtGetChildPointer(GenTree* parent) const
             }
             break;
 
+        case GT_PHI:
+            for (GenTreePhi::Use& use : parent->AsPhi()->Uses())
+            {
+                if (use.GetNode() == this)
+                {
+                    return &use.NodeRef();
+                }
+            }
+            break;
+
         case GT_CMPXCHG:
             if (this == parent->gtCmpXchg.gtOpLocation)
             {
@@ -4758,10 +4805,6 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
             return false;
 
         // Variadic nodes
-        case GT_PHI:
-            assert(this->AsUnOp()->gtOp1 != nullptr);
-            return this->AsUnOp()->gtOp1->TryGetUseList(def, use);
-
         case GT_FIELD_LIST:
             return TryGetUseList(def, use);
 
@@ -4801,6 +4844,17 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
 #endif // FEATURE_HW_INTRINSICS
 
         // Special nodes
+        case GT_PHI:
+            for (GenTreePhi::Use& phiUse : AsPhi()->Uses())
+            {
+                if (phiUse.GetNode() == def)
+                {
+                    *use = &phiUse.NodeRef();
+                    return true;
+                }
+            }
+            return false;
+
         case GT_CMPXCHG:
         {
             GenTreeCmpXchg* const cmpXchg = this->AsCmpXchg();
@@ -7340,6 +7394,19 @@ GenTree* Compiler::gtCloneExpr(
         }
         break;
 
+        case GT_PHI:
+        {
+            copy                      = new (this, GT_PHI) GenTreePhi(tree->TypeGet());
+            GenTreePhi::Use** prevUse = &copy->AsPhi()->gtUses;
+            for (GenTreePhi::Use& use : tree->AsPhi()->Uses())
+            {
+                *prevUse = new (this, CMK_ASTNode)
+                    GenTreePhi::Use(gtCloneExpr(use.GetNode(), addFlags, deepVarNum, deepVarVal), *prevUse);
+                prevUse = &((*prevUse)->NextRef());
+            }
+        }
+        break;
+
         case GT_CMPXCHG:
             copy = new (this, GT_CMPXCHG)
                 GenTreeCmpXchg(tree->TypeGet(),
@@ -8094,6 +8161,16 @@ unsigned GenTree::NumChildren()
         // Special
         switch (OperGet())
         {
+            case GT_PHI:
+            {
+                unsigned count = 0;
+                for (GenTreePhi::Use& use : AsPhi()->Uses())
+                {
+                    count++;
+                }
+                return count;
+            }
+
             case GT_CMPXCHG:
                 return 3;
 
@@ -8207,6 +8284,17 @@ GenTree* GenTree::GetChild(unsigned childNum)
         // Special
         switch (OperGet())
         {
+            case GT_PHI:
+                for (GenTreePhi::Use& use : AsPhi()->Uses())
+                {
+                    if (childNum == 0)
+                    {
+                        return use.GetNode();
+                    }
+                    childNum--;
+                }
+                unreached();
+
             case GT_CMPXCHG:
                 switch (childNum)
                 {
@@ -8355,12 +8443,12 @@ GenTree* GenTree::GetChild(unsigned childNum)
 }
 
 GenTreeUseEdgeIterator::GenTreeUseEdgeIterator()
-    : m_advance(nullptr), m_node(nullptr), m_edge(nullptr), m_argList(nullptr), m_state(-1)
+    : m_advance(nullptr), m_node(nullptr), m_edge(nullptr), m_statePtr(nullptr), m_state(-1)
 {
 }
 
 GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node)
-    : m_advance(nullptr), m_node(node), m_edge(nullptr), m_argList(nullptr), m_state(0)
+    : m_advance(nullptr), m_node(node), m_edge(nullptr), m_statePtr(nullptr), m_state(0)
 {
     assert(m_node != nullptr);
 
@@ -8458,19 +8546,15 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node)
             return;
 
         // Variadic nodes
-        case GT_PHI:
-            SetEntryStateForList(m_node->AsUnOp()->gtOp1);
-            return;
-
         case GT_FIELD_LIST:
-            SetEntryStateForList(m_node);
+            SetEntryStateForList(m_node->AsArgList());
             return;
 
 #ifdef FEATURE_SIMD
         case GT_SIMD:
             if (m_node->AsSIMD()->gtSIMDIntrinsicID == SIMDIntrinsicInitN)
             {
-                SetEntryStateForList(m_node->AsSIMD()->gtOp1);
+                SetEntryStateForList(m_node->AsSIMD()->gtOp1->AsArgList());
             }
             else
             {
@@ -8488,7 +8572,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node)
             }
             else if (m_node->AsHWIntrinsic()->gtOp1->OperIsList())
             {
-                SetEntryStateForList(m_node->AsHWIntrinsic()->gtOp1);
+                SetEntryStateForList(m_node->AsHWIntrinsic()->gtOp1->AsArgList());
             }
             else
             {
@@ -8511,6 +8595,12 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node)
             return;
 
         // Special nodes
+        case GT_PHI:
+            m_statePtr = m_node->AsPhi()->gtUses;
+            m_advance  = &GenTreeUseEdgeIterator::AdvancePhi;
+            AdvancePhi();
+            return;
+
         case GT_CMPXCHG:
             m_edge = &m_node->AsCmpXchg()->gtOpLocation;
             assert(*m_edge != nullptr);
@@ -8721,6 +8811,25 @@ void GenTreeUseEdgeIterator::AdvanceStoreDynBlk()
     assert(*m_edge != nullptr);
 }
 
+//------------------------------------------------------------------------
+// GenTreeUseEdgeIterator::AdvancePhi: produces the next operand of a Phi node and advances the state.
+//
+void GenTreeUseEdgeIterator::AdvancePhi()
+{
+    assert(m_state == 0);
+
+    if (m_statePtr == nullptr)
+    {
+        m_state = -1;
+    }
+    else
+    {
+        GenTreePhi::Use* currentUse = static_cast<GenTreePhi::Use*>(m_statePtr);
+        m_edge                      = &currentUse->NodeRef();
+        m_statePtr                  = currentUse->GetNext();
+    }
+}
+
 //------------------------------------------------------------------------
 // GenTreeUseEdgeIterator::AdvanceBinOp: produces the next operand of a binary node and advances the state.
 //
@@ -8777,25 +8886,25 @@ void GenTreeUseEdgeIterator::AdvanceList()
 {
     assert(m_state == 0);
 
-    if (m_argList == nullptr)
+    if (m_statePtr == nullptr)
     {
         m_state = -1;
     }
     else
     {
-        GenTreeArgList* listNode = m_argList->AsArgList();
+        GenTreeArgList* listNode = static_cast<GenTreeArgList*>(m_statePtr);
         m_edge                   = &listNode->gtOp1;
-        m_argList                = listNode->Rest();
+        m_statePtr               = listNode->Rest();
     }
 }
 
 //------------------------------------------------------------------------
 // GenTreeUseEdgeIterator::SetEntryStateForList: produces the first operand of a list node.
 //
-void GenTreeUseEdgeIterator::SetEntryStateForList(GenTree* list)
+void GenTreeUseEdgeIterator::SetEntryStateForList(GenTreeArgList* list)
 {
-    m_argList = list;
-    m_advance = &GenTreeUseEdgeIterator::AdvanceList;
+    m_statePtr = list;
+    m_advance  = &GenTreeUseEdgeIterator::AdvanceList;
     AdvanceList();
 }
 
@@ -8819,8 +8928,8 @@ void          GenTreeUseEdgeIterator::AdvanceCall()
     switch (state)
     {
         case CALL_INSTANCE:
-            m_argList = call->gtCallArgs;
-            m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_ARGS>;
+            m_statePtr = call->gtCallArgs;
+            m_advance  = &GenTreeUseEdgeIterator::AdvanceCall<CALL_ARGS>;
             if (call->gtCallObjp != nullptr)
             {
                 m_edge = &call->gtCallObjp;
@@ -8829,23 +8938,23 @@ void          GenTreeUseEdgeIterator::AdvanceCall()
             __fallthrough;
 
         case CALL_ARGS:
-            if (m_argList != nullptr)
+            if (m_statePtr != nullptr)
             {
-                GenTreeArgList* argNode = m_argList->AsArgList();
+                GenTreeArgList* argNode = static_cast<GenTreeArgList*>(m_statePtr);
                 m_edge                  = &argNode->gtOp1;
-                m_argList               = argNode->Rest();
+                m_statePtr              = argNode->Rest();
                 return;
             }
-            m_argList = call->gtCallLateArgs;
-            m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_LATE_ARGS>;
+            m_statePtr = call->gtCallLateArgs;
+            m_advance  = &GenTreeUseEdgeIterator::AdvanceCall<CALL_LATE_ARGS>;
             __fallthrough;
 
         case CALL_LATE_ARGS:
-            if (m_argList != nullptr)
+            if (m_statePtr != nullptr)
             {
-                GenTreeArgList* argNode = m_argList->AsArgList();
+                GenTreeArgList* argNode = static_cast<GenTreeArgList*>(m_statePtr);
                 m_edge                  = &argNode->gtOp1;
-                m_argList               = argNode->Rest();
+                m_statePtr              = argNode->Rest();
                 return;
             }
             m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_CONTROL_EXPR>;
@@ -10795,6 +10904,20 @@ void Compiler::gtDispTree(GenTree*     tree,
 
     switch (tree->gtOper)
     {
+        case GT_PHI:
+            gtDispCommonEndLine(tree);
+
+            if (!topOnly)
+            {
+                for (GenTreePhi::Use& use : tree->AsPhi()->Uses())
+                {
+                    char block[32];
+                    sprintf_s(block, sizeof(block), "pred " FMT_BB, use.GetNode()->AsPhiArg()->gtPredBB->bbNum);
+                    gtDispChild(use.GetNode(), indentStack, (use.GetNext() == nullptr) ? IIArcBottom : IIArc, block);
+                }
+            }
+            break;
+
         case GT_FIELD:
             if (FieldSeqStore::IsPseudoField(tree->gtField.gtFldHnd))
             {
index 778e9e127d881cee74f77eb7cf2375a038f1c11b..e52d5d5afd19cef75028d5123eb2ba4dbc3c9fe3 100644 (file)
@@ -1574,7 +1574,6 @@ public:
         assert(OperIsSimple(gtOper));
         switch (gtOper)
         {
-            case GT_PHI:
             case GT_LEA:
             case GT_RETFILT:
             case GT_NOP:
@@ -2174,6 +2173,177 @@ public:
     inline GenTree(genTreeOps oper, var_types type DEBUGARG(bool largeNode = false));
 };
 
+// Represents a GT_PHI node - a variable sized list of GT_PHI_ARG nodes.
+// All PHI_ARG nodes must represent uses of the same local variable and
+// the PHI node's type must be the same as the local variable's type.
+//
+// The PHI node does not represent a definition by itself, it is always
+// the RHS of a GT_ASG node. The LHS of the ASG node is always a GT_LCL_VAR
+// node, that is a definition for the same local variable referenced by
+// all the used PHI_ARG nodes:
+//
+//   ASG(LCL_VAR(lcl7), PHI(PHI_ARG(lcl7), PHI_ARG(lcl7), PHI_ARG(lcl7)))
+//
+// PHI nodes are also present in LIR, where GT_STORE_LCL_VAR replaces the
+// ASG node.
+//
+// The order of the PHI_ARG uses is not currently relevant and it may be
+// the same or not as the order of the predecessor blocks.
+//
+struct GenTreePhi final : public GenTree
+{
+    class Use
+    {
+        GenTree* m_node;
+        Use*     m_next;
+
+    public:
+        Use(GenTree* node, Use* next = nullptr) : m_node(node), m_next(next)
+        {
+            assert(node->OperIs(GT_PHI_ARG));
+        }
+
+        GenTree*& NodeRef()
+        {
+            return m_node;
+        }
+
+        GenTree* GetNode() const
+        {
+            assert(m_node->OperIs(GT_PHI_ARG));
+            return m_node;
+        }
+
+        void SetNode(GenTree* node)
+        {
+            assert(node->OperIs(GT_PHI_ARG));
+            m_node = node;
+        }
+
+        Use*& NextRef()
+        {
+            return m_next;
+        }
+
+        Use* GetNext() const
+        {
+            return m_next;
+        }
+    };
+
+    class UseIterator
+    {
+        Use* m_use;
+
+    public:
+        UseIterator(Use* use) : m_use(use)
+        {
+        }
+
+        Use& operator*() const
+        {
+            return *m_use;
+        }
+
+        Use* operator->() const
+        {
+            return m_use;
+        }
+
+        UseIterator& operator++()
+        {
+            m_use = m_use->GetNext();
+            return *this;
+        }
+
+        bool operator==(const UseIterator& i) const
+        {
+            return m_use == i.m_use;
+        }
+
+        bool operator!=(const UseIterator& i) const
+        {
+            return m_use != i.m_use;
+        }
+    };
+
+    class UseList
+    {
+        Use* m_uses;
+
+    public:
+        UseList(Use* uses) : m_uses(uses)
+        {
+        }
+
+        UseIterator begin() const
+        {
+            return UseIterator(m_uses);
+        }
+
+        UseIterator end() const
+        {
+            return UseIterator(nullptr);
+        }
+    };
+
+    Use* gtUses;
+
+    GenTreePhi(var_types type) : GenTree(GT_PHI, type), gtUses(nullptr)
+    {
+    }
+
+    UseList Uses()
+    {
+        return UseList(gtUses);
+    }
+
+    //--------------------------------------------------------------------------
+    // Equals: Checks if 2 PHI nodes are equal.
+    //
+    // Arguments:
+    //    phi1 - The first PHI node
+    //    phi2 - The second PHI node
+    //
+    // Return Value:
+    //    true if the 2 PHI nodes have the same type, number of uses, and the
+    //    uses are equal.
+    //
+    // Notes:
+    //    The order of uses must be the same for equality, even if the
+    //    order is not usually relevant and is not guaranteed to reflect
+    //    a particular order of the predecessor blocks.
+    //
+    static bool Equals(GenTreePhi* phi1, GenTreePhi* phi2)
+    {
+        if (phi1->TypeGet() != phi2->TypeGet())
+        {
+            return false;
+        }
+
+        GenTreePhi::UseIterator i1   = phi1->Uses().begin();
+        GenTreePhi::UseIterator end1 = phi1->Uses().end();
+        GenTreePhi::UseIterator i2   = phi2->Uses().begin();
+        GenTreePhi::UseIterator end2 = phi2->Uses().end();
+
+        for (; (i1 != end1) && (i2 != end2); ++i1, ++i2)
+        {
+            if (!Compare(i1->GetNode(), i2->GetNode()))
+            {
+                return false;
+            }
+        }
+
+        return (i1 == end1) && (i2 == end2);
+    }
+
+#if DEBUGGABLE_GENTREE
+    GenTreePhi() : GenTree()
+    {
+    }
+#endif
+};
+
 //------------------------------------------------------------------------
 // GenTreeUseEdgeIterator: an iterator that will produce each use edge of a GenTree node in the order in which
 //                         they are used.
@@ -2214,8 +2384,10 @@ class GenTreeUseEdgeIterator final
     AdvanceFn m_advance;
     GenTree*  m_node;
     GenTree** m_edge;
-    GenTree*  m_argList;
-    int       m_state;
+    // Pointer sized state storage, GenTreeArgList* or GenTreePhi::Use* currently.
+    void* m_statePtr;
+    // Integer sized state storage, usually the operand index for non-list based nodes.
+    int m_state;
 
     GenTreeUseEdgeIterator(GenTree* node);
 
@@ -2226,6 +2398,7 @@ class GenTreeUseEdgeIterator final
     void AdvanceArrOffset();
     void AdvanceDynBlk();
     void AdvanceStoreDynBlk();
+    void AdvancePhi();
 
     template <bool ReverseOperands>
     void           AdvanceBinOp();
@@ -2233,7 +2406,7 @@ class GenTreeUseEdgeIterator final
 
     // An advance function for list-like nodes (Phi, SIMDIntrinsicInitN, FieldList)
     void AdvanceList();
-    void SetEntryStateForList(GenTree* list);
+    void SetEntryStateForList(GenTreeArgList* list);
 
     // The advance function for call nodes
     template <int state>
@@ -2263,7 +2436,7 @@ public:
             return m_state == other.m_state;
         }
 
-        return (m_node == other.m_node) && (m_edge == other.m_edge) && (m_argList == other.m_argList) &&
+        return (m_node == other.m_node) && (m_edge == other.m_edge) && (m_statePtr == other.m_statePtr) &&
                (m_state == other.m_state);
     }
 
index b451885f9868882c99c8d10560a89732b42b2b47..b7d5646c76e6b2f4ed11bc2a070388364febb901 100644 (file)
@@ -266,7 +266,7 @@ GTNODE(END_LFIN         , GenTreeVal         ,0,(GTK_LEAF|GTK_NOVALUE))   // end
 //  Nodes used for optimizations.
 //-----------------------------------------------------------------------------
 
-GTNODE(PHI              , GenTreeOp          ,0,GTK_UNOP)               // phi node for ssa.
+GTNODE(PHI              , GenTreePhi         ,0,GTK_SPECIAL)              // phi node for ssa.
 GTNODE(PHI_ARG          , GenTreePhiArg      ,0,(GTK_LEAF|GTK_LOCAL))     // phi(phiarg, phiarg, phiarg)
 
 //-----------------------------------------------------------------------------
index d574b8f5ff87ee833567778b28cc0921626d7f24..e8b1bde77aa9afe6a0e2ed13d7697e3d8d137758 100644 (file)
@@ -100,6 +100,7 @@ GTSTRUCT_2(Obj         , GT_OBJ, GT_STORE_OBJ)
 GTSTRUCT_2(DynBlk      , GT_DYN_BLK, GT_STORE_DYN_BLK)
 GTSTRUCT_1(Qmark       , GT_QMARK)
 GTSTRUCT_1(PhiArg      , GT_PHI_ARG)
+GTSTRUCT_1(Phi         , GT_PHI)
 GTSTRUCT_1(StoreInd    , GT_STOREIND)
 GTSTRUCT_N(Indir       , GT_STOREIND, GT_IND, GT_NULLCHECK, GT_BLK, GT_STORE_BLK, GT_OBJ, GT_STORE_OBJ, GT_DYN_BLK, GT_STORE_DYN_BLK)
 #if FEATURE_ARG_SPLIT
index 8ff4553df56cf07184c03a24e65a4a0d3a8c6409..fbf6d74cd18d5fca39b50c8382a541469ac7b6af 100644 (file)
@@ -14935,6 +14935,15 @@ GenTree* Compiler::fgMorphTree(GenTree* tree, MorphAddrContext* mac)
             }
             break;
 
+        case GT_PHI:
+            tree->gtFlags &= ~GTF_ALL_EFFECT;
+            for (GenTreePhi::Use& use : tree->AsPhi()->Uses())
+            {
+                use.SetNode(fgMorphTree(use.GetNode()));
+                tree->gtFlags |= use.GetNode()->gtFlags & GTF_ALL_EFFECT;
+            }
+            break;
+
         case GT_CMPXCHG:
             tree->gtCmpXchg.gtOpLocation  = fgMorphTree(tree->gtCmpXchg.gtOpLocation);
             tree->gtCmpXchg.gtOpValue     = fgMorphTree(tree->gtCmpXchg.gtOpValue);
index 14fcdbde26d4496843118a8c267999c923237a0a..7c41a9e38b1bb9580cea9aed104032ee40b8ef1f 100644 (file)
@@ -7394,15 +7394,13 @@ void Compiler::fgCreateLoopPreHeader(unsigned lnum)
         {
             break;
         }
-        GenTreeArgList* args = op2->gtGetOp1()->AsArgList();
-        while (args != nullptr)
+        for (GenTreePhi::Use& use : op2->AsPhi()->Uses())
         {
-            GenTreePhiArg* phiArg = args->Current()->AsPhiArg();
+            GenTreePhiArg* phiArg = use.GetNode()->AsPhiArg();
             if (phiArg->gtPredBB == head)
             {
                 phiArg->gtPredBB = preHead;
             }
-            args = args->Rest();
         }
     }
 
index 6f0d46043e2da9281c9f18257780b43ee7ea6161..389273e765dc00450eeac0c1941d7ad54ff4e695 100644 (file)
@@ -405,14 +405,14 @@ bool RangeCheck::IsMonotonicallyIncreasing(GenTree* expr, bool rejectNegativeCon
     }
     else if (expr->OperGet() == GT_PHI)
     {
-        for (GenTreeArgList* args = expr->gtOp.gtOp1->AsArgList(); args != nullptr; args = args->Rest())
+        for (GenTreePhi::Use& use : expr->AsPhi()->Uses())
         {
             // If the arg is already in the path, skip.
-            if (m_pSearchPath->Lookup(args->Current()))
+            if (m_pSearchPath->Lookup(use.GetNode()))
             {
                 continue;
             }
-            if (!IsMonotonicallyIncreasing(args->Current(), rejectNegativeConst))
+            if (!IsMonotonicallyIncreasing(use.GetNode(), rejectNegativeConst))
             {
                 JITDUMP("Phi argument not monotonic\n");
                 return false;
@@ -986,9 +986,9 @@ bool RangeCheck::DoesVarDefOverflow(GenTreeLclVarCommon* lcl)
 
 bool RangeCheck::DoesPhiOverflow(BasicBlock* block, GenTree* expr)
 {
-    for (GenTreeArgList* args = expr->gtOp.gtOp1->AsArgList(); args != nullptr; args = args->Rest())
+    for (GenTreePhi::Use& use : expr->AsPhi()->Uses())
     {
-        GenTree* arg = args->Current();
+        GenTree* arg = use.GetNode();
         if (m_pSearchPath->Lookup(arg))
         {
             continue;
@@ -1123,21 +1123,21 @@ Range RangeCheck::ComputeRange(BasicBlock* block, GenTree* expr, bool monotonic
     // If phi, then compute the range for arguments, calling the result "dependent" when looping begins.
     else if (expr->OperGet() == GT_PHI)
     {
-        for (GenTreeArgList* args = expr->gtOp.gtOp1->AsArgList(); args != nullptr; args = args->Rest())
+        for (GenTreePhi::Use& use : expr->AsPhi()->Uses())
         {
             Range argRange = Range(Limit(Limit::keUndef));
-            if (m_pSearchPath->Lookup(args->Current()))
+            if (m_pSearchPath->Lookup(use.GetNode()))
             {
-                JITDUMP("PhiArg [%06d] is already being computed\n", Compiler::dspTreeID(args->Current()));
+                JITDUMP("PhiArg [%06d] is already being computed\n", Compiler::dspTreeID(use.GetNode()));
                 argRange = Range(Limit(Limit::keDependent));
             }
             else
             {
-                argRange = GetRange(block, args->Current(), monotonic DEBUGARG(indent + 1));
+                argRange = GetRange(block, use.GetNode(), monotonic DEBUGARG(indent + 1));
             }
             assert(!argRange.LowerLimit().IsUndef());
             assert(!argRange.UpperLimit().IsUndef());
-            MergeAssertion(block, args->Current(), &argRange DEBUGARG(indent + 1));
+            MergeAssertion(block, use.GetNode(), &argRange DEBUGARG(indent + 1));
             JITDUMP("Merging ranges %s %s:", range.ToString(m_pCompiler->getAllocatorDebugOnly()),
                     argRange.ToString(m_pCompiler->getAllocatorDebugOnly()));
             range = RangeOps::Merge(range, argRange, monotonic);
index 555325f95338c4cc9f541da0150bd4034b7829f9..6670f7a8ae7a6ae3ab11f5d2d47218b5eb0ea8b8 100644 (file)
@@ -564,7 +564,7 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge
 
     // First, remove any preceeding list nodes, which are not otherwise visited by the tree walk.
     //
-    // NOTE: GT_FIELD_LIST head nodes, and GT_LIST nodes used by phi nodes will in fact be visited.
+    // NOTE: GT_FIELD_LIST head nodes, and GT_LIST nodes used by GT_HWIntrinsic nodes will in fact be visited.
     for (GenTree* prev = node->gtPrev; prev != nullptr && prev->OperIsAnyList() && !(prev->OperIsFieldListHead());
          prev          = node->gtPrev)
     {
index e130cae177746f84fe6bfaf58e1464feda0a7c02..bcf9b6dd65547cf661a43378766ddbdbf59aab92 100644 (file)
@@ -673,6 +673,99 @@ static GenTree* GetPhiNode(BasicBlock* block, unsigned lclNum)
     return nullptr;
 }
 
+//------------------------------------------------------------------------
+// InsertPhi: Insert a new GT_PHI statement.
+//
+// Arguments:
+//    block  - The block where to insert the statement
+//    lclNum - The variable number
+//
+void SsaBuilder::InsertPhi(BasicBlock* block, unsigned lclNum)
+{
+    var_types type = m_pCompiler->lvaGetDesc(lclNum)->TypeGet();
+
+    GenTree* lhs = m_pCompiler->gtNewLclvNode(lclNum, type);
+    // PHIs and all the associated nodes do not generate any code so the costs are always 0
+    lhs->SetCosts(0, 0);
+    GenTree* phi = new (m_pCompiler, GT_PHI) GenTreePhi(type);
+    phi->SetCosts(0, 0);
+    GenTree* asg = m_pCompiler->gtNewAssignNode(lhs, phi);
+    // Evaluate the assignment RHS (the PHI node) first. This way the LHS will end up right
+    // in front of the assignment in the linear order, that ensures that using gtGetParent
+    // on the LHS to find the assignment doesn't have to traverse the PHI and its args.
+    asg->gtFlags |= GTF_REVERSE_OPS;
+    asg->SetCosts(0, 0);
+
+    // Create the statement and chain everything in linear order - PHI, LCL_VAR, ASG
+    GenTreeStmt* stmt = m_pCompiler->gtNewStmt(asg);
+    stmt->gtStmtList  = phi;
+    phi->gtNext       = lhs;
+    lhs->gtPrev       = phi;
+    lhs->gtNext       = asg;
+    asg->gtPrev       = lhs;
+
+#ifdef DEBUG
+    unsigned seqNum = 1;
+    for (GenTree* node = stmt->gtStmtList; node != nullptr; node = node->gtNext)
+    {
+        node->gtSeqNum = seqNum++;
+    }
+#endif // DEBUG
+
+    m_pCompiler->fgInsertStmtAtBeg(block, stmt);
+
+    JITDUMP("Added PHI definition for V%02u at start of " FMT_BB ".\n", lclNum, block->bbNum);
+}
+
+//------------------------------------------------------------------------
+// AddPhiArg: Add a new GT_PHI_ARG node to an existing GT_PHI node.
+//
+// Arguments:
+//    block  - The block that contains the statement
+//    stmt   - The statement that contains the GT_PHI node
+//    lclNum - The variable number
+//    ssaNum - The SSA number
+//    pred   - The predecessor block
+//
+void SsaBuilder::AddPhiArg(
+    BasicBlock* block, GenTreeStmt* stmt, GenTreePhi* phi, unsigned lclNum, unsigned ssaNum, BasicBlock* pred)
+{
+#ifdef DEBUG
+    // Make sure it isn't already present: we should only add each definition once.
+    for (GenTreePhi::Use& use : phi->Uses())
+    {
+        assert(use.GetNode()->AsPhiArg()->GetSsaNum() != ssaNum);
+    }
+#endif // DEBUG
+
+    var_types type = m_pCompiler->lvaGetDesc(lclNum)->TypeGet();
+
+    GenTree* phiArg = new (m_pCompiler, GT_PHI_ARG) GenTreePhiArg(type, lclNum, ssaNum, pred);
+    // Costs are not relevant for PHI args.
+    phiArg->SetCosts(0, 0);
+    // The argument order doesn't matter so just insert at the front of the list because
+    // it's easier. It's also easier to insert in linear order since the first argument
+    // will be first in linear order as well.
+    phi->gtUses = new (m_pCompiler, CMK_ASTNode) GenTreePhi::Use(phiArg, phi->gtUses);
+
+    GenTree* head = stmt->gtStmtList;
+    assert(head->OperIs(GT_PHI, GT_PHI_ARG));
+    stmt->gtStmtList = phiArg;
+    phiArg->gtNext   = head;
+    head->gtPrev     = phiArg;
+
+#ifdef DEBUG
+    unsigned seqNum = 1;
+    for (GenTree* node = stmt->gtStmtList; node != nullptr; node = node->gtNext)
+    {
+        node->gtSeqNum = seqNum++;
+    }
+#endif // DEBUG
+
+    DBG_SSA_JITDUMP("Added PHI arg u:%d for V%02u from " FMT_BB " in " FMT_BB ".\n", ssaNum, lclNum, pred->bbNum,
+                    block->bbNum);
+}
+
 /**
  * Inserts phi functions at DF(b) for variables v that are live after the phi
  * insertion point i.e., v in live-in(b).
@@ -741,24 +834,7 @@ void SsaBuilder::InsertPhiFunctions(BasicBlock** postOrder, int count)
                 {
                     // We have a variable i that is defined in block j and live at l, and l belongs to dom frontier of
                     // j. So insert a phi node at l.
-                    JITDUMP("Inserting phi definition for V%02u at start of " FMT_BB ".\n", lclNum,
-                            bbInDomFront->bbNum);
-
-                    GenTree* phiLhs = m_pCompiler->gtNewLclvNode(lclNum, m_pCompiler->lvaTable[lclNum].TypeGet());
-
-                    // Create 'phiRhs' as a GT_PHI node for 'lclNum', it will eventually hold a GT_LIST of GT_PHI_ARG
-                    // nodes. However we have to construct this list so for now the gtOp1 of 'phiRhs' is a nullptr.
-                    // It will get replaced with a GT_LIST of GT_PHI_ARG nodes in
-                    // SsaBuilder::AssignPhiNodeRhsVariables() and in SsaBuilder::AddDefToHandlerPhis()
-
-                    GenTree* phiRhs =
-                        m_pCompiler->gtNewOperNode(GT_PHI, m_pCompiler->lvaTable[lclNum].TypeGet(), nullptr);
-
-                    GenTree* phiAsg = m_pCompiler->gtNewAssignNode(phiLhs, phiRhs);
-
-                    GenTreeStmt* stmt = m_pCompiler->fgNewStmtAtBeg(bbInDomFront, phiAsg);
-                    m_pCompiler->gtSetStmtInfo(stmt);
-                    m_pCompiler->fgSetStmtSeq(stmt);
+                    InsertPhi(bbInDomFront, lclNum);
                 }
             }
         }
@@ -1006,32 +1082,10 @@ void SsaBuilder::AddDefToHandlerPhis(BasicBlock* block, unsigned lclNum, unsigne
                     if (tree->gtOp.gtOp1->gtLclVar.gtLclNum == lclNum)
                     {
                         // It's the definition for the right local.  Add "ssaNum" to the RHS.
-                        GenTree*        phi  = tree->gtOp.gtOp2;
-                        GenTreeArgList* args = nullptr;
-                        if (phi->gtOp.gtOp1 != nullptr)
-                        {
-                            args = phi->gtOp.gtOp1->AsArgList();
-                        }
-#ifdef DEBUG
-                        // Make sure it isn't already present: we should only add each definition once.
-                        for (GenTreeArgList* curArgs = args; curArgs != nullptr; curArgs = curArgs->Rest())
-                        {
-                            GenTreePhiArg* phiArg = curArgs->Current()->AsPhiArg();
-                            assert(phiArg->gtSsaNum != ssaNum);
-                        }
-#endif
-                        var_types      typ = m_pCompiler->lvaTable[lclNum].TypeGet();
-                        GenTreePhiArg* newPhiArg =
-                            new (m_pCompiler, GT_PHI_ARG) GenTreePhiArg(typ, lclNum, ssaNum, block);
-
-                        phi->gtOp.gtOp1 = new (m_pCompiler, GT_LIST) GenTreeArgList(newPhiArg, args);
-                        m_pCompiler->gtSetStmtInfo(stmt);
-                        m_pCompiler->fgSetStmtSeq(stmt);
+                        AddPhiArg(handler, stmt, tree->gtGetOp2()->AsPhi(), lclNum, ssaNum, block);
 #ifdef DEBUG
                         phiFound = true;
 #endif
-                        DBG_SSA_JITDUMP("   Added phi arg u:%d for V%02u to phi defn in handler block " FMT_BB ".\n",
-                                        ssaNum, lclNum, handler->bbNum);
                         break;
                     }
                 }
@@ -1245,9 +1299,7 @@ void SsaBuilder::AssignPhiNodeRhsVariables(BasicBlock* block, SsaRenameState* pR
             GenTree* tree = stmt->gtStmtExpr;
             assert(tree->IsPhiDefn());
 
-            // Get the phi node from GT_ASG.
-            GenTree* phiNode = tree->gtOp.gtOp2;
-            assert(phiNode->gtOp.gtOp1 == nullptr || phiNode->gtOp.gtOp1->OperGet() == GT_LIST);
+            GenTreePhi* phi = tree->gtGetOp2()->AsPhi();
 
             unsigned lclNum = tree->gtOp.gtOp1->gtLclVar.gtLclNum;
             unsigned ssaNum = pRenameState->Top(lclNum);
@@ -1255,29 +1307,20 @@ void SsaBuilder::AssignPhiNodeRhsVariables(BasicBlock* block, SsaRenameState* pR
             // (Can we assert that its the head of the list?  This should only happen when we add
             // during renaming for a definition that occurs within a try, and then that's the last
             // value of the var within that basic block.)
-            GenTreeArgList* argList = (phiNode->gtOp.gtOp1 == nullptr ? nullptr : phiNode->gtOp.gtOp1->AsArgList());
-            bool            found   = false;
-            while (argList != nullptr)
+
+            bool found = false;
+            for (GenTreePhi::Use& use : phi->Uses())
             {
-                if (argList->Current()->AsLclVarCommon()->GetSsaNum() == ssaNum)
+                if (use.GetNode()->AsPhiArg()->GetSsaNum() == ssaNum)
                 {
                     found = true;
                     break;
                 }
-                argList = argList->Rest();
             }
             if (!found)
             {
-                GenTree* newPhiArg =
-                    new (m_pCompiler, GT_PHI_ARG) GenTreePhiArg(tree->gtOp.gtOp1->TypeGet(), lclNum, ssaNum, block);
-                argList             = (phiNode->gtOp.gtOp1 == nullptr ? nullptr : phiNode->gtOp.gtOp1->AsArgList());
-                phiNode->gtOp.gtOp1 = new (m_pCompiler, GT_LIST) GenTreeArgList(newPhiArg, argList);
-                DBG_SSA_JITDUMP("  Added phi arg u:%d for V%02u from " FMT_BB " in " FMT_BB ".\n", ssaNum, lclNum,
-                                block->bbNum, succ->bbNum);
+                AddPhiArg(succ, stmt, phi, lclNum, ssaNum, block);
             }
-
-            m_pCompiler->gtSetStmtInfo(stmt);
-            m_pCompiler->fgSetStmtSeq(stmt);
         }
 
         // Now handle memory.
@@ -1402,17 +1445,15 @@ void SsaBuilder::AssignPhiNodeRhsVariables(BasicBlock* block, SsaRenameState* pR
                         continue;
                     }
 
-                    GenTree* phiNode = tree->gtOp.gtOp2;
-                    assert(phiNode->gtOp.gtOp1 == nullptr || phiNode->gtOp.gtOp1->OperGet() == GT_LIST);
-                    GenTreeArgList* argList = reinterpret_cast<GenTreeArgList*>(phiNode->gtOp.gtOp1);
+                    GenTreePhi* phi = tree->gtGetOp2()->AsPhi();
 
                     unsigned ssaNum = pRenameState->Top(lclNum);
 
                     // See if this ssaNum is already an arg to the phi.
                     bool alreadyArg = false;
-                    for (GenTreeArgList* curArgs = argList; curArgs != nullptr; curArgs = curArgs->Rest())
+                    for (GenTreePhi::Use& use : phi->Uses())
                     {
-                        if (curArgs->Current()->gtPhiArg.gtSsaNum == ssaNum)
+                        if (use.GetNode()->AsPhiArg()->GetSsaNum() == ssaNum)
                         {
                             alreadyArg = true;
                             break;
@@ -1420,16 +1461,7 @@ void SsaBuilder::AssignPhiNodeRhsVariables(BasicBlock* block, SsaRenameState* pR
                     }
                     if (!alreadyArg)
                     {
-                        // Add the new argument.
-                        GenTree* newPhiArg =
-                            new (m_pCompiler, GT_PHI_ARG) GenTreePhiArg(lclVar->TypeGet(), lclNum, ssaNum, block);
-                        phiNode->gtOp.gtOp1 = new (m_pCompiler, GT_LIST) GenTreeArgList(newPhiArg, argList);
-
-                        DBG_SSA_JITDUMP("  Added phi arg u:%d for V%02u from " FMT_BB " in " FMT_BB ".\n", ssaNum,
-                                        lclNum, block->bbNum, handlerStart->bbNum);
-
-                        m_pCompiler->gtSetStmtInfo(stmt);
-                        m_pCompiler->fgSetStmtSeq(stmt);
+                        AddPhiArg(handlerStart, stmt, phi, lclNum, ssaNum, block);
                     }
                 }
 
index 86c1d1254357058838c3a68080bf153ee05f18d3..8ec8162db49dd26fc8ee7c0ecf14b1a1d0923b32 100644 (file)
@@ -94,6 +94,13 @@ private:
     // Compute the iterated dominance frontier for the specified block.
     void ComputeIteratedDominanceFrontier(BasicBlock* b, const BlkToBlkVectorMap* mapDF, BlkVector* bIDF);
 
+    // Insert a new GT_PHI statement.
+    void InsertPhi(BasicBlock* block, unsigned lclNum);
+
+    // Add a new GT_PHI_ARG node to an existing GT_PHI node
+    void AddPhiArg(
+        BasicBlock* block, GenTreeStmt* stmt, GenTreePhi* phi, unsigned lclNum, unsigned ssaNum, BasicBlock* pred);
+
     // Requires "postOrder" to hold the blocks of the flowgraph in topologically sorted order. Requires
     // count to be the valid entries in the "postOrder" array. Inserts GT_PHI nodes at the beginning
     // of basic blocks that require them like so:
index 8e7f00f4ae02fcc67d086cbd7bc48d160ac62238..5b2e350ca69a527deef1e7fc86f81da105a926a6 100644 (file)
@@ -5947,125 +5947,84 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk)
     compCurStmtNum = blk->bbStmtNum - 1; // Set compCurStmtNum
 #endif
 
+    GenTreeStmt* stmt = blk->firstStmt();
+
     // First: visit phi's.  If "newVNForPhis", give them new VN's.  If not,
     // first check to see if all phi args have the same value.
-    GenTreeStmt* firstNonPhi = blk->FirstNonPhiDef();
-    for (GenTreeStmt* phiDefStmt = blk->firstStmt(); phiDefStmt != firstNonPhi; phiDefStmt = phiDefStmt->getNextStmt())
+    for (; (stmt != nullptr) && stmt->IsPhiDefnStmt(); stmt = stmt->getNextStmt())
     {
-        // TODO-Cleanup: It has been proposed that we should have an IsPhiDef predicate.  We would use it
-        // in Block::FirstNonPhiDef as well.
-        GenTree* phiDef = phiDefStmt->gtStmtExpr;
-        assert(phiDef->OperGet() == GT_ASG);
-        GenTreeLclVarCommon* newSsaVar = phiDef->gtOp.gtOp1->AsLclVarCommon();
-
-        ValueNumPair phiAppVNP;
-        ValueNumPair sameVNPair;
-
-        GenTree* phiFunc = phiDef->gtOp.gtOp2;
+        GenTree* asg = stmt->gtStmtExpr;
+        assert(asg->OperIs(GT_ASG));
 
-        // At this point a GT_PHI node should never have a nullptr for gtOp1
-        // and the gtOp1 should always be a GT_LIST node.
-        GenTree* phiOp1 = phiFunc->gtOp.gtOp1;
-        noway_assert(phiOp1 != nullptr);
-        noway_assert(phiOp1->OperGet() == GT_LIST);
+        GenTreeLclVar* newSsaDef = asg->AsOp()->gtGetOp1()->AsLclVar();
+        ValueNumPair   phiVNP;
+        ValueNumPair   sameVNP;
 
-        GenTreeArgList* phiArgs = phiFunc->gtOp.gtOp1->AsArgList();
-
-        // A GT_PHI node should have more than one argument.
-        noway_assert(phiArgs->Rest() != nullptr);
+        for (GenTreePhi::Use& use : asg->AsOp()->gtGetOp2()->AsPhi()->Uses())
+        {
+            GenTreePhiArg* phiArg         = use.GetNode()->AsPhiArg();
+            ValueNum       phiArgSsaNumVN = vnStore->VNForIntCon(phiArg->GetSsaNum());
+            ValueNumPair   phiArgVNP      = lvaGetDesc(phiArg)->GetPerSsaData(phiArg->GetSsaNum())->m_vnPair;
 
-        GenTreeLclVarCommon* phiArg = phiArgs->Current()->AsLclVarCommon();
-        phiArgs                     = phiArgs->Rest();
+            phiArg->gtVNPair = phiArgVNP;
 
-        phiAppVNP.SetBoth(vnStore->VNForIntCon(phiArg->gtSsaNum));
-        bool allSameLib  = true;
-        bool allSameCons = true;
-        sameVNPair       = lvaTable[phiArg->gtLclNum].GetPerSsaData(phiArg->gtSsaNum)->m_vnPair;
-        if (!sameVNPair.BothDefined())
-        {
-            allSameLib  = false;
-            allSameCons = false;
-        }
-        while (phiArgs != nullptr)
-        {
-            phiArg = phiArgs->Current()->AsLclVarCommon();
-            // Set the VN of the phi arg.
-            phiArg->gtVNPair = lvaTable[phiArg->gtLclNum].GetPerSsaData(phiArg->gtSsaNum)->m_vnPair;
-            if (phiArg->gtVNPair.BothDefined())
+            if (phiVNP.GetLiberal() == ValueNumStore::NoVN)
             {
-                if (phiArg->gtVNPair.GetLiberal() != sameVNPair.GetLiberal())
-                {
-                    allSameLib = false;
-                }
-                if (phiArg->gtVNPair.GetConservative() != sameVNPair.GetConservative())
-                {
-                    allSameCons = false;
-                }
+                // This is the first PHI argument
+                phiVNP  = ValueNumPair(phiArgSsaNumVN, phiArgSsaNumVN);
+                sameVNP = phiArgVNP;
             }
             else
             {
-                allSameLib  = false;
-                allSameCons = false;
+                phiVNP = vnStore->VNPairForFunc(newSsaDef->TypeGet(), VNF_Phi,
+                                                ValueNumPair(phiArgSsaNumVN, phiArgSsaNumVN), phiVNP);
+
+                if ((sameVNP.GetLiberal() != phiArgVNP.GetLiberal()) ||
+                    (sameVNP.GetConservative() != phiArgVNP.GetConservative()))
+                {
+                    // If this argument's VNs are different from "same" then change "same" to NoVN.
+                    // Note that this means that if any argument's VN is NoVN then the final result
+                    // will also be NoVN, which is what we want.
+                    sameVNP.SetBoth(ValueNumStore::NoVN);
+                }
             }
-            ValueNumPair phiArgSsaVNP;
-            phiArgSsaVNP.SetBoth(vnStore->VNForIntCon(phiArg->gtSsaNum));
-            phiAppVNP = vnStore->VNPairForFunc(newSsaVar->TypeGet(), VNF_Phi, phiArgSsaVNP, phiAppVNP);
-            phiArgs   = phiArgs->Rest();
         }
 
-        ValueNumPair newVNPair;
-        if (allSameLib)
-        {
-            newVNPair.SetLiberal(sameVNPair.GetLiberal());
-        }
-        else
-        {
-            newVNPair.SetLiberal(phiAppVNP.GetLiberal());
-        }
-        if (allSameCons)
+#ifdef DEBUG
+        // There should be at least to 2 PHI arguments so phiVN's VNs should always be VNF_Phi functions.
+        VNFuncApp phiFunc;
+        assert(vnStore->GetVNFunc(phiVNP.GetLiberal(), &phiFunc) && (phiFunc.m_func == VNF_Phi));
+        assert(vnStore->GetVNFunc(phiVNP.GetConservative(), &phiFunc) && (phiFunc.m_func == VNF_Phi));
+#endif
+
+        ValueNumPair newSsaDefVNP;
+
+        if (sameVNP.BothDefined())
         {
-            newVNPair.SetConservative(sameVNPair.GetConservative());
+            // If all the args of the phi had the same value(s, liberal and conservative), then there wasn't really
+            // a reason to have the phi -- just pass on that value.
+            newSsaDefVNP = sameVNP;
         }
         else
         {
-            newVNPair.SetConservative(phiAppVNP.GetConservative());
+            // They were not the same; we need to create a phi definition.
+            ValueNum lclNumVN = ValueNum(newSsaDef->GetLclNum());
+            ValueNum ssaNumVN = ValueNum(newSsaDef->GetSsaNum());
+
+            newSsaDefVNP = vnStore->VNPairForFunc(newSsaDef->TypeGet(), VNF_PhiDef, ValueNumPair(lclNumVN, lclNumVN),
+                                                  ValueNumPair(ssaNumVN, ssaNumVN), phiVNP);
         }
 
-        LclSsaVarDsc* newSsaVarDsc = lvaTable[newSsaVar->gtLclNum].GetPerSsaData(newSsaVar->GetSsaNum());
-        // If all the args of the phi had the same value(s, liberal and conservative), then there wasn't really
-        // a reason to have the phi -- just pass on that value.
-        if (allSameLib && allSameCons)
-        {
-            newSsaVarDsc->m_vnPair = newVNPair;
+        LclSsaVarDsc* newSsaDefDsc = lvaGetDesc(newSsaDef)->GetPerSsaData(newSsaDef->GetSsaNum());
+        newSsaDefDsc->m_vnPair     = newSsaDefVNP;
 #ifdef DEBUG
-            if (verbose)
-            {
-                printf("In SSA definition, incoming phi args all same, set VN of local %d/%d to ",
-                       newSsaVar->GetLclNum(), newSsaVar->GetSsaNum());
-                vnpPrint(newVNPair, 1);
-                printf(".\n");
-            }
-#endif // DEBUG
-        }
-        else
+        if (verbose)
         {
-            // They were not the same; we need to create a phi definition.
-            ValueNumPair lclNumVNP;
-            lclNumVNP.SetBoth(ValueNum(newSsaVar->GetLclNum()));
-            ValueNumPair ssaNumVNP;
-            ssaNumVNP.SetBoth(ValueNum(newSsaVar->GetSsaNum()));
-            ValueNumPair vnPhiDef =
-                vnStore->VNPairForFunc(newSsaVar->TypeGet(), VNF_PhiDef, lclNumVNP, ssaNumVNP, phiAppVNP);
-            newSsaVarDsc->m_vnPair = vnPhiDef;
-#ifdef DEBUG
-            if (verbose)
-            {
-                printf("SSA definition: set VN of local %d/%d to ", newSsaVar->GetLclNum(), newSsaVar->GetSsaNum());
-                vnpPrint(vnPhiDef, 1);
-                printf(".\n");
-            }
-#endif // DEBUG
+            printf("SSA PHI definition: set VN of local %d/%d to ", newSsaDef->GetLclNum(), newSsaDef->GetSsaNum());
+            vnpPrint(newSsaDefVNP, 1);
+            printf(" %s.\n", sameVNP.BothDefined() ? "(all same)" : "");
         }
+#endif // DEBUG
     }
 
     // Now do the same for each MemoryKind.
@@ -6157,7 +6116,7 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk)
     }
 
     // Now iterate over the remaining statements, and their trees.
-    for (GenTreeStmt* stmt = firstNonPhi; stmt != nullptr; stmt = stmt->getNextStmt())
+    for (; stmt != nullptr; stmt = stmt->getNextStmt())
     {
 #ifdef DEBUG
         compCurStmtNum++;