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

//----------------------------------------------------------
// CompileResult.cpp - CompileResult contains the stuff generated by a compilation
//----------------------------------------------------------

#include "standardpch.h"
#include "compileresult.h"
#include "methodcontext.h"
#include "spmirecordhelper.h"
#include "spmidumphelper.h"
#include "spmiutil.h"

CompileResult::CompileResult()
{
#define LWM(map, key, value) map = nullptr;
#include "crlwmlist.h"

    // Not persisted to disk.  though should it be?
    CallTargetTypes = new LightWeightMap<DWORDLONG, DWORD>();

    allocMemDets.hotCodeSize   = 0;
    allocMemDets.coldCodeSize  = 0;
    allocMemDets.roDataSize    = 0;
    allocMemDets.xcptnsCount   = 0;
    allocMemDets.flag          = (CorJitAllocMemFlag)0;
    allocMemDets.hotCodeBlock  = nullptr;
    allocMemDets.coldCodeBlock = nullptr;
    allocMemDets.roDataBlock   = nullptr;

    allocGCInfoDets.retval = nullptr;
    allocGCInfoDets.size   = 0;

    memoryTracker = nullptr;
}

CompileResult::~CompileResult()
{
#define LWM(map, key, value)                                                                                           \
    delete map;
#include "crlwmlist.h"

    delete CallTargetTypes;
    delete memoryTracker;
}

// Is the CompileResult empty? Define this as whether all the maps that store information given by the JIT are empty.
// This is useful when determining if a function won't apply after a "mcs -removeDump -thin" operation has been run.
bool CompileResult::IsEmpty()
{
    bool isEmpty = true;

#define LWM(map, key, value)                                                                                           \
    if (map != nullptr)                                                                                                \
        isEmpty = false;
#include "crlwmlist.h"

    return isEmpty;
}

// Allocate memory associated with this CompileResult. Keep track of it in a list so we can free it all later.
void* CompileResult::allocateMemory(size_t sizeInBytes)
{
    if (memoryTracker == nullptr)
        memoryTracker = new MemoryTracker();
    return memoryTracker->allocate(sizeInBytes);
}

void CompileResult::recAssert(const char* assertText)
{
    if (AssertLog == nullptr)
        AssertLog = new DenseLightWeightMap<DWORD>();

    AssertLog->Append(AssertLog->AddBuffer((const unsigned char*)assertText, (DWORD)strlen(assertText) + 1));
}
void CompileResult::dmpAssertLog(DWORD key, DWORD value)
{
    const char* assert = (const char*)AssertLog->GetBuffer(value);
    printf("AssertLog key %u, value '%s'", key, assert);
    AssertLog->Unlock();
}
const char* CompileResult::repAssert()
{
    if ((AssertLog == nullptr) || (AssertLog->GetCount() == 0))
        return nullptr;
    return (const char*)AssertLog->GetBuffer(AssertLog->Get((DWORD)0));
}

void CompileResult::AddCall(const char* name)
{
    if (CallLog == nullptr)
        CallLog = new DenseLightWeightMap<DWORD>();
    // if(name[0] != '+')
    // CallLog->Append(CallLog->AddBuffer((const unsigned char *)name, (DWORD)strlen(name)+1));
}
unsigned int CompileResult::CallLog_GetCount()
{
    return CallLog->GetCount();
}

bool CompileResult::CallLog_Contains(const char* str)
{
    return (CallLog->Contains((unsigned char*)str, (unsigned int)strlen(str)) > 0);
}
void CompileResult::dmpCallLog(DWORD key, DWORD value)
{
    const char* temp = (const char*)CallLog->GetBuffer(value);
    printf("CallLog %u '%s'", key, temp);
    CallLog->Unlock();
}

void CompileResult::dumpToConsole()
{
    printf("***************************************** CompileResult\n");

#define LWM(map, key, value) dumpLWM(this, map)
#define DENSELWM(map, value) dumpLWMDense(this, map)
#include "crlwmlist.h"

    printf("-----------------------------------------\n");
}

// Note - EE allocates these blocks (and the exception blocks) in a single linear region.
// Note - EE assures that RoBlock is 8 byte aligned
void CompileResult::recAllocMem(ULONG              hotCodeSize,
                                ULONG              coldCodeSize,
                                ULONG              roDataSize,
                                ULONG              xcptnsCount,
                                CorJitAllocMemFlag flag,
                                void**             hotCodeBlock,
                                void**             coldCodeBlock,
                                void**             roDataBlock)
{
    // Grab the values, so we can scrape the real answers in the capture method
    allocMemDets.hotCodeSize   = hotCodeSize;
    allocMemDets.coldCodeSize  = coldCodeSize;
    allocMemDets.roDataSize    = roDataSize;
    allocMemDets.xcptnsCount   = xcptnsCount;
    allocMemDets.flag          = flag;
    allocMemDets.hotCodeBlock  = *hotCodeBlock;
    allocMemDets.coldCodeBlock = *coldCodeBlock;
    allocMemDets.roDataBlock   = *roDataBlock;
}
void CompileResult::recAllocMemCapture()
{
    if (AllocMem == nullptr)
        AllocMem = new LightWeightMap<DWORD, Agnostic_AllocMemDetails>();

    Agnostic_AllocMemDetails value;

    value.hotCodeSize  = (DWORD)allocMemDets.hotCodeSize;
    value.coldCodeSize = (DWORD)allocMemDets.coldCodeSize;
    value.roDataSize   = (DWORD)allocMemDets.roDataSize;
    value.xcptnsCount  = (DWORD)allocMemDets.xcptnsCount;
    value.flag         = (DWORD)allocMemDets.flag;
    value.hotCodeBlock_offset =
        (DWORD)AllocMem->AddBuffer((const unsigned char*)allocMemDets.hotCodeBlock, allocMemDets.hotCodeSize);
    value.coldCodeBlock_offset =
        (DWORD)AllocMem->AddBuffer((const unsigned char*)allocMemDets.coldCodeBlock, allocMemDets.coldCodeSize);
    value.roDataBlock_offset =
        (DWORD)AllocMem->AddBuffer((const unsigned char*)allocMemDets.roDataBlock, allocMemDets.roDataSize);
    value.hotCodeBlock  = CastPointer(allocMemDets.hotCodeBlock);
    value.coldCodeBlock = CastPointer(allocMemDets.coldCodeBlock);
    value.roDataBlock   = CastPointer(allocMemDets.roDataBlock);

    AllocMem->Add(0, value);
}

void CompileResult::dmpAllocMem(DWORD key, const Agnostic_AllocMemDetails& value)
{
    printf("AllocMem key 0, value hotCodeSize-%u coldCodeSize-%u roDataSize-%u xcptnsCount-%u flag-%08X "
           "hotCodeBlock_offset-%u coldCodeBlock_offset-%u roDataBlock_offset-%u hotCodeBlock-%016" PRIX64 " "
           "coldCodeBlock-%016" PRIX64 " roDataBlock-%016" PRIX64,
           value.hotCodeSize, value.coldCodeSize, value.roDataSize, value.xcptnsCount, value.flag,
           value.hotCodeBlock_offset, value.coldCodeBlock_offset, value.roDataBlock_offset, value.hotCodeBlock,
           value.coldCodeBlock, value.roDataBlock);
}

