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;
}
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
case GT_NOP:
case GT_RETURN:
case GT_RETFILT:
- case GT_PHI:
case GT_RUNTIMELOOKUP:
{
GenTreeUnOp* const unOp = node->AsUnOp();
}
// 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();
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)
{
return;
// Variadic nodes
- case GT_PHI:
- assert(this->AsUnOp()->gtOp1 != nullptr);
- this->AsUnOp()->gtOp1->VisitListOperands(visitor);
- return;
-
case GT_FIELD_LIST:
VisitListOperands(visitor);
return;
#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();
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);
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);
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) &&
}
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))
{
}
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));
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);
}
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)
{
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);
#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();
}
break;
+ case GT_PHI:
+ {
+ copy = new (this, GT_PHI) GenTreePhi(tree->TypeGet());
+ GenTreePhi::Use** prevUse = ©->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(),
// Special
switch (OperGet())
{
+ case GT_PHI:
+ {
+ unsigned count = 0;
+ for (GenTreePhi::Use& use : AsPhi()->Uses())
+ {
+ count++;
+ }
+ return count;
+ }
+
case GT_CMPXCHG:
return 3;
// 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)
{
}
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);
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
{
}
else if (m_node->AsHWIntrinsic()->gtOp1->OperIsList())
{
- SetEntryStateForList(m_node->AsHWIntrinsic()->gtOp1);
+ SetEntryStateForList(m_node->AsHWIntrinsic()->gtOp1->AsArgList());
}
else
{
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);
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 = ¤tUse->NodeRef();
+ m_statePtr = currentUse->GetNext();
+ }
+}
+
//------------------------------------------------------------------------
// GenTreeUseEdgeIterator::AdvanceBinOp: produces the next operand of a binary node and advances the state.
//
{
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();
}
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;
__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>;
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))
{
assert(OperIsSimple(gtOper));
switch (gtOper)
{
- case GT_PHI:
case GT_LEA:
case GT_RETFILT:
case GT_NOP:
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.
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);
void AdvanceArrOffset();
void AdvanceDynBlk();
void AdvanceStoreDynBlk();
+ void AdvancePhi();
template <bool ReverseOperands>
void AdvanceBinOp();
// 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>
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);
}
// 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)
//-----------------------------------------------------------------------------
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
}
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);
{
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();
}
}
}
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;
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;
// 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);
// 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)
{
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).
{
// 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);
}
}
}
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;
}
}
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);
// (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.
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;
}
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);
}
}
// 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:
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.
}
// 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++;