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

Copyright (C) 2018-2021 Intel Corporation

SPDX-License-Identifier: MIT

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

#ifndef IGA_BXML_TYPES_HPP
#define IGA_BXML_TYPES_HPP

// C++17 allows inline variables, but until then we need a data structure
#include <array>
#include <cstdint>

namespace iga {
struct BfnFC {
  uint32_t value;
  constexpr explicit BfnFC(uint8_t val) : value(val) {}
  const char *c_str() const;
};

// used for if, else, and goto
enum class BranchCtrl {
  OFF,
  ON,
};

// used for instructions that lack a subfunction
enum class InvalidFC { INVALID = -1 };

// Math function control (selector)
enum class MathFC {
  INVALID = -1,
  INV = 1,
  LOG,
  EXP,
  SQT,
  RSQT,
  SIN,
  COS,
  FDIV,
  POW,
  IDIV,
  IQOT,
  IREM,
  INVM,
  RSQTM,
  TANH,
  SIGM
};
// static const MathFC ALL_MathFCs[] ... need C++17
static const std::array<MathFC, 16> ALL_MathFCs{
    MathFC::INV,  MathFC::LOG,   MathFC::EXP,  MathFC::SQT,
    MathFC::RSQT, MathFC::SIN,   MathFC::COS,  MathFC::FDIV,
    MathFC::POW,  MathFC::IDIV,  MathFC::IQOT, MathFC::IREM,
    MathFC::INVM, MathFC::RSQTM, MathFC::TANH, MathFC::SIGM,
};
unsigned GetSourceCount(MathFC mfc);
static inline bool IsMacro(MathFC mfc) {
  return mfc == MathFC::INVM || mfc == MathFC::RSQTM;
}
enum class ShuffleFC {
  INVALID = -1,
  UP = 0,
  UPZ,
  DN,
  DNZ,
  XOR,
  IDX,
  IDX4
};

static const std::array<ShuffleFC, 7>
      ALL_ShuffleFC {
        ShuffleFC::UP, ShuffleFC::UPZ, ShuffleFC::DN, ShuffleFC::DNZ,
        ShuffleFC::XOR, ShuffleFC::IDX, ShuffleFC::IDX4
      };

enum class LfsrFC {
  INVALID = -1,
  LFSR_b32 = 0, // Single 32-bit seed/polynomial
  LFSR_b16v2,   // Two 16-bit seeds/polynomials packed in 32 bits
  LFSR_b8v4     // Four 8-bit seeds/polynomials packed in 32 bits
};

static const std::array<LfsrFC, 3>
      ALL_LfsrFC {
        LfsrFC::LFSR_b32, LfsrFC::LFSR_b16v2, LfsrFC::LFSR_b8v4
      };

// Send shared function ID
enum class SFID {
  INVALID = -1,
  NULL_ = 0,             // the null shared function
  UGML,                  // untyped global memory (low bandwidth) for XeHPC
  SMPL,                  // sampler
  GTWY,                  // gateway
  DC2,                   // data cache 2
  RC,                    // render cache
  URB,                   // unified return buffer
  TS,                    // thread spawner (until XeHPG)
  VME,                   // video motion estimation (until XeHPG)
  DCRO,                  // data cache read-only
  DC0,                   // data cache 0
  PIXI,                  // pixel interpolator
  DC1,                   // data cache 1
  CRE,                   // check and refinement engine (until XeHPG)
  SLM,                   // shared local memory (XeHPG+)
  UGM,                   // untyped global memory (XeHPG+)
  BTD,                   // bindless thread dispatcher (XeHPG+)
  RTA,                   // ray tracing accellerator (XeHPG+)
  TGM,                   // typed global memory (XeHPG+)
  A0REG = 0x100,         // for <XE SFID can be indirect
};
static const std::array<SFID,
                        19
                        >
    ALL_SFIDS{
        SFID::NULL_, SFID::UGML, SFID::SMPL, SFID::GTWY, SFID::DC2,
        SFID::RC,    SFID::URB,  SFID::TS,   SFID::VME,  SFID::DCRO,
        SFID::DC0,   SFID::PIXI, SFID::DC1,  SFID::CRE,  SFID::SLM,
        SFID::UGM,   SFID::BTD,  SFID::RTA,  SFID::TGM,
};


// The sync instruction's subfunction
enum class SyncFC {
  INVALID = -1,
  NOP = 0,
  ALLRD = 2,
  ALLWR = 3,
  FLUSH = 12,
  FENCE = 13, // XeHPC
  BAR = 14,
  HOST = 15,
};
// static const SyncFC ALL_SyncFCs[] ...
static const std::array<SyncFC, 7> ALL_SyncFCs {
    SyncFC::NOP,   SyncFC::ALLRD, SyncFC::ALLWR, SyncFC::FLUSH,
    SyncFC::FENCE, // XeHPC
    SyncFC::BAR,   SyncFC::HOST,
};

// The DpasFC function control fuses the systolic count and repeat counts
// into a single subfunction.
enum class DpasFC {
  // ordinal values are now (SystolicCount << 8) | (RepeatCount)
  // encoding has to do something special because it's platform specific
  INVALID = -1,
  //
  F_1X1 = (1 << 8) | 1,
  F_1X2 = (1 << 8) | 2,
  F_1X3 = (1 << 8) | 3,
  F_1X4 = (1 << 8) | 4,
  F_1X5 = (1 << 8) | 5,
  F_1X6 = (1 << 8) | 6,
  F_1X7 = (1 << 8) | 7,
  F_1X8 = (1 << 8) | 8,
  F_2X1 = (2 << 8) | 1,
  F_2X2 = (2 << 8) | 2,
  F_2X3 = (2 << 8) | 3,
  F_2X4 = (2 << 8) | 4,
  F_2X5 = (2 << 8) | 5,
  F_2X6 = (2 << 8) | 6,
  F_2X7 = (2 << 8) | 7,
  F_2X8 = (2 << 8) | 8,
  F_4X1 = (4 << 8) | 1,
  F_4X2 = (4 << 8) | 2,
  F_4X3 = (4 << 8) | 3,
  F_4X4 = (4 << 8) | 4,
  F_4X5 = (4 << 8) | 5,
  F_4X6 = (4 << 8) | 6,
  F_4X7 = (4 << 8) | 7,
  F_4X8 = (4 << 8) | 8,
  F_8X1 = (8 << 8) | 1,
  F_8X2 = (8 << 8) | 2,
  F_8X3 = (8 << 8) | 3,
  F_8X4 = (8 << 8) | 4,
  F_8X5 = (8 << 8) | 5,
  F_8X6 = (8 << 8) | 6,
  F_8X7 = (8 << 8) | 7,
  F_8X8 = (8 << 8) | 8,
};

// static const DpasFC ALL_DpasFCs[] ...
static const std::array<DpasFC, 48> ALL_DpasFCs {
  DpasFC::F_1X1, DpasFC::F_1X2, DpasFC::F_1X3, DpasFC::F_1X4, DpasFC::F_1X5,
      DpasFC::F_1X6, DpasFC::F_1X7, DpasFC::F_1X8, DpasFC::F_2X1, DpasFC::F_2X2,
      DpasFC::F_2X3, DpasFC::F_2X4, DpasFC::F_2X5, DpasFC::F_2X6, DpasFC::F_2X7,
      DpasFC::F_2X8, DpasFC::F_4X1, DpasFC::F_4X2, DpasFC::F_4X3, DpasFC::F_4X4,
      DpasFC::F_4X5, DpasFC::F_4X6, DpasFC::F_4X7, DpasFC::F_4X8, DpasFC::F_8X1,
      DpasFC::F_8X2, DpasFC::F_8X3, DpasFC::F_8X4, DpasFC::F_8X5, DpasFC::F_8X6,
      DpasFC::F_8X7, DpasFC::F_8X8,
};

static inline uint32_t GetDpasSystolicDepth(DpasFC sf) {
  return uint32_t(sf) >> 8;
}
static inline uint32_t GetDpasRepeatCount(DpasFC sf) {
  return uint32_t(sf) & 0xFF;
}
static inline uint32_t GetDpasSystolicDepthEncoding(DpasFC sf) {
  switch (GetDpasSystolicDepth(sf)) {
  case 1:
    return 0;
  case 2:
    return 1;
  case 4:
    return 2;
  case 8:
    return 3;
  default:
    return 0xFFFFFFFF; // unreachable
  }
}
static inline uint32_t GetDpasRepeatCountEncoding(DpasFC sf) {
  return GetDpasRepeatCount(sf) - 1;
}
static inline DpasFC GetDpasFC(uint32_t sysD, uint32_t repC) {
  return DpasFC((sysD << 8) | repC);
}

// dnscl instruction's modes and types
enum class DnsclMode {
  INVALID = -1,
  MODE0, // Packs the output following the downscale instruction's Mode0
  MODE1, // Packs the output following the downscale instruction's Mode1
  MODE2, // Packs the output following the downscale instruction's Mode2
  MODE3  // Packs the output following the downscale instruction's Mode3
};

static const std::array<DnsclMode, 4>
  ALL_DnsclMode {
    DnsclMode::MODE0,
    DnsclMode::MODE1,
    DnsclMode::MODE2,
    DnsclMode::MODE3
  };

enum class ConvDstDataType {
  INVALID = -1,
  E2M1,
  INT4
};

static const std::array<ConvDstDataType, 3>
  ALL_ConvDstDataType {
    ConvDstDataType::E2M1,
    ConvDstDataType::INT4
  };

enum class ConvSrcDataType {
  INVALID = -1,
  HF,
  BF,
  F32 // Same as F type
};

static const std::array<ConvSrcDataType, 3>
  ALL_ConvSrcDataType {
    ConvSrcDataType::HF,
    ConvSrcDataType::BF,
    ConvSrcDataType::F32,
  };

enum class RoundingMode {
  INVALID = -1,
  SRAND,
  RNE
};

static const std::array<RoundingMode, 2>
  ALL_RoundingMode {
    RoundingMode::SRAND,
    RoundingMode::RNE,
  };

struct DnsclFC {
  // dnscl FC is composed by 4 parts: ConvSrcDataType, ConvDstDataType,
  // DnsclMode and RoundingMode.
  ConvSrcDataType convSrcTy : 8;
  ConvDstDataType convDstTy : 8;
  DnsclMode dnsclMode       : 8;
  RoundingMode roundingMode : 8;

