// [AsmJit]
// Machine Code Generation for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.

#ifndef _ASMJIT_CORE_RADEFS_P_H
#define _ASMJIT_CORE_RADEFS_P_H

#include "../core/build.h"
#ifndef ASMJIT_NO_COMPILER

#include "../core/compiler.h"
#include "../core/logging.h"
#include "../core/support.h"
#include "../core/zone.h"
#include "../core/zonevector.h"

ASMJIT_BEGIN_NAMESPACE

//! \cond INTERNAL
//! \addtogroup asmjit_ra
//! \{

// ============================================================================
// [Logging]
// ============================================================================

#ifndef ASMJIT_NO_LOGGING
# define ASMJIT_RA_LOG_FORMAT(...)  \
  do {                              \
    if (logger)                     \
      logger->logf(__VA_ARGS__);    \
  } while (0)
# define ASMJIT_RA_LOG_COMPLEX(...) \
  do {                              \
    if (logger) {                   \
      __VA_ARGS__                   \
    }                               \
  } while (0)
#else
# define ASMJIT_RA_LOG_FORMAT(...) ((void)0)
# define ASMJIT_RA_LOG_COMPLEX(...) ((void)0)
#endif

// ============================================================================
// [Forward Declarations]
// ============================================================================

class RAPass;
class RABlock;
struct RAStackSlot;

typedef ZoneVector<RABlock*> RABlocks;
typedef ZoneVector<RAWorkReg*> RAWorkRegs;

// ============================================================================
// [asmjit::RAStrategy]
// ============================================================================

struct RAStrategy {
  uint8_t _type;

  enum StrategyType : uint32_t {
    kStrategySimple  = 0,
    kStrategyComplex = 1
  };

  inline RAStrategy() noexcept { reset(); }
  inline void reset() noexcept { memset(this, 0, sizeof(*this)); }

  inline uint32_t type() const noexcept { return _type; }
  inline void setType(uint32_t type) noexcept { _type = uint8_t(type); }

  inline bool isSimple() const noexcept { return _type == kStrategySimple; }
  inline bool isComplex() const noexcept { return _type >= kStrategyComplex; }
};

// ============================================================================
// [asmjit::RAArchTraits]
// ============================================================================

//! Traits.
struct RAArchTraits {
  enum Flags : uint32_t {
    //! Registers can be swapped by a single instruction.
    kHasSwap = 0x01u
  };

  uint8_t _flags[BaseReg::kGroupVirt];

  //! \name Construction & Destruction
  //! \{

  inline RAArchTraits() noexcept { reset(); }
  inline void reset() noexcept { memset(_flags, 0, sizeof(_flags)); }

  //! \}

  //! \name Accessors
  //! \{

  inline bool hasFlag(uint32_t group, uint32_t flag) const noexcept { return (_flags[group] & flag) != 0; }
  inline bool hasSwap(uint32_t group) const noexcept { return hasFlag(group, kHasSwap); }

  inline uint8_t& operator[](uint32_t group) noexcept {
    ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
    return _flags[group];
  }

  inline const uint8_t& operator[](uint32_t group) const noexcept {
    ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
    return _flags[group];
  }

  //! \}
};

// ============================================================================
// [asmjit::RARegCount]
// ============================================================================

//! Count of virtual or physical registers per group.
//!
//! \note This class uses 8-bit integers to represent counters, it's only used
//! in places where this is sufficient - for example total count of machine's
//! physical registers, count of virtual registers per instruction, etc. There
//! is also `RALiveCount`, which uses 32-bit integers and is indeed much safer.
struct RARegCount {
  union {
    uint8_t _regs[4];
    uint32_t _packed;
  };

  //! \name Construction & Destruction
  //! \{

  //! Resets all counters to zero.
  inline void reset() noexcept { _packed = 0; }

  //! \}

  //! \name Overloaded Operators
  //! \{

  inline uint8_t& operator[](uint32_t index) noexcept {
    ASMJIT_ASSERT(index < BaseReg::kGroupVirt);
    return _regs[index];
  }

  inline const uint8_t& operator[](uint32_t index) const noexcept {
    ASMJIT_ASSERT(index < BaseReg::kGroupVirt);
    return _regs[index];
  }

  inline RARegCount& operator=(const RARegCount& other) noexcept = default;

  inline bool operator==(const RARegCount& other) const noexcept { return _packed == other._packed; }
  inline bool operator!=(const RARegCount& other) const noexcept { return _packed != other._packed; }

  //! \}

  //! \name Utilities
  //! \{

  //! Returns the count of registers by the given register `group`.
  inline uint32_t get(uint32_t group) const noexcept {
    ASMJIT_ASSERT(group < BaseReg::kGroupVirt);

    uint32_t shift = Support::byteShiftOfDWordStruct(group);
    return (_packed >> shift) & uint32_t(0xFF);
  }

