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

Copyright (C) 2017-2021 Intel Corporation

SPDX-License-Identifier: MIT

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

#include <string.h>
#include <sstream>
#include "Common_ISA_util.h"
#include "Common_ISA_framework.h"
#include "G4_Opcode.h"
#include "PreDefinedVars.h"


vISAPreDefinedSurface vISAPreDefSurf[COMMON_ISA_NUM_PREDEFINED_SURF_VER_3_1] =
{
    { 0, PREDEF_SURF_0, "%slm" },
    { 1, PREDEF_SURF_1, "T1" },
    { 2, PREDEF_SURF_2, "T2" },
    { 3, PREDEF_SURF_3, "TSS" },
    { 4, PREDEF_SURF_252, "%bss" },
    { 5, PREDEF_SURF_255, "%scratch" },
};

typedef struct {
    VISA_Align  CISAAlign;
    const char* AlignName;
    uint16_t    AlignBytes;
} CISAAlignInfo;

static CISAAlignInfo CISAAlignTable[ALIGN_TOTAL_NUM] =
{
    { ALIGN_BYTE,    "byte",      1 },
    { ALIGN_WORD,    "word",      2 },
    { ALIGN_DWORD,   "dword",     4 },
    { ALIGN_QWORD,   "qword",     8 },
    { ALIGN_OWORD,   "oword",    16 },
    { ALIGN_GRF,     "GRF",       0 },  // dynamic
    { ALIGN_2_GRF,   "GRFx2",     0 },  // dynamic
    { ALIGN_HWORD,   "hword",    32 },
    { ALIGN_32WORD,  "wordx32",  64 },
    { ALIGN_64WORD,  "wordx64", 128 }
};

const char* Common_ISA_Get_Align_Name(VISA_Align align)
{
    return CISAAlignTable[align].AlignName;
}

uint32_t getAlignInBytes(VISA_Align A, unsigned grfSize)
{
    switch (A)
    {
    case ALIGN_GRF:   return grfSize;
    case ALIGN_2_GRF: return 2 * grfSize;
    default:
        break;
    }
    return CISAAlignTable[A].AlignBytes;
}

VISA_Align getCISAAlign(uint32_t AlignInBytes)
{
    if (AlignInBytes >= 128)
        return ALIGN_64WORD;
    if (AlignInBytes >= 64)
        return ALIGN_32WORD;
    if (AlignInBytes >= 32)
        return ALIGN_HWORD;
    if (AlignInBytes >= 16)
        return ALIGN_OWORD;
    if (AlignInBytes >= 8)
        return ALIGN_QWORD;
    if (AlignInBytes >= 4)
        return ALIGN_DWORD;
    if (AlignInBytes >= 2)
        return ALIGN_WORD;
    return ALIGN_BYTE;
}

GenPrecision_Info_t GenPrecisionTable[] = {
    /*  0 */ { GenPrecision::INVALID, 0, nullptr },
    /*  1 */ { GenPrecision::U1,      1, "u1"    },
    /*  2 */ { GenPrecision::S1,      1, "s1"    },
    /*  3 */ { GenPrecision::U2,      2, "u2"    },
    /*  4 */ { GenPrecision::S2,      2, "s2"    },
    /*  5 */ { GenPrecision::U4,      4, "u4"    },
    /*  6 */ { GenPrecision::S4,      4, "s4"    },
    /*  7 */ { GenPrecision::U8,      8, "u8"    },
    /*  8 */ { GenPrecision::S8,      8, "s8"    },
    /*  9 */ { GenPrecision::BF16,   16, "bf"    },
    /* 10 */ { GenPrecision::FP16,   16, "hf"    },
    /* 11 */ { GenPrecision::INVALID, 0, nullptr },  // unused
    /* 12 */ { GenPrecision::TF32,   32, "tf32"  },
};
static_assert((int)GenPrecision::INVALID == 0);
static_assert((int)GenPrecision::U1 == 1);
static_assert((int)GenPrecision::S1 == 2);
static_assert((int)GenPrecision::U2 == 3);
static_assert((int)GenPrecision::S2 == 4);
static_assert((int)GenPrecision::U4 == 5);
static_assert((int)GenPrecision::S4 == 6);
static_assert((int)GenPrecision::U8 == 7);
static_assert((int)GenPrecision::S8 == 8);
static_assert((int)GenPrecision::BF16 == 9);
static_assert((int)GenPrecision::FP16 == 10);
static_assert((int)GenPrecision::TF32 == 12);
static_assert((int)GenPrecision::TOTAL_NUM == 13);


const char* Common_ISA_Get_Modifier_Name(VISA_Modifier modifier)
{
    switch (modifier)
    {
        case MODIFIER_NONE:    return "";
        case MODIFIER_ABS:     return "(abs)";
        case MODIFIER_NEG:     return "(-)";
        case MODIFIER_NEG_ABS: return "(-abs)";
        case MODIFIER_SAT:     return "sat";
        case MODIFIER_NOT:     return "(~)";
        default:
            MUST_BE_TRUE(false, "Invalid modifier.");
            return "invalid_modifier";
    }
}

short Common_ISA_Get_Region_Value(Common_ISA_Region_Val val)
{
    switch (val) {
        case REGION_NULL:
            return -1;
        case REGION_0:
            return 0;
        case REGION_1:
            return 1;
        case REGION_2:
            return 2;
        case REGION_4:
            return 4;
        case REGION_8:
            return 8;
        case REGION_16:
            return 16;
        case REGION_32:
            return 32;
        default:
            MUST_BE_TRUE(false, " illegal region value: " << (int)val);
            return -1;
    }
}

