From 9a5a61192c61e4a535e8ef206356d0975e8b113c Mon Sep 17 00:00:00 2001 From: mikedn Date: Thu, 3 Oct 2019 09:36:01 +0300 Subject: [PATCH] Stop using LIST nodes for FIELD_LIST (dotnet/coreclr#26800) * Stop using LIST nodes for FIELD_LIST * Change GT_FIELD_LIST type to TYP_STRUCT * Smaller GenTreeField::Use * Delete out of date comment * Cleanup LowerArg duplicated code * Add AddField/InsertField Commit migrated from https://github.com/dotnet/coreclr/commit/0a00ee7fdfd113c8c2d47c85ed210de78cab4bdd --- src/coreclr/src/jit/codegenarmarch.cpp | 13 +- src/coreclr/src/jit/codegenlinear.cpp | 7 +- src/coreclr/src/jit/codegenxarch.cpp | 29 +-- src/coreclr/src/jit/compiler.h | 11 ++ src/coreclr/src/jit/compiler.hpp | 16 +- src/coreclr/src/jit/decomposelongs.cpp | 29 ++- src/coreclr/src/jit/decomposelongs.h | 2 +- src/coreclr/src/jit/flowgraph.cpp | 19 +- src/coreclr/src/jit/gentree.cpp | 193 ++++++++++++++----- src/coreclr/src/jit/gentree.h | 333 ++++++++++++++++++++++++--------- src/coreclr/src/jit/gtlist.h | 2 +- src/coreclr/src/jit/gtstructs.h | 2 +- src/coreclr/src/jit/lower.cpp | 113 +++++------ src/coreclr/src/jit/lowerxarch.cpp | 82 ++------ src/coreclr/src/jit/lsraarmarch.cpp | 19 +- src/coreclr/src/jit/lsrabuild.cpp | 3 - src/coreclr/src/jit/lsraxarch.cpp | 35 ++-- src/coreclr/src/jit/morph.cpp | 136 +++++--------- src/coreclr/src/jit/rationalize.cpp | 12 +- 19 files changed, 609 insertions(+), 447 deletions(-) diff --git a/src/coreclr/src/jit/codegenarmarch.cpp b/src/coreclr/src/jit/codegenarmarch.cpp index 800412f..61896ef 100644 --- a/src/coreclr/src/jit/codegenarmarch.cpp +++ b/src/coreclr/src/jit/codegenarmarch.cpp @@ -1120,10 +1120,9 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode) // Evaluate each of the GT_FIELD_LIST items into their register // and store their register into the outgoing argument area unsigned regIndex = 0; - for (GenTreeFieldList* fieldListPtr = source->AsFieldList(); fieldListPtr != nullptr; - fieldListPtr = fieldListPtr->Rest()) + for (GenTreeFieldList::Use& use : source->AsFieldList()->Uses()) { - GenTree* nextArgNode = fieldListPtr->gtGetOp1(); + GenTree* nextArgNode = use.GetNode(); regNumber fieldReg = nextArgNode->gtRegNum; genConsumeReg(nextArgNode); @@ -2288,12 +2287,10 @@ void CodeGen::genCallInstruction(GenTreeCall* call) // Deal with multi register passed struct args. if (argNode->OperGet() == GT_FIELD_LIST) { - GenTreeArgList* argListPtr = argNode->AsArgList(); - unsigned iterationNum = 0; - regNumber argReg = curArgTabEntry->regNum; - for (; argListPtr != nullptr; argListPtr = argListPtr->Rest(), iterationNum++) + regNumber argReg = curArgTabEntry->regNum; + for (GenTreeFieldList::Use& use : argNode->AsFieldList()->Uses()) { - GenTree* putArgRegNode = argListPtr->gtOp.gtOp1; + GenTree* putArgRegNode = use.GetNode(); assert(putArgRegNode->gtOper == GT_PUTARG_REG); genConsumeReg(putArgRegNode); diff --git a/src/coreclr/src/jit/codegenlinear.cpp b/src/coreclr/src/jit/codegenlinear.cpp index 852844c..811e063 100644 --- a/src/coreclr/src/jit/codegenlinear.cpp +++ b/src/coreclr/src/jit/codegenlinear.cpp @@ -1651,10 +1651,9 @@ void CodeGen::genPutArgStkFieldList(GenTreePutArgStk* putArgStk, unsigned outArg // Evaluate each of the GT_FIELD_LIST items into their register // and store their register into the outgoing argument area. unsigned argOffset = putArgStk->getArgOffset(); - for (GenTreeFieldList* fieldListPtr = putArgStk->gtOp1->AsFieldList(); fieldListPtr != nullptr; - fieldListPtr = fieldListPtr->Rest()) + for (GenTreeFieldList::Use& use : putArgStk->gtOp1->AsFieldList()->Uses()) { - GenTree* nextArgNode = fieldListPtr->gtOp.gtOp1; + GenTree* nextArgNode = use.GetNode(); genConsumeReg(nextArgNode); regNumber reg = nextArgNode->gtRegNum; @@ -1663,7 +1662,7 @@ void CodeGen::genPutArgStkFieldList(GenTreePutArgStk* putArgStk, unsigned outArg // Emit store instructions to store the registers produced by the GT_FIELD_LIST into the outgoing // argument area. - unsigned thisFieldOffset = argOffset + fieldListPtr->gtFieldOffset; + unsigned thisFieldOffset = argOffset + use.GetOffset(); GetEmitter()->emitIns_S_R(ins_Store(type), attr, reg, outArgVarNum, thisFieldOffset); // We can't write beyond the arg area unless this is a tail call, in which case we use diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index 36fc8f3..3570a08 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -5361,23 +5361,12 @@ void CodeGen::genCallInstruction(GenTreeCall* call) // Deal with multi register passed struct args. if (argNode->OperGet() == GT_FIELD_LIST) { - GenTreeFieldList* fieldListPtr = argNode->AsFieldList(); - unsigned iterationNum = 0; - for (; fieldListPtr != nullptr; fieldListPtr = fieldListPtr->Rest(), iterationNum++) + unsigned regIndex = 0; + for (GenTreeFieldList::Use& use : argNode->AsFieldList()->Uses()) { - GenTree* putArgRegNode = fieldListPtr->gtOp.gtOp1; + GenTree* putArgRegNode = use.GetNode(); assert(putArgRegNode->gtOper == GT_PUTARG_REG); - regNumber argReg = REG_NA; - - if (iterationNum == 0) - { - argReg = curArgTabEntry->regNum; - } - else - { - assert(iterationNum == 1); - argReg = curArgTabEntry->GetOtherRegNum(); - } + regNumber argReg = curArgTabEntry->getRegNum(regIndex++); genConsumeReg(putArgRegNode); @@ -5431,7 +5420,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) assert(curArgTabEntry != nullptr); assert(size == (curArgTabEntry->numSlots * TARGET_POINTER_SIZE)); #ifdef FEATURE_PUT_STRUCT_ARG_STK - if (source->TypeGet() == TYP_STRUCT) + if (!source->OperIs(GT_FIELD_LIST) && (source->TypeGet() == TYP_STRUCT)) { GenTreeObj* obj = source->AsObj(); unsigned argBytes = roundUp(obj->GetLayout()->GetSize(), TARGET_POINTER_SIZE); @@ -7729,11 +7718,11 @@ void CodeGen::genPutArgStkFieldList(GenTreePutArgStk* putArgStk) assert(genCountBits(rsvdRegs) == (unsigned)((intTmpReg == REG_NA) ? 0 : 1) + ((simdTmpReg == REG_NA) ? 0 : 1)); } - for (GenTreeFieldList* current = fieldList; current != nullptr; current = current->Rest()) + for (GenTreeFieldList::Use& use : fieldList->Uses()) { - GenTree* const fieldNode = current->Current(); - const unsigned fieldOffset = current->gtFieldOffset; - var_types fieldType = current->gtFieldType; + GenTree* const fieldNode = use.GetNode(); + const unsigned fieldOffset = use.GetOffset(); + var_types fieldType = use.GetType(); // Long-typed nodes should have been handled by the decomposition pass, and lowering should have sorted the // field list in descending order by offset. diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 8b40f7c..f71d81c 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9981,6 +9981,17 @@ public: } break; + case GT_FIELD_LIST: + for (GenTreeFieldList::Use& use : node->AsFieldList()->Uses()) + { + result = WalkTree(&use.NodeRef(), node); + if (result == fgWalkResult::WALK_ABORT) + { + return result; + } + } + break; + case GT_CMPXCHG: { GenTreeCmpXchg* const cmpXchg = node->AsCmpXchg(); diff --git a/src/coreclr/src/jit/compiler.hpp b/src/coreclr/src/jit/compiler.hpp index 1836dc4..0343c63 100644 --- a/src/coreclr/src/jit/compiler.hpp +++ b/src/coreclr/src/jit/compiler.hpp @@ -4269,11 +4269,7 @@ void GenTree::VisitOperands(TVisitor visitor) visitor(this->AsUnOp()->gtOp1); return; - // Variadic nodes - case GT_FIELD_LIST: - VisitListOperands(visitor); - return; - +// Variadic nodes #ifdef FEATURE_SIMD case GT_SIMD: if (this->AsSIMD()->gtSIMDIntrinsicID == SIMDIntrinsicInitN) @@ -4312,6 +4308,16 @@ void GenTree::VisitOperands(TVisitor visitor) } return; + case GT_FIELD_LIST: + for (GenTreeFieldList::Use& field : AsFieldList()->Uses()) + { + if (visitor(field.GetNode()) == VisitResult::Abort) + { + break; + } + } + return; + case GT_CMPXCHG: { GenTreeCmpXchg* const cmpXchg = this->AsCmpXchg(); diff --git a/src/coreclr/src/jit/decomposelongs.cpp b/src/coreclr/src/jit/decomposelongs.cpp index a5b6782..df5bf42e 100644 --- a/src/coreclr/src/jit/decomposelongs.cpp +++ b/src/coreclr/src/jit/decomposelongs.cpp @@ -684,7 +684,7 @@ GenTree* DecomposeLongs::DecomposeCnsLng(LIR::Use& use) // DecomposeFieldList: Decompose GT_FIELD_LIST. // // Arguments: -// listNode - the head of the FIELD_LIST that contains the given GT_LONG. +// fieldList - the GT_FIELD_LIST node that uses the given GT_LONG node. // longNode - the node to decompose // // Return Value: @@ -693,34 +693,29 @@ GenTree* DecomposeLongs::DecomposeCnsLng(LIR::Use& use) // Notes: // Split a LONG field list element into two elements: one for each half of the GT_LONG. // -GenTree* DecomposeLongs::DecomposeFieldList(GenTreeFieldList* listNode, GenTreeOp* longNode) +GenTree* DecomposeLongs::DecomposeFieldList(GenTreeFieldList* fieldList, GenTreeOp* longNode) { assert(longNode->OperGet() == GT_LONG); - // We are given the head of the field list. We need to find the actual node that uses - // the `GT_LONG` so that we can split it. - for (; listNode != nullptr; listNode = listNode->Rest()) + + GenTreeFieldList::Use* loUse = nullptr; + for (GenTreeFieldList::Use& use : fieldList->Uses()) { - if (listNode->Current() == longNode) + if (use.GetNode() == longNode) { + loUse = &use; break; } } - assert(listNode != nullptr); + assert(loUse != nullptr); Range().Remove(longNode); - GenTree* rest = listNode->gtOp2; - - GenTreeFieldList* loNode = listNode; - loNode->gtType = TYP_INT; - loNode->gtOp1 = longNode->gtOp1; - loNode->gtFieldType = TYP_INT; + loUse->SetNode(longNode->gtGetOp1()); + loUse->SetType(TYP_INT); - GenTreeFieldList* hiNode = - new (m_compiler, GT_FIELD_LIST) GenTreeFieldList(longNode->gtOp2, loNode->gtFieldOffset + 4, TYP_INT, loNode); - hiNode->gtOp2 = rest; + fieldList->InsertFieldLIR(m_compiler, loUse, longNode->gtGetOp2(), loUse->GetOffset() + 4, TYP_INT); - return listNode->gtNext; + return fieldList->gtNext; } //------------------------------------------------------------------------ diff --git a/src/coreclr/src/jit/decomposelongs.h b/src/coreclr/src/jit/decomposelongs.h index c008fa2..0388b5c 100644 --- a/src/coreclr/src/jit/decomposelongs.h +++ b/src/coreclr/src/jit/decomposelongs.h @@ -45,7 +45,7 @@ private: GenTree* DecomposeStoreLclFld(LIR::Use& use); GenTree* DecomposeCast(LIR::Use& use); GenTree* DecomposeCnsLng(LIR::Use& use); - GenTree* DecomposeFieldList(GenTreeFieldList* listNode, GenTreeOp* longNode); + GenTree* DecomposeFieldList(GenTreeFieldList* fieldList, GenTreeOp* longNode); GenTree* DecomposeCall(LIR::Use& use); GenTree* DecomposeInd(LIR::Use& use); GenTree* DecomposeStoreInd(LIR::Use& use); diff --git a/src/coreclr/src/jit/flowgraph.cpp b/src/coreclr/src/jit/flowgraph.cpp index fe158af..fef17ce 100644 --- a/src/coreclr/src/jit/flowgraph.cpp +++ b/src/coreclr/src/jit/flowgraph.cpp @@ -18838,6 +18838,13 @@ void Compiler::fgSetTreeSeqHelper(GenTree* tree, bool isLIR) } break; + case GT_FIELD_LIST: + for (GenTreeFieldList::Use& use : tree->AsFieldList()->Uses()) + { + fgSetTreeSeqHelper(use.GetNode(), isLIR); + } + break; + case GT_CMPXCHG: // Evaluate the trees left to right fgSetTreeSeqHelper(tree->gtCmpXchg.gtOpLocation, isLIR); @@ -18889,8 +18896,7 @@ void Compiler::fgSetTreeSeqFinish(GenTree* tree, bool isLIR) { tree->gtFlags &= ~GTF_REVERSE_OPS; - if ((tree->OperGet() == GT_LIST) || (tree->OperGet() == GT_ARGPLACE) || - (tree->OperGet() == GT_FIELD_LIST && !tree->AsFieldList()->IsFieldListHead())) + if (tree->OperIs(GT_LIST, GT_ARGPLACE)) { return; } @@ -21209,7 +21215,6 @@ void Compiler::fgDebugCheckFlags(GenTree* tree) break; case GT_LIST: - case GT_FIELD_LIST: if ((op2 != nullptr) && op2->OperIsAnyList()) { ArrayStack stack(getAllocator(CMK_DebugOnly)); @@ -21432,6 +21437,14 @@ void Compiler::fgDebugCheckFlags(GenTree* tree) } break; + case GT_FIELD_LIST: + for (GenTreeFieldList::Use& use : tree->AsFieldList()->Uses()) + { + fgDebugCheckFlags(use.GetNode()); + chkFlags |= (use.GetNode()->gtFlags & GTF_ALL_EFFECT); + } + break; + case GT_CMPXCHG: chkFlags |= (GTF_GLOB_REF | GTF_ASG); diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index 5d69e55..ef95417 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -793,6 +793,29 @@ regMaskTP GenTree::gtGetRegMask() const return resultMask; } +void GenTreeFieldList::AddField(Compiler* compiler, GenTree* node, unsigned offset, var_types type) +{ + m_uses.AddUse(new (compiler, CMK_ASTNode) Use(node, offset, type)); + gtFlags |= node->gtFlags & GTF_ALL_EFFECT; +} + +void GenTreeFieldList::AddFieldLIR(Compiler* compiler, GenTree* node, unsigned offset, var_types type) +{ + m_uses.AddUse(new (compiler, CMK_ASTNode) Use(node, offset, type)); +} + +void GenTreeFieldList::InsertField(Compiler* compiler, Use* insertAfter, GenTree* node, unsigned offset, var_types type) +{ + m_uses.InsertUse(insertAfter, new (compiler, CMK_ASTNode) Use(node, offset, type)); + gtFlags |= node->gtFlags & GTF_ALL_EFFECT; +} + +void GenTreeFieldList::InsertFieldLIR( + Compiler* compiler, Use* insertAfter, GenTree* node, unsigned offset, var_types type) +{ + m_uses.InsertUse(insertAfter, new (compiler, CMK_ASTNode) Use(node, offset, type)); +} + //--------------------------------------------------------------- // GetOtherRegMask: Get the reg mask of gtOtherRegs of call node // @@ -1549,6 +1572,9 @@ AGAIN: case GT_PHI: return GenTreePhi::Equals(op1->AsPhi(), op2->AsPhi()); + case GT_FIELD_LIST: + return GenTreeFieldList::Equals(op1->AsFieldList(), op2->AsFieldList()); + case GT_CMPXCHG: return Compare(op1->gtCmpXchg.gtOpLocation, op2->gtCmpXchg.gtOpLocation) && Compare(op1->gtCmpXchg.gtOpValue, op2->gtCmpXchg.gtOpValue) && @@ -1775,6 +1801,16 @@ AGAIN: } break; + case GT_FIELD_LIST: + for (GenTreeFieldList::Use& use : tree->AsFieldList()->Uses()) + { + if (gtHasRef(use.GetNode(), lclNum, defOnly)) + { + return true; + } + } + break; + case GT_CMPXCHG: if (gtHasRef(tree->gtCmpXchg.gtOpLocation, lclNum, defOnly)) { @@ -2202,6 +2238,13 @@ AGAIN: } break; + case GT_FIELD_LIST: + for (GenTreeFieldList::Use& use : tree->AsFieldList()->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)); @@ -3505,7 +3548,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) break; case GT_LIST: - case GT_FIELD_LIST: case GT_NOP: costEx = 0; costSz = 0; @@ -3879,7 +3921,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) goto DONE; case GT_LIST: - case GT_FIELD_LIST: { const bool isListCallArgs = false; const bool callArgsInRegs = false; @@ -4170,7 +4211,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) break; case GT_LIST: - case GT_FIELD_LIST: break; default: @@ -4378,6 +4418,20 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) costSz = 0; break; + case GT_FIELD_LIST: + level = 0; + costEx = 0; + costSz = 0; + for (GenTreeFieldList::Use& use : tree->AsFieldList()->Uses()) + { + unsigned opLevel = gtSetEvalOrder(use.GetNode()); + level = max(level, opLevel); + gtSetEvalOrder(use.GetNode()); + costEx += use.GetNode()->GetCostEx(); + costSz += use.GetNode()->GetCostSz(); + } + break; + case GT_CMPXCHG: level = gtSetEvalOrder(tree->gtCmpXchg.gtOpLocation); @@ -4684,6 +4738,16 @@ GenTree** GenTree::gtGetChildPointer(GenTree* parent) const } break; + case GT_FIELD_LIST: + for (GenTreeFieldList::Use& use : parent->AsFieldList()->Uses()) + { + if (this == use.GetNode()) + { + return &use.NodeRef(); + } + } + break; + case GT_CMPXCHG: if (this == parent->gtCmpXchg.gtOpLocation) { @@ -4900,15 +4964,12 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use) } return false; - // Variadic nodes - case GT_FIELD_LIST: - return TryGetUseList(def, use); - +// Variadic nodes #if FEATURE_ARG_SPLIT case GT_PUTARG_SPLIT: if (this->AsUnOp()->gtOp1->gtOper == GT_FIELD_LIST) { - return this->AsUnOp()->gtOp1->TryGetUseList(def, use); + return this->AsUnOp()->gtOp1->TryGetUse(def, use); } if (def == this->AsUnOp()->gtOp1) { @@ -4951,6 +5012,17 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use) } return false; + case GT_FIELD_LIST: + for (GenTreeFieldList::Use& fieldUse : AsFieldList()->Uses()) + { + if (fieldUse.GetNode() == def) + { + *use = &fieldUse.NodeRef(); + return true; + } + } + return false; + case GT_CMPXCHG: { GenTreeCmpXchg* const cmpXchg = this->AsCmpXchg(); @@ -5541,15 +5613,6 @@ GenTree::VtablePtr GenTree::GetVtableForOper(genTreeOps oper) } break; - // Handle GT_LIST (but not GT_FIELD_LIST, which is also in a GTSTRUCT_1). - - case GT_LIST: - { - GenTreeArgList gt; - res = *reinterpret_cast(>); - } - break; - // We don't need to handle GTSTRUCT_N for LclVarCommon, since all those allowed opers are specified // in their proper subtype. Similarly for GenTreeIndir. @@ -7193,13 +7256,6 @@ GenTree* Compiler::gtCloneExpr( copy->gtOp.gtOp2 = tree->gtOp.gtOp2; break; - case GT_FIELD_LIST: - copy = new (this, GT_FIELD_LIST) GenTreeFieldList(tree->gtOp.gtOp1, tree->AsFieldList()->gtFieldOffset, - tree->AsFieldList()->gtFieldType, nullptr); - copy->gtOp.gtOp2 = tree->gtOp.gtOp2; - copy->gtFlags = (copy->gtFlags & ~GTF_FIELD_LIST_HEAD) | (tree->gtFlags & GTF_FIELD_LIST_HEAD); - break; - case GT_INDEX: { GenTreeIndex* asInd = tree->AsIndex(); @@ -7474,6 +7530,15 @@ GenTree* Compiler::gtCloneExpr( } break; + case GT_FIELD_LIST: + copy = new (this, GT_FIELD_LIST) GenTreeFieldList(); + for (GenTreeFieldList::Use& use : tree->AsFieldList()->Uses()) + { + copy->AsFieldList()->AddField(this, gtCloneExpr(use.GetNode(), addFlags, deepVarNum, deepVarVal), + use.GetOffset(), use.GetType()); + } + break; + case GT_CMPXCHG: copy = new (this, GT_CMPXCHG) GenTreeCmpXchg(tree->TypeGet(), @@ -8258,6 +8323,16 @@ unsigned GenTree::NumChildren() return count; } + case GT_FIELD_LIST: + { + unsigned count = 0; + for (GenTreeFieldList::Use& use : AsFieldList()->Uses()) + { + count++; + } + return count; + } + case GT_CMPXCHG: return 3; @@ -8382,6 +8457,17 @@ GenTree* GenTree::GetChild(unsigned childNum) } unreached(); + case GT_FIELD_LIST: + for (GenTreeFieldList::Use& use : AsFieldList()->Uses()) + { + if (childNum == 0) + { + return use.GetNode(); + } + childNum--; + } + unreached(); + case GT_CMPXCHG: switch (childNum) { @@ -8635,11 +8721,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) } return; - // Variadic nodes - case GT_FIELD_LIST: - SetEntryStateForList(m_node->AsArgList()); - return; - +// Variadic nodes #ifdef FEATURE_SIMD case GT_SIMD: if (m_node->AsSIMD()->gtSIMDIntrinsicID == SIMDIntrinsicInitN) @@ -8685,6 +8767,12 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) return; // Special nodes + case GT_FIELD_LIST: + m_statePtr = m_node->AsFieldList()->Uses().GetHead(); + m_advance = &GenTreeUseEdgeIterator::AdvanceFieldList; + AdvanceFieldList(); + return; + case GT_PHI: m_statePtr = m_node->AsPhi()->gtUses; m_advance = &GenTreeUseEdgeIterator::AdvancePhi; @@ -8902,6 +8990,25 @@ void GenTreeUseEdgeIterator::AdvanceStoreDynBlk() } //------------------------------------------------------------------------ +// GenTreeUseEdgeIterator::AdvanceFieldList: produces the next operand of a FieldList node and advances the state. +// +void GenTreeUseEdgeIterator::AdvanceFieldList() +{ + assert(m_state == 0); + + if (m_statePtr == nullptr) + { + m_state = -1; + } + else + { + GenTreeFieldList::Use* currentUse = static_cast(m_statePtr); + m_edge = ¤tUse->NodeRef(); + m_statePtr = currentUse->GetNext(); + } +} + +//------------------------------------------------------------------------ // GenTreeUseEdgeIterator::AdvancePhi: produces the next operand of a Phi node and advances the state. // void GenTreeUseEdgeIterator::AdvancePhi() @@ -9749,15 +9856,6 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _ printf((tree->gtFlags & GTF_JCMP_EQ) ? "EQ" : "NE"); goto DASH; - case GT_FIELD_LIST: - if (tree->gtFlags & GTF_FIELD_LIST_HEAD) - { - printf("H"); - --msgLength; - break; - } - goto DASH; - default: DASH: printf("-"); @@ -10799,11 +10897,6 @@ void Compiler::gtDispTree(GenTree* tree, } } } - else if (tree->OperIsFieldList()) - { - printf(" %s at offset %d", varTypeName(tree->AsFieldList()->gtFieldType), - tree->AsFieldList()->gtFieldOffset); - } #if FEATURE_PUT_STRUCT_ARG_STK else if (tree->OperGet() == GT_PUTARG_STK) { @@ -10994,6 +11087,20 @@ void Compiler::gtDispTree(GenTree* tree, switch (tree->gtOper) { + case GT_FIELD_LIST: + gtDispCommonEndLine(tree); + + if (!topOnly) + { + for (GenTreeFieldList::Use& use : tree->AsFieldList()->Uses()) + { + char offset[32]; + sprintf_s(offset, sizeof(offset), "ofs %u", use.GetOffset()); + gtDispChild(use.GetNode(), indentStack, (use.GetNext() == nullptr) ? IIArcBottom : IIArc, offset); + } + } + break; + case GT_PHI: gtDispCommonEndLine(tree); diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index 2250902..6bc6a51 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -885,9 +885,6 @@ public: #define GTF_ARRLEN_ARR_IDX 0x80000000 // GT_ARR_LENGTH -- Length which feeds into an array index expression #define GTF_ARRLEN_NONFAULTING 0x20000000 // GT_ARR_LENGTH -- An array length operation that cannot fault. Same as GT_IND_NONFAULTING. -#define GTF_FIELD_LIST_HEAD 0x80000000 // GT_FIELD_LIST -- Indicates that this is the first field in a list of - // struct fields constituting a single call argument. - #define GTF_SIMD12_OP 0x80000000 // GT_SIMD -- Indicates that the operands need to be handled as SIMD12 // even if they have been retyped as SIMD16. @@ -967,16 +964,11 @@ public: if (gtType == TYP_VOID) { // These are the only operators which can produce either VOID or non-VOID results. - assert(OperIs(GT_NOP, GT_CALL, GT_FIELD_LIST, GT_COMMA) || OperIsCompare() || OperIsLong() || - OperIsSIMD() || OperIsHWIntrinsic()); + assert(OperIs(GT_NOP, GT_CALL, GT_COMMA) || OperIsCompare() || OperIsLong() || OperIsSIMD() || + OperIsHWIntrinsic()); return false; } - if (gtOper == GT_FIELD_LIST) - { - return (gtFlags & GTF_FIELD_LIST_HEAD) != 0; - } - return true; } @@ -998,10 +990,6 @@ public: // be present as children of an LIR node. return (gtNext == nullptr) && (gtPrev == nullptr); - case GT_FIELD_LIST: - // Only the head of the FIELD_LIST is present in the block's LIR sequence. - return (((gtFlags & GTF_FIELD_LIST_HEAD) != 0) || ((gtNext == nullptr) && (gtPrev == nullptr))); - case GT_ADDR: { // ADDR ndoes may only be present in LIR if the location they refer to is not a @@ -1529,11 +1517,6 @@ public: return OperIsLong(gtOper); } - bool OperIsFieldListHead() - { - return (gtOper == GT_FIELD_LIST) && ((gtFlags & GTF_FIELD_LIST_HEAD) != 0); - } - bool OperIsConditionalJump() const { return (gtOper == GT_JTRUE) || (gtOper == GT_JCMP) || (gtOper == GT_JCC); @@ -1595,7 +1578,6 @@ public: switch (gtOper) { case GT_LIST: - case GT_FIELD_LIST: case GT_INTRINSIC: case GT_LEA: #ifdef FEATURE_SIMD @@ -1638,19 +1620,9 @@ public: return OperIsList(gtOper); } - static bool OperIsFieldList(genTreeOps gtOper) - { - return gtOper == GT_FIELD_LIST; - } - - bool OperIsFieldList() const - { - return OperIsFieldList(gtOper); - } - static bool OperIsAnyList(genTreeOps gtOper) { - return OperIsList(gtOper) || OperIsFieldList(gtOper); + return OperIsList(gtOper); } bool OperIsAnyList() const @@ -2341,6 +2313,248 @@ struct GenTreePhi final : public GenTree #endif }; +// Represents a list of fields constituting a struct, when it is passed as an argument. +// +struct GenTreeFieldList : public GenTree +{ + class Use + { + GenTree* m_node; + Use* m_next; + uint16_t m_offset; + var_types m_type; + + public: + Use(GenTree* node, unsigned offset, var_types type) + : m_node(node), m_next(nullptr), m_offset(static_cast(offset)), m_type(type) + { + // We can save space on 32 bit hosts by storing the offset as uint16_t. Struct promotion + // only accepts structs which are much smaller than that - 128 bytes = max 4 fields * max + // SIMD vector size (32 bytes). + assert(offset <= UINT16_MAX); + } + + GenTree*& NodeRef() + { + return m_node; + } + + GenTree* GetNode() const + { + return m_node; + } + + void SetNode(GenTree* node) + { + assert(node != nullptr); + m_node = node; + } + + Use*& NextRef() + { + return m_next; + } + + Use* GetNext() const + { + return m_next; + } + + void SetNext(Use* next) + { + m_next = next; + } + + unsigned GetOffset() const + { + return m_offset; + } + + var_types GetType() const + { + return m_type; + } + + void SetType(var_types type) + { + m_type = type; + } + }; + + class UseIterator + { + Use* use; + + public: + UseIterator(Use* use) : use(use) + { + } + + Use& operator*() + { + return *use; + } + + Use* operator->() + { + return use; + } + + void operator++() + { + use = use->GetNext(); + } + + bool operator==(const UseIterator& other) + { + return use == other.use; + } + + bool operator!=(const UseIterator& other) + { + return use != other.use; + } + }; + + class UseList + { + Use* m_head; + Use* m_tail; + + public: + UseList() : m_head(nullptr), m_tail(nullptr) + { + } + + Use* GetHead() const + { + return m_head; + } + + UseIterator begin() const + { + return m_head; + } + + UseIterator end() const + { + return nullptr; + } + + void AddUse(Use* newUse) + { + assert(newUse->GetNext() == nullptr); + + if (m_head == nullptr) + { + m_head = newUse; + } + else + { + m_tail->SetNext(newUse); + } + + m_tail = newUse; + } + + void InsertUse(Use* insertAfter, Use* newUse) + { + assert(newUse->GetNext() == nullptr); + + newUse->SetNext(insertAfter->GetNext()); + insertAfter->SetNext(newUse); + + if (m_tail == insertAfter) + { + m_tail = newUse; + } + } + + void Reverse() + { + m_tail = m_head; + m_head = nullptr; + + for (Use *next, *use = m_tail; use != nullptr; use = next) + { + next = use->GetNext(); + use->SetNext(m_head); + m_head = use; + } + } + + bool IsSorted() const + { + unsigned offset = 0; + for (GenTreeFieldList::Use& use : *this) + { + if (use.GetOffset() < offset) + { + return false; + } + offset = use.GetOffset(); + } + return true; + } + }; + +private: + UseList m_uses; + +public: + GenTreeFieldList() : GenTree(GT_FIELD_LIST, TYP_STRUCT) + { + SetContained(); + } + + UseList& Uses() + { + return m_uses; + } + + // Add a new field use to the end of the use list and update side effect flags. + void AddField(Compiler* compiler, GenTree* node, unsigned offset, var_types type); + // Add a new field use to the end of the use list without updating side effect flags. + void AddFieldLIR(Compiler* compiler, GenTree* node, unsigned offset, var_types type); + // Insert a new field use after the specified use and update side effect flags. + void InsertField(Compiler* compiler, Use* insertAfter, GenTree* node, unsigned offset, var_types type); + // Insert a new field use after the specified use without updating side effect flags. + void InsertFieldLIR(Compiler* compiler, Use* insertAfter, GenTree* node, unsigned offset, var_types type); + + //-------------------------------------------------------------------------- + // Equals: Check if 2 FIELD_LIST nodes are equal. + // + // Arguments: + // list1 - The first FIELD_LIST node + // list2 - The second FIELD_LIST node + // + // Return Value: + // true if the 2 FIELD_LIST nodes have the same type, number of uses, and the + // uses are equal. + // + static bool Equals(GenTreeFieldList* list1, GenTreeFieldList* list2) + { + assert(list1->TypeGet() == TYP_STRUCT); + assert(list2->TypeGet() == TYP_STRUCT); + + UseIterator i1 = list1->Uses().begin(); + UseIterator end1 = list1->Uses().end(); + UseIterator i2 = list2->Uses().begin(); + UseIterator end2 = list2->Uses().end(); + + for (; (i1 != end1) && (i2 != end2); ++i1, ++i2) + { + if (!Compare(i1->GetNode(), i2->GetNode()) || (i1->GetOffset() != i2->GetOffset()) || + (i1->GetType() != i2->GetType())) + { + return false; + } + } + + return (i1 == end1) && (i2 == end2); + } +}; + //------------------------------------------------------------------------ // GenTreeUseEdgeIterator: an iterator that will produce each use edge of a GenTree node in the order in which // they are used. @@ -2395,6 +2609,7 @@ class GenTreeUseEdgeIterator final void AdvanceArrOffset(); void AdvanceDynBlk(); void AdvanceStoreDynBlk(); + void AdvanceFieldList(); void AdvancePhi(); template @@ -3070,59 +3285,6 @@ struct GenTreeArgList : public GenTreeOp } }; -// Represents a list of fields constituting a struct, when it is passed as an argument. -// The first field of the struct is marked with the GTF_FIELD_LIST_HEAD flag, and -// in LIR form it is the only member of the list that is threaded into the execution -// order. -// It differs from the GenTreeArgList in a couple of ways: -// - The entire list represents a single argument. -// - It contains additional fields to provide the offset and type of the field. -// -struct GenTreeFieldList : public GenTreeArgList -{ - unsigned gtFieldOffset; - var_types gtFieldType; - - bool IsFieldListHead() const - { - return (gtFlags & GTF_FIELD_LIST_HEAD) != 0; - } - -#if DEBUGGABLE_GENTREE - GenTreeFieldList() : GenTreeArgList() - { - } -#endif - - GenTreeFieldList*& Rest() - { - assert(gtOp2 == nullptr || gtOp2->OperGet() == GT_FIELD_LIST); - return *reinterpret_cast(>Op2); - } - - GenTreeFieldList(GenTree* arg, unsigned fieldOffset, var_types fieldType, GenTreeFieldList* prevList) - : GenTreeArgList(GT_FIELD_LIST, arg, nullptr) - { - // While GT_FIELD_LIST can be in a GT_LIST, GT_FIELD_LISTs cannot be nested or have GT_LISTs. - assert(!arg->OperIsAnyList()); - gtFieldOffset = fieldOffset; - gtFieldType = fieldType; - gtType = fieldType; - if (prevList == nullptr) - { - gtFlags |= GTF_FIELD_LIST_HEAD; - - // A GT_FIELD_LIST head is always contained. Other nodes return false from IsValue() - // and should not be marked as contained. - SetContained(); - } - else - { - prevList->gtOp2 = this; - } - } -}; - // There was quite a bit of confusion in the code base about which of gtOp1 and gtOp2 was the // 'then' and 'else' clause of a colon node. Adding these accessors, while not enforcing anything, // at least *allows* the programmer to be obviously correct. @@ -6316,10 +6478,9 @@ inline bool GenTree::IsValidCallArgument() { if (OperIsList()) { - // GT_FIELD_LIST is the only list allowed. return false; } - if (OperIsFieldList()) + if (OperIs(GT_FIELD_LIST)) { #if !FEATURE_MULTIREG_ARGS && !FEATURE_PUT_STRUCT_ARG_STK diff --git a/src/coreclr/src/jit/gtlist.h b/src/coreclr/src/jit/gtlist.h index 0785084..7676c5e 100644 --- a/src/coreclr/src/jit/gtlist.h +++ b/src/coreclr/src/jit/gtlist.h @@ -229,7 +229,6 @@ GTNODE(BT , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE)) // The GTNODE(JTRUE , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) GTNODE(LIST , GenTreeArgList ,0,(GTK_BINOP|GTK_NOVALUE)) -GTNODE(FIELD_LIST , GenTreeFieldList ,0,GTK_BINOP) // List of fields of a struct, when passed as an argument //----------------------------------------------------------------------------- // Other nodes that have special structure: @@ -240,6 +239,7 @@ GTNODE(ARR_ELEM , GenTreeArrElem ,0,GTK_SPECIAL) // Multi GTNODE(ARR_INDEX , GenTreeArrIndex ,0,(GTK_BINOP|GTK_EXOP)) // Effective, bounds-checked index for one dimension of a multi-dimensional array element GTNODE(ARR_OFFSET , GenTreeArrOffs ,0,GTK_SPECIAL) // Flattened offset of multi-dimensional array element GTNODE(CALL , GenTreeCall ,0,(GTK_SPECIAL|GTK_NOCONTAIN)) +GTNODE(FIELD_LIST , GenTreeFieldList ,0,GTK_SPECIAL) // List of fields of a struct, when passed as an argument //----------------------------------------------------------------------------- // Statement operator nodes: diff --git a/src/coreclr/src/jit/gtstructs.h b/src/coreclr/src/jit/gtstructs.h index e8b1bde..3e30996 100644 --- a/src/coreclr/src/jit/gtstructs.h +++ b/src/coreclr/src/jit/gtstructs.h @@ -68,7 +68,7 @@ GTSTRUCT_1(Cast , GT_CAST) GTSTRUCT_1(Box , GT_BOX) GTSTRUCT_1(Field , GT_FIELD) GTSTRUCT_1(Call , GT_CALL) -GTSTRUCT_2_SPECIAL(ArgList , GT_LIST, GT_FIELD_LIST) +GTSTRUCT_1(ArgList , GT_LIST) GTSTRUCT_1(FieldList , GT_FIELD_LIST) GTSTRUCT_1(Colon , GT_COLON) GTSTRUCT_1(FptrVal , GT_FTN_ADDR) diff --git a/src/coreclr/src/jit/lower.cpp b/src/coreclr/src/jit/lower.cpp index 0f154b4..2a0c1c3 100644 --- a/src/coreclr/src/jit/lower.cpp +++ b/src/coreclr/src/jit/lower.cpp @@ -1064,20 +1064,25 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, fgArgTabEntry* inf } else { - GenTreeFieldList* fieldListPtr = arg->AsFieldList(); - for (unsigned index = 0; index < info->numRegs; fieldListPtr = fieldListPtr->Rest(), index++) + unsigned regIndex = 0; + for (GenTreeFieldList::Use& use : arg->AsFieldList()->Uses()) { - var_types regType = fieldListPtr->gtGetOp1()->TypeGet(); + if (regIndex >= info->numRegs) + { + break; + } + var_types regType = use.GetNode()->TypeGet(); // Account for the possibility that float fields may be passed in integer registers. - if (varTypeIsFloating(regType) && !genIsValidFloatReg(argSplit->GetRegNumByIdx(index))) + if (varTypeIsFloating(regType) && !genIsValidFloatReg(argSplit->GetRegNumByIdx(regIndex))) { regType = (regType == TYP_FLOAT) ? TYP_INT : TYP_LONG; } - argSplit->m_regType[index] = regType; - - // Clear the register assignments on the fieldList nodes, as these are contained. - fieldListPtr->gtRegNum = REG_NA; + argSplit->m_regType[regIndex] = regType; + regIndex++; } + + // Clear the register assignment on the fieldList node, as these are contained. + arg->gtRegNum = REG_NA; } } else @@ -1088,26 +1093,19 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, fgArgTabEntry* inf #if FEATURE_MULTIREG_ARGS if ((info->numRegs > 1) && (arg->OperGet() == GT_FIELD_LIST)) { - assert(arg->OperGet() == GT_FIELD_LIST); - - assert(arg->AsFieldList()->IsFieldListHead()); unsigned int regIndex = 0; - for (GenTreeFieldList* fieldListPtr = arg->AsFieldList(); fieldListPtr != nullptr; - fieldListPtr = fieldListPtr->Rest()) + for (GenTreeFieldList::Use& use : arg->AsFieldList()->Uses()) { regNumber argReg = info->getRegNum(regIndex); - GenTree* curOp = fieldListPtr->gtOp.gtOp1; + GenTree* curOp = use.GetNode(); var_types curTyp = curOp->TypeGet(); // Create a new GT_PUTARG_REG node with op1 GenTree* newOper = comp->gtNewPutArgReg(curTyp, curOp, argReg); // Splice in the new GT_PUTARG_REG node in the GT_FIELD_LIST - ReplaceArgWithPutArgOrBitcast(&fieldListPtr->gtOp.gtOp1, newOper); + ReplaceArgWithPutArgOrBitcast(&use.NodeRef(), newOper); regIndex++; - - // Initialize all the gtRegNum's since the list won't be traversed in an LIR traversal. - fieldListPtr->gtRegNum = REG_NA; } // Just return arg. The GT_FIELD_LIST is not replaced. @@ -1317,46 +1315,35 @@ void Lowering::LowerArg(GenTreeCall* call, GenTree** ppArg) #if !defined(_TARGET_64BIT_) if (varTypeIsLong(type)) { - bool isReg = (info->regNum != REG_STK); - if (isReg) + noway_assert(arg->OperIs(GT_LONG)); + GenTreeFieldList* fieldList = new (comp, GT_FIELD_LIST) GenTreeFieldList(); + fieldList->AddFieldLIR(comp, arg->AsOp()->gtGetOp1(), 0, TYP_INT); + fieldList->AddFieldLIR(comp, arg->AsOp()->gtGetOp2(), 4, TYP_INT); + GenTree* newArg = NewPutArg(call, fieldList, info, type); + + if (info->regNum != REG_STK) { - noway_assert(arg->OperGet() == GT_LONG); assert(info->numRegs == 2); - - GenTree* argLo = arg->gtGetOp1(); - GenTree* argHi = arg->gtGetOp2(); - - GenTreeFieldList* fieldList = new (comp, GT_FIELD_LIST) GenTreeFieldList(argLo, 0, TYP_INT, nullptr); - // Only the first fieldList node (GTF_FIELD_LIST_HEAD) is in the instruction sequence. - (void)new (comp, GT_FIELD_LIST) GenTreeFieldList(argHi, 4, TYP_INT, fieldList); - GenTree* putArg = NewPutArg(call, fieldList, info, type); - - BlockRange().InsertBefore(arg, putArg); - BlockRange().Remove(arg); - *ppArg = fieldList; - assert(info->GetNode() == fieldList); + // In the register argument case, NewPutArg replaces the original field list args with new + // GT_PUTARG_REG nodes, inserts them in linear order and returns the field list. So the + // only thing left to do is to insert the field list itself in linear order. + assert(newArg == fieldList); + BlockRange().InsertBefore(arg, newArg); } else { - assert(arg->OperGet() == GT_LONG); // For longs, we will replace the GT_LONG with a GT_FIELD_LIST, and put that under a PUTARG_STK. // Although the hi argument needs to be pushed first, that will be handled by the general case, // in which the fields will be reversed. assert(info->numSlots == 2); - GenTree* argLo = arg->gtGetOp1(); - GenTree* argHi = arg->gtGetOp2(); - GenTreeFieldList* fieldList = new (comp, GT_FIELD_LIST) GenTreeFieldList(argLo, 0, TYP_INT, nullptr); - // Only the first fieldList node (GTF_FIELD_LIST_HEAD) is in the instruction sequence. - (void)new (comp, GT_FIELD_LIST) GenTreeFieldList(argHi, 4, TYP_INT, fieldList); - GenTree* putArg = NewPutArg(call, fieldList, info, type); - putArg->gtRegNum = info->regNum; - - // We can't call ReplaceArgWithPutArgOrBitcast here because it presumes that we are keeping the original - // arg. - BlockRange().InsertBefore(arg, fieldList, putArg); - BlockRange().Remove(arg); - *ppArg = putArg; + newArg->gtRegNum = REG_STK; + BlockRange().InsertBefore(arg, fieldList, newArg); } + + *ppArg = newArg; + assert(info->GetNode() == newArg); + + BlockRange().Remove(arg); } else #endif // !defined(_TARGET_64BIT_) @@ -1409,37 +1396,36 @@ GenTree* Lowering::LowerFloatArg(GenTree** pArg, fgArgTabEntry* info) GenTree* arg = *pArg; if (info->regNum != REG_STK) { - if (arg->OperIsFieldList()) + if (arg->OperIs(GT_FIELD_LIST)) { - GenTreeFieldList* currListNode = arg->AsFieldList(); - regNumber currRegNumber = info->regNum; - // Transform fields that are passed as registers in place. - unsigned fieldRegCount; - for (unsigned i = 0; i < info->numRegs; i += fieldRegCount) + regNumber currRegNumber = info->regNum; + unsigned regIndex = 0; + for (GenTreeFieldList::Use& use : arg->AsFieldList()->Uses()) { - assert(currListNode != nullptr); - GenTree* node = currListNode->Current(); + if (regIndex >= info->numRegs) + { + break; + } + GenTree* node = use.GetNode(); if (varTypeIsFloating(node)) { GenTree* intNode = LowerFloatArgReg(node, currRegNumber); assert(intNode != nullptr); - ReplaceArgWithPutArgOrBitcast(currListNode->pCurrent(), intNode); - currListNode->ChangeType(intNode->TypeGet()); + ReplaceArgWithPutArgOrBitcast(&use.NodeRef(), intNode); } if (node->TypeGet() == TYP_DOUBLE) { currRegNumber = REG_NEXT(REG_NEXT(currRegNumber)); - fieldRegCount = 2; + regIndex += 2; } else { currRegNumber = REG_NEXT(currRegNumber); - fieldRegCount = 1; + regIndex += 1; } - currListNode = currListNode->Rest(); } // List fields were replaced in place. return arg; @@ -5395,11 +5381,10 @@ void Lowering::CheckCallArg(GenTree* arg) { GenTreeFieldList* list = arg->AsFieldList(); assert(list->isContained()); - assert(list->IsFieldListHead()); - for (; list != nullptr; list = list->Rest()) + for (GenTreeFieldList::Use& use : list->Uses()) { - assert(list->Current()->OperIsPutArg()); + assert(use.GetNode()->OperIsPutArg()); } } break; diff --git a/src/coreclr/src/jit/lowerxarch.cpp b/src/coreclr/src/jit/lowerxarch.cpp index e87d582..d569028 100644 --- a/src/coreclr/src/jit/lowerxarch.cpp +++ b/src/coreclr/src/jit/lowerxarch.cpp @@ -408,86 +408,28 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) // void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) { -#ifdef _TARGET_X86_ - if (putArgStk->gtOp1->gtOper == GT_FIELD_LIST) + GenTree* src = putArgStk->gtGetOp1(); + + if (src->OperIs(GT_FIELD_LIST)) { +#ifdef _TARGET_X86_ putArgStk->gtPutArgStkKind = GenTreePutArgStk::Kind::Invalid; - GenTreeFieldList* fieldList = putArgStk->gtOp1->AsFieldList(); + GenTreeFieldList* fieldList = src->AsFieldList(); // The code generator will push these fields in reverse order by offset. Reorder the list here s.t. the order // of uses is visible to LSRA. - unsigned fieldCount = 0; - GenTreeFieldList* head = nullptr; - for (GenTreeFieldList *current = fieldList, *next; current != nullptr; current = next) - { - next = current->Rest(); - - // First, insert the field node into the sorted list. - GenTreeFieldList* prev = nullptr; - for (GenTreeFieldList* cursor = head;; cursor = cursor->Rest()) - { - // If the offset of the current list node is greater than the offset of the cursor or if we have - // reached the end of the list, insert the current node before the cursor and terminate. - if ((cursor == nullptr) || (current->gtFieldOffset > cursor->gtFieldOffset)) - { - if (prev == nullptr) - { - assert(cursor == head); - head = current; - } - else - { - prev->Rest() = current; - } - - current->Rest() = cursor; - break; - } - } - - fieldCount++; - } - - // In theory, the upper bound for the size of a field list is 8: these constructs only appear when passing the - // collection of lclVars that represent the fields of a promoted struct lclVar, and we do not promote struct - // lclVars with more than 4 fields. If each of these lclVars is of type long, decomposition will split the - // corresponding field list nodes in two, giving an upper bound of 8. - // - // The reason that this is important is that the algorithm we use above to sort the field list is O(N^2): if - // the maximum size of a field list grows significantly, we will need to reevaluate it. - assert(fieldCount <= 8); - - // The sort above may have changed which node is at the head of the list. Update the PUTARG_STK node if - // necessary. - if (head != fieldList) - { - head->gtFlags |= GTF_FIELD_LIST_HEAD; - head->SetContained(); - - fieldList->ClearContained(); - fieldList->gtFlags &= ~GTF_FIELD_LIST_HEAD; - -#ifdef DEBUG - head->gtSeqNum = fieldList->gtSeqNum; -#endif // DEBUG - - BlockRange().InsertAfter(fieldList, head); - BlockRange().Remove(fieldList); - - fieldList = head; - putArgStk->gtOp1 = fieldList; - putArgStk->gtType = fieldList->gtType; - } + assert(fieldList->Uses().IsSorted()); + fieldList->Uses().Reverse(); // Now that the fields have been sorted, the kind of code we will generate. bool allFieldsAreSlots = true; unsigned prevOffset = putArgStk->getArgSize(); - for (GenTreeFieldList* current = fieldList; current != nullptr; current = current->Rest()) + for (GenTreeFieldList::Use& use : fieldList->Uses()) { - GenTree* const fieldNode = current->Current(); + GenTree* const fieldNode = use.GetNode(); const var_types fieldType = fieldNode->TypeGet(); - const unsigned fieldOffset = current->gtFieldOffset; + const unsigned fieldOffset = use.GetOffset(); assert(fieldType != TYP_LONG); // We can treat as a slot any field that is stored at a slot boundary, where the previous @@ -547,11 +489,9 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) { putArgStk->gtPutArgStkKind = GenTreePutArgStk::Kind::Push; } +#endif // _TARGET_X86_ return; } -#endif // _TARGET_X86_ - - GenTree* src = putArgStk->gtOp1; #ifdef FEATURE_PUT_STRUCT_ARG_STK if (src->TypeGet() != TYP_STRUCT) diff --git a/src/coreclr/src/jit/lsraarmarch.cpp b/src/coreclr/src/jit/lsraarmarch.cpp index 9f93979..2adfd79 100644 --- a/src/coreclr/src/jit/lsraarmarch.cpp +++ b/src/coreclr/src/jit/lsraarmarch.cpp @@ -252,23 +252,23 @@ int LinearScan::BuildCall(GenTreeCall* call) assert(argNode->isContained()); // There could be up to 2-4 PUTARG_REGs in the list (3 or 4 can only occur for HFAs) - for (GenTreeFieldList* entry = argNode->AsFieldList(); entry != nullptr; entry = entry->Rest()) + for (GenTreeFieldList::Use& use : argNode->AsFieldList()->Uses()) { #ifdef DEBUG - assert(entry->Current()->OperIs(GT_PUTARG_REG)); - assert(entry->Current()->gtRegNum == argReg); + assert(use.GetNode()->OperIs(GT_PUTARG_REG)); + assert(use.GetNode()->gtRegNum == argReg); // Update argReg for the next putarg_reg (if any) argReg = genRegArgNext(argReg); #if defined(_TARGET_ARM_) // A double register is modelled as an even-numbered single one - if (entry->Current()->TypeGet() == TYP_DOUBLE) + if (use.GetNode()->TypeGet() == TYP_DOUBLE) { argReg = genRegArgNext(argReg); } #endif // _TARGET_ARM_ #endif - BuildUse(entry->Current(), genRegMask(entry->Current()->gtRegNum)); + BuildUse(use.GetNode(), genRegMask(use.GetNode()->gtRegNum)); srcCount++; } } @@ -398,9 +398,9 @@ int LinearScan::BuildPutArgStk(GenTreePutArgStk* argNode) { assert(putArgChild->isContained()); // We consume all of the items in the GT_FIELD_LIST - for (GenTreeFieldList* current = putArgChild->AsFieldList(); current != nullptr; current = current->Rest()) + for (GenTreeFieldList::Use& use : putArgChild->AsFieldList()->Uses()) { - BuildUse(current->Current()); + BuildUse(use.GetNode()); srcCount++; } } @@ -490,10 +490,9 @@ int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode) // To avoid redundant moves, have the argument operand computed in the // register in which the argument is passed to the call. - for (GenTreeFieldList* fieldListPtr = putArgChild->AsFieldList(); fieldListPtr != nullptr; - fieldListPtr = fieldListPtr->Rest()) + for (GenTreeFieldList::Use& use : putArgChild->AsFieldList()->Uses()) { - GenTree* node = fieldListPtr->gtGetOp1(); + GenTree* node = use.GetNode(); assert(!node->isContained()); // The only multi-reg nodes we should see are OperIsMultiRegOp() unsigned currentRegCount; diff --git a/src/coreclr/src/jit/lsrabuild.cpp b/src/coreclr/src/jit/lsrabuild.cpp index 0009b24..26bf1c0 100644 --- a/src/coreclr/src/jit/lsrabuild.cpp +++ b/src/coreclr/src/jit/lsrabuild.cpp @@ -1546,9 +1546,6 @@ void LinearScan::buildRefPositionsForNode(GenTree* tree, BasicBlock* block, Lsra assert(tree->OperGet() != GT_LIST); assert(tree->OperGet() != GT_CLS_VAR); - // The LIR traversal visits only the first node in a GT_FIELD_LIST. - assert((tree->OperGet() != GT_FIELD_LIST) || tree->AsFieldList()->IsFieldListHead()); - // The set of internal temporary registers used by this node are stored in the // gtRsvdRegs register mask. Clear it out. tree->gtRsvdRegs = RBM_NONE; diff --git a/src/coreclr/src/jit/lsraxarch.cpp b/src/coreclr/src/jit/lsraxarch.cpp index 082cd27..bf387fb 100644 --- a/src/coreclr/src/jit/lsraxarch.cpp +++ b/src/coreclr/src/jit/lsraxarch.cpp @@ -1096,10 +1096,10 @@ int LinearScan::BuildCall(GenTreeCall* call) } else if (argNode->OperGet() == GT_FIELD_LIST) { - for (GenTreeFieldList* entry = argNode->AsFieldList(); entry != nullptr; entry = entry->Rest()) + for (GenTreeFieldList::Use& use : argNode->AsFieldList()->Uses()) { - assert(entry->Current()->OperIsPutArgReg()); - HandleFloatVarArgs(call, entry->Current(), &callHasFloatRegArgs); + assert(use.GetNode()->OperIsPutArgReg()); + HandleFloatVarArgs(call, use.GetNode(), &callHasFloatRegArgs); } } } @@ -1127,11 +1127,11 @@ int LinearScan::BuildCall(GenTreeCall* call) #ifdef UNIX_AMD64_ABI else if (argNode->OperGet() == GT_FIELD_LIST) { - for (GenTreeFieldList* entry = argNode->AsFieldList(); entry != nullptr; entry = entry->Rest()) + for (GenTreeFieldList::Use& use : argNode->AsFieldList()->Uses()) { - assert(entry->Current()->OperIsPutArgReg()); + assert(use.GetNode()->OperIsPutArgReg()); srcCount++; - BuildUse(entry->Current(), genRegMask(entry->Current()->gtRegNum)); + BuildUse(use.GetNode(), genRegMask(use.GetNode()->gtRegNum)); } } #endif // UNIX_AMD64_ABI @@ -1167,13 +1167,12 @@ int LinearScan::BuildCall(GenTreeCall* call) assert(argNode->isContained()); assert(varTypeIsStruct(argNode) || curArgTabEntry->isStruct); - int i = 0; - for (GenTreeFieldList* entry = argNode->AsFieldList(); entry != nullptr; entry = entry->Rest()) + unsigned regIndex = 0; + for (GenTreeFieldList::Use& use : argNode->AsFieldList()->Uses()) { - const regNumber argReg = (i == 0) ? curArgTabEntry->regNum : curArgTabEntry->GetOtherRegNum(); - assert(entry->Current()->gtRegNum == argReg); - assert(i < 2); - i++; + const regNumber argReg = curArgTabEntry->getRegNum(regIndex); + assert(use.GetNode()->gtRegNum == argReg); + regIndex++; } } else @@ -1483,11 +1482,11 @@ int LinearScan::BuildPutArgStk(GenTreePutArgStk* putArgStk) unsigned prevOffset = putArgStk->getArgSize(); // We need to iterate over the fields twice; once to determine the need for internal temps, // and once to actually build the uses. - for (GenTreeFieldList* current = putArgStk->gtOp1->AsFieldList(); current != nullptr; current = current->Rest()) + for (GenTreeFieldList::Use& use : putArgStk->gtOp1->AsFieldList()->Uses()) { - GenTree* const fieldNode = current->Current(); + GenTree* const fieldNode = use.GetNode(); const var_types fieldType = fieldNode->TypeGet(); - const unsigned fieldOffset = current->gtFieldOffset; + const unsigned fieldOffset = use.GetOffset(); #ifdef _TARGET_X86_ assert(fieldType != TYP_LONG); @@ -1497,7 +1496,7 @@ int LinearScan::BuildPutArgStk(GenTreePutArgStk* putArgStk) // Note that we need to check the GT_FIELD_LIST type, not 'fieldType'. This is because the // GT_FIELD_LIST will be TYP_SIMD12 whereas the fieldType might be TYP_SIMD16 for lclVar, where // we "round up" to 16. - if ((current->gtFieldType == TYP_SIMD12) && (simdTemp == nullptr)) + if ((use.GetType() == TYP_SIMD12) && (simdTemp == nullptr)) { simdTemp = buildInternalFloatRegisterDefForNode(putArgStk); } @@ -1527,9 +1526,9 @@ int LinearScan::BuildPutArgStk(GenTreePutArgStk* putArgStk) prevOffset = fieldOffset; } - for (GenTreeFieldList* current = putArgStk->gtOp1->AsFieldList(); current != nullptr; current = current->Rest()) + for (GenTreeFieldList::Use& use : putArgStk->gtOp1->AsFieldList()->Uses()) { - GenTree* const fieldNode = current->Current(); + GenTree* const fieldNode = use.GetNode(); if (!fieldNode->isContained()) { BuildUse(fieldNode); diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index c6f5210..8972152 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -3398,7 +3398,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) if (!passUsingFloatRegs && ((intArgRegNum + size) > MAX_REG_ARG)) { // This indicates a partial enregistration of a struct type - assert((isStructArg) || argx->OperIsFieldList() || argx->OperIsCopyBlkOp() || + assert((isStructArg) || argx->OperIs(GT_FIELD_LIST) || argx->OperIsCopyBlkOp() || (argx->gtOper == GT_COMMA && (argx->gtFlags & GTF_ASG))); unsigned numRegsPartial = MAX_REG_ARG - intArgRegNum; assert((unsigned char)numRegsPartial == numRegsPartial); @@ -3927,16 +3927,13 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) noway_assert(!reMorphing); #ifdef _TARGET_X86_ - // Build the mkrefany as a GT_FIELD_LIST - GenTreeFieldList* fieldList = new (this, GT_FIELD_LIST) - GenTreeFieldList(argx->gtOp.gtOp1, OFFSETOF__CORINFO_TypedReference__dataPtr, TYP_BYREF, nullptr); - (void)new (this, GT_FIELD_LIST) - GenTreeFieldList(argx->gtOp.gtOp2, OFFSETOF__CORINFO_TypedReference__type, TYP_I_IMPL, fieldList); - fgArgTabEntry* fp = Compiler::gtArgEntryByNode(call, argx); + GenTreeFieldList* fieldList = new (this, GT_FIELD_LIST) GenTreeFieldList(); + fieldList->AddField(this, argx->AsOp()->gtGetOp1(), OFFSETOF__CORINFO_TypedReference__dataPtr, TYP_BYREF); + fieldList->AddField(this, argx->AsOp()->gtGetOp2(), OFFSETOF__CORINFO_TypedReference__type, TYP_I_IMPL); + fgArgTabEntry* fp = gtArgEntryByNode(call, argx); args->SetNode(fieldList); assert(fp->GetNode() == fieldList); - #else // !_TARGET_X86_ // Get a new temp @@ -3997,29 +3994,32 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) { // Make a GT_FIELD_LIST of the field lclVars. GenTreeLclVarCommon* lcl = lclNode->AsLclVarCommon(); - LclVarDsc* varDsc = &(lvaTable[lcl->GetLclNum()]); - GenTreeFieldList* fieldList = nullptr; + LclVarDsc* varDsc = lvaGetDesc(lcl); + GenTreeFieldList* fieldList = new (this, GT_FIELD_LIST) GenTreeFieldList(); + + fgArgTabEntry* fp = gtArgEntryByNode(call, argx); + args->SetNode(fieldList); + assert(fp->GetNode() == fieldList); + for (unsigned fieldLclNum = varDsc->lvFieldLclStart; fieldLclNum < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++fieldLclNum) { - LclVarDsc* fieldVarDsc = &lvaTable[fieldLclNum]; - if (fieldList == nullptr) + LclVarDsc* fieldVarDsc = lvaGetDesc(fieldLclNum); + GenTree* fieldLcl; + + if (fieldLclNum == varDsc->lvFieldLclStart) { lcl->SetLclNum(fieldLclNum); lcl->ChangeOper(GT_LCL_VAR); - lcl->gtType = fieldVarDsc->lvType; - fieldList = new (this, GT_FIELD_LIST) - GenTreeFieldList(lcl, fieldVarDsc->lvFldOffset, fieldVarDsc->lvType, nullptr); - fgArgTabEntry* fp = Compiler::gtArgEntryByNode(call, argx); - args->SetNode(fieldList); - assert(fp->GetNode() == fieldList); + lcl->gtType = fieldVarDsc->TypeGet(); + fieldLcl = lcl; } else { - GenTree* fieldLcl = gtNewLclvNode(fieldLclNum, fieldVarDsc->lvType); - fieldList = new (this, GT_FIELD_LIST) - GenTreeFieldList(fieldLcl, fieldVarDsc->lvFldOffset, fieldVarDsc->lvType, fieldList); + fieldLcl = gtNewLclvNode(fieldLclNum, fieldVarDsc->TypeGet()); } + + fieldList->AddField(this, fieldLcl, fieldVarDsc->lvFldOffset, fieldVarDsc->TypeGet()); } } } @@ -4566,15 +4566,14 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry { // We can use the struct promoted field as the two arguments - GenTree* loLclVar = gtNewLclvNode(loVarNum, loType); - GenTree* hiLclVar = gtNewLclvNode(hiVarNum, hiType); - // Create a new tree for 'arg' // replace the existing LDOBJ(ADDR(LCLVAR)) // with a FIELD_LIST(LCLVAR-LO, FIELD_LIST(LCLVAR-HI, nullptr)) // - newArg = new (this, GT_FIELD_LIST) GenTreeFieldList(loLclVar, 0, loType, nullptr); - (void)new (this, GT_FIELD_LIST) GenTreeFieldList(hiLclVar, TARGET_POINTER_SIZE, hiType, newArg); + + newArg = new (this, GT_FIELD_LIST) GenTreeFieldList(); + newArg->AddField(this, gtNewLclvNode(loVarNum, loType), 0, loType); + newArg->AddField(this, gtNewLclvNode(hiVarNum, hiType), TARGET_POINTER_SIZE, hiType); } } } @@ -4705,20 +4704,15 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry // Create a new tree for 'arg' // replace the existing LDOBJ(ADDR(LCLVAR)) - // with a FIELD_LIST(LCLFLD-LO, FIELD_LIST(LCLFLD-HI, nullptr) ...) + // with a FIELD_LIST(LCLFLD-LO, LCLFLD-HI) // - unsigned offset = baseOffset; - GenTreeFieldList* listEntry = nullptr; + unsigned offset = baseOffset; + newArg = new (this, GT_FIELD_LIST) GenTreeFieldList(); for (unsigned inx = 0; inx < elemCount; inx++) { - elemSize = genTypeSize(type[inx]); GenTree* nextLclFld = gtNewLclFldNode(varNum, type[inx], offset); - listEntry = new (this, GT_FIELD_LIST) GenTreeFieldList(nextLclFld, offset, type[inx], listEntry); - if (newArg == nullptr) - { - newArg = listEntry; - } - offset += elemSize; + newArg->AddField(this, nextLclFld, offset, type[inx]); + offset += genTypeSize(type[inx]); } } // Are we passing a GT_OBJ struct? @@ -4747,11 +4741,10 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry // with a FIELD_LIST(IND(EXPR), FIELD_LIST(IND(EXPR+8), nullptr) ...) // - unsigned offset = 0; - GenTreeFieldList* listEntry = nullptr; + newArg = new (this, GT_FIELD_LIST) GenTreeFieldList(); + unsigned offset = 0; for (unsigned inx = 0; inx < elemCount; inx++) { - elemSize = genTypeSize(type[inx]); GenTree* curAddr = baseAddr; if (offset != 0) { @@ -4768,12 +4761,8 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry // For safety all GT_IND should have at least GT_GLOB_REF set. curItem->gtFlags |= GTF_GLOB_REF; - listEntry = new (this, GT_FIELD_LIST) GenTreeFieldList(curItem, offset, type[inx], listEntry); - if (newArg == nullptr) - { - newArg = listEntry; - } - offset += elemSize; + newArg->AddField(this, curItem, offset, type[inx]); + offset += genTypeSize(type[inx]); } } } @@ -4788,28 +4777,6 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry #endif noway_assert(newArg != nullptr); - noway_assert(newArg->OperIsFieldList()); - - // We need to propagate any GTF_ALL_EFFECT flags from the end of the list back to the beginning. - // This is verified in fgDebugCheckFlags(). - - ArrayStack stack(getAllocator(CMK_ArrayStack)); - GenTree* tree; - for (tree = newArg; (tree->gtGetOp2() != nullptr) && tree->gtGetOp2()->OperIsFieldList(); tree = tree->gtGetOp2()) - { - stack.Push(tree); - } - - unsigned propFlags = (tree->gtOp.gtOp1->gtFlags & GTF_ALL_EFFECT); - tree->gtFlags |= propFlags; - - while (!stack.Empty()) - { - tree = stack.Pop(); - propFlags |= (tree->gtOp.gtOp1->gtFlags & GTF_ALL_EFFECT); - propFlags |= (tree->gtGetOp2()->gtFlags & GTF_ALL_EFFECT); - tree->gtFlags |= propFlags; - } #ifdef DEBUG if (verbose) @@ -4837,28 +4804,20 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry // GenTreeFieldList* Compiler::fgMorphLclArgToFieldlist(GenTreeLclVarCommon* lcl) { - LclVarDsc* varDsc = &(lvaTable[lcl->GetLclNum()]); - assert(varDsc->lvPromoted == true); - - unsigned fieldCount = varDsc->lvFieldCnt; - GenTreeFieldList* listEntry = nullptr; - GenTreeFieldList* newArg = nullptr; - unsigned fieldLclNum = varDsc->lvFieldLclStart; + LclVarDsc* varDsc = lvaGetDesc(lcl); + assert(varDsc->lvPromoted); + unsigned fieldCount = varDsc->lvFieldCnt; + unsigned fieldLclNum = varDsc->lvFieldLclStart; - // We can use the struct promoted field as arguments + GenTreeFieldList* fieldList = new (this, GT_FIELD_LIST) GenTreeFieldList(); for (unsigned i = 0; i < fieldCount; i++) { - LclVarDsc* fieldVarDsc = &lvaTable[fieldLclNum]; - GenTree* lclVar = gtNewLclvNode(fieldLclNum, fieldVarDsc->lvType); - listEntry = new (this, GT_FIELD_LIST) - GenTreeFieldList(lclVar, fieldVarDsc->lvFldOffset, fieldVarDsc->lvType, listEntry); - if (newArg == nullptr) - { - newArg = listEntry; - } + LclVarDsc* fieldVarDsc = lvaGetDesc(fieldLclNum); + GenTree* lclVar = gtNewLclvNode(fieldLclNum, fieldVarDsc->TypeGet()); + fieldList->AddField(this, lclVar, fieldVarDsc->lvFldOffset, fieldVarDsc->TypeGet()); fieldLclNum++; } - return newArg; + return fieldList; } //------------------------------------------------------------------------ @@ -14660,6 +14619,15 @@ GenTree* Compiler::fgMorphTree(GenTree* tree, MorphAddrContext* mac) } break; + case GT_FIELD_LIST: + tree->gtFlags &= ~GTF_ALL_EFFECT; + for (GenTreeFieldList::Use& use : tree->AsFieldList()->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); diff --git a/src/coreclr/src/jit/rationalize.cpp b/src/coreclr/src/jit/rationalize.cpp index 1494f8f..36f64cb 100644 --- a/src/coreclr/src/jit/rationalize.cpp +++ b/src/coreclr/src/jit/rationalize.cpp @@ -562,9 +562,8 @@ 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 GT_HWIntrinsic nodes will in fact be visited. - for (GenTree* prev = node->gtPrev; prev != nullptr && prev->OperIsAnyList() && !(prev->OperIsFieldListHead()); - prev = node->gtPrev) + // NOTE: GT_LIST nodes used by GT_HWIntrinsic nodes will in fact be visited. + for (GenTree* prev = node->gtPrev; (prev != nullptr) && prev->OperIs(GT_LIST); prev = node->gtPrev) { prev->gtFlags &= ~GTF_REVERSE_OPS; BlockRange().Remove(prev); @@ -574,13 +573,10 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge node->gtFlags &= ~GTF_REVERSE_OPS; // In addition, remove the current node if it is a GT_LIST node that is not an aggregate. - if (node->OperIsAnyList()) + if (node->OperIs(GT_LIST)) { GenTreeArgList* list = node->AsArgList(); - if (!list->OperIsFieldListHead()) - { - BlockRange().Remove(list); - } + BlockRange().Remove(list); return Compiler::WALK_CONTINUE; } -- 2.7.4