  //! Sets the register count by a register `group`.
  inline void set(uint32_t group, uint32_t n) noexcept {
    ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
    ASMJIT_ASSERT(n <= 0xFF);

    uint32_t shift = Support::byteShiftOfDWordStruct(group);
    _packed = (_packed & ~uint32_t(0xFF << shift)) + (n << shift);
  }

  //! Adds the register count by a register `group`.
  inline void add(uint32_t group, uint32_t n = 1) noexcept {
    ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
    ASMJIT_ASSERT(0xFF - uint32_t(_regs[group]) >= n);

    uint32_t shift = Support::byteShiftOfDWordStruct(group);
    _packed += n << shift;
  }

  //! \}
};

// ============================================================================
// [asmjit::RARegIndex]
// ============================================================================

struct RARegIndex : public RARegCount {
  //! Build register indexes based on the given `count` of registers.
  inline void buildIndexes(const RARegCount& count) noexcept {
    uint32_t x = uint32_t(count._regs[0]);
    uint32_t y = uint32_t(count._regs[1]) + x;
    uint32_t z = uint32_t(count._regs[2]) + y;

    ASMJIT_ASSERT(y <= 0xFF);
    ASMJIT_ASSERT(z <= 0xFF);
    _packed = Support::bytepack32_4x8(0, x, y, z);
  }
};

// ============================================================================
// [asmjit::RARegMask]
// ============================================================================

//! Registers mask.
struct RARegMask {
  uint32_t _masks[BaseReg::kGroupVirt];

  //! \name Construction & Destruction
  //! \{

  inline void init(const RARegMask& other) noexcept {
    for (uint32_t i = 0; i < BaseReg::kGroupVirt; i++)
      _masks[i] = other._masks[i];
  }

  //! Reset all register masks to zero.
  inline void reset() noexcept {
    for (uint32_t i = 0; i < BaseReg::kGroupVirt; i++)
      _masks[i] = 0;
  }

  //! \}

  //! \name Overloaded Operators
  //! \{

  inline RARegMask& operator=(const RARegMask& other) noexcept = default;

  inline bool operator==(const RARegMask& other) const noexcept {
    return _masks[0] == other._masks[0] &&
           _masks[1] == other._masks[1] &&
           _masks[2] == other._masks[2] &&
           _masks[3] == other._masks[3] ;
  }

  inline bool operator!=(const RARegMask& other) const noexcept {
    return !operator==(other);
  }

  inline uint32_t& operator[](uint32_t index) noexcept {
    ASMJIT_ASSERT(index < BaseReg::kGroupVirt);
    return _masks[index];
  }

  inline const uint32_t& operator[](uint32_t index) const noexcept {
    ASMJIT_ASSERT(index < BaseReg::kGroupVirt);
    return _masks[index];
  }

  //! \}

  //! \name Utilities
  //! \{

  //! Tests whether all register masks are zero (empty).
  inline bool empty() const noexcept {
    uint32_t m = 0;
    for (uint32_t i = 0; i < BaseReg::kGroupVirt; i++)
      m |= _masks[i];
    return m == 0;
  }

  inline bool has(uint32_t group, uint32_t mask = 0xFFFFFFFFu) const noexcept {
    ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
    return (_masks[group] & mask) != 0;
  }

  template<class Operator>
  inline void op(const RARegMask& other) noexcept {
    for (uint32_t i = 0; i < BaseReg::kGroupVirt; i++)
      _masks[i] = Operator::op(_masks[i], other._masks[i]);
  }

  template<class Operator>
  inline void op(uint32_t group, uint32_t input) noexcept {
    _masks[group] = Operator::op(_masks[group], input);
  }

  //! \}
};

// ============================================================================
// [asmjit::RARegsStats]
// ============================================================================

//! Information associated with each instruction, propagated to blocks, loops,
//! and the whole function. This information can be used to do minor decisions
//! before the register allocator tries to do its job. For example to use fast
//! register allocation inside a block or loop it cannot have clobbered and/or
//! fixed registers, etc...
struct RARegsStats {
  uint32_t _packed;

  enum Index : uint32_t {
    kIndexUsed       = 0,
    kIndexFixed      = 8,
    kIndexClobbered  = 16
  };

  enum Mask : uint32_t {
    kMaskUsed        = 0xFFu << kIndexUsed,
    kMaskFixed       = 0xFFu << kIndexFixed,
    kMaskClobbered   = 0xFFu << kIndexClobbered
  };

  inline void reset() noexcept { _packed = 0; }
  inline void combineWith(const RARegsStats& other) noexcept { _packed |= other._packed; }