G4_opcode GetGenOpcodeFromVISAOpcode(ISA_Opcode opcode)
{
    switch (opcode)
    {
        case ISA_RESERVED_0:
            return G4_illegal;
        case ISA_ADD:
            return G4_add;
        case ISA_AVG:
            return G4_avg;
        case ISA_DIV:
            return G4_math;
        case ISA_DP2:
            return G4_dp2;
        case ISA_DP3:
            return G4_dp3;
        case ISA_DP4:
            return G4_dp4;
        case ISA_DPH:
            return G4_dph;
        case ISA_DP4A:
            return G4_dp4a;
        case ISA_DPAS:
            return G4_dpas;
        case ISA_DPASW:
            return G4_dpasw;
        case ISA_ADD3:
        case ISA_ADD3O:
            return G4_add3;
        case ISA_BFN:
            return G4_bfn;
        case ISA_BF_CVT:
        case ISA_FCVT:
            return G4_fcvt;
        case ISA_SRND:
            return G4_srnd;
        case ISA_EXP:
            return G4_math;
        case ISA_FRC:
            return G4_frc;
        case ISA_LINE:
            return G4_line;
        case ISA_LOG:
            return G4_math;
        case ISA_LRP:
            return G4_lrp;
        case ISA_MAD:
            return G4_pseudo_mad;
        case ISA_MOD:
            return G4_math;
        case ISA_MUL:
            return G4_mul;
        case ISA_PLANE:
            return G4_pln;
        case ISA_POW:
            return G4_math;
        case ISA_RNDD:
            return G4_rndd;
        case ISA_RNDE:
            return G4_rnde;
        case ISA_RNDU:
            return G4_rndu;
        case ISA_RNDZ:
            return G4_rndz;
        case ISA_SAD2:
            return G4_sad2;
        case ISA_SAD2ADD:
            return G4_pseudo_sada2;
        case ISA_SIN:
            return G4_math;
        case ISA_COS:
            return G4_math;
        case ISA_SQRT:
            return G4_math;
        case ISA_RSQRT:
            return G4_math;
        case ISA_INV:
            return G4_math;
        case ISA_LZD:
            return G4_lzd;
        case ISA_AND:
            return G4_and;
        case ISA_OR:
            return G4_or;
        case ISA_XOR:
            return G4_xor;
        case ISA_NOT:
            return G4_not;
        case ISA_SHL:
            return G4_shl;
        case ISA_SHR:
            return G4_shr;
        case ISA_ASR:
            return G4_asr;
        case ISA_ROL:
            return G4_rol;
        case ISA_ROR:
            return G4_ror;
        case ISA_BFE:
            return G4_bfe;
        case ISA_BFI:
            return G4_bfi1;
        case ISA_BFREV:
            return G4_bfrev;
        case ISA_CBIT:
            return G4_cbit;
        case ISA_FBL:
            return G4_fbl;
        case ISA_FBH:
            return G4_fbh;
        case ISA_ADDR_ADD:
            return G4_add;
        case ISA_MOV:
            return G4_mov;
        case ISA_SEL:
        case ISA_FMINMAX:
            return G4_sel;
        case ISA_SETP:
            break;
        case ISA_CMP:
            return G4_cmp;
        case ISA_SUBROUTINE:
            break;
        case ISA_LABEL:
            return G4_label;
        case ISA_JMP:
            return G4_jmpi;
        case ISA_CALL:
            return G4_call;
        case ISA_RET:
            return G4_return;
        case ISA_MULH:
            return G4_mulh;
        case ISA_ADDC:
            return G4_addc;
        case ISA_SUBB:
            return G4_subb;
        case ISA_OWORD_LD:
        case ISA_OWORD_ST:
        case ISA_MEDIA_LD:
        case ISA_MEDIA_ST:
        case ISA_GATHER:
        case ISA_SCATTER:
        case ISA_OWORD_LD_UNALIGNED:
        case ISA_SAMPLE:
        case ISA_SAMPLE_UNORM:
        case ISA_FILE:
        case ISA_LOC:
        case ISA_DWORD_ATOMIC:
            break;
        case ISA_GOTO:
            return G4_goto;
        case ISA_MADW:
            return G4_madw;
            break;
        default:
            MUST_BE_TRUE(0, "Invalid opcode in common ISA.");
            break;
    }
    return G4_illegal;
}

G4_Type GetGenTypeFromVISAType(VISA_Type type)
{
    switch (type)
    {
    case ISA_TYPE_UD:
        return Type_UD;
    case ISA_TYPE_D:
        return Type_D;
    case ISA_TYPE_UW:
        return Type_UW;
    case ISA_TYPE_W:
        return Type_W;
    case ISA_TYPE_UB:
        return Type_UB;
    case ISA_TYPE_B:
        return Type_B;
    case ISA_TYPE_DF:
        return Type_DF;
    case ISA_TYPE_F:
        return Type_F;
    case ISA_TYPE_VF:
        return Type_VF;
    case ISA_TYPE_V:
        return Type_V;
    case ISA_TYPE_BOOL:
        return Type_BOOL;
    case ISA_TYPE_UV:
        return Type_UV;
    case ISA_TYPE_Q:
        return Type_Q;
    case ISA_TYPE_UQ:
        return Type_UQ;
    case ISA_TYPE_HF:
        return Type_HF;
    case ISA_TYPE_BF:
        return Type_BF;
    default:
        return Type_UNDEF;
    }
}

VISA_Type Get_Common_ISA_Type_From_G4_Type(G4_Type type)
{
    switch (type)
    {
    case Type_UD:
        return ISA_TYPE_UD;
    case Type_D:
        return ISA_TYPE_D;
    case Type_UW:
        return ISA_TYPE_UW;
    case Type_W:
        return ISA_TYPE_W;
    case Type_UB:
        return ISA_TYPE_UB;
    case Type_B:
        return ISA_TYPE_B;
    case Type_DF:
        return ISA_TYPE_DF;
    case Type_F:
        return ISA_TYPE_F;
    case Type_VF:
        return ISA_TYPE_VF;
    case Type_V:
        return ISA_TYPE_V;
    case Type_BOOL:
        return ISA_TYPE_BOOL;
    case Type_UV:
        return ISA_TYPE_UV;
    case Type_Q:
        return ISA_TYPE_Q;
    case Type_UQ:
        return ISA_TYPE_UQ;
    case Type_HF:
        return ISA_TYPE_HF;
    case Type_BF:
        return ISA_TYPE_BF;
    default:
        return ISA_TYPE_NUM;
    }
}

