/*========================== begin_copyright_notice ============================
Copyright (C) 2021-2025 Intel Corporation

SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/

/*!
* @file Implementation of the decoder of SEND messages
*/

#include <iostream>
#include <sstream>

#include "gen_send_decoder.h"

/* ===================================================================================================== */
// Data types
/* ===================================================================================================== */
/// Data Port 0 Message Types (MT_DP0)
typedef enum
{
    MT0R_OWB     = 0x00, ///< Block Read message                     Message descriptor: MSD0R_OWB
    MT0R_OWAB    = 0x01, ///< Aligned Block Read message             Message descriptor: MSD0R_OWAB
    MT0R_OWDB    = 0x02, ///< Oword Dual Block Read message          Message descriptor: MSD0R_OWDB
    MT0R_DWS     = 0x03, ///< Dword Scattered Read message           Message descriptor: MSD0R_DWS
    MT0R_BS      = 0x04, ///< Byte Scattered Read message            Message descriptor: MSD0R_BS
    MT0_MEMFENCE = 0x07, ///< Memory Fence message                   Message descriptor: MSD_MEMFENCE
    MT0W_OWB     = 0x08, ///< Block Write message                    Message descriptor: MSD0W_OWB
    MT0W_OWAB    = 0x09, ///< Aligned Block Write message            Message descriptor: MSD0W_OWAB
    MT0W_OWDB    = 0x0A, ///< Oword Dual Block Write message         Message descriptor: MSD0W_OWDB
    MT0W_DWS     = 0x0B, ///< Dword Scattered Write message          Message descriptor: MSD0W_DWS
    MT0W_BS      = 0x0C, ///< Byte Scattered Write message           Message descriptor: MSD0W_BS
} MT_DP0_TYPE;


/// Data Port 1 Message Types (MT_DP1)
typedef enum
{
    MT1R_T              = 0x00, ///< Transpose Read message                                        Message descriptor: MSD1R_TT
    MT1R_US             = 0x01, ///< Untyped Surface Read message                                  Message descriptor: MSD1R_US (BTI)
    MT1A_UI             = 0x02, ///< Untyped Atomic Integer Operation message                      Message descriptor: MSD1R_DWAI1, MSD1R_DWAI2, MSD1R_DWAI3, MSD1W_DWAI1, MSD1W_DWAI2, MSD1W_DWAI3
    MT1A_UHI            = 0x03, ///< Untyped Atomic Half Integer Operation message
    MT1R_MB             = 0x04, ///< Media Block Read message                                      Message descriptor: MSD1R_MB
    MT1R_TS             = 0x05, ///< Typed Surface Read message                                    Message descriptor: MSD1R_TS (BTI)
    MT1A_TA             = 0x06, ///< Typed Atomic Integer Operation message                        Message descriptor: MSD1R_DWTAI1, MSD1R_DWTAI2, MSD1R_DWTAI3, MSD1W_DWTAI1, MSD1W_DWTAI2, MSD1W_DWTAI3
    MT1A_TAH            = 0x07, ///< Typed Atomic Half Integer Operation message
    MT1RS_US            = 0x08, ///< Reserved Ignored  BDW+
    MT1W_US             = 0x09, ///< Untyped Surface Write mesage                                  Message descriptor: MSD1W_US (BTI)
    MT1W_MB             = 0x0A, ///< Media Block Write message                                     Message descriptor: MSD1W_MB
    MT1A_TC             = 0x0B, ///< Typed Atomic Counter Operation message                        Message descriptor: MSD1R_DWAC1, MSD1R_DWAC2, MSD1W_DWAC1, MSD1W_DWAC2
    MT1A_TCH            = 0x0C, ///< Typed Atomic Half Counter Operation message
    MT1W_TS             = 0x0D, ///< Typed Surface Write message                                   Message descriptor: MSD1W_TS (BTI)
    MT1RS_TS            = 0x0E, ///< Reserved Ignored  BDW+
    MT1R_A64_SB         = 0x10, ///< A64 Scattered Read message                                    Message descriptor: MSD1R_A64_BS, MSD1R_A64_DWS, MSD1R_A64_QWS (stateless)
    MT1R_A64_US         = 0x11, ///< A64 Untyped Surface Read message                              Message descriptor: MSD1R_A64_US
    MT1A_A64_UI         = 0x12, ///< A64 Untyped Atomic Integer Operation message                  Message descriptor: MSD1R_A64_DWAI1, MSD1R_A64_DWAI2, MSD1R_A64_DWAI3, MSD1W_A64_DWAI1, MSD1W_A64_DWAI2, MSD1W_A64_DWAI3,
                                ///<                                                                                   MSD1R_A64_QWAI1, MSD1R_A64_QWAI2, MSD1R_A64_QWAI3, MSD1W_A64_QWAI1, MSD1W_A64_QWAI2, MSD1W_A64_QWAI3
    MT1A_A64_UHI        = 0x13, ///< A64 Untyped Atomic Half Integer Operation message
    MT1R_A64_B          = 0x14, ///< A64 Block Read message                                        Message descriptor: MSD1R_A64_OWB, MSD1R_A64_OWUB, MSD1R_A64_OWDB, MSD1R_A64_HWB
    MT1W_A64_B          = 0x15, ///< A64 Block Write message                                       Message descriptor: MSD1W_A64_OWB, MSD1W_A64_OWUB, MSD1W_A64_OWDB, MSD1W_A64_HWB
    MT1RS_A64_US        = 0x18, ///< A64 Reserved Ignored  BDW+
    MT1W_A64_US         = 0x19, ///< A64 Untyped Surface Write message                             Message descriptor: MSD1W_A64_US
    MT1W_A64_SB         = 0x1A, ///< A64 Scattered Write message                                   Message descriptor: MSD1W_A64_BS, MSD1W_A64_DWS, MSD1W_A64_QWS (stateless)
    MT1A_UF             = 0x1B, ///< Untyped Atomic Float Operation message                        Message descriptor: MSD1R_DWAF2, MSD1R_DWAF3, MSD1W_DWAF2, MSD1W_DWAF3
    MT1A_UHF            = 0x1C, ///< Untyped Atomic Half Float Operation message
    MT1A_A64_UF         = 0x1D, ///< A64 Untyped Atomic Float Operation message                    Message descriptor: MSD1R_A64_DWAF2, MSD1R_A64_DWAF3, MSD1W_A64_DWAF2, MSD1W_A64_DWAF3
    MT1A_A64_UHF        = 0x1E, ///< A64 Untyped Atomic Half Float Operation message
} MT_DP1_TYPE;

/// GEN Extended Message Descriptor structure
struct ExtMSD
{
    ExtMSD(uint32_t raw) { *(reinterpret_cast<uint32_t*>(this)) = raw; }
    operator uint32_t() const { return  *(reinterpret_cast<const uint32_t*>(this)); }

