// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include "common.h"

#ifdef FEATURE_TIERED_COMPILATION

#include "callcounting.h"
#include "threadsuspend.h"

#ifndef DACCESS_COMPILE
extern "C" void STDCALL OnCallCountThresholdReachedStub();
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CallCountingStub

#ifndef DACCESS_COMPILE
const PCODE CallCountingStub::TargetForThresholdReached = (PCODE)GetEEFuncEntryPoint(OnCallCountThresholdReachedStub);
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CallCountingManager::CallCountingInfo

#ifndef DACCESS_COMPILE

CallCountingManager::CallCountingInfo::CallCountingInfo(NativeCodeVersion codeVersion)
    : m_codeVersion(codeVersion),
    m_callCountingStub(nullptr),
    m_remainingCallCount(0),
    m_stage(Stage::Disabled)
{
    WRAPPER_NO_CONTRACT;
    _ASSERTE(!codeVersion.IsNull());
}

CallCountingManager::CallCountingInfo *
CallCountingManager::CallCountingInfo::CreateWithCallCountingDisabled(NativeCodeVersion codeVersion)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    return new CallCountingInfo(codeVersion);
}

CallCountingManager::CallCountingInfo::CallCountingInfo(NativeCodeVersion codeVersion, CallCount callCountThreshold)
    : m_codeVersion(codeVersion),
    m_callCountingStub(nullptr),
    m_remainingCallCount(callCountThreshold),
    m_stage(Stage::StubIsNotActive)
{
    WRAPPER_NO_CONTRACT;
    _ASSERTE(!codeVersion.IsNull());
    _ASSERTE(callCountThreshold != 0);
}

CallCountingManager::CallCountingInfo::~CallCountingInfo()
{
    WRAPPER_NO_CONTRACT;
    _ASSERTE(m_stage == Stage::Complete);
    _ASSERTE(m_callCountingStub == nullptr);
}

#endif // !DACCESS_COMPILE

CallCountingManager::PTR_CallCountingInfo CallCountingManager::CallCountingInfo::From(PTR_CallCount remainingCallCountCell)
{
    WRAPPER_NO_CONTRACT;
    _ASSERTE(remainingCallCountCell != nullptr);

    return PTR_CallCountingInfo(dac_cast<TADDR>(remainingCallCountCell) - offsetof(CallCountingInfo, m_remainingCallCount));
}

NativeCodeVersion CallCountingManager::CallCountingInfo::GetCodeVersion() const
{
    WRAPPER_NO_CONTRACT;
    return m_codeVersion;
}

#ifndef DACCESS_COMPILE

const CallCountingStub *CallCountingManager::CallCountingInfo::GetCallCountingStub() const
{
    WRAPPER_NO_CONTRACT;
    _ASSERTE(m_stage != Stage::Disabled);

    return m_callCountingStub;
}

void CallCountingManager::CallCountingInfo::SetCallCountingStub(const CallCountingStub *callCountingStub)
{
    WRAPPER_NO_CONTRACT;
    _ASSERTE(g_pConfig->TieredCompilation_UseCallCountingStubs());
    _ASSERTE(m_stage == Stage::StubIsNotActive);
    _ASSERTE(m_callCountingStub == nullptr);
    _ASSERTE(callCountingStub != nullptr);

    ++s_callCountingStubCount;
    m_callCountingStub = callCountingStub;
}

void CallCountingManager::CallCountingInfo::ClearCallCountingStub()
{
    WRAPPER_NO_CONTRACT;
    _ASSERTE(m_stage == Stage::StubIsNotActive || m_stage == Stage::Complete);
    _ASSERTE(m_callCountingStub != nullptr);

    m_callCountingStub = nullptr;
    // The total and completed stub counts are updated along with deleting stubs
}

PTR_CallCount CallCountingManager::CallCountingInfo::GetRemainingCallCountCell()
{
    WRAPPER_NO_CONTRACT;
    _ASSERTE(m_stage != Stage::Disabled);
    //_ASSERTE(m_callCountingStub != nullptr);

    return &m_remainingCallCount;
}

#endif // !DACCESS_COMPILE

CallCountingManager::CallCountingInfo::Stage CallCountingManager::CallCountingInfo::GetStage() const
{
    WRAPPER_NO_CONTRACT;
    return m_stage;
}

#ifndef DACCESS_COMPILE
FORCEINLINE void CallCountingManager::CallCountingInfo::SetStage(Stage stage)
{
    WRAPPER_NO_CONTRACT;
    _ASSERTE(m_stage != Stage::Disabled);
    _ASSERTE(stage <= Stage::Complete);

    switch (stage)
    {
        case Stage::StubIsNotActive:
            _ASSERTE(m_stage == Stage::StubMayBeActive);
            _ASSERTE(m_callCountingStub != nullptr);
            _ASSERTE(s_activeCallCountingStubCount != 0);
            --s_activeCallCountingStubCount;
            break;

        case Stage::StubMayBeActive:
            _ASSERTE(m_callCountingStub != nullptr);
            FALLTHROUGH;

        case Stage::PendingCompletion:
            _ASSERTE(m_stage == Stage::StubIsNotActive || m_stage == Stage::StubMayBeActive);
            if (m_stage == Stage::StubIsNotActive && m_callCountingStub != nullptr)
            {
                ++s_activeCallCountingStubCount;
            }
            break;

        case Stage::Complete:
            _ASSERTE(m_stage != Stage::Complete);
            if (m_callCountingStub != nullptr)
            {
                if (m_stage != Stage::StubIsNotActive)
                {
                    _ASSERTE(s_activeCallCountingStubCount != 0);
                    --s_activeCallCountingStubCount;
                }
                ++s_completedCallCountingStubCount;
            }
            break;

        default:
            UNREACHABLE();
    }

    m_stage = stage;
}
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CallCountingManager::CallCountingInfo::CodeVersionHashTraits

CallCountingManager::CallCountingInfo::CodeVersionHashTraits::key_t
CallCountingManager::CallCountingInfo::CodeVersionHashTraits::GetKey(const element_t &e)
{
    WRAPPER_NO_CONTRACT;
    return e->GetCodeVersion();
}

BOOL CallCountingManager::CallCountingInfo::CodeVersionHashTraits::Equals(const key_t &k1, const key_t &k2)
{
    WRAPPER_NO_CONTRACT;
    return k1 == k2;
}

