-----------------
1. If this is an argument that is passed in a register, then the existing
-node is moved to the late argument list and a new `GT_ARGPLACE` (placeholder)
-node replaces it in the early argument list.
-2. Additionally, if `m_needPlace` is true (only for `FEATURE_FIXED_OUT_ARGS`)
-then the existing node is moved to the late argument list and a new
-`GT_ARGPLACE` (placeholder) node replaces it in the `early argument list.
+node is moved to the late argument list.
+2. Similarly, if `m_needPlace` is true (only for `FEATURE_FIXED_OUT_ARGS`)
+then the existing node is moved to the late argument list.
3. Otherwise the argument is left in the early argument and it will be
evaluated directly into the outgoing arg area or pushed on the stack.
opSize = TARGET_POINTER_SIZE * 2;
alignment = TARGET_POINTER_SIZE;
}
- else if (op->IsArgPlaceHolderNode())
- {
- CORINFO_CLASS_HANDLE clsHnd = op->AsArgPlace()->gtArgPlaceClsHnd;
- assert(clsHnd != 0);
- opSize = roundUp(compiler->info.compCompHnd->getClassSize(clsHnd), TARGET_POINTER_SIZE);
- alignment = roundUp(compiler->info.compCompHnd->getClassAlignmentRequirement(clsHnd), TARGET_POINTER_SIZE);
- }
else
{
assert(!"Unhandled gtOper");
void CodeGen::genNumberOperandUse(GenTree* const operand, int& useNum) const
{
assert(operand != nullptr);
-
- // Ignore argument placeholders.
- if (operand->OperGet() == GT_ARGPLACE)
- {
- return;
- }
-
assert(operand->gtUseNum == -1);
if (!operand->isContained() && !operand->IsCopyOrReload())
opSize = TARGET_POINTER_SIZE * 2;
alignment = TARGET_POINTER_SIZE;
}
- else if (op->IsArgPlaceHolderNode())
- {
- CORINFO_CLASS_HANDLE clsHnd = op->AsArgPlace()->gtArgPlaceClsHnd;
- assert(clsHnd != 0);
- opSize = roundUp(compiler->info.compCompHnd->getClassSize(clsHnd), TARGET_POINTER_SIZE);
- alignment = roundUp(compiler->info.compCompHnd->getClassAlignmentRequirement(clsHnd), TARGET_POINTER_SIZE);
- }
else
{
assert(!"Unhandled gtOper");
// The call will pop its arguments.
// for each putarg_stk:
target_ssize_t stackArgBytes = 0;
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
GenTree* argNode = arg.GetEarlyNode();
if (argNode->OperIs(GT_PUTARG_STK) && ((argNode->gtFlags & GTF_LATE_ARG) == 0))
GenTree* gtNewNothingNode();
- GenTree* gtNewArgPlaceHolderNode(var_types type, CORINFO_CLASS_HANDLE clsHnd);
-
GenTree* gtUnusedValNode(GenTree* expr);
GenTree* gtNewKeepAliveNode(GenTree* op);
case GT_JMPTABLE:
case GT_CLS_VAR:
case GT_CLS_VAR_ADDR:
- case GT_ARGPLACE:
case GT_PHYSREG:
case GT_EMITNOP:
case GT_PINVOKE_PROLOG:
{
GenTreeCall* const call = node->AsCall();
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
result = WalkTree(&arg.EarlyNodeRef(), call);
if (result == fgWalkResult::WALK_ABORT)
gtFlags &= ~(GTF_ALL_EFFECT | GTF_REVERSE_OPS);
}
-// return new arg placeholder node. Does not do anything but has a type associated
-// with it so we can keep track of register arguments in lists associated w/ call nodes
-
-inline GenTree* Compiler::gtNewArgPlaceHolderNode(var_types type, CORINFO_CLASS_HANDLE clsHnd)
-{
- GenTree* node = new (this, GT_ARGPLACE) GenTreeArgPlace(type, clsHnd);
- return node;
-}
-
/*****************************************************************************/
inline GenTree* Compiler::gtUnusedValNode(GenTree* expr)
case GT_JMPTABLE:
case GT_CLS_VAR:
case GT_CLS_VAR_ADDR:
- case GT_ARGPLACE:
case GT_PHYSREG:
case GT_EMITNOP:
case GT_PINVOKE_PROLOG:
{
GenTreeCall* const call = this->AsCall();
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
if (visitor(arg.GetEarlyNode()) == VisitResult::Abort)
{
{
// TODO-Cleanup: this is a patch for a violation in our GT_ASG propagation.
// see https://github.com/dotnet/runtime/issues/13758
- actualFlags |= arg.GetEarlyNode()->gtFlags & GTF_ASG;
+ if (arg.GetEarlyNode() != nullptr)
+ {
+ actualFlags |= arg.GetEarlyNode()->gtFlags & GTF_ASG;
+ }
+
if (arg.GetLateNode() != nullptr)
{
actualFlags |= arg.GetLateNode()->gtFlags & GTF_ASG;
uint8_t* classProfile = m_schema[*m_currentSchemaIndex].Offset + m_profileMemory;
*m_currentSchemaIndex += 2; // There are 2 schema entries per class probe
+ assert(!call->gtArgs.AreArgsComplete());
CallArg* objUse = nullptr;
if (compiler->impIsCastHelperEligibleForClassProbe(call))
{
assert(call->gtArgs.HasThisPointer());
assert(call->gtArgs.CountArgs() == 3);
- GenTree* targetMethod = call->gtArgs.GetArgByIndex(2)->GetEarlyNode();
+ assert(!call->gtArgs.AreArgsComplete());
+ GenTree* targetMethod = call->gtArgs.GetArgByIndex(2)->GetNode();
noway_assert(targetMethod->TypeGet() == TYP_I_IMPL);
genTreeOps oper = targetMethod->OperGet();
CORINFO_METHOD_HANDLE targetMethodHnd = nullptr;
else if (oper == GT_CALL && targetMethod->AsCall()->gtCallMethHnd == eeFindHelper(CORINFO_HELP_VIRTUAL_FUNC_PTR))
{
assert(targetMethod->AsCall()->gtArgs.CountArgs() == 3);
- GenTree* handleNode = targetMethod->AsCall()->gtArgs.GetArgByIndex(2)->GetEarlyNode();
+ GenTree* handleNode = targetMethod->AsCall()->gtArgs.GetArgByIndex(2)->GetNode();
if (handleNode->OperGet() == GT_CNS_INT)
{
GenTreeCall* runtimeLookupCall = qmarkNode->AsOp()->gtOp2->AsOp()->gtOp1->AsCall();
// This could be any of CORINFO_HELP_RUNTIMEHANDLE_(METHOD|CLASS)(_LOG?)
- GenTree* tokenNode = runtimeLookupCall->gtArgs.GetArgByIndex(1)->GetEarlyNode();
+ GenTree* tokenNode = runtimeLookupCall->gtArgs.GetArgByIndex(1)->GetNode();
noway_assert(tokenNode->OperGet() == GT_CNS_INT);
targetMethodHnd = CORINFO_METHOD_HANDLE(tokenNode->AsIntCon()->gtCompileTimeHandle);
}
{
JITDUMP("optimized\n");
- GenTree* thisPointer = call->gtArgs.GetThisArg()->GetEarlyNode();
- GenTree* targetObjPointers = call->gtArgs.GetArgByIndex(1)->GetEarlyNode();
+ GenTree* thisPointer = call->gtArgs.GetThisArg()->GetNode();
+ GenTree* targetObjPointers = call->gtArgs.GetArgByIndex(1)->GetNode();
CORINFO_LOOKUP pLookup;
info.compCompHnd->getReadyToRunDelegateCtorHelper(&ldftnToken->m_token, ldftnToken->m_tokenConstraint,
clsHnd, &pLookup);
{
JITDUMP("optimized\n");
- GenTree* thisPointer = call->gtArgs.GetArgByIndex(0)->GetEarlyNode();
- GenTree* targetObjPointers = call->gtArgs.GetArgByIndex(1)->GetEarlyNode();
+ GenTree* thisPointer = call->gtArgs.GetArgByIndex(0)->GetNode();
+ GenTree* targetObjPointers = call->gtArgs.GetArgByIndex(1)->GetNode();
call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_DELEGATE_CTOR, TYP_VOID, thisPointer, targetObjPointers);
CORINFO_LOOKUP entryPoint;
//
// Arguments:
// tree - the tree to sequence
-// isLIR - whether the sequencing is being done for LIR. If so,
-// ARGPLACE nodes will not be threaded into the linear
-// order, and the GTF_REVERSE_OPS flag will be cleared
-// on all the nodes.
+// isLIR - whether the sequencing is being done for LIR. If so, the
+// GTF_REVERSE_OPS flag will be cleared on all nodes.
//
// Return Value:
// The first node to execute in the sequenced tree.
if (m_isLIR)
{
node->ClearReverseOp();
-
- // ARGPLACE nodes are not threaded into the LIR sequence.
- if (node->OperIs(GT_ARGPLACE))
- {
- return fgWalkResult::WALK_CONTINUE;
- }
}
node->gtPrev = m_prevNode;
for (CallArg& arg : m_callAncestor->gtArgs.Args())
{
- m_useFlags |= (arg.GetEarlyNode()->gtFlags & GTF_GLOB_EFFECT);
+ m_useFlags |= (arg.GetNode()->gtFlags & GTF_GLOB_EFFECT);
}
if (oldUseFlags != m_useFlags)
static_assert_no_msg(sizeof(GenTreeRetExpr) <= TREE_NODE_SZ_LARGE); // *** large node
static_assert_no_msg(sizeof(GenTreeILOffset) <= TREE_NODE_SZ_SMALL);
static_assert_no_msg(sizeof(GenTreeClsVar) <= TREE_NODE_SZ_SMALL);
- static_assert_no_msg(sizeof(GenTreeArgPlace) <= TREE_NODE_SZ_SMALL);
static_assert_no_msg(sizeof(GenTreePhiArg) <= TREE_NODE_SZ_SMALL);
static_assert_no_msg(sizeof(GenTreeAllocObj) <= TREE_NODE_SZ_LARGE); // *** large node
#ifndef FEATURE_PUT_STRUCT_ARG_STK
{
return false;
}
- }
- if ((i1 != end1) || (i2 != end2))
- {
- return false;
- }
- }
-
- {
- CallArgs::LateArgIterator i1 = c1->gtArgs.LateArgs().begin();
- CallArgs::LateArgIterator end1 = c1->gtArgs.LateArgs().end();
- CallArgs::LateArgIterator i2 = c2->gtArgs.LateArgs().begin();
- CallArgs::LateArgIterator end2 = c2->gtArgs.LateArgs().end();
-
- for (; (i1 != end1) && (i2 != end2); ++i1, ++i2)
- {
if (!Compare(i1->GetLateNode(), i2->GetLateNode()))
{
return false;
if ((*link)->IsArgAddedLate())
{
JITDUMP("Removing arg %s [%06u] to prepare for re-morphing call\n",
- getWellKnownArgName((*link)->GetWellKnownArg()), Compiler::dspTreeID((*link)->GetEarlyNode()));
+ getWellKnownArgName((*link)->GetWellKnownArg()), Compiler::dspTreeID((*link)->GetNode()));
*link = (*link)->GetNext();
}
case GT_LABEL:
return true;
- case GT_ARGPLACE:
- if ((op1->gtType == TYP_STRUCT) &&
- (op1->AsArgPlace()->gtArgPlaceClsHnd != op2->AsArgPlace()->gtArgPlaceClsHnd))
- {
- break;
- }
- return true;
-
default:
break;
}
case GT_CALL:
for (CallArg& arg : tree->AsCall()->gtArgs.Args())
{
- hash = genTreeHashAdd(hash, gtHashValue(arg.GetEarlyNode()));
+ if (arg.GetEarlyNode() != nullptr)
+ {
+ hash = genTreeHashAdd(hash, gtHashValue(arg.GetEarlyNode()));
+ }
+
+ if (arg.GetLateNode() != nullptr)
+ {
+ hash = genTreeHashAdd(hash, gtHashValue(arg.GetLateNode()));
+ }
}
if (tree->AsCall()->gtCallType == CT_INDIRECT)
hash = genTreeHashAdd(hash, tree->AsCall()->gtCallMethHnd);
}
- for (CallArg& arg : tree->AsCall()->gtArgs.LateArgs())
- {
- hash = genTreeHashAdd(hash, gtHashValue(arg.GetLateNode()));
- }
break;
#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
}
else
{
- for (CallArg& arg : args->Args())
+ for (CallArg& arg : args->EarlyArgs())
{
GenTree* node = arg.GetEarlyNode();
unsigned level = gtSetEvalOrder(node);
update(node, level);
}
}
+
+ // TODO-ARGS: Quirk to match old costs assigned to 'this'
+ CallArg* thisArg = args->GetThisArg();
+ if ((thisArg != nullptr) && (thisArg->GetEarlyNode() == nullptr))
+ {
+ costSz++;
+ }
}
*callCostEx += costEx;
break;
case GT_PHI_ARG:
- case GT_ARGPLACE:
level = 0;
costEx = 0;
costSz = 0;
case GT_JMPTABLE:
case GT_CLS_VAR:
case GT_CLS_VAR_ADDR:
- case GT_ARGPLACE:
case GT_PHYSREG:
case GT_EMITNOP:
case GT_PINVOKE_PROLOG:
copy = new (this, GT_MEMORYBARRIER) GenTree(GT_MEMORYBARRIER, TYP_VOID);
goto DONE;
- case GT_ARGPLACE:
- copy = gtNewArgPlaceHolderNode(tree->gtType, tree->AsArgPlace()->gtArgPlaceClsHnd);
- goto DONE;
-
case GT_FTN_ADDR:
copy = new (this, oper) GenTreeFptrVal(tree->gtType, tree->AsFptrVal()->gtFptrMethod);
case GT_JMPTABLE:
case GT_CLS_VAR:
case GT_CLS_VAR_ADDR:
- case GT_ARGPLACE:
case GT_PHYSREG:
case GT_EMITNOP:
case GT_PINVOKE_PROLOG:
switch (state)
{
case CALL_ARGS:
- if (m_statePtr != nullptr)
+ while (m_statePtr != nullptr)
{
CallArg* arg = static_cast<CallArg*>(m_statePtr);
m_edge = &arg->EarlyNodeRef();
m_statePtr = arg->GetNext();
- return;
+
+ if (*m_edge != nullptr)
+ {
+ return;
+ }
}
m_statePtr = &*call->gtArgs.LateArgs().begin();
m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_LATE_ARGS>;
{
CallArg* arg = static_cast<CallArg*>(m_statePtr);
m_edge = &arg->LateNodeRef();
- m_statePtr = arg->GetLateNext();
+ assert(*m_edge != nullptr);
+ m_statePtr = arg->GetLateNext();
return;
}
m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_CONTROL_EXPR>;
}
}
- if (tree->IsArgPlaceHolderNode() && (tree->AsArgPlace()->gtArgPlaceClsHnd != nullptr))
- {
- printf(" => [clsHnd=%08X]", dspPtr(tree->AsArgPlace()->gtArgPlaceClsHnd));
- }
-
if (tree->gtOper == GT_RUNTIMELOOKUP)
{
#ifdef TARGET_64BIT
case GT_PROF_HOOK:
case GT_CATCH_ARG:
case GT_MEMORYBARRIER:
- case GT_ARGPLACE:
case GT_PINVOKE_PROLOG:
case GT_JMPTABLE:
break;
//
void Compiler::gtDispArgList(GenTreeCall* call, GenTree* lastCallOperand, IndentStack* indentStack)
{
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
- if (!arg.GetEarlyNode()->IsNothingNode() && !arg.GetEarlyNode()->IsArgPlaceHolderNode())
- {
- char buf[256];
- gtGetArgMsg(call, &arg, buf, sizeof(buf));
- gtDispChild(arg.GetEarlyNode(), indentStack, (arg.GetEarlyNode() == lastCallOperand) ? IIArcBottom : IIArc,
- buf, false);
- }
+ char buf[256];
+ gtGetArgMsg(call, &arg, buf, sizeof(buf));
+ gtDispChild(arg.GetEarlyNode(), indentStack, (arg.GetEarlyNode() == lastCallOperand) ? IIArcBottom : IIArc, buf,
+ false);
}
}
IndentInfo operandArc = IIArcTop;
for (GenTree* operand : node->Operands())
{
- if (operand->IsArgPlaceHolderNode() || !operand->IsValue())
+ if (!operand->IsValue())
{
// Either of these situations may happen with calls.
continue;
{
case NI_System_Enum_HasFlag:
{
- GenTree* thisOp = call->gtArgs.GetArgByIndex(0)->GetEarlyNode();
- GenTree* flagOp = call->gtArgs.GetArgByIndex(1)->GetEarlyNode();
+ GenTree* thisOp = call->gtArgs.GetArgByIndex(0)->GetNode();
+ GenTree* flagOp = call->gtArgs.GetArgByIndex(1)->GetNode();
GenTree* result = gtOptimizeEnumHasFlag(thisOp, flagOp);
if (result != nullptr)
case NI_System_Type_op_Inequality:
{
noway_assert(call->TypeGet() == TYP_INT);
- GenTree* op1 = call->gtArgs.GetArgByIndex(0)->GetEarlyNode();
- GenTree* op2 = call->gtArgs.GetArgByIndex(1)->GetEarlyNode();
+ GenTree* op1 = call->gtArgs.GetArgByIndex(0)->GetNode();
+ GenTree* op2 = call->gtArgs.GetArgByIndex(1)->GetNode();
// If either operand is known to be a RuntimeType, this can be folded
GenTree* result = gtFoldTypeEqualityCall(ni == NI_System_Type_op_Equality, op1, op2);
JITDUMP("Optimizing compare of types-from-handles to instead compare handles\n");
assert((tree->AsOp()->gtGetOp1()->AsCall()->gtArgs.CountArgs() == 1) &&
(tree->AsOp()->gtGetOp2()->AsCall()->gtArgs.CountArgs() == 1));
- GenTree* op1ClassFromHandle = tree->AsOp()->gtGetOp1()->AsCall()->gtArgs.GetArgByIndex(0)->GetEarlyNode();
- GenTree* op2ClassFromHandle = tree->AsOp()->gtGetOp2()->AsCall()->gtArgs.GetArgByIndex(0)->GetEarlyNode();
+ GenTree* op1ClassFromHandle = tree->AsOp()->gtGetOp1()->AsCall()->gtArgs.GetArgByIndex(0)->GetNode();
+ GenTree* op2ClassFromHandle = tree->AsOp()->gtGetOp2()->AsCall()->gtArgs.GetArgByIndex(0)->GetNode();
CORINFO_CLASS_HANDLE cls1Hnd = NO_CLASS_HANDLE;
CORINFO_CLASS_HANDLE cls2Hnd = NO_CLASS_HANDLE;
return tree;
}
+ if (call->gtArgs.AreArgsComplete())
+ {
+ // We cannot handle folding the call away when remorphing.
+ return tree;
+ }
+
JITDUMP("\nAttempting to optimize BOX_NULLABLE(&x) %s null [%06u]\n", GenTree::OpName(oper), dspTreeID(tree));
// Get the address of the struct being boxed
- GenTree* const arg = call->gtArgs.GetArgByIndex(1)->GetEarlyNode();
+ GenTree* const arg = call->gtArgs.GetArgByIndex(1)->GetNode();
- if (arg->OperIs(GT_ADDR) && ((arg->gtFlags & GTF_LATE_ARG) == 0))
+ if (arg->OperIs(GT_ADDR))
{
CORINFO_CLASS_HANDLE nullableHnd = gtGetStructHandle(arg->AsOp()->gtOp1);
CORINFO_FIELD_HANDLE fieldHnd = info.compCompHnd->getFieldInClass(nullableHnd, 0);
{
// I'm a little worried that args that assign to temps that are late args will look like
// side effects...but better to be conservative for now.
- if (gtTreeHasSideEffects(arg.GetEarlyNode(), flags))
+ if ((arg.GetEarlyNode() != nullptr) && gtTreeHasSideEffects(arg.GetEarlyNode(), flags))
{
return true;
}
- if (arg.GetLateNode() != nullptr && gtTreeHasSideEffects(arg.GetLateNode(), flags))
+ if ((arg.GetLateNode() != nullptr) && gtTreeHasSideEffects(arg.GetLateNode(), flags))
{
return true;
}
case GT_RET_EXPR:
structHnd = tree->AsRetExpr()->gtRetClsHnd;
break;
- case GT_ARGPLACE:
- structHnd = tree->AsArgPlace()->gtArgPlaceClsHnd;
- break;
case GT_INDEX:
structHnd = tree->AsIndex()->gtStructElemClass;
break;
return t1->GetIconHandleFlag() == t2->GetIconHandleFlag();
}
- bool IsArgPlaceHolderNode() const
- {
- return OperGet() == GT_ARGPLACE;
- }
bool IsCall() const
{
return OperGet() == GT_CALL;
{
}
- CallArg& operator*() const
+ // clang-format off
+ CallArg& operator*() const { return *m_arg; }
+ CallArg* operator->() const { return m_arg; }
+ CallArg* GetArg() const { return m_arg; }
+ // clang-format on
+
+ CallArgIterator& operator++()
{
- return *m_arg;
+ m_arg = (m_arg->*Next)();
+ return *this;
}
- CallArg* operator->() const
+ bool operator==(const CallArgIterator& i) const
{
- return m_arg;
+ return m_arg == i.m_arg;
}
- CallArg* GetArg() const
+ bool operator!=(const CallArgIterator& i) const
{
- return m_arg;
+ return m_arg != i.m_arg;
}
+ };
- CallArgIterator& operator++()
+ class EarlyArgIterator
+ {
+ friend class CallArgs;
+
+ CallArg* m_arg;
+
+ static CallArg* NextEarlyArg(CallArg* cur)
{
- m_arg = (m_arg->*Next)();
+ while ((cur != nullptr) && (cur->GetEarlyNode() == nullptr))
+ {
+ cur = cur->GetNext();
+ }
+
+ return cur;
+ }
+
+ public:
+ explicit EarlyArgIterator(CallArg* arg) : m_arg(arg)
+ {
+ }
+
+ // clang-format off
+ CallArg& operator*() const { return *m_arg; }
+ CallArg* operator->() const { return m_arg; }
+ CallArg* GetArg() const { return m_arg; }
+ // clang-format on
+
+ EarlyArgIterator& operator++()
+ {
+ m_arg = NextEarlyArg(m_arg->GetNext());
return *this;
}
- bool operator==(const CallArgIterator& i) const
+ bool operator==(const EarlyArgIterator& i) const
{
return m_arg == i.m_arg;
}
- bool operator!=(const CallArgIterator& i) const
+ bool operator!=(const EarlyArgIterator& i) const
{
return m_arg != i.m_arg;
}
return IteratorPair<ArgIterator>(ArgIterator(m_head), ArgIterator(nullptr));
}
+ IteratorPair<EarlyArgIterator> EarlyArgs()
+ {
+ CallArg* firstEarlyArg = EarlyArgIterator::NextEarlyArg(m_head);
+ return IteratorPair<EarlyArgIterator>(EarlyArgIterator(firstEarlyArg), EarlyArgIterator(nullptr));
+ }
+
IteratorPair<LateArgIterator> LateArgs()
{
return IteratorPair<LateArgIterator>(LateArgIterator(m_lateHead), LateArgIterator(nullptr));
CallArg* retBufArg = gtArgs.GetRetBufferArg();
GenTree* lclRetBufArgNode = retBufArg->GetEarlyNode();
- if (lclRetBufArgNode->IsArgPlaceHolderNode())
+ if (lclRetBufArgNode == nullptr)
{
lclRetBufArgNode = retBufArg->GetLateNode();
}
#endif
};
-/* gtArgPlace -- 'register argument placeholder' (GT_ARGPLACE) */
-
-struct GenTreeArgPlace : public GenTree
-{
- CORINFO_CLASS_HANDLE gtArgPlaceClsHnd; // Needed when we have a TYP_STRUCT argument
-
- GenTreeArgPlace(var_types type, CORINFO_CLASS_HANDLE clsHnd) : GenTree(GT_ARGPLACE, type), gtArgPlaceClsHnd(clsHnd)
- {
- }
-#if DEBUGGABLE_GENTREE
- GenTreeArgPlace() : GenTree()
- {
- }
-#endif
-};
-
/* gtPhiArg -- phi node rhs argument, var = phi(phiarg, phiarg, phiarg...); GT_PHI_ARG */
struct GenTreePhiArg : public GenTreeLclVarCommon
{
newState.isUnderIndir = false;
newState.isAssignSrc = false;
{
- for (CallArg& arg : tree->AsCall()->gtArgs.Args())
+ CallArg* thisArg = tree->AsCall()->gtArgs.GetThisArg();
+ if (thisArg != nullptr)
{
- if (arg.GetWellKnownArg() == WellKnownArg::ThisPointer)
- {
- newState.isUnderIndir = true;
- }
+ // TODO-ARGS: This is a quirk for previous behavior where
+ // we set this to true for the 'this' arg. The flag can
+ // then remain set after the recursive call, depending on
+ // what the child node is, e.g. GT_ARGPLACE did not clear
+ // the flag, so when processing the second arg we would
+ // also have isUnderIndir = true.
+ newState.isUnderIndir = true;
+ }
+ for (CallArg& arg : tree->AsCall()->gtArgs.EarlyArgs())
+ {
comp->fgWalkTreePre(&arg.EarlyNodeRef(), gsMarkPtrsAndAssignGroups, (void*)&newState);
}
for (CallArg& arg : tree->AsCall()->gtArgs.LateArgs())
GTNODE(RET_EXPR , GenTreeRetExpr ,0,GTK_LEAF|DBK_NOTLIR) // Place holder for the return expression from an inline candidate
GTNODE(CLS_VAR , GenTreeClsVar ,0,GTK_LEAF) // Static data member
-GTNODE(ARGPLACE , GenTreeArgPlace ,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTLIR) // Placeholder for a "late arg" in the original arg list.
-
//-----------------------------------------------------------------------------
// Constant nodes:
//-----------------------------------------------------------------------------
GTSTRUCT_1(ILOffset , GT_IL_OFFSET)
GTSTRUCT_2(CopyOrReload, GT_COPY, GT_RELOAD)
GTSTRUCT_2(ClsVar , GT_CLS_VAR, GT_CLS_VAR_ADDR)
-GTSTRUCT_1(ArgPlace , GT_ARGPLACE)
GTSTRUCT_1(CmpXchg , GT_CMPXCHG)
GTSTRUCT_1(AddrMode , GT_LEA)
GTSTRUCT_N(Blk , GT_BLK, GT_STORE_BLK, GT_OBJ, GT_STORE_OBJ, GT_STORE_DYN_BLK)
makeTemp = true;
break;
- case GT_ARGPLACE:
- structVal->AsArgPlace()->gtArgPlaceClsHnd = structHnd;
- break;
-
case GT_INDEX:
// This will be transformed to an OBJ later.
alreadyNormalized = true;
if ((ni == NI_System_MemoryExtensions_AsSpan) || (ni == NI_System_String_op_Implicit))
{
assert(argCall->gtArgs.CountArgs() == 1);
- GenTree* arg = argCall->gtArgs.GetArgByIndex(0)->GetEarlyNode();
+ GenTree* arg = argCall->gtArgs.GetArgByIndex(0)->GetNode();
if (arg->OperIs(GT_CNS_STR))
{
return arg->AsStrCon();
checkBlock->bbJumpKind = BBJ_COND;
// Fetch method table from object arg to call.
- GenTree* thisTree = compiler->gtCloneExpr(origCall->gtArgs.GetThisArg()->GetEarlyNode());
+ GenTree* thisTree = compiler->gtCloneExpr(origCall->gtArgs.GetThisArg()->GetNode());
// Create temp for this if the tree is costly.
if (!thisTree->IsLocal())
// copy 'this' to temp with exact type.
const unsigned thisTemp = compiler->lvaGrabTemp(false DEBUGARG("guarded devirt this exact temp"));
- GenTree* clonedObj = compiler->gtCloneExpr(origCall->gtArgs.GetThisArg()->GetEarlyNode());
+ GenTree* clonedObj = compiler->gtCloneExpr(origCall->gtArgs.GetThisArg()->GetNode());
GenTree* assign = compiler->gtNewTempAssign(thisTemp, clonedObj);
compiler->lvaSetClass(thisTemp, clsHnd, true);
compiler->fgNewStmtAtEnd(thenBlock, assign);
checkBlock = CreateAndInsertBasicBlock(BBJ_COND, currBlock);
assert(sizeCheck->GetEarlyNode()->OperIs(GT_LE));
- GenTree* sizeJmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, sizeCheck->GetEarlyNode());
+ GenTree* sizeJmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, sizeCheck->GetNode());
Statement* sizeJmpStmt = compiler->fgNewStmtFromTree(sizeJmpTree, stmt->GetDebugInfo());
compiler->fgInsertStmtAtEnd(checkBlock, sizeJmpStmt);
checkBlock2 = CreateAndInsertBasicBlock(BBJ_COND, checkBlock);
assert(nullCheck->GetEarlyNode()->OperIs(GT_EQ));
- GenTree* nullJmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, nullCheck->GetEarlyNode());
+ GenTree* nullJmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, nullCheck->GetNode());
Statement* nullJmpStmt = compiler->fgNewStmtFromTree(nullJmpTree, stmt->GetDebugInfo());
compiler->fgInsertStmtAtEnd(checkBlock2, nullJmpStmt);
}
// The first argument is the real first argument for the call now.
origCall->gtArgs.Remove(resultHandle);
- GenTree* asg = compiler->gtNewTempAssign(resultLclNum, resultHandle->GetEarlyNode());
+ GenTree* asg = compiler->gtNewTempAssign(resultLclNum, resultHandle->GetNode());
Statement* asgStmt = compiler->gtNewStmt(asg, stmt->GetDebugInfo());
compiler->fgInsertStmtAtEnd(thenBlock, asgStmt);
}
// mark the entire struct as address exposed results in CQ regressions.
GenTreeCall* callTree = user->IsCall() ? user->AsCall() : nullptr;
bool isThisArg = (callTree != nullptr) && callTree->gtArgs.HasThisPointer() &&
- (val.Node() == callTree->gtArgs.GetThisArg()->GetEarlyNode());
+ (val.Node() == callTree->gtArgs.GetThisArg()->GetNode());
bool exposeParentLcl = varDsc->lvIsStructField && !isThisArg;
bool hasHiddenStructArg = false;
if (varTypeIsStruct(varDsc) && varDsc->lvIsTemp)
{
if ((callTree != nullptr) && callTree->gtArgs.HasRetBuffer() &&
- (val.Node() == callTree->gtArgs.GetRetBufferArg()->GetEarlyNode()))
+ (val.Node() == callTree->gtArgs.GetRetBufferArg()->GetNode()))
{
assert(!exposeParentLcl);
// Mark the node's operands
firstNode->VisitOperands([&markCount](GenTree* operand) -> GenTree::VisitResult {
- // Do not mark nodes that do not appear in the execution order
- if (operand->OperGet() == GT_ARGPLACE)
- {
- return GenTree::VisitResult::Continue;
- }
-
operand->gtLIRFlags |= LIR::Flags::Mark;
markCount++;
return GenTree::VisitResult::Continue;
{
for (GenTree* operand : node->Operands())
{
- // ARGPLACE nodes are not represented in the LIR sequence. Ignore them.
- if (operand->OperIs(GT_ARGPLACE))
- {
- continue;
- }
-
if (operand->isContained())
{
UseNodeOperands(operand);
// other code that relies on being able to reach all the operands from a call node.
// The GT_NOP case is because sometimes we eliminate stack argument stores as dead, but
// instead of removing them we replace with a NOP.
- // ARGPLACE nodes are not represented in the LIR sequence. Ignore them.
// The argument of a JTRUE doesn't produce a value (just sets a flag).
- assert(((node->OperGet() == GT_CALL) &&
- (def->OperIsStore() || def->OperIs(GT_PUTARG_STK, GT_NOP, GT_ARGPLACE))) ||
+ assert(((node->OperGet() == GT_CALL) && (def->OperIsStore() || def->OperIs(GT_PUTARG_STK, GT_NOP))) ||
((node->OperGet() == GT_JTRUE) && (def->TypeGet() == TYP_VOID) &&
((def->gtFlags & GTF_SET_FLAGS) != 0)));
continue;
// Note that atomic ops may be stores and still produce a value.
if (!arg->IsValue())
{
- assert((arg->OperIsStore() && !arg->IsValue()) || arg->IsArgPlaceHolderNode() || arg->IsNothingNode() ||
- arg->OperIsCopyBlkOp());
+ assert((arg->OperIsStore() && !arg->IsValue()) || arg->IsNothingNode() || arg->OperIsCopyBlkOp());
return;
}
void Lowering::LowerArgsForCall(GenTreeCall* call)
{
JITDUMP("args:\n======\n");
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
LowerArg(call, &arg, false);
}
if (insertionPoint == nullptr)
{
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
assert(!arg.GetEarlyNode()->OperIs(GT_PUTARG_REG)); // We don't expect to see these in early args
// call could over-write the stack arg that is setup earlier.
ArrayStack<GenTree*> putargs(comp->getAllocator(CMK_ArrayStack));
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
if (arg.GetEarlyNode()->OperIs(GT_PUTARG_STK))
{
LowerNode(regNode);
// Finally move all GT_PUTARG_* nodes
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
GenTree* node = arg.GetEarlyNode();
// Non-value nodes in early args are setup nodes for late args.
#ifdef REG_DISPATCH_INDIRECT_CALL_ADDR
// Now insert the call target as an extra argument.
//
- GenTree* placeHolder = comp->gtNewArgPlaceHolderNode(callTarget->TypeGet(), NO_CLASS_HANDLE);
- CallArg* targetArg = call->gtArgs.PushBack(comp, placeHolder, WellKnownArg::DispatchIndirectCallTarget);
- placeHolder->gtFlags |= GTF_LATE_ARG;
+ CallArg* targetArg = call->gtArgs.PushBack(comp, nullptr, WellKnownArg::DispatchIndirectCallTarget);
targetArg->SetLateNode(callTarget);
call->gtArgs.PushLateBack(targetArg);
targetArg->AbiInfo.SetByteSize(TARGET_POINTER_SIZE, TARGET_POINTER_SIZE, false, false);
// Lower the newly added args now that call is updated
- LowerArg(call, targetArg, false /* late */);
LowerArg(call, targetArg, true /* late */);
// Finally update the call to be a helper call
{
if (!arg->IsValue() && !arg->OperIsPutArgStk())
{
- assert((arg->OperIsStore() && !arg->IsValue()) || arg->IsArgPlaceHolderNode() || arg->IsNothingNode() ||
- arg->OperIsCopyBlkOp());
+ assert(arg->OperIsStore() || arg->OperIsCopyBlkOp());
return;
}
//
void Lowering::CheckCall(GenTreeCall* call)
{
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
CheckCallArg(arg.GetEarlyNode());
}
}
}
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
if (arg.GetEarlyNode()->OperIs(GT_PUTARG_STK))
{
{
assert(operand != nullptr);
assert(operandString != nullptr);
- if (operand->OperIs(GT_ARGPLACE))
- {
- return;
- }
int dstCount = ComputeOperandDstCount(operand);
srcCount = 0;
break;
- case GT_ARGPLACE:
case GT_NO_OP:
case GT_START_NONGC:
case GT_PROF_HOOK:
srcCount = 0;
break;
- case GT_ARGPLACE:
case GT_NO_OP:
case GT_START_NONGC:
srcCount = 0;
// because the code generator doesn't actually consider it live,
// so it can't be spilled.
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
GenTree* argNode = arg.GetEarlyNode();
//
int LinearScan::ComputeOperandDstCount(GenTree* operand)
{
- // GT_ARGPLACE is the only non-LIR node that is currently in the trees at this stage, though
- // note that it is not in the linear order.
- if (operand->OperIs(GT_ARGPLACE))
- {
- return 0;
- }
-
if (operand->isContained())
{
int dstCount = 0;
//
void LinearScan::buildRefPositionsForNode(GenTree* tree, LsraLocation currentLoc)
{
- // The LIR traversal doesn't visit GT_ARGPLACE nodes.
// GT_CLS_VAR nodes should have been eliminated by rationalizer.
- assert(tree->OperGet() != GT_ARGPLACE);
assert(tree->OperGet() != GT_CLS_VAR);
// The set of internal temporary registers used by this node are stored in the
srcCount = 0;
break;
- case GT_ARGPLACE:
case GT_NO_OP:
case GT_START_NONGC:
srcCount = 0;
// because the code generator doesn't actually consider it live,
// so it can't be spilled.
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
GenTree* argNode = arg.GetEarlyNode();
srcCount = 0;
break;
- case GT_ARGPLACE:
case GT_NO_OP:
case GT_START_NONGC:
srcCount = 0;
//
void CallArg::Dump(Compiler* comp)
{
- printf("CallArg[[%06u].%s", comp->dspTreeID(GetNode()), GenTree::OpName(GetEarlyNode()->OperGet()));
+ printf("CallArg[[%06u].%s", comp->dspTreeID(GetNode()), GenTree::OpName(GetNode()->OperGet()));
printf(" %s", varTypeName(AbiInfo.ArgType));
printf(" (%s)", AbiInfo.PassedByRef ? "By ref" : "By value");
if (AbiInfo.GetRegNum() != REG_STK)
{
GenTree* argx = arg.GetEarlyNode();
+ if (argx == nullptr)
+ {
+ // Should only happen if remorphing in which case we do not need to
+ // make a decision about temps.
+ continue;
+ }
+
if (arg.AbiInfo.GetRegNum() == REG_STK)
{
assert(m_hasStackArgs);
break;
}
- if (!prevArg.GetEarlyNode()->IsInvariant())
+ if ((prevArg.GetEarlyNode() != nullptr) && !prevArg.GetEarlyNode()->IsInvariant())
{
SetNeedsTemp(&prevArg);
}
// For all previous arguments, if they have any GTF_ALL_EFFECT
// we require that they be evaluated into a temp
- if ((prevArg.GetEarlyNode()->gtFlags & GTF_ALL_EFFECT) != 0)
+ if ((prevArg.GetEarlyNode() != nullptr) && ((prevArg.GetEarlyNode()->gtFlags & GTF_ALL_EFFECT) != 0))
{
SetNeedsTemp(&prevArg);
}
//
if (hasStackArgsWeCareAbout || hasStructRegArgWeCareAbout)
{
- for (CallArg& arg : Args())
+ for (CallArg& arg : EarlyArgs())
{
GenTree* argx = arg.GetEarlyNode();
assert(HasThisPointer());
SetNeedsTemp(GetThisArg());
- for (CallArg& arg : Args())
+ for (CallArg& arg : EarlyArgs())
{
if ((arg.GetEarlyNode()->gtFlags & GTF_ALL_EFFECT) != 0)
{
{
GenTree* argx = arg->GetEarlyNode();
+ assert(argx != nullptr);
// put constants at the end of the table
//
if (argx->gtOper == GT_CNS_INT)
if (!arg->m_processed)
{
GenTree* argx = arg->GetEarlyNode();
+ assert(argx != nullptr);
// put calls at the beginning of the table
//
if (!arg->m_processed)
{
GenTree* argx = arg->GetEarlyNode();
+ assert(argx != nullptr);
if ((argx->gtOper == GT_LCL_VAR) || (argx->gtOper == GT_LCL_FLD))
{
if (!arg->m_processed)
{
GenTree* argx = arg->GetEarlyNode();
+ assert(argx != nullptr);
// We should have already handled these kinds of args
assert(argx->gtOper != GT_LCL_VAR);
// Get a new Obj node temp to use it as a call argument.
// gtNewObjNode will set the GTF_EXCEPT flag if this is not a local stack object.
- argNode = comp->gtNewObjNode(comp->lvaGetStruct(tmpVarNum), addrNode);
+ argNode = comp->gtNewObjNode(comp->lvaGetStruct(tmpVarNum), addrNode);
#endif // not (TARGET_AMD64 or TARGET_ARM64 or TARGET_ARM or TARGET_LOONGARCH64)
// will be assigned to a temp in the early node and passed as the local
// in the late node. This can happen for both register and stack args.
//
-// 2. Early: GT_ARGPLACE, Late: <any node>
+// 2. Early: nullptr, Late: <any node>
// All arguments that are placed in registers need to appear as a late
// node. Some stack arguments may also require this pattern, for example
// if a later argument trashes the outgoing arg area by requiring a
// call.
-// If the argument does not otherwise need to be evaluated into a temp,
-// we create just a placeholder GT_ARGPLACE for the early list and move
-// it into the late list.
+// If the argument does not otherwise need to be evaluated into a temp
+// we just move it into the late list.
//
-// 3. Early: <any node>, Late: no node
+// 3. Early: <any node>, Late: nullptr
// Arguments that are passed on stack and that do not need an explicit
// assignment in the early node list do not require any late node.
//
CallArg& arg = *(sortedArgs[i]);
assert(arg.GetLateNode() == nullptr);
- GenTree* argx = arg.GetEarlyNode();
+ GenTree* argx = arg.GetEarlyNode();
+ assert(argx != nullptr);
+
GenTree* setupArg = nullptr;
GenTree* defArg;
continue;
}
- /* No temp needed - move the whole node to the gtCallLateArgs list */
-
- /* The argument is deferred and put in the late argument list */
+ // No temp needed - move the whole node to the late list
defArg = argx;
- // Create a placeholder node to put in its place in gtCallLateArgs.
-
- // For a struct type we also need to record the class handle of the arg.
- CORINFO_CLASS_HANDLE clsHnd = NO_CLASS_HANDLE;
-
#if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)
// All structs are either passed (and retyped) as integral types, OR they
// are passed by reference.
noway_assert(argx->gtType != TYP_STRUCT);
-#else // !defined(TARGET_AMD64) || defined(UNIX_AMD64_ABI)
-
- if (defArg->TypeGet() == TYP_STRUCT)
- {
- clsHnd = comp->gtGetStructHandleIfPresent(defArg);
- noway_assert(clsHnd != NO_CLASS_HANDLE);
- }
-
#endif // !(defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI))
- setupArg = comp->gtNewArgPlaceHolderNode(defArg->gtType, clsHnd);
-
- /* mark the placeholder node as a late argument */
- setupArg->gtFlags |= GTF_LATE_ARG;
-
#ifdef DEBUG
if (comp->verbose)
{
}
comp->gtDispTree(argx);
- printf("Replaced with placeholder node:\n");
- comp->gtDispTree(setupArg);
+ printf("Moved to late list\n");
}
#endif
+
+ arg.SetEarlyNode(nullptr);
}
if (setupArg != nullptr)
{
- noway_assert(arg.GetEarlyNode() == argx);
arg.SetEarlyNode(setupArg);
}
if (call->gtCallMoreFlags & GTF_CALL_M_WRAPPER_DELEGATE_INV)
{
CallArg* thisArg = GetThisArg();
- assert(thisArg != nullptr);
+ assert((thisArg != nullptr) && (thisArg->GetEarlyNode() != nullptr));
GenTree* cloned;
if (thisArg->GetEarlyNode()->OperIsLocal())
if (call->gtCallMoreFlags & GTF_CALL_M_UNMGD_THISCALL)
{
- noway_assert(call->gtArgs.GetArgByIndex(0)->GetEarlyNode()->TypeGet() == TYP_I_IMPL ||
- call->gtArgs.GetArgByIndex(0)->GetEarlyNode()->TypeGet() == TYP_BYREF ||
- call->gtArgs.GetArgByIndex(0)->GetEarlyNode()->gtOper ==
- GT_NOP); // the arg was already morphed to a register (fgMorph called twice)
+ noway_assert((call->gtArgs.GetArgByIndex(0)->GetEarlyNode() == nullptr) ||
+ (call->gtArgs.GetArgByIndex(0)->GetEarlyNode()->TypeGet() == TYP_I_IMPL) ||
+ (call->gtArgs.GetArgByIndex(0)->GetEarlyNode()->TypeGet() == TYP_BYREF));
maxRegArgs = 1;
}
else
const CorInfoType corType = strip(comp->info.compCompHnd->getArgType(sig, sigArg, &argClass));
const var_types sigType = JITtype2varType(corType);
- const GenTree* nodeArg = arg.GetEarlyNode();
+ const GenTree* nodeArg = arg.GetNode();
assert(nodeArg != nullptr);
const var_types nodeType = nodeArg->TypeGet();
for (CallArg& arg : Args())
{
+ assert(arg.GetEarlyNode() != nullptr);
GenTree* argx = arg.GetEarlyNode()->gtSkipPutArgType();
// Change the node to TYP_I_IMPL so we don't report GC info
argx->gtType = TYP_I_IMPL;
}
- // We should never have any ArgPlaceHolder nodes at this point.
- assert(!argx->IsArgPlaceHolderNode());
-
// Setup any HFA information about 'argx'
bool isHfaArg = false;
var_types hfaType = TYP_UNDEF;
// Morph the arg node, and update the parent and argEntry pointers.
GenTree* argx = *parentArgx;
- argx = fgMorphTree(argx);
- *parentArgx = argx;
+ if (argx == nullptr)
+ {
+ // Skip node that was moved to late args during remorphing, no work to be done.
+ assert(reMorphing);
+ continue;
+ }
+
+ argx = fgMorphTree(argx);
+ *parentArgx = argx;
if (arg.GetWellKnownArg() == WellKnownArg::ThisPointer)
{
bool isStructArg = arg.AbiInfo.IsStruct;
GenTree* argObj = argx->gtEffectiveVal(true /*commaOnly*/);
- if (isStructArg && varTypeIsStruct(argObj) && !argObj->OperIs(GT_ASG, GT_MKREFANY, GT_FIELD_LIST, GT_ARGPLACE))
+ if (isStructArg && varTypeIsStruct(argObj) && !argObj->OperIs(GT_ASG, GT_MKREFANY, GT_FIELD_LIST))
{
CORINFO_CLASS_HANDLE objClass = gtGetStructHandle(argObj);
unsigned originalSize;
for (CallArg& arg : call->gtArgs.Args())
{
- bool isLateArg = (arg.GetEarlyNode()->gtFlags & GTF_LATE_ARG) != 0;
+ bool isLateArg = arg.GetLateNode() != nullptr;
GenTree* argx = arg.GetNode();
if (!arg.AbiInfo.IsStruct)
if (varDsc->lvPromoted)
{
JITDUMP("Arg [%06u] is promoted implicit byref V%02u, so no tail call\n",
- dspTreeID(arg.GetEarlyNode()), lclNum);
+ dspTreeID(arg.GetNode()), lclNum);
}
else
{
JITDUMP("Arg [%06u] is unpromoted implicit byref V%02u, seeing if we can still tail call\n",
- dspTreeID(arg.GetEarlyNode()), lclNum);
+ dspTreeID(arg.GetNode()), lclNum);
// We have to worry about introducing aliases if we bypass copying
// the struct at the call. We'll do some limited analysis to see if we
if (call->gtArgs.HasThisPointer())
{
- var_types thisArgType = call->gtArgs.GetThisArg()->GetEarlyNode()->TypeGet();
+ var_types thisArgType = call->gtArgs.GetThisArg()->GetNode()->TypeGet();
if (thisArgType != TYP_REF)
{
flags |= CORINFO_TAILCALL_THIS_ARG_IS_BYREF;
call->ClearExpandedEarly();
}
else if ((tailCallResult == TAILCALL_OPTIMIZED) &&
- ((call->gtArgs.GetThisArg()->GetEarlyNode()->gtFlags & GTF_SIDE_EFFECT) != 0))
+ ((call->gtArgs.GetThisArg()->GetNode()->gtFlags & GTF_SIDE_EFFECT) != 0))
{
// We generate better code when we expand this late in lower instead.
//
if (origCall->gtArgs.HasRetBuffer())
{
JITDUMP("Transferring retbuf\n");
- GenTree* retBufArg = origCall->gtArgs.GetRetBufferArg()->GetEarlyNode();
+ GenTree* retBufArg = origCall->gtArgs.GetRetBufferArg()->GetNode();
assert(info.compRetBuffArg != BAD_VAR_NUM);
assert(retBufArg->OperIsLocal());
if (thisArg != nullptr)
{
GenTree* thisPtr = nullptr;
- GenTree* objp = thisArg->GetEarlyNode();
+ GenTree* objp = thisArg->GetNode();
if ((call->IsDelegateInvoke() || call->IsVirtualVtable()) && !objp->OperIs(GT_LCL_VAR))
{
Statement* paramAssignmentInsertionPoint = lastStmt;
// Process early args. They may contain both setup statements for late args and actual args.
- // Early args don't include 'this' arg. We need to account for that so that the call to gtArgEntryByArgNum
- // below has the correct second argument.
- for (CallArg& arg : recursiveTailCall->gtArgs.Args())
+ for (CallArg& arg : recursiveTailCall->gtArgs.EarlyArgs())
{
GenTree* earlyArg = arg.GetEarlyNode();
- if (!earlyArg->IsNothingNode() && !earlyArg->IsArgPlaceHolderNode())
+ if ((earlyArg->gtFlags & GTF_LATE_ARG) != 0)
{
- if ((earlyArg->gtFlags & GTF_LATE_ARG) != 0)
- {
- // This is a setup node so we need to hoist it.
- Statement* earlyArgStmt = gtNewStmt(earlyArg, callDI);
- fgInsertStmtBefore(block, earlyArgInsertionPoint, earlyArgStmt);
- }
- else
+ // This is a setup node so we need to hoist it.
+ Statement* earlyArgStmt = gtNewStmt(earlyArg, callDI);
+ fgInsertStmtBefore(block, earlyArgInsertionPoint, earlyArgStmt);
+ }
+ else
+ {
+ // This is an actual argument that needs to be assigned to the corresponding caller parameter.
+ // Late-added non-standard args are extra args that are not passed as locals, so skip those
+ if (!arg.IsArgAddedLate())
{
- // This is an actual argument that needs to be assigned to the corresponding caller parameter.
- // Late-added non-standard args are extra args that are not passed as locals, so skip those
- if (!arg.IsArgAddedLate())
+ Statement* paramAssignStmt =
+ fgAssignRecursiveCallArgToCallerParam(earlyArg, &arg,
+ fgGetArgParameterLclNum(recursiveTailCall, &arg), block,
+ callDI, tmpAssignmentInsertionPoint,
+ paramAssignmentInsertionPoint);
+ if ((tmpAssignmentInsertionPoint == lastStmt) && (paramAssignStmt != nullptr))
{
- Statement* paramAssignStmt =
- fgAssignRecursiveCallArgToCallerParam(earlyArg, &arg,
- fgGetArgParameterLclNum(recursiveTailCall, &arg), block,
- callDI, tmpAssignmentInsertionPoint,
- paramAssignmentInsertionPoint);
- if ((tmpAssignmentInsertionPoint == lastStmt) && (paramAssignStmt != nullptr))
- {
- // All temp assignments will happen before the first param assignment.
- tmpAssignmentInsertionPoint = paramAssignStmt;
- }
+ // All temp assignments will happen before the first param assignment.
+ tmpAssignmentInsertionPoint = paramAssignStmt;
}
}
}
// Either or both of the array and index arguments may have been spilled to temps by `fgMorphArgs`. Copy
// the spill trees as well if necessary.
GenTreeOp* argSetup = nullptr;
- for (CallArg& arg : call->gtArgs.Args())
+ for (CallArg& arg : call->gtArgs.EarlyArgs())
{
GenTree* const argNode = arg.GetEarlyNode();
if (argNode->OperGet() != GT_ASG)
}
break;
- case GT_ARGPLACE:
- // Remove argplace and list nodes from the execution order.
- BlockRange().Remove(node);
- break;
-
#if defined(TARGET_XARCH) || defined(TARGET_ARM)
case GT_CLS_VAR:
{
m_lclVarReads.Add(compiler, lclNum);
}
- if (!operand->IsArgPlaceHolderNode() && operand->isContained())
+ if (operand->isContained())
{
AddNode(compiler, operand);
}
tree->gtVNPair = vnStore->VNPForVoid();
break;
- case GT_ARGPLACE:
- // This node is a standin for an argument whose value will be computed later. (Perhaps it's
- // a register argument, and we don't want to preclude use of the register in arg evaluation yet.)
- // We defer giving this a value number now; we'll reset it later, when numbering the call.
- break;
-
case GT_PHI_ARG:
// This one is special because we should never process it in this method: it should
// always be taken care of, when needed, during pre-processing of a blocks phi definitions.
case VNF_JitNewArr:
{
- generateUniqueVN = true;
- // TODO-ARGS: Should this really be early arg?
- ValueNumPair vnp1 = vnStore->VNPNormalPair(args->GetArgByIndex(1)->GetEarlyNode()->gtVNPair);
+ generateUniqueVN = true;
+ ValueNumPair vnp1 = vnStore->VNPNormalPair(args->GetArgByIndex(1)->GetNode()->gtVNPair);
// The New Array helper may throw an overflow exception
vnpExc = vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_NewArrOverflowExc, vnp1));
case VNF_JitReadyToRunNewArr:
{
generateUniqueVN = true;
- ValueNumPair vnp1 = vnStore->VNPNormalPair(args->GetArgByIndex(0)->GetEarlyNode()->gtVNPair);
+ ValueNumPair vnp1 = vnStore->VNPNormalPair(args->GetArgByIndex(0)->GetNode()->gtVNPair);
// The New Array helper may throw an overflow exception
vnpExc = vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_NewArrOverflowExc, vnp1));
if (call->IsR2RRelativeIndir())
{
#ifdef DEBUG
- assert(args->GetArgByIndex(0)->GetEarlyNode()->OperGet() == GT_ARGPLACE);
-
- // Find the corresponding late arg.
GenTree* indirectCellAddress = args->GetArgByIndex(0)->GetNode();
assert(indirectCellAddress->IsCnsIntOrI() && indirectCellAddress->GetRegNum() == REG_R2R_INDIRECT_PARAM);
#endif // DEBUG
void Compiler::fgValueNumberCall(GenTreeCall* call)
{
- // First: do value numbering of any argument placeholder nodes in the argument list
- // (by transferring from the VN of the late arg that they are standing in for...)
-
- for (CallArg& arg : call->gtArgs.LateArgs())
- {
- if (arg.GetEarlyNode()->OperIs(GT_ARGPLACE))
- {
- assert(arg.GetLateNode()->gtVNPair.BothDefined());
- arg.GetEarlyNode()->gtVNPair = arg.GetLateNode()->gtVNPair;
-#ifdef DEBUG
- if (verbose)
- {
- printf("VN of ARGPLACE tree ");
- Compiler::printTreeID(arg.GetEarlyNode());
- printf(" updated to ");
- vnpPrint(arg.GetEarlyNode()->gtVNPair, 1);
- printf("\n");
- }
-#endif
- }
- }
-
if (call->gtCallType == CT_HELPER)
{
bool modHeap = fgValueNumberHelperCall(call);