GenTreePtr gtNewIndexRef(var_types typ, GenTreePtr arrayOp, GenTreePtr indexOp);
GenTreeArgList* gtNewArgList(GenTreePtr op);
-
GenTreeArgList* gtNewArgList(GenTreePtr op1, GenTreePtr op2);
GenTreeArgList* gtNewArgList(GenTreePtr op1, GenTreePtr op2, GenTreePtr op3);
+ GenTreeArgList* gtNewAggregate(GenTree* element);
+
static fgArgTabEntryPtr gtArgEntryByArgNum(GenTreePtr call, unsigned argNum);
static fgArgTabEntryPtr gtArgEntryByNode(GenTreePtr call, GenTreePtr node);
fgArgTabEntryPtr gtArgEntryByLateArgIndex(GenTreePtr call, unsigned lateArgInx);
{
// If we are sequencing a node that does not appear in LIR,
// do not add it to the list.
- if (isLIR && ((tree->OperGet() == GT_LIST) || tree->OperGet() == GT_ARGPLACE))
+ if (isLIR && (((tree->OperGet() == GT_LIST) && !tree->AsArgList()->IsAggregate()) || tree->OperGet() == GT_ARGPLACE))
{
return;
}
}
break;
+ case GT_LIST:
+ {
+ GenTreeArgList* list = tree->AsArgList();
+ if (list->IsAggregate())
+ {
+ for (; list != nullptr; list = list->Rest())
+ {
+ result = fgWalkTreePostRec<computeStack>(&list->gtOp1, fgWalkData);
+ if (result == WALK_ABORT)
+ {
+ return result;
+ }
+ }
+ break;
+ }
+
+ // GT_LIST nodes that do not represent aggregate arguments intentionally fall through to the
+ // default node processing below.
+ __fallthrough;
+ }
+
default:
if (kind & GTK_SMPOP)
{
return nullptr;
}
-bool GenTree::TryGetUse(GenTree* def, GenTree*** use, bool expandMultiRegArgs)
+bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
{
- for (GenTree** useEdge : UseEdges(expandMultiRegArgs))
+ for (GenTree** useEdge : UseEdges())
{
if (*useEdge == def)
{
return new (this, GT_LIST) GenTreeArgList(arg1, gtNewArgList(arg2));
}
+//------------------------------------------------------------------------
+// Compiler::gtNewAggregate:
+// Creates a new aggregate argument node. These nodes are used to
+// represent arguments that are composed of multiple values (e.g.
+// the lclVars that represent the fields of a promoted struct).
+//
+// Note that aggregate arguments are currently represented by GT_LIST
+// nodes that are marked with the GTF_LIST_AGGREGATE flag. This
+// representation may be changed in the future to instead use its own
+// node type (e.g. GT_AGGREGATE).
+//
+// Arguments:
+// firstElement - The first element in the aggregate's list of values.
+//
+// Returns:
+// The newly-created aggregate node.
+GenTreeArgList* Compiler::gtNewAggregate(GenTree* firstElement)
+{
+ GenTreeArgList* agg = gtNewArgList(firstElement);
+ agg->gtFlags |= GTF_LIST_AGGREGATE;
+ return agg;
+}
+
/*****************************************************************************
*
* Create a list out of the three values.
: m_node(nullptr)
, m_edge(nullptr)
, m_argList(nullptr)
- , m_multiRegArg(nullptr)
- , m_expandMultiRegArgs(false)
, m_state(-1)
{
}
-GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node, bool expandMultiRegArgs)
+GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node)
: m_node(node)
, m_edge(nullptr)
, m_argList(nullptr)
- , m_multiRegArg(nullptr)
- , m_expandMultiRegArgs(expandMultiRegArgs)
, m_state(0)
{
assert(m_node != nullptr);
}
// Call, phi, and SIMD nodes are handled by MoveNext{Call,Phi,SIMD}UseEdge, repsectively.
- //
- // If FEATURE_MULTIREG_ARGS is enabled, so PUTARG_STK nodes also have special handling.
case GT_CALL:
case GT_PHI:
#ifdef FEATURE_SIMD
case GT_SIMD:
#endif
-#if FEATURE_MULTIREG_ARGS
- case GT_PUTARG_STK:
-#endif
-
break;
case GT_INITBLK:
}
break;
+ case GT_LIST:
+ if (m_node->AsArgList()->IsAggregate())
+ {
+ // List nodes that represent aggregates are handled by MoveNextAggregateUseEdge.
+ break;
+ }
+ __fallthrough;
+
default:
if (m_node->OperIsConst() || m_node->OperIsLeaf())
{
//
void GenTreeUseEdgeIterator::MoveToNextCallUseEdge()
{
+ enum
+ {
+ CALL_INSTANCE = 0,
+ CALL_ARGS = 1,
+ CALL_LATE_ARGS = 2,
+ CALL_CONTROL_EXPR = 3,
+ CALL_COOKIE = 4,
+ CALL_ADDRESS = 5,
+ CALL_TERMINAL = 6,
+ };
+
GenTreeCall* call = m_node->AsCall();
for (;;)
{
switch (m_state)
{
- case 0:
- m_state = 1;
+ case CALL_INSTANCE:
+ m_state = CALL_ARGS;
m_argList = call->gtCallArgs;
if (call->gtCallObjp != nullptr)
}
break;
- case 1:
- case 3:
+ case CALL_ARGS:
+ case CALL_LATE_ARGS:
if (m_argList == nullptr)
{
- m_state += 2;
+ m_state++;
- if (m_state == 3)
+ if (m_state == CALL_LATE_ARGS)
{
m_argList = call->gtCallLateArgs;
}
else
{
GenTreeArgList* argNode = m_argList->AsArgList();
- if (m_expandMultiRegArgs && argNode->gtOp1->OperGet() == GT_LIST)
- {
- m_state += 1;
- m_multiRegArg = argNode->gtOp1;
- }
- else
- {
- m_edge = &argNode->gtOp1;
- m_argList = argNode->Rest();
- return;
- }
- }
- break;
-
- case 2:
- case 4:
- if (m_multiRegArg == nullptr)
- {
- m_state -= 1;
- m_argList = m_argList->AsArgList()->Rest();
- }
- else
- {
- GenTreeArgList* regNode = m_multiRegArg->AsArgList();
- m_edge = ®Node->gtOp1;
- m_multiRegArg = regNode->Rest();
+ m_edge = &argNode->gtOp1;
+ m_argList = argNode->Rest();
return;
}
break;
- case 5:
- m_state = call->gtCallType == CT_INDIRECT ? 6 : 8;
+ case CALL_CONTROL_EXPR:
+ m_state = call->gtCallType == CT_INDIRECT ? CALL_COOKIE : CALL_TERMINAL;
if (call->gtControlExpr != nullptr)
{
}
break;
- case 6:
+ case 4:
assert(call->gtCallType == CT_INDIRECT);
- m_state = 7;
+ m_state = CALL_ADDRESS;
if (call->gtCallCookie != nullptr)
{
}
break;
- case 7:
+ case 5:
assert(call->gtCallType == CT_INDIRECT);
- m_state = 8;
+ m_state = CALL_TERMINAL;
if (call->gtCallAddr != nullptr)
{
m_edge = &call->gtCallAddr;
}
#endif // FEATURE_SIMD
-#if FEATURE_MULTIREG_ARGS
-void GenTreeUseEdgeIterator::MoveToNextPutArgStkUseEdge()
+void GenTreeUseEdgeIterator::MoveToNextAggregateUseEdge()
{
- assert(m_node->OperGet() == GT_PUTARG_STK);
-
- GenTreeUnOp* putArg = m_node->AsUnOp();
+ assert(m_node->OperGet() == GT_LIST);
+ assert(m_node->AsArgList()->IsAggregate());
for (;;)
{
switch (m_state)
{
case 0:
- if ((putArg->gtOp1->OperGet() != GT_LIST) || !m_expandMultiRegArgs)
- {
- m_state = 2;
- m_edge = &putArg->gtOp1;
- return;
- }
-
m_state = 1;
- m_argList = putArg->gtOp1;
+ m_argList = m_node;
break;
case 1:
}
else
{
- GenTreeArgList* argNode = m_argList->AsArgList();
- m_edge = &argNode->gtOp1;
- m_argList = argNode->Rest();
+ GenTreeArgList* aggNode = m_argList->AsArgList();
+ m_edge = &aggNode->gtOp1;
+ m_argList = aggNode->Rest();
return;
}
break;
}
}
}
-#endif // FEATURE_MULTIREG_ARGS
//------------------------------------------------------------------------
// GenTreeUseEdgeIterator::operator++:
MoveToNextSIMDUseEdge();
}
#endif
-#if FEATURE_MULTIREG_ARGS
- else if (op == GT_PUTARG_STK)
+ else if ((op == GT_LIST) && (m_node->AsArgList()->IsAggregate()))
{
- MoveToNextPutArgStkUseEdge();
+ MoveToNextAggregateUseEdge();
}
-#endif
else
{
m_edge = GetNextUseEdge();
return *this;
}
-GenTreeUseEdgeIterator GenTree::UseEdgesBegin(bool expandMultiRegArgs)
+GenTreeUseEdgeIterator GenTree::UseEdgesBegin()
{
- return GenTreeUseEdgeIterator(this, expandMultiRegArgs);
+ return GenTreeUseEdgeIterator(this);
}
GenTreeUseEdgeIterator GenTree::UseEdgesEnd()
return GenTreeUseEdgeIterator();
}
-IteratorPair<GenTreeUseEdgeIterator> GenTree::UseEdges(bool expandMultiRegArgs)
+IteratorPair<GenTreeUseEdgeIterator> GenTree::UseEdges()
{
- return MakeIteratorPair(UseEdgesBegin(expandMultiRegArgs), UseEdgesEnd());
+ return MakeIteratorPair(UseEdgesBegin(), UseEdgesEnd());
}
-GenTreeOperandIterator GenTree::OperandsBegin(bool expandMultiRegArgs)
+GenTreeOperandIterator GenTree::OperandsBegin()
{
- return GenTreeOperandIterator(this, expandMultiRegArgs);
+ return GenTreeOperandIterator(this);
}
GenTreeOperandIterator GenTree::OperandsEnd()
return GenTreeOperandIterator();
}
-IteratorPair<GenTreeOperandIterator> GenTree::Operands(bool expandMultiRegArgs)
+IteratorPair<GenTreeOperandIterator> GenTree::Operands()
{
- return MakeIteratorPair(OperandsBegin(expandMultiRegArgs), OperandsEnd());
+ return MakeIteratorPair(OperandsBegin(), OperandsEnd());
}
bool GenTree::Precedes(GenTree* other)
// Visit operands
IndentInfo operandArc = IIArcTop;
int callArgNumber = 0;
- const bool expandMultiRegArgs = false;
- for (GenTree* operand : node->Operands(expandMultiRegArgs))
+ for (GenTree* operand : node->Operands())
{
if (operand->IsArgPlaceHolderNode() || !operand->IsValue())
{
#endif // FEATURE_SIMD
//---------------------------------------------------------------------------------------
+// GenTreeArgList::Prepend:
+// Prepends an element to a GT_LIST.
+//
+// Arguments:
+// compiler - The compiler context.
+// element - The element to prepend.
+//
+// Returns:
+// The new head of the list.
+GenTreeArgList* GenTreeArgList::Prepend(Compiler* compiler, GenTree* element)
+{
+ GenTreeArgList* head = compiler->gtNewListNode(element, this);
+ head->gtFlags |= (gtFlags & GTF_LIST_AGGREGATE);
+ gtFlags &= ~GTF_LIST_AGGREGATE;
+ return head;
+}
+
+//---------------------------------------------------------------------------------------
// InitializeStructReturnType:
// Initialize the Return Type Descriptor for a method that returns a struct type
//
#define GTF_ARRLEN_ARR_IDX 0x80000000 // GT_ARR_LENGTH -- Length which feeds into an array index expression
+#define GTF_LIST_AGGREGATE 0x80000000 // GT_LIST -- Indicates that this list should be treated as an
+ // anonymous aggregate value (e.g. a multi-value argument).
+
//----------------------------------------------------------------
#define GTF_STMT_CMPADD 0x80000000 // GT_STMT -- added by compiler
return gtType != TYP_VOID;
}
+ if (gtOper == GT_LIST)
+ {
+ return (gtFlags & GTF_LIST_AGGREGATE) != 0;
+ }
+
return true;
}
return IsNothingNode();
case GT_ARGPLACE:
- case GT_LIST:
- // ARGPLACE and LIST nodes may not be present in a block's LIR sequence, but they may
+ // ARGPLACE nodes may not be present in a block's LIR sequence, but they may
// be present as children of an LIR node.
return (gtNext == nullptr) && (gtPrev == nullptr);
+ case GT_LIST:
+ // LIST nodes may only be present in an LIR sequence if they represent aggregates.
+ // They are always allowed, however, as children of an LIR node.
+ return ((gtFlags & GTF_LIST_AGGREGATE) != 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
return OperIsSIMD(gtOper);
}
+ bool OperIsAggregate()
+ {
+ return (gtOper == GT_LIST) && ((gtFlags & GTF_LIST_AGGREGATE) != 0);
+ }
+
// Requires that "op" is an op= operator. Returns
// the corresponding "op".
static genTreeOps OpAsgToOper(genTreeOps op);
// Given a tree node, if this node uses that node, return the use as an out parameter and return true.
// Otherwise, return false.
- bool TryGetUse(GenTree* def, GenTree*** use, bool expandMultiRegArgs = true);
+ bool TryGetUse(GenTree* def, GenTree*** use);
// Get the parent of this node, and optionally capture the pointer to the child so that it can be modified.
GenTreePtr gtGetParent(GenTreePtr** parentChildPtrPtr);
// Returns an iterator that will produce the use edge to each operand of this node. Differs
// from the sequence of nodes produced by a loop over `GetChild` in its handling of call, phi,
- // and block op nodes. If `expandMultiRegArgs` is true, an multi-reg args passed to a call
- // will appear be expanded from their GT_LIST node into that node's contents.
- GenTreeUseEdgeIterator GenTree::UseEdgesBegin(bool expandMultiRegArgs = true);
+ // and block op nodes.
+ GenTreeUseEdgeIterator GenTree::UseEdgesBegin();
GenTreeUseEdgeIterator GenTree::UseEdgesEnd();
- IteratorPair<GenTreeUseEdgeIterator> GenTree::UseEdges(bool expandMultiRegArgs = true);
+ IteratorPair<GenTreeUseEdgeIterator> GenTree::UseEdges();
// Returns an iterator that will produce each operand of this node. Differs from the sequence
// of nodes produced by a loop over `GetChild` in its handling of call, phi, and block op
- // nodes. If `expandMultiRegArgs` is true, an multi-reg args passed to a call will appear
- // be expanded from their GT_LIST node into that node's contents.
- GenTreeOperandIterator OperandsBegin(bool expandMultiRegArgs = true);
+ // nodes.
+ GenTreeOperandIterator OperandsBegin();
GenTreeOperandIterator OperandsEnd();
// Returns a range that will produce the operands of this node in use order.
- IteratorPair<GenTreeOperandIterator> Operands(bool expandMultiRegArgs = true);
+ IteratorPair<GenTreeOperandIterator> Operands();
bool Precedes(GenTree* other);
class GenTreeUseEdgeIterator final
{
friend class GenTreeOperandIterator;
- friend GenTreeUseEdgeIterator GenTree::UseEdgesBegin(bool expandMultiRegArgs);
+ friend GenTreeUseEdgeIterator GenTree::UseEdgesBegin();
friend GenTreeUseEdgeIterator GenTree::UseEdgesEnd();
GenTree* m_node;
GenTree** m_edge;
GenTree* m_argList;
- GenTree* m_multiRegArg;
- bool m_expandMultiRegArgs;
int m_state;
- GenTreeUseEdgeIterator(GenTree* node, bool expandMultiRegArgs);
+ GenTreeUseEdgeIterator(GenTree* node);
GenTree** GetNextUseEdge() const;
void MoveToNextCallUseEdge();
#ifdef FEATURE_SIMD
void MoveToNextSIMDUseEdge();
#endif
-#if FEATURE_MULTIREG_ARGS
- void MoveToNextPutArgStkUseEdge();
-#endif
+ void MoveToNextAggregateUseEdge();
public:
GenTreeUseEdgeIterator();
// `GenTree::OperandsBegin` and `GenTree::OperandsEnd`.
class GenTreeOperandIterator final
{
- friend GenTreeOperandIterator GenTree::OperandsBegin(bool expandMultiRegArgs);
+ friend GenTreeOperandIterator GenTree::OperandsBegin();
friend GenTreeOperandIterator GenTree::OperandsEnd();
GenTreeUseEdgeIterator m_useEdges;
- GenTreeOperandIterator(GenTree* node, bool expandMultiRegArgs) : m_useEdges(node, expandMultiRegArgs)
+ GenTreeOperandIterator(GenTree* node) : m_useEdges(node)
{
}
// method names for the arguments.
struct GenTreeArgList : public GenTreeOp
{
+ bool IsAggregate() const
+ {
+ return (gtFlags & GTF_LIST_AGGREGATE) != 0;
+ }
+
GenTreePtr& Current()
{
return gtOp1;
gtFlags |= rest->gtFlags & GTF_ALL_EFFECT;
}
}
+
+ GenTreeArgList* Prepend(Compiler* compiler, GenTree* element);
};
// There was quite a bit of confusion in the code base about which of gtOp1 and gtOp2 was the
GTNODE(JTRUE , "jmpTrue" ,0,GTK_UNOP|GTK_NOVALUE)
-GTNODE(LIST , "<list>" ,0,GTK_BINOP|GTK_NOVALUE)
+GTNODE(LIST , "<list>" ,0,GTK_BINOP)
//-----------------------------------------------------------------------------
// Other nodes that have special structure:
for (GenTree* operand : firstNode->Operands())
{
// Do not mark nodes that do not appear in the execution order
- assert(operand->OperGet() != GT_LIST);
if (operand->OperGet() == GT_ARGPLACE)
{
continue;
// clang-format on
assert(arg->OperGet() == GT_LIST);
+
GenTreeArgList* argListPtr = arg->AsArgList();
+ assert(argListPtr->IsAggregate());
for (unsigned ctr = 0; argListPtr != nullptr; argListPtr = argListPtr->Rest(), ctr++)
{
if ((info->numRegs > 1) && (arg->OperGet() == GT_LIST))
{
assert(arg->OperGet() == GT_LIST);
+
GenTreeArgList* argListPtr = arg->AsArgList();
+ assert(argListPtr->IsAggregate());
for (unsigned ctr = 0; argListPtr != nullptr; argListPtr = argListPtr->Rest(), ctr++)
{
// If an extra node is returned, splice it in the right place in the tree.
if (arg != putArg)
{
- // putArg and arg are equals if arg is GT_LIST (a list of multiple LCL_FLDs to be passed in registers.)
ReplaceArgWithPutArgOrCopy(ppArg, putArg);
}
}
#endif
case GT_LIST:
- for (GenTreeArgList* list = arg->AsArgList(); list != nullptr; list = list->Rest())
{
- assert(list->Current()->OperIsPutArg());
+ GenTreeArgList* list = arg->AsArgList();
+ assert(list->IsAggregate());
+
+ for (; list != nullptr; list = list->Rest())
+ {
+ assert(list->Current()->OperIsPutArg());
+ }
}
break;
operand->OperIsCompare());
return 0;
}
- else if (operand->OperIsStore() || operand->TypeGet() == TYP_VOID)
+ else if (!operand->OperIsAggregate() && (operand->OperIsStore() || operand->TypeGet() == TYP_VOID))
{
// Stores and void-typed operands may be encountered when processing call nodes, which contain
// pointers to argument setup stores.
}
else
{
- // If a non-void-types operand is not an unsued value and does not have source registers, that
- // argument is contained within its parent and produces `sum(operand_dst_count)` registers.
+ // If an aggregate or non-void-typed operand is not an unsued value and does not have source registers,
+ // that argument is contained within its parent and produces `sum(operand_dst_count)` registers.
int dstCount = 0;
- for (GenTree* op : operand->Operands(true))
+ for (GenTree* op : operand->Operands())
{
dstCount += ComputeOperandDstCount(op);
}
static int ComputeAvailableSrcCount(GenTree* node)
{
int numSources = 0;
- for (GenTree* operand : node->Operands(true))
+ for (GenTree* operand : node->Operands())
{
numSources += ComputeOperandDstCount(operand);
}
assert(!isRegPairType(tree->TypeGet()));
#endif // _TARGET_ARM_
- // The tree traversal doesn't visit GT_LIST or GT_ARGPLACE nodes
- if (tree->OperGet() == GT_LIST || tree->OperGet() == GT_ARGPLACE)
- {
- return;
- }
+ // The LIR traversal doesn't visit non-aggregate GT_LIST or GT_ARGPLACE nodes
+ assert(tree->OperGet() != GT_ARGPLACE);
+ assert((tree->OperGet() != GT_LIST) || tree->AsArgList()->IsAggregate());
// These nodes are eliminated by the Rationalizer.
if (tree->OperGet() == GT_CLS_VAR)
{
// Get the location info for the register defined by the first operand.
LocationInfoList operandDefs;
- bool found = operandToLocationInfoMap.TryGetValue(*(tree->OperandsBegin(true)), &operandDefs);
+ bool found = operandToLocationInfoMap.TryGetValue(*(tree->OperandsBegin()), &operandDefs);
assert(found);
// Since we only expect to consume one register, we should only have a single register to
// Get the register information for the first operand of the node.
LocationInfoList operandDefs;
- bool found = operandToLocationInfoMap.TryGetValue(*(tree->OperandsBegin(true)), &operandDefs);
+ bool found = operandToLocationInfoMap.TryGetValue(*(tree->OperandsBegin()), &operandDefs);
assert(found);
// Preference the destination to the interval of the first register defined by the first operand.
int internalCount = buildInternalRegisterDefsForNode(tree, currentLoc, internalRefs);
// pop all ref'd tree temps
- GenTreeOperandIterator iterator = tree->OperandsBegin(true);
+ GenTreeOperandIterator iterator = tree->OperandsBegin();
// `operandDefs` holds the list of `LocationInfo` values for the registers defined by the current
// operand. `operandDefsIterator` points to the current `LocationInfo` value in `operandDefs`.
#endif // FEATURE_PARTIAL_SIMD_CALLEE_SAVE
bool isContainedNode =
- !noAdd && consume == 0 && produce == 0 && tree->TypeGet() != TYP_VOID && !tree->OperIsStore();
+ !noAdd && consume == 0 && produce == 0 && (tree->OperIsAggregate() || (tree->TypeGet() != TYP_VOID && !tree->OperIsStore()));
if (isContainedNode)
{
// Contained nodes map to the concatenated lists of their operands.
- for (GenTree* op : tree->Operands(true))
+ for (GenTree* op : tree->Operands())
{
if (!op->gtLsraInfo.definesAnyRegisters)
{
.eightByteClassifications[1],
fgEntryPtr->structDesc.eightByteSizes[1]),
lclCommon->gtLclNum, fgEntryPtr->structDesc.eightByteOffsets[1]);
- // Note this should actually be: secondNode = gtNewArgList(newLclField)
- GenTreeArgList* secondNode = gtNewListNode(newLclField, nullptr);
- secondNode->gtType = originalType; // Preserve the type. It is a special case.
+
+ GenTreeArgList* aggregate = gtNewAggregate(newLclField);
+ aggregate->gtType = originalType; // Preserve the type. It is a special case.
newLclField->gtFieldSeq = FieldSeqStore::NotAField();
// First field
arg->gtType =
GetTypeFromClassificationAndSizes(fgEntryPtr->structDesc.eightByteClassifications[0],
fgEntryPtr->structDesc.eightByteSizes[0]);
- arg = gtNewListNode(arg, secondNode);
+ arg = aggregate->Prepend(this, arg);
arg->gtType = type; // Preserve the type. It is a special case.
}
else
// replace the existing LDOBJ(ADDR(LCLVAR))
// with a LIST(LCLVAR-LO, LIST(LCLVAR-HI, nullptr))
//
- newArg = gtNewListNode(loLclVar, gtNewArgList(hiLclVar));
+ newArg = gtNewAggregate(hiLclVar)->Prepend(this, loLclVar);
}
}
}
GenTreePtr nextLclFld = gtNewLclFldNode(varNum, type[inx], offset);
if (newArg == nullptr)
{
- newArg = gtNewArgList(nextLclFld);
+ newArg = gtNewAggregate(nextLclFld);
}
else
{
- newArg = gtNewListNode(nextLclFld, newArg);
+ newArg = newArg->Prepend(this, nextLclFld);
}
}
}
GenTreePtr curItem = gtNewOperNode(GT_IND, type[inx], curAddr);
if (newArg == nullptr)
{
- newArg = gtNewArgList(curItem);
+ newArg = gtNewAggregate(curItem);
}
else
{
- newArg = gtNewListNode(curItem, newArg);
+ newArg = newArg->Prepend(this, curItem);
}
}
}
}
+#ifdef DEBUG
// If we reach here we should have set newArg to something
if (newArg == nullptr)
{
-#ifdef DEBUG
gtDispTree(argValue);
-#endif
assert(!"Missing case in fgMorphMultiregStructArg");
}
-#ifdef DEBUG
if (verbose)
{
printf("fgMorphMultiregStructArg created tree:\n");
// First, remove any preceeding GT_LIST nodes, which are not otherwise visited by the tree walk.
//
- // NOTE: GT_LIST nodes that are used by block ops and phi nodes will in fact be visited.
- for (GenTree* prev = node->gtPrev; prev != nullptr && prev->OperGet() == GT_LIST; prev = node->gtPrev)
+ // NOTE: GT_LIST nodes that are used as aggregates, by block ops, and by phi nodes will in fact be visited.
+ for (GenTree* prev = node->gtPrev;
+ prev != nullptr && prev->OperGet() == GT_LIST && !(prev->AsArgList()->IsAggregate());
+ prev = node->gtPrev)
{
BlockRange().Remove(prev);
}
- // In addition, remove the current node if it is a GT_LIST node.
- if ((*useEdge)->OperGet() == GT_LIST)
+ // In addition, remove the current node if it is a GT_LIST node that is not an aggregate.
+ if (node->OperGet() == GT_LIST)
{
- BlockRange().Remove(*useEdge);
+ GenTreeArgList* list = node->AsArgList();
+ if (!list->IsAggregate())
+ {
+ BlockRange().Remove(list);
+ }
return Compiler::WALK_CONTINUE;
}
WorkingDir=JIT\Performance\CodeQuality\Roslyn\CscBench
Expected=0
MaxAllowedDurationSeconds=600
-Categories=Pri0;EXPECTED_FAIL;ISSUE_6893;LONG_RUNNING
+Categories=Pri0;EXPECTED_PASS;LONG_RUNNING
HostStyle=0
[SciMark.cmd_8027]
RelativePath=JIT\Performance\CodeQuality\SciMark\SciMark\SciMark.cmd
WorkingDir=managed\Compilation\Compilation
Expected=0
MaxAllowedDurationSeconds=800
-Categories=Pri0;RT;EXPECTED_FAIL;ISSUE_6893;NATIVE_INTEROP;LONG_RUNNING
+Categories=Pri0;RT;EXPECTED_PASS;NATIVE_INTEROP;LONG_RUNNING
HostStyle=0
[generics.cmd_9787]
RelativePath=readytorun\generics\generics.cmd