CallCountingManager::CallCountingInfo::CodeVersionHashTraits::count_t
CallCountingManager::CallCountingInfo::CodeVersionHashTraits::Hash(const key_t &k)
{
    WRAPPER_NO_CONTRACT;
    return (count_t)dac_cast<TADDR>(k.GetMethodDesc()) + k.GetVersionId();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CallCountingManager::CallCountingStubAllocator

CallCountingManager::CallCountingStubAllocator::CallCountingStubAllocator() : m_heap(nullptr)
{
    WRAPPER_NO_CONTRACT;
}

CallCountingManager::CallCountingStubAllocator::~CallCountingStubAllocator()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifndef DACCESS_COMPILE
    LoaderHeap *heap = m_heap;
    if (heap != nullptr)
    {
        delete m_heap;
    }
#endif
}

#ifndef DACCESS_COMPILE

void CallCountingManager::CallCountingStubAllocator::Reset()
{
    WRAPPER_NO_CONTRACT;

    this->~CallCountingStubAllocator();
    new(this) CallCountingStubAllocator();
}

const CallCountingStub *CallCountingManager::CallCountingStubAllocator::AllocateStub(
    CallCount *remainingCallCountCell,
    PCODE targetForMethod)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    LoaderHeap *heap = m_heap;
    if (heap == nullptr)
    {
        heap = AllocateHeap();
    }

    SIZE_T sizeInBytes = sizeof(CallCountingStub);
    AllocMemHolder<void> allocationAddressHolder(heap->AllocAlignedMem(sizeInBytes, 1));
    CallCountingStub *stub = (CallCountingStub*)(void*)allocationAddressHolder;
    allocationAddressHolder.SuppressRelease();
    stub->Initialize(targetForMethod, remainingCallCountCell);

    return stub;
}

#if defined(TARGET_ARM64) && defined(TARGET_UNIX)
    #define ENUM_PAGE_SIZE(size) \
        extern "C" void CallCountingStubCode##size(); \
        extern "C" void CallCountingStubCode##size##_End();

    ENUM_PAGE_SIZES
    #undef ENUM_PAGE_SIZE
#else
extern "C" void CallCountingStubCode();
extern "C" void CallCountingStubCode_End();
#endif

#ifdef TARGET_X86
extern "C" size_t CallCountingStubCode_RemainingCallCountCell_Offset;
extern "C" size_t CallCountingStubCode_TargetForMethod_Offset;
extern "C" size_t CallCountingStubCode_TargetForThresholdReached_Offset;

#define SYMBOL_VALUE(name) ((size_t)&name)

#endif

#if defined(TARGET_ARM64) && defined(TARGET_UNIX)
void (*CallCountingStub::CallCountingStubCode)();
#endif

#ifndef DACCESS_COMPILE

void CallCountingStub::StaticInitialize()
{
#if defined(TARGET_ARM64) && defined(TARGET_UNIX)
    int pageSize = GetStubCodePageSize();
    #define ENUM_PAGE_SIZE(size) \
        case size: \
            CallCountingStubCode = CallCountingStubCode##size; \
            _ASSERTE((SIZE_T)((BYTE*)CallCountingStubCode##size##_End - (BYTE*)CallCountingStubCode##size) <= CallCountingStub::CodeSize); \
            break;

    switch (pageSize)
    {
        ENUM_PAGE_SIZES
        default:
            EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, W("Unsupported OS page size"));
    }
    #undef ENUM_PAGE_SIZE
#else
    _ASSERTE((SIZE_T)((BYTE*)CallCountingStubCode_End - (BYTE*)CallCountingStubCode) <= CallCountingStub::CodeSize);
#endif
}

#endif // DACCESS_COMPILE

void CallCountingStub::GenerateCodePage(BYTE* pageBase, BYTE* pageBaseRX, SIZE_T pageSize)
{
#ifdef TARGET_X86
    int totalCodeSize = (pageSize / CallCountingStub::CodeSize) * CallCountingStub::CodeSize;

    for (int i = 0; i < totalCodeSize; i += CallCountingStub::CodeSize)
    {
        memcpy(pageBase + i, (const void*)CallCountingStubCode, CallCountingStub::CodeSize);

        // Set absolute addresses of the slots in the stub
        BYTE* pCounterSlot = pageBaseRX + i + pageSize + offsetof(CallCountingStubData, RemainingCallCountCell);
        *(BYTE**)(pageBase + i + SYMBOL_VALUE(CallCountingStubCode_RemainingCallCountCell_Offset)) = pCounterSlot;

        BYTE* pTargetSlot = pageBaseRX + i + pageSize + offsetof(CallCountingStubData, TargetForMethod);
        *(BYTE**)(pageBase + i + SYMBOL_VALUE(CallCountingStubCode_TargetForMethod_Offset)) = pTargetSlot;

        BYTE* pCountReachedZeroSlot = pageBaseRX + i + pageSize + offsetof(CallCountingStubData, TargetForThresholdReached);
        *(BYTE**)(pageBase + i + SYMBOL_VALUE(CallCountingStubCode_TargetForThresholdReached_Offset)) = pCountReachedZeroSlot;
    }
#else // TARGET_X86
    FillStubCodePage(pageBase, (const void*)PCODEToPINSTR((PCODE)CallCountingStubCode), CallCountingStub::CodeSize, pageSize);
#endif
}


NOINLINE LoaderHeap *CallCountingManager::CallCountingStubAllocator::AllocateHeap()
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    _ASSERTE(m_heap == nullptr);

    LoaderHeap *heap = new LoaderHeap(0, 0, &m_heapRangeList, UnlockedLoaderHeap::HeapKind::Interleaved, true /* fUnlocked */, CallCountingStub::GenerateCodePage, CallCountingStub::CodeSize);
    m_heap = heap;
    return heap;
}

#endif // !DACCESS_COMPILE

bool CallCountingManager::CallCountingStubAllocator::IsStub(TADDR entryPoint)
{
    WRAPPER_NO_CONTRACT;
    _ASSERTE(entryPoint != (TADDR)NULL);

    return !!m_heapRangeList.IsInRange(entryPoint);
}

#ifdef DACCESS_COMPILE