// We can't allocate memory at the same address used during recording, so we pass back code/data block pointers
// that point into the AllocMem LightWeightMap, but also return what the original addresses were during recording.
void CompileResult::repAllocMem(ULONG*              hotCodeSize,
                                ULONG*              coldCodeSize,
                                ULONG*              roDataSize,
                                ULONG*              xcptnsCount,
                                CorJitAllocMemFlag* flag,
                                unsigned char**     hotCodeBlock,
                                unsigned char**     coldCodeBlock,
                                unsigned char**     roDataBlock,
                                void**              orig_hotCodeBlock,
                                void**              orig_coldCodeBlock,
                                void**              orig_roDataBlock)
{
    Agnostic_AllocMemDetails value;

    value = AllocMem->Get(0);

    *hotCodeSize  = (ULONG)value.hotCodeSize;
    *coldCodeSize = (ULONG)value.coldCodeSize;
    *roDataSize   = (ULONG)value.roDataSize;
    *xcptnsCount  = (ULONG)value.xcptnsCount;
    *flag         = (CorJitAllocMemFlag)value.flag;

    if (*hotCodeSize > 0)
        *hotCodeBlock = AllocMem->GetBuffer(value.hotCodeBlock_offset);
    else
        *hotCodeBlock = nullptr;

    if (*coldCodeSize > 0)
        *coldCodeBlock = AllocMem->GetBuffer(value.coldCodeBlock_offset);
    else
        *coldCodeBlock = nullptr;

    if (*roDataSize > 0)
        *roDataBlock = AllocMem->GetBuffer(value.roDataBlock_offset);
    else
        *roDataBlock = nullptr;

    *orig_hotCodeBlock  = (void*)value.hotCodeBlock;
    *orig_coldCodeBlock = (void*)value.coldCodeBlock;
    *orig_roDataBlock   = (void*)value.roDataBlock;
}

// Note - Ownership of pMap is transferred with this call. In replay icorjitinfo we should free it.
void CompileResult::recSetBoundaries(CORINFO_METHOD_HANDLE ftn, ULONG32 cMap, ICorDebugInfo::OffsetMapping* pMap)
{
    if (SetBoundaries == nullptr)
        SetBoundaries = new LightWeightMap<DWORD, Agnostic_SetBoundaries>();

    Agnostic_SetBoundaries value;

    value.ftn  = CastHandle(ftn);
    value.cMap = (DWORD)cMap;
    value.pMap_offset =
        (DWORD)SetBoundaries->AddBuffer((const unsigned char*)pMap, sizeof(ICorDebugInfo::OffsetMapping) * cMap);

    SetBoundaries->Add(0, value);
}
void CompileResult::dmpSetBoundaries(DWORD key, const Agnostic_SetBoundaries& value)
{
    ICorDebugInfo::OffsetMapping* om = (ICorDebugInfo::OffsetMapping*)SetBoundaries->GetBuffer(value.pMap_offset);
    printf("SetBoundaries key 0, value ftn-%016" PRIX64 " cMap-%u %u{", value.ftn, value.cMap, value.pMap_offset);
    for (unsigned int i = 0; i < value.cMap; i++)
    {
        if (i != 0)
            printf(", ");
        printf("%u %u %u", om[i].ilOffset, om[i].nativeOffset, om[i].source);
    }
    printf("}");
    SetBoundaries->Unlock();
}
bool CompileResult::repSetBoundaries(CORINFO_METHOD_HANDLE* ftn, ULONG32* cMap, ICorDebugInfo::OffsetMapping** pMap)
{
    if ((SetBoundaries == nullptr) || (SetBoundaries->GetCount() == 0))
    {
        *ftn  = (CORINFO_METHOD_HANDLE)-1;
        *cMap = -1;
        *pMap = nullptr;
        return false;
    }
    Agnostic_SetBoundaries value;

    value = SetBoundaries->Get(0);

    *ftn  = (CORINFO_METHOD_HANDLE)value.ftn;
    *cMap = (ULONG32)value.cMap;
    *pMap = (ICorDebugInfo::OffsetMapping*)SetBoundaries->GetBuffer(value.pMap_offset);
    return true;
}

// Note - Ownership of vars is transferred with this call. In replay icorjitinfo we should free it.
void CompileResult::recSetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo* vars)
{
    if (SetVars == nullptr)
        SetVars = new LightWeightMap<DWORD, Agnostic_SetVars>();

    Agnostic_SetVars value;

    value.ftn         = CastHandle(ftn);
    value.cVars       = (DWORD)cVars;
    value.vars_offset = (DWORD)SetVars->AddBuffer((const unsigned char*)vars,
                                                  sizeof(ICorDebugInfo::NativeVarInfo) *
                                                      cVars); // not deep enough.. vlt memory is pointer sized.

    SetVars->Add(0, value);
}
void CompileResult::dmpSetVars(DWORD key, const Agnostic_SetVars& value)
{
    ICorDebugInfo::NativeVarInfo* om = (ICorDebugInfo::NativeVarInfo*)SetVars->GetBuffer(value.vars_offset);
    printf("SetVars key %u, value ftn-%016" PRIX64 " cVars-%u %u{", key, value.ftn, value.cVars, value.vars_offset);
    for (unsigned int i = 0; i < value.cVars; i++)
    {
        if (i != 0)
            printf(", ");
        printf("so-%u eo-%u var-%u", om[i].startOffset, om[i].endOffset, om[i].varNumber);
    }
    printf("}");
    SetVars->Unlock();
}
bool CompileResult::repSetVars(CORINFO_METHOD_HANDLE* ftn, ULONG32* cVars, ICorDebugInfo::NativeVarInfo** vars)
{
    if ((SetVars == nullptr) || (SetVars->GetCount() == 0))
    {
        *ftn   = (CORINFO_METHOD_HANDLE)-1;
        *cVars = -1;
        *vars  = nullptr;
        return false;
    }

    Agnostic_SetVars value;

    value = SetVars->Get(0);

    *ftn   = (CORINFO_METHOD_HANDLE)value.ftn;
    *cVars = (ULONG32)value.cVars;
    *vars  = (ICorDebugInfo::NativeVarInfo*)SetVars->GetBuffer(value.vars_offset);

    return true;
}

