From 31f64c8511c23688fd5fed7df55209eb470eb307 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 30 Jan 2025 19:56:19 +0100 Subject: [PATCH 1/3] JIT: Unify handling of InstParam argument during inlining * Handle InstParam in the same way as all other arguments * Insert the evaluation of the InstParam in the right spot when inlining succeeded Fix #112092 --- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/fginline.cpp | 28 ++++++++--- src/coreclr/jit/gentree.cpp | 31 ++++++++++++ src/coreclr/jit/gentree.h | 1 + src/coreclr/jit/importer.cpp | 95 ++++++++++++++---------------------- 5 files changed, 90 insertions(+), 67 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 473dc3dc47153a..cf9e295fbb7987 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5210,7 +5210,7 @@ class Compiler InlineCandidateInfo** ppInlineCandidateInfo, InlineResult* inlineResult); - void impInlineRecordArgInfo(InlineInfo* pInlineInfo, CallArg* arg, unsigned argNum, InlineResult* inlineResult); + void impInlineRecordArgInfo(InlineInfo* pInlineInfo, CallArg* arg, InlArgInfo* argInfo, InlineResult* inlineResult); void impInlineInitVars(InlineInfo* pInlineInfo); diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index ba55f989961d8d..6b14c28fb2fad2 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -2045,20 +2045,32 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) } } - // Append the InstParam - if (inlineInfo->inlInstParamArgInfo != nullptr) +#ifdef DEBUG + if (call->gtArgs.CountArgs() > 0) { - fgInsertInlineeArgument(*inlineInfo->inlInstParamArgInfo, block, &afterStmt, &newStmt, callDI); + JITDUMP("\nArguments setup:\n"); } +#endif - // Treat arguments that had to be assigned to temps - if (inlineInfo->argCnt) + unsigned ilArgNum = 0; + for (CallArg& arg : call->gtArgs.Args()) { - JITDUMP("\nArguments setup:\n"); - for (unsigned argNum = 0; argNum < inlineInfo->argCnt; argNum++) + InlArgInfo* argInfo = nullptr; + switch (arg.GetWellKnownArg()) { - fgInsertInlineeArgument(inlArgInfo[argNum], block, &afterStmt, &newStmt, callDI); + case WellKnownArg::RetBuffer: + continue; + case WellKnownArg::InstParam: + argInfo = inlineInfo->inlInstParamArgInfo; + break; + default: + assert(ilArgNum < inlineInfo->argCnt); + argInfo = &inlineInfo->inlArgInfo[ilArgNum++]; + break; } + + assert(argInfo != nullptr); + fgInsertInlineeArgument(*argInfo, block, &afterStmt, &newStmt, callDI); } // Add the CCTOR check if asked for. diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 7c86705f00dd3e..a906dd016ddc84 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -1581,6 +1581,37 @@ unsigned CallArgs::GetIndex(CallArg* arg) return (unsigned)-1; } +//--------------------------------------------------------------- +// GetIndex: Get the user arg index for the specified argument (IL index). +// +// Parameters: +// arg - The argument to obtain the index of. +// +// Returns: +// The user index. +// +unsigned CallArgs::GetUserIndex(CallArg* arg) +{ + unsigned i = 0; + for (CallArg& a : Args()) + { + if (!a.IsUserArg()) + { + continue; + } + + if (&a == arg) + { + return i; + } + + i++; + } + + assert(!"Could not find argument in arg list"); + return (unsigned)-1; +} + //--------------------------------------------------------------- // Reverse: Reverse the specified subrange of arguments. // diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index e7c42dc83c146b..641f0b05e1f61a 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4920,6 +4920,7 @@ class CallArgs CallArg* GetArgByIndex(unsigned index); CallArg* GetUserArgByIndex(unsigned index); unsigned GetIndex(CallArg* arg); + unsigned GetUserIndex(CallArg* arg); bool IsEmpty() const { diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 0435ecf0b514e4..64f930ae5058d4 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -12928,7 +12928,7 @@ void Compiler::impCanInlineIL(CORINFO_METHOD_HANDLE fncHandle, // Arguments: // pInlineInfo - inline info for the inline candidate // arg - the caller argument -// argNum - logical index of this argument +// argInfo - Structure to record information into // inlineResult - result of ongoing inline evaluation // // Notes: @@ -12940,12 +12940,10 @@ void Compiler::impCanInlineIL(CORINFO_METHOD_HANDLE fncHandle, void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo, CallArg* arg, - unsigned argNum, + InlArgInfo* argInfo, InlineResult* inlineResult) { - InlArgInfo* inlCurArgInfo = &pInlineInfo->inlArgInfo[argNum]; - - inlCurArgInfo->arg = arg; + argInfo->arg = arg; GenTree* curArgVal = arg->GetNode(); assert(!curArgVal->OperIs(GT_RET_EXPR)); @@ -12958,7 +12956,7 @@ void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo, if (varTypeIsStruct(varDsc)) { - inlCurArgInfo->argIsByRefToStructLocal = true; + argInfo->argIsByRefToStructLocal = true; #ifdef FEATURE_SIMD if (varTypeIsSIMD(varDsc)) { @@ -12973,22 +12971,19 @@ void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo, if (curArgVal->gtFlags & GTF_ALL_EFFECT) { - inlCurArgInfo->argHasGlobRef = (curArgVal->gtFlags & GTF_GLOB_REF) != 0; - inlCurArgInfo->argHasSideEff = (curArgVal->gtFlags & (GTF_ALL_EFFECT & ~GTF_GLOB_REF)) != 0; + argInfo->argHasGlobRef = (curArgVal->gtFlags & GTF_GLOB_REF) != 0; + argInfo->argHasSideEff = (curArgVal->gtFlags & (GTF_ALL_EFFECT & ~GTF_GLOB_REF)) != 0; } if (curArgVal->gtOper == GT_LCL_VAR) { - inlCurArgInfo->argIsLclVar = true; - - /* Remember the "original" argument number */ - INDEBUG(curArgVal->AsLclVar()->gtLclILoffs = argNum;) + argInfo->argIsLclVar = true; } if (impIsInvariant(curArgVal)) { - inlCurArgInfo->argIsInvariant = true; - if (inlCurArgInfo->argIsThis && (curArgVal->gtOper == GT_CNS_INT) && (curArgVal->AsIntCon()->gtIconVal == 0)) + argInfo->argIsInvariant = true; + if (argInfo->argIsThis && (curArgVal->gtOper == GT_CNS_INT) && (curArgVal->AsIntCon()->gtIconVal == 0)) { // Abort inlining at this call site inlineResult->NoteFatal(InlineObservation::CALLSITE_ARG_HAS_NULL_THIS); @@ -12997,13 +12992,15 @@ void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo, } else if (gtIsTypeof(curArgVal)) { - inlCurArgInfo->argIsInvariant = true; - inlCurArgInfo->argHasSideEff = false; + argInfo->argIsInvariant = true; + argInfo->argHasSideEff = false; } - bool isExact = false; - bool isNonNull = false; - inlCurArgInfo->argIsExact = (gtGetClassHandle(curArgVal, &isExact, &isNonNull) != NO_CLASS_HANDLE) && isExact; + argInfo->argIsThis = arg->GetWellKnownArg() == WellKnownArg::ThisPointer; + + bool isExact = false; + bool isNonNull = false; + argInfo->argIsExact = (gtGetClassHandle(curArgVal, &isExact, &isNonNull) != NO_CLASS_HANDLE) && isExact; // If the arg is a local that is address-taken, we can't safely // directly substitute it into the inlinee. @@ -13014,51 +13011,51 @@ void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo, // which is safe in this case. // // Instead mark the arg as having a caller local ref. - if (!inlCurArgInfo->argIsInvariant && gtHasLocalsWithAddrOp(curArgVal)) + if (!argInfo->argIsInvariant && gtHasLocalsWithAddrOp(curArgVal)) { - inlCurArgInfo->argHasCallerLocalRef = true; + argInfo->argHasCallerLocalRef = true; } #ifdef DEBUG if (verbose) { - if (inlCurArgInfo->argIsThis) + if (arg->GetWellKnownArg() != WellKnownArg::None) { - printf("thisArg:"); + printf("%s:", getWellKnownArgName(arg->GetWellKnownArg())); } else { - printf("\nArgument #%u:", argNum); + printf("IL argument #%u:", pInlineInfo->iciCall->gtArgs.GetUserIndex(arg)); } - if (inlCurArgInfo->argIsLclVar) + if (argInfo->argIsLclVar) { printf(" is a local var"); } - if (inlCurArgInfo->argIsInvariant) + if (argInfo->argIsInvariant) { printf(" is a constant or invariant"); } - if (inlCurArgInfo->argHasGlobRef) + if (argInfo->argHasGlobRef) { printf(" has global refs"); } - if (inlCurArgInfo->argHasCallerLocalRef) + if (argInfo->argHasCallerLocalRef) { printf(" has caller local ref"); } - if (inlCurArgInfo->argHasSideEff) + if (argInfo->argHasSideEff) { printf(" has side effects"); } - if (inlCurArgInfo->argHasLdargaOp) + if (argInfo->argHasLdargaOp) { printf(" has ldarga effect"); } - if (inlCurArgInfo->argHasStargOp) + if (argInfo->argHasStargOp) { printf(" has starg effect"); } - if (inlCurArgInfo->argIsByRefToStructLocal) + if (argInfo->argIsByRefToStructLocal) { printf(" is byref to a struct local"); } @@ -13114,51 +13111,33 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) unsigned ilArgCnt = 0; for (CallArg& arg : call->gtArgs.Args()) { + InlArgInfo* argInfo; switch (arg.GetWellKnownArg()) { case WellKnownArg::ThisPointer: - inlArgInfo[ilArgCnt].argIsThis = true; + argInfo = &inlArgInfo[ilArgCnt++]; + argInfo->argIsThis = true; break; case WellKnownArg::RetBuffer: // This does not appear in the table of inline arg info; do not include them continue; case WellKnownArg::InstParam: - { - InlArgInfo* ctxInfo = new (this, CMK_Inlining) InlArgInfo{}; - ctxInfo->arg = &arg; - ctxInfo->argTmpNum = BAD_VAR_NUM; - ctxInfo->argIsLclVar = arg.GetNode()->OperIs(GT_LCL_VAR); - if (arg.GetNode()->IsCnsIntOrI()) - { - ctxInfo->argIsInvariant = true; - } - else - { - // Conservative approach - ctxInfo->argHasSideEff = true; - ctxInfo->argHasGlobRef = true; - } - pInlineInfo->inlInstParamArgInfo = ctxInfo; - continue; - } + pInlineInfo->inlInstParamArgInfo = argInfo = new (this, CMK_Inlining) InlArgInfo{}; + break; default: + argInfo = &inlArgInfo[ilArgCnt++]; break; } arg.SetEarlyNode(gtFoldExpr(arg.GetEarlyNode())); - impInlineRecordArgInfo(pInlineInfo, &arg, ilArgCnt, inlineResult); + impInlineRecordArgInfo(pInlineInfo, &arg, argInfo, inlineResult); if (inlineResult->IsFailure()) { return; } - - ilArgCnt++; } - /* Make sure we got the arg number right */ - assert(ilArgCnt == methInfo->args.totalILArgs()); - #ifdef FEATURE_SIMD bool foundSIMDType = pInlineInfo->hasSIMDTypeArgLocalOrReturn; #endif // FEATURE_SIMD @@ -13538,7 +13517,7 @@ unsigned Compiler::impInlineFetchLocal(unsigned lclNum DEBUGARG(const char* reas // // This method will side effect inlArgInfo. It should only be called // for actual uses of the argument in the inlinee. - +// GenTree* Compiler::impInlineFetchArg(InlArgInfo& argInfo, const InlLclVarInfo& lclInfo) { // Cache the relevant arg and lcl info for this argument. From c6747f71fec33ef5acdd9e80addfc8e8ec8b2756 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 4 Feb 2025 11:55:58 +0100 Subject: [PATCH 2/3] Nit --- src/coreclr/jit/fginline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 6b14c28fb2fad2..d66609dc788b4c 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -2046,7 +2046,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) } #ifdef DEBUG - if (call->gtArgs.CountArgs() > 0) + if (call->gtArgs.CountUserArgs() > 0) { JITDUMP("\nArguments setup:\n"); } From c6cad00f36e080a6281ce5b968dbb9a014c8770b Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 4 Feb 2025 12:33:09 +0100 Subject: [PATCH 3/3] Nit --- src/coreclr/jit/importer.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 64f930ae5058d4..789ed8b6ae223f 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -12980,6 +12980,8 @@ void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo, argInfo->argIsLclVar = true; } + argInfo->argIsThis = arg->GetWellKnownArg() == WellKnownArg::ThisPointer; + if (impIsInvariant(curArgVal)) { argInfo->argIsInvariant = true; @@ -12996,8 +12998,6 @@ void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo, argInfo->argHasSideEff = false; } - argInfo->argIsThis = arg->GetWellKnownArg() == WellKnownArg::ThisPointer; - bool isExact = false; bool isNonNull = false; argInfo->argIsExact = (gtGetClassHandle(curArgVal, &isExact, &isNonNull) != NO_CLASS_HANDLE) && isExact; @@ -13114,10 +13114,6 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) InlArgInfo* argInfo; switch (arg.GetWellKnownArg()) { - case WellKnownArg::ThisPointer: - argInfo = &inlArgInfo[ilArgCnt++]; - argInfo->argIsThis = true; - break; case WellKnownArg::RetBuffer: // This does not appear in the table of inline arg info; do not include them continue;