void CallCountingManager::CallCountingStubAllocator::EnumerateHeapRanges(CLRDataEnumMemoryFlags flags)
{
    WRAPPER_NO_CONTRACT;
    m_heapRangeList.EnumMemoryRegions(flags);
}

#endif // DACCESS_COMPILE

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CallCountingManager::MethodDescForwarderStubHashTraits

CallCountingManager::MethodDescForwarderStubHashTraits::key_t
CallCountingManager::MethodDescForwarderStubHashTraits::GetKey(const element_t &e)
{
    WRAPPER_NO_CONTRACT;
    return e->GetMethodDesc();
}

BOOL CallCountingManager::MethodDescForwarderStubHashTraits::Equals(const key_t &k1, const key_t &k2)
{
    WRAPPER_NO_CONTRACT;
    return k1 == k2;
}

CallCountingManager::MethodDescForwarderStubHashTraits::count_t
CallCountingManager::MethodDescForwarderStubHashTraits::Hash(const key_t &k)
{
    WRAPPER_NO_CONTRACT;
    return (count_t)(size_t)k;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CallCountingManager::CallCountingManagerHashTraits

CallCountingManager::CallCountingManagerHashTraits::key_t
CallCountingManager::CallCountingManagerHashTraits::GetKey(const element_t &e)
{
    WRAPPER_NO_CONTRACT;
    return e;
}

BOOL CallCountingManager::CallCountingManagerHashTraits::Equals(const key_t &k1, const key_t &k2)
{
    WRAPPER_NO_CONTRACT;
    return k1 == k2;
}

CallCountingManager::CallCountingManagerHashTraits::count_t
CallCountingManager::CallCountingManagerHashTraits::Hash(const key_t &k)
{
    WRAPPER_NO_CONTRACT;
    return (count_t)dac_cast<TADDR>(k);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CallCountingManager

CallCountingManager::PTR_CallCountingManagerHash CallCountingManager::s_callCountingManagers = PTR_NULL;
COUNT_T CallCountingManager::s_callCountingStubCount = 0;
COUNT_T CallCountingManager::s_activeCallCountingStubCount = 0;
COUNT_T CallCountingManager::s_completedCallCountingStubCount = 0;

CallCountingManager::CallCountingManager()
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifndef DACCESS_COMPILE
    CodeVersionManager::LockHolder codeVersioningLockHolder;
    s_callCountingManagers->Add(this);
#endif
}

CallCountingManager::~CallCountingManager()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifndef DACCESS_COMPILE
    CodeVersionManager::LockHolder codeVersioningLockHolder;

    for (auto itEnd = m_callCountingInfoByCodeVersionHash.End(), it = m_callCountingInfoByCodeVersionHash.Begin();
        it != itEnd;
        ++it)
    {
        CallCountingInfo *callCountingInfo = *it;
        delete callCountingInfo;
    }

    s_callCountingManagers->Remove(this);
#endif
}

#ifndef DACCESS_COMPILE
void CallCountingManager::StaticInitialize()
{
    WRAPPER_NO_CONTRACT;
    s_callCountingManagers = PTR_CallCountingManagerHash(new CallCountingManagerHash());
    CallCountingStub::StaticInitialize();
}
#endif

bool CallCountingManager::IsCallCountingEnabled(NativeCodeVersion codeVersion)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    _ASSERTE(!codeVersion.IsNull());
    _ASSERTE(codeVersion.IsDefaultVersion());
    _ASSERTE(codeVersion.GetMethodDesc()->IsEligibleForTieredCompilation());

    CodeVersionManager::LockHolder codeVersioningLockHolder;

    PTR_CallCountingInfo callCountingInfo = m_callCountingInfoByCodeVersionHash.Lookup(codeVersion);
    return callCountingInfo == NULL || callCountingInfo->GetStage() != CallCountingInfo::Stage::Disabled;
}

#ifndef DACCESS_COMPILE

void CallCountingManager::DisableCallCounting(NativeCodeVersion codeVersion)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    _ASSERTE(!codeVersion.IsNull());
    _ASSERTE(codeVersion.IsDefaultVersion());
    _ASSERTE(codeVersion.GetMethodDesc()->IsEligibleForTieredCompilation());

    CodeVersionManager::LockHolder codeVersioningLockHolder;

    CallCountingInfo *callCountingInfo = m_callCountingInfoByCodeVersionHash.Lookup(codeVersion);
    if (callCountingInfo != nullptr)
    {
        // Call counting may already have been disabled due to the possibility of concurrent or reentering JIT of the same
        // native code version of a method. The call counting info is created with call counting enabled or disabled and it
        // cannot be changed thereafter for consistency in dependents of the info.
        _ASSERTE(callCountingInfo->GetStage() == CallCountingInfo::Stage::Disabled);
        return;
    }

    NewHolder<CallCountingInfo> callCountingInfoHolder = CallCountingInfo::CreateWithCallCountingDisabled(codeVersion);
    m_callCountingInfoByCodeVersionHash.Add(callCountingInfoHolder);
    callCountingInfoHolder.SuppressRelease();
}

