/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jit_Registers_h
#define jit_Registers_h

#include "mozilla/Array.h"

#include "jit/IonTypes.h"
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
#  include "jit/x86-shared/Architecture-x86-shared.h"
#elif defined(JS_CODEGEN_ARM)
#  include "jit/arm/Architecture-arm.h"
#elif defined(JS_CODEGEN_ARM64)
#  include "jit/arm64/Architecture-arm64.h"
#elif defined(JS_CODEGEN_MIPS64)
#  include "jit/mips64/Architecture-mips64.h"
#elif defined(JS_CODEGEN_LOONG64)
#  include "jit/loong64/Architecture-loong64.h"
#elif defined(JS_CODEGEN_RISCV64)
#  include "jit/riscv64/Architecture-riscv64.h"
#elif defined(JS_CODEGEN_WASM32)
#  include "jit/wasm32/Architecture-wasm32.h"
#elif defined(JS_CODEGEN_NONE)
#  include "jit/none/Architecture-none.h"
#else
#  error "Unknown architecture!"
#endif

namespace js {
namespace jit {

struct Register {
  using Codes = Registers;
  using Encoding = Codes::Encoding;
  using Code = Codes::Code;
  using SetType = Codes::SetType;

  Encoding reg_;
  explicit constexpr Register(Encoding e) : reg_(e) {}
  Register() : reg_(Encoding(Codes::Invalid)) {}

  static Register FromCode(Code i) {
    MOZ_ASSERT(i < Registers::Total);
    Register r{Encoding(i)};
    return r;
  }
  static Register FromName(const char* name) {
    Code code = Registers::FromName(name);
    Register r{Encoding(code)};
    return r;
  }
  constexpr static Register Invalid() {
    Register r{Encoding(Codes::Invalid)};
    return r;
  }
  constexpr Code code() const { return Code(reg_); }
  Encoding encoding() const {
    MOZ_ASSERT(Code(reg_) < Registers::Total);
    return reg_;
  }
  const char* name() const { return Registers::GetName(code()); }
  constexpr bool operator==(Register other) const { return reg_ == other.reg_; }
  constexpr bool operator!=(Register other) const { return reg_ != other.reg_; }
  bool volatile_() const {
    return !!((SetType(1) << code()) & Registers::VolatileMask);
  }
  bool aliases(const Register& other) const { return reg_ == other.reg_; }
  uint32_t numAliased() const { return 1; }

  Register aliased(uint32_t aliasIdx) const {
    MOZ_ASSERT(aliasIdx == 0);
    return *this;
  }

  SetType alignedOrDominatedAliasedSet() const { return SetType(1) << code(); }

  static constexpr RegTypeName DefaultType = RegTypeName::GPR;

  template <RegTypeName = DefaultType>
  static SetType LiveAsIndexableSet(SetType s) {
    return SetType(0);
  }

  template <RegTypeName Name = DefaultType>
  static SetType AllocatableAsIndexableSet(SetType s) {
    static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable");
    return SetType(0);
  }

  static uint32_t SetSize(SetType x) { return Codes::SetSize(x); }
  static uint32_t FirstBit(SetType x) { return Codes::FirstBit(x); }
  static uint32_t LastBit(SetType x) { return Codes::LastBit(x); }

  // Returns the offset of |reg| on the stack, assuming all registers in |set|
  // were pushed in order (e.g. by |PushRegsInMask|). This is computed by
  // clearing the lower bits (registers that were pushed later).
  static size_t OffsetOfPushedRegister(SetType set, Register reg) {
    return sizeof(Codes::RegisterContent) * Codes::SetSize(set >> reg.code());
  }
};

// Architectures where the stack pointer is not a plain register with a standard
// register encoding must define JS_HAS_HIDDEN_SP and HiddenSPEncoding.

#ifdef JS_HAS_HIDDEN_SP
struct RegisterOrSP {
  // The register code -- but possibly one that cannot be represented as a bit
  // position in a 32-bit vector.
  uint32_t code;

