Repository: dotnet/runtime
File: src/coreclr/jit/fgbasic.cpp (C++)

Code before:
    if (compDonotInline())
    {
        return;
    }

    unsigned XTnum;

    // Are there any exception handlers?
    //
    if (info.compXcptnsCount > 0)
    {
        assert(!compIsForInlining() || opts.compInlineMethodsWithEH);

        if (compIsForInlining())
        {
            // Verify we can expand the EH table as needed to incorporate the callee's EH clauses.
            // Failing here should be extremely rare.
            //
            unsigned numEHEntries = info.compXcptnsCount;
            // We will introduce another EH clause before inlining finishes to restore async contexts
            if ((info.compMethodInfo->options & CORINFO_ASYNC_SAVE_CONTEXTS) != 0)
            {
                numEHEntries++;
            }

            EHblkDsc* const dsc = fgTryAddEHTableEntries(0, numEHEntries, /* deferAdding */ true);
            if (dsc == nullptr)
            {
                compInlineResult->NoteFatal(InlineObservation::CALLSITE_EH_TABLE_FULL);
            }
        }

        // Check and mark all the exception handlers
        //
        for (XTnum = 0; XTnum < info.compXcptnsCount; XTnum++)
        {
            CORINFO_EH_CLAUSE clause;
            info.compCompHnd->getEHinfo(info.compMethodHnd, XTnum, &clause);
            noway_assert(clause.HandlerLength != (unsigned)-1);

            // If we're inlining, and the inlinee has a catch clause, we are currently
            // unable to convey the type of the catch properly, as it is represented
            // by a token. So, abandon inlining.
            //
            // TODO: if inlining methods with catches is rare, consider
            // transforming class catches into runtime filters like we do in
            // fgCreateFiltersForGenericExceptions
            //
            if (compIsForInlining())
            {
                const bool isFinallyFaultOrFilter =

Review comment (bug):
The inlinee EH-table capacity pre-check is only executed when `info.compXcptnsCount > 0`, but async methods with `CORINFO_ASYNC_SAVE_CONTEXTS` can still gain an additional EH clause via `SaveAsyncContexts()` even when the IL has no EH. In that case the root inliner may later try to grow the root EH table for this inlinee (see `fginline.cpp`’s unconditional `fgTryAddEHTableEntries(...); assert(outermostEbd != nullptr);`) without having performed this safety check, risking failure/corruption when the EH index space is exhausted. Consider running this pre-check whenever `CORINFO_ASYNC_SAVE_CONTEXTS` is set (even if `compXcptnsCount == 0`), or otherwise ensuring the check accounts for the final inlinee EH clause count after `SaveAsyncContexts()`.

Code after:
    if (compDonotInline())
    {
        return;
    }

    unsigned XTnum;

    // Are there any exception handlers?
    //
    if (info.compXcptnsCount > 0 || ((info.compMethodInfo->options & CORINFO_ASYNC_SAVE_CONTEXTS) != 0))
    {
        assert(!compIsForInlining() || opts.compInlineMethodsWithEH);

        if (compIsForInlining())
        {
            // Verify we can expand the EH table as needed to incorporate the callee's EH clauses.
            // Failing here should be extremely rare.
            //
            unsigned numEHEntries = info.compXcptnsCount;
            // We will introduce another EH clause before inlining finishes to restore async contexts
            if ((info.compMethodInfo->options & CORINFO_ASYNC_SAVE_CONTEXTS) != 0)
            {
                numEHEntries++;
            }

            EHblkDsc* const dsc = fgTryAddEHTableEntries(0, numEHEntries, /* deferAdding */ true);
            if (dsc == nullptr)
            {
                compInlineResult->NoteFatal(InlineObservation::CALLSITE_EH_TABLE_FULL);
            }
        }

        // Check and mark all the exception handlers
        //
        for (XTnum = 0; XTnum < info.compXcptnsCount; XTnum++)
        {
            CORINFO_EH_CLAUSE clause;
            info.compCompHnd->getEHinfo(info.compMethodHnd, XTnum, &clause);
            noway_assert(clause.HandlerLength != (unsigned)-1);

            // If we're inlining, and the inlinee has a catch clause, we are currently
            // unable to convey the type of the catch properly, as it is represented
            // by a token. So, abandon inlining.
            //
            // TODO: if inlining methods with catches is rare, consider
            // transforming class catches into runtime filters like we do in
            // fgCreateFiltersForGenericExceptions
            //
            if (compIsForInlining())
            {
                const bool isFinallyFaultOrFilter =