From ae442e58692b6c1e5b19920d25c449e0f2107f19 Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Fri, 12 Apr 2024 19:06:35 +0000 Subject: [PATCH] JIT: Allow helper calls that always throw to be marked as no-return (#100900) Fixes #100458 by addressing two issues: When flagging a call node as no-return with GTF_CALL_M_DOES_NOT_RETURN, we should always increment Compiler::optNoReturnCallCount to avoid asserts in Compiler::fgTailMergeThrows. Previously, we weren't doing this in a unified place, which seemed error-prone. When incrementing the optNoReturnCallCount member of an inlinee compiler, ensure this information is propagated to the inlinee's parent compiler. In a similar vein, if we try to inline a call, and the inlinee compiler determines it does not return, make sure we increment optNoReturnCallCount in the parent compiler object if the inline fails -- we've kept the call, and we now know it doesn't return. With these changes, I can now mark helper calls that always throw as no-return; this unblocks morph to convert BBJ_ALWAYS blocks with helper calls that throw into BBJ_THROW blocks, and has the nice side effect of improving the efficacy of throw merging. Since I was touching relevant code, I decided to improve our usage of GenTreeCall::IsHelperCall, too. --- src/coreclr/jit/assertionprop.cpp | 4 ++-- src/coreclr/jit/codegenarmarch.cpp | 6 +++--- src/coreclr/jit/codegenlinear.cpp | 17 +++++------------ src/coreclr/jit/codegenloongarch64.cpp | 6 +++--- src/coreclr/jit/codegenriscv64.cpp | 6 +++--- src/coreclr/jit/codegenxarch.cpp | 8 ++++---- src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/compiler.h | 8 ++++++++ src/coreclr/jit/compiler.hpp | 15 +++++++++++---- src/coreclr/jit/fgbasic.cpp | 4 +--- src/coreclr/jit/fgehopt.cpp | 7 +++---- src/coreclr/jit/fginline.cpp | 11 ++++++++++- src/coreclr/jit/flowgraph.cpp | 3 ++- src/coreclr/jit/gentree.cpp | 18 +++++++++--------- src/coreclr/jit/gentree.h | 2 +- src/coreclr/jit/helperexpansion.cpp | 2 +- src/coreclr/jit/importercalls.cpp | 18 +++++++++--------- src/coreclr/jit/liveness.cpp | 2 +- src/coreclr/jit/lower.cpp | 4 ++-- src/coreclr/jit/morph.cpp | 12 +++++------- src/coreclr/jit/objectalloc.cpp | 2 +- src/coreclr/jit/optcse.cpp | 2 +- src/coreclr/jit/optimizer.cpp | 8 ++++---- src/coreclr/jit/valuenum.cpp | 2 +- 24 files changed, 91 insertions(+), 78 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index ebcb101663a308..cefb3838032959 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2327,8 +2327,8 @@ AssertionInfo Compiler::optAssertionGenJtrue(GenTree* tree) std::swap(op1, op2); } // Validate op1 and op2 - if ((op1->gtOper != GT_CALL) || (op1->AsCall()->gtCallType != CT_HELPER) || (op1->TypeGet() != TYP_REF) || // op1 - (op2->gtOper != GT_CNS_INT) || (op2->AsIntCon()->gtIconVal != 0)) // op2 + if (!op1->OperIs(GT_CALL) || !op1->AsCall()->IsHelperCall() || !op1->TypeIs(TYP_REF) || // op1 + !op2->OperIs(GT_CNS_INT) || (op2->AsIntCon()->gtIconVal != 0)) // op2 { return NO_ASSERTION_INDEX; } diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index a9e2a41f73f945..041200a2abb343 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -3530,7 +3530,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) #ifdef DEBUG // Pass the call signature information down into the emitter so the emitter can associate // native call sites with the signatures they were generated from. - if (call->gtCallType != CT_HELPER) + if (!call->IsHelperCall()) { sigInfo = call->callSig; } @@ -3693,7 +3693,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) else { // Generate a direct call to a non-virtual user defined or helper method - assert(call->gtCallType == CT_HELPER || call->gtCallType == CT_USER_FUNC); + assert(call->IsHelperCall() || (call->gtCallType == CT_USER_FUNC)); void* addr = nullptr; #ifdef FEATURE_READYTORUN @@ -3704,7 +3704,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) } else #endif // FEATURE_READYTORUN - if (call->gtCallType == CT_HELPER) + if (call->IsHelperCall()) { CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(methHnd); noway_assert(helperNum != CORINFO_HELP_UNDEF); diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 351ca14942838b..6a01b6bf3c050e 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -721,9 +721,7 @@ void CodeGen::genCodeForBBlist() if ((call != nullptr) && (call->gtOper == GT_CALL)) { - if ((call->AsCall()->gtCallMoreFlags & GTF_CALL_M_DOES_NOT_RETURN) != 0 || - ((call->AsCall()->gtCallType == CT_HELPER) && - Compiler::s_helperCallProperties.AlwaysThrow(call->AsCall()->GetHelperNum()))) + if (call->AsCall()->IsNoReturn()) { instGen(INS_BREAKPOINT); // This should never get executed } @@ -761,19 +759,14 @@ void CodeGen::genCodeForBBlist() case BBJ_ALWAYS: { +#ifdef DEBUG GenTree* call = block->lastNode(); if ((call != nullptr) && (call->gtOper == GT_CALL)) { - if ((call->AsCall()->gtCallMoreFlags & GTF_CALL_M_DOES_NOT_RETURN) != 0 || - ((call->AsCall()->gtCallType == CT_HELPER) && - Compiler::s_helperCallProperties.AlwaysThrow(call->AsCall()->GetHelperNum()))) - { - // NOTE: We should probably never see a BBJ_ALWAYS block ending with a throw in a first place. - // If that is fixed, this condition can be just an assert. - // For the reasons why we insert a BP, see the similar code in "case BBJ_THROW:" above. - instGen(INS_BREAKPOINT); // This should never get executed - } + // At this point, BBJ_ALWAYS should never end with a call that doesn't return. + assert(!call->AsCall()->IsNoReturn()); } +#endif // DEBUG // If this block jumps to the next one, we might be able to skip emitting the jump if (block->CanRemoveJumpToNext(compiler)) diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index 2fba5633838a60..fdb99f1c29f5c9 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -6518,7 +6518,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) #ifdef DEBUG // Pass the call signature information down into the emitter so the emitter can associate // native call sites with the signatures they were generated from. - if (call->gtCallType != CT_HELPER) + if (!call->IsHelperCall()) { sigInfo = call->callSig; } @@ -6635,7 +6635,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) else { // Generate a direct call to a non-virtual user defined or helper method - assert(call->gtCallType == CT_HELPER || call->gtCallType == CT_USER_FUNC); + assert(call->IsHelperCall() || (call->gtCallType == CT_USER_FUNC)); void* addr = nullptr; #ifdef FEATURE_READYTORUN @@ -6646,7 +6646,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) } else #endif // FEATURE_READYTORUN - if (call->gtCallType == CT_HELPER) + if (call->IsHelperCall()) { CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(methHnd); noway_assert(helperNum != CORINFO_HELP_UNDEF); diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 6e6703e9e2db76..546ba7b3180899 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -6594,7 +6594,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) #ifdef DEBUG // Pass the call signature information down into the emitter so the emitter can associate // native call sites with the signatures they were generated from. - if (call->gtCallType != CT_HELPER) + if (!call->IsHelperCall()) { sigInfo = call->callSig; } @@ -6711,7 +6711,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) else { // Generate a direct call to a non-virtual user defined or helper method - assert(call->gtCallType == CT_HELPER || call->gtCallType == CT_USER_FUNC); + assert(call->IsHelperCall() || (call->gtCallType == CT_USER_FUNC)); void* addr = nullptr; #ifdef FEATURE_READYTORUN @@ -6722,7 +6722,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) } else #endif // FEATURE_READYTORUN - if (call->gtCallType == CT_HELPER) + if (call->IsHelperCall()) { CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(methHnd); noway_assert(helperNum != CORINFO_HELP_UNDEF); diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 3e5f1a4b38a691..7c5b0563345a3c 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -6132,7 +6132,7 @@ void CodeGen::genCall(GenTreeCall* call) // This needs to be here, rather than above where fPossibleSyncHelperCall is set, // so the GC state vars have been updated before creating the label. - if ((call->gtCallType == CT_HELPER) && (compiler->info.compFlags & CORINFO_FLG_SYNCH)) + if (call->IsHelperCall() && (compiler->info.compFlags & CORINFO_FLG_SYNCH)) { CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(call->gtCallMethHnd); noway_assert(helperNum != CORINFO_HELP_UNDEF); @@ -6239,7 +6239,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA #ifdef DEBUG // Pass the call signature information down into the emitter so the emitter can associate // native call sites with the signatures they were generated from. - if (call->gtCallType != CT_HELPER) + if (!call->IsHelperCall()) { sigInfo = call->callSig; } @@ -6439,10 +6439,10 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA else { // Generate a direct call to a non-virtual user defined or helper method - assert(call->gtCallType == CT_HELPER || call->gtCallType == CT_USER_FUNC); + assert(call->IsHelperCall() || (call->gtCallType == CT_USER_FUNC)); void* addr = nullptr; - if (call->gtCallType == CT_HELPER) + if (call->IsHelperCall()) { // Direct call to a helper method. CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(methHnd); diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index c834a4f6f27810..f8dd928ec5a769 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -8407,7 +8407,7 @@ void Compiler::compCallArgStats() if (call->AsCall()->gtCallThisArg == nullptr) { - if (call->AsCall()->gtCallType == CT_HELPER) + if (call->AsCall()->IsHelperCall()) { argHelperCalls++; } diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 05b3e07ebf63d1..fbcb783ff11893 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7412,6 +7412,14 @@ class Compiler optNoReturnCallCount++; } + void setCallDoesNotReturn(GenTreeCall* const call) + { + assert(call != nullptr); + assert(!call->IsNoReturn()); + call->gtCallMoreFlags |= GTF_CALL_M_DOES_NOT_RETURN; + setMethodHasNoReturnCalls(); + } + unsigned optNoReturnCallCount; // Recursion bound controls how far we can go backwards tracking for a SSA value. diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 3c9b9ac9e5e284..022a92bd740a3f 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -1384,10 +1384,17 @@ inline GenTree* Compiler::gtNewIconEmbFldHndNode(CORINFO_FIELD_HANDLE fldHnd) inline GenTreeCall* Compiler::gtNewHelperCallNode( unsigned helper, var_types type, GenTree* arg1, GenTree* arg2, GenTree* arg3) { - GenTreeFlags flags = s_helperCallProperties.NoThrow((CorInfoHelpFunc)helper) ? GTF_EMPTY : GTF_EXCEPT; - GenTreeCall* result = gtNewCallNode(CT_HELPER, eeFindHelper(helper), type); - result->gtFlags |= flags; + GenTreeCall* const result = gtNewCallNode(CT_HELPER, eeFindHelper(helper), type); + if (!s_helperCallProperties.NoThrow((CorInfoHelpFunc)helper)) + { + result->gtFlags |= GTF_EXCEPT; + + if (s_helperCallProperties.AlwaysThrow((CorInfoHelpFunc)helper)) + { + setCallDoesNotReturn(result); + } + } #if DEBUG // Helper calls are never candidates. @@ -3747,7 +3754,7 @@ inline bool Compiler::IsStaticHelperEligibleForExpansion(GenTree* tree, bool* is inline bool Compiler::IsSharedStaticHelper(GenTree* tree) { - if (tree->gtOper != GT_CALL || tree->AsCall()->gtCallType != CT_HELPER) + if (!tree->OperIs(GT_CALL) || !tree->AsCall()->IsHelperCall()) { return false; } diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 37683b188c303b..61ab0e9fe720c9 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -2645,9 +2645,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed info.compCompHnd->notifyMethodInfoUsage(impInlineInfo->iciCall->gtCallMethHnd)) { // Mark the call node as "no return" as it can impact caller's code quality. - impInlineInfo->iciCall->gtCallMoreFlags |= GTF_CALL_M_DOES_NOT_RETURN; - // Mark root method as containing a noreturn call. - impInlineRoot()->setMethodHasNoReturnCalls(); + setCallDoesNotReturn(impInlineInfo->iciCall); // NOTE: we also ask VM whether we're allowed to do so - we don't want to mark a call // as "no-return" if its IL may change. diff --git a/src/coreclr/jit/fgehopt.cpp b/src/coreclr/jit/fgehopt.cpp index 47127fc0ad20fb..a26eaf0bfa8b3d 100644 --- a/src/coreclr/jit/fgehopt.cpp +++ b/src/coreclr/jit/fgehopt.cpp @@ -1910,7 +1910,7 @@ PhaseStatus Compiler::fgTailMergeThrows() // The second pass modifies flow so that predecessors of // non-canonical throw blocks now transfer control to the // appropriate canonical block. - int numCandidates = 0; + unsigned numCandidates = 0; // First pass // @@ -1960,9 +1960,6 @@ PhaseStatus Compiler::fgTailMergeThrows() continue; } - // Sanity check -- only user funcs should be marked do not return - assert(call->gtCallType == CT_USER_FUNC); - // Ok, we've found a suitable call. See if this is one we know // about already, or something new. BasicBlock* canonicalBlock = nullptr; @@ -1987,6 +1984,8 @@ PhaseStatus Compiler::fgTailMergeThrows() } } + assert(numCandidates <= optNoReturnCallCount); + // Bail if no candidates were found if (numCandidates == 0) { diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 0f0fb3c7c484ea..6812dfbffdb7e9 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -910,6 +910,12 @@ void Compiler::fgMorphCallInline(GenTreeCall* call, InlineResult* inlineResult) noway_assert(fgMorphStmt->GetRootNode() == call); fgMorphStmt->SetRootNode(gtNewNothingNode()); } + + // Inlinee compiler may have determined call does not return; if so, update this compiler's state. + if (call->IsNoReturn()) + { + setMethodHasNoReturnCalls(); + } } } @@ -1520,7 +1526,7 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo) } // - // At this point, we have successully inserted inlinee's code. + // At this point, we have successfully inserted inlinee's code. // // @@ -1581,6 +1587,9 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo) } } + // Update no-return call count + optNoReturnCallCount += InlineeCompiler->optNoReturnCallCount; + // Update optMethodFlags #ifdef DEBUG diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 600907981ad377..6f3a3af6f34eaf 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -609,8 +609,9 @@ bool Compiler::fgIsThrow(GenTree* tree) return false; } GenTreeCall* call = tree->AsCall(); - if ((call->gtCallType == CT_HELPER) && s_helperCallProperties.AlwaysThrow(eeGetHelperNum(call->gtCallMethHnd))) + if (call->IsHelperCall() && s_helperCallProperties.AlwaysThrow(eeGetHelperNum(call->gtCallMethHnd))) { + assert(call->IsNoReturn()); noway_assert(call->gtFlags & GTF_EXCEPT); return true; } diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 7e90d26a3d6820..c9d258d82db82e 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -2223,7 +2223,7 @@ GenTree* Compiler::getArrayLengthFromAllocation(GenTree* tree DEBUGARG(BasicBloc { GenTreeCall* call = tree->AsCall(); - if (call->gtCallType == CT_HELPER) + if (call->IsHelperCall()) { CorInfoHelpFunc helper = eeGetHelperNum(call->gtCallMethHnd); switch (helper) @@ -2380,7 +2380,7 @@ bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool { // Generally all GT_CALL nodes are considered to have side-effects, but we may have extra information about helper // calls that can prove them side-effect-free. - if (gtCallType != CT_HELPER) + if (!IsHelperCall()) { // If needed, we can annotate other special intrinsic methods as side effect free as well. if (IsSpecialIntrinsic(compiler, NI_System_Type_GetTypeFromHandle)) @@ -10997,7 +10997,7 @@ void Compiler::gtDispNodeName(GenTree* tree) callType = "CALLV"; } } - else if (tree->AsCall()->gtCallType == CT_HELPER) + else if (tree->AsCall()->IsHelperCall()) { ctType = " help"; } @@ -16726,7 +16726,7 @@ bool Compiler::gtTreeHasSideEffects(GenTree* tree, GenTreeFlags flags /* = GTF_S { // Generally all trees that contain GT_CALL nodes are considered to have side-effects. // - if (tree->AsCall()->gtCallType == CT_HELPER) + if (tree->AsCall()->IsHelperCall()) { // If this node is a helper call we may not care about the side-effects. // Note that gtNodeHasSideEffects checks the side effects of the helper itself @@ -17188,7 +17188,7 @@ void Compiler::gtExtractSideEffList(GenTree* expr, // Generally all GT_CALL nodes are considered to have side-effects. // So if we get here it must be a helper call that we decided it does // not have side effects that we needed to keep. - assert(!node->OperIs(GT_CALL) || (node->AsCall()->gtCallType == CT_HELPER)); + assert(!node->OperIs(GT_CALL) || node->AsCall()->IsHelperCall()); } if ((m_flags & GTF_IS_IN_CSE) != 0) @@ -17486,7 +17486,7 @@ Compiler::TypeProducerKind Compiler::gtGetTypeProducerKind(GenTree* tree) { if (tree->gtOper == GT_CALL) { - if (tree->AsCall()->gtCallType == CT_HELPER) + if (tree->AsCall()->IsHelperCall()) { if (gtIsTypeHandleToRuntimeTypeHelper(tree->AsCall())) { @@ -18568,7 +18568,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b objClass = sig.retTypeClass; } } - else if (call->gtCallType == CT_HELPER) + else if (call->IsHelperCall()) { objClass = gtGetHelperCallClassHandle(call, pIsExact, pIsNonNull); } @@ -18735,7 +18735,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b // CORINFO_CLASS_HANDLE Compiler::gtGetHelperCallClassHandle(GenTreeCall* call, bool* pIsExact, bool* pIsNonNull) { - assert(call->gtCallType == CT_HELPER); + assert(call->IsHelperCall()); *pIsNonNull = false; *pIsExact = false; @@ -27082,7 +27082,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet() const GenTree* Compiler::gtNewMustThrowException(unsigned helper, var_types type, CORINFO_CLASS_HANDLE clsHnd) { GenTreeCall* node = gtNewHelperCallNode(helper, TYP_VOID); - node->gtCallMoreFlags |= GTF_CALL_M_DOES_NOT_RETURN; + assert(node->IsNoReturn()); if (type != TYP_VOID) { unsigned dummyTemp = lvaGrabTemp(true DEBUGARG("dummy temp of must thrown exception")); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index d5dbad500c16d2..c5ada2379c787f 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -9987,7 +9987,7 @@ inline bool GenTree::IsCnsVec() const inline bool GenTree::IsHelperCall() { - return OperGet() == GT_CALL && AsCall()->gtCallType == CT_HELPER; + return OperGet() == GT_CALL && AsCall()->IsHelperCall(); } inline var_types GenTree::CastFromType() diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index 6c8251eee257f5..17d6a6d4830077 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -2440,7 +2440,7 @@ bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt, if (typeCheckNotNeeded || (typeCheckFailedAction == TypeCheckFailedAction::CallHelper_AlwaysThrows)) { // fallback call is used only to throw InvalidCastException - call->gtCallMoreFlags |= GTF_CALL_M_DOES_NOT_RETURN; + setCallDoesNotReturn(call); fallbackBb = fgNewBBFromTreeAfter(BBJ_THROW, lastTypeCheckBb, call, debugInfo, true); } else if (typeCheckFailedAction == TypeCheckFailedAction::ReturnNull) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 66769f037e2826..7fba06f2b7b3c0 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -1437,8 +1437,8 @@ var_types Compiler::impImportCall(OPCODE opcode, if (call->IsCall()) { GenTreeCall* callNode = call->AsCall(); - if ((callNode->gtCallType == CT_HELPER) && (gtIsTypeHandleToRuntimeTypeHelper(callNode) || - gtIsTypeHandleToRuntimeTypeHandleHelper(callNode))) + if (callNode->IsHelperCall() && (gtIsTypeHandleToRuntimeTypeHelper(callNode) || + gtIsTypeHandleToRuntimeTypeHandleHelper(callNode))) { spillStack = false; } @@ -2362,8 +2362,8 @@ GenTree* Compiler::impInitializeArrayIntrinsic(CORINFO_SIG_INFO* sig) // // Check to see if the ldtoken helper call is what we see here. - if (fieldTokenNode->gtOper != GT_CALL || (fieldTokenNode->AsCall()->gtCallType != CT_HELPER) || - (fieldTokenNode->AsCall()->gtCallMethHnd != eeFindHelper(CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD))) + if (!fieldTokenNode->OperIs(GT_CALL) || + !fieldTokenNode->AsCall()->IsHelperCall(eeFindHelper(CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD))) { return nullptr; } @@ -2416,7 +2416,7 @@ GenTree* Compiler::impInitializeArrayIntrinsic(CORINFO_SIG_INFO* sig) // GenTree* newArrayCall = arrayLocalStore->AsLclVar()->Data(); - if ((newArrayCall->gtOper != GT_CALL) || (newArrayCall->AsCall()->gtCallType != CT_HELPER)) + if (!newArrayCall->OperIs(GT_CALL) || !newArrayCall->AsCall()->IsHelperCall()) { return nullptr; } @@ -2705,8 +2705,8 @@ GenTree* Compiler::impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig) // // Check to see if the ldtoken helper call is what we see here. - if (fieldTokenNode->gtOper != GT_CALL || (fieldTokenNode->AsCall()->gtCallType != CT_HELPER) || - (fieldTokenNode->AsCall()->gtCallMethHnd != eeFindHelper(CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD))) + if (!fieldTokenNode->OperIs(GT_CALL) || + !fieldTokenNode->AsCall()->IsHelperCall(eeFindHelper(CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD))) { return nullptr; } @@ -3616,7 +3616,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, { GenTree* op1 = impStackTop(0).val; CorInfoHelpFunc typeHandleHelper; - if (op1->gtOper == GT_CALL && (op1->AsCall()->gtCallType == CT_HELPER) && + if (op1->gtOper == GT_CALL && op1->AsCall()->IsHelperCall() && gtIsTypeHandleToRuntimeTypeHandleHelper(op1->AsCall(), &typeHandleHelper)) { op1 = impPopStack().val; @@ -7101,7 +7101,7 @@ void Compiler::impMarkInlineCandidateHelper(GenTreeCall* call, /* Ignore helper calls */ - if (call->gtCallType == CT_HELPER) + if (call->IsHelperCall()) { assert(!call->IsGuardedDevirtualizationCandidate()); inlineResult->NoteFatal(InlineObservation::CALLSITE_IS_CALL_TO_HELPER); diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index 2f3f23826f8568..c67e44a3de8b00 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -262,7 +262,7 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree) { GenTreeCall* call = tree->AsCall(); bool modHeap = true; - if (call->gtCallType == CT_HELPER) + if (call->IsHelperCall()) { CorInfoHelpFunc helpFunc = eeGetHelperNum(call->gtCallMethHnd); diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index ea061b9ef75b76..9790d67a93f5f6 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -5200,7 +5200,7 @@ GenTreeLclVar* Lowering::SpillStructCallResult(GenTreeCall* call) const GenTree* Lowering::LowerDirectCall(GenTreeCall* call) { - noway_assert(call->gtCallType == CT_USER_FUNC || call->gtCallType == CT_HELPER); + noway_assert(call->gtCallType == CT_USER_FUNC || call->IsHelperCall()); // Non-virtual direct/indirect calls: Work out if the address of the // call is known at JIT time. If not it is either an indirect call @@ -5218,7 +5218,7 @@ GenTree* Lowering::LowerDirectCall(GenTreeCall* call) } else #endif - if (call->gtCallType == CT_HELPER) + if (call->IsHelperCall()) { noway_assert(helperNum != CORINFO_HELP_UNDEF); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 8e9b07e8da6ccf..fe5cb92ebff176 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -6490,7 +6490,7 @@ GenTree* Compiler::fgMorphTailCallViaHelpers(GenTreeCall* call, CORINFO_TAILCALL DISPTREE(call); // Don't support tail calling helper methods - assert(call->gtCallType != CT_HELPER); + assert(!call->IsHelperCall()); // We come this route only for tail prefixed calls that cannot be dispatched as // fast tail calls @@ -7042,7 +7042,7 @@ void Compiler::fgMorphTailCallViaJitHelper(GenTreeCall* call) assert(call->IsVirtual() || (call->gtCallType != CT_INDIRECT) || (call->gtCallCookie == nullptr)); // Don't support tail calling helper methods - assert(call->gtCallType != CT_HELPER); + assert(!call->IsHelperCall()); // We come this route only for tail prefixed calls that cannot be dispatched as // fast tail calls @@ -7758,7 +7758,7 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) // Morph stelem.ref helper call to store a null value, into a store into an array without the helper. // This needs to be done after the arguments are morphed to ensure constant propagation has already taken place. - if (opts.OptimizationEnabled() && (call->gtCallType == CT_HELPER) && + if (opts.OptimizationEnabled() && call->IsHelperCall() && (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_ARRADDR_ST))) { assert(call->gtArgs.CountArgs() == 3); @@ -14712,12 +14712,11 @@ bool Compiler::fgExpandQmarkStmt(BasicBlock* block, Statement* stmt) if (hasTrueExpr) { - if (trueExpr->OperIs(GT_CALL) && (trueExpr->AsCall()->gtCallMoreFlags & GTF_CALL_M_DOES_NOT_RETURN)) + if (trueExpr->OperIs(GT_CALL) && trueExpr->AsCall()->IsNoReturn()) { Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->GetDebugInfo()); fgInsertStmtAtEnd(thenBlock, trueStmt); fgConvertBBToThrowBB(thenBlock); - setMethodHasNoReturnCalls(); introducedThrow = true; } else @@ -14736,12 +14735,11 @@ bool Compiler::fgExpandQmarkStmt(BasicBlock* block, Statement* stmt) // Assign the falseExpr into the dst or tmp, insert in elseBlock if (hasFalseExpr) { - if (falseExpr->OperIs(GT_CALL) && (falseExpr->AsCall()->gtCallMoreFlags & GTF_CALL_M_DOES_NOT_RETURN)) + if (falseExpr->OperIs(GT_CALL) && falseExpr->AsCall()->IsNoReturn()) { Statement* falseStmt = fgNewStmtFromTree(falseExpr, stmt->GetDebugInfo()); fgInsertStmtAtEnd(elseBlock, falseStmt); fgConvertBBToThrowBB(elseBlock); - setMethodHasNoReturnCalls(); introducedThrow = true; } else diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 0c995997d81388..3d6206e0f723cf 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -637,7 +637,7 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack* parent { GenTreeCall* asCall = parent->AsCall(); - if (asCall->gtCallType == CT_HELPER) + if (asCall->IsHelperCall()) { // TODO-ObjectStackAllocation: Special-case helpers here that // 1. Don't make objects escape. diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp index 7fcaac35c43b45..55533e306d8f40 100644 --- a/src/coreclr/jit/optcse.cpp +++ b/src/coreclr/jit/optcse.cpp @@ -1869,7 +1869,7 @@ bool CSE_HeuristicCommon::CanConsiderTree(GenTree* tree, bool isReturn) // more exceptions (NullRef) so we abandon this CSE. // If we don't mark CALL ALLOC_HELPER as a CSE candidate, we are able // to use GT_IND(x) in [2] as a CSE def. - if ((call->gtCallType == CT_HELPER) && + if (call->IsHelperCall() && Compiler::s_helperCallProperties.IsAllocator(m_pCompiler->eeGetHelperNum(call->gtCallMethHnd))) { return false; diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 2767686ecfd7cb..09921b2d3aebe8 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -4899,7 +4899,7 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop, if (op1->OperIs(GT_CALL)) { GenTreeCall* call = op1->AsCall(); - if ((call->gtCallType == CT_HELPER) && + if (call->IsHelperCall() && s_helperCallProperties.MayRunCctor(eeGetHelperNum(call->gtCallMethHnd))) { // Hoisting the comma is ok because it would hoist the initialization along @@ -4945,7 +4945,7 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop, if (treeIsHoistable && tree->IsCall()) { GenTreeCall* call = tree->AsCall(); - if (call->gtCallType != CT_HELPER) + if (!call->IsHelperCall()) { INDEBUG(failReason = "non-helper call";) treeIsHoistable = false; @@ -5027,7 +5027,7 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop, // Further, if it may run a cctor, it must be labeled as "Hoistable" // (meaning it won't run a cctor because the class is not precise-init). GenTreeCall* call = tree->AsCall(); - if (call->gtCallType != CT_HELPER) + if (!call->IsHelperCall()) { m_beforeSideEffect = false; } @@ -5759,7 +5759,7 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk, FlowGraphNatura // Record that this loop contains a call AddContainsCallAllContainingLoops(mostNestedLoop); - if (call->gtCallType == CT_HELPER) + if (call->IsHelperCall()) { CorInfoHelpFunc helpFunc = eeGetHelperNum(call->gtCallMethHnd); if (s_helperCallProperties.MutatesHeap(helpFunc)) diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 65239b356e461f..a38a5bdede2bc9 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -12685,7 +12685,7 @@ bool Compiler::fgValueNumberSpecialIntrinsic(GenTreeCall* call) void Compiler::fgValueNumberCall(GenTreeCall* call) { - if (call->gtCallType == CT_HELPER) + if (call->IsHelperCall()) { bool modHeap = fgValueNumberHelperCall(call);