  inline bool hasUsed() const noexcept { return (_packed & kMaskUsed) != 0u; }
  inline bool hasUsed(uint32_t group) const noexcept { return (_packed & Support::bitMask(kIndexUsed + group)) != 0u; }
  inline void makeUsed(uint32_t group) noexcept { _packed |= Support::bitMask(kIndexUsed + group); }

  inline bool hasFixed() const noexcept { return (_packed & kMaskFixed) != 0u; }
  inline bool hasFixed(uint32_t group) const noexcept { return (_packed & Support::bitMask(kIndexFixed + group)) != 0u; }
  inline void makeFixed(uint32_t group) noexcept { _packed |= Support::bitMask(kIndexFixed + group); }

  inline bool hasClobbered() const noexcept { return (_packed & kMaskClobbered) != 0u; }
  inline bool hasClobbered(uint32_t group) const noexcept { return (_packed & Support::bitMask(kIndexClobbered + group)) != 0u; }
  inline void makeClobbered(uint32_t group) noexcept { _packed |= Support::bitMask(kIndexClobbered + group); }
};

// ============================================================================
// [asmjit::RALiveCount]
// ============================================================================

//! Count of live registers, per group.
class RALiveCount {
public:
  uint32_t n[BaseReg::kGroupVirt];

  //! \name Construction & Destruction
  //! \{

  inline RALiveCount() noexcept { reset(); }
  inline RALiveCount(const RALiveCount& other) noexcept = default;

  inline void init(const RALiveCount& other) noexcept {
    for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++)
      n[group] = other.n[group];
  }

  inline void reset() noexcept {
    for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++)
      n[group] = 0;
  }

  //! \}

  //! \name Overloaded Operators
  //! \{

  inline RALiveCount& operator=(const RALiveCount& other) noexcept = default;

  inline uint32_t& operator[](uint32_t group) noexcept { return n[group]; }
  inline const uint32_t& operator[](uint32_t group) const noexcept { return n[group]; }

  //! \}

  //! \name Utilities
  //! \{

  template<class Operator>
  inline void op(const RALiveCount& other) noexcept {
    for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++)
      n[group] = Operator::op(n[group], other.n[group]);
  }

  //! \}
};

// ============================================================================
// [asmjit::LiveInterval]
// ============================================================================

struct LiveInterval {
  uint32_t a, b;

  enum Misc : uint32_t {
    kNaN = 0,
    kInf = 0xFFFFFFFFu
  };

  //! \name Construction & Destruction
  //! \{

  inline LiveInterval() noexcept : a(0), b(0) {}
  inline LiveInterval(uint32_t a, uint32_t b) noexcept : a(a), b(b) {}
  inline LiveInterval(const LiveInterval& other) noexcept : a(other.a), b(other.b) {}

  inline void init(uint32_t aVal, uint32_t bVal) noexcept {
    a = aVal;
    b = bVal;
  }
  inline void init(const LiveInterval& other) noexcept { init(other.a, other.b); }
  inline void reset() noexcept { init(0, 0); }

  //! \}

  //! \name Overloaded Operators
  //! \{

  inline LiveInterval& operator=(const LiveInterval& other) = default;

  //! \}

  //! \name Accessors
  //! \{

  inline bool isValid() const noexcept { return a < b; }
  inline uint32_t width() const noexcept { return b - a; }

  //! \}
};

// ============================================================================
// [asmjit::RALiveSpan<T>]
// ============================================================================

template<typename T>
class RALiveSpan : public LiveInterval, public T {
public:
  typedef T DataType;

  //! \name Construction & Destruction
  //! \{

  inline RALiveSpan() noexcept : LiveInterval(), T() {}
  inline RALiveSpan(const RALiveSpan<T>& other) noexcept : LiveInterval(other), T() {}
  inline RALiveSpan(const LiveInterval& interval, const T& data) noexcept : LiveInterval(interval), T(data) {}
  inline RALiveSpan(uint32_t a, uint32_t b) noexcept : LiveInterval(a, b), T() {}
  inline RALiveSpan(uint32_t a, uint32_t b, const T& data) noexcept : LiveInterval(a, b), T(data) {}

  inline void init(const RALiveSpan<T>& other) noexcept {
    LiveInterval::init(static_cast<const LiveInterval&>(other));
    T::init(static_cast<const T&>(other));
  }

  inline void init(const RALiveSpan<T>& span, const T& data) noexcept {
    LiveInterval::init(static_cast<const LiveInterval&>(span));
    T::init(data);
  }

  inline void init(const LiveInterval& interval, const T& data) noexcept {
    LiveInterval::init(interval);
    T::init(data);
  }

  //! \}