    uint32_t _sfid  : 4; ///< Shared Function ID
    uint32_t _res1  : 1; ///< reserved MBZ
    uint32_t _eot   : 1; ///< End-of-thread
    uint32_t _len   : 4; ///< extended message length - number of 256-bit GRF registers starting from Src1 to be sent out on the request message payload (0-15)
    uint32_t _res2  : 2; ///< reserved MBZ
    uint32_t _rtai  : 3; ///< render target array index
    uint32_t _alpha : 1; ///< setting this bit indicates that Src0 Alpha is present in the Render Target Write Message.
    uint32_t _pixsh : 4; ///< Pixel shading phase for CPS+PS inner loop (CNL+)
    uint32_t _nrt   : 1; ///< Null Render Target
    uint32_t _smpid : 4; ///< Sample ID
    uint32_t _res3  : 7; ///< reserved MBZ
};
static_assert(sizeof(ExtMSD) == sizeof(uint32_t), "ExtMSD: unexpected size");

/// Generic Data Port 0 Message Descriptor
struct MSD0
{
    MSD0(uint32_t raw) { *(reinterpret_cast<uint32_t*>(this)) = raw; }
    operator uint32_t() const { return  *(reinterpret_cast<const uint32_t*>(this)); }

    uint32_t _bti      : 8; ///< bti
    uint32_t _specific : 6; ///<
    uint32_t _type     : 4; ///< message type
    uint32_t _scratch  : 1; ///< 0 = legacy message, 1 = scratch block
    uint32_t _header   : 1; ///< header present
    uint32_t _rLength  : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLength  : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _reserved : 3; ///< MBZ
};
static_assert(sizeof(MSD0) == sizeof(uint32_t), "MSD0: unexpected size");

/// Generic Data Port 1 Message Descriptor
struct MSD1
{
    MSD1(uint32_t raw) { *(reinterpret_cast<uint32_t*>(this)) = raw; }
    operator uint32_t() const { return  *(reinterpret_cast<const uint32_t*>(this)); }

    uint32_t _bti    : 8; ///< Binding table index
    uint32_t _cmask  : 4; ///< Channel mask
    uint32_t _slotg  : 2; ///< Slot group
    uint32_t _type   : 5; ///< message type
    uint32_t _header : 1; ///< header present
    uint32_t _rLen   : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen   : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res    : 3; ///< reserved MBZ
};
static_assert(sizeof(MSD1) == sizeof(uint32_t), "MSD1: unexpected size");

/// Specific Message Descriptors
struct MSD0RW_SB
{
    MSD0RW_SB(uint32_t raw) { *(reinterpret_cast<uint32_t*>(this)) = raw; }
    operator uint32_t() const { return  *(reinterpret_cast<const uint32_t*>(this)); }

    uint32_t _addressOffset : 12; ///< GeneralStateOffset[16:5]
    uint32_t _dataElem      : 2;  ///< Specifies the number of registers to be read or written
    uint32_t _res1          : 1;  ///< MBZ
    uint32_t _iar           : 1;  ///< invalidate after read, for write MBZ
    uint32_t _channelMode   : 1;  ///< Pre-CNL: Specifies whether the read or write operation occurs on all 4 Dwords if any of those channel enables are set,
                                  ///< or else only on the dwords whose corresponding channel enable is set.
                                  ///< CNL+: reserved MBZ
    uint32_t _opType        : 1;  ///< operation type: 0 - read, 1 - write
    uint32_t _type          : 1;  ///< 1 = Scratch Block Message
    uint32_t _header        : 1;  ///< header present
    uint32_t _rLen          : 5;  ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen          : 4;  ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res3          : 3;  ///< MBZ
};
static_assert(sizeof(MSD0RW_SB) == sizeof(uint32_t), "MSD0RW_SB: unexpected size");

typedef struct
{
    uint32_t _bti       : 8; ///< binding table index        MDC_STATELESS
    uint32_t _operation : 4; ///< Atomic Integer Operation   MDC_AOP1
    uint32_t _dataWidth : 1; ///< Data Width - 0 - 32-bit, 1 - 64-bit
    uint32_t _retCtrl   : 1; ///< Return data control
    uint32_t _type      : 5; ///< messaage type
    uint32_t _header    : 1; ///< header present MDC_MHF
    uint32_t _rLen      : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen      : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res3      : 3; ///< MBZ
} MSD1R_A64_DWAI1, MSD1R_A64_DWAI2, MSD1R_A64_DWAI3, MSD1W_A64_DWAI1, MSD1W_A64_DWAI2, MSD1W_A64_DWAI3,
MSD1R_A64_QWAI1, MSD1R_A64_QWAI2, MSD1R_A64_QWAI3, MSD1W_A64_QWAI1, MSD1W_A64_QWAI2, MSD1W_A64_QWAI3;

typedef struct
{
    uint32_t _bti      : 8; ///< binding table index
    uint32_t _dataElem : 3; ///< Specifies the number of registers to be read or written          MDC_DB_OW
    uint32_t _res1     : 2; ///< MBZ
    uint32_t _iar      : 1; ///< invalidate after read - for write operation MBZ
    uint32_t _type     : 4; ///< messaage type
    uint32_t _legacy   : 1; ///< Legacy message  MBZ
    uint32_t _header   : 1; ///< header present
    uint32_t _rLen     : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen     : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res3     : 3; ///< MBZ
} MSD0R_OWB, MSD0W_OWB, MSD0R_OWAB, MSD0W_OWAB;

typedef struct
{
    uint32_t _bti      : 8; ///< binding table index
    uint32_t _dataElem : 2; ///< Specifies the number of contiguous Owords to be read or written  MDC_DB_OWD
    uint32_t _res1     : 3; ///< MBZ
    uint32_t _iar      : 1; ///< invalidate after read - for write operation MBZ
    uint32_t _type     : 4; ///< messaage type
    uint32_t _legacy   : 1; ///< Legacy message  MBZ
    uint32_t _header   : 1; ///< header present
    uint32_t _rLen     : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen     : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res3     : 3; ///< MBZ
} MSD0R_OWDB, MSD0W_OWDB;

typedef struct
{
    uint32_t _bti : 8; ///< binding table index
    uint32_t _simd : 1; ///< Specifies the SIMD mode of the message (number of slots processed) 0=SIMD8, 1=SIMD16       MDC_SM2
    uint32_t _res1 : 1; ///< MBZ 
    uint32_t _dataElem : 2; ///< Specifies the number of bytes to be read or written                                    MDC_DS
    uint32_t _res2 : 2; ///< MBZ
    uint32_t _type : 4; ///< messaage type
    uint32_t _legacy : 1; ///< Legacy message  MBZ
    uint32_t _header : 1; ///< header present
    uint32_t _rLen : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res3 : 3; ///< MBZ
} MSD0R_BS, MSD0W_BS;