// Note - Ownership of patchpointInfo is transferred with this call. In replay icorjitinfo we should free it.
void CompileResult::recSetPatchpointInfo(PatchpointInfo* patchpointInfo)
{
    if (SetPatchpointInfo == nullptr)
        SetPatchpointInfo = new LightWeightMap<DWORD, Agnostic_SetPatchpointInfo>();

    Agnostic_SetPatchpointInfo value;
    value.index = (DWORD)SetPatchpointInfo->AddBuffer((const unsigned char*) patchpointInfo, patchpointInfo->PatchpointInfoSize());
    SetPatchpointInfo->Add(0, value);
}
void CompileResult::dmpSetPatchpointInfo(DWORD key, const Agnostic_SetPatchpointInfo& value)
{
    PatchpointInfo* patchpointInfo = (PatchpointInfo*)SetPatchpointInfo->GetBuffer(value.index);
    printf("SetPatchpointInfo key %u, index %u{", key, value.index);
    // todo -- dump contents
    printf("}");
    SetPatchpointInfo->Unlock();
}
bool CompileResult::repSetPatchpointInfo(PatchpointInfo** patchpointInfo)
{
    if ((SetPatchpointInfo == nullptr) || (SetPatchpointInfo->GetCount() == 0))
    {
        *patchpointInfo = nullptr;
        return false;
    }

    Agnostic_SetPatchpointInfo value;
    value = SetPatchpointInfo->Get(0);
    *patchpointInfo = (PatchpointInfo*)SetPatchpointInfo->GetBuffer(value.index);
    return true;
}

void CompileResult::recAllocGCInfo(size_t size, void* retval)
{
    allocGCInfoDets.size   = size;
    allocGCInfoDets.retval = retval;
}
void CompileResult::recAllocGCInfoCapture()
{
    if (AllocGCInfo == nullptr)
        AllocGCInfo = new LightWeightMap<DWORD, Agnostic_AllocGCInfo>();

    Agnostic_AllocGCInfo value;

    value.size = allocGCInfoDets.size;
    value.retval_offset =
        (DWORD)AllocGCInfo->AddBuffer((const unsigned char*)allocGCInfoDets.retval, (DWORD)allocGCInfoDets.size);

    AllocGCInfo->Add(0, value);
}
void CompileResult::dmpAllocGCInfo(DWORD key, const Agnostic_AllocGCInfo& value)
{
    const unsigned char* buff = AllocGCInfo->GetBuffer(value.retval_offset);
    printf("AllocGCInfo key 0, ");
    printf("sz-%" PRIu64 " %p{ ", value.size, buff);
    for (unsigned int i = 0; i < value.size; i++)
        printf("%02X ", *(buff + i));
    printf("}");
    AllocGCInfo->Unlock();
}
void CompileResult::repAllocGCInfo(size_t* size, void** retval)
{
    Agnostic_AllocGCInfo value;

    value = AllocGCInfo->Get(0);

    *size = (size_t)value.size;
    if (*size > 0)
        *retval = (void*)AllocGCInfo->GetBuffer(value.retval_offset);
}

void CompileResult::recCompileMethod(uint8_t** nativeEntry, uint32_t* nativeSizeOfCode, CorJitResult result)
{
    if (CompileMethod == nullptr)
        CompileMethod = new LightWeightMap<DWORD, Agnostic_CompileMethodResults>();

    Agnostic_CompileMethodResults value;
    value.nativeEntry      = CastPointer(*nativeEntry);
    value.nativeSizeOfCode = (DWORD)*nativeSizeOfCode;
    value.CorJitResult     = (DWORD)result;

    CompileMethod->Add(0, value);
}
void CompileResult::dmpCompileMethod(DWORD key, const Agnostic_CompileMethodResults& value)
{
    printf("CompileMethod key %u, value nativeEntry-%016" PRIX64 " nativeSizeOfCode-%u CorJitResult-%u", key,
           value.nativeEntry, value.nativeSizeOfCode, value.CorJitResult);
}
void CompileResult::repCompileMethod(BYTE** nativeEntry, ULONG* nativeSizeOfCode, CorJitResult* result)
{
    Agnostic_CompileMethodResults value;
    value             = CompileMethod->Get(0);
    *nativeEntry      = (BYTE*)value.nativeEntry;
    *nativeSizeOfCode = (ULONG)value.nativeSizeOfCode;
    *result           = (CorJitResult)value.CorJitResult;
}

void CompileResult::recMessageLog(const char* fmt, ...)
{
    // TODO-Cleanup: ???
    return;
    if (MessageLog == nullptr)
        MessageLog = new DenseLightWeightMap<DWORD>();

    va_list args;

    // retrieve the variable arguments
    va_start(args, fmt);

    size_t len = _vscprintf(fmt, args) + 1; // space for the terminator

    unsigned char* messageLogBuffer = new unsigned char[len];
    vsprintf_s((char*)messageLogBuffer, len, fmt, args);
    messageLogBuffer[len - 1] = 0;
    MessageLog->Append(MessageLog->AddBuffer(messageLogBuffer, (DWORD)len));
    delete[] messageLogBuffer;
}
void CompileResult::dmpMessageLog(DWORD key, DWORD value)
{
    printf("MessageLog NYI");
}

void CompileResult::recClassMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_HANDLE cls)
{
    if (ClassMustBeLoadedBeforeCodeIsRun == nullptr)
        ClassMustBeLoadedBeforeCodeIsRun = new DenseLightWeightMap<DWORDLONG>();

    ClassMustBeLoadedBeforeCodeIsRun->Append(CastHandle(cls));
}
void CompileResult::dmpClassMustBeLoadedBeforeCodeIsRun(DWORD key, DWORDLONG value)
{
    printf("ClassMustBeLoadedBeforeCodeIsRun key %u, value cls-%016" PRIX64, key, value);
}

void CompileResult::recReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd,
                                              CORINFO_METHOD_HANDLE inlineeHnd,
                                              CorInfoInline         inlineResult,
                                              const char*           reason)
{
    if (ReportInliningDecision == nullptr)
        ReportInliningDecision = new DenseLightWeightMap<Agnostic_ReportInliningDecision>();

    Agnostic_ReportInliningDecision value;

    value.inlinerHnd   = CastHandle(inlinerHnd);
    value.inlineeHnd   = CastHandle(inlineeHnd);
    value.inlineResult = (DWORD)inlineResult;
    if (reason != nullptr)
        value.reason_offset =
            (DWORD)ReportInliningDecision->AddBuffer((unsigned char*)reason, (DWORD)strlen(reason) + 1);
    else
        value.reason_offset = -1;

    ReportInliningDecision->Append(value);
}
void CompileResult::dmpReportInliningDecision(DWORD key, const Agnostic_ReportInliningDecision& value)
{
    const char* reason = (const char*)ReportInliningDecision->GetBuffer(value.reason_offset);
    printf("ReportInliningDecision key %u, value inliner-%016" PRIX64 " inlinee-%016" PRIX64 " res-%u reason-'%s'", key,
           value.inlinerHnd, value.inlineeHnd, value.inlineResult, reason);
    ReportInliningDecision->Unlock();
}
CorInfoInline CompileResult::repReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd,
                                                       CORINFO_METHOD_HANDLE inlineeHnd)
{
    CorInfoInline result = INLINE_FAIL;
    if (ReportInliningDecision != nullptr)
    {
        Agnostic_ReportInliningDecision* items = ReportInliningDecision->GetRawItems();
        unsigned int                     cnt   = ReportInliningDecision->GetCount();
        for (unsigned int i = 0; i < cnt; i++)
        {
            if ((items[i].inlinerHnd == CastHandle(inlinerHnd)) && (items[i].inlineeHnd == CastHandle(inlineeHnd)) &&
                (items[i].inlineResult == INLINE_PASS))
                result = INLINE_PASS;
        }
    }
    return result;
}