  //! \name Overloaded Operators
  //! \{

  inline RALiveSpan& operator=(const RALiveSpan& other) = default;

  //! \}
};

// ============================================================================
// [asmjit::RALiveSpans<T>]
// ============================================================================

template<typename T>
class RALiveSpans {
public:
  ASMJIT_NONCOPYABLE(RALiveSpans<T>)

  typedef typename T::DataType DataType;
  ZoneVector<T> _data;

  //! \name Construction & Destruction
  //! \{

  inline RALiveSpans() noexcept : _data() {}

  inline void reset() noexcept { _data.reset(); }
  inline void release(ZoneAllocator* allocator) noexcept { _data.release(allocator); }

  //! \}

  //! \name Accessors
  //! \{

  inline bool empty() const noexcept { return _data.empty(); }
  inline uint32_t size() const noexcept { return _data.size(); }

  inline T* data() noexcept { return _data.data(); }
  inline const T* data() const noexcept { return _data.data(); }

  inline bool isOpen() const noexcept {
    uint32_t size = _data.size();
    return size > 0 && _data[size - 1].b == LiveInterval::kInf;
  }

  //! \}

  //! \name Utilities
  //! \{

  inline void swap(RALiveSpans<T>& other) noexcept { _data.swap(other._data); }

  //! Open the current live span.
  ASMJIT_INLINE Error openAt(ZoneAllocator* allocator, uint32_t start, uint32_t end) noexcept {
    bool wasOpen;
    return openAt(allocator, start, end, wasOpen);
  }

  ASMJIT_INLINE Error openAt(ZoneAllocator* allocator, uint32_t start, uint32_t end, bool& wasOpen) noexcept {
    uint32_t size = _data.size();
    wasOpen = false;

    if (size > 0) {
      T& last = _data[size - 1];
      if (last.b >= start) {
        wasOpen = last.b > start;
        last.b = end;
        return kErrorOk;
      }
    }

    return _data.append(allocator, T(start, end));
  }

  inline void closeAt(uint32_t end) noexcept {
    ASMJIT_ASSERT(!empty());

    uint32_t size = _data.size();
    _data[size - 1].b = end;
  }

  //! Returns the sum of width of all spans.
  //!
  //! \note Don't overuse, this iterates over all spans so it's O(N).
  //! It should be only called once and then cached.
  ASMJIT_INLINE uint32_t width() const noexcept {
    uint32_t width = 0;
    for (const T& span : _data)
      width += span.width();
    return width;
  }

  inline T& operator[](uint32_t index) noexcept { return _data[index]; }
  inline const T& operator[](uint32_t index) const noexcept { return _data[index]; }

  inline bool intersects(const RALiveSpans<T>& other) const noexcept {
    return intersects(*this, other);
  }

  ASMJIT_INLINE Error nonOverlappingUnionOf(ZoneAllocator* allocator, const RALiveSpans<T>& x, const RALiveSpans<T>& y, const DataType& yData) noexcept {
    uint32_t finalSize = x.size() + y.size();
    ASMJIT_PROPAGATE(_data.reserve(allocator, finalSize));

    T* dstPtr = _data.data();
    const T* xSpan = x.data();
    const T* ySpan = y.data();

    const T* xEnd = xSpan + x.size();
    const T* yEnd = ySpan + y.size();

    // Loop until we have intersection or either `xSpan == xEnd` or `ySpan == yEnd`,
    // which means that there is no intersection. We advance either `xSpan` or `ySpan`
    // depending on their ranges.
    if (xSpan != xEnd && ySpan != yEnd) {
      uint32_t xa, ya;
      xa = xSpan->a;
      for (;;) {
        while (ySpan->b <= xa) {
          dstPtr->init(*ySpan, yData);
          dstPtr++;
          if (++ySpan == yEnd)
            goto Done;
        }

        ya = ySpan->a;
        while (xSpan->b <= ya) {
          *dstPtr++ = *xSpan;
          if (++xSpan == xEnd)
            goto Done;
        }

        // We know that `xSpan->b > ySpan->a`, so check if `ySpan->b > xSpan->a`.
        xa = xSpan->a;
        if (ySpan->b > xa)
          return 0xFFFFFFFFu;
      }
    }

  Done:
    while (xSpan != xEnd) {
      *dstPtr++ = *xSpan++;
    }

    while (ySpan != yEnd) {
      dstPtr->init(*ySpan, yData);
      dstPtr++;
      ySpan++;
    }

    _data._setEndPtr(dstPtr);
    return kErrorOk;
  }

