/* -*- 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_riscv64_Architecture_riscv64_h
#define jit_riscv64_Architecture_riscv64_h

// JitSpewer.h is included through MacroAssembler implementations for other
// platforms, so include it here to avoid inadvertent build bustage.
#include "mozilla/MathAlgorithms.h"

#include <algorithm>
#include <iterator>

#include "jit/JitSpewer.h"
#include "jit/shared/Architecture-shared.h"
#include "js/Utility.h"

namespace js {
namespace jit {

static const uint32_t SimdMemoryAlignment =
    16;  // Make it 4 to avoid a bunch of div-by-zero warnings

// RISCV64 has 32 64-bit integer registers, x0 though x31.
//  The program counter is not accessible as a register.

// RISCV INT Register Convention:
// Name          Alias          Usage
// x0            zero           hardwired to 0, ignores writes
// x1            ra             return address for calls
// x2            sp             stack pointer
// x3            gp             global pointer
// x4            tp             thread pointer
// x5-x7         t0-t2          temporary register 0
// x8            fp/s0          Callee-saved register 0 or frame pointer
// x9            s1             Callee-saved register 1
// x10-x11       a0-a1          return value or function argument
// x12-x17       a2-a7          function argument 2
// x18-x27       s2-s11         Callee-saved register
// x28-x31       t3-t6          temporary register 3

// RISCV-64 FP Register Convention:
//  Name         Alias           Usage
//  $f0-$f7      $ft0-$ft7       Temporary registers
//  $f8-$f9      $fs0-$fs1       Callee-saved registers
//  $f10-$f11    $fa0-$fa1       Return values
//  $f12-$f17    $fa2-$fa7       Args values
//  $f18-$f27    $fs2-$fs11      Callee-saved registers
//  $f28-$f31    $ft8-$ft11      Temporary registers
class Registers {
 public:
  enum RegisterID {
    x0 = 0,
    x1,
    x2,
    x3,
    x4,
    x5,
    x6,
    x7,
    x8,
    x9,
    x10,
    x11,
    x12,
    x13,
    x14,
    x15,
    x16,
    x17,
    x18,
    x19,
    x20,
    x21,
    x22,
    x23,
    x24,
    x25,
    x26,
    x27,
    x28,
    x29,
    x30,
    x31,
    zero = x0,
    ra = x1,
    sp = x2,
    gp = x3,
    tp = x4,
    t0 = x5,
    t1 = x6,
    t2 = x7,
    fp = x8,
    s1 = x9,
    a0 = x10,
    a1 = x11,
    a2 = x12,
    a3 = x13,
    a4 = x14,
    a5 = x15,
    a6 = x16,
    a7 = x17,
    s2 = x18,
    s3 = x19,
    s4 = x20,
    s5 = x21,
    s6 = x22,
    s7 = x23,
    s8 = x24,
    s9 = x25,
    s10 = x26,
    s11 = x27,
    t3 = x28,
    t4 = x29,
    t5 = x30,
    t6 = x31,
    invalid_reg,
  };
  typedef uint8_t Code;
  typedef RegisterID Encoding;
  union RegisterContent {
    uintptr_t r;
  };

  typedef uint32_t SetType;

  static uint32_t SetSize(SetType x) {
    static_assert(sizeof(SetType) == 4, "SetType must be 32 bits");
    return mozilla::CountPopulation32(x);
  }
  static uint32_t FirstBit(SetType x) {
    return mozilla::CountTrailingZeroes32(x);
  }
  static uint32_t LastBit(SetType x) {
    return 31 - mozilla::CountLeadingZeroes32(x);
  }
  static const char* GetName(uint32_t code) {
    static const char* const Names[] = {
        "zero", "ra", "sp", "gp", "tp",  "t0",  "t1", "t2", "fp", "s1", "a0",
        "a1",   "a2", "a3", "a4", "a5",  "a6",  "a7", "s2", "s3", "s4", "s5",
        "s6",   "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"};
    static_assert(Total == std::size(Names), "Table is the correct size");
    if (code >= Total) {
      return "invalid";
    }
    return Names[code];
  }

  static Code FromName(const char*);

  static const Encoding StackPointer = sp;
  static const Encoding Invalid = invalid_reg;
  static const uint32_t Total = 32;
  static const uint32_t TotalPhys = 32;
  static const uint32_t Allocatable = 24;
  static const SetType NoneMask = 0x0;
  static const SetType AllMask = 0xFFFFFFFF;
  static const SetType ArgRegMask =
      (1 << Registers::a0) | (1 << Registers::a1) | (1 << Registers::a2) |
      (1 << Registers::a3) | (1 << Registers::a4) | (1 << Registers::a5) |
      (1 << Registers::a6) | (1 << Registers::a7);

  static const SetType VolatileMask =
      ArgRegMask | (1 << Registers::t0) | (1 << Registers::t1) |
      (1 << Registers::t2) | (1 << Registers::t3) | (1 << Registers::t4) |
      (1 << Registers::t5) | (1 << Registers::t6);

  // We use this constant to save registers when entering functions. This
  // is why $ra is added here even though it is not "Non Volatile".
  static const SetType NonVolatileMask =
      (1 << Registers::ra) | (1 << Registers::fp) | (1 << Registers::s1) |
      (1 << Registers::s2) | (1 << Registers::s3) | (1 << Registers::s4) |
      (1 << Registers::s5) | (1 << Registers::s6) | (1 << Registers::s7) |
      (1 << Registers::s8) | (1 << Registers::s9) | (1 << Registers::s10) |
      (1 << Registers::s11);

  static const SetType NonAllocatableMask =
      (1 << Registers::zero) |  // Always be zero.
      (1 << Registers::t4) |    // Scratch reg
      (1 << Registers::t5) |    // Scratch reg
      (1 << Registers::t6) |    // Scratch reg or call reg
      (1 << Registers::s11) |   // Scratch reg
      (1 << Registers::ra) | (1 << Registers::tp) | (1 << Registers::sp) |
      (1 << Registers::fp) | (1 << Registers::gp);

  static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;

  // Registers returned from a JS -> JS call.
  static const SetType JSCallMask = (1 << Registers::a2);

  // Registers returned from a JS -> C call.
  static const SetType CallMask = (1 << Registers::a0);

  static const SetType WrapperMask = VolatileMask;
};

// Smallest integer type that can hold a register bitmask.
typedef uint32_t PackedRegisterMask;

class FloatRegisters {
 public:
  enum FPRegisterID {
    f0 = 0,
    f1,
    f2,
    f3,
    f4,
    f5,
    f6,
    f7,
    f8,
    f9,
    f10,
    f11,
    f12,
    f13,
    f14,
    f15,
    f16,
    f17,
    f18,
    f19,
    f20,
    f21,
    f22,
    f23,
    f24,
    f25,
    f26,
    f27,
    f28,
    f29,
    f30,
    f31,
    invalid_reg,
    ft0 = f0,
    ft1 = f1,
    ft2 = f2,
    ft3 = f3,
    ft4 = f4,
    ft5 = f5,
    ft6 = f6,
    ft7 = f7,
    fs0 = f8,
    fs1 = f9,
    fa0 = f10,
    fa1 = f11,
    fa2 = f12,
    fa3 = f13,
    fa4 = f14,
    fa5 = f15,
    fa6 = f16,
    fa7 = f17,
    fs2 = f18,
    fs3 = f19,
    fs4 = f20,
    fs5 = f21,
    fs6 = f22,
    fs7 = f23,
    fs8 = f24,
    fs9 = f25,
    fs10 = f26,
    fs11 = f27,  // Scratch register
    ft8 = f28,
    ft9 = f29,
    ft10 = f30,  // Scratch register
    ft11 = f31
  };

  enum Kind : uint8_t { Double, NumTypes, Single };

  typedef FPRegisterID Code;
  typedef FPRegisterID Encoding;
  union RegisterContent {
    float s;
    double d;
  };

  static const char* GetName(uint32_t code) {
    static const char* const Names[] = {
        "ft0", "ft1", "ft2",  "ft3",  "ft4", "ft5", "ft6",  "ft7",
        "fs0", "fs2", "fa0",  "fa1",  "fa2", "fa3", "fa4",  "fa5",
        "fa6", "fa7", "fs2",  "fs3",  "fs4", "fs5", "fs6",  "fs7",
        "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11"};
    static_assert(TotalPhys == std::size(Names), "Table is the correct size");
    if (code >= Total) {
      return "invalid";
    }
    return Names[code];
  }

  static Code FromName(const char* name);

  typedef uint32_t SetType;

  static const Code Invalid = invalid_reg;
  static const uint32_t Total = 32;
  static const uint32_t TotalPhys = 32;
  static const uint32_t Allocatable = 23;
  static const SetType AllPhysMask = 0xFFFFFFFF;
  static const SetType AllMask = 0xFFFFFFFF;
  static const SetType AllDoubleMask = AllMask;
  // Single values are stored as 64 bits values (NaN-boxed) when pushing them to
  // the stack, we do not require making distinctions between the 2 types, and
  // therefore the masks are overlapping.See The RISC-V Instruction Set Manual
  // for 14.2 NaN Boxing of Narrower Values.
  static const SetType AllSingleMask = AllMask;
  static const SetType NonVolatileMask =
      SetType((1 << FloatRegisters::fs0) | (1 << FloatRegisters::fs1) |
              (1 << FloatRegisters::fs2) | (1 << FloatRegisters::fs3) |
              (1 << FloatRegisters::fs4) | (1 << FloatRegisters::fs5) |
              (1 << FloatRegisters::fs6) | (1 << FloatRegisters::fs7) |
              (1 << FloatRegisters::fs8) | (1 << FloatRegisters::fs9) |
              (1 << FloatRegisters::fs10) | (1 << FloatRegisters::fs11));
  static const SetType VolatileMask = AllMask & ~NonVolatileMask;

  // fs11/ft10 is the scratch register.
  static const SetType NonAllocatableMask =
      SetType((1 << FloatRegisters::fs11) | (1 << FloatRegisters::ft10));

  static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
};

template <typename T>
class TypedRegisterSet;

struct FloatRegister {
 public:
  typedef FloatRegisters Codes;
  typedef Codes::Code Code;
  typedef Codes::Encoding Encoding;
  typedef Codes::SetType SetType;

  static uint32_t SetSize(SetType x) {
    static_assert(sizeof(SetType) == 4, "SetType must be 32 bits");
    x &= FloatRegisters::AllPhysMask;
    return mozilla::CountPopulation32(x);
  }

  static uint32_t FirstBit(SetType x) {
    static_assert(sizeof(SetType) == 4, "SetType");
    return mozilla::CountTrailingZeroes64(x);
  }
  static uint32_t LastBit(SetType x) {
    static_assert(sizeof(SetType) == 4, "SetType");
    return 31 - mozilla::CountLeadingZeroes64(x);
  }

  static FloatRegister FromCode(uint32_t i) {
    uint32_t code = i & 0x1f;
    return FloatRegister(Code(code));
  }
  bool isSimd128() const { return false; }
  bool isInvalid() const { return invalid_; }
  FloatRegister asSingle() const {
    MOZ_ASSERT(!invalid_);
    return FloatRegister(Encoding(encoding_), FloatRegisters::Single);
  }
  FloatRegister asDouble() const {
    MOZ_ASSERT(!invalid_);
    return FloatRegister(Encoding(encoding_), FloatRegisters::Double);
  }
  FloatRegister asSimd128() const { MOZ_CRASH(); }
  constexpr Code code() const {
    MOZ_ASSERT(!invalid_);
    return encoding_;
  }
  Encoding encoding() const { return encoding_; }
  const char* name() const { return FloatRegisters::GetName(code()); }
  bool volatile_() const {
    MOZ_ASSERT(!invalid_);
    return !!((SetType(1) << code()) & FloatRegisters::VolatileMask);
  }
  bool operator!=(FloatRegister other) const { return code() != other.code(); }
  bool operator==(FloatRegister other) const { return code() == other.code(); }
  bool aliases(FloatRegister other) const {
    return other.encoding_ == encoding_;
  }
  uint32_t numAliased() const { return 1; }
  FloatRegister aliased(uint32_t aliasIdx) const {
    MOZ_ASSERT(aliasIdx == 0);
    return *this;
  }
  // Ensure that two floating point registers' types are equivalent.
  bool equiv(FloatRegister other) const {
    MOZ_ASSERT(!invalid_);
    return kind_ == other.kind_;
  }
  constexpr uint32_t size() const {
    MOZ_ASSERT(!invalid_);
    if (kind_ == FloatRegisters::Double) {
      return sizeof(double);
    }
    MOZ_ASSERT(kind_ == FloatRegisters::Single);
    return sizeof(float);
  }
  uint32_t numAlignedAliased() { return numAliased(); }
  FloatRegister alignedAliased(uint32_t aliasIdx) {
    MOZ_ASSERT(aliasIdx < numAliased());
    return aliased(aliasIdx);
  }
  SetType alignedOrDominatedAliasedSet() const { return SetType(1) << code(); }
  static constexpr RegTypeName DefaultType = RegTypeName::Float64;

  template <RegTypeName Name = 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 LiveAsIndexableSet<Name>(s);
  }

  FloatRegister singleOverlay() const;
  FloatRegister doubleOverlay() const;

  static TypedRegisterSet<FloatRegister> ReduceSetForPush(
      const TypedRegisterSet<FloatRegister>& s);

  uint32_t getRegisterDumpOffsetInBytes() {
#ifdef ENABLE_WASM_SIMD
#  error "Needs more careful logic if SIMD is enabled"
#endif

    return code() * sizeof(double);
  }
  static Code FromName(const char* name);

  // This is used in static initializers, so produce a bogus value instead of
  // crashing.
  static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s);

 private:
  typedef Codes::Kind Kind;
  // These fields only hold valid values: an invalid register is always
  // represented as a valid encoding and kind with the invalid_ bit set.
  Encoding encoding_;  // 32 encodings
  Kind kind_;          // Double, Single; more later
  bool invalid_;

 public:
  constexpr FloatRegister(Encoding encoding, Kind kind)
      : encoding_(encoding), kind_(kind), invalid_(false) {
    MOZ_ASSERT(uint32_t(encoding) < Codes::Total);
  }

  constexpr FloatRegister(Encoding encoding)
      : encoding_(encoding), kind_(FloatRegisters::Double), invalid_(false) {
    MOZ_ASSERT(uint32_t(encoding) < Codes::Total);
  }

  constexpr FloatRegister()
      : encoding_(FloatRegisters::invalid_reg),
        kind_(FloatRegisters::Double),
        invalid_(true) {}

  bool isSingle() const {
    MOZ_ASSERT(!invalid_);
    // On riscv64 arch, float register and double register using the same
    // register file.
    return kind_ == FloatRegisters::Single || kind_ == FloatRegisters::Double;
  }
  bool isDouble() const {
    MOZ_ASSERT(!invalid_);
    return kind_ == FloatRegisters::Double;
  }

  Encoding code() { return encoding_; }
};

template <>
inline FloatRegister::SetType
FloatRegister::LiveAsIndexableSet<RegTypeName::Float32>(SetType set) {
  return set & FloatRegisters::AllSingleMask;
}

template <>
inline FloatRegister::SetType
FloatRegister::LiveAsIndexableSet<RegTypeName::Float64>(SetType set) {
  return set & FloatRegisters::AllDoubleMask;
}

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

inline bool hasUnaliasedDouble() { return false; }
inline bool hasMultiAlias() { return false; }

static const uint32_t ShadowStackSpace = 0;
static const uint32_t JumpImmediateRange = INT32_MAX;

#ifdef JS_NUNBOX32
static const int32_t NUNBOX32_TYPE_OFFSET = 4;
static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0;
#endif

static const uint32_t SpillSlotSize =
    std::max(sizeof(Registers::RegisterContent),
             sizeof(FloatRegisters::RegisterContent));

inline uint32_t GetRISCV64Flags() { return 0; }

}  // namespace jit
}  // namespace js

#endif /* jit_riscv64_Architecture_riscv64_h */