void CompileResult::recSetEHcount(unsigned cEH)
{
    if (SetEHcount == nullptr)
        SetEHcount = new LightWeightMap<DWORD, DWORD>();

    SetEHcount->Add((DWORD)0, (DWORD)cEH);
}
void CompileResult::dmpSetEHcount(DWORD key, DWORD value)
{
    printf("SetEHcount key %u, value %u", key, value);
}
ULONG CompileResult::repSetEHcount()
{
    if (SetEHcount == nullptr)
        SetEHcount = new LightWeightMap<DWORD, DWORD>();

    ULONG ehCount;
    int   index = SetEHcount->GetIndex(0);
    if (index < 0)
        ehCount = 0;
    else
        ehCount = (ULONG)SetEHcount->Get(index);
    return ehCount;
}

void CompileResult::recSetEHinfo(unsigned EHnumber, const CORINFO_EH_CLAUSE* clause)
{
    if (SetEHinfo == nullptr)
        SetEHinfo = new LightWeightMap<DWORD, Agnostic_CORINFO_EH_CLAUSE>();

    Agnostic_CORINFO_EH_CLAUSE value;
    value.Flags         = (DWORD)clause->Flags;
    value.TryOffset     = (DWORD)clause->TryOffset;
    value.TryLength     = (DWORD)clause->TryLength;
    value.HandlerOffset = (DWORD)clause->HandlerOffset;
    value.HandlerLength = (DWORD)clause->HandlerLength;
    value.ClassToken    = (DWORD)clause->ClassToken;

    SetEHinfo->Add((DWORD)EHnumber, value);
}
void CompileResult::dmpSetEHinfo(DWORD key, const Agnostic_CORINFO_EH_CLAUSE& value)
{
    printf("SetEHinfo key %u, value flg-%u to-%u tl-%u ho-%u hl-%u", key, value.Flags, value.TryOffset, value.TryLength,
           value.HandlerOffset, value.HandlerLength);
    if ((CORINFO_EH_CLAUSE_FLAGS)value.Flags == CORINFO_EH_CLAUSE_FILTER)
    {
        printf(" fo-%u", value.ClassToken); // FilterOffset
    }
    else if ((CORINFO_EH_CLAUSE_FLAGS)value.Flags == CORINFO_EH_CLAUSE_NONE)
    {
        printf(" cls-%08X", value.ClassToken);
    }
    // else, no need to print for finally/fault handlers
}
void CompileResult::repSetEHinfo(unsigned EHnumber,
                                 ULONG*   flags,
                                 ULONG*   tryOffset,
                                 ULONG*   tryLength,
                                 ULONG*   handlerOffset,
                                 ULONG*   handlerLength,
                                 ULONG*   classToken)
{
    Agnostic_CORINFO_EH_CLAUSE value;
    value = SetEHinfo->Get(EHnumber);

    *flags         = (ULONG)value.Flags;
    *tryOffset     = (ULONG)value.TryOffset;
    *tryLength     = (ULONG)value.TryLength;
    *handlerOffset = (ULONG)value.HandlerOffset;
    *handlerLength = (ULONG)value.HandlerLength;
    *classToken    = (ULONG)value.ClassToken;
}

void CompileResult::recSetMethodAttribs(CORINFO_METHOD_HANDLE ftn, CorInfoMethodRuntimeFlags attribs)
{
    if (SetMethodAttribs == nullptr)
        SetMethodAttribs = new LightWeightMap<DWORDLONG, DWORD>();

    int index = SetMethodAttribs->GetIndex(CastHandle(ftn));
    if (index == -1)
    {
        SetMethodAttribs->Add(CastHandle(ftn), (DWORD)attribs);
    }
    else
    {
        DWORD existingAttribs = SetMethodAttribs->GetItem(index);
        SetMethodAttribs->Update(index, existingAttribs | (DWORD)attribs);
    }
}
void CompileResult::dmpSetMethodAttribs(DWORDLONG key, DWORD value)
{
    printf("SetMethodAttribs key ftn-%016" PRIX64 ", value attr-%08X", key, value);
}
CorInfoMethodRuntimeFlags CompileResult::repSetMethodAttribs(CORINFO_METHOD_HANDLE ftn)
{
    if (SetMethodAttribs == nullptr)
        return (CorInfoMethodRuntimeFlags)0;

    int index = SetMethodAttribs->GetIndex(CastHandle(ftn));
    if (index == -1)
        return (CorInfoMethodRuntimeFlags)0;

    CorInfoMethodRuntimeFlags result = (CorInfoMethodRuntimeFlags)SetMethodAttribs->GetItem(index);
    return result;
}

void CompileResult::recMethodMustBeLoadedBeforeCodeIsRun(CORINFO_METHOD_HANDLE method)
{
    if (MethodMustBeLoadedBeforeCodeIsRun == nullptr)
        MethodMustBeLoadedBeforeCodeIsRun = new DenseLightWeightMap<DWORDLONG>();

    MethodMustBeLoadedBeforeCodeIsRun->Append(CastHandle(method));
}
void CompileResult::dmpMethodMustBeLoadedBeforeCodeIsRun(DWORD key, DWORDLONG value)
{
    printf("MethodMustBeLoadedBeforeCodeIsRun key %u, value ftn-%016" PRIX64, key, value);
}

void CompileResult::recReportTailCallDecision(CORINFO_METHOD_HANDLE callerHnd,
                                              CORINFO_METHOD_HANDLE calleeHnd,
                                              bool                  fIsTailPrefix,
                                              CorInfoTailCall       tailCallResult,
                                              const char*           reason)
{
    if (ReportTailCallDecision == nullptr)
        ReportTailCallDecision = new DenseLightWeightMap<Agnostic_ReportTailCallDecision>();

    Agnostic_ReportTailCallDecision value;

    value.callerHnd      = CastHandle(callerHnd);
    value.calleeHnd      = CastHandle(calleeHnd);
    value.fIsTailPrefix  = (DWORD)fIsTailPrefix;
    value.tailCallResult = (DWORD)tailCallResult;
    if (reason != nullptr) // protect strlen
        value.reason_index =
            (DWORD)ReportTailCallDecision->AddBuffer((unsigned char*)reason, (DWORD)strlen(reason) + 1);
    else
        value.reason_index = (DWORD)-1;

    ReportTailCallDecision->Append(value);
}
void CompileResult::dmpReportTailCallDecision(DWORD key, const Agnostic_ReportTailCallDecision& value)
{
    const char* reason = (const char*)ReportTailCallDecision->GetBuffer(value.reason_index);
    printf("ReportTailCallDecision key-%u, value cr-%016" PRIX64 " ce-%016" PRIX64 " tail-%u call-%u -%s", key, value.callerHnd,
           value.calleeHnd, value.tailCallResult, value.tailCallResult, reason);
    ReportTailCallDecision->Unlock();
}