G4_SrcModifier GetGenSrcModFromVISAMod(VISA_Modifier mod)
{
    switch (mod)
    {
    case MODIFIER_NONE:
        return Mod_src_undef;
    case MODIFIER_ABS:
        return Mod_Abs;
    case MODIFIER_NEG:
        return Mod_Minus;
    case MODIFIER_NEG_ABS:
        return Mod_Minus_Abs;
    case MODIFIER_NOT:
        return Mod_Not;
    default:
        MUST_BE_TRUE(0, "Wrong src modifier");
        return Mod_src_undef;
    }
}

G4_CondModifier Get_G4_CondModifier_From_Common_ISA_CondModifier(VISA_Cond_Mod cmod)
{
    switch (cmod) {
        case ISA_CMP_E:
            return Mod_e;
        case ISA_CMP_NE:
            return Mod_ne;
        case ISA_CMP_G:
            return Mod_g;
        case ISA_CMP_GE:
            return Mod_ge;
        case ISA_CMP_L:
            return Mod_l;
        case ISA_CMP_LE:
            return Mod_le;
        case ISA_CMP_UNDEF:
            return Mod_cond_undef;
        default:
            MUST_BE_TRUE(0, "Invalid CISA Conditional Modifier.");
            return Mod_cond_undef;
    }
}

bool hasPredicate(ISA_Opcode op)
{
    switch (ISA_Inst_Table[op].type)
    {
    case ISA_Inst_Mov:
        return !(op == ISA_SETP || op == ISA_MOVS || op == ISA_FMINMAX || op == ISA_BF_CVT || op == ISA_FCVT);
    case ISA_Inst_Arith:
    case ISA_Inst_Logic:
    {
        if (op == ISA_SRND)
        {
            return false;
        }
        return true;
    }
    case ISA_Inst_Compare:
    case ISA_Inst_Address:
    case ISA_Inst_Data_Port:
    case ISA_Inst_Sampler:
    case ISA_Inst_Misc:
        return (op == ISA_DWORD_ATOMIC || op == ISA_GATHER_SCALED ||
                op == ISA_GATHER4_SCALED || op == ISA_GATHER4_TYPED ||
                op == ISA_SCATTER_SCALED || op == ISA_SCATTER4_SCALED ||
                op == ISA_SCATTER4_TYPED || op == ISA_RAW_SEND ||
                op == ISA_RAW_SENDS || op == ISA_3D_SAMPLE ||
                op == ISA_3D_LOAD || op == ISA_3D_GATHER4 ||
                op == ISA_3D_RT_WRITE || op == ISA_3D_URB_WRITE ||
                op == ISA_3D_TYPED_ATOMIC || op == ISA_QW_GATHER || op == ISA_QW_SCATTER

                );
    case ISA_Inst_Flow:
        return !(op == ISA_SUBROUTINE || op == ISA_LABEL || op == ISA_SWITCHJMP);
    case ISA_Inst_LSC:
        return true;
    case ISA_Inst_SIMD_Flow:
        return op == ISA_GOTO;
    case ISA_Inst_SVM:
        return true;
    default:
        return false;
    }
}

bool hasExecSize(ISA_Opcode op, uint8_t subOp)
{
    switch (ISA_Inst_Table[op].type)
    {
        case ISA_Inst_Mov:
        case ISA_Inst_Arith:
        case ISA_Inst_Logic:
        case ISA_Inst_Compare:
        case ISA_Inst_Address:
            return true;
        case ISA_Inst_Data_Port:
            if (op == ISA_MEDIA_LD || op == ISA_MEDIA_ST) {
                return false;
            } else
                return true;
        case ISA_Inst_SVM:
            if (subOp == SVM_BLOCK_LD || subOp == SVM_BLOCK_ST || subOp == 0) {
                return false;
            } else
                return true;
        case ISA_Inst_LSC:
            return true;
        case ISA_Inst_Sampler:
        case ISA_Inst_Misc:
            if (op == ISA_RAW_SEND || op == ISA_RAW_SENDS || op == ISA_3D_SAMPLE ||
                op == ISA_3D_LOAD || op == ISA_3D_GATHER4 || op == ISA_3D_URB_WRITE ||
                op == ISA_3D_INFO)
            {
                return true;
            }
            else if (op == ISA_DPAS || op == ISA_DPASW)
            {
                return true;
            }
            else
                return false;
        case ISA_Inst_Flow:
            if (op == ISA_SUBROUTINE || op == ISA_LABEL || op == ISA_FADDR)
            {
                return false;
            }
            else
                return true;
        case ISA_Inst_SIMD_Flow:
            return true;
        default:
            return false;
    }
}


bool hasLabelSrc(ISA_Opcode op)
{
    if (ISA_Inst_Table[op].type == ISA_Inst_Flow)
    {
        if (op == ISA_RET || op == ISA_FRET || op == ISA_IFCALL || op == ISA_FADDR)
            return false;
        else //(op == ISA_SUBROUTINE || op == ISA_LABEL || op == ISA_JMP || op == ISA_CALL || op == ISA_FCALL)
            return true;
    }
    else if (op == ISA_GOTO)
        return true;
    else
        return false;
}

unsigned Get_Common_ISA_SVM_Block_Num(VISA_SVM_Block_Num num)
{
    switch (num)
    {
        case SVM_BLOCK_NUM_1: return 1;
        case SVM_BLOCK_NUM_2: return 2;
        case SVM_BLOCK_NUM_4: return 4;
        case SVM_BLOCK_NUM_8: return 8;
        default: MUST_BE_TRUE(false, "Illegal SVM block number (should be 1, 2, 4, or 8).");
    }
    return 0;
}

VISA_SVM_Block_Num valueToVISASVMBlockNum(unsigned int value)
{
    switch (value)
    {
    case 1:
        return SVM_BLOCK_NUM_1;
    case 2:
        return SVM_BLOCK_NUM_2;
    case 4:
        return SVM_BLOCK_NUM_4;
    case 8:
        return SVM_BLOCK_NUM_8;
    default:
        MUST_BE_TRUE(false, "invalid SVM block number");
        return SVM_BLOCK_NUM_1;
    }
}

