void Compiler::fgFindJumpTargets()

in src/coreclr/jit/fgbasic.cpp [1087:2747]


void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, FixedBitVect* jumpTarget)
{
    const BYTE* codeBegp = codeAddr;
    const BYTE* codeEndp = codeAddr + codeSize;
    unsigned    varNum;
    var_types   varType      = DUMMY_INIT(TYP_UNDEF); // TYP_ type
    bool        typeIsNormed = false;
    FgStack     pushedStack;
    const bool  isForceInline = (info.compFlags & CORINFO_FLG_FORCEINLINE) != 0;
    const bool  isInlining    = compIsForInlining();
    unsigned    retBlocks     = 0;
    int         prefixFlags   = 0;
    bool        preciseScan   = makeInlineObservations && compInlineResult->GetPolicy()->RequiresPreciseScan();
    const bool  resolveTokens = preciseScan;

    // Track offsets where IL instructions begin in DEBUG builds. Used to
    // validate debug info generated by the JIT.
    assert(codeSize == compInlineContext->GetILSize());
    INDEBUG(FixedBitVect* ilInstsSet = FixedBitVect::bitVectInit(codeSize, this));

    if (makeInlineObservations)
    {
        // Set default values for profile (to avoid NoteFailed in CALLEE_IL_CODE_SIZE's handler)
        // these will be overridden later.
        compInlineResult->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE_WEIGHTS, true);
        compInlineResult->NoteDouble(InlineObservation::CALLSITE_PROFILE_FREQUENCY, 1.0);
        // Observe force inline state and code size.
        compInlineResult->NoteBool(InlineObservation::CALLEE_IS_FORCE_INLINE, isForceInline);
        compInlineResult->NoteInt(InlineObservation::CALLEE_IL_CODE_SIZE, codeSize);

        // Determine if call site is within a try.
        if (isInlining && impInlineInfo->iciBlock->hasTryIndex())
        {
            compInlineResult->Note(InlineObservation::CALLSITE_IN_TRY_REGION);
        }

        // Determine if the call site is in a no-return block
        if (isInlining && impInlineInfo->iciBlock->KindIs(BBJ_THROW))
        {
            compInlineResult->Note(InlineObservation::CALLSITE_IN_NORETURN_REGION);
        }

        // Determine if the call site is in a loop.
        if (isInlining && impInlineInfo->iciBlock->HasFlag(BBF_BACKWARD_JUMP))
        {
            compInlineResult->Note(InlineObservation::CALLSITE_IN_LOOP);
        }

#ifdef DEBUG

        // If inlining, this method should still be a candidate.
        if (isInlining)
        {
            assert(compInlineResult->IsCandidate());
        }

#endif // DEBUG

        // note that we're starting to look at the opcodes.
        compInlineResult->Note(InlineObservation::CALLEE_BEGIN_OPCODE_SCAN);
    }

    CORINFO_RESOLVED_TOKEN resolvedToken;

    OPCODE opcode     = CEE_NOP;
    OPCODE prevOpcode = CEE_NOP;
    bool   handled    = false;
    while (codeAddr < codeEndp)
    {
        prevOpcode = opcode;
        opcode     = (OPCODE)getU1LittleEndian(codeAddr);

        INDEBUG(ilInstsSet->bitVectSet((UINT)(codeAddr - codeBegp)));

        codeAddr += sizeof(int8_t);

        if (!handled && preciseScan)
        {
            // Push something unknown to the stack since we couldn't find anything useful for inlining
            pushedStack.PushUnknown();
        }
        handled = false;

    DECODE_OPCODE:

        if ((unsigned)opcode >= CEE_COUNT)
        {
            BADCODE3("Illegal opcode", ": %02X", (int)opcode);
        }

        if ((opcode >= CEE_LDARG_0 && opcode <= CEE_STLOC_S) || (opcode >= CEE_LDARG && opcode <= CEE_STLOC))
        {
            opts.lvRefCount++;
        }

        if (makeInlineObservations && (opcode >= CEE_LDNULL) && (opcode <= CEE_LDC_R8))
        {
            // LDTOKEN and LDSTR are handled below
            pushedStack.PushConstant();
            handled = true;
        }

        unsigned sz = opcodeSizes[opcode];

        switch (opcode)
        {
            case CEE_PREFIX1:
            {
                if (codeAddr >= codeEndp)
                {
                    goto TOO_FAR;
                }
                opcode = (OPCODE)(256 + getU1LittleEndian(codeAddr));
                codeAddr += sizeof(int8_t);
                goto DECODE_OPCODE;
            }

            case CEE_PREFIX2:
            case CEE_PREFIX3:
            case CEE_PREFIX4:
            case CEE_PREFIX5:
            case CEE_PREFIX6:
            case CEE_PREFIX7:
            case CEE_PREFIXREF:
            {
                BADCODE3("Illegal opcode", ": %02X", (int)opcode);
            }

            case CEE_SIZEOF:
            case CEE_LDTOKEN:
            case CEE_LDSTR:
            {
                if (preciseScan)
                {
                    pushedStack.PushConstant();
                    handled = true;
                }
                break;
            }

            case CEE_DUP:
            {
                if (preciseScan)
                {
                    pushedStack.Push(pushedStack.Top());
                    handled = true;
                }
                break;
            }

            case CEE_THROW:
            {
                if (makeInlineObservations)
                {
                    compInlineResult->Note(InlineObservation::CALLEE_THROW_BLOCK);
                }
                break;
            }

            case CEE_BOX:
            {
                if (makeInlineObservations)
                {
                    int toSkip =
                        impBoxPatternMatch(nullptr, codeAddr + sz, codeEndp, BoxPatterns::MakeInlineObservation);
                    if (toSkip > 0)
                    {
                        // toSkip > 0 means we most likely will hit a pattern (e.g. box+isinst+brtrue) that
                        // will be folded into a const

                        if (preciseScan)
                        {
                            codeAddr += toSkip;
                        }
                    }
                }
                break;
            }

            case CEE_CASTCLASS:
            case CEE_ISINST:
            {
                if (makeInlineObservations)
                {
                    FgStack::FgSlot slot = pushedStack.Top();
                    if (FgStack::IsConstantOrConstArg(slot, impInlineInfo) ||
                        FgStack::IsExactArgument(slot, impInlineInfo))
                    {
                        compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR_UN);
                        handled = true; // and keep argument in the pushedStack
                    }
                    else if (FgStack::IsArgument(slot))
                    {
                        compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_CAST);
                        handled = true; // and keep argument in the pushedStack
                    }
                }
                break;
            }

            case CEE_CALL:
            case CEE_CALLVIRT:
            {
                // There has to be code after the call, otherwise the inlinee is unverifiable.
                if (isInlining)
                {
                    noway_assert(codeAddr < codeEndp - sz);
                }

                if (!makeInlineObservations)
                {
                    break;
                }

                CORINFO_METHOD_HANDLE methodHnd   = nullptr;
                bool                  isIntrinsic = false;
                NamedIntrinsic        ni          = NI_Illegal;

                if (resolveTokens)
                {
                    impResolveToken(codeAddr, &resolvedToken, CORINFO_TOKENKIND_Method);
                    methodHnd   = resolvedToken.hMethod;
                    isIntrinsic = eeIsIntrinsic(methodHnd);
                }

                if (isIntrinsic)
                {
                    ni = lookupNamedIntrinsic(methodHnd);

                    bool foldableIntrinsic = false;

                    if (IsMathIntrinsic(ni))
                    {
                        // Most Math(F) intrinsics have single arguments
                        foldableIntrinsic = FgStack::IsConstantOrConstArg(pushedStack.Top(), impInlineInfo);
                    }
                    else
                    {
                        switch (ni)
                        {
                            // These are most likely foldable without arguments
                            case NI_System_Collections_Generic_Comparer_get_Default:
                            case NI_System_Collections_Generic_EqualityComparer_get_Default:
                            case NI_System_Enum_HasFlag:
                            case NI_System_GC_KeepAlive:
                            {
                                pushedStack.PushUnknown();
                                foldableIntrinsic = true;
                                break;
                            }

                            case NI_System_SpanHelpers_ClearWithoutReferences:
                            case NI_System_SpanHelpers_Fill:
                            case NI_System_SpanHelpers_SequenceEqual:
                            case NI_System_SpanHelpers_Memmove:
                            {
                                if (FgStack::IsConstArgument(pushedStack.Top(), impInlineInfo))
                                {
                                    // Constant (at its call-site) argument feeds the Memmove/Memcmp length argument.
                                    // We most likely will be able to unroll it.
                                    // It is important to only raise this hint for constant arguments, if it's just a
                                    // constant in the inlinee itself then we don't need to inline it for unrolling.
                                    compInlineResult->Note(InlineObservation::CALLSITE_UNROLLABLE_MEMOP);
                                }
                                break;
                            }

                            case NI_System_Span_get_Item:
                            case NI_System_ReadOnlySpan_get_Item:
                            {
                                if (FgStack::IsArgument(pushedStack.Top(0)) || FgStack::IsArgument(pushedStack.Top(1)))
                                {
                                    compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_RANGE_CHECK);
                                }
                                break;
                            }

                            case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant:
                                if (FgStack::IsConstArgument(pushedStack.Top(), impInlineInfo))
                                {
                                    compInlineResult->Note(InlineObservation::CALLEE_CONST_ARG_FEEDS_ISCONST);
                                }
                                else
                                {
                                    compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_ISCONST);
                                }
                                // RuntimeHelpers.IsKnownConstant is always folded into a const
                                pushedStack.PushConstant();
                                foldableIntrinsic = true;
                                break;

                            // These are foldable if the first argument is a constant
                            case NI_PRIMITIVE_LeadingZeroCount:
                            case NI_PRIMITIVE_Log2:
                            case NI_PRIMITIVE_PopCount:
                            case NI_PRIMITIVE_TrailingZeroCount:
                            case NI_System_Type_get_IsEnum:
                            case NI_System_Type_GetEnumUnderlyingType:
                            case NI_System_Type_get_IsValueType:
                            case NI_System_Type_get_IsPrimitive:
                            case NI_System_Type_get_IsByRefLike:
                            case NI_System_Type_get_IsGenericType:
                            case NI_System_Type_GetTypeFromHandle:
                            case NI_System_String_get_Length:
                            case NI_System_Buffers_Binary_BinaryPrimitives_ReverseEndianness:
#if defined(FEATURE_HW_INTRINSICS)
#if defined(TARGET_ARM64)
                            case NI_ArmBase_Arm64_LeadingZeroCount:
                            case NI_ArmBase_Arm64_ReverseElementBits:
                            case NI_ArmBase_LeadingZeroCount:
                            case NI_ArmBase_ReverseElementBits:
                            case NI_Vector64_Create:
                            case NI_Vector64_CreateScalar:
                            case NI_Vector64_CreateScalarUnsafe:
#endif // TARGET_ARM64
                            case NI_Vector128_Create:
                            case NI_Vector128_CreateScalar:
                            case NI_Vector128_CreateScalarUnsafe:
                            case NI_VectorT_Create:
#if defined(TARGET_XARCH)
                            case NI_BMI1_TrailingZeroCount:
                            case NI_BMI1_X64_TrailingZeroCount:
                            case NI_LZCNT_LeadingZeroCount:
                            case NI_LZCNT_X64_LeadingZeroCount:
                            case NI_POPCNT_PopCount:
                            case NI_POPCNT_X64_PopCount:
                            case NI_Vector256_Create:
                            case NI_Vector512_Create:
                            case NI_Vector256_CreateScalar:
                            case NI_Vector512_CreateScalar:
                            case NI_Vector256_CreateScalarUnsafe:
                            case NI_Vector512_CreateScalarUnsafe:
                            case NI_X86Base_BitScanForward:
                            case NI_X86Base_X64_BitScanForward:
                            case NI_X86Base_BitScanReverse:
                            case NI_X86Base_X64_BitScanReverse:
#endif // TARGET_XARCH
#endif // FEATURE_HW_INTRINSICS
                            {
                                // Top() in order to keep it as is in case of foldableIntrinsic
                                if (FgStack::IsConstantOrConstArg(pushedStack.Top(), impInlineInfo))
                                {
                                    foldableIntrinsic = true;
                                }
                                break;
                            }

                            // These are foldable if two arguments are constants
                            case NI_PRIMITIVE_RotateLeft:
                            case NI_PRIMITIVE_RotateRight:
                            case NI_System_Type_op_Equality:
                            case NI_System_Type_op_Inequality:
                            case NI_System_String_get_Chars:
                            case NI_System_Type_IsAssignableTo:
                            case NI_System_Type_IsAssignableFrom:
                            {
                                if (FgStack::IsConstantOrConstArg(pushedStack.Top(0), impInlineInfo) &&
                                    FgStack::IsConstantOrConstArg(pushedStack.Top(1), impInlineInfo))
                                {
                                    foldableIntrinsic = true;
                                    pushedStack.PushConstant();
                                }
                                break;
                            }

                            case NI_IsSupported_True:
                            case NI_IsSupported_False:
                            case NI_IsSupported_Type:
                            {
                                foldableIntrinsic = true;
                                pushedStack.PushConstant();
                                break;
                            }

                            case NI_Vector_GetCount:
                            {
                                foldableIntrinsic = true;
                                pushedStack.PushConstant();
                                // TODO: for FEATURE_SIMD check if it's a loop condition - we unroll such loops.
                                break;
                            }

                            case NI_SRCS_UNSAFE_Add:
                            case NI_SRCS_UNSAFE_AddByteOffset:
                            case NI_SRCS_UNSAFE_AreSame:
                            case NI_SRCS_UNSAFE_ByteOffset:
                            case NI_SRCS_UNSAFE_IsAddressGreaterThan:
                            case NI_SRCS_UNSAFE_IsAddressLessThan:
                            case NI_SRCS_UNSAFE_IsNullRef:
                            case NI_SRCS_UNSAFE_Subtract:
                            case NI_SRCS_UNSAFE_SubtractByteOffset:
                            {
                                // These are effectively primitive binary operations so the
                                // handling roughly mirrors the handling for CEE_ADD and
                                // friends that exists elsewhere in this method

                                if (!preciseScan)
                                {
                                    switch (ni)
                                    {
                                        case NI_SRCS_UNSAFE_AreSame:
                                        case NI_SRCS_UNSAFE_IsAddressGreaterThan:
                                        case NI_SRCS_UNSAFE_IsAddressLessThan:
                                        case NI_SRCS_UNSAFE_IsNullRef:
                                        {
                                            fgObserveInlineConstants(opcode, pushedStack, isInlining);
                                            break;
                                        }

                                        default:
                                        {
                                            break;
                                        }
                                    }
                                }
                                else
                                {
                                    // Unlike the normal binary operation handling, this is an intrinsic call that will
                                    // get replaced
                                    // with simple IR, so we care about `const op const` as well.

                                    FgStack::FgSlot arg0;

                                    bool isArg0Arg, isArg0Const, isArg1Const;
                                    bool isArg1Arg, isArg0ConstArg, isArg1ConstArg;

                                    if (ni == NI_SRCS_UNSAFE_IsNullRef)
                                    {
                                        // IsNullRef is unary, but it always compares against 0

                                        arg0 = pushedStack.Top(0);

                                        isArg0Arg      = FgStack::IsArgument(arg0);
                                        isArg0Const    = FgStack::IsConstant(arg0);
                                        isArg0ConstArg = FgStack::IsConstArgument(arg0, impInlineInfo);

                                        isArg1Arg      = false;
                                        isArg1Const    = true;
                                        isArg1ConstArg = false;
                                    }
                                    else
                                    {
                                        arg0 = pushedStack.Top(1);

                                        isArg0Arg      = FgStack::IsArgument(arg0);
                                        isArg0Const    = FgStack::IsConstant(arg0);
                                        isArg0ConstArg = FgStack::IsConstArgument(arg0, impInlineInfo);

                                        FgStack::FgSlot arg1 = pushedStack.Top(0);

                                        isArg1Arg      = FgStack::IsArgument(arg0);
                                        isArg1Const    = FgStack::IsConstant(arg1);
                                        isArg1ConstArg = FgStack::IsConstantOrConstArg(arg1, impInlineInfo);
                                    }

                                    // Const op ConstArg -> ConstArg
                                    if (isArg0Const && isArg1ConstArg)
                                    {
                                        // keep stack unchanged
                                        foldableIntrinsic = true;
                                    }
                                    // ConstArg op Const    -> ConstArg
                                    // ConstArg op ConstArg -> ConstArg
                                    else if (isArg0ConstArg && (isArg1Const || isArg1ConstArg))
                                    {
                                        if (isArg1Const)
                                        {
                                            pushedStack.Push(arg0);
                                        }
                                        foldableIntrinsic = true;
                                    }
                                    // Const op Const -> Const
                                    else if (isArg0Const && isArg1Const)
                                    {
                                        // both are constants so we still want to track this as foldable, unlike
                                        // what is done for the regulary binary operator handling, since we have
                                        // a CEE_CALL node and not something more primitive
                                        foldableIntrinsic = true;
                                    }
                                    // Arg op ConstArg
                                    // Arg op Const
                                    else if (isArg0Arg && (isArg1Const || isArg1ConstArg))
                                    {
                                        // "Arg op CNS" --> keep arg0 in the stack for the next ops
                                        pushedStack.Push(arg0);
                                        handled = true;

                                        // TODO-CQ: The normal binary operator handling pushes arg0
                                        // and tracks this as CALLEE_BINARY_EXRP_WITH_CNS. We can't trivially
                                        // do the same here without more work.
                                    }
                                    // ConstArg op Arg
                                    // Const    op Arg
                                    else if (isArg1Arg && (isArg0Const || isArg0ConstArg))
                                    {
                                        // "CNS op ARG" --> keep arg1 in the stack for the next ops
                                        handled = true;

                                        // TODO-CQ: The normal binary operator handling keeps arg1
                                        // and tracks this as CALLEE_BINARY_EXRP_WITH_CNS. We can't trivially
                                        // do the same here without more work.
                                    }

                                    // X op ConstArg
                                    if (isArg1ConstArg)
                                    {
                                        pushedStack.Push(arg0);
                                        handled = true;
                                    }
                                }

                                break;
                            }

                            case NI_SRCS_UNSAFE_AsPointer:
                            {
                                // These are effectively primitive unary operations so the
                                // handling roughly mirrors the handling for CEE_CONV_U and
                                // friends that exists elsewhere in this method

                                FgStack::FgSlot arg = pushedStack.Top();

                                if (FgStack::IsConstArgument(arg, impInlineInfo))
                                {
                                    foldableIntrinsic = true;
                                }
                                else if (FgStack::IsArgument(arg))
                                {
                                    handled = true;
                                }
                                else if (FgStack::IsConstant(arg))
                                {
                                    // input is a constant so we still want to track this as foldable, unlike
                                    // what is done for the regulary unary operator handling, since we have
                                    // a CEE_CALL node and not something more primitive
                                    foldableIntrinsic = true;
                                }

                                break;
                            }

#if defined(FEATURE_HW_INTRINSICS)
#if defined(TARGET_ARM64)
                            case NI_Vector64_As:
                            case NI_Vector64_AsByte:
                            case NI_Vector64_AsDouble:
                            case NI_Vector64_AsInt16:
                            case NI_Vector64_AsInt32:
                            case NI_Vector64_AsInt64:
                            case NI_Vector64_AsNInt:
                            case NI_Vector64_AsNUInt:
                            case NI_Vector64_AsSByte:
                            case NI_Vector64_AsSingle:
                            case NI_Vector64_AsUInt16:
                            case NI_Vector64_AsUInt32:
                            case NI_Vector64_AsUInt64:
                            case NI_Vector64_op_UnaryPlus:
#endif // TARGET_XARCH
                            case NI_Vector128_As:
                            case NI_Vector128_AsByte:
                            case NI_Vector128_AsDouble:
                            case NI_Vector128_AsInt16:
                            case NI_Vector128_AsInt32:
                            case NI_Vector128_AsInt64:
                            case NI_Vector128_AsNInt:
                            case NI_Vector128_AsNUInt:
                            case NI_Vector128_AsSByte:
                            case NI_Vector128_AsSingle:
                            case NI_Vector128_AsUInt16:
                            case NI_Vector128_AsUInt32:
                            case NI_Vector128_AsUInt64:
                            case NI_Vector128_AsVector4:
                            case NI_Vector128_op_UnaryPlus:
                            case NI_VectorT_As:
                            case NI_VectorT_AsVectorByte:
                            case NI_VectorT_AsVectorDouble:
                            case NI_VectorT_AsVectorInt16:
                            case NI_VectorT_AsVectorInt32:
                            case NI_VectorT_AsVectorInt64:
                            case NI_VectorT_AsVectorNInt:
                            case NI_VectorT_AsVectorNUInt:
                            case NI_VectorT_AsVectorSByte:
                            case NI_VectorT_AsVectorSingle:
                            case NI_VectorT_AsVectorUInt16:
                            case NI_VectorT_AsVectorUInt32:
                            case NI_VectorT_AsVectorUInt64:
                            case NI_VectorT_op_Explicit:
                            case NI_VectorT_op_UnaryPlus:
#if defined(TARGET_XARCH)
                            case NI_Vector256_As:
                            case NI_Vector256_AsByte:
                            case NI_Vector256_AsDouble:
                            case NI_Vector256_AsInt16:
                            case NI_Vector256_AsInt32:
                            case NI_Vector256_AsInt64:
                            case NI_Vector256_AsNInt:
                            case NI_Vector256_AsNUInt:
                            case NI_Vector256_AsSByte:
                            case NI_Vector256_AsSingle:
                            case NI_Vector256_AsUInt16:
                            case NI_Vector256_AsUInt32:
                            case NI_Vector256_AsUInt64:
                            case NI_Vector256_op_UnaryPlus:
                            case NI_Vector512_As:
                            case NI_Vector512_AsByte:
                            case NI_Vector512_AsDouble:
                            case NI_Vector512_AsInt16:
                            case NI_Vector512_AsInt32:
                            case NI_Vector512_AsInt64:
                            case NI_Vector512_AsNInt:
                            case NI_Vector512_AsNUInt:
                            case NI_Vector512_AsSByte:
                            case NI_Vector512_AsSingle:
                            case NI_Vector512_AsUInt16:
                            case NI_Vector512_AsUInt32:
                            case NI_Vector512_AsUInt64:
                            case NI_Vector512_op_UnaryPlus:
#endif // TARGET_XARCH
#endif // FEATURE_HW_INTRINSICS
                            case NI_SRCS_UNSAFE_As:
                            case NI_SRCS_UNSAFE_AsRef:
                            case NI_SRCS_UNSAFE_BitCast:
                            case NI_SRCS_UNSAFE_SkipInit:
                            {
                                // TODO-CQ: These are no-ops in that they never produce any IR
                                // and simply return op1 untouched. We should really track them
                                // as such and adjust the multiplier even more, but we'll settle
                                // for marking it as foldable until additional work can happen.

                                foldableIntrinsic = true;
                                break;
                            }

#if defined(FEATURE_HW_INTRINSICS)
#if defined(TARGET_ARM64)
                            case NI_Vector64_get_AllBitsSet:
                            case NI_Vector64_get_One:
                            case NI_Vector64_get_Zero:
#endif // TARGET_ARM64
                            case NI_Vector128_get_AllBitsSet:
                            case NI_Vector128_get_One:
                            case NI_Vector128_get_Zero:
                            case NI_VectorT_get_AllBitsSet:
                            case NI_VectorT_get_One:
                            case NI_VectorT_get_Zero:
#if defined(TARGET_XARCH)
                            case NI_Vector256_get_AllBitsSet:
                            case NI_Vector256_get_One:
                            case NI_Vector256_get_Zero:
                            case NI_Vector512_get_AllBitsSet:
                            case NI_Vector512_get_One:
                            case NI_Vector512_get_Zero:
#endif // TARGET_XARCH
#endif // FEATURE_HW_INTRINSICS
                            {
                                // These always produce a vector constant

                                foldableIntrinsic = true;

                                // TODO-CQ: We should really push a constant onto the stack
                                // However, this isn't trivially possible without the inliner
                                // understanding a new type of "vector constant" so it doesn't
                                // negatively impact other possible checks/handling

                                break;
                            }

                            case NI_SRCS_UNSAFE_NullRef:
                            case NI_SRCS_UNSAFE_SizeOf:
                            {
                                // These always produce a constant

                                foldableIntrinsic = true;
                                pushedStack.PushConstant();

                                break;
                            }

                            default:
                            {
                                break;
                            }
                        }
                    }

                    if (foldableIntrinsic)
                    {
                        compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_INTRINSIC);
                        handled = true;
                    }
                    else if (ni != NI_Illegal)
                    {
                        // Otherwise note "intrinsic" (most likely will be lowered as single instructions)
                        // except Math where only a few intrinsics won't end up as normal calls
                        if (!IsMathIntrinsic(ni) || IsTargetIntrinsic(ni))
                        {
                            compInlineResult->Note(InlineObservation::CALLEE_INTRINSIC);
                        }
                    }
                }

                if ((codeAddr < codeEndp - sz) && (OPCODE)getU1LittleEndian(codeAddr + sz) == CEE_RET)
                {
                    // If the method has a call followed by a ret, assume that
                    // it is a wrapper method.
                    compInlineResult->Note(InlineObservation::CALLEE_LOOKS_LIKE_WRAPPER);
                }

                if (!isIntrinsic && !handled && FgStack::IsArgument(pushedStack.Top()))
                {
                    // Optimistically assume that "call(arg)" returns something arg-dependent.
                    // However, we don't know how many args it expects and its return type.
                    handled = true;
                }
            }
            break;

            case CEE_LDIND_I1:
            case CEE_LDIND_U1:
            case CEE_LDIND_I2:
            case CEE_LDIND_U2:
            case CEE_LDIND_I4:
            case CEE_LDIND_U4:
            case CEE_LDIND_I8:
            case CEE_LDIND_I:
            case CEE_LDIND_R4:
            case CEE_LDIND_R8:
            case CEE_LDIND_REF:
            {
                if (FgStack::IsArgument(pushedStack.Top()))
                {
                    handled = true;
                }
                break;
            }

            // Unary operators:
            case CEE_CONV_I:
            case CEE_CONV_U:
            case CEE_CONV_I1:
            case CEE_CONV_I2:
            case CEE_CONV_I4:
            case CEE_CONV_I8:
            case CEE_CONV_R4:
            case CEE_CONV_R8:
            case CEE_CONV_U4:
            case CEE_CONV_U8:
            case CEE_CONV_U2:
            case CEE_CONV_U1:
            case CEE_CONV_R_UN:
            case CEE_CONV_OVF_I:
            case CEE_CONV_OVF_U:
            case CEE_CONV_OVF_I1:
            case CEE_CONV_OVF_U1:
            case CEE_CONV_OVF_I2:
            case CEE_CONV_OVF_U2:
            case CEE_CONV_OVF_I4:
            case CEE_CONV_OVF_U4:
            case CEE_CONV_OVF_I8:
            case CEE_CONV_OVF_U8:
            case CEE_CONV_OVF_I_UN:
            case CEE_CONV_OVF_U_UN:
            case CEE_CONV_OVF_I1_UN:
            case CEE_CONV_OVF_I2_UN:
            case CEE_CONV_OVF_I4_UN:
            case CEE_CONV_OVF_I8_UN:
            case CEE_CONV_OVF_U1_UN:
            case CEE_CONV_OVF_U2_UN:
            case CEE_CONV_OVF_U4_UN:
            case CEE_CONV_OVF_U8_UN:
            case CEE_NOT:
            case CEE_NEG:
            {
                if (makeInlineObservations)
                {
                    FgStack::FgSlot arg = pushedStack.Top();
                    if (FgStack::IsConstArgument(arg, impInlineInfo))
                    {
                        compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR_UN);
                        handled = true;
                    }
                    else if (FgStack::IsArgument(arg) || FgStack::IsConstant(arg))
                    {
                        handled = true;
                    }
                }
                break;
            }

            // Binary operators:
            case CEE_ADD:
            case CEE_SUB:
            case CEE_MUL:
            case CEE_DIV:
            case CEE_DIV_UN:
            case CEE_REM:
            case CEE_REM_UN:
            case CEE_AND:
            case CEE_OR:
            case CEE_XOR:
            case CEE_SHL:
            case CEE_SHR:
            case CEE_SHR_UN:
            case CEE_ADD_OVF:
            case CEE_ADD_OVF_UN:
            case CEE_MUL_OVF:
            case CEE_MUL_OVF_UN:
            case CEE_SUB_OVF:
            case CEE_SUB_OVF_UN:
            case CEE_CEQ:
            case CEE_CGT:
            case CEE_CGT_UN:
            case CEE_CLT:
            case CEE_CLT_UN:
            {
                if (!makeInlineObservations)
                {
                    break;
                }

                if (!preciseScan)
                {
                    switch (opcode)
                    {
                        case CEE_CEQ:
                        case CEE_CGT:
                        case CEE_CGT_UN:
                        case CEE_CLT:
                        case CEE_CLT_UN:
                            fgObserveInlineConstants(opcode, pushedStack, isInlining);
                            break;
                        default:
                            break;
                    }
                }
                else
                {
                    FgStack::FgSlot arg0 = pushedStack.Top(1);
                    FgStack::FgSlot arg1 = pushedStack.Top(0);

                    // Const op ConstArg -> ConstArg
                    if (FgStack::IsConstant(arg0) && FgStack::IsConstArgument(arg1, impInlineInfo))
                    {
                        // keep stack unchanged
                        handled = true;
                        compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR);
                    }
                    // ConstArg op Const    -> ConstArg
                    // ConstArg op ConstArg -> ConstArg
                    else if (FgStack::IsConstArgument(arg0, impInlineInfo) &&
                             FgStack::IsConstantOrConstArg(arg1, impInlineInfo))
                    {
                        if (FgStack::IsConstant(arg1))
                        {
                            pushedStack.Push(arg0);
                        }
                        handled = true;
                        compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR);
                    }
                    // Const op Const -> Const
                    else if (FgStack::IsConstant(arg0) && FgStack::IsConstant(arg1))
                    {
                        // both are constants, but we're mostly interested in cases where a const arg leads to
                        // a foldable expression.
                        handled = true;
                    }
                    // Arg op ConstArg
                    // Arg op Const
                    else if (FgStack::IsArgument(arg0) && FgStack::IsConstantOrConstArg(arg1, impInlineInfo))
                    {
                        // "Arg op CNS" --> keep arg0 in the stack for the next ops
                        pushedStack.Push(arg0);
                        handled = true;
                        compInlineResult->Note(InlineObservation::CALLEE_BINARY_EXRP_WITH_CNS);
                    }
                    // ConstArg op Arg
                    // Const    op Arg
                    else if (FgStack::IsArgument(arg1) && FgStack::IsConstantOrConstArg(arg0, impInlineInfo))
                    {
                        // "CNS op ARG" --> keep arg1 in the stack for the next ops
                        handled = true;
                        compInlineResult->Note(InlineObservation::CALLEE_BINARY_EXRP_WITH_CNS);
                    }
                    // X / ConstArg
                    // X % ConstArg
                    if (FgStack::IsConstArgument(arg1, impInlineInfo))
                    {
                        if ((opcode == CEE_DIV) || (opcode == CEE_DIV_UN) || (opcode == CEE_REM) ||
                            (opcode == CEE_REM_UN))
                        {
                            compInlineResult->Note(InlineObservation::CALLSITE_DIV_BY_CNS);
                        }
                        pushedStack.Push(arg0);
                        handled = true;
                    }
                }
                break;
            }

            // Jumps
            case CEE_LEAVE:
            case CEE_LEAVE_S:
            case CEE_BR:
            case CEE_BR_S:
            case CEE_BRFALSE:
            case CEE_BRFALSE_S:
            case CEE_BRTRUE:
            case CEE_BRTRUE_S:
            case CEE_BEQ:
            case CEE_BEQ_S:
            case CEE_BGE:
            case CEE_BGE_S:
            case CEE_BGE_UN:
            case CEE_BGE_UN_S:
            case CEE_BGT:
            case CEE_BGT_S:
            case CEE_BGT_UN:
            case CEE_BGT_UN_S:
            case CEE_BLE:
            case CEE_BLE_S:
            case CEE_BLE_UN:
            case CEE_BLE_UN_S:
            case CEE_BLT:
            case CEE_BLT_S:
            case CEE_BLT_UN:
            case CEE_BLT_UN_S:
            case CEE_BNE_UN:
            case CEE_BNE_UN_S:
            {
                if (codeAddr > codeEndp - sz)
                {
                    goto TOO_FAR;
                }

                // Compute jump target address
                signed jmpDist = (sz == 1) ? getI1LittleEndian(codeAddr) : getI4LittleEndian(codeAddr);

                if ((jmpDist == 0) &&
                    (opcode == CEE_LEAVE || opcode == CEE_LEAVE_S || opcode == CEE_BR || opcode == CEE_BR_S) &&
                    opts.DoEarlyBlockMerging())
                {
                    break; /* NOP */
                }

                unsigned jmpAddr = (IL_OFFSET)(codeAddr - codeBegp) + sz + jmpDist;

                // Make sure target is reasonable
                if (jmpAddr >= codeSize)
                {
                    BADCODE3("code jumps to outer space", " at offset %04X", (IL_OFFSET)(codeAddr - codeBegp));
                }

                if (makeInlineObservations && (jmpDist < 0))
                {
                    compInlineResult->Note(InlineObservation::CALLEE_BACKWARD_JUMP);
                }

                // Mark the jump target
                jumpTarget->bitVectSet(jmpAddr);

                // See if jump might be sensitive to inlining
                if (!preciseScan && makeInlineObservations && (opcode != CEE_BR_S) && (opcode != CEE_BR))
                {
                    fgObserveInlineConstants(opcode, pushedStack, isInlining);
                }
                else if (preciseScan && makeInlineObservations)
                {
                    switch (opcode)
                    {
                        // Binary
                        case CEE_BEQ:
                        case CEE_BGE:
                        case CEE_BGT:
                        case CEE_BLE:
                        case CEE_BLT:
                        case CEE_BNE_UN:
                        case CEE_BGE_UN:
                        case CEE_BGT_UN:
                        case CEE_BLE_UN:
                        case CEE_BLT_UN:
                        case CEE_BEQ_S:
                        case CEE_BGE_S:
                        case CEE_BGT_S:
                        case CEE_BLE_S:
                        case CEE_BLT_S:
                        case CEE_BNE_UN_S:
                        case CEE_BGE_UN_S:
                        case CEE_BGT_UN_S:
                        case CEE_BLE_UN_S:
                        case CEE_BLT_UN_S:
                        {
                            FgStack::FgSlot op1 = pushedStack.Top(1);
                            FgStack::FgSlot op2 = pushedStack.Top(0);

                            if (FgStack::IsConstantOrConstArg(op1, impInlineInfo) &&
                                FgStack::IsConstantOrConstArg(op2, impInlineInfo))
                            {
                                compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_BRANCH);
                            }
                            if (FgStack::IsConstArgument(op1, impInlineInfo) ||
                                FgStack::IsConstArgument(op2, impInlineInfo))
                            {
                                compInlineResult->Note(InlineObservation::CALLSITE_CONSTANT_ARG_FEEDS_TEST);
                            }

                            if ((FgStack::IsArgument(op1) && FgStack::IsArrayLen(op2)) ||
                                (FgStack::IsArgument(op2) && FgStack::IsArrayLen(op1)))
                            {
                                compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_RANGE_CHECK);
                            }
                            else if ((FgStack::IsArgument(op1) && FgStack::IsConstantOrConstArg(op2, impInlineInfo)) ||
                                     (FgStack::IsArgument(op2) && FgStack::IsConstantOrConstArg(op1, impInlineInfo)))
                            {
                                compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_CONSTANT_TEST);
                            }
                            else if (FgStack::IsArgument(op1) || FgStack::IsArgument(op2))
                            {
                                compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_TEST);
                            }
                            else if (FgStack::IsConstant(op1) || FgStack::IsConstant(op2))
                            {
                                compInlineResult->Note(InlineObservation::CALLEE_BINARY_EXRP_WITH_CNS);
                            }
                            break;
                        }

                        // Unary
                        case CEE_BRFALSE_S:
                        case CEE_BRTRUE_S:
                        case CEE_BRFALSE:
                        case CEE_BRTRUE:
                        {
                            if (FgStack::IsConstantOrConstArg(pushedStack.Top(), impInlineInfo))
                            {
                                compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_BRANCH);
                            }
                            else if (FgStack::IsArgument(pushedStack.Top()))
                            {
                                // E.g. brtrue is basically "if (X == 0)"
                                compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_CONSTANT_TEST);
                            }
                            break;
                        }

                        default:
                            break;
                    }
                }
            }
            break;

            case CEE_LDFLDA:
            case CEE_LDFLD:
            case CEE_STFLD:
            {
                if (FgStack::IsArgument(pushedStack.Top()))
                {
                    compInlineResult->Note(InlineObservation::CALLEE_ARG_STRUCT_FIELD_ACCESS);
                    handled = true; // keep argument on top of the stack
                }
                break;
            }

            case CEE_LDELEM_I1:
            case CEE_LDELEM_U1:
            case CEE_LDELEM_I2:
            case CEE_LDELEM_U2:
            case CEE_LDELEM_I4:
            case CEE_LDELEM_U4:
            case CEE_LDELEM_I8:
            case CEE_LDELEM_I:
            case CEE_LDELEM_R4:
            case CEE_LDELEM_R8:
            case CEE_LDELEM_REF:
            case CEE_STELEM_I:
            case CEE_STELEM_I1:
            case CEE_STELEM_I2:
            case CEE_STELEM_I4:
            case CEE_STELEM_I8:
            case CEE_STELEM_R4:
            case CEE_STELEM_R8:
            case CEE_STELEM_REF:
            case CEE_LDELEM:
            case CEE_STELEM:
            {
                if (!preciseScan)
                {
                    break;
                }
                if (FgStack::IsArgument(pushedStack.Top()) || FgStack::IsArgument(pushedStack.Top(1)))
                {
                    compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_RANGE_CHECK);
                }
                break;
            }

            case CEE_SWITCH:
            {
                if (makeInlineObservations)
                {
                    compInlineResult->Note(InlineObservation::CALLEE_HAS_SWITCH);
                    if (FgStack::IsConstantOrConstArg(pushedStack.Top(), impInlineInfo))
                    {
                        compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_SWITCH);
                    }

                    // Fail fast, if we're inlining and can't handle this.
                    if (isInlining && compInlineResult->IsFailure())
                    {
                        return;
                    }
                }

                // Make sure we don't go past the end reading the number of cases
                if (codeAddr > codeEndp - sizeof(DWORD))
                {
                    goto TOO_FAR;
                }

                // Read the number of cases
                unsigned jmpCnt = getU4LittleEndian(codeAddr);
                codeAddr += sizeof(DWORD);

                if (jmpCnt > codeSize / sizeof(DWORD))
                {
                    goto TOO_FAR;
                }

                // Find the end of the switch table
                unsigned jmpBase = (unsigned)((codeAddr - codeBegp) + jmpCnt * sizeof(DWORD));

                // Make sure there is more code after the switch
                if (jmpBase >= codeSize)
                {
                    goto TOO_FAR;
                }

                // jmpBase is also the target of the default case, so mark it
                jumpTarget->bitVectSet(jmpBase);

                // Process table entries
                while (jmpCnt > 0)
                {
                    unsigned jmpAddr = jmpBase + getI4LittleEndian(codeAddr);
                    codeAddr += 4;

                    if (jmpAddr >= codeSize)
                    {
                        BADCODE3("jump target out of range", " at offset %04X", (IL_OFFSET)(codeAddr - codeBegp));
                    }

                    jumpTarget->bitVectSet(jmpAddr);
                    jmpCnt--;
                }

                // We've advanced past all the bytes in this instruction
                sz = 0;
            }
            break;

            case CEE_UNALIGNED:
            {
                noway_assert(sz == sizeof(int8_t));
                prefixFlags |= PREFIX_UNALIGNED;

                codeAddr += sizeof(int8_t);

                impValidateMemoryAccessOpcode(codeAddr, codeEndp, false);
                handled = true;
                goto OBSERVE_OPCODE;
            }

            case CEE_CONSTRAINED:
            {
                noway_assert(sz == sizeof(unsigned));
                prefixFlags |= PREFIX_CONSTRAINED;

                codeAddr += sizeof(unsigned);

                {
                    OPCODE actualOpcode = impGetNonPrefixOpcode(codeAddr, codeEndp);

                    if (actualOpcode != CEE_CALLVIRT && actualOpcode != CEE_CALL && actualOpcode != CEE_LDFTN)
                    {
                        BADCODE("constrained. has to be followed by callvirt, call or ldftn");
                    }
                }
                handled = true;
                goto OBSERVE_OPCODE;
            }

            case CEE_READONLY:
            {
                noway_assert(sz == 0);
                prefixFlags |= PREFIX_READONLY;

                {
                    OPCODE actualOpcode = impGetNonPrefixOpcode(codeAddr, codeEndp);

                    if ((actualOpcode != CEE_LDELEMA) && !impOpcodeIsCallOpcode(actualOpcode))
                    {
                        BADCODE("readonly. has to be followed by ldelema or call");
                    }
                }
                handled = true;
                goto OBSERVE_OPCODE;
            }

            case CEE_VOLATILE:
            {
                noway_assert(sz == 0);
                prefixFlags |= PREFIX_VOLATILE;

                impValidateMemoryAccessOpcode(codeAddr, codeEndp, true);
                handled = true;
                goto OBSERVE_OPCODE;
            }

            case CEE_TAILCALL:
            {
                noway_assert(sz == 0);
                prefixFlags |= PREFIX_TAILCALL_EXPLICIT;

                {
                    OPCODE actualOpcode = impGetNonPrefixOpcode(codeAddr, codeEndp);

                    if (!impOpcodeIsCallOpcode(actualOpcode))
                    {
                        BADCODE("tailcall. has to be followed by call, callvirt or calli");
                    }
                }
                handled = true;
                goto OBSERVE_OPCODE;
            }

            case CEE_STARG:
            case CEE_STARG_S:
            {
                noway_assert(sz == sizeof(BYTE) || sz == sizeof(WORD));

                if (codeAddr > codeEndp - sz)
                {
                    goto TOO_FAR;
                }

                varNum = (sz == sizeof(BYTE)) ? getU1LittleEndian(codeAddr) : getU2LittleEndian(codeAddr);

                if (isInlining)
                {
                    if (varNum < impInlineInfo->argCnt)
                    {
                        impInlineInfo->inlArgInfo[varNum].argHasStargOp = true;
                    }
                }
                else
                {
                    // account for possible hidden param
                    varNum = compMapILargNum(varNum);

                    // This check is only intended to prevent an AV.  Bad varNum values will later
                    // be handled properly by the verifier.
                    if (varNum < lvaTableCnt)
                    {
                        // In non-inline cases, note written-to arguments.
                        lvaTable[varNum].lvHasILStoreOp = 1;
                    }
                }
            }
            break;

            case CEE_STLOC_0:
            case CEE_STLOC_1:
            case CEE_STLOC_2:
            case CEE_STLOC_3:
                varNum = (opcode - CEE_STLOC_0);
                goto STLOC;

            case CEE_STLOC:
            case CEE_STLOC_S:
            {
                noway_assert(sz == sizeof(BYTE) || sz == sizeof(WORD));

                if (codeAddr > codeEndp - sz)
                {
                    goto TOO_FAR;
                }

                varNum = (sz == sizeof(BYTE)) ? getU1LittleEndian(codeAddr) : getU2LittleEndian(codeAddr);

            STLOC:
                if (isInlining)
                {
                    InlLclVarInfo& lclInfo = impInlineInfo->lclVarInfo[varNum + impInlineInfo->argCnt];

                    if (lclInfo.lclHasStlocOp)
                    {
                        lclInfo.lclHasMultipleStlocOp = 1;
                    }
                    else
                    {
                        lclInfo.lclHasStlocOp = 1;
                    }
                }
                else
                {
                    varNum += info.compArgsCount;

                    // This check is only intended to prevent an AV.  Bad varNum values will later
                    // be handled properly by the verifier.
                    if (varNum < lvaTableCnt)
                    {
                        // In non-inline cases, note written-to locals.
                        if (lvaTable[varNum].lvHasILStoreOp)
                        {
                            lvaTable[varNum].lvHasMultipleILStoreOp = 1;
                        }
                        else
                        {
                            lvaTable[varNum].lvHasILStoreOp = 1;
                        }
                    }
                }
            }
            break;

            case CEE_LDLOC_0:
            case CEE_LDLOC_1:
            case CEE_LDLOC_2:
            case CEE_LDLOC_3:
                //
                if (preciseScan && makeInlineObservations && (prevOpcode == (CEE_STLOC_3 - (CEE_LDLOC_3 - opcode))))
                {
                    // Fold stloc+ldloc
                    pushedStack.Push(pushedStack.Top(1)); // throw away SLOT_UNKNOWN inserted by STLOC
                    handled = true;
                }
                break;

            case CEE_LDARGA:
            case CEE_LDARGA_S:
            case CEE_LDLOCA:
            case CEE_LDLOCA_S:
            {
                // Handle address-taken args or locals
                noway_assert(sz == sizeof(BYTE) || sz == sizeof(WORD));

                if (codeAddr > codeEndp - sz)
                {
                    goto TOO_FAR;
                }

                varNum = (sz == sizeof(BYTE)) ? getU1LittleEndian(codeAddr) : getU2LittleEndian(codeAddr);

                if (isInlining)
                {
                    if (opcode == CEE_LDLOCA || opcode == CEE_LDLOCA_S)
                    {
                        varType = impInlineInfo->lclVarInfo[varNum + impInlineInfo->argCnt].lclTypeInfo;

                        impInlineInfo->lclVarInfo[varNum + impInlineInfo->argCnt].lclHasLdlocaOp = true;
                    }
                    else
                    {
                        noway_assert(opcode == CEE_LDARGA || opcode == CEE_LDARGA_S);

                        varType = impInlineInfo->lclVarInfo[varNum].lclTypeInfo;

                        impInlineInfo->inlArgInfo[varNum].argHasLdargaOp = true;

                        pushedStack.PushArgument(varNum);
                        handled = true;
                    }
                }
                else
                {
                    if (opcode == CEE_LDLOCA || opcode == CEE_LDLOCA_S)
                    {
                        if (varNum >= info.compMethodInfo->locals.numArgs)
                        {
                            BADCODE("bad local number");
                        }

                        varNum += info.compArgsCount;
                    }
                    else
                    {
                        noway_assert(opcode == CEE_LDARGA || opcode == CEE_LDARGA_S);

                        if (varNum >= info.compILargsCount)
                        {
                            BADCODE("bad argument number");
                        }

                        varNum = compMapILargNum(varNum); // account for possible hidden param
                    }

                    varType = (var_types)lvaTable[varNum].lvType;

                    // Determine if the next instruction will consume
                    // the address. If so we won't mark this var as
                    // address taken.
                    //
                    // We will put structs on the stack and changing
                    // the addrTaken of a local requires an extra pass
                    // in the morpher so we won't apply this
                    // optimization to structs.
                    //
                    // Debug code spills for every IL instruction, and
                    // therefore it will split statements, so we will
                    // need the address.  Note that this optimization
                    // is based in that we know what trees we will
                    // generate for this ldfld, and we require that we
                    // won't need the address of this local at all

                    const bool notStruct    = !varTypeIsStruct(lvaGetDesc(varNum));
                    const bool notLastInstr = (codeAddr < codeEndp - sz);
                    const bool notDebugCode = !opts.compDbgCode;

                    if (notStruct && notLastInstr && notDebugCode && impILConsumesAddr(codeAddr + sz))
                    {
                        // We can skip the addrtaken, as next IL instruction consumes
                        // the address.
                    }
                    else
                    {
                        lvaTable[varNum].lvHasLdAddrOp = 1;
                        if (!info.compIsStatic && (varNum == 0))
                        {
                            // Addr taken on "this" pointer is significant,
                            // go ahead to mark it as permanently addr-exposed here.
                            // This may be conservative, but probably not very.
                            lvaSetVarAddrExposed(0 DEBUGARG(AddressExposedReason::TOO_CONSERVATIVE));
                        }
                    }
                } // isInlining

                typeIsNormed = !varTypeIsGC(varType) && !varTypeIsStruct(varType);
            }
            break;

            case CEE_JMP:
                retBlocks++;

