/*===================== begin_copyright_notice ==================================

Copyright (c) 2017 Intel Corporation

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


======================= end_copyright_notice ==================================*/

#ifndef DEBUG_INFO_H
#define DEBUG_INFO_H
#include <string>
#include "VISADefines.h"
#include "FlowGraph.h"
#include "Common_BinaryEncoding.h"
#include "RegAlloc.h"
#include "GraphColor.h"

// G4IR instructions added by JIT that do not result from lowering
// any CISA bytecode will be assigned CISA offset = 0xffffffff.
// This includes pseudo nodes, G4_labels, mov introduced for copying
// r0 for pre-emption support.
constexpr int UNMAPPABLE_VISA_INDEX = IR_Builder::OrphanVISAIndex;

#define CALLER_SAVE_START 1
#define CALLEE_SAVE_START 60
namespace vISA
{
class G4_Declare;
}
class CISA_IR_Builder;
class VISAKernelImpl;

struct VarnameMap;
namespace vISA
{
class DebugInfoState;
}
#if defined(_DEBUG) || defined(_INTERNAL)
#define DEBUG_RELEASE_INTERNAL_DLL_EXPORT_ONLY DLL_EXPORT
#else
#define DEBUG_RELEASE_INTERNAL_DLL_EXPORT_ONLY
#endif

DEBUG_RELEASE_INTERNAL_DLL_EXPORT_ONLY int decodeAndDumpDebugInfo(char* filename);
void emitDebugInfo(CISA_IR_Builder* builder, std::string filename);
void emitDebugInfo(VISAKernelImpl* curKernel, std::string filename);
void emitDebugInfo(VISAKernelImpl* kernel, std::list<VISAKernelImpl*>& functions, std::string filename);
void emitDebugInfoToMem(VISAKernelImpl* curKernel, void*& info, unsigned& size);
void emitDebugInfoToMem(VISAKernelImpl* kernel, std::list<VISAKernelImpl*>& functions, void*& info, unsigned& size);

void emitRegisterMapping(vISA::G4_Kernel& kernel, std::vector<VarnameMap*>& varsMap);
void generateCISAByteOffsetFromOffset(std::map<unsigned int, unsigned int>& mapCISAIndexCISAOffset,
                                      std::vector<std::pair<unsigned int, unsigned int>>& mapCISAIndexGenOffset,
                                      std::vector<std::pair<unsigned int, unsigned int>>& mapCISAOffsetGenOffset);
void generateByteOffsetMapping(vISA::G4_Kernel& kernel, std::vector<std::pair<unsigned int, unsigned int>>& mapping, std::list<vISA::G4_BB*>& stackCallEntryBBs);
void updateRelocOffset(VISAKernelImpl& kernel);
void resetGenOffsets(vISA::G4_Kernel& kernel);

void addCallFrameInfo(VISAKernelImpl* kernel);

// For ranges colored during graph coloring
void updateDebugInfo(vISA::G4_Kernel& kernel, vISA::G4_INST* inst,
    vISA::LivenessAnalysis& liveAnalysis, vISA::LiveRange* lrs[], BitSet& live,
    vISA::DebugInfoState* state, bool closeAllOpenIntervals);
// For ranges allocated by local RA
void updateDebugInfo(vISA::G4_Kernel& kernel,
    std::vector<vISA::LocalLiveRange*>& liveIntervals);
// For ranges updated by augmentation
void updateDebugInfo(vISA::G4_Kernel& kernel,
    std::vector<std::tuple<vISA::G4_Declare*, vISA::G4_INST*, vISA::G4_INST*>> augmentationLiveIntervals);
// For ranges assigned using unique assignments in local RA
void updateDebugInfo(vISA::G4_Kernel& kernel, vISA::G4_Declare* dcl,
    uint32_t start, uint32_t end);
void updateDebugInfo(vISA::G4_Kernel& kernel, vISA::G4_Declare* dcl, uint32_t offset);