typedef struct
{
    uint32_t _bti      : 8; ///< binding table index
    uint32_t _atomicOp : 4; ///< Specifies the atomic integer operation to be performed. MDC_AOP1
    uint32_t _simd     : 1; ///< Specifies the SIMD mode of the message (number of slots processed) 0=SIMD8, 1=SIMD16   MDC_SM2
    uint32_t _ret      : 1; ///< Return data control
    uint32_t _type     : 5; ///< messaage type
    uint32_t _header   : 1; ///< header present
    uint32_t _rLen     : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen     : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res3     : 3; ///< MBZ
} MSD1R_DWAI1, MSD1R_DWAI2, MSD1R_DWAI3, MSD1W_DWAI1, MSD1W_DWAI2, MSD1W_DWAI3;

typedef struct
{
    uint32_t _bti    : 8; ///< binding table index
    uint32_t _cmask  : 4; ///< channel mask - Specifies which RGBA channels are included in the message payload
    uint32_t _simd   : 2; ///< SIMD mode: 0 - SIMD16, 1 - SIMD8, other values ignored
    uint32_t _type   : 5; ///< message type
    uint32_t _header : 1; ///< header present
    uint32_t _rLen   : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen   : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res    : 3; ///< reserved MBZ
} MSD1R_US, MSD1W_US, MSD1W_A64_US, MSD1R_A64_US;


typedef struct
{
    uint32_t _bti    : 8; ///< binding table index
    uint32_t _cmask  : 4; ///< channel mask - Specifies which RGBA channels are included in the message payload
    uint32_t _slotg  : 2; ///< Specifies the Slot Group mode of the message (which slots are processed)
                          ///< Controls which 8 bits of Pixel/Sample Mask in the message header are ANDed with the execution mask
                          ///< to determine which slots are accessed.This field is ignored if the header is not present.
                          ///< 1 = use low 8 slots, 2 = use high 8 slots - all others are ignored
    uint32_t _type   : 5; ///< message type
    uint32_t _header : 1; ///< header present
    uint32_t _rLen   : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen   : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res    : 3; ///< reserved MBZ
} MSD1R_TS, MSD1W_TS;

typedef struct
{
    uint32_t _bti : 8; ///< binding table index
    uint32_t _atomicOp : 4; ///< Specifies the atomic integer operation to be performed. MDC_AOP1
    uint32_t _slot : 1; ///< Specifies the Slot Group mode of the message (which slots are processed)
    uint32_t _ret : 1; ///< Return data control
    uint32_t _type : 5; ///< messaage type
    uint32_t _header : 1; ///< header present
    uint32_t _rLen : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res3 : 3; ///< MBZ
} MSD1R_DWTAI1, MSD1R_DWTAI2, MSD1R_DWTAI3, MSD1W_DWTAI1, MSD1W_DWTAI2, MSD1W_DWTAI3;

typedef struct
{
    uint32_t _bti      : 8; ///< must be 0xFF or 0xFD indicating specifies the message is stateless
    uint32_t _stype    : 2; ///< A64 Scattered Message Subtype: 0 = byte read/write, 1 Dword read/write, 2 = Qword read/write
    uint32_t _dataElem : 2; ///< Specifies the number of data elements to be read or written: 0=DE1, 1=DE2, 2=DE4, 3=DE8   DE1,2,3 supported for B,DW,QW; DE8 support DW and QW
    uint32_t _simd     : 1; ///< SKL+: SIMD mode. 0 = SIMD8, 1 = SIMD16. PreSKL - ignored
    uint32_t _iar      : 1; ///< invalidate after read. For write MBZ
    uint32_t _type     : 5; ///< message type
    uint32_t _header   : 1; ///< header present
    uint32_t _rLen     : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen     : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res      : 3; ///< reserved MBZ
} MSD1R_A64_BS, MSD1R_A64_BS, MSD1R_A64_DWS, MSD1R_A64_QWS, MSD1W_A64_BS, MSD1W_A64_DWS, MSD1W_A64_QWS;

typedef struct
{
    uint32_t _bti      : 8; ///< must be 0xFF or 0xFD indicating specifies the message is stateless
    uint32_t _dataElem : 3; ///< Specifies the number of contiguous Owords/Hwords to be read or written
    uint32_t _stype    : 2; ///< A64 Block Message Subtype
    uint32_t _iar      : 1; ///< invalidate after read. For write MBZ
    uint32_t _type     : 5; ///< message type
    uint32_t _header   : 1; ///< header present
    uint32_t _rLen     : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen     : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res      : 3; ///< reserved MBZ
} MSD1R_A64_OWB, MSD1W_A64_OWB, MSD1R_A64_OWDB, MSD1W_A64_OWDB, MSD1R_A64_OWUB, MSD1W_A64_OWUB;

typedef struct 
{
    uint32_t _bti      : 8; ///< binding table index
    uint32_t _vlso      : 3; ///< If enabled, specifies the Vertical Line Stride and Vertical Line Stride Offset override fields
    uint32_t _res2     : 3; ///< reserved MBZ
    uint32_t _type     : 5; ///< message type
    uint32_t _header   : 1; ///< header present
    uint32_t _rLen     : 5; ///< response length - the number of 256-bit GRF registers expected as the message response payload (0-16)
    uint32_t _mLen     : 4; ///< message length - the number of 256-bit GRF registers sent as the message payload (including the header)
    uint32_t _res      : 3; ///< reserved MBZ
} MSD1R_MB, MSD1W_MB;


struct MSDUW_DWS
{
    MSDUW_DWS(uint32_t raw) { *(reinterpret_cast<uint32_t*>(this)) = raw; }
    operator uint32_t() const { return  *(reinterpret_cast<const uint32_t*>(this)); }

    uint32_t _urbOpcode             : 4; //URB Opcode 7=Write 8=Read
    uint32_t _globalOffset          : 11; //Specifies the offset, in units of Oword elements, from the start of the URB handle for the access.
    uint32_t _channelMaskPresent    : 1; // Indicates the channel Mask Message phase is not present.
    uint32_t _reserved1             : 1; //
    uint32_t _perSlotOffsetPresent  : 1; // Specifies if per-slot offset message payload is present. If present, it will be added to the Global Offset.
    uint32_t _reserved2             : 1; //
    uint32_t _headerPresent         : 1; // Indicates that the message requires a header.
    uint32_t _responseLength        : 5; // Specifies the number of 256-bit GRF registers expected as the message response payload. Valid value ranges are 0 to 16.
    uint32_t _messageLength         : 4; // Specifies the number of 256-bit GRF registers sent as the message payload (including the header).Valid value ranges are 1 to 15.
    uint32_t _reserved3             : 3; //

};

/* ===================================================================================================== */
// Constants
/* ===================================================================================================== */
static const uint32_t MDC_DB_OW[8] = { 1, 1, 2, 4, 8, 0, 0, 0 };
static const uint32_t MDC_DS[4] = { 1, 2, 4, 0 };
static const uint32_t MDC_DWS_DS[4] = { 1, 2, 4, 8 };