void CompileResult::recReportFatalError(CorJitResult result)
{
    if (ReportFatalError == nullptr)
        ReportFatalError = new DenseLightWeightMap<DWORD>();

    ReportFatalError->Append((DWORD)result);
}
void CompileResult::dmpReportFatalError(DWORD key, DWORD value)
{
    printf("ReportFatalError key Count-%u, value result-%08X", key, value);
}

void CompileResult::recRecordRelocation(void* location, void* target, uint16_t fRelocType, int32_t addlDelta)
{
    repRecordRelocation(location, target, fRelocType, addlDelta);
}

const char* relocationTypeToString(uint16_t fRelocType)
{
    switch (fRelocType)
    {
        // From winnt.h
        case IMAGE_REL_BASED_ABSOLUTE:
            return "absolute";
        case IMAGE_REL_BASED_HIGH:
            return "high";
        case IMAGE_REL_BASED_LOW:
            return "low";
        case IMAGE_REL_BASED_HIGHLOW:
            return "highlow";
        case IMAGE_REL_BASED_HIGHADJ:
            return "highadj";
        case IMAGE_REL_BASED_DIR64:
            return "dir64";

        // From corinfo.h
        case IMAGE_REL_BASED_REL32:
            return "rel32";
        case IMAGE_REL_BASED_THUMB_BRANCH24:
            return "thumb_branch24";
        default:
            return "UNKNOWN";
    }
}
void CompileResult::dmpRecordRelocation(DWORD key, const Agnostic_RecordRelocation& value)
{
    printf("RecordRelocation key %u, value loc-%016" PRIX64 " tgt-%016" PRIX64 " fRelocType-%u(%s) addlDelta:%d", key,
           value.location, value.target, value.fRelocType, relocationTypeToString((uint16_t)value.fRelocType),
           (int32_t)value.addlDelta);
}
void CompileResult::repRecordRelocation(void* location, void* target, uint16_t fRelocType, int32_t addlDelta)
{
    if (RecordRelocation == nullptr)
        RecordRelocation = new DenseLightWeightMap<Agnostic_RecordRelocation>();

    Agnostic_RecordRelocation value;

    value.location   = CastPointer(location);
    value.target     = CastPointer(target);
    value.fRelocType = (DWORD)fRelocType;
    value.addlDelta  = (DWORD)addlDelta;

    RecordRelocation->Append(value);
}