  constexpr explicit DnsclFC(ConvSrcDataType srcTy, ConvDstDataType dstTy,
                             DnsclMode dm, RoundingMode rm)
      : convSrcTy(srcTy), convDstTy(dstTy), dnsclMode(dm), roundingMode(rm) {}
};

// An instruction subfunction is a set of immediate-encoded control bits
// that control the behavior of some EU instruction.  We permit an
// Instruction to have exactly one of these fields.
//
// For instance, the 'math' instruction (Op::MATH) must have a subfunction
// to select the specific math operation to execute.
//
//
// This is a union data type containing all the valid subfunctions
// that an instruction may have
// e.g.
//    send.<SUBFUNC> ...
//    sync.<SUBFUNC> ...
//    math.<SUBFUNC> ...
// For instructions that contain multiple paramters pair the two
// together into a single value via a pairing function
// (search for "Pairing Function" if unfamiliar).
// E.g. DPAS has both a systolic depth and repeat count.
// One way to pair that is to use the high half of the integer
// to hold one, and the low half to hold the other.
//
// NOTE: it's fine to use struct pair{uint16_t x; uint16_t y;};
// so long as the structure fits in 4B (we could make this wider, but
// don't go crazy and beware of issues with ragged union sizes).
//
// **: SFID is a littly wonky here since in <=GEN11 it is part of the
// send extended descriptor.  Hence, parsing and decoding have to get
// it in different parts of their respective algorithms.
struct Subfunction {
  union {
    InvalidFC invalid;   // for operations without a subfunction
    BranchCtrl branch;   // for if/else/goto on GEN8+
    MathFC math;         // op == Op::MATH
    SFID send;           // op == Op::SEND*  (**)
    SyncFC sync;         // op == Op::SYNC
    BfnFC bfn;           // BFN subfunction index (low 8 bits are relevent)
    DpasFC dpas;         // op == Op::DPAS || op == Op::DPASW
    ShuffleFC shuffle;   // shuffle function (up, upz, dn, dnz, id, idx4)
    LfsrFC    lfsr;      // lfsr function control
    DnsclFC   dnscl;     // dnscl function control
    // NOTE: this does *not* correspond to any direct encoding
    // since that can vary from platform to platform.
    uint32_t bits;
  };
  constexpr Subfunction() : Subfunction(InvalidFC::INVALID) {}
  constexpr Subfunction(InvalidFC sf) : invalid(sf) {}
  constexpr Subfunction(BranchCtrl sf) : branch(sf) {}
  constexpr Subfunction(MathFC sf) : math(sf) {}
  constexpr Subfunction(SFID sf) : send(sf) {}
  constexpr Subfunction(SyncFC sf) : sync(sf) {}
  constexpr Subfunction(BfnFC bf) : bfn(bf) {}
  constexpr Subfunction(DpasFC sf) : dpas(sf) {}
  constexpr Subfunction(ShuffleFC sf) : shuffle(sf) {}
  constexpr Subfunction(LfsrFC sf) : lfsr(sf) {}
  constexpr Subfunction(DnsclFC sf) : dnscl(sf) {}

  bool isValid() const { return invalid != InvalidFC::INVALID; }
};
static_assert(sizeof(Subfunction) == 4, "Subfunction should be 4B");
} // namespace iga
#endif // _IGA_BXML_TYPES_HPP