/* ===================================================================================================== */
// Utilities
/* ===================================================================================================== */
/// @return The number of bits corresponding to the specified channel offset identifier in GED
static uint32_t ConvertGedChannelOffsetToBits(GED_CHANNEL_OFFSET gedChannelOffset)
{
    switch (gedChannelOffset)
    {
    case GED_CHANNEL_OFFSET_M0:      return  0;
    case GED_CHANNEL_OFFSET_M4:      return  4;
    case GED_CHANNEL_OFFSET_M8:      return  8;
    case GED_CHANNEL_OFFSET_M12:     return 12;
    case GED_CHANNEL_OFFSET_M16:     return 16;
    case GED_CHANNEL_OFFSET_M20:     return 20;
    case GED_CHANNEL_OFFSET_M24:     return 24;
    case GED_CHANNEL_OFFSET_M28:     return 28;
    default:                         return 0;
    }
}

/// @return Number of channels enabled in the specified RGBA mask
static uint32_t RGBAMaskCount(uint32_t cmask)
{
    /// Count number of bits set in (0xf - cmask)
    uint32_t value = 0xf - uint8_t(cmask);
    uint32_t count; for (count = 0; value != 0 ; count++) { value &= (value - 1); }
    return count;
}

/* ===================================================================================================== */
// DcSendMsg implementation
/* ===================================================================================================== */
DcSendMsg::DcSendMsg() :
    _isWrite(false), _isScatter(false), _isScratch(false), _isAtomic(false), _isMemFence(false), _isMedia(false), _isEOT(false),
    _elementSize(0), _numElements(0), _addrPayloadLength(0), _dataPayloadLength(0), _src0Length(0), _src1Length(0), _src0(-1), _src1(-1), _execSize(0),
    _channelOffset(0), _globalOffset(0), _cmask(0xf), _isA64(false), _bti(-1), _simdWidth(0), _sfid(GED_SFID_INVALID), _type(GED_MESSAGE_TYPE_INVALID),
    _opcode(GED_DP_OPCODE_INVALID), _transpose(GED_DP_TRANSPOSE_INVALID) {}

DcSendMsg::DcSendMsg(const ged_ins_t& gedIns)
{
    *this = Decode(gedIns);
}

DcSendMsg DcSendMsg::Decode(const ged_ins_t& gedIns, bool isVerbose)
{
    DcSendMsg msg;

    auto reportUnsupportedSfid = [&](const std::string& sfidName)
    {
        if (isVerbose) { std::cerr <<  sfidName << " SFID is not yet supported" << std::endl; }
        msg._sfid = GED_SFID_INVALID;
    };

    GED_RETURN_VALUE status;
    ged_ins_t gi = gedIns;

    if (GED_GetDescRegFile(&gi, nullptr) != GED_REG_FILE_IMM)       { return msg; }

    GED_SFID sfid = GED_GetSFID(&gi, &status);
    if (status != GED_RETURN_VALUE_SUCCESS)                         { return msg; }

    switch (sfid)
    {
    case GED_SFID_DP_DC0:
    {
        msg = DecodeDp0Descriptors(gi, isVerbose);
        break;
    }
    case GED_SFID_DP_DC1:
    {
        msg = DecodeDp1Descriptors(gi, isVerbose);
        break;
    }
    case GED_SFID_UGM:
    case GED_SFID_TGM:
    case GED_SFID_UGML:
    case GED_SFID_SLM:
    {
        msg = DecodeLscDescriptors(gi, isVerbose);
        break;
    }
    case GED_SFID_URB:
    {
        msg = DecodeUrbDescriptors(gi, isVerbose);
        break;
    }
    default:
        break;
    }

    msg.DecodeSendGeneralAttr(gi);

    if (msg._sfid == GED_SFID_INVALID && msg._isEOT != GED_EOT_EOT)
    {
        reportUnsupportedSfid(GED_GetSFIDString(sfid));
    }

    return msg;
}