// When we do a replay, we replace the CompileResult that was originally recorded. In most cases,
// though, as part of our collection process we do a "thinning" operation, using "mcs -removeDup -thin",
// which removes the CompileResult from the stored file. Because of this, we lose access to the original
// addresses.
//
// Unfortunately, we may have generated code based on the original relative addresses. For example,
// we might have generated a relocation based on the results of a call to GetFunctionEntryPoint, and
// the delta from the generated code to that address fit in a rel32 reloc. Or, it didn't fit, but the
// VM constructed a jump stub in the JIT code section that would fit, and returned that.
//
// Ideally, we would just keep all the original addresses and relocations, even if we delete the CompileResult.
// If a new relocation has the same target as an original relocation, then simply substitute the original
// delta and use that instead of the newly computed delta.
//
// For now, for rel32 relocations, if it doesn't fit, then simply pick an address immediately after the
// current section (using originalAddr), assuming we needed a jump stub. We'll let multiple calls to potentially
// different functions use the same address because even if they used different ones, and diffs were generated,
// no textual diffs would appear because most of the textual call names are "hackishMethodName".
//
void CompileResult::applyRelocs(RelocContext* rc, unsigned char* block1, ULONG blocksize1, void* originalAddr)
{
    if (RecordRelocation == nullptr)
        return;
    if (blocksize1 == 0)
        return;

    size_t section_begin = (size_t)block1;
    size_t section_end   = (size_t)block1 + (size_t)blocksize1; // address is exclusive

    LogDebug("applyRelocs block [%p,%p) block size %u, orig addr %p", block1, block1 + blocksize1, blocksize1,
             originalAddr);

    for (unsigned int i = 0; i < RecordRelocation->GetCount(); i++)
    {
        Agnostic_RecordRelocation tmp = RecordRelocation->GetRawItems()[i];

        if (Logger::IsLogLevelEnabled(LOGLEVEL_DEBUG))
        {
            printf("  ");
            dmpRecordRelocation(i, tmp);
            printf("\n");
        }

        const SPMI_TARGET_ARCHITECTURE targetArch = GetSpmiTargetArchitecture();

        const DWORD relocType = tmp.fRelocType;
        bool wasRelocHandled  = false;

        // Do platform specific relocations first.

        if ((targetArch == SPMI_TARGET_ARCHITECTURE_X86) || (targetArch == SPMI_TARGET_ARCHITECTURE_ARM))
        {
            if (relocType == IMAGE_REL_BASED_HIGHLOW)
            {
                DWORDLONG fixupLocation = tmp.location;

                size_t address = section_begin + (size_t)fixupLocation - (size_t)originalAddr;
                if ((section_begin <= address) && (address < section_end)) // A reloc for our section?
                {
                    LogDebug("    fixupLoc-%016" PRIX64 " (@%p) : %08X => %08X", fixupLocation, address, *(DWORD*)address,
                        (DWORD)tmp.target);
                    *(DWORD*)address = (DWORD)tmp.target;
                }
                wasRelocHandled = true;
            }
        }

        if (targetArch == SPMI_TARGET_ARCHITECTURE_ARM)
        {
            DWORDLONG fixupLocation = tmp.location;
            DWORDLONG address       = section_begin + (size_t)fixupLocation - (size_t)originalAddr;

            switch (relocType)
            {
                case IMAGE_REL_BASED_THUMB_MOV32:
                case IMAGE_REL_BASED_REL_THUMB_MOV32_PCREL:
                {
                    INT32 delta  = (INT32)(tmp.target - fixupLocation);
                    if ((section_begin <= address) && (address < section_end)) // A reloc for our section?
                    {
                        PutThumb2Mov32((UINT16*)address, (UINT32)delta);
                    }
                    wasRelocHandled = true;
                }
                break;

                case IMAGE_REL_BASED_THUMB_BRANCH24:
                {
                    INT32 delta = (INT32)(tmp.target - fixupLocation);
                    if ((section_begin <= address) && (address < section_end)) // A reloc for our section?
                    {
                        if (!FitsInThumb2BlRel24(delta))
                        {
                            DWORDLONG target = (DWORDLONG)originalAddr + (DWORDLONG)blocksize1;
                            delta            = (INT32)(target - fixupLocation);
                        }
                        PutThumb2BlRel24((UINT16*)address, delta);
                    }
                    wasRelocHandled = true;
                }
                break;

                default:
                    break;
            }
        }

        if (targetArch == SPMI_TARGET_ARCHITECTURE_ARM64)
        {
            DWORDLONG fixupLocation = tmp.location;
            DWORDLONG address       = section_begin + (size_t)fixupLocation - (size_t)originalAddr;

            switch (relocType)
            {
                case IMAGE_REL_ARM64_BRANCH26: // 26 bit offset << 2 & sign ext, for B and BL
                {
                    if ((section_begin <= address) && (address < section_end)) // A reloc for our section?
                    {
                        INT64 delta = (INT64)(tmp.target - fixupLocation);
                        if (!FitsInRel28(delta))
                        {
                            // Assume here that we would need a jump stub for this relocation and pretend
                            // that the jump stub is located right at the end of the method.
                            DWORDLONG target = (DWORDLONG)originalAddr + (DWORDLONG)blocksize1;
                            delta = (INT64)(target - fixupLocation);
                        }
                        PutArm64Rel28((UINT32*)address, (INT32)delta);
                    }
                    wasRelocHandled = true;
                }
                break;

                case IMAGE_REL_ARM64_PAGEBASE_REL21: // ADRP 21 bit PC-relative page address
                {
                    if ((section_begin <= address) && (address < section_end)) // A reloc for our section?
                    {
                        INT64 targetPage        = (INT64)tmp.target & 0xFFFFFFFFFFFFF000LL;
                        INT64 fixupLocationPage = (INT64)fixupLocation & 0xFFFFFFFFFFFFF000LL;
                        INT64 pageDelta         = (INT64)(targetPage - targetPage);
                        INT32 imm21             = (INT32)(pageDelta >> 12) & 0x1FFFFF;
                        PutArm64Rel21((UINT32*)address, imm21);
                    }
                    wasRelocHandled = true;
                }
                break;

                case IMAGE_REL_ARM64_PAGEOFFSET_12A: // ADD 12 bit page offset
                {
                    if ((section_begin <= address) && (address < section_end)) // A reloc for our section?
                    {
                        INT32 imm12 = (INT32)(SIZE_T)tmp.target & 0xFFFLL;
                        PutArm64Rel12((UINT32*)address, imm12);
                    }
                    wasRelocHandled = true;
                }
                break;

                default:
                    break;
            }
        }

        if (targetArch == SPMI_TARGET_ARCHITECTURE_LOONGARCH64)
        {
            Assert(!"FIXME: Not Implements on loongarch64");
        }

        if (IsSpmiTarget64Bit())
        {
            if (relocType == IMAGE_REL_BASED_DIR64)
            {
                DWORDLONG fixupLocation = tmp.location;

                // Write 64-bits into location
                size_t address = section_begin + (size_t)fixupLocation - (size_t)originalAddr;
                if ((section_begin <= address) && (address < section_end)) // A reloc for our section?
                {
                    LogDebug("    fixupLoc-%016" PRIX64 " (@%p) %016" PRIX64 " => %016" PRIX64, fixupLocation, address,
                        *(DWORDLONG*)address, tmp.target);
                    *(DWORDLONG*)address = tmp.target;
                }

                wasRelocHandled = true;
            }
        }

        if (wasRelocHandled)
            continue;

        // Now do all-platform relocations.
        if (tmp.fRelocType == IMAGE_REL_BASED_REL32)
        {
            DWORDLONG fixupLocation = tmp.location;

            size_t address = section_begin + (size_t)fixupLocation - (size_t)originalAddr;
            if ((section_begin <= address) && (address < section_end)) // A reloc for our section?
            {
                DWORDLONG target        = tmp.target + (int32_t)tmp.addlDelta;
                DWORDLONG baseAddr      = fixupLocation + sizeof(INT32);
                INT64     delta         = (INT64)(target - baseAddr);
                bool      deltaIsFinal  = false;

                if (IsSpmiTarget64Bit())
                {
                    if (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_AMD64)
                    {
                        // For just AMD64:
                        // The VM attempts to allocate the JIT code buffer near the CLR assemblies, so 32-bit
                        // offsets (and REL32 relocations) can be used in the code. If this doesn't work out,
                        // such that a REL32 relocation doesn't fit, the VM throws away the JIT result, disables
                        // using REL32 relocations, and restarts compilation. SuperPMI doesn't know where the
                        // original compilation (during the collection) was allocated (though maybe we should
                        // add that to the MC, not just the CompileResult), and we don't have any control over
                        // where the JIT buffer is allocated. To handle this, if the getRelocTypeHint() was
                        // called on the target address, and the VM returned IMAGE_REL_BASED_REL32, then simply
                        // use the low-order 32 bits of the target address. This is unique enough for assembly
                        // diffs, because the delta will compare identically and won't be dependent on where
                        // SuperPMI allocated the JIT memory.

                        if (rc->mc->GetRelocTypeHint != nullptr)
                        {
                            DWORDLONG key   = tmp.target;
                            int       index = rc->mc->GetRelocTypeHint->GetIndex(key);
                            if (index == -1)
                            {
                                // See if the original address is in the replay address map. This happens for
                                // relocations on static field addresses found via getFieldInfo().
                                void* origAddr = repAddressMap((void*)tmp.target);
                                if ((origAddr != (void*)-1) && (origAddr != nullptr))
                                {
                                    key   = CastPointer(origAddr);
                                    index = rc->mc->GetRelocTypeHint->GetIndex(key);
                                    if (index != -1)
                                    {
                                        LogDebug("    Using address map: target %016" PRIX64 ", original target %016" PRIX64,
                                            tmp.target, key);
                                    }
                                }
                            }

                            if (index != -1)
                            {
                                WORD retVal = (WORD)rc->mc->GetRelocTypeHint->Get(key);
                                if (retVal == IMAGE_REL_BASED_REL32)
                                {
                                    LogDebug("    REL32 target used as argument to getRelocTypeHint: setting delta=%d (0x%X)",
                                             (int)key, (int)key);
                                    delta        = (INT64)(int)key;
                                    deltaIsFinal = true;
                                }
                            }
                        }
                    }

                    if (!deltaIsFinal)
                    {
                        // Check if tmp.target is the result of a call to getHelperFtn(). If so, the VM would create a
                        // jump stub if the REL32 address doesn't fit. We don't want to fail with a REL32 overflow if
                        // the actual target address doesn't fit, so use the low-order 32 bits of the address.
                        // We need to iterate the entire table since we don't know the helper function id.

                        if (rc->mc->GetHelperFtn != nullptr)
                        {
                            for (unsigned int idx = 0; idx < rc->mc->GetHelperFtn->GetCount(); idx++)
                            {
                                DLDL value = rc->mc->GetHelperFtn->GetItem(idx);
                                if (value.B == tmp.target)
                                {
                                    LogDebug("    REL32 target is result of getHelperFtn(): setting delta=%d (0x%X)",
                                             (int)tmp.target, (int)tmp.target);
                                    delta        = (INT64)(int)tmp.target;
                                    deltaIsFinal = true;
                                    break; // No need to consider the remaining GetHelperFtn entries
                                }
                            }
                        }
                    }

                    if (!deltaIsFinal)
                    {
                        // Check if tmp.target is the result of a call to GetFunctionEntryPoint(). As for helper
                        // functions, above, the VM would create a jump stub if the REL32 address doesn't fit.

                        if (rc->mc->GetFunctionEntryPoint != nullptr)
                        {
                            for (unsigned int idx = 0; idx < rc->mc->GetFunctionEntryPoint->GetCount(); idx++)
                            {
                                DLD value = rc->mc->GetFunctionEntryPoint->GetItem(idx);
                                if (value.A == tmp.target)
                                {
                                    LogDebug("    REL32 target is result of getFunctionEntryPoint(): setting delta=%d (0x%X)",
                                             (int)tmp.target, (int)tmp.target);
                                    delta        = (INT64)(int)tmp.target;
                                    deltaIsFinal = true;
                                    break; // No need to consider the remaining GetFunctionEntryPoint entries
                                }
                            }
                        }
                    }

                    if (!deltaIsFinal)
                    {
                        // If the relocation points to the RO-data section, we need to be careful that the relocation
                        // fits in 32-bits for both the baseline and diff compilations. To do that, we pretend the RO
                        // data section exists immediately after the current code section.

                        if ((rc->originalRoDataAddress <= (size_t)target) &&
                            ((size_t)target < rc->originalRoDataAddress + rc->roDataSize))
                        {
                            size_t ro_section_offset     = (size_t)target - rc->originalRoDataAddress;
                            size_t ro_section_fake_start = (size_t)-1;

                            // Looks like the target is in the RO data section.
                            if ((rc->originalHotCodeAddress <= (size_t)fixupLocation) &&
                                ((size_t)fixupLocation < rc->originalHotCodeAddress + rc->hotCodeSize))
                            {
                                // Fixup location is in the hot section
                                ro_section_fake_start = rc->originalHotCodeAddress + rc->hotCodeSize;
                                delta                 = (INT64)(ro_section_fake_start + ro_section_offset - baseAddr);
                                deltaIsFinal          = true;
                                LogDebug("    REL32 hot code target is in RO data section: setting delta=%d (0x%X)",
                                         delta, delta);
                            }
                            else if ((rc->originalColdCodeAddress <= (size_t)fixupLocation) &&
                                     ((size_t)fixupLocation < rc->originalColdCodeAddress + rc->coldCodeSize))
                            {
                                // Fixup location is in the cold section
                                ro_section_fake_start = rc->originalColdCodeAddress + rc->coldCodeSize;
                                delta                 = (INT64)(ro_section_fake_start + ro_section_offset - baseAddr);
                                deltaIsFinal          = true;
                                LogDebug("    REL32 cold code target is in RO data section: setting delta=%d (0x%X)",
                                         delta, delta);
                            }
                        }
                    }

                    if (delta != (INT64)(int)delta)
                    {
                        // This isn't going to fit in a signed 32-bit address. Use something that will fit,
                        // since we assume that original compilation fit fine.
                        // This is only an issue for 32-bit offsets on 64-bit targets.
                        target         = (DWORDLONG)originalAddr + (DWORDLONG)blocksize1;
                        INT64 newdelta = (INT64)(target - baseAddr);

                        LogDebug("    REL32 overflow. Mapping target to %016" PRIX64 ". Mapping delta: %016" PRIX64 " => %016" PRIX64,
                                 target, delta, newdelta);

                        delta = newdelta;
                    }
                }

                if (IsSpmiTarget32Bit())
                {
                    // Only 32 bits matters. And we don't care about sign. Note that SuperPMI will sometimes return
                    // arbitrary values, such as 0xCAFE0003 from MethodContext::repGetHelperFtn().
                    delta &= 0xFFFFFFFF;
                }
                else
                {
                    if (delta != (INT64)(int)delta)
                    {
                        LogError("REL32 relocation overflows field! delta=0x%016" PRIX64, delta);
                    }
                }

                if ((targetArch == SPMI_TARGET_ARCHITECTURE_AMD64) && !deltaIsFinal)
                {
                    // During an actual compile, recordRelocation() will be called before the compile
                    // is actually finished, and it will write the relative offset into the fixupLocation.
                    // Then, emitEndCodeGen() will patch forward jumps by subtracting any adjustment due
                    // to overestimation of instruction sizes. Because we're applying the relocs after the
                    // compile has finished, we need to reverse that: i.e. add in the (negative) adjustment
                    // that's now in the fixupLocation.
                    INT32 adjustment = *(INT32*)address;
                    delta += adjustment;
                }

                // Write 32-bits into location
                LogDebug("    fixupLoc-%016" PRIX64 " (@%p) : %08X => %08X", fixupLocation, address, *(DWORD*)address, delta);
                *(DWORD*)address = (DWORD)delta;
            }

            wasRelocHandled = true;
        }

        if (!wasRelocHandled)
        {
            LogError("Unknown reloc type %u", tmp.fRelocType);
        }
    }
}