  static ASMJIT_INLINE bool intersects(const RALiveSpans<T>& x, const RALiveSpans<T>& y) noexcept {
    const T* xSpan = x.data();
    const T* ySpan = y.data();

    const T* xEnd = xSpan + x.size();
    const T* yEnd = ySpan + y.size();

    // Loop until we have intersection or either `xSpan == xEnd` or `ySpan == yEnd`,
    // which means that there is no intersection. We advance either `xSpan` or `ySpan`
    // depending on their end positions.
    if (xSpan == xEnd || ySpan == yEnd)
      return false;

    uint32_t xa, ya;
    xa = xSpan->a;

    for (;;) {
      while (ySpan->b <= xa)
        if (++ySpan == yEnd)
          return false;

      ya = ySpan->a;
      while (xSpan->b <= ya)
        if (++xSpan == xEnd)
          return false;

      // We know that `xSpan->b > ySpan->a`, so check if `ySpan->b > xSpan->a`.
      xa = xSpan->a;
      if (ySpan->b > xa)
        return true;
    }
  }

  //! \}
};

// ============================================================================
// [asmjit::RALiveStats]
// ============================================================================

//! Statistics about a register liveness.
class RALiveStats {
public:
  uint32_t _width;
  float _freq;
  float _priority;

  //! \name Construction & Destruction
  //! \{

  inline RALiveStats()
    : _width(0),
      _freq(0.0f),
      _priority(0.0f) {}

  //! \}

  //! \name Accessors
  //! \{

  inline uint32_t width() const noexcept { return _width; }
  inline float freq() const noexcept { return _freq; }
  inline float priority() const noexcept { return _priority; }

  //! \}
};

// ============================================================================
// [asmjit::LiveRegData]
// ============================================================================

struct LiveRegData {
  uint32_t id;

  inline LiveRegData() noexcept : id(BaseReg::kIdBad) {}
  inline explicit LiveRegData(uint32_t id) noexcept : id(id) {}
  inline explicit LiveRegData(const LiveRegData& other) noexcept : id(other.id) {}

  inline void init(const LiveRegData& other) noexcept { id = other.id; }

  inline bool operator==(const LiveRegData& other) const noexcept { return id == other.id; }
  inline bool operator!=(const LiveRegData& other) const noexcept { return id != other.id; }
};

typedef RALiveSpan<LiveRegData> LiveRegSpan;
typedef RALiveSpans<LiveRegSpan> LiveRegSpans;

// ============================================================================
// [asmjit::RATiedReg]
// ============================================================================

//! Tied register merges one ore more register operand into a single entity. It
//! contains information about its access (Read|Write) and allocation slots
//! (Use|Out) that are used by the register allocator and liveness analysis.
struct RATiedReg {
  //! WorkReg id.
  uint32_t _workId;
  //! Allocation flags.
  uint32_t _flags;
  //! Registers where input {R|X} can be allocated to.
  uint32_t _allocableRegs;
  //! Indexes used to rewrite USE regs.
  uint32_t _useRewriteMask;
  //! Indexes used to rewrite OUT regs.
  uint32_t _outRewriteMask;

  union {
    struct {
      //! How many times the VirtReg is referenced in all operands.
      uint8_t _refCount;
      //! Physical register for use operation (ReadOnly / ReadWrite).
      uint8_t _useId;
      //! Physical register for out operation (WriteOnly).
      uint8_t _outId;
      //! Reserved for future use (padding).
      uint8_t _rmSize;
    };
    //! Packed data.
    uint32_t _packed;
  };

  //! Flags.
  //!
  //! Register access information is encoded in 4 flags in total:
  //!
  //!   - `kRead`  - Register is Read    (ReadWrite if combined with `kWrite`).
  //!   - `kWrite` - Register is Written (ReadWrite if combined with `kRead`).
  //!   - `kUse`   - Encoded as Read or ReadWrite.
  //!   - `kOut`   - Encoded as WriteOnly.
  //!
  //! Let's describe all of these on two X86 instructions:
  //!
  //!   - ADD x{R|W|Use},  x{R|Use}              -> {x:R|W|Use            }
  //!   - LEA x{  W|Out}, [x{R|Use} + x{R|Out}]  -> {x:R|W|Use|Out        }
  //!   - ADD x{R|W|Use},  y{R|Use}              -> {x:R|W|Use     y:R|Use}
  //!   - LEA x{  W|Out}, [x{R|Use} + y{R|Out}]  -> {x:R|W|Use|Out y:R|Use}
  //!
  //! It should be obvious from the example above how these flags get created.
  //! Each operand contains READ/WRITE information, which is then merged to
  //! RATiedReg's flags. However, we also need to represent the possitility to
  //! use see the operation as two independent operations - USE and OUT, because
  //! the register allocator will first allocate USE registers, and then assign
  //! OUT registers independently of USE registers.
  enum Flags : uint32_t {
    kRead         = OpRWInfo::kRead,     //!< Register is read.
    kWrite        = OpRWInfo::kWrite,    //!< Register is written.
    kRW           = OpRWInfo::kRW,       //!< Register both read and written.