#if !defined(TARGET_X86) && !defined(TARGET_ARM)
                if (!isInlining)
                {
                    // We transform this into a set of ldarg's + tail call and
                    // thus may push more onto the stack than originally thought.
                    // This doesn't interfere with verification because CEE_JMP
                    // is never verifiable, and there's nothing unsafe you can
                    // do with a an IL stack overflow if the JIT is expecting it.
                    info.compMaxStack = max(info.compMaxStack, info.compILargsCount);
                    break;
                }
#endif // !TARGET_X86 && !TARGET_ARM

                // If we are inlining, we need to fail for a CEE_JMP opcode, just like
                // the list of other opcodes (for all platforms).

                FALLTHROUGH;

            case CEE_MKREFANY:
            case CEE_RETHROW:
                if (makeInlineObservations)
                {
                    // Arguably this should be NoteFatal, but the legacy behavior is
                    // to ignore this for the prejit root.
                    compInlineResult->Note(InlineObservation::CALLEE_UNSUPPORTED_OPCODE);

                    // Fail fast if we're inlining...
                    if (isInlining)
                    {
                        assert(compInlineResult->IsFailure());
                        return;
                    }
                }
                break;

            case CEE_LOCALLOC:

                compLocallocSeen = true;

                // We now allow localloc callees to become candidates in some cases.
                if (makeInlineObservations)
                {
                    compInlineResult->Note(InlineObservation::CALLEE_HAS_LOCALLOC);
                    if (isInlining && compInlineResult->IsFailure())
                    {
                        return;
                    }
                }
                break;

            case CEE_LDARG_0:
            case CEE_LDARG_1:
            case CEE_LDARG_2:
            case CEE_LDARG_3:
                if (makeInlineObservations)
                {
                    pushedStack.PushArgument(opcode - CEE_LDARG_0);
                    handled = true;
                }
                break;

            case CEE_LDARG_S:
            case CEE_LDARG:
            {
                if (codeAddr > codeEndp - sz)
                {
                    goto TOO_FAR;
                }

                varNum = (sz == sizeof(BYTE)) ? getU1LittleEndian(codeAddr) : getU2LittleEndian(codeAddr);

                if (makeInlineObservations)
                {
                    pushedStack.PushArgument(varNum);
                    handled = true;
                }
            }
            break;

            case CEE_LDLEN:
                if (makeInlineObservations)
                {
                    pushedStack.PushArrayLen();
                    handled = true;
                }
                break;

            case CEE_RET:
                retBlocks++;
                break;

            default:
                break;
        }

        // Skip any remaining operands this opcode may have
        codeAddr += sz;

        // Clear any prefix flags that may have been set
        prefixFlags = 0;

        // Increment the number of observed instructions
        opts.instrCount++;

    OBSERVE_OPCODE:

        // Note the opcode we just saw
        if (makeInlineObservations)
        {
            InlineObservation obs =
                typeIsNormed ? InlineObservation::CALLEE_OPCODE_NORMED : InlineObservation::CALLEE_OPCODE;
            compInlineResult->NoteInt(obs, opcode);
        }

        typeIsNormed = false;
    }

    if (codeAddr != codeEndp)
    {
    TOO_FAR:
        BADCODE3("Code ends in the middle of an opcode, or there is a branch past the end of the method",
                 " at offset %04X", (IL_OFFSET)(codeAddr - codeBegp));
    }

    INDEBUG(compInlineContext->SetILInstsSet(ilInstsSet));

    if (makeInlineObservations)
    {
        compInlineResult->Note(InlineObservation::CALLEE_END_OPCODE_SCAN);

        // If there are no return blocks we know it does not return, however if there
        // return blocks we don't know it returns as it may be counting unreachable code.
        // However we will still make the CALLEE_DOES_NOT_RETURN observation.

        compInlineResult->NoteBool(InlineObservation::CALLEE_DOES_NOT_RETURN, retBlocks == 0);

        if ((retBlocks == 0) && isInlining &&
            info.compCompHnd->notifyMethodInfoUsage(impInlineInfo->iciCall->gtCallMethHnd))
        {
            // Mark the call node as "no return" as it can impact caller's code quality.
            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.
        }

        // If the inline is viable and discretionary, do the
        // profitability screening.
        if (compInlineResult->IsDiscretionaryCandidate())
        {
            // Make some callsite specific observations that will feed
            // into the profitability model.
            impMakeDiscretionaryInlineObservations(impInlineInfo, compInlineResult);

            // None of those observations should have changed the
            // inline's viability.
            assert(compInlineResult->IsCandidate());

            if (isInlining)
            {
                // Assess profitability...
                CORINFO_METHOD_INFO* methodInfo = &impInlineInfo->inlineCandidateInfo->methInfo;
                compInlineResult->DetermineProfitability(methodInfo);

                if (compInlineResult->IsFailure())
                {
                    impInlineRoot()->m_inlineStrategy->NoteUnprofitable();
                    JITDUMP("\n\nInline expansion aborted, inline not profitable\n");
                    return;
                }
                else
                {
                    // The inline is still viable.
                    assert(compInlineResult->IsCandidate());
                }
            }
            else
            {
                // Prejit root case. Profitability assessment for this
                // is done over in compCompileHelper.
            }
        }
    }

    // None of the local vars in the inlinee should have address taken or been written to.
    // Therefore we should NOT need to enter this "if" statement.
    if (!isInlining && !info.compIsStatic)
    {
        fgAdjustForAddressExposedOrWrittenThis();
    }

    // Now that we've seen the IL, set lvSingleDef for root method
    // locals.
    //
    // We could also do this for root method arguments but single-def
    // arguments are set by the caller and so we don't know anything
    // about the possible values or types.
    //
    // For inlinees we do this over in impInlineFetchLocal and
    // impInlineFetchArg (here args are included as we sometimes get
    // new information about the types of inlinee args).
    if (!isInlining)
    {
        const unsigned firstLcl = info.compArgsCount;
        const unsigned lastLcl  = firstLcl + info.compMethodInfo->locals.numArgs;
        for (unsigned lclNum = firstLcl; lclNum < lastLcl; lclNum++)
        {
            LclVarDsc* lclDsc = lvaGetDesc(lclNum);
            assert(lclDsc->lvSingleDef == 0);
            lclDsc->lvSingleDef = !lclDsc->lvHasMultipleILStoreOp && !lclDsc->lvHasLdAddrOp;

            if (lclDsc->lvSingleDef)
            {
                JITDUMP("Marked V%02u as a single def local\n", lclNum);
            }
        }
    }
}