void CompileResult::recProcessName(const char* name)
{
    if (ProcessName == nullptr)
        ProcessName = new DenseLightWeightMap<DWORD>();

    DWORD index = (DWORD)-1;
    if (name != nullptr)
        index = (DWORD)ProcessName->AddBuffer((unsigned char*)name, (DWORD)strlen(name) + 1);

    ProcessName->Append(index);
}
void CompileResult::dmpProcessName(DWORD key, DWORD value)
{
    const char* procName = (const char*)ProcessName->GetBuffer(value);
    printf("ProcessName key %u, value '%s'", key, procName);
    ProcessName->Unlock();
}
const char* CompileResult::repProcessName()
{
    if (ProcessName == nullptr)
        return "hackishProcessName";

    if (ProcessName->GetCount() > 0)
    {
        return (const char*)ProcessName->GetBuffer(ProcessName->Get((DWORD)0));
    }
    return nullptr;
}

void CompileResult::recAddressMap(void* originalAddress, void* replayAddress, unsigned int size)
{
    if (AddressMap == nullptr)
        AddressMap = new LightWeightMap<DWORDLONG, Agnostic_AddressMap>();

    Agnostic_AddressMap value;

    value.Address = CastPointer(originalAddress);
    value.size    = (DWORD)size;

    AddressMap->Add(CastPointer(replayAddress), value);
}
void CompileResult::dmpAddressMap(DWORDLONG key, const Agnostic_AddressMap& value)
{
    printf("AddressMap key %016" PRIX64 ", value addr-%016" PRIX64 ", size-%u", key, value.Address, value.size);
}
void* CompileResult::repAddressMap(void* replayAddress)
{
    if (AddressMap == nullptr)
        return nullptr;

    int index = AddressMap->GetIndex(CastPointer(replayAddress));

    if (index != -1)
    {
        Agnostic_AddressMap value;
        value = AddressMap->Get(CastPointer(replayAddress));
        return (void*)value.Address;
    }

    return nullptr;
}
void* CompileResult::searchAddressMap(void* newAddress)
{
    if (AddressMap == nullptr)
        return (void*)-1;
    for (unsigned int i = 0; i < AddressMap->GetCount(); i++)
    {
        DWORDLONG           replayAddress = AddressMap->GetRawKeys()[i];
        Agnostic_AddressMap value         = AddressMap->Get(replayAddress);
        if ((replayAddress <= CastPointer(newAddress)) && (CastPointer(newAddress) < (replayAddress + value.size)))
            return (void*)(value.Address + (CastPointer(newAddress) - replayAddress));
    }
    return (void*)-1;
}