DcSendMsg DcSendMsg::DecodeDp1Descriptors(ged_ins_t& gedIns, bool isVerbose)
{
    DcSendMsg msg;
    uint32_t msgDesc, extMsgDesc;
    msgDesc = GED_GetMsgDesc(&gedIns, nullptr);
    extMsgDesc = GED_GetExMsgDescImm(&gedIns, nullptr);

    auto reportUnsupportedMsg = [&](const std::string& msgName)
    {
        if (isVerbose) { std::cerr <<  msgName << " message is not yet supported" << std::endl; }
        msg._type = GED_MESSAGE_TYPE_INVALID;
    };

    MSD1   msd(msgDesc);
    ExtMSD emd(extMsgDesc);

    GED_REG_FILE exMsgDescRegFile = GED_GetExDescRegFile((ged_ins_t*)&gedIns, nullptr);

    msg._sfid = GED_SFID_DP_DC1;
    msg._bti = msd._bti;
    msg._src0Length = msd._mLen;

    uint32_t src1Length = GED_GetSrc1Length((ged_ins_t*)&gedIns, nullptr);
    if (src1Length != uint32_t(-1))
    {
        msg._src1Length = src1Length;
    }
    else
    {
        msg._src1Length = (exMsgDescRegFile == GED_REG_FILE_IMM) ? emd._len : 0;
    }

    switch (msd._type)
    {
    case MT1R_T:       // Transpose Read message                                    Message descriptor: MSD1R_TT
    {
        msg._type = GED_MESSAGE_TYPE_MT1R_T;
        reportUnsupportedMsg("MT1R_T");
        break;
    }
    case MT1W_US:      // Untyped Surface Write mesage                              Message descriptor: MSD1W_US (BTI)
    case MT1R_US:      // Untyped Surface Read message                              Message descriptor: MSD1R_US (BTI)
    {
        msg._type = (msd._type == MT1W_US ? GED_MESSAGE_TYPE_MT1W_US : GED_MESSAGE_TYPE_MT1R_US);
        msg._isWrite = (msd._type == MT1W_US);
        MSD1R_US* m = (MSD1R_US*)&msd;

        msg._elementSize = 4;
        msg._isScatter = true;

        // MDC_SM3
        msg._simdWidth = m->_simd == 1 ? 16 : m->_simd == 2 ? 8 : 0;
        msg._cmask = m->_cmask;
        msg._numElements = RGBAMaskCount(msg._cmask);
        break;
    }
    case MT1W_A64_US:  // A64 Untyped Surface Write message                         Message descriptor: MSD1W_A64_US
    case MT1R_A64_US:  // A64 Untyped Surface Read message                          Message descriptor: MSD1R_A64_US
    {
        msg._type = (msd._type == MT1W_A64_US ? GED_MESSAGE_TYPE_MT1W_A64_US : GED_MESSAGE_TYPE_MT1R_A64_US);
        msg._isWrite = (msd._type == MT1W_A64_US);
        msg._isA64 = true;
        msg._elementSize = 4;
        msg._isScatter = true;

        MSD1W_A64_US* m = (MSD1W_A64_US*)&msd;

        // MDC_SM3
        msg._simdWidth = m->_simd == 1 ? 16 : m->_simd == 2 ? 8 : 0;

        msg._cmask = m->_cmask;
        msg._numElements = RGBAMaskCount(msg._cmask);
        break;
    }
    case MT1A_UI:      // Untyped Atomic Integer Operation message                  Message descriptor: MSD1R_DWAI1, MSD1R_DWAI2, MSD1R_DWAI3, MSD1W_DWAI1, MSD1W_DWAI2, MSD1W_DWAI3
    {
        msg._type = GED_MESSAGE_TYPE_MT1A_UI;
        MSD1R_DWAI1* m = (MSD1R_DWAI1*)&msd;
        msg._isWrite = (m->_rLen == 0);

        msg._simdWidth = m->_simd == 0 ? 16 : 8;
        msg._isAtomic = true;
        break;
    }
    case MT1A_UHI:     // Untyped Atomic Half Integer Operation message
    {
        msg._type = GED_MESSAGE_TYPE_MT1A_UHI;
        msg._isAtomic = true;
        reportUnsupportedMsg("MT1A_UHI");
        break;
    }
    case MT1W_MB:      // Media Block Write message                                 Message descriptor: MSD1W_MB
    case MT1R_MB:      // Media Block Read message                                  Message descriptor: MSD1R_MB
    {
        msg._type = (msd._type == MT1W_MB ? GED_MESSAGE_TYPE_MT1W_MB : GED_MESSAGE_TYPE_MT1R_MB);
        msg._isWrite = (msd._type == MT1W_MB);
        msg._isMedia = true;
        msg._elementSize = 1;
        msg._addrPayloadLength = 0;
        msg._simdWidth = 1;
        msg._numElements = 256;
        break;
    }
    case MT1W_TS:      // Typed Surface Write message                               Message descriptor: MSD1W_TS (BTI)
    case MT1R_TS:      // Typed Surface Read message                                Message descriptor: MSD1R_TS (BTI)
    {
        msg._type = (msd._type == MT1W_TS ? GED_MESSAGE_TYPE_MT1W_TS : GED_MESSAGE_TYPE_MT1R_TS);
        msg._isWrite = (msd._type == MT1W_TS);
        MSD1R_TS* m = (MSD1R_TS*)&msd;

        msg._simdWidth = 8;
        msg._elementSize = 4;
        msg._isScatter = true;
        msg._cmask = m->_cmask;
        msg._numElements = RGBAMaskCount(msg._cmask);
        break;
    }
    case MT1A_TA:      // Typed Atomic Integer Operation message                        Message descriptor: MSD1R_DWTAI1, MSD1R_DWTAI2, MSD1R_DWTAI3, MSD1W_DWTAI1, MSD1W_DWTAI2, MSD1W_DWTAI3
    {
        msg._type = GED_MESSAGE_TYPE_MT1A_TA;
        MSD1R_DWTAI1* m = (MSD1R_DWTAI1*)&msd;
        msg._isWrite = (m->_rLen == 0);

        msg._simdWidth = 8;
        msg._isAtomic = true;
        break;
    }
    case MT1A_TAH:     // Typed Atomic Half Integer Operation message
    {
        msg._type = GED_MESSAGE_TYPE_MT1A_TAH;
        msg._isAtomic = true;
        reportUnsupportedMsg("MT1A_TAH");
        break;
    }
    case MT1RS_US:     // Reserved Ignored  BDW+
    {
        reportUnsupportedMsg("MT1RS_US");
        break;
    }
    case MT1A_TC:      // Typed Atomic Counter Operation message                    Message descriptor: MSD1R_DWAC1, MSD1R_DWAC2, MSD1W_DWAC1, MSD1W_DWAC2
    {
        msg._type = GED_MESSAGE_TYPE_MT1A_TC;
        msg._isAtomic = true;
        reportUnsupportedMsg("MT1A_TC");
        break;
    }
    case MT1A_TCH:     // Typed Atomic Half Counter Operation message
    {
        msg._type = GED_MESSAGE_TYPE_MT1A_TCH;
        msg._isAtomic = true;
        reportUnsupportedMsg("MT1A_TCH");
        break;
    }
    case MT1RS_TS:     // Reserved Ignored  BDW+
    {        
        reportUnsupportedMsg("MT1RS_TS");
        break;
    }
    case MT1W_A64_SB:  // A64 Scattered Write message                               Message descriptor: MSD1W_A64_BS, MSD1W_A64_DWS, MSD1W_A64_QWS (stateless)
    case MT1R_A64_SB:  // A64 Scattered Read message                                Message descriptor: MSD1R_A64_BS, MSD1R_A64_DWS, MSD1R_A64_QWS (stateless)
    {
        msg._type = (msd._type == MT1W_A64_SB ? GED_MESSAGE_TYPE_MT1W_A64_SB : GED_MESSAGE_TYPE_MT1R_A64_SB);
        msg._isWrite = (msd._type == MT1W_A64_SB);
        msg._isA64 = true;
        msg._isScatter = true;

        MSD1R_A64_BS* m = (MSD1R_A64_BS*)&msd;
        switch (m->_stype)
        {
        case 0:
            msg._elementSize = 1;
            break; // "B"
        case 1:
            msg._elementSize = 4;
            break; // "DW"
        case 2:
            msg._elementSize = 8;
            break; // "QW"
        default:
            break;
        }

        msg._numElements = 1 << m->_dataElem;

        // MDC_SM2
        msg._simdWidth = m->_simd == 1 ? 16 : 8;

        break;
    }
    case MT1A_A64_UI:  // A64 Untyped Atomic Integer Operation message              Message descriptor: MSD1R_A64_DWAI1, MSD1R_A64_DWAI2, MSD1R_A64_DWAI3, MSD1W_A64_DWAI1, MSD1W_A64_DWAI2, MSD1W_A64_DWAI3,
    {                  //                                                                               MSD1R_A64_QWAI1, MSD1R_A64_QWAI2, MSD1R_A64_QWAI3, MSD1W_A64_QWAI1, MSD1W_A64_QWAI2, MSD1W_A64_QWAI3
        msg._type = GED_MESSAGE_TYPE_MT1A_A64_UI;
        msg._isA64 = true;
        msg._isAtomic = true;
        msg._isScatter = true;

        MSD1R_A64_DWAI1* m = (MSD1R_A64_DWAI1*)&msd;
        msg._isWrite = (m->_rLen == 0);
        msg._elementSize = m->_dataWidth == 0 ? 4 : 8;
        msg._numElements = 1;
        msg._simdWidth = 8;
        msg._numElements = 1;

        break;
    }
    case MT1A_A64_UHI: // A64 Untyped Atomic Half Integer Operation message
    {
        msg._type = GED_MESSAGE_TYPE_MT1A_A64_UHI;
        msg._isA64 = true;
        msg._isAtomic = true;
        reportUnsupportedMsg("MT1A_A64_UHI");
        break;
    }
    case MT1W_A64_B:   // A64 Block Write message                                   Message descriptor: MSD1W_A64_OWB, MSD1W_A64_OWUB, MSD1W_A64_OWDB, MSD1W_A64_HWB
    case MT1R_A64_B:   // A64 Block Read message                                    Message descriptor: MSD1R_A64_OWB, MSD1R_A64_OWUB, MSD1R_A64_OWDB, MSD1R_A64_HWB
    {
        msg._type = (msd._type == MT1W_A64_B ? GED_MESSAGE_TYPE_MT1W_A64_B : GED_MESSAGE_TYPE_MT1R_A64_B);
        msg._isWrite = (msd._type == MT1W_A64_B);
        msg._isA64 = true;

        MSD1R_A64_OWB* m = (MSD1R_A64_OWB*)&msd;
        switch (m->_stype)
        {
        case 0:
            msg._elementSize = 16;
            break; // "OW"
        case 1:
            msg._elementSize = 16;
            break; // "OW"
        case 2:
            msg._elementSize = 32;
            break; // "HW"
        case 3:
            msg._elementSize = 32;
            break; // "HW"
        default:
            break;
        }

        switch (m->_dataElem)
        {
        case 0:
        case 1:
            msg._numElements = 1;
            break;
        case 2:
            msg._numElements = 2;
            break;
        case 3:
            msg._numElements = 4;
            break;
        case 4:
            msg._numElements = 8;
            break;
        default:
            msg._numElements = 0;
            break;
        }

        break;
    }
    case MT1RS_A64_US: // A64 Reserved Ignored  BDW+
    {
        msg._isA64 = true;
        reportUnsupportedMsg("MT1RS_A64_US");
        break;
    }
    case MT1A_UF:      // Untyped Atomic Float Operation message                    Message descriptor: MSD1R_DWAF2, MSD1R_DWAF3, MSD1W_DWAF2, MSD1W_DWAF3
    {
        msg._type = GED_MESSAGE_TYPE_MT1A_UF;
        msg._isAtomic = true;
        reportUnsupportedMsg("MT1A_UF");
        break;
    }
    case MT1A_UHF:     // Untyped Atomic Half Float Operation message
    {
        msg._type = GED_MESSAGE_TYPE_MT1A_UHF;
        msg._isAtomic = true;
        reportUnsupportedMsg("MT1A_UHF");
        break;
    }
    case MT1A_A64_UF:  // A64 Untyped Atomic Float Operation message                Message descriptor: MSD1R_A64_DWAF2, MSD1R_A64_DWAF3, MSD1W_A64_DWAF2, MSD1W_A64_DWAF3
    {
        msg._type = GED_MESSAGE_TYPE_MT1A_A64_UF;
        msg._isA64 = true;
        reportUnsupportedMsg("MT1A_S64_UF");
        break;
    }
    case MT1A_A64_UHF: // A64 Untyped Atomic Half Float Operation message
    {
        msg._type = GED_MESSAGE_TYPE_MT1A_A64_UHF;
        msg._isA64 = true;
        msg._isAtomic = true;
        reportUnsupportedMsg("MT1A_S64_UHF");
        break;
    }
    default:
    {
        reportUnsupportedMsg(std::to_string(msd._type));
        break;
    }
    };

    if (msg._type != GED_MESSAGE_TYPE_INVALID)
    {
        if (msg._isScatter)
        {
            GED_MODEL modelId = GED_GetModel(&gedIns);
            uint32_t grfSize = (modelId <= GED_MODEL_XE_HPG) ? 32 : 64;
            msg._addrPayloadLength = msg.AddrSize() * msg._simdWidth / grfSize;
        }
        msg._addrPayloadLength += msd._header;
        if (isVerbose) { std::cout << msg.ToString() << std::endl; }
    }

    return msg;
}