    kUse          = 0x00000100u,         //!< Register has a USE slot (read/rw).
    kOut          = 0x00000200u,         //!< Register has an OUT slot (write-only).
    kUseRM        = 0x00000400u,         //!< Register in USE slot can be patched to memory.
    kOutRM        = 0x00000800u,         //!< Register in OUT slot can be patched to memory.

    kUseFixed     = 0x00001000u,         //!< Register has a fixed USE slot.
    kOutFixed     = 0x00002000u,         //!< Register has a fixed OUT slot.
    kUseDone      = 0x00004000u,         //!< Register USE slot has been allocated.
    kOutDone      = 0x00008000u,         //!< Register OUT slot has been allocated.

    kDuplicate    = 0x00010000u,         //!< Register must be duplicated (function call only).
    kLast         = 0x00020000u,         //!< Last occurrence of this VirtReg in basic block.
    kKill         = 0x00040000u,         //!< Kill this VirtReg after use.

    // Architecture specific flags are used during RATiedReg building to ensure
    // that architecture-specific constraints are handled properly. These flags
    // are not really needed after RATiedReg[] is built and copied to `RAInst`.

    kX86Gpb       = 0x01000000u          //!< This RATiedReg references GPB-LO or GPB-HI.
  };

  static_assert(kRead  == 0x1, "RATiedReg::kRead flag must be 0x1");
  static_assert(kWrite == 0x2, "RATiedReg::kWrite flag must be 0x2");
  static_assert(kRW    == 0x3, "RATiedReg::kRW combination must be 0x3");

  //! \name Construction & Destruction
  //! \{

  ASMJIT_INLINE void init(uint32_t workId, uint32_t flags, uint32_t allocableRegs, uint32_t useId, uint32_t useRewriteMask, uint32_t outId, uint32_t outRewriteMask, uint32_t rmSize = 0) noexcept {
    _workId = workId;
    _flags = flags;
    _allocableRegs = allocableRegs;
    _useRewriteMask = useRewriteMask;
    _outRewriteMask = outRewriteMask;
    _refCount = 1;
    _useId = uint8_t(useId);
    _outId = uint8_t(outId);
    _rmSize = uint8_t(rmSize);
  }

  //! \}

  //! \name Overloaded Operators
  //! \{

  inline RATiedReg& operator=(const RATiedReg& other) noexcept = default;

  //! \}

  //! \name Accessors
  //! \{

  //! Returns the associated WorkReg id.
  inline uint32_t workId() const noexcept { return _workId; }