// Returns true if the code entry point was updated to reflect the active code version, false otherwise. In normal paths, the
// code entry point is not updated only when the use of call counting stubs is disabled, as in that case returning to the
// prestub is necessary for further call counting. On exception, the code entry point may or may not have been updated and it's
// up to the caller to decide how to proceed.
bool CallCountingManager::SetCodeEntryPoint(
    NativeCodeVersion activeCodeVersion,
    PCODE codeEntryPoint,
    bool wasMethodCalled,
    bool *createTieringBackgroundWorkerRef)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        PRECONDITION(!activeCodeVersion.IsNull());
        MODE_ANY;
    }
    CONTRACTL_END;

    MethodDesc *methodDesc = activeCodeVersion.GetMethodDesc();
    _ASSERTE(!methodDesc->MayHaveEntryPointSlotsToBackpatch() || MethodDescBackpatchInfoTracker::IsLockOwnedByCurrentThread());
    _ASSERTE(CodeVersionManager::IsLockOwnedByCurrentThread());
    _ASSERTE(
        activeCodeVersion ==
        methodDesc->GetCodeVersionManager()->GetActiveILCodeVersion(methodDesc).GetActiveNativeCodeVersion(methodDesc));
    _ASSERTE(codeEntryPoint != (PCODE)NULL);
    _ASSERTE(codeEntryPoint == activeCodeVersion.GetNativeCode());
    _ASSERTE(!wasMethodCalled || createTieringBackgroundWorkerRef != nullptr);
    _ASSERTE(createTieringBackgroundWorkerRef == nullptr || !*createTieringBackgroundWorkerRef);

    if (!methodDesc->IsEligibleForTieredCompilation() ||
        (
            // For a default code version that is not tier 0, call counting will have been disabled by this time (checked
            // below). Avoid the redundant and not-insignificant expense of GetOptimizationTier() on a default code version.
            !activeCodeVersion.IsDefaultVersion() &&
            activeCodeVersion.IsFinalTier()
        ) ||
        !g_pConfig->TieredCompilation_CallCounting())
    {
        methodDesc->SetCodeEntryPoint(codeEntryPoint);
        return true;
    }

    const CallCountingStub *callCountingStub;
    CallCountingManager *callCountingManager = methodDesc->GetLoaderAllocator()->GetCallCountingManager();
    CallCountingInfoByCodeVersionHash &callCountingInfoByCodeVersionHash =
        callCountingManager->m_callCountingInfoByCodeVersionHash;
    CallCountingInfo *callCountingInfo = callCountingInfoByCodeVersionHash.Lookup(activeCodeVersion);
    do
    {
        if (callCountingInfo != nullptr)
        {
            _ASSERTE(callCountingInfo->GetCodeVersion() == activeCodeVersion);

            CallCountingInfo::Stage callCountingStage = callCountingInfo->GetStage();
            if (callCountingStage >= CallCountingInfo::Stage::PendingCompletion)
            {
                // Call counting is disabled, complete, or pending completion. The pending completion stage here would be
                // relatively rare, let it be handled elsewhere.
                methodDesc->SetCodeEntryPoint(codeEntryPoint);
                return true;
            }

            _ASSERTE(!activeCodeVersion.IsFinalTier());

            // If the tiering delay is active, postpone further work
            if (GetAppDomain()
                    ->GetTieredCompilationManager()
                    ->TrySetCodeEntryPointAndRecordMethodForCallCounting(methodDesc, codeEntryPoint))
            {
                if (callCountingStage == CallCountingInfo::Stage::StubMayBeActive)
                {
                    callCountingInfo->SetStage(CallCountingInfo::Stage::StubIsNotActive);
                }
                return true;
            }

            do
            {
                if (!wasMethodCalled)
                {
                    break;
                }

                CallCount remainingCallCount = --*callCountingInfo->GetRemainingCallCountCell();
                if (remainingCallCount != 0)
                {
                    break;
                }

                callCountingInfo->SetStage(CallCountingInfo::Stage::PendingCompletion);
                if (!activeCodeVersion.GetILCodeVersion().HasAnyOptimizedNativeCodeVersion(activeCodeVersion))
                {
                    GetAppDomain()
                        ->GetTieredCompilationManager()
                        ->AsyncPromoteToTier1(activeCodeVersion, createTieringBackgroundWorkerRef);
                }
                methodDesc->SetCodeEntryPoint(codeEntryPoint);
                callCountingInfo->SetStage(CallCountingInfo::Stage::Complete);
                return true;
            } while (false);

            callCountingStub = callCountingInfo->GetCallCountingStub();
            if (callCountingStub != nullptr)
            {
                break;
            }
        }
        else
        {
            _ASSERTE(!activeCodeVersion.IsFinalTier());

            // If the tiering delay is active, postpone further work
            if (GetAppDomain()
                    ->GetTieredCompilationManager()
                    ->TrySetCodeEntryPointAndRecordMethodForCallCounting(methodDesc, codeEntryPoint))
            {
                return true;
            }

            CallCount callCountThreshold = g_pConfig->TieredCompilation_CallCountThreshold();
            _ASSERTE(callCountThreshold != 0);

            // Let's tier up all cast helpers faster than other methods. This is because we want to import them as
            // direct calls in codegen and they need to be promoted earlier than their callers.
            if (methodDesc->GetMethodTable() == g_pCastHelpers)
            {
                callCountThreshold = max<CallCount>(1, (CallCount)(callCountThreshold / 2));
            }

            NewHolder<CallCountingInfo> callCountingInfoHolder = new CallCountingInfo(activeCodeVersion, callCountThreshold);
            callCountingInfoByCodeVersionHash.Add(callCountingInfoHolder);
            callCountingInfo = callCountingInfoHolder.Extract();
        }

        if (!g_pConfig->TieredCompilation_UseCallCountingStubs())
        {
            // Call counting is not yet complete, so reset or don't set the code entry point to continue counting calls

            if (wasMethodCalled)
            {
                return false;
            }

            // This path is reached after activating a code version when publishing its code entry point. The method may
            // currently be pointing to the code entry point of a different code version, so an explicit reset is necessary.
            methodDesc->ResetCodeEntryPoint();
            return true;
        }

        callCountingStub =
            callCountingManager->m_callCountingStubAllocator.AllocateStub(
                callCountingInfo->GetRemainingCallCountCell(),
                codeEntryPoint);
        callCountingInfo->SetCallCountingStub(callCountingStub);
    } while (false);

    PCODE callCountingCodeEntryPoint = callCountingStub->GetEntryPoint();
    if (methodDesc->MayHaveEntryPointSlotsToBackpatch())
    {
        // The call counting stub should not be the entry point that is called first in the process of a call
        // - Stubs should be deletable. Many methods will have call counting stubs associated with them, and although the memory
        //   involved is typically insignificant compared to the average memory overhead per method, by steady-state it would
        //   otherwise be unnecessary memory overhead serving no purpose.
        // - In order to be able to delete a stub, the jitted code of a method cannot be allowed to load the stub as the entry
        //   point of a callee into a register in a GC-safe point that allows for the stub to be deleted before the register is
        //   reused to call the stub. On some processor architectures, perhaps the JIT can guarantee that it would not load the
        //   entry point into a register before the call, but this is not possible on arm32 or arm64. Rather, perhaps the
        //   region containing the load and call would not be considered GC-safe. Calls are considered GC-safe points, and this
        //   may cause many methods that are currently fully interruptible to have to be partially interruptible and record
        //   extra GC info instead. This would be nontrivial and there would be tradeoffs.
        // - For any method that may have an entry point slot that would be backpatched with the call counting stub's entry
        //   point, a small forwarder stub (precode) is created. The forwarder stub has loader allocator lifetime and forwards to
        //   the larger call counting stub. This is a simple solution for now and seems to have negligible impact.
        // - Reusing FuncPtrStubs was considered. FuncPtrStubs are currently not used as a code entry point for a virtual or
        //   interface method and may be bypassed. For example, a call may call through the vtable slot, or a devirtualized call
        //   may call through a FuncPtrStub. The target of a FuncPtrStub is a code entry point and is backpatched when a
        //   method's active code entry point changes. Mixing the current use of FuncPtrStubs with the use as a forwarder for
        //   call counting does not seem trivial and would likely complicate its use. There may not be much gain in reusing
        //   FuncPtrStubs, as typically, they are created for only a small percentage of virtual/interface methods.

        MethodDescForwarderStubHash &methodDescForwarderStubHash = callCountingManager->m_methodDescForwarderStubHash;
        Precode *forwarderStub = methodDescForwarderStubHash.Lookup(methodDesc);
        if (forwarderStub == nullptr)
        {
            AllocMemTracker forwarderStubAllocationTracker;
            forwarderStub =
                Precode::Allocate(
                    methodDesc->GetPrecodeType(),
                    methodDesc,
                    methodDesc->GetLoaderAllocator(),
                    &forwarderStubAllocationTracker);
            methodDescForwarderStubHash.Add(forwarderStub);
            forwarderStubAllocationTracker.SuppressRelease();
        }

        forwarderStub->SetTargetInterlocked(callCountingCodeEntryPoint, false);
        callCountingCodeEntryPoint = forwarderStub->GetEntryPoint();
    }
    else
    {
        _ASSERTE(methodDesc->IsVersionableWithPrecode());
    }

    methodDesc->SetCodeEntryPoint(callCountingCodeEntryPoint);
    callCountingInfo->SetStage(CallCountingInfo::Stage::StubMayBeActive);
    return true;
}