DcSendMsg DcSendMsg::DecodeDp0Descriptors(ged_ins_t& gedIns, bool isVerbose)
{
    DcSendMsg msg;
    uint32_t msgDesc, extMsgDesc;
    msgDesc = GED_GetMsgDesc(&gedIns, nullptr);
    extMsgDesc = GED_GetExMsgDescImm(&gedIns, nullptr);

    auto reportUnsupportedMsg = [&](const std::string& msgName)
    {
        if (isVerbose) { std::cerr <<  msgName << " message is not yet supported" << std::endl; }
        msg._type = GED_MESSAGE_TYPE_INVALID;
    };

    MSD0   msd(msgDesc);
    ExtMSD emd(extMsgDesc);

    GED_REG_FILE exMsgDescRegFile = GED_GetExDescRegFile((ged_ins_t*)&gedIns, nullptr);

    msg._sfid = GED_SFID_DP_DC0;
    msg._bti = msd._bti;
    msg._isScratch = msd._scratch;
    msg._src0Length = msd._mLength;
    msg._addrPayloadLength = 0;

    uint32_t src1Length = GED_GetSrc1Length((ged_ins_t*)&gedIns, nullptr);
    if (src1Length != uint32_t(-1))
    {
        msg._src1Length = src1Length;
    }
    else
    {
        msg._src1Length = (exMsgDescRegFile == GED_REG_FILE_IMM) ? emd._len : 0;
    }

    if (msg._isScratch)
    {
        MSD0RW_SB msd(msgDesc);

        msg._isWrite = msd._opType;
        msg._addrPayloadLength = 1;

        msg._elementSize = 32;
        msg._numElements = 1 << msd._dataElem;

        if (msg._isWrite)
        {
            msg._src1Length = msg._numElements;
        }
    }
    else
    {
        switch (msd._type)
        {

        case MT0W_OWB:  // Block Write message                  Message descriptor: MSD0W_OWB
        case MT0R_OWB:  // Block Read message                   Message descriptor: MSD0R_OWB
        {
            msg._type = (msd._type == MT0W_OWB ? GED_MESSAGE_TYPE_MT0W_OWB : GED_MESSAGE_TYPE_MT0R_OWB);
            msg._isWrite = (msd._type == MT0W_OWB);
            MSD0R_OWB* m = (MSD0R_OWB*)&msd;

            msg._elementSize = 16;
            msg._numElements = MDC_DB_OW[m->_dataElem];
            break;
        }
        case MT0W_OWAB: // Aligned Block Write message            Message descriptor: MSD0W_OWAB (rejected)
        case MT0R_OWAB: // Aligned Block Read message             Message descriptor: MSD0R_OWAB
        {
            msg._type = (msd._type == MT0R_OWAB ? GED_MESSAGE_TYPE_INVALID : GED_MESSAGE_TYPE_MT0R_OWAB);
            msg._isWrite = (msd._type == MT0W_OWAB);
            MSD0R_OWB* m = (MSD0R_OWB*)&msd;

            msg._elementSize = 16;
            msg._numElements = MDC_DB_OW[m->_dataElem];
            break;
        }
        case MT0W_OWDB: // Oword Dual Block Write message         Message descriptor: MSD0W_OWDB
        case MT0R_OWDB: // Oword Dual Block Read message          Message descriptor: MSD0R_OWDB
        {
            msg._type = (msd._type == MT0W_OWDB ? GED_MESSAGE_TYPE_MT0W_OWDB : GED_MESSAGE_TYPE_MT0R_OWDB);
            msg._isWrite = (msd._type == MT0W_OWDB);
            MSD0R_OWDB* m = (MSD0R_OWDB*)&msd;

            msg._elementSize = 32;
            msg._numElements = (1 << m->_dataElem);
            break;
        }
        case MT0W_DWS: // Dword Scattered Write message          Message descriptor: MSD0W_DWS
        case MT0R_DWS: // Dword Scattered Read message           Message descriptor: MSD0R_DWS
        {
            msg._type = (msd._type == MT0W_DWS ? GED_MESSAGE_TYPE_MT0W_DWS : GED_MESSAGE_TYPE_MT0R_DWS);
            msg._isWrite = (msd._type == MT0W_DWS);
            msg._isScatter = true;

            MSD0R_BS* m = (MSD0R_BS*)&msd;

            msg._simdWidth = m->_simd == 1 ? 16 : 8;
            msg._elementSize = 4;
            msg._numElements = MDC_DWS_DS[m->_dataElem];
            break;
        }
        case MT0W_BS: // Byte Scattered Write message           Message descriptor: MSD0W_BS
        case MT0R_BS: // Byte Scattered Read message            Message descriptor: MSD0R_BS
        {
            msg._type = (msd._type == MT0W_BS ? GED_MESSAGE_TYPE_MT0W_BS : GED_MESSAGE_TYPE_MT0R_BS);
            msg._isWrite = (msd._type == MT0W_BS);
            msg._isScatter = true;

            MSD0R_BS* m = (MSD0R_BS*)&msd;

            msg._simdWidth = m->_simd == 1 ? 16 : 8;
            msg._elementSize = 1;
            msg._numElements = MDC_DS[m->_dataElem];
            break;
        }
        case MT0_MEMFENCE: // Memory Fence message                   Message descriptor: MSD_MEMFENCE
        {
            msg._type = GED_MESSAGE_TYPE_MT0_MEMFENCE;
            msg._isMemFence = true;
            break;
        }
        default:
        {
            reportUnsupportedMsg(std::to_string(msd._type));
            break;
        }
        };
    }

    if (msg._type != GED_MESSAGE_TYPE_INVALID)
    {
        if (!msg._isMemFence)
        {
            if (msg._isScatter)
            {
                GED_MODEL modelId = GED_GetModel(&gedIns);
                uint32_t grfSize = (modelId <= GED_MODEL_XE_HPG) ? 32 : 64;
                msg._addrPayloadLength = msg.AddrSize() * msg._simdWidth / grfSize;
            }
            msg._addrPayloadLength += msd._header;
        }
        if (isVerbose) { std::cout << msg.ToString() << std::endl; }
    }

    return msg;
}