VISA_SVM_Block_Type valueToVISASVMBlockType(unsigned int value)
{
    switch (value)
    {
    case 1:
        return SVM_BLOCK_TYPE_BYTE;
    case 4:
        return SVM_BLOCK_TYPE_DWORD;
    case 8:
        return SVM_BLOCK_TYPE_QWORD;

    default:
        MUST_BE_TRUE(false, "invalid SVM block number");
        return SVM_BLOCK_TYPE_BYTE;
    }

}

unsigned Get_Common_ISA_SVM_Block_Size(VISA_SVM_Block_Type size)
{
    switch (size)
    {
        case SVM_BLOCK_TYPE_BYTE: return 1;
        case SVM_BLOCK_TYPE_DWORD: return 4;
        case SVM_BLOCK_TYPE_QWORD: return 8;
        default: MUST_BE_TRUE(false, "Illegal SVM block size (should be 1, 4, or 8).");
    }
    return 0;
}

unsigned Get_VISA_Oword_Num(VISA_Oword_Num num)
{
    switch (num) {
        case OWORD_NUM_1:
            return 1;
        case OWORD_NUM_2:
            return 2;
        case OWORD_NUM_4:
            return 4;
        case OWORD_NUM_8:
            return 8;
        case OWORD_NUM_16:
            return 16;
        default:
            MUST_BE_TRUE(false, "illegal Oword number (should be 0..3).");
            return 0;
    }
}

unsigned Get_VISA_Exec_Size(VISA_Exec_Size size)
{
    switch (size) {
        case EXEC_SIZE_1:
            return 1;
        case EXEC_SIZE_2:
            return 2;
        case EXEC_SIZE_4:
            return 4;
        case EXEC_SIZE_8:
            return 8;
        case EXEC_SIZE_16:
            return 16;
        case EXEC_SIZE_32:
            return 32;
        default:
            MUST_BE_TRUE(false, "illegal common ISA execsize (should be 0..5).");
            return 0;
    }
}

bool IsMathInst(ISA_Opcode op)
{
    switch (op) {
        case ISA_INV:
        case ISA_DIV:
        case ISA_MOD:
        case ISA_LOG:
        case ISA_EXP:
        case ISA_SQRT:
        case ISA_RSQRT:
        case ISA_SIN:
        case ISA_COS:
        case ISA_POW:
            return true;
        default:
            return false;
    }
}

bool IsIntType(VISA_Type type)
{
    switch (type) {
        case ISA_TYPE_UD:
        case ISA_TYPE_D:
        case ISA_TYPE_UW:
        case ISA_TYPE_W:
        case ISA_TYPE_UB:
        case ISA_TYPE_B:
        case ISA_TYPE_Q:
        case ISA_TYPE_UQ:
            return true;
        default:
            return false;
    }
}

bool IsIntOrIntVecType(VISA_Type type)
{
    return type == ISA_TYPE_V || type == ISA_TYPE_UV || IsIntType(type);
}

bool IsSingedIntType(VISA_Type type)
{
    switch (type) {
        case ISA_TYPE_D:
        case ISA_TYPE_W:
        case ISA_TYPE_B:
        case ISA_TYPE_Q:
            return true;
        default:
            return false;
    }
}

bool IsUnsignedIntType(VISA_Type type)
{
    switch (type) {
        case ISA_TYPE_UD:
        case ISA_TYPE_UW:
        case ISA_TYPE_UB:
        case ISA_TYPE_UQ:
            return true;
        default:
            return false;
    }
}

unsigned short Get_Common_ISA_Region_Value(Common_ISA_Region_Val val)
{
    switch (val) {
    case REGION_0:
        return 0;
    case REGION_1:
        return 1;
    case REGION_2:
        return 2;
    case REGION_4:
        return 4;
    case REGION_8:
        return 8;
    case REGION_16:
        return 16;
    case REGION_32:
        return 32;
    default:
        return UNDEFINED_SHORT;  //???
    }
}

Common_ISA_Region_Val Get_CISA_Region_Val(short val)
{
    if (val == (short)0x8000) {
        return REGION_NULL;
    }
    else {
        switch (val) {
        case 0: return REGION_0;
        case 1: return REGION_1;
        case 2: return REGION_2;
        case 4: return REGION_4;
        case 8: return REGION_8;
        case 16: return REGION_16;
        case 32: return REGION_32;
        case -1:
            return REGION_NULL;
        default:
            MUST_BE_TRUE(0, "Invalid Region value.");
            return REGION_NULL;
        }
    }
}

unsigned short Create_CISA_Region(
            unsigned short vstride,
            unsigned short width,
            unsigned short hstride)
{
    unsigned short region = 0;
    region |= (unsigned short)Get_CISA_Region_Val(vstride) & 0xF;
    region |= ((unsigned short)Get_CISA_Region_Val(width) & 0xF) << 4;
    region |= ((unsigned short)Get_CISA_Region_Val(hstride) & 0xF) << 8;
    return region;
}

unsigned Round_Down_Pow2(unsigned n)
{
    unsigned int i = 1;
    while (n >= i) i <<= 1;
    return (i>>1);
}
unsigned Round_Up_Pow2(unsigned n)
{
    unsigned int i = 1;
    if (n == 0)
        return 0;
    while (n > i) i <<= 1;
    return i;
}

G4_opcode Get_Pseudo_Opcode(ISA_Opcode op)
{
    switch (op) {
        case ISA_AND:
            return G4_pseudo_and;
        case ISA_OR:
            return G4_pseudo_or;
        case ISA_XOR:
            return G4_pseudo_xor;
        case ISA_NOT:
            return G4_pseudo_not;
        default:
            return G4_illegal;
    }
    return G4_illegal;
}