extern "C" PCODE STDCALL OnCallCountThresholdReached(TransitionBlock *transitionBlock, TADDR stubIdentifyingToken)
{
    WRAPPER_NO_CONTRACT;
    return CallCountingManager::OnCallCountThresholdReached(transitionBlock, stubIdentifyingToken);
}

PCODE CallCountingManager::OnCallCountThresholdReached(TransitionBlock *transitionBlock, TADDR stubIdentifyingToken)
{
    STATIC_CONTRACT_THROWS;
    STATIC_CONTRACT_GC_TRIGGERS;
    STATIC_CONTRACT_MODE_COOPERATIVE;

    PCODE codeEntryPoint = 0;

    BEGIN_PRESERVE_LAST_ERROR;

    MAKE_CURRENT_THREAD_AVAILABLE();

#ifdef _DEBUG
    Thread::ObjectRefFlush(CURRENT_THREAD);
#endif

    // Get the code version from the call counting stub/info in cooperative GC mode to synchronize with deletion. The stub/info
    // may be deleted only when the runtime is suspended, so when we are in cooperative GC mode it is safe to read from them.
    NativeCodeVersion codeVersion =
        CallCountingInfo::From(CallCountingStub::From(stubIdentifyingToken)->GetRemainingCallCountCell())->GetCodeVersion();

    MethodDesc *methodDesc = codeVersion.GetMethodDesc();
    FrameWithCookie<CallCountingHelperFrame> frameWithCookie(transitionBlock, methodDesc);
    CallCountingHelperFrame *frame = &frameWithCookie;
    frame->Push(CURRENT_THREAD);

    INSTALL_MANAGED_EXCEPTION_DISPATCHER;
    INSTALL_UNWIND_AND_CONTINUE_HANDLER;

    // The switch to preemptive GC mode no longer guarantees that the stub/info will be valid. Only the code version will be
    // used going forward under appropriate locking to synchronize further with deletion.
    GCX_PREEMP_THREAD_EXISTS(CURRENT_THREAD);

    _ASSERTE(!codeVersion.IsFinalTier());

    codeEntryPoint = codeVersion.GetNativeCode();
    do
    {
        {
            CallCountingManager *callCountingManager = methodDesc->GetLoaderAllocator()->GetCallCountingManager();

            CodeVersionManager::LockHolder codeVersioningLockHolder;

            CallCountingInfo *callCountingInfo = callCountingManager->m_callCountingInfoByCodeVersionHash.Lookup(codeVersion);
            if (callCountingInfo == nullptr)
            {
                break;
            }

            CallCountingInfo::Stage callCountingStage = callCountingInfo->GetStage();
            if (callCountingStage >= CallCountingInfo::Stage::PendingCompletion)
            {
                break;
            }

            // Fully completing call counting for a method is relative expensive. Call counting with stubs is relatively cheap.
            // Since many methods will typically reach the call count threshold at roughly the same time (a perf spike),
            // delegate as much of the overhead as possible to the background. This significantly decreases the degree of the
            // perf spike.
            callCountingManager->m_callCountingInfosPendingCompletion.Append(callCountingInfo);
            callCountingInfo->SetStage(CallCountingInfo::Stage::PendingCompletion);
        }

        GetAppDomain()->GetTieredCompilationManager()->AsyncCompleteCallCounting();
    } while (false);

    UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
    UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;

    frame->Pop(CURRENT_THREAD);

    END_PRESERVE_LAST_ERROR;

    return codeEntryPoint;
}

COUNT_T CallCountingManager::GetCountOfCodeVersionsPendingCompletion()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_PREEMPTIVE;
    }
    CONTRACTL_END;

    COUNT_T count = 0;

    CodeVersionManager::LockHolder codeVersioningLockHolder;

    for (auto itEnd = s_callCountingManagers->End(), it = s_callCountingManagers->Begin(); it != itEnd; ++it)
    {
        CallCountingManager *callCountingManager = *it;
        count += callCountingManager->m_callCountingInfosPendingCompletion.GetCount();
    }

    return count;
}