DcSendMsg DcSendMsg::DecodeLscDescriptors(ged_ins_t& gedIns, bool isVerbose)
{
    GED_MODEL modelId = GED_GetModel(&gedIns);
    uint32_t grfSize = (modelId == GED_MODEL_XE_HPG) ? 32 : 64;

    DcSendMsg msg;
    uint32_t msgDesc, extMsgDesc;
    msgDesc    = GED_GetMsgDesc(&gedIns, nullptr);
    extMsgDesc = GED_GetExMsgDescImm(&gedIns, nullptr);

    auto reportUnsupportedMsg = [&](const std::string& msgName)
    {
        if (isVerbose) { std::cerr << msgName << " message is not yet supported" << std::endl; }
        msg._opcode = GED_DP_OPCODE_INVALID;
    };

    msg._sfid       = GED_GetSFID((ged_ins_t*)&gedIns, nullptr);
    msg._type       = GED_MESSAGE_TYPE_INVALID;
    msg._opcode     = GED_GetDPOpcode(msgDesc, modelId, nullptr);
    msg._cmask      = 0;
    msg._src0Length = GED_GetMessageLength(msgDesc, modelId, nullptr);
    msg._src1Length = GED_GetSrc1Length((ged_ins_t*)&gedIns, nullptr);

    msg._isMedia    = false;
    msg._isScratch  = false; //evaluation is not supported for LSC
    msg._isMemFence = (msg._opcode == GED_DP_OPCODE_FENCE);

    if (!msg._isMemFence)
    {
        switch (msg._opcode)
        {
        case GED_DP_OPCODE_ATOMIC_INCREMENT:
        case GED_DP_OPCODE_ATOMIC_DECREMENT:
        case GED_DP_OPCODE_ATOMIC_LOAD:
        case GED_DP_OPCODE_ATOMIC_STORE:
        case GED_DP_OPCODE_ATOMIC_ADD:
        case GED_DP_OPCODE_ATOMIC_SUB:
        case GED_DP_OPCODE_ATOMIC_MIN:
        case GED_DP_OPCODE_ATOMIC_MAX:
        case GED_DP_OPCODE_ATOMIC_UMIN:
        case GED_DP_OPCODE_ATOMIC_UMAX:
        case GED_DP_OPCODE_ATOMIC_COMPARE_EXCHANGE:
        case GED_DP_OPCODE_ATOMIC_FLOAT_ADD:
        case GED_DP_OPCODE_ATOMIC_FLOAT_SUB:
        case GED_DP_OPCODE_ATOMIC_FLOAT_MIN:
        case GED_DP_OPCODE_ATOMIC_FLOAT_MAX:
        case GED_DP_OPCODE_ATOMIC_FLOAT_COMPARE_EXCHANGE:
        case GED_DP_OPCODE_ATOMIC_AND:
        case GED_DP_OPCODE_ATOMIC_OR:
        case GED_DP_OPCODE_ATOMIC_XOR:
            msg._isAtomic = true; break;
        default:
            msg._isAtomic = false; break;
        }

        if (msg._isAtomic)
        {
            msg._isScatter = true;
        }
        else
        {
            switch (msg._opcode)
            {
            case GED_DP_OPCODE_LOAD:
            case GED_DP_OPCODE_STORE:
            case GED_DP_OPCODE_STORE_UNCOMPRESSED:
            case GED_DP_OPCODE_STORE_CMASK:
            case GED_DP_OPCODE_LOAD_CMASK:
                msg._isScatter = true; break;

            case GED_DP_OPCODE_FENCE:
            case GED_DP_OPCODE_LOAD_2D_BLOCK:
            case GED_DP_OPCODE_STORE_2D_BLOCK:
                msg._isScatter = false; break;

            default:
                reportUnsupportedMsg(std::to_string(msg._opcode));
                break;
            }
        }

        if (msg._opcode != GED_DP_OPCODE_INVALID)
        {
            GED_DP_DATA_SIZE dataSize = GED_GetDPDataSize(msgDesc, modelId, nullptr);
            switch (dataSize)
            {
            case GED_DP_DATA_SIZE_D8:       msg._elementSize = 1;  break;
            case GED_DP_DATA_SIZE_D16:      msg._elementSize = 2;  break;
            case GED_DP_DATA_SIZE_D32:      msg._elementSize = 4;  break;
            case GED_DP_DATA_SIZE_D64:      msg._elementSize = 8;  break;
            case GED_DP_DATA_SIZE_D8U32:    msg._elementSize = 1;  break;
            case GED_DP_DATA_SIZE_D16U32:   msg._elementSize = 2;  break;
            case GED_DP_DATA_SIZE_D16BF32:  msg._elementSize = 2;  break;
            default:                        msg._elementSize = 0;  break;
            }

            GED_DP_VECT_SIZE vectSize = GED_GetDPVectSize(msgDesc, modelId, nullptr);
            switch (vectSize)
            {
            case GED_DP_VECT_SIZE_V1:  msg._numElements = 1;  break;
            case GED_DP_VECT_SIZE_V2:  msg._numElements = 2;  break;
            case GED_DP_VECT_SIZE_V3:  msg._numElements = 3;  break;
            case GED_DP_VECT_SIZE_V4:  msg._numElements = 4;  break;
            case GED_DP_VECT_SIZE_V8:  msg._numElements = 8;  break;
            case GED_DP_VECT_SIZE_V16: msg._numElements = 16; break;
            case GED_DP_VECT_SIZE_V32: msg._numElements = 32; break;
            case GED_DP_VECT_SIZE_V64: msg._numElements = 64; break;
            default:  msg._numElements = 0; break;
            }

            msg._addrPayloadLength = msg._src0Length;
            msg._isWrite           = GED_GetResponseLength(msgDesc, modelId, nullptr) == 0;
            msg._isA64             = (GED_GetDPAddrSize(msgDesc, modelId, nullptr) == GED_DP_ADDR_SIZE_A64);
            msg._bti               = msg._isA64 ? 0xff : extMsgDesc >> 24;

            msg._transpose = GED_GetDPTranspose(msgDesc, modelId, nullptr);
            if (msg._transpose == GED_DP_TRANSPOSE_SIMD)
            {
                msg._simdWidth = 1;
            }
            else
            {
                if (modelId == GED_MODEL_XE_HPG)
                {
                    msg._simdWidth = (msg._src0Length * grfSize / msg.AddrSize()) <= 8 ? 8 : 16;
                }
                else
                {
                    msg._simdWidth = (msg._src0Length * grfSize / msg.AddrSize()) <= 16 ? 16 : 32;
                }
            }
        }
    }

    if (isVerbose) { std::cout << msg.ToString() << std::endl; }

    return msg;
}