  //! Checks if the given `flag` is set, see `Flags`.
  inline bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }

  //! Returns TiedReg flags, see `RATiedReg::Flags`.
  inline uint32_t flags() const noexcept { return _flags; }
  //! Adds tied register flags, see `Flags`.
  inline void addFlags(uint32_t flags) noexcept { _flags |= flags; }

  //! Tests whether the register is read (writes `true` also if it's Read/Write).
  inline bool isRead() const noexcept { return hasFlag(kRead); }
  //! Tests whether the register is written (writes `true` also if it's Read/Write).
  inline bool isWrite() const noexcept { return hasFlag(kWrite); }
  //! Tests whether the register is read only.
  inline bool isReadOnly() const noexcept { return (_flags & kRW) == kRead; }
  //! Tests whether the register is write only.
  inline bool isWriteOnly() const noexcept { return (_flags & kRW) == kWrite; }
  //! Tests whether the register is read and written.
  inline bool isReadWrite() const noexcept { return (_flags & kRW) == kRW; }

  //! Tests whether the tied register has use operand (Read/ReadWrite).
  inline bool isUse() const noexcept { return hasFlag(kUse); }
  //! Tests whether the tied register has out operand (Write).
  inline bool isOut() const noexcept { return hasFlag(kOut); }

  //! Tests whether the USE slot can be patched to memory operand.
  inline bool hasUseRM() const noexcept { return hasFlag(kUseRM); }
  //! Tests whether the OUT slot can be patched to memory operand.
  inline bool hasOutRM() const noexcept { return hasFlag(kOutRM); }

  inline uint32_t rmSize() const noexcept { return _rmSize; }

  inline void makeReadOnly() noexcept {
    _flags = (_flags & ~(kOut | kWrite)) | kUse;
    _useRewriteMask |= _outRewriteMask;
    _outRewriteMask = 0;
  }

  inline void makeWriteOnly() noexcept {
    _flags = (_flags & ~(kUse | kRead)) | kOut;
    _outRewriteMask |= _useRewriteMask;
    _useRewriteMask = 0;
  }

  //! Tests whether the register would duplicate.
  inline bool isDuplicate() const noexcept { return hasFlag(kDuplicate); }

  //! Tests whether the register (and the instruction it's part of) appears last in the basic block.
  inline bool isLast() const noexcept { return hasFlag(kLast); }
  //! Tests whether the register should be killed after USEd and/or OUTed.
  inline bool isKill() const noexcept { return hasFlag(kKill); }

  //! Tests whether the register is OUT or KILL (used internally by local register allocator).
  inline bool isOutOrKill() const noexcept { return hasFlag(kOut | kKill); }

  inline uint32_t allocableRegs() const noexcept { return _allocableRegs; }

  inline uint32_t refCount() const noexcept { return _refCount; }
  inline void addRefCount(uint32_t n = 1) noexcept { _refCount = uint8_t(_refCount + n); }

  //! Tests whether the register must be allocated to a fixed physical register before it's used.
  inline bool hasUseId() const noexcept { return _useId != BaseReg::kIdBad; }
  //! Tests whether the register must be allocated to a fixed physical register before it's written.
  inline bool hasOutId() const noexcept { return _outId != BaseReg::kIdBad; }

  //! Returns a physical register id used for 'use' operation.
  inline uint32_t useId() const noexcept { return _useId; }
  //! Returns a physical register id used for 'out' operation.
  inline uint32_t outId() const noexcept { return _outId; }

  inline uint32_t useRewriteMask() const noexcept { return _useRewriteMask; }
  inline uint32_t outRewriteMask() const noexcept { return _outRewriteMask; }

  //! Sets a physical register used for 'use' operation.
  inline void setUseId(uint32_t index) noexcept { _useId = uint8_t(index); }
  //! Sets a physical register used for 'out' operation.
  inline void setOutId(uint32_t index) noexcept { _outId = uint8_t(index); }

  inline bool isUseDone() const noexcept { return hasFlag(kUseDone); }
  inline bool isOutDone() const noexcept { return hasFlag(kUseDone); }

  inline void markUseDone() noexcept { addFlags(kUseDone); }
  inline void markOutDone() noexcept { addFlags(kUseDone); }

  //! \}
};

// ============================================================================
// [asmjit::RAWorkReg]
// ============================================================================

class RAWorkReg {
public:
  ASMJIT_NONCOPYABLE(RAWorkReg)

  //! RAPass specific ID used during analysis and allocation.
  uint32_t _workId;
  //! Copy of ID used by `VirtReg`.
  uint32_t _virtId;

  //! Permanent association with `VirtReg`.
  VirtReg* _virtReg;
  //! Temporary association with `RATiedReg`.
  RATiedReg* _tiedReg;
  //! Stack slot associated with the register.
  RAStackSlot* _stackSlot;

  //! Copy of a signature used by `VirtReg`.
  RegInfo _info;
  //! RAPass specific flags used during analysis and allocation.
  uint32_t _flags;
  //! IDs of all physical registers this WorkReg has been allocated to.
  uint32_t _allocatedMask;
  //! IDs of all physical registers that are clobbered during the lifetime of
  //! this WorkReg.
  //!
  //! This mask should be updated by `RAPass::buildLiveness()`, because it's
  //! global and should be updated after unreachable code has been removed.
  uint32_t _clobberSurvivalMask;

  //! A byte-mask where each bit represents one valid byte of the register.
  uint64_t _regByteMask;

  //! Argument index (or `kNoArgIndex` if none).
  uint8_t _argIndex;
  //! Global home register ID (if any, assigned by RA).
  uint8_t _homeRegId;
  //! Global hint register ID (provided by RA or user).
  uint8_t _hintRegId;

  //! Live spans of the `VirtReg`.
  LiveRegSpans _liveSpans;
  //! Live statistics.
  RALiveStats _liveStats;

  //! All nodes that read/write this VirtReg/WorkReg.
  ZoneVector<BaseNode*> _refs;
  //! All nodes that write to this VirtReg/WorkReg.
  ZoneVector<BaseNode*> _writes;

  enum Ids : uint32_t {
    kIdNone = 0xFFFFFFFFu
  };

  enum Flags : uint32_t {
    //! Has been coalesced to another WorkReg.
    kFlagCoalesced = 0x00000001u,
    //! Stack slot has to be allocated.
    kFlagStackUsed = 0x00000002u,
    //! Stack allocation is preferred.
    kFlagStackPreferred = 0x00000004u,
    //! Marked for stack argument reassignment.
    kFlagStackArgToStack = 0x00000008u,