void CallCountingManager::CompleteCallCounting()
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_PREEMPTIVE;
    }
    CONTRACTL_END;

    _ASSERTE(GetThreadNULLOk() == TieredCompilationManager::GetBackgroundWorkerThread());

    AppDomain *appDomain = GetAppDomain();
    TieredCompilationManager *tieredCompilationManager = appDomain->GetTieredCompilationManager();
    CodeVersionManager *codeVersionManager = appDomain->GetCodeVersionManager();

    MethodDescBackpatchInfoTracker::ConditionalLockHolder slotBackpatchLockHolder;
    CodeVersionManager::LockHolder codeVersioningLockHolder;

    for (auto itEnd = s_callCountingManagers->End(), it = s_callCountingManagers->Begin(); it != itEnd; ++it)
    {
        CallCountingManager *callCountingManager = *it;
        SArray<CallCountingInfo *> &callCountingInfosPendingCompletion =
            callCountingManager->m_callCountingInfosPendingCompletion;
        COUNT_T callCountingInfoCount = callCountingInfosPendingCompletion.GetCount();
        if (callCountingInfoCount == 0)
        {
            continue;
        }

        CallCountingInfo **callCountingInfos = callCountingInfosPendingCompletion.GetElements();
        for (COUNT_T i = 0; i < callCountingInfoCount; ++i)
        {
            CallCountingInfo *callCountingInfo = callCountingInfos[i];
            CallCountingInfo::Stage callCountingStage = callCountingInfo->GetStage();
            if (callCountingStage != CallCountingInfo::Stage::PendingCompletion)
            {
                continue;
            }

            NativeCodeVersion codeVersion = callCountingInfo->GetCodeVersion();
            MethodDesc *methodDesc = codeVersion.GetMethodDesc();
            _ASSERTE(codeVersionManager == methodDesc->GetCodeVersionManager());
            EX_TRY
            {
                if (!codeVersion.GetILCodeVersion().HasAnyOptimizedNativeCodeVersion(codeVersion))
                {
                    bool createTieringBackgroundWorker = false;
                    tieredCompilationManager->AsyncPromoteToTier1(codeVersion, &createTieringBackgroundWorker);
                    _ASSERTE(!createTieringBackgroundWorker); // the current thread is the background worker thread
                }

                // The active code version may have changed externally after the call counting stub was activated,
                // deactivating the call counting stub without our knowledge. Check the active code version and determine
                // what needs to be done.
                NativeCodeVersion activeCodeVersion =
                    codeVersionManager->GetActiveILCodeVersion(methodDesc).GetActiveNativeCodeVersion(methodDesc);
                do
                {
                    if (activeCodeVersion == codeVersion)
                    {
                        methodDesc->SetCodeEntryPoint(activeCodeVersion.GetNativeCode());
                        break;
                    }

                    // There is at least one case where the IL code version is changed inside the code versioning lock, the
                    // lock is released and reacquired, then the method's code entry point is reset. So if this path is
                    // reached between those locks, the method would still be pointing to the call counting stub. Once the
                    // stub is marked as complete, it may be deleted, so in all cases update the method's code entry point
                    // to ensure that the method is no longer pointing to the call counting stub.

                    if (!activeCodeVersion.IsNull())
                    {
                        PCODE activeNativeCode = activeCodeVersion.GetNativeCode();
                        if (activeNativeCode != 0)
                        {
                            methodDesc->SetCodeEntryPoint(activeNativeCode);
                            break;
                        }
                    }

                    methodDesc->ResetCodeEntryPoint();
                } while (false);

                callCountingInfo->SetStage(CallCountingInfo::Stage::Complete);
            }
            EX_CATCH
            {
                // Avoid abandoning call counting completion for all recorded call counting infos on exception. Since this
                // is happening on a background thread, following the general policy so far, the exception will be caught,
                // logged, and ignored anyway, so make an attempt to complete call counting for each item. Individual items
                // that fail will result in those code versions not getting promoted (similar to elsewhere).
                STRESS_LOG1(LF_TIEREDCOMPILATION, LL_WARNING, "CallCountingManager::CompleteCallCounting: "
                    "Exception, hr=0x%x\n",
                    GET_EXCEPTION()->GetHR());
            }
            EX_END_CATCH(RethrowTerminalExceptions);
        }

        callCountingInfosPendingCompletion.Clear();
        if (callCountingInfosPendingCompletion.GetAllocation() > 64)
        {
            callCountingInfosPendingCompletion.Trim();
            EX_TRY
            {
                callCountingInfosPendingCompletion.Preallocate(64);
            }
            EX_CATCH
            {
            }
            EX_END_CATCH(RethrowTerminalExceptions);
        }
    }
}

void CallCountingManager::StopAndDeleteAllCallCountingStubs()
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_PREEMPTIVE;
    }
    CONTRACTL_END;

    _ASSERTE(GetThreadNULLOk() == TieredCompilationManager::GetBackgroundWorkerThread());

    // If a number of call counting stubs have completed, we can try to delete them to reclaim some memory. Deleting
    // involves suspending the runtime and will delete all call counting stubs, and after that some call counting stubs may
    // be recreated in the foreground. The threshold is to decrease the impact of both of those overheads.
    COUNT_T deleteCallCountingStubsAfter = g_pConfig->TieredCompilation_DeleteCallCountingStubsAfter();
    if (deleteCallCountingStubsAfter == 0 || s_completedCallCountingStubCount < deleteCallCountingStubsAfter)
    {
        return;
    }

    TieredCompilationManager *tieredCompilationManager = GetAppDomain()->GetTieredCompilationManager();

    ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_OTHER);
    struct AutoRestartEE
    {
        ~AutoRestartEE()
        {
            WRAPPER_NO_CONTRACT;
            ThreadSuspend::RestartEE(false, true);
        }
    } autoRestartEE;

    MethodDescBackpatchInfoTracker::ConditionalLockHolder slotBackpatchLockHolder;
    CodeVersionManager::LockHolder codeVersioningLockHolder;

    // After the following, no method's entry point would be pointing to a call counting stub
    StopAllCallCounting(tieredCompilationManager);

    // Call counting has been stopped above and call counting stubs will soon be deleted. Ensure that call counting stubs
    // will not be used after resuming the runtime. The following ensures that other threads will not use an old cached
    // entry point value that will not be valid. Do this here in case of exception later.
    MemoryBarrier(); // flush writes from this thread first to guarantee ordering
    FlushProcessWriteBuffers();

    // At this point, allocated call counting stubs won't be used anymore. Call counting stubs and corresponding infos may
    // now be safely deleted. Note that call counting infos may not be deleted prior to this point because call counting
    // stubs refer to the remaining call count in the info, and the call counting info is necessary to get a code version
    // from a call counting stub address.
    DeleteAllCallCountingStubs();
}