void CompileResult::recReserveUnwindInfo(BOOL isFunclet, BOOL isColdCode, ULONG unwindSize)
{
    if (ReserveUnwindInfo == nullptr)
        ReserveUnwindInfo = new DenseLightWeightMap<Agnostic_ReserveUnwindInfo>();

    Agnostic_ReserveUnwindInfo value;

    value.isFunclet  = (DWORD)isFunclet;
    value.isColdCode = (DWORD)isColdCode;
    value.unwindSize = (DWORD)unwindSize;

    ReserveUnwindInfo->Append(value);
}
void CompileResult::dmpReserveUnwindInfo(DWORD key, const Agnostic_ReserveUnwindInfo& value)
{
    printf("ReserveUnwindInfo key %u, value isFun-%u isCold-%u usz-%u", key, value.isFunclet, value.isColdCode,
           value.unwindSize);
}

void CompileResult::recAllocUnwindInfo(BYTE*          pHotCode,
                                       BYTE*          pColdCode,
                                       ULONG          startOffset,
                                       ULONG          endOffset,
                                       ULONG          unwindSize,
                                       BYTE*          pUnwindBlock,
                                       CorJitFuncKind funcKind)
{
    if (AllocUnwindInfo == nullptr)
        AllocUnwindInfo = new DenseLightWeightMap<Agnostic_AllocUnwindInfo>();

    Agnostic_AllocUnwindInfo value;
    value.pHotCode           = CastPointer(pHotCode);
    value.pColdCode          = CastPointer(pColdCode);
    value.startOffset        = (DWORD)startOffset;
    value.endOffset          = (DWORD)endOffset;
    value.unwindSize         = (DWORD)unwindSize;
    value.pUnwindBlock_index = AllocUnwindInfo->AddBuffer((unsigned char*)pUnwindBlock, unwindSize);
    value.funcKind           = funcKind;

    AllocUnwindInfo->Append(value);
}
void CompileResult::dmpAllocUnwindInfo(DWORD key, const Agnostic_AllocUnwindInfo& value)
{
    printf("AllocUnwindInfo key %u, value pHot-%016" PRIX64 " pCold-%016" PRIX64 " startOff-%u endOff-%u unwindSz-%u blki-%u "
           "funcKind-%u",
           key, value.pHotCode, value.pColdCode, value.startOffset, value.endOffset, value.unwindSize,
           value.pUnwindBlock_index, value.funcKind);
}

void CompileResult::recRecordCallSite(ULONG instrOffset, CORINFO_SIG_INFO* callSig, CORINFO_METHOD_HANDLE methodHandle)
{
    repRecordCallSite(instrOffset, callSig, methodHandle);
}

void CompileResult::dmpRecordCallSiteWithSignature(DWORD key, const Agnostic_RecordCallSite& value) const
{
    printf("RecordCallSite key %u, callSig-%s ftn-%016" PRIX64,
           key,
           SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INFO(value.callSig, RecordCallSiteWithSignature, CrSigInstHandleMap).c_str(),
           value.methodHandle);
}

void CompileResult::dmpRecordCallSiteWithoutSignature(DWORD key, DWORDLONG methodHandle) const
{
    printf("RecordCallSite without call signature key %u, ftn-%016" PRIX64, key, methodHandle);
}

void CompileResult::repRecordCallSite(ULONG instrOffset, CORINFO_SIG_INFO* callSig, CORINFO_METHOD_HANDLE methodHandle)
{

    if (RecordCallSiteWithSignature == nullptr)
    {
        // The most call site records have only `methodHandle`, so creating two separate maps give us better performance
        // and smaller memory consumption. Note: we are not reading values from these maps during a normal replay.
        RecordCallSiteWithSignature = new LightWeightMap<DWORD, Agnostic_RecordCallSite>();
        if (recordCallSitesWithoutSig)
        {
            RecordCallSiteWithoutSignature = new LightWeightMap<DWORD, DWORDLONG>();
        }
    }

    if (callSig != nullptr)
    {
        Agnostic_RecordCallSite value;
        ZeroMemory(&value, sizeof(Agnostic_RecordCallSite));
        value.callSig      = SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INFO(*callSig, RecordCallSiteWithSignature, CrSigInstHandleMap);
        value.methodHandle = CastHandle(methodHandle);
        RecordCallSiteWithSignature->Add(instrOffset, value);
    }
    else if (recordCallSitesWithoutSig)
    {
        RecordCallSiteWithoutSignature->Add(instrOffset, CastHandle(methodHandle));
    }
}

bool CompileResult::fndRecordCallSiteSigInfo(ULONG instrOffset, CORINFO_SIG_INFO* pCallSig)
{
    if (RecordCallSiteWithSignature == nullptr)
        return false;

    if (RecordCallSiteWithSignature->GetIndex(instrOffset) == -1)
        return false;

    Agnostic_RecordCallSite value = RecordCallSiteWithSignature->Get(instrOffset);

    if (value.callSig.callConv == (DWORD)-1)
        return false;

    *pCallSig = SpmiRecordsHelper::Restore_CORINFO_SIG_INFO(value.callSig, RecordCallSiteWithSignature, CrSigInstHandleMap);

    return true;
}

bool CompileResult::fndRecordCallSiteMethodHandle(ULONG instrOffset, CORINFO_METHOD_HANDLE* pMethodHandle)
{

    if (RecordCallSiteWithSignature != nullptr && RecordCallSiteWithSignature->GetIndex(instrOffset) != -1)
    {
        Agnostic_RecordCallSite value = RecordCallSiteWithSignature->Get(instrOffset);
        *pMethodHandle = (CORINFO_METHOD_HANDLE)value.methodHandle;
    }
    else if (RecordCallSiteWithoutSignature != nullptr && RecordCallSiteWithoutSignature->GetIndex(instrOffset) != -1)
    {
        CORINFO_METHOD_HANDLE value = (CORINFO_METHOD_HANDLE)RecordCallSiteWithoutSignature->Get(instrOffset);
        *pMethodHandle = value;
    }
    return false;
}

void CompileResult::dmpCrSigInstHandleMap(DWORD key, DWORDLONG value)
{
    printf("CrSigInstHandleMap key %u, value %016" PRIX64, key, value);
}
