diff --git a/src/coreclr/src/jit/assertionprop.cpp b/src/coreclr/src/jit/assertionprop.cpp index 135bc1e5083fc..b86a567d85631 100644 --- a/src/coreclr/src/jit/assertionprop.cpp +++ b/src/coreclr/src/jit/assertionprop.cpp @@ -2163,22 +2163,20 @@ void Compiler::optAssertionGen(GenTree* tree) break; case GT_CALL: + { // A virtual call can create a non-null assertion. We transform some virtual calls into non-virtual calls // with a GTF_CALL_NULLCHECK flag set. - if ((tree->gtFlags & GTF_CALL_NULLCHECK) || tree->AsCall()->IsVirtual()) + // Ignore tail calls because they have 'this` pointer in the regular arg list and an implicit null check. + GenTreeCall* const call = tree->AsCall(); + if (call->NeedsNullCheck() || (call->IsVirtual() && !call->IsTailCall())) { - // Retrieve the 'this' arg - GenTree* thisArg = gtGetThisArg(tree->AsCall()); - if (thisArg == nullptr) - { - // For tail calls we lose the this pointer in the argument list but that's OK because a null check - // was made explicit, so we get the assertion when we walk the GT_IND in the argument list. - noway_assert(tree->AsCall()->IsTailCall()); - break; - } + // Retrieve the 'this' arg. + GenTree* thisArg = gtGetThisArg(call); + assert(thisArg != nullptr); assertionInfo = optCreateAssertion(thisArg, nullptr, OAK_NOT_EQUAL); } - break; + } + break; case GT_CAST: // We only create this assertion for global assertion prop diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index e2ae7339c6d50..fdd9307ab68fb 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -6082,7 +6082,6 @@ GenTree* Compiler::gtNewStringLiteralNode(InfoAccessType iat, void* pValue) #ifdef DEBUG tree->AsIntCon()->gtTargetHandle = (size_t)pValue; #endif - tree = gtNewOperNode(GT_NOP, TYP_REF, tree); // prevents constant folding break; case IAT_PVALUE: // The value needs to be accessed via an indirection @@ -8535,15 +8534,21 @@ bool Compiler::gtCompareTree(GenTree* op1, GenTree* op2) return false; } +//------------------------------------------------------------------------ +// gtGetThisArg: Return this pointer node for the call. +// +// Arguments: +// call - the call node with a this argument. +// +// Return value: +// the this pointer node. +// GenTree* Compiler::gtGetThisArg(GenTreeCall* call) { - if (call->gtCallThisArg == nullptr) - { - return nullptr; - } + assert(call->gtCallThisArg != nullptr); GenTree* thisArg = call->gtCallThisArg->GetNode(); - if (thisArg->OperIs(GT_NOP, GT_ASG) == false) + if (!thisArg->OperIs(GT_ASG)) { if ((thisArg->gtFlags & GTF_LATE_ARG) == 0) { @@ -8551,41 +8556,38 @@ GenTree* Compiler::gtGetThisArg(GenTreeCall* call) } } - if (call->gtCallLateArgs != nullptr) - { - unsigned argNum = 0; - fgArgTabEntry* thisArgTabEntry = gtArgEntryByArgNum(call, argNum); - GenTree* result = thisArgTabEntry->GetNode(); + assert(call->gtCallLateArgs != nullptr); - // Assert if we used DEBUG_DESTROY_NODE. - assert(result->gtOper != GT_COUNT); + unsigned argNum = 0; + fgArgTabEntry* thisArgTabEntry = gtArgEntryByArgNum(call, argNum); + GenTree* result = thisArgTabEntry->GetNode(); + + // Assert if we used DEBUG_DESTROY_NODE. + assert(result->gtOper != GT_COUNT); #if !FEATURE_FIXED_OUT_ARGS && defined(DEBUG) - // Check that call->fgArgInfo used in gtArgEntryByArgNum was not - // left outdated by assertion propogation updates. - // There is no information about registers of late args for platforms - // with FEATURE_FIXED_OUT_ARGS that is why this debug check is under - // !FEATURE_FIXED_OUT_ARGS. - regNumber thisReg = REG_ARG_0; - regList list = call->regArgList; - int index = 0; - for (GenTreeCall::Use& use : call->LateArgs()) + // Check that call->fgArgInfo used in gtArgEntryByArgNum was not + // left outdated by assertion propogation updates. + // There is no information about registers of late args for platforms + // with FEATURE_FIXED_OUT_ARGS that is why this debug check is under + // !FEATURE_FIXED_OUT_ARGS. + regNumber thisReg = REG_ARG_0; + regList list = call->regArgList; + int index = 0; + for (GenTreeCall::Use& use : call->LateArgs()) + { + assert(index < call->regArgListCount); + regNumber curArgReg = list[index]; + if (curArgReg == thisReg) { - assert(index < call->regArgListCount); - regNumber curArgReg = list[index]; - if (curArgReg == thisReg) - { - assert(result == use.GetNode()); - } - - index++; + assert(result == use.GetNode()); } -#endif // !FEATURE_FIXED_OUT_ARGS && defined(DEBUG) - return result; + index++; } +#endif // !FEATURE_FIXED_OUT_ARGS && defined(DEBUG) - return nullptr; + return result; } bool GenTree::gtSetFlags() const diff --git a/src/coreclr/src/jit/lower.cpp b/src/coreclr/src/jit/lower.cpp index 6494b9515626b..713e1d3e08389 100644 --- a/src/coreclr/src/jit/lower.cpp +++ b/src/coreclr/src/jit/lower.cpp @@ -3762,6 +3762,7 @@ GenTree* Lowering::LowerDelegateInvoke(GenTreeCall* call) thisArgNode = comp->gtGetThisArg(call); } + assert(thisArgNode != nullptr); assert(thisArgNode->gtOper == GT_PUTARG_REG); GenTree* originalThisExpr = thisArgNode->AsOp()->gtOp1; GenTree* thisExpr = originalThisExpr; diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index e172a9c21db57..416d4244ae330 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -8435,6 +8435,10 @@ void Compiler::fgMorphTailCallViaJitHelper(GenTreeCall* call) thisPtr = objp; } + // TODO-Cleanup: we leave it as a virtual stub call to + // use logic in `LowerVirtualStubCall`, clear GTF_CALL_VIRT_KIND_MASK here + // and change `LowerCall` to recognize it as a direct call. + // During rationalization tmp="this" and null check will // materialize as embedded stmts in right execution order. assert(thisPtr != nullptr);