    // TODO: Used?
    kFlagDirtyStats       = 0x80000000u
  };

  enum ArgIndex : uint32_t {
    kNoArgIndex = 0xFFu
  };

  //! \name Construction & Destruction
  //! \{

  ASMJIT_INLINE RAWorkReg(VirtReg* vReg, uint32_t workId) noexcept
    : _workId(workId),
      _virtId(vReg->id()),
      _virtReg(vReg),
      _tiedReg(nullptr),
      _stackSlot(nullptr),
      _info(vReg->info()),
      _flags(kFlagDirtyStats),
      _allocatedMask(0),
      _clobberSurvivalMask(0),
      _regByteMask(0),
      _argIndex(kNoArgIndex),
      _homeRegId(BaseReg::kIdBad),
      _hintRegId(BaseReg::kIdBad),
      _liveSpans(),
      _liveStats(),
      _refs() {}

  //! \}

  //! \name Accessors
  //! \{

  inline uint32_t workId() const noexcept { return _workId; }
  inline uint32_t virtId() const noexcept { return _virtId; }

  inline const char* name() const noexcept { return _virtReg->name(); }
  inline uint32_t nameSize() const noexcept { return _virtReg->nameSize(); }

  inline uint32_t typeId() const noexcept { return _virtReg->typeId(); }

  inline bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }
  inline uint32_t flags() const noexcept { return _flags; }
  inline void addFlags(uint32_t flags) noexcept { _flags |= flags; }

  inline bool isStackUsed() const noexcept { return hasFlag(kFlagStackUsed); }
  inline void markStackUsed() noexcept { addFlags(kFlagStackUsed); }

  inline bool isStackPreferred() const noexcept { return hasFlag(kFlagStackPreferred); }
  inline void markStackPreferred() noexcept { addFlags(kFlagStackPreferred); }

  //! Tests whether this RAWorkReg has been coalesced with another one (cannot be used anymore).
  inline bool isCoalesced() const noexcept { return hasFlag(kFlagCoalesced); }

  inline const RegInfo& info() const noexcept { return _info; }
  inline uint32_t group() const noexcept { return _info.group(); }
  inline uint32_t signature() const noexcept { return _info.signature(); }

  inline VirtReg* virtReg() const noexcept { return _virtReg; }

  inline bool hasTiedReg() const noexcept { return _tiedReg != nullptr; }
  inline RATiedReg* tiedReg() const noexcept { return _tiedReg; }
  inline void setTiedReg(RATiedReg* tiedReg) noexcept { _tiedReg = tiedReg; }
  inline void resetTiedReg() noexcept { _tiedReg = nullptr; }

  inline bool hasStackSlot() const noexcept { return _stackSlot != nullptr; }
  inline RAStackSlot* stackSlot() const noexcept { return _stackSlot; }

  inline LiveRegSpans& liveSpans() noexcept { return _liveSpans; }
  inline const LiveRegSpans& liveSpans() const noexcept { return _liveSpans; }

  inline RALiveStats& liveStats() noexcept { return _liveStats; }
  inline const RALiveStats& liveStats() const noexcept { return _liveStats; }

  inline bool hasArgIndex() const noexcept { return _argIndex != kNoArgIndex; }
  inline uint32_t argIndex() const noexcept { return _argIndex; }
  inline void setArgIndex(uint32_t index) noexcept { _argIndex = uint8_t(index); }

  inline bool hasHomeRegId() const noexcept { return _homeRegId != BaseReg::kIdBad; }
  inline uint32_t homeRegId() const noexcept { return _homeRegId; }
  inline void setHomeRegId(uint32_t physId) noexcept { _homeRegId = uint8_t(physId); }

  inline bool hasHintRegId() const noexcept { return _hintRegId != BaseReg::kIdBad; }
  inline uint32_t hintRegId() const noexcept { return _hintRegId; }
  inline void setHintRegId(uint32_t physId) noexcept { _hintRegId = uint8_t(physId); }

  inline uint32_t allocatedMask() const noexcept { return _allocatedMask; }
  inline void addAllocatedMask(uint32_t mask) noexcept { _allocatedMask |= mask; }

  inline uint32_t clobberSurvivalMask() const noexcept { return _clobberSurvivalMask; }
  inline void addClobberSurvivalMask(uint32_t mask) noexcept { _clobberSurvivalMask |= mask; }

  inline uint64_t regByteMask() const noexcept { return _regByteMask; }
  inline void setRegByteMask(uint64_t mask) noexcept { _regByteMask = mask; }

  //! \}
};

//! \}
//! \endcond

ASMJIT_END_NAMESPACE

#endif // !ASMJIT_NO_COMPILER
#endif // _ASMJIT_CORE_RADEFS_P_H