VISA_EMask_Ctrl Get_Next_EMask(VISA_EMask_Ctrl currEMask, G4_ExecSize execSize)
{
    switch (execSize) {
    default: // Next eMask is only valid for SIMD4, SIMD8, and SIMD16.
        break;
    case 16:
        switch (currEMask) {
        case vISA_EMASK_M1:     return vISA_EMASK_M5;
        case vISA_EMASK_M1_NM:  return vISA_EMASK_M5_NM;
        default: break;
        }
        break;
    case 8:
        switch (currEMask) {
        case vISA_EMASK_M1:     return vISA_EMASK_M3;
        case vISA_EMASK_M1_NM:  return vISA_EMASK_M3_NM;
        case vISA_EMASK_M3:     return vISA_EMASK_M5;
        case vISA_EMASK_M3_NM:  return vISA_EMASK_M5_NM;
        case vISA_EMASK_M5:     return vISA_EMASK_M7;
        case vISA_EMASK_M5_NM:  return vISA_EMASK_M7_NM;
        default: break;
        }
        break;
    case 4:
        switch (currEMask) {
        case vISA_EMASK_M1:     return vISA_EMASK_M2;
        case vISA_EMASK_M1_NM:  return vISA_EMASK_M2_NM;
        case vISA_EMASK_M2:     return vISA_EMASK_M3;
        case vISA_EMASK_M2_NM:  return vISA_EMASK_M3_NM;
        case vISA_EMASK_M3:     return vISA_EMASK_M4;
        case vISA_EMASK_M3_NM:  return vISA_EMASK_M4_NM;
        case vISA_EMASK_M4:     return vISA_EMASK_M5;
        case vISA_EMASK_M4_NM:  return vISA_EMASK_M5_NM;
        case vISA_EMASK_M5:     return vISA_EMASK_M6;
        case vISA_EMASK_M5_NM:  return vISA_EMASK_M6_NM;
        case vISA_EMASK_M6:     return vISA_EMASK_M7;
        case vISA_EMASK_M6_NM:  return vISA_EMASK_M7_NM;
        case vISA_EMASK_M7:     return vISA_EMASK_M8;
        case vISA_EMASK_M7_NM:  return vISA_EMASK_M8_NM;
        default: break;
        }
        break;
    }

    return vISA_NUM_EMASK;
}

G4_InstOpts Get_Gen4_Emask(VISA_EMask_Ctrl cisa_emask, G4_ExecSize exec_size)
{

    switch (exec_size.value)
    {
    case 32:
        switch (cisa_emask)
        {
        case vISA_EMASK_M1:
            return InstOpt_NoOpt;
        case vISA_EMASK_M5:
            return InstOpt_M16;
        case vISA_EMASK_M1_NM:
            return InstOpt_WriteEnable;
        case vISA_EMASK_M5_NM:
            return InstOpt_M16 | InstOpt_WriteEnable;
        default:
            ASSERT_USER(false, "Invalid emask for SIMD32 inst");
            return InstOpt_NoOpt;
        }
        break;
    case 16:
        {
            switch (cisa_emask)
            {
                case vISA_EMASK_M1:
                    return InstOpt_M0;
                case vISA_EMASK_M5:
                    return InstOpt_M16;
                case vISA_EMASK_M1_NM:
                    return InstOpt_M0 | InstOpt_WriteEnable;
                case vISA_EMASK_M5_NM:
                    return InstOpt_M16 | InstOpt_WriteEnable;
                default:
                    MUST_BE_TRUE(false, "Invalid emask for SIMD16 inst");
                    return InstOpt_NoOpt;
            }
        }
        break;
    case 8:
        {
            switch (cisa_emask)
            {
                case vISA_EMASK_M1:
                    return InstOpt_M0;
                case vISA_EMASK_M3:
                    return InstOpt_M8;
                case vISA_EMASK_M5:
                    return InstOpt_M16;
                case vISA_EMASK_M7:
                    return InstOpt_M24;
                case vISA_EMASK_M1_NM:
                    return InstOpt_M0 | InstOpt_WriteEnable;
                case vISA_EMASK_M3_NM:
                    return InstOpt_M8 | InstOpt_WriteEnable;
                case vISA_EMASK_M5_NM:
                    return InstOpt_M16 | InstOpt_WriteEnable;
                case vISA_EMASK_M7_NM:
                    return InstOpt_M24 | InstOpt_WriteEnable;
                default:
                    MUST_BE_TRUE(false, "Invalid emask for SIMD8 inst");
                    return InstOpt_NoOpt;
            }
        }
    default:
        // size 4, 2, 1
        {
            switch (cisa_emask)
            {
                case vISA_EMASK_M1:
                    return InstOpt_M0;
                case vISA_EMASK_M2:
                    return InstOpt_M4;
                case vISA_EMASK_M3:
                    return InstOpt_M8;
                case vISA_EMASK_M4:
                    return InstOpt_M12;
                case vISA_EMASK_M5:
                    return InstOpt_M16;
                case vISA_EMASK_M6:
                    return InstOpt_M20;
                case vISA_EMASK_M7:
                    return InstOpt_M24;
                case vISA_EMASK_M8:
                    return InstOpt_M28;
                case vISA_EMASK_M1_NM:
                    return InstOpt_M0 | InstOpt_WriteEnable;
                case vISA_EMASK_M2_NM:
                    return InstOpt_M4 | InstOpt_WriteEnable;
                case vISA_EMASK_M3_NM:
                    return InstOpt_M8 | InstOpt_WriteEnable;
                case vISA_EMASK_M4_NM:
                    return InstOpt_M12 | InstOpt_WriteEnable;
                case vISA_EMASK_M5_NM:
                    return InstOpt_M16 | InstOpt_WriteEnable;
                case vISA_EMASK_M6_NM:
                    return InstOpt_M20 | InstOpt_WriteEnable;
                case vISA_EMASK_M7_NM:
                    return InstOpt_M24 | InstOpt_WriteEnable;
                case vISA_EMASK_M8_NM:
                    return InstOpt_M28 | InstOpt_WriteEnable;
                default:
                    MUST_BE_TRUE(false, "Invalid emask for SIMD4 inst.");
                    return InstOpt_NoOpt;
            }
        }
    }
}