  explicit RegisterOrSP(uint32_t code) : code(code) {}
  explicit RegisterOrSP(Register r) : code(r.code()) {}
};

static inline bool IsHiddenSP(RegisterOrSP r) {
  return r.code == HiddenSPEncoding;
}

static inline Register AsRegister(RegisterOrSP r) {
  MOZ_ASSERT(!IsHiddenSP(r));
  return Register::FromCode(r.code);
}

static inline Register AsRegister(Register r) { return r; }

inline bool operator==(Register r, RegisterOrSP e) {
  return r.code() == e.code;
}

inline bool operator!=(Register r, RegisterOrSP e) { return !(r == e); }

inline bool operator==(RegisterOrSP e, Register r) { return r == e; }

inline bool operator!=(RegisterOrSP e, Register r) { return r != e; }

inline bool operator==(RegisterOrSP lhs, RegisterOrSP rhs) {
  return lhs.code == rhs.code;
}

inline bool operator!=(RegisterOrSP lhs, RegisterOrSP rhs) {
  return !(lhs == rhs);
}
#else
// On platforms where there's nothing special about SP, make RegisterOrSP be
// just Register, and return false for IsHiddenSP(r) for any r so that we use
// "normal" code for handling the SP.  This reduces ifdeffery throughout the
// jit.
using RegisterOrSP = Register;

static inline bool IsHiddenSP(RegisterOrSP r) { return false; }

static inline Register AsRegister(RegisterOrSP r) { return r; }
#endif

template <>
inline Register::SetType Register::LiveAsIndexableSet<RegTypeName::GPR>(
    SetType set) {
  return set;
}

template <>
inline Register::SetType Register::LiveAsIndexableSet<RegTypeName::Any>(
    SetType set) {
  return set;
}

template <>
inline Register::SetType Register::AllocatableAsIndexableSet<RegTypeName::GPR>(
    SetType set) {
  return set;
}

#if JS_BITS_PER_WORD == 32
// Note, some platform code depends on INT64LOW_OFFSET being zero.
static const uint32_t INT64LOW_OFFSET = 0 * sizeof(int32_t);
static const uint32_t INT64HIGH_OFFSET = 1 * sizeof(int32_t);
#endif

struct Register64 {
#ifdef JS_PUNBOX64
  Register reg;
#else
  Register high;
  Register low;
#endif

#ifdef JS_PUNBOX64
  explicit constexpr Register64(Register r) : reg(r) {}
  constexpr bool operator==(Register64 other) const { return reg == other.reg; }
  constexpr bool operator!=(Register64 other) const { return reg != other.reg; }
  Register scratchReg() { return reg; }
  static constexpr Register64 Invalid() {
    return Register64(Register::Invalid());
  }
#else
  constexpr Register64(Register h, Register l) : high(h), low(l) {}
  constexpr bool operator==(Register64 other) const {
    return high == other.high && low == other.low;
  }
  constexpr bool operator!=(Register64 other) const {
    return high != other.high || low != other.low;
  }
  Register scratchReg() { return high; }
  Register secondScratchReg() { return low; }
  static constexpr Register64 Invalid() {
    return Register64(Register::Invalid(), Register::Invalid());
  }
#endif
};

class RegisterDump {
 public:
  using GPRArray = mozilla::Array<Registers::RegisterContent, Registers::Total>;
  using FPUArray = mozilla::Array<FloatRegisters::RegisterContent,
                                  FloatRegisters::TotalPhys>;

 protected:  // Silence Clang warning.
  GPRArray regs_;
  FPUArray fpregs_;

 public:
  static size_t offsetOfRegister(Register reg) {
    return offsetof(RegisterDump, regs_) + reg.code() * sizeof(uintptr_t);
  }
  static size_t offsetOfRegister(FloatRegister reg) {
    return offsetof(RegisterDump, fpregs_) + reg.getRegisterDumpOffsetInBytes();
  }
};

// Class for mapping each register to an offset.
class RegisterOffsets {
  mozilla::Array<uint32_t, Registers::Total> offsets_;

  // Sentinel value representing an uninitialized offset.
  static constexpr uint32_t InvalidOffset = UINT32_MAX;

 public:
  RegisterOffsets() {
    for (size_t i = 0; i < Registers::Total; i++) {
      offsets_[i] = InvalidOffset;
    }
  }

  RegisterOffsets(const RegisterOffsets&) = delete;
  void operator=(const RegisterOffsets&) = delete;

  bool hasOffset(Register reg) const {
    return offsets_[reg.code()] != InvalidOffset;
  }
  uint32_t getOffset(Register reg) const {
    MOZ_ASSERT(hasOffset(reg));
    return offsets_[reg.code()];
  }
  void setOffset(Register reg, size_t offset) {
    MOZ_ASSERT(offset < InvalidOffset);
    offsets_[reg.code()] = uint32_t(offset);
  }
};

class MacroAssembler;

// Declares a register as owned within the scope of the object.
// In debug mode, owned register state is tracked within the MacroAssembler,
// and an assert will fire if ownership is conflicting.
// In contrast to ARM64's UseScratchRegisterScope, this class has no overhead
// in non-debug builds.
template <class RegisterType>
struct AutoGenericRegisterScope : public RegisterType {
  // Prevent MacroAssembler templates from creating copies,
  // which causes the destructor to fire more than once.
  AutoGenericRegisterScope(const AutoGenericRegisterScope& other) = delete;

#ifdef DEBUG
  MacroAssembler& masm_;
  bool released_;
  explicit AutoGenericRegisterScope(MacroAssembler& masm, RegisterType reg);
  ~AutoGenericRegisterScope();
  void release();
  void reacquire();
#else
  constexpr explicit AutoGenericRegisterScope(MacroAssembler& masm,
                                              RegisterType reg)
      : RegisterType(reg) {}
  void release() {}
  void reacquire() {}
#endif
};

using AutoRegisterScope = AutoGenericRegisterScope<Register>;
using AutoFloatRegisterScope = AutoGenericRegisterScope<FloatRegister>;

}  // namespace jit
}  // namespace js

#endif /* jit_Registers_h */
