diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index c9e18626699d41..aaa70bb20ae5fd 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3911,8 +3911,10 @@ class Compiler void impPopCallArgs(CORINFO_SIG_INFO* sig, GenTreeCall* call); - bool impCheckImplicitArgumentCoercion(var_types sigType, var_types nodeType) const; +public: + static bool impCheckImplicitArgumentCoercion(var_types sigType, var_types nodeType); +private: void impPopReverseCallArgs(CORINFO_SIG_INFO* sig, GenTreeCall* call, unsigned skipReverseCount); //---------------- Spilling the importer stack ---------------------------- @@ -4136,10 +4138,7 @@ class Compiler InlineCandidateInfo** ppInlineCandidateInfo, InlineResult* inlineResult); - void impInlineRecordArgInfo(InlineInfo* pInlineInfo, - GenTree* curArgVal, - unsigned argNum, - InlineResult* inlineResult); + void impInlineRecordArgInfo(InlineInfo* pInlineInfo, CallArg* arg, unsigned argNum, InlineResult* inlineResult); void impInlineInitVars(InlineInfo* pInlineInfo); @@ -4458,8 +4457,6 @@ class Compiler BasicBlock* canonicalBlock, flowList* predEdge); - GenTree* fgCheckCallArgUpdate(GenTree* parent, GenTree* child, var_types origType); - #if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM) // Sometimes we need to defer updating the BBF_FINALLY_TARGET bit. fgNeedToAddFinallyTargetBits signals // when this is necessary. @@ -10729,7 +10726,6 @@ class GenTreeVisitor case GT_NULLCHECK: case GT_PUTARG_REG: case GT_PUTARG_STK: - case GT_PUTARG_TYPE: case GT_RETURNTRAP: case GT_NOP: case GT_FIELD: diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 8eab3eb4fb1950..21551a7fcce113 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -991,11 +991,11 @@ inline GenTree* Compiler::gtNewIconEmbFldHndNode(CORINFO_FIELD_HANDLE fldHnd) // Arguments: // helper - Call helper // type - Type of the node -// args - Call args +// args - Call args (struct args not supported) // // Return Value: // New CT_HELPER node - +// inline GenTreeCall* Compiler::gtNewHelperCallNode( unsigned helper, var_types type, GenTree* arg1, GenTree* arg2, GenTree* arg3) { @@ -1011,19 +1011,19 @@ inline GenTreeCall* Compiler::gtNewHelperCallNode( if (arg3 != nullptr) { - result->gtArgs.PushFront(this, arg3); + result->gtArgs.PushFront(this, NewCallArg::Primitive(arg3)); result->gtFlags |= arg3->gtFlags & GTF_ALL_EFFECT; } if (arg2 != nullptr) { - result->gtArgs.PushFront(this, arg2); + result->gtArgs.PushFront(this, NewCallArg::Primitive(arg2)); result->gtFlags |= arg2->gtFlags & GTF_ALL_EFFECT; } if (arg1 != nullptr) { - result->gtArgs.PushFront(this, arg1); + result->gtArgs.PushFront(this, NewCallArg::Primitive(arg1)); result->gtFlags |= arg1->gtFlags & GTF_ALL_EFFECT; } @@ -4265,7 +4265,6 @@ void GenTree::VisitOperands(TVisitor visitor) case GT_NULLCHECK: case GT_PUTARG_REG: case GT_PUTARG_STK: - case GT_PUTARG_TYPE: #if FEATURE_ARG_SPLIT case GT_PUTARG_SPLIT: #endif // FEATURE_ARG_SPLIT diff --git a/src/coreclr/jit/decomposelongs.cpp b/src/coreclr/jit/decomposelongs.cpp index ae7a6b04777b06..a4267ae1d685a9 100644 --- a/src/coreclr/jit/decomposelongs.cpp +++ b/src/coreclr/jit/decomposelongs.cpp @@ -1355,10 +1355,11 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) unreached(); } - GenTreeCall* call = m_compiler->gtNewHelperCallNode(helper, TYP_LONG); - call->gtArgs.PushFront(m_compiler, shiftByOp); - call->gtArgs.PushFront(m_compiler, hiOp1, WellKnownArg::ShiftHigh); - call->gtArgs.PushFront(m_compiler, loOp1, WellKnownArg::ShiftLow); + GenTreeCall* call = m_compiler->gtNewHelperCallNode(helper, TYP_LONG); + NewCallArg loArg = NewCallArg::Primitive(loOp1).WellKnown(WellKnownArg::ShiftLow); + NewCallArg hiArg = NewCallArg::Primitive(hiOp1).WellKnown(WellKnownArg::ShiftHigh); + NewCallArg shiftByArg = NewCallArg::Primitive(shiftByOp); + call->gtArgs.PushFront(m_compiler, loArg, hiArg, shiftByArg); call->gtFlags |= shift->gtFlags & GTF_ALL_EFFECT; if (shift->IsUnusedValue()) diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index c6eda94488811d..ec7ca643d0b0dc 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -540,15 +540,6 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr JITDUMP("Updating type of the return GT_IND expression to TYP_BYREF\n"); inlineCandidate->gtType = TYP_BYREF; } - else - { - // - under a call if we changed size of the argument. - GenTree* putArgType = comp->fgCheckCallArgUpdate(data->parent, inlineCandidate, retType); - if (putArgType != nullptr) - { - inlineCandidate = putArgType; - } - } } tree->ReplaceWith(inlineCandidate, comp); @@ -824,14 +815,8 @@ Compiler::fgWalkResult Compiler::fgLateDevirtualization(GenTree** pTree, fgWalkD { const var_types retType = tree->TypeGet(); GenTree* foldedTree = comp->gtFoldExpr(tree); - - GenTree* putArgType = comp->fgCheckCallArgUpdate(data->parent, foldedTree, retType); - if (putArgType != nullptr) - { - foldedTree = putArgType; - } - *pTree = foldedTree; - *madeChanges = true; + *pTree = foldedTree; + *madeChanges = true; } return WALK_CONTINUE; @@ -1538,11 +1523,21 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) { const InlArgInfo& argInfo = inlArgInfo[argNum]; const bool argIsSingleDef = !argInfo.argHasLdargaOp && !argInfo.argHasStargOp; - GenTree* argNode = inlArgInfo[argNum].argNode; - const bool argHasPutArg = argNode->OperIs(GT_PUTARG_TYPE); + CallArg* arg = argInfo.arg; + GenTree* argNode = arg->GetNode(); + + // TODO-ARGS: This can probably be relaxed, the old comment was: + // argHasPutArg disqualifies the arg from a direct substitution because we don't have information about + // its user. For example: replace `LCL_VAR short` with `PUTARG_TYPE short->LCL_VAR int`, + // we should keep `PUTARG_TYPE` iff the user is a call that needs `short` and delete it otherwise. + // + // Today we no longer have this contextual PUTARG_TYPE and morph + // should properly handle substituting a TYP_INT node for a + // TYP_SHORT LCL_VAR (at least for a call arg). + bool argHasPutArg = !varTypeIsStruct(arg->GetSignatureType()) && + (genTypeSize(argNode) != genTypeSize(arg->GetSignatureType())); BasicBlockFlags bbFlags = BBF_EMPTY; - argNode = argNode->gtSkipPutArgType(); argNode = argNode->gtRetExprVal(&bbFlags); if (argInfo.argHasTmp) @@ -1563,9 +1558,6 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) GenTree* argSingleUseNode = argInfo.argBashTmpNode; - // argHasPutArg disqualifies the arg from a direct substitution because we don't have information about - // its user. For example: replace `LCL_VAR short` with `PUTARG_TYPE short->LCL_VAR int`, - // we should keep `PUTARG_TYPE` iff the user is a call that needs `short` and delete it otherwise. if ((argSingleUseNode != nullptr) && !(argSingleUseNode->gtFlags & GTF_VAR_CLONED) && argIsSingleDef && !argHasPutArg) { diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 1b55d27c54dd69..3bc6250b1d5fae 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -1216,19 +1216,19 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, if (ctorData.pArg3 != nullptr) { GenTree* arg3 = gtNewIconHandleNode(size_t(ctorData.pArg3), GTF_ICON_FTN_ADDR); - lastArg = call->gtArgs.PushBack(this, arg3); + lastArg = call->gtArgs.PushBack(this, NewCallArg::Primitive(arg3)); } if (ctorData.pArg4 != nullptr) { GenTree* arg4 = gtNewIconHandleNode(size_t(ctorData.pArg4), GTF_ICON_FTN_ADDR); - lastArg = call->gtArgs.InsertAfter(this, lastArg, arg4); + lastArg = call->gtArgs.InsertAfter(this, lastArg, NewCallArg::Primitive(arg4)); } if (ctorData.pArg5 != nullptr) { GenTree* arg5 = gtNewIconHandleNode(size_t(ctorData.pArg5), GTF_ICON_FTN_ADDR); - lastArg = call->gtArgs.InsertAfter(this, lastArg, arg5); + lastArg = call->gtArgs.InsertAfter(this, lastArg, NewCallArg::Primitive(arg5)); } } else @@ -4186,41 +4186,3 @@ void Compiler::fgLclFldAssign(unsigned lclNum) lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::LocalField)); } } - -//------------------------------------------------------------------------ -// fgCheckCallArgUpdate: check if we are replacing a call argument and add GT_PUTARG_TYPE if necessary. -// -// Arguments: -// parent - the parent that could be a call; -// child - the new child node; -// origType - the original child type; -// -// Returns: -// PUT_ARG_TYPE node if it is needed, nullptr otherwise. -// -GenTree* Compiler::fgCheckCallArgUpdate(GenTree* parent, GenTree* child, var_types origType) -{ - if ((parent == nullptr) || !parent->IsCall()) - { - return nullptr; - } - const var_types newType = child->TypeGet(); - if (newType == origType) - { - return nullptr; - } - if (varTypeIsStruct(origType) || (genTypeSize(origType) == genTypeSize(newType))) - { - assert(!varTypeIsStruct(newType)); - return nullptr; - } - GenTree* putArgType = gtNewOperNode(GT_PUTARG_TYPE, origType, child); -#if defined(DEBUG) - if (verbose) - { - printf("For call [%06d] the new argument's type [%06d]", dspTreeID(parent), dspTreeID(child)); - printf(" does not match the original type size, add a GT_PUTARG_TYPE [%06d]\n", dspTreeID(parent)); - } -#endif - return putArgType; -} diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index f904d6e2346389..2177b8e7381278 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -1171,6 +1171,23 @@ unsigned CallArgABIInformation::GetStackByteSize() const return stackByteSize; } +#ifdef DEBUG +void NewCallArg::ValidateTypes() +{ + assert(Compiler::impCheckImplicitArgumentCoercion(SignatureType, Node->TypeGet())); + + if (varTypeIsStruct(SignatureType)) + { + assert(SignatureClsHnd != NO_CLASS_HANDLE); + + Compiler* comp = JitTls::GetCompiler(); + CORINFO_CLASS_HANDLE clsHnd = comp->gtGetStructHandleIfPresent(Node); + assert((clsHnd == nullptr) || (SignatureClsHnd == clsHnd) || + (comp->info.compCompHnd->getClassSize(SignatureClsHnd) == comp->info.compCompHnd->getClassSize(clsHnd))); + } +} +#endif + //--------------------------------------------------------------- // IsArgAddedLate: Check if this is an argument that is added late, by // `DetermineArgABIInformation`. @@ -1606,20 +1623,18 @@ bool CallArgs::IsNonStandard(Compiler* comp, GenTreeCall* call, CallArg* arg) // // Parameters: // comp - The compiler. -// node - The IR node for the argument. -// wellKnownArg - The kind of argument, if special. +// arg - The builder for the new arg. // // Returns: // The created representative for the argument. // -CallArg* CallArgs::PushFront(Compiler* comp, GenTree* node, WellKnownArg wellKnownArg) +CallArg* CallArgs::PushFront(Compiler* comp, const NewCallArg& arg) { - CallArg* arg = new (comp, CMK_CallArgs) CallArg(wellKnownArg); - arg->SetEarlyNode(node); - arg->SetNext(m_head); - m_head = arg; - AddedWellKnownArg(wellKnownArg); - return arg; + CallArg* callArg = new (comp, CMK_CallArgs) CallArg(arg); + callArg->SetNext(m_head); + m_head = callArg; + AddedWellKnownArg(arg.WellKnownArg); + return callArg; } //--------------------------------------------------------------- @@ -1627,13 +1642,12 @@ CallArg* CallArgs::PushFront(Compiler* comp, GenTree* node, WellKnownArg wellKno // // Parameters: // comp - The compiler. -// node - The IR node for the argument. -// wellKnownArg - The kind of argument, if special. +// arg - The builder for the new arg. // // Returns: // The created representative for the argument. // -CallArg* CallArgs::PushBack(Compiler* comp, GenTree* node, WellKnownArg wellKnownArg) +CallArg* CallArgs::PushBack(Compiler* comp, const NewCallArg& arg) { CallArg** slot = &m_head; while (*slot != nullptr) @@ -1641,9 +1655,8 @@ CallArg* CallArgs::PushBack(Compiler* comp, GenTree* node, WellKnownArg wellKnow slot = &(*slot)->NextRef(); } - *slot = new (comp, CMK_CallArgs) CallArg(wellKnownArg); - (*slot)->SetEarlyNode(node); - AddedWellKnownArg(wellKnownArg); + *slot = new (comp, CMK_CallArgs) CallArg(arg); + AddedWellKnownArg(arg.WellKnownArg); return *slot; } @@ -1653,13 +1666,12 @@ CallArg* CallArgs::PushBack(Compiler* comp, GenTree* node, WellKnownArg wellKnow // Parameters: // comp - The compiler. // after - The existing argument to insert the new argument after. -// node - The IR node for the argument. -// wellKnownArg - The kind of argument, if special. +// arg - The builder for the new arg. // // Returns: // The created representative for the argument. // -CallArg* CallArgs::InsertAfter(Compiler* comp, CallArg* after, GenTree* node, WellKnownArg wellKnownArg) +CallArg* CallArgs::InsertAfter(Compiler* comp, CallArg* after, const NewCallArg& arg) { #ifdef DEBUG bool found = false; @@ -1675,7 +1687,7 @@ CallArg* CallArgs::InsertAfter(Compiler* comp, CallArg* after, GenTree* node, We assert(found && "Could not find arg to insert after in argument list"); #endif - return InsertAfterUnchecked(comp, after, node, wellKnownArg); + return InsertAfterUnchecked(comp, after, arg); } //--------------------------------------------------------------- @@ -1684,19 +1696,17 @@ CallArg* CallArgs::InsertAfter(Compiler* comp, CallArg* after, GenTree* node, We // Parameters: // comp - The compiler. // after - The existing argument to insert the new argument after. -// node - The IR node for the argument. -// wellKnownArg - The kind of argument, if special. +// arg - The builder for the new arg. // // Returns: // The created representative for the argument. // -CallArg* CallArgs::InsertAfterUnchecked(Compiler* comp, CallArg* after, GenTree* node, WellKnownArg wellKnownArg) +CallArg* CallArgs::InsertAfterUnchecked(Compiler* comp, CallArg* after, const NewCallArg& arg) { - CallArg* newArg = new (comp, CMK_CallArgs) CallArg(wellKnownArg); - newArg->SetEarlyNode(node); + CallArg* newArg = new (comp, CMK_CallArgs) CallArg(arg); newArg->SetNext(after->GetNext()); after->SetNext(newArg); - AddedWellKnownArg(wellKnownArg); + AddedWellKnownArg(arg.WellKnownArg); return newArg; } @@ -1718,21 +1728,23 @@ CallArg* CallArgs::InsertAfterUnchecked(Compiler* comp, CallArg* after, GenTree* // CallArg* CallArgs::InsertInstParam(Compiler* comp, GenTree* node) { + NewCallArg newArg = NewCallArg::Primitive(node).WellKnown(WellKnownArg::InstParam); + if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L) { CallArg* retBufferArg = GetRetBufferArg(); if (retBufferArg != nullptr) { - return InsertAfter(comp, retBufferArg, node, WellKnownArg::InstParam); + return InsertAfter(comp, retBufferArg, newArg); } else { - return InsertAfterThisOrFirst(comp, node, WellKnownArg::InstParam); + return InsertAfterThisOrFirst(comp, newArg); } } else { - return PushBack(comp, node, WellKnownArg::InstParam); + return PushBack(comp, newArg); } } @@ -1742,22 +1754,21 @@ CallArg* CallArgs::InsertInstParam(Compiler* comp, GenTree* node) // // Parameters: // comp - The compiler. -// node - The IR node for the argument. -// wellKnownArg - The kind of argument, if special. +// arg - The builder for the new arg. // // Returns: // The created representative for the argument. // -CallArg* CallArgs::InsertAfterThisOrFirst(Compiler* comp, GenTree* node, WellKnownArg wellKnownArg) +CallArg* CallArgs::InsertAfterThisOrFirst(Compiler* comp, const NewCallArg& arg) { CallArg* thisArg = GetThisArg(); if (thisArg != nullptr) { - return InsertAfter(comp, thisArg, node, wellKnownArg); + return InsertAfter(comp, thisArg, arg); } else { - return PushFront(comp, node, wellKnownArg); + return PushFront(comp, arg); } } @@ -5876,7 +5887,6 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) case GT_NULLCHECK: case GT_PUTARG_REG: case GT_PUTARG_STK: - case GT_PUTARG_TYPE: case GT_RETURNTRAP: case GT_NOP: case GT_RETURN: @@ -6172,8 +6182,6 @@ GenTree* GenTree::gtRetExprVal(BasicBlockFlags* pbbFlags /* = nullptr */) GenTree* retExprVal = this; BasicBlockFlags bbFlags = BBF_EMPTY; - assert(!retExprVal->OperIs(GT_PUTARG_TYPE)); - while (retExprVal->OperIs(GT_RET_EXPR)) { const GenTreeRetExpr* retExpr = retExprVal->AsRetExpr(); @@ -8599,17 +8607,20 @@ void CallArgs::InternalCopyFrom(Compiler* comp, CallArgs* other, CopyNodeFunc co CallArg** tail = &m_head; for (CallArg& arg : other->Args()) { - CallArg* carg = new (comp, CMK_CallArgs) CallArg(arg.GetWellKnownArg()); - carg->m_earlyNode = arg.m_earlyNode != nullptr ? copyNode(arg.m_earlyNode) : nullptr; - carg->m_lateNode = arg.m_lateNode != nullptr ? copyNode(arg.m_lateNode) : nullptr; - carg->m_needTmp = arg.m_needTmp; - carg->m_needPlace = arg.m_needPlace; - carg->m_isTmp = arg.m_isTmp; - carg->m_processed = arg.m_processed; - carg->m_tmpNum = arg.m_tmpNum; - carg->AbiInfo = arg.AbiInfo; - *tail = carg; - tail = &carg->m_next; + CallArg* carg = new (comp, CMK_CallArgs) CallArg(); + carg->m_earlyNode = arg.m_earlyNode != nullptr ? copyNode(arg.m_earlyNode) : nullptr; + carg->m_lateNode = arg.m_lateNode != nullptr ? copyNode(arg.m_lateNode) : nullptr; + carg->m_signatureClsHnd = arg.m_signatureClsHnd; + carg->m_tmpNum = arg.m_tmpNum; + carg->m_signatureType = arg.m_signatureType; + carg->m_wellKnownArg = arg.m_wellKnownArg; + carg->m_needTmp = arg.m_needTmp; + carg->m_needPlace = arg.m_needPlace; + carg->m_isTmp = arg.m_isTmp; + carg->m_processed = arg.m_processed; + carg->AbiInfo = arg.AbiInfo; + *tail = carg; + tail = &carg->m_next; } // Now copy late pointers. Note that these may not come in order. @@ -9101,7 +9112,6 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) case GT_NULLCHECK: case GT_PUTARG_REG: case GT_PUTARG_STK: - case GT_PUTARG_TYPE: case GT_BSWAP: case GT_BSWAP16: case GT_KEEPALIVE: @@ -15254,7 +15264,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, for (size_t i = 0; i < nArgs; i++) { - call->gtArgs.PushFront(this, args[i]); + call->gtArgs.PushFront(this, NewCallArg::Primitive(args[i])); call->gtFlags |= args[i]->gtFlags & GTF_ALL_EFFECT; } diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 8131b9e1a54dd3..822a4c98a50bc7 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1750,8 +1750,6 @@ struct GenTree // Tunnel through any GT_RET_EXPRs GenTree* gtRetExprVal(BasicBlockFlags* pbbFlags = nullptr); - inline GenTree* gtSkipPutArgType(); - // Return the child of this node if it is a GT_RELOAD or GT_COPY; otherwise simply return the node itself inline GenTree* gtSkipReloadOrCopy(); @@ -4077,6 +4075,8 @@ struct CallArgABIInformation // argument type, but when a struct is passed as a scalar type, this is // that type. Note that if a struct is passed by reference, this will still // be the struct type. + // TODO-ARGS: Reconsider whether we need this, it does not make much sense + // to have this instead of using just the type of the arg node. var_types ArgType : 5; // True when the argument fills a register slot skipped due to alignment // requirements of previous arguments. @@ -4198,45 +4198,108 @@ struct CallArgABIInformation } }; +struct NewCallArg +{ + // The node being passed. + GenTree* Node = nullptr; + // The signature type of the node. + var_types SignatureType = TYP_UNDEF; + // The class handle if SignatureType == TYP_STRUCT. + CORINFO_CLASS_HANDLE SignatureClsHnd = NO_CLASS_HANDLE; + // The type of well known arg + WellKnownArg WellKnownArg = WellKnownArg::None; + + NewCallArg WellKnown(::WellKnownArg type) const + { + NewCallArg copy = *this; + copy.WellKnownArg = type; + return copy; + } + + static NewCallArg Struct(GenTree* node, var_types type, CORINFO_CLASS_HANDLE clsHnd) + { + assert(varTypeIsStruct(node) && varTypeIsStruct(type)); + NewCallArg arg; + arg.Node = node; + arg.SignatureType = type; + arg.SignatureClsHnd = clsHnd; + arg.ValidateTypes(); + return arg; + } + + static NewCallArg Primitive(GenTree* node, var_types type = TYP_UNDEF) + { + assert(!varTypeIsStruct(node) && !varTypeIsStruct(type)); + NewCallArg arg; + arg.Node = node; + arg.SignatureType = type == TYP_UNDEF ? node->TypeGet() : type; + arg.ValidateTypes(); + return arg; + } + +#ifdef DEBUG + void ValidateTypes(); +#else + void ValidateTypes() + { + } +#endif +}; + class CallArg { friend class CallArgs; - GenTree* m_earlyNode; - GenTree* m_lateNode; - CallArg* m_next; - CallArg* m_lateNext; - WellKnownArg m_wellKnownArg : 5; + GenTree* m_earlyNode; + GenTree* m_lateNode; + CallArg* m_next; + CallArg* m_lateNext; + // The class handle for the signature type (when varTypeIsStruct(SignatureType)). + CORINFO_CLASS_HANDLE m_signatureClsHnd; + // The LclVar number if we had to force evaluation of this arg. + unsigned m_tmpNum; + // The type of the argument in the signature. + var_types m_signatureType : 5; + // The type of well-known argument this is. + WellKnownArg m_wellKnownArg : 5; // True when we force this argument's evaluation into a temp LclVar. bool m_needTmp : 1; // True when we must replace this argument with a placeholder node. bool m_needPlace : 1; - // True when we setup a temp LclVar for this argument due to size issues - // with the struct. + // True when we setup a temp LclVar for this argument. bool m_isTmp : 1; // True when we have decided the evaluation order for this argument in LateArgs bool m_processed : 1; - // The LclVar number if we had to force evaluation of this arg - unsigned m_tmpNum; - -public: - CallArgABIInformation AbiInfo; - CallArg(WellKnownArg specialArgKind) +private: + CallArg() : m_earlyNode(nullptr) , m_lateNode(nullptr) , m_next(nullptr) , m_lateNext(nullptr) - , m_wellKnownArg(specialArgKind) + , m_signatureClsHnd(NO_CLASS_HANDLE) + , m_tmpNum(BAD_VAR_NUM) + , m_signatureType(TYP_UNDEF) + , m_wellKnownArg(WellKnownArg::None) , m_needTmp(false) , m_needPlace(false) , m_isTmp(false) , m_processed(false) - , m_tmpNum(BAD_VAR_NUM) { } +public: + CallArgABIInformation AbiInfo; + + CallArg(const NewCallArg& arg) : CallArg() + { + m_earlyNode = arg.Node; + m_wellKnownArg = arg.WellKnownArg; + m_signatureType = arg.SignatureType; + m_signatureClsHnd = arg.SignatureClsHnd; + } + CallArg(const CallArg&) = delete; CallArg& operator=(CallArg&) = delete; @@ -4253,6 +4316,8 @@ class CallArg CallArg*& LateNextRef() { return m_lateNext; } CallArg* GetLateNext() { return m_lateNext; } void SetLateNext(CallArg* lateNext) { m_lateNext = lateNext; } + CORINFO_CLASS_HANDLE GetSignatureClassHandle() { return m_signatureClsHnd; } + var_types GetSignatureType() { return m_signatureType; } WellKnownArg GetWellKnownArg() { return m_wellKnownArg; } bool IsTemp() { return m_isTmp; } // clang-format on @@ -4336,26 +4401,23 @@ class CallArgs // Reverse the args from [index..index + count) in place. void Reverse(unsigned index, unsigned count); - CallArg* PushFront(Compiler* comp, GenTree* node, WellKnownArg wellKnownArg = WellKnownArg::None); - CallArg* PushBack(Compiler* comp, GenTree* node, WellKnownArg wellKnownArg = WellKnownArg::None); - CallArg* InsertAfter(Compiler* comp, CallArg* after, GenTree* node, WellKnownArg wellKnownArg = WellKnownArg::None); - CallArg* InsertAfterUnchecked(Compiler* comp, - CallArg* after, - GenTree* node, - WellKnownArg wellKnownArg = WellKnownArg::None); + CallArg* PushFront(Compiler* comp, const NewCallArg& arg); + CallArg* PushBack(Compiler* comp, const NewCallArg& arg); + CallArg* InsertAfter(Compiler* comp, CallArg* after, const NewCallArg& arg); + CallArg* InsertAfterUnchecked(Compiler* comp, CallArg* after, const NewCallArg& arg); CallArg* InsertInstParam(Compiler* comp, GenTree* node); - CallArg* InsertAfterThisOrFirst(Compiler* comp, GenTree* node, WellKnownArg wellKnownArg = WellKnownArg::None); + CallArg* InsertAfterThisOrFirst(Compiler* comp, const NewCallArg& arg); void PushLateBack(CallArg* arg); void Remove(CallArg* arg); template void InternalCopyFrom(Compiler* comp, CallArgs* other, CopyNodeFunc copyFunc); - template - void PushFront(Compiler* comp, GenTree* node, T... rest) + template + void PushFront(Compiler* comp, const NewCallArg& arg, Args&&... rest) { - PushFront(comp, rest...); - PushFront(comp, node); + PushFront(comp, std::forward(rest)...); + PushFront(comp, arg); } void ResetFinalArgsAndABIInfo(); @@ -8425,7 +8487,6 @@ inline GenTree* GenTree::gtEffectiveVal(bool commaOnly /* = false */) GenTree* effectiveVal = this; for (;;) { - assert(!effectiveVal->OperIs(GT_PUTARG_TYPE)); if (effectiveVal->gtOper == GT_COMMA) { effectiveVal = effectiveVal->AsOp()->gtGetOp2(); @@ -8474,27 +8535,6 @@ inline GenTree* GenTree::gtCommaAssignVal() return result; } -//------------------------------------------------------------------------- -// gtSkipPutArgType - skip PUTARG_TYPE if it is presented. -// -// Returns: -// the original tree or its child if it was a PUTARG_TYPE. -// -// Notes: -// PUTARG_TYPE should be skipped when we are doing transformations -// that are not affected by ABI, for example: inlining, implicit byref morphing. -// -inline GenTree* GenTree::gtSkipPutArgType() -{ - if (OperIs(GT_PUTARG_TYPE)) - { - GenTree* res = AsUnOp()->gtGetOp1(); - assert(!res->OperIs(GT_PUTARG_TYPE)); - return res; - } - return this; -} - inline GenTree* GenTree::gtSkipReloadOrCopy() { // There can be only one reload or copy (we can't have a reload/copy of a reload/copy) diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 9d75e56fab408f..d0aceb1ba07d21 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -96,7 +96,6 @@ GTNODE(ALLOCOBJ , GenTreeAllocObj ,0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // GTNODE(INIT_VAL , GenTreeOp ,0,GTK_UNOP) // Initialization value for an initBlk GTNODE(BOX , GenTreeBox ,0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // Marks its first operands (a local) as being a box -GTNODE(PUTARG_TYPE , GenTreeOp ,0,GTK_UNOP|DBK_NOTLIR) // Saves argument type between importation and morph GTNODE(RUNTIMELOOKUP , GenTreeRuntimeLookup, 0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // Runtime handle lookup GTNODE(ARR_ADDR , GenTreeArrAddr ,0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // Wraps an array address expression diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 10fd15056777bb..0cbf52b0e0d9c4 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -860,6 +860,19 @@ void Compiler::impPopCallArgs(CORINFO_SIG_INFO* sig, GenTreeCall* call) { // Morph trees that aren't already OBJs or MKREFANY to be OBJs assert(ti.IsType(TI_STRUCT)); + + // The argument and parameter types can be different for legitimate + // reasons, but we expect them to be compatible in those cases. One + // example where this happens is when inlining shared code into a + // non-generic function, in which case we might see the __Canon in + // the parameter type but exact types in the signature type. + // + // TODO-ARGS: Remove this quirk; we should be able to use the + // signature type that is different in the rare case above. It will + // cause positive diffs, but that is probably an indication that we + // have downstream phases that should be using + // `ClassLayout::AreCompatible` instead. + // classHnd = ti.GetClassHandleForValueClass(); bool forceNormalization = false; @@ -921,23 +934,23 @@ void Compiler::impPopCallArgs(CORINFO_SIG_INFO* sig, GenTreeCall* call) } } - const var_types nodeArgType = argNode->TypeGet(); - if (!varTypeIsStruct(jitSigType) && genTypeSize(nodeArgType) != genTypeSize(jitSigType)) + NewCallArg arg; + if (varTypeIsStruct(jitSigType)) { - assert(!varTypeIsStruct(nodeArgType)); - // Some ABI require precise size information for call arguments less than target pointer size, - // for example arm64 OSX. Create a special node to keep this information until morph - // consumes it into `CallArgs`. - argNode = gtNewOperNode(GT_PUTARG_TYPE, jitSigType, argNode); + arg = NewCallArg::Struct(argNode, jitSigType, classHnd); + } + else + { + arg = NewCallArg::Primitive(argNode, jitSigType); } if (lastArg == nullptr) { - lastArg = call->gtArgs.PushFront(this, argNode); + lastArg = call->gtArgs.PushFront(this, arg); } else { - lastArg = call->gtArgs.InsertAfterUnchecked(this, lastArg, argNode); + lastArg = call->gtArgs.InsertAfterUnchecked(this, lastArg, arg); } call->gtFlags |= argNode->gtFlags & GTF_GLOB_EFFECT; @@ -988,7 +1001,7 @@ static bool TypeIs(var_types type1, var_types type2, T... rest) // - it can't check long -> native int case on 64-bit platforms, // so the behavior is different depending on the target bitness. // -bool Compiler::impCheckImplicitArgumentCoercion(var_types sigType, var_types nodeType) const +bool Compiler::impCheckImplicitArgumentCoercion(var_types sigType, var_types nodeType) { if (sigType == nodeType) { @@ -1239,6 +1252,8 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, WellKnownArg wellKnownArgType = srcCall->ShouldHaveRetBufArg() ? WellKnownArg::RetBuffer : WellKnownArg::None; + NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType); + #if !defined(TARGET_ARM) // Unmanaged instance methods on Windows or Unix X86 need the retbuf arg after the first (this) parameter if ((TargetOS::IsWindows || compUnixX86Abi()) && srcCall->IsUnmanaged()) @@ -1253,18 +1268,18 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, if (srcCall->gtArgs.Args().begin() == srcCall->gtArgs.Args().end()) { // Empty arg list - srcCall->gtArgs.PushFront(this, destAddr, wellKnownArgType); + srcCall->gtArgs.PushFront(this, newArg); } else if (srcCall->GetUnmanagedCallConv() == CorInfoCallConvExtension::Thiscall) { // For thiscall, the "this" parameter is not included in the argument list reversal, // so we need to put the return buffer as the last parameter. - srcCall->gtArgs.PushBack(this, destAddr, wellKnownArgType); + srcCall->gtArgs.PushBack(this, newArg); } else if (srcCall->gtArgs.Args().begin()->GetNext() == nullptr) { // Only 1 arg, so insert at beginning - srcCall->gtArgs.PushFront(this, destAddr, wellKnownArgType); + srcCall->gtArgs.PushFront(this, newArg); } else { @@ -1281,17 +1296,17 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, } assert(secondLastArg && "Expected to find second last arg"); - srcCall->gtArgs.InsertAfter(this, secondLastArg, destAddr, wellKnownArgType); + srcCall->gtArgs.InsertAfter(this, secondLastArg, newArg); } #else if (srcCall->gtArgs.Args().begin() == srcCall->gtArgs.Args().end()) { - srcCall->gtArgs.PushFront(this, destAddr, wellKnownArgType); + srcCall->gtArgs.PushFront(this, newArg); } else { - srcCall->gtArgs.InsertAfter(this, &*srcCall->gtArgs.Args().begin(), destAddr, wellKnownArgType); + srcCall->gtArgs.InsertAfter(this, &*srcCall->gtArgs.Args().begin(), newArg); } #endif } @@ -1301,10 +1316,10 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, // The argument list has already been reversed. // Insert the return buffer as the last node so it will be pushed on to the stack last // as required by the native ABI. - srcCall->gtArgs.PushBack(this, destAddr, wellKnownArgType); + srcCall->gtArgs.PushBack(this, newArg); #else // insert the return value buffer into the argument list as first byref parameter - srcCall->gtArgs.PushFront(this, destAddr, wellKnownArgType); + srcCall->gtArgs.PushFront(this, newArg); #endif } } @@ -1312,7 +1327,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, #endif // !defined(TARGET_ARM) { // insert the return value buffer into the argument list as first byref parameter after 'this' - srcCall->gtArgs.InsertAfterThisOrFirst(this, destAddr, wellKnownArgType); + srcCall->gtArgs.InsertAfterThisOrFirst(this, newArg); } // now returns void, not a struct @@ -1375,13 +1390,14 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, } else if (src->gtOper == GT_RET_EXPR) { + noway_assert(src->AsRetExpr()->gtInlineCandidate->OperIs(GT_CALL)); GenTreeCall* call = src->AsRetExpr()->gtInlineCandidate->AsCall(); - noway_assert(call->gtOper == GT_CALL); if (call->ShouldHaveRetBufArg()) { // insert the return value buffer into the argument list as first byref parameter after 'this' - call->gtArgs.InsertAfterThisOrFirst(this, destAddr, WellKnownArg::RetBuffer); + call->gtArgs.InsertAfterThisOrFirst(this, + NewCallArg::Primitive(destAddr).WellKnown(WellKnownArg::RetBuffer)); // now returns void, not a struct src->gtType = TYP_VOID; @@ -2370,7 +2386,10 @@ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken // ((sizeCheck fails || nullCheck fails))) ? (helperCall : handle). // Add checks and the handle as call arguments, indirect call transformer will handle this. - helperCall->gtArgs.PushFront(this, nullCheck, sizeCheck, handleForResult); + NewCallArg nullCheckArg = NewCallArg::Primitive(nullCheck); + NewCallArg sizeCheckArg = NewCallArg::Primitive(sizeCheck); + NewCallArg handleForResultArg = NewCallArg::Primitive(handleForResult); + helperCall->gtArgs.PushFront(this, nullCheckArg, sizeCheckArg, handleForResultArg); result = helperCall; addExpRuntimeLookupCandidate(helperCall); } @@ -8426,7 +8445,7 @@ void Compiler::impInsertHelperCall(CORINFO_HELPER_DESC* helperInfo) default: NO_WAY("Illegal helper arg type"); } - callout->gtArgs.PushFront(this, currentArg); + callout->gtArgs.PushFront(this, NewCallArg::Primitive(currentArg)); } impAppendTree(callout, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); @@ -8707,8 +8726,7 @@ var_types Compiler::impImportCall(OPCODE opcode, bool bIntrinsicImported = false; CORINFO_SIG_INFO calliSig; - GenTree* extraArg = nullptr; - WellKnownArg extraArgKind = WellKnownArg::None; + NewCallArg extraArg; /*------------------------------------------------------------------------- * First create the call node @@ -9013,7 +9031,8 @@ var_types Compiler::impImportCall(OPCODE opcode, GenTree* fptr = impImportLdvirtftn(thisPtr, pResolvedToken, callInfo); assert(fptr != nullptr); - call->AsCall()->gtArgs.PushFront(this, thisPtrCopy, WellKnownArg::ThisPointer); + call->AsCall() + ->gtArgs.PushFront(this, NewCallArg::Primitive(thisPtrCopy).WellKnown(WellKnownArg::ThisPointer)); // Now make an indirect call through the function pointer @@ -9340,9 +9359,9 @@ var_types Compiler::impImportCall(OPCODE opcode, varCookie = info.compCompHnd->getVarArgsHandle(sig, &pVarCookie); assert((!varCookie) != (!pVarCookie)); - assert(extraArg == nullptr); - extraArg = gtNewIconEmbHndNode(varCookie, pVarCookie, GTF_ICON_VARG_HDL, sig); - extraArgKind = WellKnownArg::VarArgsCookie; + GenTree* cookieNode = gtNewIconEmbHndNode(varCookie, pVarCookie, GTF_ICON_VARG_HDL, sig); + assert(extraArg.Node == nullptr); + extraArg = NewCallArg::Primitive(cookieNode).WellKnown(WellKnownArg::VarArgsCookie); } //------------------------------------------------------------------------- @@ -9464,9 +9483,8 @@ var_types Compiler::impImportCall(OPCODE opcode, } } - assert(extraArg == nullptr); - extraArg = instParam; - extraArgKind = WellKnownArg::InstParam; + assert(extraArg.Node == nullptr); + extraArg = NewCallArg::Primitive(instParam).WellKnown(WellKnownArg::InstParam); } if ((opcode == CEE_NEWOBJ) && ((clsFlags & CORINFO_FLG_DELEGATE) != 0)) @@ -9488,18 +9506,18 @@ var_types Compiler::impImportCall(OPCODE opcode, // The main group of arguments impPopCallArgs(sig, call->AsCall()); - if (extraArg != nullptr) + if (extraArg.Node != nullptr) { if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L) { - call->AsCall()->gtArgs.PushFront(this, extraArg, extraArgKind); + call->AsCall()->gtArgs.PushFront(this, extraArg); } else { - call->AsCall()->gtArgs.PushBack(this, extraArg, extraArgKind); + call->AsCall()->gtArgs.PushBack(this, extraArg); } - call->gtFlags |= extraArg->gtFlags & GTF_GLOB_EFFECT; + call->gtFlags |= extraArg.Node->gtFlags & GTF_GLOB_EFFECT; } //------------------------------------------------------------------------- @@ -9526,7 +9544,7 @@ var_types Compiler::impImportCall(OPCODE opcode, // Store the "this" value in the call call->gtFlags |= obj->gtFlags & GTF_GLOB_EFFECT; - call->AsCall()->gtArgs.PushFront(this, obj, WellKnownArg::ThisPointer); + call->AsCall()->gtArgs.PushFront(this, NewCallArg::Primitive(obj).WellKnown(WellKnownArg::ThisPointer)); // Is this a virtual or interface call? if (call->AsCall()->IsVirtual()) @@ -16042,6 +16060,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) } case CEE_REFANYVAL: + { // get the class handle and make a ICON node out of it @@ -16060,11 +16079,16 @@ void Compiler::impImportBlockCode(BasicBlock* block) op1 = impNormStructVal(op1, impGetRefAnyClass(), (unsigned)CHECK_SPILL_ALL); // Call helper GETREFANY(classHandle, op1); - op1 = gtNewHelperCallNode(CORINFO_HELP_GETREFANY, TYP_BYREF, op2, op1); + GenTreeCall* helperCall = gtNewHelperCallNode(CORINFO_HELP_GETREFANY, TYP_BYREF); + NewCallArg clsHandleArg = NewCallArg::Primitive(op2); + NewCallArg typedRefArg = NewCallArg::Struct(op1, TYP_STRUCT, impGetRefAnyClass()); + helperCall->gtArgs.PushFront(this, clsHandleArg, typedRefArg); + helperCall->gtFlags |= (op1->gtFlags | op2->gtFlags) & GTF_ALL_EFFECT; + op1 = helperCall; impPushOnStack(op1, tiRetVal); break; - + } case CEE_REFANYTYPE: op1 = impPopStack().val; @@ -19080,7 +19104,7 @@ void Compiler::impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo, I CORINFO_CLASS_HANDLE sigClass; CorInfoType corType = strip(info.compCompHnd->getArgType(&sig, sigArg, &sigClass)); - GenTree* argNode = argUse == nullptr ? nullptr : argUse->GetEarlyNode()->gtSkipPutArgType(); + GenTree* argNode = argUse == nullptr ? nullptr : argUse->GetEarlyNode(); if (corType == CORINFO_TYPE_CLASS) { @@ -19504,7 +19528,7 @@ void Compiler::impCheckCanInline(GenTreeCall* call, // // Arguments: // pInlineInfo - inline info for the inline candidate -// curArgVal - tree for the caller actual argument value +// arg - the caller argument // argNum - logical index of this argument // inlineResult - result of ongoing inline evaluation // @@ -19516,15 +19540,15 @@ void Compiler::impCheckCanInline(GenTreeCall* call, // pass the argument into the inlinee. void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo, - GenTree* curArgVal, + CallArg* arg, unsigned argNum, InlineResult* inlineResult) { InlArgInfo* inlCurArgInfo = &pInlineInfo->inlArgInfo[argNum]; - inlCurArgInfo->argNode = curArgVal; // Save the original tree, with PUT_ARG and RET_EXPR. + inlCurArgInfo->arg = arg; + GenTree* curArgVal = arg->GetNode(); - curArgVal = curArgVal->gtSkipPutArgType(); curArgVal = curArgVal->gtRetExprVal(); if (curArgVal->gtOper == GT_MKREFANY) @@ -19702,8 +19726,8 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) break; } - GenTree* actualArg = gtFoldExpr(arg.GetEarlyNode()); - impInlineRecordArgInfo(pInlineInfo, actualArg, ilArgCnt, inlineResult); + arg.SetEarlyNode(gtFoldExpr(arg.GetEarlyNode())); + impInlineRecordArgInfo(pInlineInfo, &arg, ilArgCnt, inlineResult); if (inlineResult->IsFailure()) { @@ -19772,6 +19796,8 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) CORINFO_ARG_LIST_HANDLE argLst; argLst = methInfo->args.args; + // TODO-ARGS: We can presumably just use type info stored in CallArgs + // instead of reiterating the signature. unsigned i; for (i = (thisArg ? 1 : 0); i < ilArgCnt; i++, argLst = info.compCompHnd->getArgNext(argLst)) { @@ -19797,114 +19823,105 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) lclVarInfo[i].lclTypeInfo = sigType; lclVarInfo[i].lclHasLdlocaOp = false; - /* Does the tree type match the signature type? */ + // Does the tree type match the signature type? - GenTree* inlArgNode = inlArgInfo[i].argNode; + GenTree* inlArgNode = inlArgInfo[i].arg->GetNode(); - if ((sigType != inlArgNode->gtType) || inlArgNode->OperIs(GT_PUTARG_TYPE)) + if (sigType == inlArgNode->gtType) { - assert(impCheckImplicitArgumentCoercion(sigType, inlArgNode->gtType)); - assert(!varTypeIsStruct(inlArgNode->gtType) && !varTypeIsStruct(sigType)); + continue; + } - /* In valid IL, this can only happen for short integer types or byrefs <-> [native] ints, - but in bad IL cases with caller-callee signature mismatches we can see other types. - Intentionally reject cases with mismatches so the jit is more flexible when - encountering bad IL. */ + assert(impCheckImplicitArgumentCoercion(sigType, inlArgNode->gtType)); + assert(!varTypeIsStruct(inlArgNode->gtType) && !varTypeIsStruct(sigType)); - bool isPlausibleTypeMatch = (genActualType(sigType) == genActualType(inlArgNode->gtType)) || - (genActualTypeIsIntOrI(sigType) && inlArgNode->gtType == TYP_BYREF) || - (sigType == TYP_BYREF && genActualTypeIsIntOrI(inlArgNode->gtType)); + // In valid IL, this can only happen for short integer types or byrefs <-> [native] ints, + // but in bad IL cases with caller-callee signature mismatches we can see other types. + // Intentionally reject cases with mismatches so the jit is more flexible when + // encountering bad IL. - if (!isPlausibleTypeMatch) - { - inlineResult->NoteFatal(InlineObservation::CALLSITE_ARG_TYPES_INCOMPATIBLE); - return; - } + bool isPlausibleTypeMatch = (genActualType(sigType) == genActualType(inlArgNode->gtType)) || + (genActualTypeIsIntOrI(sigType) && inlArgNode->gtType == TYP_BYREF) || + (sigType == TYP_BYREF && genActualTypeIsIntOrI(inlArgNode->gtType)); + + if (!isPlausibleTypeMatch) + { + inlineResult->NoteFatal(InlineObservation::CALLSITE_ARG_TYPES_INCOMPATIBLE); + return; + } - GenTree** pInlArgNode; - if (inlArgNode->OperIs(GT_PUTARG_TYPE)) + // The same size but different type of the arguments. + GenTree** pInlArgNode = &inlArgInfo[i].arg->EarlyNodeRef(); + + // Is it a narrowing or widening cast? + // Widening casts are ok since the value computed is already + // normalized to an int (on the IL stack) + if (genTypeSize(inlArgNode->gtType) >= genTypeSize(sigType)) + { + if (sigType == TYP_BYREF) { - // There was a widening or narrowing cast. - GenTreeUnOp* putArgType = inlArgNode->AsUnOp(); - pInlArgNode = &putArgType->gtOp1; - inlArgNode = putArgType->gtOp1; + lclVarInfo[i].lclVerTypeInfo = typeInfo(varType2tiType(TYP_I_IMPL)); } - else + else if (inlArgNode->gtType == TYP_BYREF) { - // The same size but different type of the arguments. - pInlArgNode = &inlArgInfo[i].argNode; - } + assert(varTypeIsIntOrI(sigType)); - /* Is it a narrowing or widening cast? - * Widening casts are ok since the value computed is already - * normalized to an int (on the IL stack) */ - if (genTypeSize(inlArgNode->gtType) >= genTypeSize(sigType)) - { - if (sigType == TYP_BYREF) + /* If possible bash the BYREF to an int */ + if (inlArgNode->IsLocalAddrExpr() != nullptr) { + inlArgNode->gtType = TYP_I_IMPL; lclVarInfo[i].lclVerTypeInfo = typeInfo(varType2tiType(TYP_I_IMPL)); } - else if (inlArgNode->gtType == TYP_BYREF) + else { - assert(varTypeIsIntOrI(sigType)); - - /* If possible bash the BYREF to an int */ - if (inlArgNode->IsLocalAddrExpr() != nullptr) - { - inlArgNode->gtType = TYP_I_IMPL; - lclVarInfo[i].lclVerTypeInfo = typeInfo(varType2tiType(TYP_I_IMPL)); - } - else - { - /* Arguments 'int <- byref' cannot be changed */ - inlineResult->NoteFatal(InlineObservation::CALLSITE_ARG_NO_BASH_TO_INT); - return; - } + // Arguments 'int <- byref' cannot be changed + inlineResult->NoteFatal(InlineObservation::CALLSITE_ARG_NO_BASH_TO_INT); + return; } - else if (genTypeSize(sigType) < TARGET_POINTER_SIZE) + } + else if (genTypeSize(sigType) < TARGET_POINTER_SIZE) + { + // Narrowing cast. + if (inlArgNode->OperIs(GT_LCL_VAR)) { - // Narrowing cast. - if (inlArgNode->OperIs(GT_LCL_VAR)) + const unsigned lclNum = inlArgNode->AsLclVarCommon()->GetLclNum(); + if (!lvaTable[lclNum].lvNormalizeOnLoad() && sigType == lvaGetRealType(lclNum)) { - const unsigned lclNum = inlArgNode->AsLclVarCommon()->GetLclNum(); - if (!lvaTable[lclNum].lvNormalizeOnLoad() && sigType == lvaGetRealType(lclNum)) - { - // We don't need to insert a cast here as the variable - // was assigned a normalized value of the right type. - continue; - } + // We don't need to insert a cast here as the variable + // was assigned a normalized value of the right type. + continue; } + } - inlArgNode = gtNewCastNode(TYP_INT, inlArgNode, false, sigType); + inlArgNode = gtNewCastNode(TYP_INT, inlArgNode, false, sigType); - inlArgInfo[i].argIsLclVar = false; - // Try to fold the node in case we have constant arguments. - if (inlArgInfo[i].argIsInvariant) - { - inlArgNode = gtFoldExprConst(inlArgNode); - assert(inlArgNode->OperIsConst()); - } - *pInlArgNode = inlArgNode; + inlArgInfo[i].argIsLclVar = false; + // Try to fold the node in case we have constant arguments. + if (inlArgInfo[i].argIsInvariant) + { + inlArgNode = gtFoldExprConst(inlArgNode); + assert(inlArgNode->OperIsConst()); } + *pInlArgNode = inlArgNode; + } #ifdef TARGET_64BIT - else if (genTypeSize(genActualType(inlArgNode->gtType)) < genTypeSize(sigType)) - { - // This should only happen for int -> native int widening - inlArgNode = gtNewCastNode(genActualType(sigType), inlArgNode, false, sigType); + else if (genTypeSize(genActualType(inlArgNode->gtType)) < genTypeSize(sigType)) + { + // This should only happen for int -> native int widening + inlArgNode = gtNewCastNode(genActualType(sigType), inlArgNode, false, sigType); - inlArgInfo[i].argIsLclVar = false; + inlArgInfo[i].argIsLclVar = false; - /* Try to fold the node in case we have constant arguments */ + /* Try to fold the node in case we have constant arguments */ - if (inlArgInfo[i].argIsInvariant) - { - inlArgNode = gtFoldExprConst(inlArgNode); - assert(inlArgNode->OperIsConst()); - } - *pInlArgNode = inlArgNode; + if (inlArgInfo[i].argIsInvariant) + { + inlArgNode = gtFoldExprConst(inlArgNode); + assert(inlArgNode->OperIsConst()); } -#endif // TARGET_64BIT + *pInlArgNode = inlArgNode; } +#endif // TARGET_64BIT } } @@ -20128,7 +20145,7 @@ GenTree* Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo, In const var_types lclTyp = lclInfo.lclTypeInfo; GenTree* op1 = nullptr; - GenTree* argNode = argInfo.argNode->gtSkipPutArgType()->gtRetExprVal(); + GenTree* argNode = argInfo.arg->GetNode()->gtRetExprVal(); if (argInfo.argIsInvariant && !argCanBeModified) { diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index e89986e7d85d0f..e5ec2ea3cc10e5 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -603,7 +603,7 @@ struct LateDevirtualizationInfo struct InlArgInfo { - GenTree* argNode; // caller node for this argument + CallArg* arg; // the caller argument GenTree* argBashTmpNode; // tmp node created, if it may be replaced with actual arg unsigned argTmpNum; // the argument tmp number unsigned argIsUsed : 1; // is this arg used at all? diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index d01a9acaedec35..a6b40dd5383b0e 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -2331,7 +2331,9 @@ void Lowering::LowerCFGCall(GenTreeCall* call) // morph, sequence and lower, to avoid redoing that for the actual target. GenTree* targetPlaceholder = comp->gtNewZeroConNode(callTarget->TypeGet()); GenTreeCall* validate = comp->gtNewHelperCallNode(CORINFO_HELP_VALIDATE_INDIRECT_CALL, TYP_VOID); - validate->gtArgs.PushFront(comp, targetPlaceholder, WellKnownArg::ValidateIndirectCallTarget); + NewCallArg newArg = + NewCallArg::Primitive(targetPlaceholder).WellKnown(WellKnownArg::ValidateIndirectCallTarget); + validate->gtArgs.PushFront(comp, newArg); comp->fgMorphTree(validate); @@ -2378,7 +2380,10 @@ void Lowering::LowerCFGCall(GenTreeCall* call) #ifdef REG_DISPATCH_INDIRECT_CALL_ADDR // Now insert the call target as an extra argument. // - CallArg* targetArg = call->gtArgs.PushBack(comp, nullptr, WellKnownArg::DispatchIndirectCallTarget); + NewCallArg callTargetNewArg = + NewCallArg::Primitive(callTarget).WellKnown(WellKnownArg::DispatchIndirectCallTarget); + CallArg* targetArg = call->gtArgs.PushBack(comp, callTargetNewArg); + targetArg->SetEarlyNode(nullptr); targetArg->SetLateNode(callTarget); call->gtArgs.PushLateBack(targetArg); @@ -4336,10 +4341,13 @@ void Lowering::InsertPInvokeMethodProlog() // TCB = CORINFO_HELP_INIT_PINVOKE_FRAME(&symFrameStart, secretArg); GenTreeCall* call = comp->gtNewHelperCallNode(CORINFO_HELP_INIT_PINVOKE_FRAME, TYP_I_IMPL); - call->gtArgs.PushBack(comp, frameAddr, WellKnownArg::PInvokeFrame); + NewCallArg frameAddrArg = NewCallArg::Primitive(frameAddr).WellKnown(WellKnownArg::PInvokeFrame); + call->gtArgs.PushBack(comp, frameAddrArg); // for x86/arm32 don't pass the secretArg. #if !defined(TARGET_X86) && !defined(TARGET_ARM) - call->gtArgs.PushBack(comp, PhysReg(REG_SECRET_STUB_PARAM), WellKnownArg::SecretStubParam); + NewCallArg stubParamArg = + NewCallArg::Primitive(PhysReg(REG_SECRET_STUB_PARAM)).WellKnown(WellKnownArg::SecretStubParam); + call->gtArgs.PushBack(comp, stubParamArg); #endif // some sanity checks on the frame list root vardsc diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 3ef4e53c9c0971..8c4c09017f5ebe 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -126,13 +126,13 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, bool morphAr if (arg2 != nullptr) { - call->gtArgs.PushFront(this, arg2); + call->gtArgs.PushFront(this, NewCallArg::Primitive(arg2)); call->gtFlags |= arg2->gtFlags & GTF_ALL_EFFECT; } if (arg1 != nullptr) { - call->gtArgs.PushFront(this, arg1); + call->gtArgs.PushFront(this, NewCallArg::Primitive(arg1)); call->gtFlags |= arg1->gtFlags & GTF_ALL_EFFECT; } @@ -636,7 +636,7 @@ const char* getWellKnownArgName(WellKnownArg arg) void CallArg::Dump(Compiler* comp) { printf("CallArg[[%06u].%s", comp->dspTreeID(GetNode()), GenTree::OpName(GetNode()->OperGet())); - printf(" %s", varTypeName(AbiInfo.ArgType)); + printf(" %s", varTypeName(m_signatureType)); printf(" (%s)", AbiInfo.PassedByRef ? "By ref" : "By value"); if (AbiInfo.GetRegNum() != REG_STK) { @@ -1983,7 +1983,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call GenTreeAddrMode(TYP_BYREF, cloned, nullptr, 0, comp->eeGetEEInfo()->offsetOfWrapperDelegateIndirectCell); // Append newArg as the last arg - PushBack(comp, newArg, WellKnownArg::WrapperDelegateCell); + PushBack(comp, NewCallArg::Primitive(newArg).WellKnown(WellKnownArg::WrapperDelegateCell)); } #endif // defined(TARGET_ARM) #ifndef TARGET_X86 @@ -2002,7 +2002,8 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call { GenTree* stubAddrArg = comp->fgGetStubAddrArg(call); // And push the stub address onto the list of arguments - InsertAfterThisOrFirst(comp, stubAddrArg, WellKnownArg::VirtualStubCell); + NewCallArg stubAddrNewArg = NewCallArg::Primitive(stubAddrArg).WellKnown(WellKnownArg::VirtualStubCell); + InsertAfterThisOrFirst(comp, stubAddrNewArg); } else { @@ -2023,10 +2024,10 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call call->gtCallCookie = nullptr; // All architectures pass the cookie in a register. - InsertAfterThisOrFirst(comp, arg, WellKnownArg::PInvokeCookie); + InsertAfterThisOrFirst(comp, NewCallArg::Primitive(arg).WellKnown(WellKnownArg::PInvokeCookie)); // put destination into R10/EAX arg = comp->gtClone(call->gtCallAddr, true); - InsertAfterThisOrFirst(comp, arg, WellKnownArg::PInvokeTarget); + InsertAfterThisOrFirst(comp, NewCallArg::Primitive(arg).WellKnown(WellKnownArg::PInvokeTarget)); // finally change this call to a helper call call->gtCallType = CT_HELPER; @@ -2065,7 +2066,8 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call #endif // TARGET_ARM // Push the stub address onto the list of arguments. - InsertAfterThisOrFirst(comp, indirectCellAddress, WellKnownArg::R2RIndirectionCell); + InsertAfterThisOrFirst(comp, + NewCallArg::Primitive(indirectCellAddress).WellKnown(WellKnownArg::R2RIndirectionCell)); } #endif @@ -2166,60 +2168,10 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc; #endif // UNIX_AMD64_ABI -#if defined(DEBUG) - // Check that we have valid information about call's argument types. - // For example: - // load byte; call(int) -> CALL(PUTARG_TYPE byte(IND byte)); - // load int; call(byte) -> CALL(PUTARG_TYPE int (IND int)); - // etc. - if (call->callSig != nullptr) - { - CORINFO_SIG_INFO* sig = call->callSig; - const unsigned sigArgsCount = sig->numArgs; - - // It could include many arguments not included in `sig->numArgs`, for example, `this`, runtime lookup, cookie - // etc. - unsigned nodeArgsCount = call->gtArgs.CountArgs(); - if (call->gtArgs.HasThisPointer()) - { - // Handle the most common argument not in the `sig->numArgs`. - // so the following check works on more methods. - nodeArgsCount--; - } - - assert(nodeArgsCount >= sigArgsCount); - if ((nodeArgsCount == sigArgsCount) && - ((Target::g_tgtArgOrder == Target::ARG_ORDER_R2L) || (nodeArgsCount == 1))) - { - CORINFO_ARG_LIST_HANDLE sigArg = sig->args; - for (CallArg& arg : call->gtArgs.Args()) - { - if (arg.GetWellKnownArg() == WellKnownArg::ThisPointer) - { - continue; - } - - CORINFO_CLASS_HANDLE argClass; - const CorInfoType corType = strip(comp->info.compCompHnd->getArgType(sig, sigArg, &argClass)); - const var_types sigType = JITtype2varType(corType); - - const GenTree* nodeArg = arg.GetNode(); - assert(nodeArg != nullptr); - const var_types nodeType = nodeArg->TypeGet(); - - assert((nodeType == sigType) || varTypeIsStruct(sigType) || - genTypeSize(nodeType) == genTypeSize(sigType)); - - sigArg = comp->info.compCompHnd->getArgNext(sigArg); - } - } - } -#endif // DEBUG - for (CallArg& arg : Args()) { assert(arg.GetEarlyNode() != nullptr); - GenTree* argx = arg.GetEarlyNode()->gtSkipPutArgType(); + GenTree* argx = arg.GetEarlyNode(); // Change the node to TYP_I_IMPL so we don't report GC info // NOTE: We deferred this from the importer because of the inliner. @@ -2254,7 +2206,6 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call if (isHfaArg) { - isHfaArg = true; hfaSlots = comp->GetHfaCount(argx); // If we have a HFA struct it's possible we transition from a method that originally @@ -2334,7 +2285,13 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call // TARGET_POINTER_SIZE stack slots, or the sum of these if the argument is split between the registers and // the stack. // - isStructArg = varTypeIsStruct(argx); + isStructArg = varTypeIsStruct(argx); + // Note that we internally in the JIT can change some struct args to + // primitive args (e.g. OBJ(x) -> IND(x)). Similarly, + // the ABI type can also change from struct to primitive (e.g. a 8-byte + // struct passed in a register). So isStructArg may be false even if + // the signature type was (or is) a struct, however only in cases where + // it does not matter. CORINFO_CLASS_HANDLE objClass = NO_CLASS_HANDLE; if (isStructArg) { @@ -2370,7 +2327,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call if (!isStructArg) { size = 1; // On AMD64, all primitives fit in a single (64-bit) 'slot' - byteSize = genTypeSize(argx); + byteSize = genTypeSize(arg.GetSignatureType()); } else { @@ -2382,7 +2339,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call size = 1; // On AMD64 Windows, all args fit in a single (64-bit) 'slot' if (!isStructArg) { - byteSize = genTypeSize(argx); + byteSize = genTypeSize(arg.GetSignatureType()); } #endif // UNIX_AMD64_ABI @@ -2417,7 +2374,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call else { size = 1; // Otherwise, all primitive types fit in a single (64-bit) 'slot' - byteSize = genTypeSize(argx); + byteSize = genTypeSize(arg.GetSignatureType()); } #elif defined(TARGET_ARM) || defined(TARGET_X86) if (isStructArg) @@ -2430,7 +2387,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call // The typical case. // Long/double type argument(s) will be modified as needed in Lowering. size = genTypeStSz(argx->gtType); - byteSize = genTypeSize(argx); + byteSize = genTypeSize(arg.GetSignatureType()); } #else #error Unsupported or unset target architecture @@ -2511,12 +2468,6 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call #endif } - const var_types argType = arg.GetEarlyNode()->TypeGet(); - if (arg.GetEarlyNode()->OperIs(GT_PUTARG_TYPE)) - { - byteSize = genTypeSize(argType); - } - // The 'size' value has now must have been set. (the original value of zero is an invalid value) assert(size != 0); assert(byteSize != 0); @@ -2526,7 +2477,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call // Arm64 Apple has a special ABI for passing small size arguments on stack, // bytes are aligned to 1-byte, shorts to 2-byte, int/float to 4-byte, etc. // It means passing 8 1-byte arguments on stack can take as small as 8 bytes. - argAlignBytes = comp->eeGetArgSizeAlignment(argType, isFloatHfa); + argAlignBytes = comp->eeGetArgSizeAlignment(arg.GetSignatureType(), isFloatHfa); } #ifdef TARGET_LOONGARCH64 @@ -2789,7 +2740,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call } #endif // TARGET_ARM - arg.AbiInfo = {}; + arg.AbiInfo = CallArgABIInformation(); arg.AbiInfo.ArgType = argx->TypeGet(); arg.AbiInfo.IsStruct = isStructArg; @@ -7498,7 +7449,7 @@ GenTree* Compiler::fgMorphTailCallViaHelpers(GenTreeCall* call, CORINFO_TAILCALL // During rationalization tmp="this" and null check will be materialized // in the right execution order. - call->gtArgs.PushFront(this, thisPtr); + call->gtArgs.PushFront(this, NewCallArg::Primitive(thisPtr, thisArg->GetSignatureType())); call->gtArgs.Remove(thisArg); } @@ -7550,7 +7501,7 @@ GenTree* Compiler::fgMorphTailCallViaHelpers(GenTreeCall* call, CORINFO_TAILCALL target = getVirtMethodPointerTree(thisPtrStubArg, call->tailCallInfo->GetToken(), &callInfo); } - call->gtArgs.PushBack(this, target); + call->gtArgs.PushBack(this, NewCallArg::Primitive(target)); } // This is now a direct call to the store args stub and not a tailcall. @@ -7705,7 +7656,10 @@ GenTree* Compiler::fgCreateCallDispatcherAndGetResult(GenTreeCall* orig GenTree* retAddrSlot = gtNewOperNode(GT_ADDR, TYP_I_IMPL, gtNewLclvNode(lvaRetAddrVar, TYP_I_IMPL)); - callDispatcherNode->gtArgs.PushFront(this, retAddrSlot, callTarget, retValArg); + NewCallArg retAddrSlotArg = NewCallArg::Primitive(retAddrSlot); + NewCallArg callTargetArg = NewCallArg::Primitive(callTarget); + NewCallArg retValCallArg = NewCallArg::Primitive(retValArg); + callDispatcherNode->gtArgs.PushFront(this, retAddrSlotArg, callTargetArg, retValCallArg); GenTree* finalTree = callDispatcherNode; @@ -8069,26 +8023,26 @@ void Compiler::fgMorphTailCallViaJitHelper(GenTreeCall* call) // During rationalization tmp="this" and null check will // materialize as embedded stmts in right execution order. assert(thisPtr != nullptr); - call->gtArgs.PushFront(this, thisPtr); + call->gtArgs.PushFront(this, NewCallArg::Primitive(thisPtr, thisArg->GetSignatureType())); call->gtArgs.Remove(thisArg); } unsigned nOldStkArgsWords = (compArgSize - (codeGen->intRegState.rsCalleeRegArgCount * REGSIZE_BYTES)) / REGSIZE_BYTES; GenTree* arg3Node = gtNewIconNode((ssize_t)nOldStkArgsWords, TYP_I_IMPL); - CallArg* arg3 = call->gtArgs.PushBack(this, arg3Node); + CallArg* arg3 = call->gtArgs.PushBack(this, NewCallArg::Primitive(arg3Node)); // Inject a placeholder for the count of outgoing stack arguments that the Lowering phase will generate. // The constant will be replaced. GenTree* arg2Node = gtNewIconNode(9, TYP_I_IMPL); - CallArg* arg2 = call->gtArgs.InsertAfter(this, arg3, arg2Node); + CallArg* arg2 = call->gtArgs.InsertAfter(this, arg3, NewCallArg::Primitive(arg2Node)); // Inject a placeholder for the flags. // The constant will be replaced. GenTree* arg1Node = gtNewIconNode(8, TYP_I_IMPL); - CallArg* arg1 = call->gtArgs.InsertAfter(this, arg2, arg1Node); + CallArg* arg1 = call->gtArgs.InsertAfter(this, arg2, NewCallArg::Primitive(arg1Node)); // Inject a placeholder for the real call target that the Lowering phase will generate. // The constant will be replaced. GenTree* arg0Node = gtNewIconNode(7, TYP_I_IMPL); - CallArg* arg0 = call->gtArgs.InsertAfter(this, arg1, arg0Node); + CallArg* arg0 = call->gtArgs.InsertAfter(this, arg1, NewCallArg::Primitive(arg0Node)); // It is now a varargs tail call. call->gtArgs.SetIsVarArgs(); @@ -11005,9 +10959,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) break; #endif - case GT_PUTARG_TYPE: - return fgMorphTree(tree->AsUnOp()->gtGetOp1()); - case GT_NULLCHECK: { op1 = tree->AsUnOp()->gtGetOp1(); diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp index 706895fa3979c0..e2e43e75ed5756 100644 --- a/src/coreclr/jit/rationalize.cpp +++ b/src/coreclr/jit/rationalize.cpp @@ -211,13 +211,13 @@ void Rationalizer::RewriteNodeAsCall(GenTree** use, if (arg2 != nullptr) { - call->gtArgs.PushFront(comp, arg2); + call->gtArgs.PushFront(comp, NewCallArg::Primitive(arg2)); call->gtFlags |= arg2->gtFlags & GTF_ALL_EFFECT; } if (arg1 != nullptr) { - call->gtArgs.PushFront(comp, arg1); + call->gtArgs.PushFront(comp, NewCallArg::Primitive(arg1)); call->gtFlags |= arg1->gtFlags & GTF_ALL_EFFECT; } @@ -308,8 +308,8 @@ void Rationalizer::SanityCheck() for (GenTree* const tree : stmt->TreeList()) { - // QMARK and PUT_ARG_TYPE nodes should have been removed before this phase. - assert(!tree->OperIs(GT_QMARK, GT_PUTARG_TYPE)); + // QMARK nodes should have been removed before this phase. + assert(!tree->OperIs(GT_QMARK)); if (tree->OperGet() == GT_ASG) {