unsigned Get_Atomic_Op(VISAAtomicOps op) {

    switch (op) {
    default:
        ASSERT_USER(false, "CISA error: Invalid vISA atomic op for DWord atomic write.");
        break;
    case ATOMIC_ADD:        return GEN_ATOMIC_ADD;
    case ATOMIC_SUB:        return GEN_ATOMIC_SUB;
    case ATOMIC_INC:        return GEN_ATOMIC_INC;
    case ATOMIC_DEC:        return GEN_ATOMIC_DEC;
    case ATOMIC_MIN:        return GEN_ATOMIC_UMIN;
    case ATOMIC_MAX:        return GEN_ATOMIC_UMAX;
    case ATOMIC_XCHG:       return GEN_ATOMIC_MOV;
    case ATOMIC_CMPXCHG:    return GEN_ATOMIC_CMPWR;
    case ATOMIC_AND:        return GEN_ATOMIC_AND;
    case ATOMIC_OR:         return GEN_ATOMIC_OR;
    case ATOMIC_XOR:        return GEN_ATOMIC_XOR;
    case ATOMIC_IMIN:       return GEN_ATOMIC_IMIN;
    case ATOMIC_IMAX:       return GEN_ATOMIC_IMAX;
    case ATOMIC_PREDEC:     return GEN_ATOMIC_PREDEC;
    case ATOMIC_FMIN:       return GEN_ATOMIC_FMIN;
    case ATOMIC_FMAX:       return GEN_ATOMIC_FMAX;
    case ATOMIC_FCMPWR:     return GEN_ATOMIC_FCMPWR;
    case ATOMIC_FADD:       return GEN_ATOMIC_FADD;
    case ATOMIC_FSUB:       return GEN_ATOMIC_FSUB;
    }
    return ~0U;
}

uint16_t Get_VISA_Type_Size(VISA_Type type)
{
    switch (type)
    {
    case ISA_TYPE_UD:
    case ISA_TYPE_D:
    case ISA_TYPE_F:
    case ISA_TYPE_V:
    case ISA_TYPE_VF:
    case ISA_TYPE_UV:
        return 4;
    case ISA_TYPE_UW:
    case ISA_TYPE_W:
    case ISA_TYPE_HF:
        return 2;
    case ISA_TYPE_UB:
    case ISA_TYPE_B:
    case ISA_TYPE_BOOL:
        return 1;
    case ISA_TYPE_DF:
    case ISA_TYPE_Q:
    case ISA_TYPE_UQ:
        return 8;
    default:
        MUST_BE_TRUE(0, "Invalid data type: size unknown.");
        return 0;


    }
}

int attribute_info_t::getSizeInBinary() const
{
    return sizeof(nameIndex) + sizeof(size) + size;
}

int label_info_t::getSizeInBinary() const
{
    int size = sizeof(name_index) + sizeof(kind) + sizeof(attribute_count);

    for (int i = 0; i < attribute_count; i++)
    {
        size += attributes[i].getSizeInBinary();
    }
    return size;
}

int var_info_t::getSizeInBinary() const
{
    /*
        var_info {
    ud name_index;
    ub bit_properties;
    uw num_elements;
    ud alias_index;
    uw alias_offset;
    ub attribute_count;
    attribute_info[attribute_count];
    }
    */
    int size = sizeof(name_index) + sizeof(bit_properties) + sizeof(num_elements) +
        sizeof(alias_index) + sizeof(alias_offset) + sizeof(alias_scope_specifier) + sizeof(attribute_count);

    for (int i = 0; i < attribute_count; i++)
    {
        size += attributes[i].getSizeInBinary();
    }
    return size;
}

int addr_info_t::getSizeInBinary() const
{
    /*
    address_info {
    ud name_index;
    uw num_elements;
    ub attribute_count;
    attribute_info[attribute_count];
    }
    */
    int size = sizeof(name_index) + sizeof(num_elements) + sizeof(attribute_count);
    for (int i = 0; i < attribute_count; i++)
    {
        size += attributes[i].getSizeInBinary();
    }
    return size;
}

int pred_info_t::getSizeInBinary() const
{
    /*
    predicate_info {
    ud name_index;
    uw num_elements;
    ub attribute_count;
    attribute_info[attribute_count];
    }
    */
    int size = sizeof(name_index) + sizeof(num_elements) + sizeof(attribute_count);
    for (int i = 0; i < attribute_count; i++)
    {
        size += attributes[i].getSizeInBinary();
    }
    return size;
}

int input_info_t::getSizeInBinary() const
{
    /*
    input_info {
    b kind;
    ud id;
    w offset;
    uw size;
    }
    */
    return sizeof(kind) + sizeof(index) + sizeof(offset) + sizeof(size);
}

int vector_opnd::getSizeInBinary() const
{
    int size = 0;

    switch (tag & 0x7)
    {
    case OPERAND_GENERAL:
        {
            size = sizeof(opnd_val.gen_opnd.index) + sizeof(opnd_val.gen_opnd.col_offset) +
                sizeof(opnd_val.gen_opnd.row_offset) + sizeof(opnd_val.gen_opnd.region);
            break;
        }
    case OPERAND_ADDRESS:
        {
            size = sizeof(opnd_val.addr_opnd.index) + sizeof(opnd_val.addr_opnd.offset) +
                sizeof(opnd_val.addr_opnd.width);
            break;
        }
    case OPERAND_INDIRECT:
        {
            size = sizeof(opnd_val.indirect_opnd.index) + sizeof(opnd_val.indirect_opnd.addr_offset) +
                sizeof(opnd_val.indirect_opnd.indirect_offset) + sizeof(opnd_val.indirect_opnd.bit_property) +
                sizeof(opnd_val.indirect_opnd.region);
            break;
        }
    case OPERAND_PREDICATE:
        {
            size = sizeof(opnd_val.pred_opnd.index);
            break;
        }
    case OPERAND_IMMEDIATE:
        {
            switch (opnd_val.const_opnd.type) {
            default:
                size = sizeof(unsigned int);
                break;
            case ISA_TYPE_Q:
            case ISA_TYPE_UQ:
            case ISA_TYPE_DF:
                size = sizeof(unsigned long long);
                break;
            }

            size += sizeof(opnd_val.const_opnd.type);
            break;
        }
    case OPERAND_STATE:
        {
            size = sizeof(opnd_val.state_opnd.index) + sizeof(opnd_val.state_opnd.offset) +
                sizeof(opnd_val.state_opnd.opnd_class);
            break;
        }
    default:
        {
            MUST_BE_TRUE(0, "Invalid Vector Operand Class. Size cannot be determined.");
            break;
        }
    }

    size += sizeof(tag);

    return size;
}