void updateCallStackLiveIntervals(vISA::G4_Kernel& kernel);

#define DEBUG_MAGIC_NUMBER ((unsigned int)0xdeadd010)

// Format of debug info
struct VarnameMap
{
#define VARMAP_VREG_FILE_ADDRESS 0
#define VARMAP_VREG_FILE_FLAG 1
#define VARMAP_VREG_FILE_GRF 2

    // 0 - address, 1 - flag, 2 - GRF
    uint8_t virtualType;

#define VARMAP_PREG_FILE_ADDRESS 0
#define VARMAP_PREG_FILE_FLAG 1
#define VARMAP_PREG_FILE_GRF 2
#define VARMAP_PREG_FILE_MEMORY 3

    // Physical register type allocated by RA: 0 - address, 1 - flag, 2 - GRF, 3 - memory
    uint8_t physicalType;
    union Mapping
    {
        struct Register
        {
            uint16_t regNum;
            // For GRF, sub reg num is in byte granularity
            uint16_t subRegNum;
        }Register;
        struct Memory
        {
            // For globals, spill offset is absolute whereas for frame auto vars,
            // it is BE_FP relative.
            uint8_t isAbs : 1;
            int32_t memoryOffset : 31;
        }Memory;
    }Mapping;

    vISA::G4_Declare* dcl;
};

// Instance of this class is created and stored in G4_Kernel.
// Any debug info related members will go here and necessary
// getters/setters should be added here. One level of indirection
// will be required from G4_Kernel instance to get to this
// instance, but this means no space/perf penalty is imposed
// on G4_Kernel.
namespace vISA
{
class KernelDebugInfo
{
private:
    G4_Kernel* kernel;
    VISAKernelImpl* visaKernel;
    std::unordered_map<G4_Declare*, LiveIntervalInfo*> debugInfoLiveIntervalMap;

    // Instruction that saves caller BE_FP to callerbpfpdcl
    G4_INST* saveCallerFP;
    // Instruction that restores caller BE_FP prior to return
    G4_INST* restoreCallerFP;
    // Instruction that updates BE_FP for current frame
    G4_INST* setupFP;
    // Instruction that destroys BE_FP for current frame
    G4_INST* restoreSP;

    // Current frame size in bytes
    uint32_t frameSize;

    // Store declare used holding return value for stack call functions,
    // NULL for kernel.
    G4_Declare* fretVar;

    // Caller save/restore
    // std::vector<std::pair<fcall inst, std::pair<first caller save, last caller restore>>>
    // One entry per fcall inst in current compilation unit
    typedef std::pair<std::vector<G4_INST*>, std::vector<G4_INST*> > SaveRestore;
    std::map<G4_INST*, SaveRestore> callerSaveRestore;
    SaveRestore calleeSaveRestore;

    INST_LIST oldInsts;

    // Store pair of cisa byte offset and gen byte offset in vector
    std::vector<std::pair<unsigned int, unsigned int>> mapCISAOffsetGenOffset;
    // Store pair of cisa index and gen byte offset in vector
    std::vector<std::pair<unsigned int, unsigned int>> mapCISAIndexGenOffset;
    // Store varname map instance for each dcl
    std::vector<VarnameMap*> varsMap;
    // Store map between CISA bytecode index and CISA offset
    std::map<unsigned int, unsigned int> mapCISAOffset;
    // Store reloc_offset of gen binary. This is emitted out to debug info.
    uint32_t reloc_offset;

    // Store set of missing VISA ids as this helps consolidate live-intervals
    // to save compile time.
    std::set<unsigned int> missingVISAIds;
    bool missingVISAIdsComputed;

    std::vector<std::pair<unsigned int, unsigned int>> genISAOffsetToVISAIndex;

public:
    LiveIntervalInfo* getLiveIntervalInfo(G4_Declare* dcl, bool createIfNULL = true);
    void *operator new(size_t sz, Mem_Manager& m);
    G4_Kernel& getKernel() { return *kernel; }
    VISAKernelImpl* getVISAKernel() { return visaKernel; }
    void setVISAKernel(VISAKernelImpl* k);