void CallCountingManager::StopAllCallCounting(TieredCompilationManager *tieredCompilationManager)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        MODE_COOPERATIVE; // for slot backpatching
    }
    CONTRACTL_END;

    _ASSERTE(GetThreadNULLOk() == TieredCompilationManager::GetBackgroundWorkerThread());
    _ASSERTE(MethodDescBackpatchInfoTracker::IsLockOwnedByCurrentThread());
    _ASSERTE(CodeVersionManager::IsLockOwnedByCurrentThread());
    _ASSERTE(tieredCompilationManager != nullptr);

    for (auto itEnd = s_callCountingManagers->End(), it = s_callCountingManagers->Begin(); it != itEnd; ++it)
    {
        CallCountingManager *callCountingManager = *it;

        CallCountingInfoByCodeVersionHash &callCountingInfoByCodeVersionHash =
            callCountingManager->m_callCountingInfoByCodeVersionHash;
        for (auto itEnd = callCountingInfoByCodeVersionHash.End(), it = callCountingInfoByCodeVersionHash.Begin();
            it != itEnd;
            ++it)
        {
            CallCountingInfo *callCountingInfo = *it;
            CallCountingInfo::Stage callCountingStage = callCountingInfo->GetStage();
            if (callCountingStage != CallCountingInfo::Stage::StubMayBeActive &&
                callCountingStage != CallCountingInfo::Stage::PendingCompletion)
            {
                continue;
            }

            NativeCodeVersion codeVersion = callCountingInfo->GetCodeVersion();
            CallCountingInfo::Stage newCallCountingStage;
            if (callCountingStage == CallCountingInfo::Stage::StubMayBeActive)
            {
                newCallCountingStage = CallCountingInfo::Stage::StubIsNotActive;
            }
            else
            {
                _ASSERTE(callCountingStage == CallCountingInfo::Stage::PendingCompletion);
                if (!codeVersion.GetILCodeVersion().HasAnyOptimizedNativeCodeVersion(codeVersion))
                {
                    bool createTieringBackgroundWorker = false;
                    tieredCompilationManager->AsyncPromoteToTier1(codeVersion, &createTieringBackgroundWorker);
                    _ASSERTE(!createTieringBackgroundWorker); // the current thread is the background worker thread
                }

                newCallCountingStage = CallCountingInfo::Stage::Complete;
            }

            // The intention is that all call counting stubs will be deleted shortly, and only methods that are called again
            // will cause stubs to be recreated, so reset the code entry point
            codeVersion.GetMethodDesc()->ResetCodeEntryPoint();
            callCountingInfo->SetStage(newCallCountingStage);
        }

        // Clear recorded call counting infos pending completion, they have been completed above
        SArray<CallCountingInfo *> &callCountingInfosPendingCompletion =
            callCountingManager->m_callCountingInfosPendingCompletion;
        if (!callCountingInfosPendingCompletion.IsEmpty())
        {
            callCountingInfosPendingCompletion.Clear();
            if (callCountingInfosPendingCompletion.GetAllocation() > 64)
            {
                callCountingInfosPendingCompletion.Trim();
                EX_TRY
                {
                    callCountingInfosPendingCompletion.Preallocate(64);
                }
                EX_CATCH
                {
                }
                EX_END_CATCH(RethrowTerminalExceptions);
            }
        }

        // Reset forwarder stubs, they are not in use anymore
        MethodDescForwarderStubHash &methodDescForwarderStubHash = callCountingManager->m_methodDescForwarderStubHash;
        for (auto itEnd = methodDescForwarderStubHash.End(), it = methodDescForwarderStubHash.Begin(); it != itEnd; ++it)
        {
            Precode *forwarderStub = *it;
            forwarderStub->ResetTargetInterlocked();
        }
    }
}

void CallCountingManager::DeleteAllCallCountingStubs()
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    _ASSERTE(CodeVersionManager::IsLockOwnedByCurrentThread());
    _ASSERTE(IsSuspendEEThread());
    _ASSERTE(s_activeCallCountingStubCount == 0);

    // An attempt will be made to delete stubs below, clear the counts first in case of exception later
    s_callCountingStubCount = 0;
    s_completedCallCountingStubCount = 0;

    for (auto itEnd = s_callCountingManagers->End(), it = s_callCountingManagers->Begin(); it != itEnd; ++it)
    {
        CallCountingManager *callCountingManager = *it;
        _ASSERTE(callCountingManager->m_callCountingInfosPendingCompletion.IsEmpty());

        // Clear the call counting stub from call counting infos and delete completed infos
        MethodDescForwarderStubHash &methodDescForwarderStubHash = callCountingManager->m_methodDescForwarderStubHash;
        CallCountingInfoByCodeVersionHash &callCountingInfoByCodeVersionHash =
            callCountingManager->m_callCountingInfoByCodeVersionHash;
        for (auto itEnd = callCountingInfoByCodeVersionHash.End(), it = callCountingInfoByCodeVersionHash.Begin();
            it != itEnd;
            ++it)
        {
            CallCountingInfo *callCountingInfo = *it;
            CallCountingInfo::Stage callCountingStage = callCountingInfo->GetStage();
            if (callCountingStage == CallCountingInfo::Stage::Disabled)
            {
                continue;
            }

            if (callCountingInfo->GetCallCountingStub() != nullptr)
            {
                callCountingInfo->ClearCallCountingStub();
            }

            if (callCountingStage != CallCountingInfo::Stage::Complete)
            {
                _ASSERTE(callCountingStage == CallCountingInfo::Stage::StubIsNotActive);
                continue;
            }

            // Currently, tier 0 is the last code version that is counted, and the method is typically not counted anymore.
            // Remove the forwarder stub if one exists, a new one will be created if necessary, for example, if a profiler adds
            // an IL code version for the method.
            Precode *const *forwarderStubPtr =
                methodDescForwarderStubHash.LookupPtr(callCountingInfo->GetCodeVersion().GetMethodDesc());
            if (forwarderStubPtr != nullptr)
            {
                methodDescForwarderStubHash.RemovePtr(const_cast<Precode **>(forwarderStubPtr));
            }

            callCountingInfoByCodeVersionHash.Remove(it);
            delete callCountingInfo;
        }

        callCountingManager->TrimCollections();

        // All call counting stubs are deleted, not just the completed stubs. Typically, there are many methods that are called
        // only a few times and don't reach the call count threshold, so many stubs may not be recreated. On the other hand,
        // some methods may still be getting called, just less frequently, then call counting stubs would be recreated in the
        // foreground, which has some overhead that is currently managed in the conditions for deleting call counting stubs.
        // There are potential solutions to reclaim as much memory as possible and to minimize the foreground overhead, but they
        // seem to involve significantly higher complexity that doesn't seem worthwhile.
        callCountingManager->m_callCountingStubAllocator.Reset();
    }
}