/*
function_info {
    ub linkage; // MBZ
    uw name_len;
    ub name[name_len];
    ud offset;
    ud size;
    uw num_syms_variable; // MBZ
    uw num_syms_function; // MBZ
}
*/
uint32_t function_info_t::getSizeInBinary() const
{
    uint32_t size = sizeof(linkage) + sizeof(name_len) +
        name_len + sizeof(offset) + sizeof(this->size);

    size += sizeof(variable_reloc_symtab.num_syms);
    size += sizeof(function_reloc_symtab.num_syms);

    return size;
}

/*
    kernel_info {
    uw name_len;
    ub name[name_len];
    ud offset;
    ud size;
    ud input_offset;
    uw num_syms_variable; // MBZ
    uw num_syms_function; // MBZ
    ub num_gen_binaries;
    gen_binary_info gen_binaries[num_gen_binaries];
}
*/
uint32_t kernel_info_t::getSizeInBinary() const
{
    uint32_t size = sizeof(name_len) + name_len
        + sizeof(offset) + sizeof(this->size)
        + sizeof(input_offset);

    size += sizeof(variable_reloc_symtab.num_syms);
    size += sizeof(function_reloc_symtab.num_syms);

    size += sizeof(num_gen_binaries);

    for (int i = 0; i < num_gen_binaries; i++)
    {
        size += sizeof(gen_binaries->platform);
        size += sizeof(gen_binaries->binary_offset);
        size += sizeof(gen_binaries->binary_size);
    }

    return size;
}

uint32_t common_isa_header::getSizeInBinary() const
{
    uint32_t size = sizeof(magic_number) + sizeof(major_version)
        + sizeof(minor_version) + sizeof(num_kernels);

    for (int i = 0; i < num_kernels; i++)
    {
        size += kernels[i].getSizeInBinary();
    }
    /*
    common_isa_header {
    ud magic_number;
    ub major_version;
    ub minor_version;
    uw num_kernels;
    kernel_info kernels[num_kernels];
    uw num_variables;
    file_scope_var_info variables[num_variables];
    uw num_functions;
    function_info functions[num_functions];
    }

    */

    // file-scope variables are no longer supported
    size += sizeof(uint16_t);

    size += sizeof(num_functions);

    for (int i = 0; i < num_functions; i++)
    {
        size += functions[i].getSizeInBinary();
    }

    return size;
}

VISA_Cond_Mod Get_Common_ISA_CondModifier_From_G4_CondModifier(G4_CondModifier  cmod)
{
    switch (cmod) {
        //case ISA_CMP_NONE:
        //    return Mod_z;
        case Mod_e:
            return ISA_CMP_E;
        case Mod_ne:
            return ISA_CMP_NE;
        case Mod_g:
            return ISA_CMP_G;
        case Mod_ge:
            return ISA_CMP_GE;
        case Mod_l:
            return ISA_CMP_L;
        case Mod_le:
            return ISA_CMP_LE;
        //case ISA_CMP_R:
        //    return Mod_r;
        //case ISA_CMP_O:
        //    return Mod_o;
        //case ISA_CMP_U:
        //    return Mod_u;
        case Mod_cond_undef:
            return ISA_CMP_UNDEF;
        default:
            MUST_BE_TRUE(0, "Invalid G4 Conditional Modifier.");
            return ISA_CMP_UNDEF;
    }
}

VISA_Exec_Size Get_VISA_Exec_Size_From_Raw_Size(unsigned int size)
{
    switch (size) {
        case 1:
            return EXEC_SIZE_1;
        case 2:
            return EXEC_SIZE_2;
        case 4:
            return EXEC_SIZE_4;
        case 8:
            return EXEC_SIZE_8;
        case 16:
            return EXEC_SIZE_16;
        case 32:
            return EXEC_SIZE_32;
        default:
            MUST_BE_TRUE(false, "illegal common ISA execsize (should be 1, 2, 4, 8, 16, 32).");
            return EXEC_SIZE_ILLEGAL;
    }
}

int state_info_t::getSizeInBinary() const
{
    int size = sizeof(name_index) + sizeof(num_elements) + sizeof(attribute_count);

    for (int i = 0; i < attribute_count; i++)
    {
        size += attributes[i].getSizeInBinary();
    }
    return size;
}

VISA_Oword_Num Get_VISA_Oword_Num_From_Number(unsigned num)
{
    switch (num) {
        case 1:
            return OWORD_NUM_1;
        case 2:
            return OWORD_NUM_2;
        case 4:
            return OWORD_NUM_4;
        case 8:
            return OWORD_NUM_8;
        case 16:
            return OWORD_NUM_16;
        default:
            MUST_BE_TRUE(false, "illegal Oword number.");
            return OWORD_NUM_ILLEGAL;
    }
}

VISA_Modifier Get_Common_ISA_SrcMod_From_G4_Mod(G4_SrcModifier mod)
{
    switch (mod)
    {
    case Mod_src_undef:
        return MODIFIER_NONE ;
    case Mod_Abs:
        return MODIFIER_ABS ;
    case Mod_Minus:
        return MODIFIER_NEG ;
    case Mod_Minus_Abs:
        return MODIFIER_NEG_ABS ;
    case Mod_Not:
        return MODIFIER_NOT;
    default:
        MUST_BE_TRUE(0, "Wrong src modifier");
        return MODIFIER_NONE;
    }
}

VISA_Type getRawOperandType(const print_format_provider_t* header, const raw_opnd& opnd)
{
    unsigned numPreDefinedVars = Get_CISA_PreDefined_Var_Count();
    if (opnd.index < numPreDefinedVars)
    {
        // One of the pre-defined variables
        return getPredefinedVarType(mapExternalToInternalPreDefVar(opnd.index));
    }

    uint32_t opnd_index = opnd.index - numPreDefinedVars;
    const var_info_t* currVar = header->getVar(opnd_index);
    return currVar->getType();
}