    KernelDebugInfo();

    void reset()
    {
        mapCISAOffsetGenOffset.clear();
        mapCISAIndexGenOffset.clear();
        varsMap.clear();
        resetRelocOffset();
        missingVISAIds.clear();
        missingVISAIdsComputed = false;
    }

    G4_Declare* getBEFP()
    {
        G4_Declare* ret = nullptr;

        if (!kernel->fg.getIsStackCallFunc())
        {
            ret = kernel->fg.builder->getBEFP();
        }
        else if (saveCallerFP)
        {
            ret = GetTopDclFromRegRegion(saveCallerFP->getSrc(0));
        }

        return ret;
    }

    G4_Declare* getCallerBEFP()
    {
        G4_Declare* ret = nullptr;

        if (saveCallerFP)
        {
            ret = GetTopDclFromRegRegion(saveCallerFP->getDst());
        }

        return ret;
    }

    G4_INST* getCallerBEFPSaveInst() { return saveCallerFP; }
    void setCallerBEFPSaveInst(G4_INST* i) { saveCallerFP = i; }

    G4_INST* getCallerBEFPRestoreInst() { return restoreCallerFP; }
    void setCallerBEFPRestoreInst(G4_INST* i) { restoreCallerFP = i; }

    G4_INST* getBEFPSetupInst() { return setupFP; }
    void setBEFPSetupInst(G4_INST* i) { setupFP = i; }

    G4_INST* getCallerSPRestoreInst() { return restoreSP; }
    void setCallerSPRestoreInst(G4_INST* i) { restoreSP = i; }

    uint32_t getFrameSize() { return frameSize; }
    void setFrameSize(uint32_t sz) { frameSize = sz; }

    G4_Declare* getFretVar() { return fretVar; }
    void setFretVar(G4_Declare* dcl) { fretVar = dcl; }

    void addCallerSaveInst(G4_INST* fcall, G4_INST* inst);
    void addCallerRestoreInst(G4_INST* fcall, G4_INST* inst);
    void addCalleeSaveInst(G4_INST* inst);
    void addCalleeRestoreInst(G4_INST* inst);
    bool isFcallWithSaveRestore(G4_INST* inst);
    void setFCallInst(G4_INST* fcall);
    bool isFCallInst(G4_INST* inst);

    std::vector<G4_INST*>& getCallerSaveInsts(G4_INST* fcall);
    std::vector<G4_INST*>& getCallerRestoreInsts(G4_INST* fcall);
    std::vector<G4_INST*>& getCalleeSaveInsts();
    std::vector<G4_INST*>& getCalleeRestoreInsts();

    void setOldInstList(G4_BB* bb)
    {
        oldInsts.clear();
        for (auto instIt = bb->begin(); instIt != bb->end(); instIt++)
            oldInsts.push_back(*instIt);
    }
    void clearOldInstList() { oldInsts.clear(); }
    INST_LIST getDeltaInstructions(G4_BB* bb);

    void resetRelocOffset() { reloc_offset = 0; }
    void updateMapping(std::list<G4_BB*>& stackCallEntryBBs);

    void generateByteOffsetMapping(std::list<G4_BB*>& stackCallEntryBBs);
    void emitRegisterMapping();
    void generateCISAByteOffsetFromOffset();
    void updateRelocOffset();
    void updateCallStackLiveIntervals();
    void generateGenISAToVISAIndex();

    void computeDebugInfo(std::list<G4_BB*>& stackCallEntryBBs);

    uint32_t getRelocOffset() { return reloc_offset; }

    void mapCISAOffsetInsert(unsigned int a, unsigned int b)
    {
        mapCISAOffset.insert(std::make_pair(a, b));
    }

