From bb6954e4520c06ddb38fbc738cae9e90db0c4e94 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 23 Apr 2022 20:58:40 +0200 Subject: [PATCH] Remove GT_ARGPLACE nodes (#68140) These do not serve much purpose today -- instead just use null and add a helper function to iterate non-null early args, which is somewhat common. In addition to saving some TP and memory, teaching the backend about null early nodes will also be beneficial because I am planning to change rationalization to null out non-values in the early arg list so that all nodes have only values as their operands in LIR. Throughput diff: ``` Collection Base # instructions Diff # instructions PDIFF aspnet.run.windows.x64.checked.mch 69,717,468,395 69,206,312,087 -0.73% benchmarks.run.windows.x64.checked.mch 54,695,846,729 54,294,078,768 -0.73% coreclr_tests.pmi.windows.x64.checked.mch 340,169,515,528 337,478,749,067 -0.79% libraries.crossgen2.windows.x64.checked.mch 128,653,906,043 126,926,566,191 -1.34% libraries.pmi.windows.x64.checked.mch 228,653,702,806 226,554,618,843 -0.92% libraries_tests.pmi.windows.x64.checked.mch 531,053,530,645 525,233,144,101 -1.10% ``` Memory stats (libraries.pmi) Before: 25961399533 bytes After: 25770612141 bytes (-0.7%) --- docs/design/coreclr/jit/jit-call-morphing.md | 8 +- src/coreclr/jit/codegenarmarch.cpp | 7 - src/coreclr/jit/codegenlinear.cpp | 7 - src/coreclr/jit/codegenloongarch64.cpp | 7 - src/coreclr/jit/codegenxarch.cpp | 2 +- src/coreclr/jit/compiler.h | 5 +- src/coreclr/jit/compiler.hpp | 12 +- src/coreclr/jit/fgdiagnostic.cpp | 6 +- src/coreclr/jit/fgprofile.cpp | 1 + src/coreclr/jit/flowgraph.cpp | 27 ++-- src/coreclr/jit/forwardsub.cpp | 2 +- src/coreclr/jit/gentree.cpp | 117 ++++++-------- src/coreclr/jit/gentree.h | 83 ++++++---- src/coreclr/jit/gschecks.cpp | 17 ++- src/coreclr/jit/gtlist.h | 2 - src/coreclr/jit/gtstructs.h | 1 - src/coreclr/jit/importer.cpp | 4 - src/coreclr/jit/importer_vectorization.cpp | 2 +- src/coreclr/jit/indirectcalltransformer.cpp | 10 +- src/coreclr/jit/lclmorph.cpp | 4 +- src/coreclr/jit/lir.cpp | 16 +- src/coreclr/jit/lower.cpp | 21 +-- src/coreclr/jit/lowerxarch.cpp | 2 +- src/coreclr/jit/lsra.cpp | 4 - src/coreclr/jit/lsraarm.cpp | 1 - src/coreclr/jit/lsraarm64.cpp | 1 - src/coreclr/jit/lsraarmarch.cpp | 2 +- src/coreclr/jit/lsrabuild.cpp | 9 -- src/coreclr/jit/lsraloongarch64.cpp | 3 +- src/coreclr/jit/lsraxarch.cpp | 1 - src/coreclr/jit/morph.cpp | 151 +++++++++---------- src/coreclr/jit/rationalize.cpp | 5 - src/coreclr/jit/sideeffects.cpp | 2 +- src/coreclr/jit/valuenum.cpp | 38 +---- 34 files changed, 230 insertions(+), 350 deletions(-) diff --git a/docs/design/coreclr/jit/jit-call-morphing.md b/docs/design/coreclr/jit/jit-call-morphing.md index e39b2edf70d88..5fa8ab1a03925 100644 --- a/docs/design/coreclr/jit/jit-call-morphing.md +++ b/docs/design/coreclr/jit/jit-call-morphing.md @@ -144,11 +144,9 @@ For arguments that are marked as not needing a temp: ----------------- 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. diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 4a32960a6187d..577b046aa088c 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -5478,13 +5478,6 @@ unsigned CodeGenInterface::InferStructOpSizeAlign(GenTree* op, unsigned* alignme 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"); diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 83efe5685c3ba..2eb21176ba1d2 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -1328,13 +1328,6 @@ void CodeGen::genConsumeRegAndCopy(GenTree* node, regNumber needReg) 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()) diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index 2fb20f3e2835b..0b813a93138c6 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -9343,13 +9343,6 @@ unsigned CodeGenInterface::InferStructOpSizeAlign(GenTree* op, unsigned* alignme 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"); diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 1c5cda19da022..32707b1528b4e 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -5377,7 +5377,7 @@ void CodeGen::genCall(GenTreeCall* call) // 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)) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index b85d269739158..07fbe157a92ce 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2605,8 +2605,6 @@ class Compiler GenTree* gtNewNothingNode(); - GenTree* gtNewArgPlaceHolderNode(var_types type, CORINFO_CLASS_HANDLE clsHnd); - GenTree* gtUnusedValNode(GenTree* expr); GenTree* gtNewKeepAliveNode(GenTree* op); @@ -10651,7 +10649,6 @@ class GenTreeVisitor 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: @@ -10838,7 +10835,7 @@ class GenTreeVisitor { 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) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index f3938b37beaf5..c1040b1f3c275 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -1256,15 +1256,6 @@ inline void GenTree::gtBashToNOP() 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) @@ -4208,7 +4199,6 @@ void GenTree::VisitOperands(TVisitor visitor) 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: @@ -4370,7 +4360,7 @@ void GenTree::VisitOperands(TVisitor visitor) { GenTreeCall* const call = this->AsCall(); - for (CallArg& arg : call->gtArgs.Args()) + for (CallArg& arg : call->gtArgs.EarlyArgs()) { if (visitor(arg.GetEarlyNode()) == VisitResult::Abort) { diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 6328919219e7e..320806dda2a53 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -3047,7 +3047,11 @@ void Compiler::fgDebugCheckFlags(GenTree* tree) { // 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; diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index a63d12682276f..d17d9328a63cf 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -1560,6 +1560,7 @@ class ClassProbeInserter 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)) { diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index a2d5cb585214f..8f3612a76ac9f 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -1057,7 +1057,8 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* 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; @@ -1071,7 +1072,7 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, 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) { @@ -1110,7 +1111,7 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, 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); } @@ -1142,8 +1143,8 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, { 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); @@ -1175,8 +1176,8 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, { 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; @@ -3852,10 +3853,8 @@ BasicBlock* Compiler::fgRngChkTarget(BasicBlock* block, SpecialCodeKind kind) // // 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. @@ -3887,12 +3886,6 @@ GenTree* Compiler::fgSetTreeSeq(GenTree* tree, bool isLIR) 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; diff --git a/src/coreclr/jit/forwardsub.cpp b/src/coreclr/jit/forwardsub.cpp index 935070cd3ba51..3620204491bde 100644 --- a/src/coreclr/jit/forwardsub.cpp +++ b/src/coreclr/jit/forwardsub.cpp @@ -288,7 +288,7 @@ class ForwardSubVisitor final : public GenTreeVisitor 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) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index f4bcbb5157bfc..64b87366b3229 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -330,7 +330,6 @@ void GenTree::InitNodeSize() 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 @@ -2204,22 +2203,7 @@ bool GenTreeCall::Equals(GenTreeCall* c1, GenTreeCall* c2) { 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; @@ -2271,7 +2255,7 @@ void CallArgs::ResetFinalArgsAndABIInfo() 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(); } @@ -2425,14 +2409,6 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK) case GT_LABEL: return true; - case GT_ARGPLACE: - if ((op1->gtType == TYP_STRUCT) && - (op1->AsArgPlace()->gtArgPlaceClsHnd != op2->AsArgPlace()->gtArgPlaceClsHnd)) - { - break; - } - return true; - default: break; } @@ -3050,7 +3026,15 @@ unsigned Compiler::gtHashValue(GenTree* tree) 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) @@ -3064,10 +3048,6 @@ unsigned Compiler::gtHashValue(GenTree* tree) 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) @@ -3417,7 +3397,7 @@ unsigned Compiler::gtSetCallArgsOrder(CallArgs* args, bool lateArgs, int* callCo } else { - for (CallArg& arg : args->Args()) + for (CallArg& arg : args->EarlyArgs()) { GenTree* node = arg.GetEarlyNode(); unsigned level = gtSetEvalOrder(node); @@ -3433,6 +3413,13 @@ unsigned Compiler::gtSetCallArgsOrder(CallArgs* args, bool lateArgs, int* callCo 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; @@ -4662,7 +4649,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) break; case GT_PHI_ARG: - case GT_ARGPLACE: level = 0; costEx = 0; costSz = 0; @@ -5906,7 +5892,6 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) 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: @@ -8224,10 +8209,6 @@ GenTree* Compiler::gtCloneExpr( 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); @@ -9153,7 +9134,6 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) 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: @@ -9556,12 +9536,16 @@ void GenTreeUseEdgeIterator::AdvanceCall() switch (state) { case CALL_ARGS: - if (m_statePtr != nullptr) + while (m_statePtr != nullptr) { CallArg* arg = static_cast(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; @@ -9572,7 +9556,8 @@ void GenTreeUseEdgeIterator::AdvanceCall() { CallArg* arg = static_cast(m_statePtr); m_edge = &arg->LateNodeRef(); - m_statePtr = arg->GetLateNext(); + assert(*m_edge != nullptr); + m_statePtr = arg->GetLateNext(); return; } m_advance = &GenTreeUseEdgeIterator::AdvanceCall; @@ -10553,11 +10538,6 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, _In_ _In_opt_ } } - if (tree->IsArgPlaceHolderNode() && (tree->AsArgPlace()->gtArgPlaceClsHnd != nullptr)) - { - printf(" => [clsHnd=%08X]", dspPtr(tree->AsArgPlace()->gtArgPlaceClsHnd)); - } - if (tree->gtOper == GT_RUNTIMELOOKUP) { #ifdef TARGET_64BIT @@ -11347,7 +11327,6 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) case GT_PROF_HOOK: case GT_CATCH_ARG: case GT_MEMORYBARRIER: - case GT_ARGPLACE: case GT_PINVOKE_PROLOG: case GT_JMPTABLE: break; @@ -12182,15 +12161,12 @@ void Compiler::gtGetLateArgMsg(GenTreeCall* call, CallArg* arg, char* bufp, unsi // 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); } } @@ -12342,7 +12318,7 @@ void Compiler::gtDispLIRNode(GenTree* node, const char* prefixMsg /* = nullptr * 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; @@ -12549,8 +12525,8 @@ GenTree* Compiler::gtFoldExprCall(GenTreeCall* call) { 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) @@ -12564,8 +12540,8 @@ GenTree* Compiler::gtFoldExprCall(GenTreeCall* call) 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); @@ -12807,8 +12783,8 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree) 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; @@ -13464,12 +13440,18 @@ GenTree* Compiler::gtFoldBoxNullable(GenTree* tree) 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); @@ -15460,12 +15442,12 @@ bool Compiler::gtNodeHasSideEffects(GenTree* tree, GenTreeFlags flags) { // 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; } @@ -17206,9 +17188,6 @@ CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleIfPresent(GenTree* tree) 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; diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 0fa58b87233e4..82c16748f86e3 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -2125,10 +2125,6 @@ struct GenTree return t1->GetIconHandleFlag() == t2->GetIconHandleFlag(); } - bool IsArgPlaceHolderNode() const - { - return OperGet() == GT_ARGPLACE; - } bool IsCall() const { return OperGet() == GT_CALL; @@ -4405,33 +4401,68 @@ class CallArgs { } - 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; } @@ -4445,6 +4476,12 @@ class CallArgs return IteratorPair(ArgIterator(m_head), ArgIterator(nullptr)); } + IteratorPair EarlyArgs() + { + CallArg* firstEarlyArg = EarlyArgIterator::NextEarlyArg(m_head); + return IteratorPair(EarlyArgIterator(firstEarlyArg), EarlyArgIterator(nullptr)); + } + IteratorPair LateArgs() { return IteratorPair(LateArgIterator(m_lateHead), LateArgIterator(nullptr)); @@ -5150,7 +5187,7 @@ struct GenTreeCall final : public GenTree CallArg* retBufArg = gtArgs.GetRetBufferArg(); GenTree* lclRetBufArgNode = retBufArg->GetEarlyNode(); - if (lclRetBufArgNode->IsArgPlaceHolderNode()) + if (lclRetBufArgNode == nullptr) { lclRetBufArgNode = retBufArg->GetLateNode(); } @@ -7150,22 +7187,6 @@ struct GenTreeClsVar : public GenTree #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 { diff --git a/src/coreclr/jit/gschecks.cpp b/src/coreclr/jit/gschecks.cpp index 977a926fb3e61..0ce7554d590b3 100644 --- a/src/coreclr/jit/gschecks.cpp +++ b/src/coreclr/jit/gschecks.cpp @@ -199,13 +199,20 @@ Compiler::fgWalkResult Compiler::gsMarkPtrsAndAssignGroups(GenTree** pTree, fgWa 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()) diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index dc067b444146f..118708981ccac 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -39,8 +39,6 @@ GTNODE(FTN_ADDR , GenTreeFptrVal ,0,GTK_LEAF) // Address 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: //----------------------------------------------------------------------------- diff --git a/src/coreclr/jit/gtstructs.h b/src/coreclr/jit/gtstructs.h index 2507c633be15c..0a34d379bd272 100644 --- a/src/coreclr/jit/gtstructs.h +++ b/src/coreclr/jit/gtstructs.h @@ -89,7 +89,6 @@ GTSTRUCT_1(RetExpr , GT_RET_EXPR) 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) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index d5b9fcb728694..9f8ace666f09c 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1727,10 +1727,6 @@ GenTree* Compiler::impNormStructVal(GenTree* structVal, makeTemp = true; break; - case GT_ARGPLACE: - structVal->AsArgPlace()->gtArgPlaceClsHnd = structHnd; - break; - case GT_INDEX: // This will be transformed to an OBJ later. alreadyNormalized = true; diff --git a/src/coreclr/jit/importer_vectorization.cpp b/src/coreclr/jit/importer_vectorization.cpp index d947ee4ea4fe3..7d1b0abe5476d 100644 --- a/src/coreclr/jit/importer_vectorization.cpp +++ b/src/coreclr/jit/importer_vectorization.cpp @@ -583,7 +583,7 @@ GenTreeStrCon* Compiler::impGetStrConFromSpan(GenTree* span) 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(); diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp index 6fc0d1a27c72c..a76bac72b8d8b 100644 --- a/src/coreclr/jit/indirectcalltransformer.cpp +++ b/src/coreclr/jit/indirectcalltransformer.cpp @@ -539,7 +539,7 @@ class IndirectCallTransformer 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()) @@ -684,7 +684,7 @@ class IndirectCallTransformer // 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); @@ -1107,13 +1107,13 @@ class IndirectCallTransformer 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); } @@ -1130,7 +1130,7 @@ class IndirectCallTransformer // 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); } diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 2e4eaf6f9b218..dae0be627ea7e 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -678,7 +678,7 @@ class LocalAddressVisitor final : public GenTreeVisitor // 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; @@ -687,7 +687,7 @@ class LocalAddressVisitor final : public GenTreeVisitor 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); diff --git a/src/coreclr/jit/lir.cpp b/src/coreclr/jit/lir.cpp index bce78a242f731..212861a1e5861 100644 --- a/src/coreclr/jit/lir.cpp +++ b/src/coreclr/jit/lir.cpp @@ -1178,12 +1178,6 @@ LIR::ReadOnlyRange LIR::Range::GetMarkedRange(unsigned markCount, // 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; @@ -1435,12 +1429,6 @@ class CheckLclVarSemanticsHelper { 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); @@ -1571,10 +1559,8 @@ bool LIR::Range::CheckLIR(Compiler* compiler, bool checkUnusedValues) const // 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; diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 01296b929fd27..87ff5a1d2aa6b 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -1354,8 +1354,7 @@ void Lowering::LowerArg(GenTreeCall* call, CallArg* callArg, bool late) // 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; } @@ -1577,7 +1576,7 @@ GenTree* Lowering::LowerFloatArgReg(GenTree* arg, regNumber regNum) 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); } @@ -1791,7 +1790,7 @@ void Lowering::InsertProfTailCallHook(GenTreeCall* call, GenTree* insertionPoint 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 @@ -1900,7 +1899,7 @@ void Lowering::LowerFastTailCall(GenTreeCall* call) // call could over-write the stack arg that is setup earlier. ArrayStack putargs(comp->getAllocator(CMK_ArrayStack)); - for (CallArg& arg : call->gtArgs.Args()) + for (CallArg& arg : call->gtArgs.EarlyArgs()) { if (arg.GetEarlyNode()->OperIs(GT_PUTARG_STK)) { @@ -2372,7 +2371,7 @@ void Lowering::LowerCFGCall(GenTreeCall* call) 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. @@ -2396,9 +2395,7 @@ void Lowering::LowerCFGCall(GenTreeCall* call) #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); @@ -2409,7 +2406,6 @@ void Lowering::LowerCFGCall(GenTreeCall* call) 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 @@ -6429,8 +6425,7 @@ void Lowering::CheckCallArg(GenTree* arg) { if (!arg->IsValue() && !arg->OperIsPutArgStk()) { - assert((arg->OperIsStore() && !arg->IsValue()) || arg->IsArgPlaceHolderNode() || arg->IsNothingNode() || - arg->OperIsCopyBlkOp()); + assert(arg->OperIsStore() || arg->OperIsCopyBlkOp()); return; } @@ -6465,7 +6460,7 @@ void Lowering::CheckCallArg(GenTree* arg) // void Lowering::CheckCall(GenTreeCall* call) { - for (CallArg& arg : call->gtArgs.Args()) + for (CallArg& arg : call->gtArgs.EarlyArgs()) { CheckCallArg(arg.GetEarlyNode()); } diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 853121bf6a2b5..c443127b8dbd1 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -4476,7 +4476,7 @@ void Lowering::ContainCheckCallOperands(GenTreeCall* call) } } - for (CallArg& arg : call->gtArgs.Args()) + for (CallArg& arg : call->gtArgs.EarlyArgs()) { if (arg.GetEarlyNode()->OperIs(GT_PUTARG_STK)) { diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index fe41bd977baa9..455a1c2502276 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -9428,10 +9428,6 @@ void LinearScan::DumpOperandDefs( { assert(operand != nullptr); assert(operandString != nullptr); - if (operand->OperIs(GT_ARGPLACE)) - { - return; - } int dstCount = ComputeOperandDstCount(operand); diff --git a/src/coreclr/jit/lsraarm.cpp b/src/coreclr/jit/lsraarm.cpp index 090780f49f9c1..e09295a603dcb 100644 --- a/src/coreclr/jit/lsraarm.cpp +++ b/src/coreclr/jit/lsraarm.cpp @@ -433,7 +433,6 @@ int LinearScan::BuildNode(GenTree* tree) srcCount = 0; break; - case GT_ARGPLACE: case GT_NO_OP: case GT_START_NONGC: case GT_PROF_HOOK: diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp index 9ab93ab7b6eb1..9af43e97ebe16 100644 --- a/src/coreclr/jit/lsraarm64.cpp +++ b/src/coreclr/jit/lsraarm64.cpp @@ -121,7 +121,6 @@ int LinearScan::BuildNode(GenTree* tree) srcCount = 0; break; - case GT_ARGPLACE: case GT_NO_OP: case GT_START_NONGC: srcCount = 0; diff --git a/src/coreclr/jit/lsraarmarch.cpp b/src/coreclr/jit/lsraarmarch.cpp index 7ce916d0a305c..e181b189788a5 100644 --- a/src/coreclr/jit/lsraarmarch.cpp +++ b/src/coreclr/jit/lsraarmarch.cpp @@ -339,7 +339,7 @@ int LinearScan::BuildCall(GenTreeCall* call) // 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(); diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index 19195e136ed63..9c1f347ef9336 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -1641,13 +1641,6 @@ void LinearScan::buildUpperVectorRestoreRefPosition(Interval* lclVarInterval, // 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; @@ -1715,9 +1708,7 @@ int LinearScan::ComputeAvailableSrcCount(GenTree* node) // 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 diff --git a/src/coreclr/jit/lsraloongarch64.cpp b/src/coreclr/jit/lsraloongarch64.cpp index 577b6dc03fc68..43e0cacf37cdc 100644 --- a/src/coreclr/jit/lsraloongarch64.cpp +++ b/src/coreclr/jit/lsraloongarch64.cpp @@ -120,7 +120,6 @@ int LinearScan::BuildNode(GenTree* tree) srcCount = 0; break; - case GT_ARGPLACE: case GT_NO_OP: case GT_START_NONGC: srcCount = 0; @@ -918,7 +917,7 @@ int LinearScan::BuildCall(GenTreeCall* call) // 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(); diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 08729f53e67fa..6f19f0f811e98 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -124,7 +124,6 @@ int LinearScan::BuildNode(GenTree* tree) srcCount = 0; break; - case GT_ARGPLACE: case GT_NO_OP: case GT_START_NONGC: srcCount = 0; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index facba7b6a4e76..6ce7e9bfb0e9b 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -635,7 +635,7 @@ const char* getWellKnownArgName(WellKnownArg arg) // 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) @@ -748,6 +748,13 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call) { 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); @@ -810,7 +817,7 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call) break; } - if (!prevArg.GetEarlyNode()->IsInvariant()) + if ((prevArg.GetEarlyNode() != nullptr) && !prevArg.GetEarlyNode()->IsInvariant()) { SetNeedsTemp(&prevArg); } @@ -875,7 +882,7 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call) // 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); } @@ -1016,7 +1023,7 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call) // if (hasStackArgsWeCareAbout || hasStructRegArgWeCareAbout) { - for (CallArg& arg : Args()) + for (CallArg& arg : EarlyArgs()) { GenTree* argx = arg.GetEarlyNode(); @@ -1080,7 +1087,7 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call) assert(HasThisPointer()); SetNeedsTemp(GetThisArg()); - for (CallArg& arg : Args()) + for (CallArg& arg : EarlyArgs()) { if ((arg.GetEarlyNode()->gtFlags & GTF_ALL_EFFECT) != 0) { @@ -1168,6 +1175,7 @@ void CallArgs::SortArgs(Compiler* comp, GenTreeCall* call, CallArg** sortedArgs) { GenTree* argx = arg->GetEarlyNode(); + assert(argx != nullptr); // put constants at the end of the table // if (argx->gtOper == GT_CNS_INT) @@ -1204,6 +1212,7 @@ void CallArgs::SortArgs(Compiler* comp, GenTreeCall* call, CallArg** sortedArgs) if (!arg->m_processed) { GenTree* argx = arg->GetEarlyNode(); + assert(argx != nullptr); // put calls at the beginning of the table // @@ -1280,6 +1289,7 @@ void CallArgs::SortArgs(Compiler* comp, GenTreeCall* call, CallArg** sortedArgs) if (!arg->m_processed) { GenTree* argx = arg->GetEarlyNode(); + assert(argx != nullptr); if ((argx->gtOper == GT_LCL_VAR) || (argx->gtOper == GT_LCL_FLD)) { @@ -1324,6 +1334,7 @@ void CallArgs::SortArgs(Compiler* comp, GenTreeCall* call, CallArg** sortedArgs) 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); @@ -1489,7 +1500,7 @@ GenTree* CallArgs::MakeTmpArgNode(Compiler* comp, CallArg* arg) // 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) @@ -1518,16 +1529,15 @@ GenTree* CallArgs::MakeTmpArgNode(Compiler* comp, CallArg* arg) // 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: +// 2. Early: nullptr, Late: // 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: , Late: no node +// 3. Early: , 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. // @@ -1547,7 +1557,9 @@ void CallArgs::EvalArgsToTemps(Compiler* comp, GenTreeCall* call) CallArg& arg = *(sortedArgs[i]); assert(arg.GetLateNode() == nullptr); - GenTree* argx = arg.GetEarlyNode(); + GenTree* argx = arg.GetEarlyNode(); + assert(argx != nullptr); + GenTree* setupArg = nullptr; GenTree* defArg; @@ -1728,38 +1740,18 @@ void CallArgs::EvalArgsToTemps(Compiler* comp, GenTreeCall* call) 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) { @@ -1773,15 +1765,15 @@ void CallArgs::EvalArgsToTemps(Compiler* comp, GenTreeCall* call) } 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); } @@ -1971,7 +1963,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call 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()) @@ -2107,10 +2099,9 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call 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 @@ -2210,7 +2201,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call 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(); @@ -2225,6 +2216,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call 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 @@ -2235,9 +2227,6 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call 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; @@ -3148,8 +3137,15 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) // 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) { @@ -3191,7 +3187,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) 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; @@ -3641,7 +3637,7 @@ void Compiler::fgMorphMultiregStructArgs(GenTreeCall* call) 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) @@ -6440,12 +6436,12 @@ bool Compiler::fgCallHasMustCopyByrefParameter(GenTreeCall* callee) 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 @@ -6906,7 +6902,7 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call) 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; @@ -6980,7 +6976,7 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call) 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. // @@ -7669,7 +7665,7 @@ GenTree* Compiler::fgCreateCallDispatcherAndGetResult(GenTreeCall* orig 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()); @@ -8052,7 +8048,7 @@ void Compiler::fgMorphTailCallViaJitHelper(GenTreeCall* call) if (thisArg != nullptr) { GenTree* thisPtr = nullptr; - GenTree* objp = thisArg->GetEarlyNode(); + GenTree* objp = thisArg->GetNode(); if ((call->IsDelegateInvoke() || call->IsVirtualVtable()) && !objp->OperIs(GT_LCL_VAR)) { @@ -8279,35 +8275,30 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa 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; } } } @@ -8700,7 +8691,7 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) // 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) diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp index c431596bcd4f0..bc0032e19b696 100644 --- a/src/coreclr/jit/rationalize.cpp +++ b/src/coreclr/jit/rationalize.cpp @@ -680,11 +680,6 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge } 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: { diff --git a/src/coreclr/jit/sideeffects.cpp b/src/coreclr/jit/sideeffects.cpp index 45b63b9339c65..5aaa877e594d8 100644 --- a/src/coreclr/jit/sideeffects.cpp +++ b/src/coreclr/jit/sideeffects.cpp @@ -296,7 +296,7 @@ void AliasSet::AddNode(Compiler* compiler, GenTree* node) m_lclVarReads.Add(compiler, lclNum); } - if (!operand->IsArgPlaceHolderNode() && operand->isContained()) + if (operand->isContained()) { AddNode(compiler, operand); } diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 3b4c006c86feb..373848f7f24f5 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -8570,12 +8570,6 @@ void Compiler::fgValueNumberTree(GenTree* tree) 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. @@ -9692,9 +9686,8 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN 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)); @@ -9731,7 +9724,7 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN 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)); @@ -9772,9 +9765,6 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN 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 @@ -9883,28 +9873,6 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN 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);