/*
 * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#ifndef CPU_X86_REGISTER_X86_HPP
#define CPU_X86_REGISTER_X86_HPP

#include "asm/register.hpp"
#include "runtime/globals.hpp"
#include "utilities/checkedCast.hpp"
#include "utilities/count_leading_zeros.hpp"
#include "utilities/powerOfTwo.hpp"

class VMRegImpl;
typedef VMRegImpl* VMReg;

// The implementation of integer registers for the x86/x64 architectures.
class Register {
private:
  int _encoding;

  constexpr Register(int encoding, bool unused) : _encoding(encoding) {}

public:
  inline friend constexpr Register as_Register(int encoding);

  enum {
    number_of_registers      = LP64_ONLY( 32 ) NOT_LP64( 8 ),
    number_of_byte_registers = LP64_ONLY( 32 ) NOT_LP64( 4 ),
    max_slots_per_register   = LP64_ONLY(  2 ) NOT_LP64( 1 )
  };

  class RegisterImpl: public AbstractRegisterImpl {
    friend class Register;

    static constexpr const RegisterImpl* first();

  public:
    // accessors
    constexpr int   raw_encoding() const { return checked_cast<int>(this - first()); }
    constexpr int       encoding() const { assert(is_valid(), "invalid register"); return raw_encoding(); }
    constexpr bool      is_valid() const { return 0 <= raw_encoding() && raw_encoding() < number_of_registers; }
    bool  has_byte_register() const { return 0 <= raw_encoding() && raw_encoding() < number_of_byte_registers; }

    // derived registers, offsets, and addresses
    inline Register successor() const;

    inline VMReg as_VMReg() const;

    const char* name() const;
  };

  constexpr Register() : _encoding(-1) {} // noreg

  int operator==(const Register r) const { return _encoding == r._encoding; }
  int operator!=(const Register r) const { return _encoding != r._encoding; }

  constexpr const RegisterImpl* operator->() const { return RegisterImpl::first() + _encoding; }

  // Actually available GP registers for use, depending on actual CPU capabilities and flags.
  static int available_gp_registers() {
#ifdef _LP64
    if (!UseAPX) {
      return number_of_registers / 2;
    }
#endif // _LP64
    return number_of_registers;
  }
};

extern const Register::RegisterImpl all_RegisterImpls[Register::number_of_registers + 1] INTERNAL_VISIBILITY;

inline constexpr const Register::RegisterImpl* Register::RegisterImpl::first() {
  return all_RegisterImpls + 1;
}

constexpr Register noreg = Register();

inline constexpr Register as_Register(int encoding) {
  if (0 <= encoding && encoding < Register::number_of_registers) {
    return Register(encoding, false);
  }
  return noreg;
}

inline Register Register::RegisterImpl::successor() const {
  assert(is_valid(), "sanity");
  return as_Register(encoding() + 1);
}

constexpr Register rax = as_Register(0);
constexpr Register rcx = as_Register(1);
constexpr Register rdx = as_Register(2);
constexpr Register rbx = as_Register(3);
constexpr Register rsp = as_Register(4);
constexpr Register rbp = as_Register(5);
constexpr Register rsi = as_Register(6);
constexpr Register rdi = as_Register(7);
#ifdef _LP64
constexpr Register r8  = as_Register( 8);
constexpr Register r9  = as_Register( 9);
constexpr Register r10 = as_Register(10);
constexpr Register r11 = as_Register(11);
constexpr Register r12 = as_Register(12);
constexpr Register r13 = as_Register(13);
constexpr Register r14 = as_Register(14);
constexpr Register r15 = as_Register(15);
constexpr Register r16 = as_Register(16);
constexpr Register r17 = as_Register(17);
constexpr Register r18 = as_Register(18);
constexpr Register r19 = as_Register(19);
constexpr Register r20 = as_Register(20);
constexpr Register r21 = as_Register(21);
constexpr Register r22 = as_Register(22);
constexpr Register r23 = as_Register(23);
constexpr Register r24 = as_Register(24);
constexpr Register r25 = as_Register(25);
constexpr Register r26 = as_Register(26);
constexpr Register r27 = as_Register(27);
constexpr Register r28 = as_Register(28);
constexpr Register r29 = as_Register(29);
constexpr Register r30 = as_Register(30);
constexpr Register r31 = as_Register(31);
#endif // _LP64


// The implementation of x87 floating point registers for the ia32 architecture.
class FloatRegister {
private:
  int _encoding;

  constexpr FloatRegister(int encoding, bool unused) : _encoding(encoding) {}

public:
  inline friend constexpr FloatRegister as_FloatRegister(int encoding);

  enum {
    number_of_registers    = 8,
    max_slots_per_register = 2
  };

  class FloatRegisterImpl: public AbstractRegisterImpl {
    friend class FloatRegister;

    static constexpr const FloatRegisterImpl* first();

  public:
    // accessors
    int   raw_encoding() const { return checked_cast<int>(this - first()); }
    int   encoding() const     { assert(is_valid(), "invalid register"); return raw_encoding(); }
    bool  is_valid() const     { return 0 <= raw_encoding() && raw_encoding() < number_of_registers; }

    // derived registers, offsets, and addresses
    inline FloatRegister successor() const;

    inline VMReg as_VMReg() const;

    const char* name() const;
  };

  constexpr FloatRegister() : _encoding(-1) {} // fnoreg

  int operator==(const FloatRegister r) const { return _encoding == r._encoding; }
  int operator!=(const FloatRegister r) const { return _encoding != r._encoding; }

  const FloatRegisterImpl* operator->() const { return FloatRegisterImpl::first() + _encoding; }
};

extern const FloatRegister::FloatRegisterImpl all_FloatRegisterImpls[FloatRegister::number_of_registers + 1] INTERNAL_VISIBILITY;

inline constexpr const FloatRegister::FloatRegisterImpl* FloatRegister::FloatRegisterImpl::first() {
  return all_FloatRegisterImpls + 1;
}

constexpr FloatRegister fnoreg = FloatRegister();

inline constexpr FloatRegister as_FloatRegister(int encoding) {
  if (0 <= encoding && encoding < FloatRegister::number_of_registers) {
    return FloatRegister(encoding, false);
  }
  return fnoreg;
}

inline FloatRegister FloatRegister::FloatRegisterImpl::successor() const {
  assert(is_valid(), "sanity");
  return as_FloatRegister(encoding() + 1);
}


// The implementation of XMM registers.
class XMMRegister {
private:
  int _encoding;

  constexpr XMMRegister(int encoding, bool unused) : _encoding(encoding) {}

public:
  inline friend constexpr XMMRegister as_XMMRegister(int encoding);

  enum {
    number_of_registers    = LP64_ONLY( 32 ) NOT_LP64(  8 ),
    max_slots_per_register = LP64_ONLY( 16 ) NOT_LP64( 16 )   // 512-bit
  };

  class XMMRegisterImpl: public AbstractRegisterImpl {
    friend class XMMRegister;

    static constexpr const XMMRegisterImpl* first();

  public:
    // accessors
    constexpr int raw_encoding() const { return checked_cast<int>(this - first()); }
    constexpr int     encoding() const { assert(is_valid(), "invalid register"); return raw_encoding(); }
    constexpr bool    is_valid() const { return 0 <= raw_encoding() && raw_encoding() < number_of_registers; }

    // derived registers, offsets, and addresses
    inline XMMRegister successor() const;

    inline VMReg as_VMReg() const;

    const char* name() const;
  };

  constexpr XMMRegister() : _encoding(-1) {} // xnoreg

  int operator==(const XMMRegister r) const { return _encoding == r._encoding; }
  int operator!=(const XMMRegister r) const { return _encoding != r._encoding; }

  constexpr const XMMRegisterImpl* operator->() const { return XMMRegisterImpl::first() + _encoding; }

  // Actually available XMM registers for use, depending on actual CPU capabilities and flags.
  static int available_xmm_registers() {
#ifdef _LP64
    if (UseAVX < 3) {
      return number_of_registers / 2;
    }
#endif // _LP64
    return number_of_registers;
  }
};

extern const XMMRegister::XMMRegisterImpl all_XMMRegisterImpls[XMMRegister::number_of_registers + 1] INTERNAL_VISIBILITY;

inline constexpr const XMMRegister::XMMRegisterImpl* XMMRegister::XMMRegisterImpl::first() {
  return all_XMMRegisterImpls + 1;
}

constexpr XMMRegister xnoreg = XMMRegister();

inline constexpr XMMRegister as_XMMRegister(int encoding) {
  if (0 <= encoding && encoding < XMMRegister::number_of_registers) {
    return XMMRegister(encoding, false);
  }
  return xnoreg;
}

inline XMMRegister XMMRegister::XMMRegisterImpl::successor() const {
  assert(is_valid(), "sanity");
  return as_XMMRegister(encoding() + 1);
}

constexpr XMMRegister xmm0  = as_XMMRegister( 0);
constexpr XMMRegister xmm1  = as_XMMRegister( 1);
constexpr XMMRegister xmm2  = as_XMMRegister( 2);
constexpr XMMRegister xmm3  = as_XMMRegister( 3);
constexpr XMMRegister xmm4  = as_XMMRegister( 4);
constexpr XMMRegister xmm5  = as_XMMRegister( 5);
constexpr XMMRegister xmm6  = as_XMMRegister( 6);
constexpr XMMRegister xmm7  = as_XMMRegister( 7);
#ifdef _LP64
constexpr XMMRegister xmm8  = as_XMMRegister( 8);
constexpr XMMRegister xmm9  = as_XMMRegister( 9);
constexpr XMMRegister xmm10 = as_XMMRegister(10);
constexpr XMMRegister xmm11 = as_XMMRegister(11);
constexpr XMMRegister xmm12 = as_XMMRegister(12);
constexpr XMMRegister xmm13 = as_XMMRegister(13);
constexpr XMMRegister xmm14 = as_XMMRegister(14);
constexpr XMMRegister xmm15 = as_XMMRegister(15);
constexpr XMMRegister xmm16 = as_XMMRegister(16);
constexpr XMMRegister xmm17 = as_XMMRegister(17);
constexpr XMMRegister xmm18 = as_XMMRegister(18);
constexpr XMMRegister xmm19 = as_XMMRegister(19);
constexpr XMMRegister xmm20 = as_XMMRegister(20);
constexpr XMMRegister xmm21 = as_XMMRegister(21);
constexpr XMMRegister xmm22 = as_XMMRegister(22);
constexpr XMMRegister xmm23 = as_XMMRegister(23);
constexpr XMMRegister xmm24 = as_XMMRegister(24);
constexpr XMMRegister xmm25 = as_XMMRegister(25);
constexpr XMMRegister xmm26 = as_XMMRegister(26);
constexpr XMMRegister xmm27 = as_XMMRegister(27);
constexpr XMMRegister xmm28 = as_XMMRegister(28);
constexpr XMMRegister xmm29 = as_XMMRegister(29);
constexpr XMMRegister xmm30 = as_XMMRegister(30);
constexpr XMMRegister xmm31 = as_XMMRegister(31);
#endif // _LP64


// The implementation of AVX-512 opmask registers.
class KRegister {
private:
  int _encoding;

  constexpr KRegister(int encoding, bool unused) : _encoding(encoding) {}

public:
  inline friend constexpr KRegister as_KRegister(int encoding);

  enum {
    number_of_registers = 8,
    // opmask registers are 64bit wide on both 32 and 64 bit targets.
    // thus two slots are reserved per register.
    max_slots_per_register = 2
  };

  class KRegisterImpl: public AbstractRegisterImpl {
    friend class KRegister;

    static constexpr const KRegisterImpl* first();

  public:

    // accessors
    int   raw_encoding() const { return checked_cast<int>(this - first()); }
    int   encoding() const     { assert(is_valid(), "invalid register"); return raw_encoding(); }
    bool  is_valid() const     { return 0 <= raw_encoding() && raw_encoding() < number_of_registers; }

    // derived registers, offsets, and addresses
    inline KRegister successor() const;

    inline VMReg as_VMReg() const;

    const char* name() const;
  };

  constexpr KRegister() : _encoding(-1) {} // knoreg

  int operator==(const KRegister r) const { return _encoding == r._encoding; }
  int operator!=(const KRegister r) const { return _encoding != r._encoding; }

  const KRegisterImpl* operator->() const { return KRegisterImpl::first() + _encoding; }
};

extern const KRegister::KRegisterImpl all_KRegisterImpls[KRegister::number_of_registers + 1] INTERNAL_VISIBILITY;

inline constexpr const KRegister::KRegisterImpl* KRegister::KRegisterImpl::first() {
  return all_KRegisterImpls + 1;
}

constexpr KRegister knoreg = KRegister();

inline constexpr KRegister as_KRegister(int encoding) {
  if (0 <= encoding && encoding < KRegister::number_of_registers) {
    return KRegister(encoding, false);
  }
  return knoreg;
}

inline KRegister KRegister::KRegisterImpl::successor() const {
  assert(is_valid(), "sanity");
  return as_KRegister(encoding() + 1);
}

constexpr KRegister k0 = as_KRegister(0);
constexpr KRegister k1 = as_KRegister(1);
constexpr KRegister k2 = as_KRegister(2);
constexpr KRegister k3 = as_KRegister(3);
constexpr KRegister k4 = as_KRegister(4);
constexpr KRegister k5 = as_KRegister(5);
constexpr KRegister k6 = as_KRegister(6);
constexpr KRegister k7 = as_KRegister(7);


// Need to know the total number of registers of all sorts for SharedInfo.
// Define a class that exports it.
class ConcreteRegisterImpl : public AbstractRegisterImpl {
 public:
  enum {
    max_gpr = Register::number_of_registers * Register::max_slots_per_register,
    max_fpr = max_gpr + FloatRegister::number_of_registers * FloatRegister::max_slots_per_register,
    max_xmm = max_fpr + XMMRegister::number_of_registers * XMMRegister::max_slots_per_register,
    max_kpr = max_xmm + KRegister::number_of_registers * KRegister::max_slots_per_register,

    // A big enough number for C2: all the registers plus flags
    // This number must be large enough to cover REG_COUNT (defined by c2) registers.
    // There is no requirement that any ordering here matches any ordering c2 gives
    // it's optoregs.

    // x86_32.ad defines additional dummy FILL0-FILL7 registers, in order to tally
    // REG_COUNT (computed by ADLC based on the number of reg_defs seen in .ad files)
    // with ConcreteRegisterImpl::number_of_registers additional count of 8 is being
    // added for 32 bit jvm.
    number_of_registers = max_kpr +       // gpr/fpr/xmm/kpr
                          NOT_LP64( 8 + ) // FILL0-FILL7 in x86_32.ad
                          1               // eflags
  };
};

template <>
inline Register AbstractRegSet<Register>::first() {
  if (_bitset == 0) { return noreg; }
  return as_Register(count_trailing_zeros(_bitset));
}

template <>
inline Register AbstractRegSet<Register>::last() {
  if (_bitset == 0) { return noreg; }
  int last = max_size() - 1 - count_leading_zeros(_bitset);
  return as_Register(last);
}

template <>
inline XMMRegister AbstractRegSet<XMMRegister>::first() {
  if (_bitset == 0) { return xnoreg; }
  return as_XMMRegister(count_trailing_zeros(_bitset));
}

template <>
inline XMMRegister AbstractRegSet<XMMRegister>::last() {
  if (_bitset == 0) { return xnoreg; }
  int last = max_size() - 1 - count_leading_zeros(_bitset);
  return as_XMMRegister(last);
}

typedef AbstractRegSet<Register> RegSet;
typedef AbstractRegSet<XMMRegister> XMMRegSet;

#endif // CPU_X86_REGISTER_X86_HPP