    std::vector<std::pair<unsigned int, unsigned int>>& getMapCISAOffsetGenOffset() { return mapCISAOffsetGenOffset; }
    std::vector<std::pair<unsigned int, unsigned int>>& getMapCISAIndexGenOffset() { return mapCISAIndexGenOffset; }
    std::vector<std::pair<unsigned int, unsigned int>>& getMapGenISAOffsetToCISAIndex() { return genISAOffsetToVISAIndex; }
    std::vector<VarnameMap*>& getVarsMap() { return varsMap; }

    uint32_t getVarIndex(G4_Declare* dcl);

    void computeMissingVISAIds();
    bool isMissingVISAId(unsigned int);
};

class SaveRestoreInfo
{
    G4_INST* i;

public:
    // Map src GRF->GRF/Memory
    union RegMap
    {
        uint32_t regNum;
        struct
        {
            int32_t offset : 31;
            uint32_t isAbs : 1;
        };
        int32_t memOff;
    };
    enum RegOrMem
    {
        Reg = 1,
        MemAbs = 2,
        MemOffBEFP = 3
    };
    std::map<uint32_t, std::pair<RegOrMem, RegMap>> saveRestoreMap;

    bool isEqual(SaveRestoreInfo& other)
    {
        auto otherMapIt = other.saveRestoreMap.begin();

        if (this->saveRestoreMap.size() != other.saveRestoreMap.size())
        {
            return false;
        }

        for (auto& thisMap : saveRestoreMap)
        {
            if (thisMap.first != (*otherMapIt).first ||
                thisMap.second.first != (*otherMapIt).second.first ||
                thisMap.second.second.memOff != (*otherMapIt).second.second.memOff)
            {
                return false;
            }

            otherMapIt++;
        }

        return true;
    }

    void update(G4_INST* inst, int32_t memOffset = 0xffff, uint32_t regWithMemOffset = 0xffff, bool absOffset = false);
    G4_INST* getInst() { return i; }
};

class SaveRestoreManager
{
    VISAKernelImpl* visaKernel;
    std::vector<SaveRestoreInfo> srInfo;
    int32_t memOffset;
    uint32_t regWithMemOffset;
    bool absOffset;

public:

    enum CallerOrCallee
    {
        Caller = 1,
        Callee = 2
    };

    void addInst(G4_INST* inst);

    SaveRestoreManager(VISAKernelImpl* k)
    {
        visaKernel = k;
        memOffset = 0xffff;
        regWithMemOffset = 0xffff;
        absOffset = false;
    }

    std::vector<SaveRestoreInfo>& getSRInfo(){ return srInfo; }

    void sieveInstructions(CallerOrCallee c);

    void emitAll();
};

class DbgDecoder
{
private:
    char* filename;
    std::FILE* dbgFile;

    void ddName();
    template<class T> void ddLiveInterval();
    void ddCalleeCallerSave(uint32_t relocOffset);

public:
    DbgDecoder(char* f)
    {
        filename = f;
        dbgFile = nullptr;
    }

    int ddDbg();
};

class DebugInfoState
{
    // Class used to store state during RA.
public:
    DebugInfoState(Mem_Manager& mem) : m(mem)
    {
        prevBitset = nullptr;
        prevInst = nullptr;
    }

    ~DebugInfoState()
    {
    }

    void setPrevBitset(BitSet& b)
    {
        if (!prevBitset)
        {
            prevBitset = (BitSet*) new (m)BitSet(b);
        }
        else
        {
            prevBitset->operator=(b);
        }
    }
    void setPrevInst(G4_INST* i)
    {
        if (i->getCISAOff() != UNMAPPABLE_VISA_INDEX)
        {
            prevInst = i;
        }
    }