VISA_Type getVectorOperandType(const print_format_provider_t* header, const vector_opnd& opnd)
{
    unsigned numPreDefinedVars = Get_CISA_PreDefined_Var_Count();
    switch (opnd.getOperandClass())
    {
        case OPERAND_GENERAL:
            if (opnd.opnd_val.gen_opnd.index < numPreDefinedVars)
            {
                // One of the pre-defined variables
                return getPredefinedVarType(mapExternalToInternalPreDefVar(opnd.getOperandIndex()));
            }
            else
            {
                const var_info_t& var = *header->getVar(opnd.getOperandIndex()-numPreDefinedVars);
                return var.getType();
            }
        case OPERAND_ADDRESS:
            return ISA_TYPE_UW;
        case OPERAND_PREDICATE:
            return ISA_TYPE_BOOL;
        case OPERAND_INDIRECT:
            return (VISA_Type)(opnd.opnd_val.indirect_opnd.bit_property & 0xF);
        case OPERAND_ADDRESSOF:
            return ISA_TYPE_UW;
        case OPERAND_IMMEDIATE:
            return (VISA_Type)(opnd.opnd_val.const_opnd.type & 0xF);
        case OPERAND_STATE:
            return ISA_TYPE_UD;
        default:
            return ISA_TYPE_UD;
    }
}

const raw_opnd& getRawOperand(const CISA_INST* inst, unsigned i)
{
    MUST_BE_TRUE(inst, "Argument Exception: argument inst is NULL.");
    MUST_BE_TRUE(inst->opnd_count > i, "No such operand, i, for instruction inst.");
    return inst->opnd_array[i]->_opnd.r_opnd;
}

const vector_opnd& getVectorOperand(const CISA_INST* inst, unsigned i)
{
    MUST_BE_TRUE(inst, "Argument Exception: argument inst is NULL.");
    MUST_BE_TRUE(inst->opnd_count > i, "No such operand, i, for instruction inst.");
    return inst->opnd_array[i]->_opnd.v_opnd;
}

CISA_opnd_type getOperandType(const CISA_INST* inst, unsigned i)
{
    MUST_BE_TRUE(inst, "Argument Exception: argument inst is NULL.");
    MUST_BE_TRUE(inst->opnd_count > i, "No such operand, i, for instruction inst.");
    return inst->opnd_array[i]->opnd_type;
}

int64_t typecastVals(const void* value, VISA_Type isaType)
{
    int64_t retVal = 0;
    switch (isaType)
    {
    case ISA_TYPE_UD:
    case ISA_TYPE_UV:
    case ISA_TYPE_VF:
    {
        retVal = (int64_t)(*((unsigned int*)value));
        break;
    }
    case ISA_TYPE_D:
    case ISA_TYPE_V:
    {
        retVal = (int64_t)(*((int*)value));
        break;
    }
    case ISA_TYPE_UW:
    {
        retVal = (int64_t)(*((uint16_t*)value));
        break;
    }
    case ISA_TYPE_W:
    {
        retVal = (int64_t)(*((int16_t*)value));
        break;
    }
    case ISA_TYPE_UB:
    {
        retVal = (int64_t)(*((uint8_t*)value));
        break;
    }
    case ISA_TYPE_B:
    {
        retVal = (int64_t)(*((int8_t*)value));
        break;
    }
    case ISA_TYPE_HF:
    {
        // clear higher bits
        retVal = (int64_t)(*((uint16_t*)value));
        break;
    }

    default:
    {
        assert(0);
        return -1;
    }
    }
    return retVal;
}

// convert binary vISA surface id to GEN surface index
int Get_PreDefined_Surf_Index(int index, TARGET_PLATFORM platform)
{
    if (platform < GENX_SKL)
    {
        switch (index)
        {
        case 1:
            return PREDEF_SURF_1_OLD;
        case 2:
            return PREDEF_SURF_2_OLD;
        case 3:
            return PREDEF_SURF_3_OLD;
        default:
            ;
            // fallthrough
        }
    }

    return vISAPreDefSurf[index].genId;

}

const char* createStringCopy(const char* name, vISA::Mem_Manager &m_mem)
{
    if (*name == '\0') {
        return "";
    }

    // TODO: look into relaxing this
    static const size_t MAX_VISA_BINARY_STRING_LENGTH = 256;

    size_t copyLen = strlen(name);
    if (copyLen >= MAX_VISA_BINARY_STRING_LENGTH)
    {
        copyLen = MAX_VISA_BINARY_STRING_LENGTH - 1;
    }
    char* copy = (char*)m_mem.alloc(copyLen + 1);
    strncpy_s(copy, copyLen + 1, name, copyLen);
    return copy;
}

std::string sanitizeLabelString(std::string str)
{
    auto isReservedChar = [](char c)
    {
        return !isalnum(c) && c != '_' && c != '$';
    };
    std::replace_if(str.begin(), str.end(), isReservedChar, '_');
    return str;
}

// This function scrubs out illegal file path characters
//
// NOTE: we must permit directory separators though since the string is a path
std::string sanitizePathString(std::string str)
{
#ifdef _WIN32
    // better cross platform behavior ./foo/bar.asm => to backslashes
    auto isFwdSlash = [](char c) {return c == '/';};
    std::replace_if(str.begin(), str.end(), isFwdSlash, '\\');
#endif

    auto isReservedChar = [](char c)
    {
#ifdef _WIN32
        // c.f. https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
        switch (c)
        {
        // we need these because we have a full path
        // case '\\': path separator
        case ':': // can be a drive suffix, but we handle this manually
        case '"':
        case '*':
        case '|':
        case '?':
        case '<':
        case '>':
            return true;
        default: return !isprint(c) && !isspace(c);
        }
        return false;
#else
        return c == ':' || (!isprint(c) && !isspace(c));
#endif
    };

#ifdef _WIN32
    if (str.length() > 2 && isalnum(str[0]) && str[1] == ':') {
        // drive prefix: D:... or D:
        std::replace_if(str.begin()+2, str.end(), isReservedChar, '_');
    } else {
        std::replace_if(str.begin(), str.end(), isReservedChar, '_');
    }
#else
    std::replace_if(str.begin(), str.end(), isReservedChar, '_');
#endif
    return str;
}

const char* toString(GenPrecision P)
{
    int ix = (int)P;
    if (ix > (int)GenPrecision::INVALID && ix < (int)GenPrecision::TOTAL_NUM)
    {
        return GenPrecisionTable[ix].Name;
    }
    return "?";
}