void CallCountingManager::TrimCollections()
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    _ASSERTE(CodeVersionManager::IsLockOwnedByCurrentThread());

    // Resize the hash tables if it would save some space. The hash tables' item counts typically spikes and then stabilizes at
    // a lower value after most of the repeatedly called methods are promoted and the call counting infos deleted above.

    COUNT_T count = m_callCountingInfoByCodeVersionHash.GetCount();
    COUNT_T capacity = m_callCountingInfoByCodeVersionHash.GetCapacity();
    if (count == 0)
    {
        if (capacity != 0)
        {
            m_callCountingInfoByCodeVersionHash.RemoveAll();
        }
    }
    else if (count <= capacity / 4)
    {
        EX_TRY
        {
            m_callCountingInfoByCodeVersionHash.Reallocate(count * 2);
        }
        EX_CATCH
        {
        }
        EX_END_CATCH(RethrowTerminalExceptions);
    }

    count = m_methodDescForwarderStubHash.GetCount();
    capacity = m_methodDescForwarderStubHash.GetCapacity();
    if (count == 0)
    {
        if (capacity != 0)
        {
            m_methodDescForwarderStubHash.RemoveAll();
        }
    }
    else if (count <= capacity / 4)
    {
        EX_TRY
        {
            m_methodDescForwarderStubHash.Reallocate(count * 2);
        }
        EX_CATCH
        {
        }
        EX_END_CATCH(RethrowTerminalExceptions);
    }
}

#endif // !DACCESS_COMPILE

bool CallCountingManager::IsCallCountingStub(PCODE entryPoint)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
        SUPPORTS_DAC;
    }
    CONTRACTL_END;

    TADDR entryAddress = PCODEToPINSTR(entryPoint);
    _ASSERTE(entryAddress != (PCODE)NULL);

    CodeVersionManager::LockHolder codeVersioningLockHolder;

    PTR_CallCountingManagerHash callCountingManagers = s_callCountingManagers;
    if (callCountingManagers == NULL)
    {
        return false;
    }

    for (auto itEnd = callCountingManagers->End(), it = callCountingManagers->Begin(); it != itEnd; ++it)
    {
        PTR_CallCountingManager callCountingManager = *it;
        if (callCountingManager->m_callCountingStubAllocator.IsStub(entryAddress))
        {
            return true;
        }
    }
    return false;
}

PCODE CallCountingManager::GetTargetForMethod(PCODE callCountingStubEntryPoint)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_COOPERATIVE; // the call counting stub cannot be deleted while inspecting it
        SUPPORTS_DAC;
    }
    CONTRACTL_END;

    _ASSERTE(IsCallCountingStub(callCountingStubEntryPoint));

    return PTR_CallCountingStub(PCODEToPINSTR(callCountingStubEntryPoint))->GetTargetForMethod();
}

#ifdef DACCESS_COMPILE

void CallCountingManager::DacEnumerateCallCountingStubHeapRanges(CLRDataEnumMemoryFlags flags)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
        SUPPORTS_DAC;
    }
    CONTRACTL_END;

    CodeVersionManager::LockHolder codeVersioningLockHolder;

    PTR_CallCountingManagerHash callCountingManagers = s_callCountingManagers;
    if (callCountingManagers == NULL)
    {
        return;
    }

    for (auto itEnd = callCountingManagers->End(), it = callCountingManagers->Begin(); it != itEnd; ++it)
    {
        PTR_CallCountingManager callCountingManager = *it;
        callCountingManager->m_callCountingStubAllocator.EnumerateHeapRanges(flags);
    }
}

#endif // DACCESS_COMPILE

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CallCountingManager::CallCountingStubManager

SPTR_IMPL(CallCountingStubManager, CallCountingStubManager, g_pManager);

#ifndef DACCESS_COMPILE

CallCountingStubManager::CallCountingStubManager()
{
    WRAPPER_NO_CONTRACT;
}

void CallCountingStubManager::Init()
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    g_pManager = new CallCountingStubManager();
    StubManager::AddStubManager(g_pManager);
}

#endif // !DACCESS_COMPILE

#ifdef _DEBUG
const char *CallCountingStubManager::DbgGetName()
{
    WRAPPER_NO_CONTRACT;
    return "CallCountingStubManager";
}
#endif

#ifdef DACCESS_COMPILE
LPCWSTR CallCountingStubManager::GetStubManagerName(PCODE addr)
{
    WRAPPER_NO_CONTRACT;
    return W("CallCountingStub");
}
#endif

BOOL CallCountingStubManager::CheckIsStub_Internal(PCODE entryPoint)
{
    WRAPPER_NO_CONTRACT;
    SUPPORTS_DAC;

    return CallCountingManager::IsCallCountingStub(entryPoint);
}

BOOL CallCountingStubManager::DoTraceStub(PCODE callCountingStubEntryPoint, TraceDestination *trace)
{
    WRAPPER_NO_CONTRACT;
    SUPPORTS_DAC;
    _ASSERTE(trace != nullptr);

    trace->InitForStub(CallCountingManager::GetTargetForMethod(callCountingStubEntryPoint));
    return true;
}

#ifdef DACCESS_COMPILE
void CallCountingStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
{
    WRAPPER_NO_CONTRACT;
    SUPPORTS_DAC;

    DAC_ENUM_VTHIS();
    EMEM_OUT(("MEM: %p CallCountingStubManager\n", dac_cast<TADDR>(this)));
    CallCountingManager::DacEnumerateCallCountingStubHeapRanges(flags);
}
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#endif // FEATURE_TIERED_COMPILATION