    BitSet* getPrevBitset() { return prevBitset; }
    G4_INST* getPrevInst() { return prevInst; }

private:
    Mem_Manager& m;
    BitSet* prevBitset;
    G4_INST* prevInst;
};
}
/* Debug info format:
struct DebugFormatHeader
{
    uint32_t magic;
    uint16_t numCompiledObjects;
    DebugInfoFormat debugInfo[numCompiledObjectsObjects];
}

struct DebugInfoFormat
{
    uint8_t kernelNameLen;
    char kernelName[kernelNameLen];
    uint32_t reloc_offset; // 0 for kernel, non-zero for stack call functions

    struct CISAOffsetMap
    {
        uint32_t numElements;
        CISAMap data[numElements];
    }

    struct CISAIndexMap
    {
        uint32_t numElements;
        CISAMap data[numElements];
    }

    struct VarInfoMap
    {
        uint32_t numElements;

        struct VarInfo[numElements]
        {
            VarName name;
            VarLiveIntervalVISA lr;
        }
    }

    uint16_t numSubs
    SubroutineInfo subInfo[numSubs];

    CallFrameInfo frameInfo;
}

struct CISAMap
{
    uint32_t cisaOffset/cisaIndex;
    uint32_t genOffset;
}

struct SubroutineInfo
{
    VarName subName;
    uint32_t startVISAOffset;
    uint32_t endVISAOffset;
    VarLiveIntervalVISA retVal;
}

struct CallFrameInfo
{
    uint16_t frameSizeInBytes;
    uint8_t befpValid;
    VarLiveIntervalGenISA befp; // Validity depends on flag befpValid
    uint8_t callerbefpValid;
    VarLiveIntervalGenISA callerbefp; // Validity depends on flag callerbefpValid
    uint8_t retAddrValid;
    VarLiveIntervalGenISA retAddr; // Validity depends on flag retAddrValid
    uint16_t numCalleeSaveEntries;
    PhyRegSaveInfoPerIP calleeSaveEntry[numCalleeSaveEntries];
    // Need this because of following:
    //
    // V10 -> r2, r3, r4, r5, r6, r7
    //
    // send (16) null:w   r1 <-- Writes 4 GRFs (r1, r2, r3, r4)
    // r4 = r0
    // r4.2 =...
    // send (16) null:w   r4 <-- Writes 4 GRFs (r5, r6, r7, r8)
    //
    uint16_t numCallerSaveEntries;
    PhyRegSaveInfoPerIP callerSaveEntry[numCallerSaveEntries];
}

struct VarName
{
    uint8_t varNameLen;
    char varName[varNameLen];
}

struct VarAlloc
{
    uint8_t virtualType; // Virtual register type from CISA file: 0 - address, 1 - flag, 2 - GRF
    uint8_t physicalType; // Physical register type allocated by RA: 0 - address, 1 - flag, 2 - GRF, 3 - memory
    Mapping mapping;
}

union Mapping
{
    struct Register
    {
        uint16_t regNum;
        uint16_t subRegNum; // for GRF, in byte offset
    }
    struct Memory
    {
        uint32_t isBaseOffBEFP : 1; // MSB of 32-bit field denotes whether base if off BE_FP (0) or absolute (1)
        int32_t memoryOffset : 31; // memory offset
    }
}

struct VarLiveIntervalVISA
{
    uint16_t numIntervals;
    LiveIntervalVISA interval[numIntervals];
}

struct VarLiveIntervalGenISA
{
    uint16_t numIntervals;
    LiveIntervalGenISA interval[numIntervals];
}

struct LiveIntervalVISA
{
    uint16_t start;
    uint16_t end;
    VarAlloc alloc;
}

struct LiveIntervalGenISA
{
    uint32_t start;
    uint32_t end;
    VarAlloc alloc;
}

struct PhyRegSaveInfoPerIP
{
    uint32_t genIPOffset;
    uint16_t numEntries;
    RegInfoMapping data[numEntries];
}

struct RegInfoMapping
{
    RegInfo src;
    uint8_t dstInReg;
    Mapping dst;
}

struct RegInfo
{
    // GRF file r0.0 -> srcRegOff = 0, r1.0 -> srcRegOff = 32.
    // Addr and flag registers can be represented beyond GRF file size of 4k. But not currently required since they are not callee save.
    uint16_t srcRegOff;
    uint16_t numBytes;
}

*/
#endif