DcSendMsg DcSendMsg::DecodeUrbDescriptors(ged_ins_t& gedIns, bool isVerbose)
{
    DcSendMsg msg = {};
    uint32_t msgDesc, extMsgDesc;
    GED_RETURN_VALUE result;
    
    msgDesc = GED_GetMsgDesc(&gedIns, &result);
    extMsgDesc = GED_GetExMsgDescImm(&gedIns, &result);

    auto reportUnsupportedMsg = [&](const std::string& msgName)
    {
        if (isVerbose) { std::cerr << msgName << " message is not yet supported" << std::endl; }
        msg._type = GED_MESSAGE_TYPE_INVALID;
    };

    MSDUW_DWS   msd(msgDesc);
    ExtMSD emd(extMsgDesc);

    constexpr uint32_t writeMsgOpcode   = 7;
    constexpr uint32_t readMsgOpcode    = 8;

    if (msd._urbOpcode != writeMsgOpcode && msd._urbOpcode != readMsgOpcode)
    {
        reportUnsupportedMsg("URB Message");
        return msg;
    }
    GED_REG_FILE exMsgDescRegFile = GED_GetExDescRegFile((ged_ins_t*)&gedIns, &result);

    msg._sfid = GED_SFID_URB;
    msg._src0Length = msd._messageLength;
    //msg._src1Length = (exMsgDescRegFile == GED_REG_FILE_IMM) ? emd._len : GED_GetSrc1Length((ged_ins_t*)&gedIns, &result);
    msg._addrPayloadLength = msg._src0Length;
    uint32_t src1Length = GED_GetSrc1Length((ged_ins_t*)&gedIns, &result);
    if (src1Length != uint32_t(-1))
    {
        msg._src1Length = src1Length;
    }
    else
    {
        msg._src1Length = (exMsgDescRegFile == GED_REG_FILE_IMM) ? emd._len : 0;
    }
    
    if (msd._perSlotOffsetPresent)
    {
        // if per-slot offset message payload is present, it will be added to the Global Offset.
        msg._globalOffset = msd._globalOffset*16; // OWORDs
    }
    
    
    msg._dataPayloadLength = (msd._urbOpcode == writeMsgOpcode) ? msg._src1Length : msd._responseLength;

    msg._isWrite = (msd._urbOpcode == writeMsgOpcode);
    msg._simdWidth = 8;
    msg._elementSize = 4; //DWORD

    return msg;
}

std::string DcSendMsg::ToString() const
{
    std::ostringstream ostr;
    if (_sfid == GED_SFID_DP_DC0 || _sfid == GED_SFID_DP_DC1)
    {
        ostr << "type = " << GED_GetMessageTypeString(Type());
    }
    else
    {
        ostr << "opcode = " << GED_GetDpOpcodeString(Opcode());
    }
    ostr << " sfid = "                  << GED_GetSFIDString(Sfid());
    ostr << " access = "                << (IsWrite() ? "W" : "R");
    ostr << " scatter = "               << (IsScatter() ? "Y" : "N");
    ostr << " addr.size = "             << AddrSize();
    ostr << " simd = "                  << SimdWidth();
    ostr << " element size = "          << ElementSize();
    ostr << " num.elements = "          << NumElements();
    ostr << " msg.length = "            << MsgLength();
    ostr << " addr.payload length = "   << AddrPayloadLength();
    ostr << " addr.model = "            << (IsBts() ? "BTS" : (IsSlm() ? "SLM" : (IsA64() ? "A64" : "A32")));

    return ostr.str();
}

bool DcSendMsg::IsValid() const
{
    return (IsUrb() || (IsLsc() && _opcode != GED_DP_OPCODE_INVALID) || (_sfid != GED_SFID_INVALID && _type != GED_MESSAGE_TYPE_INVALID));
}

void DcSendMsg::DecodeSendGeneralAttr(ged_ins_t& gedIns)
{
    _sfid          = GED_GetSFID((ged_ins_t*)&gedIns, nullptr);
    _isEOT         = (GED_GetEOT(&gedIns, nullptr) == GED_EOT_EOT);
    _src0          = GED_GetSrc0RegNum(&gedIns, nullptr);
    _src1          = GED_GetSrc1RegNum(&gedIns, nullptr);
    _execSize      = GED_GetExecSize(&gedIns, nullptr);
    _channelOffset = ConvertGedChannelOffsetToBits(GED_GetChannelOffset(&gedIns, nullptr));
}