/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * 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_mips32_Architecture_mips32_h
#define jit_mips32_Architecture_mips32_h

#include "mozilla/MathAlgorithms.h"

#include <limits.h>
#include <stdint.h>

#include "jit/mips-shared/Architecture-mips-shared.h"

#include "js/Utility.h"

namespace js {
namespace jit {

// Shadow stack space is not required on MIPS.
static const uint32_t ShadowStackSpace = 4 * sizeof(uintptr_t);

// These offsets are specific to nunboxing, and capture offsets into the
// components of a js::Value.
// Size of MIPS32 general purpose registers is 32 bits.
static const int32_t NUNBOX32_TYPE_OFFSET = 4;
static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0;

// Size of each bailout table entry.
// For MIPS this is 2 instructions relative call.
static const uint32_t BAILOUT_TABLE_ENTRY_SIZE = 2 * sizeof(void*);

// MIPS32 can have two types of floating-point coprocessors:
// - 32 bit floating-point coprocessor - In this case, there are 32 single
// precision registers and pairs of even and odd float registers are used as
// double precision registers. Example: f0 (double) is composed of
// f0 and f1 (single).
// - 64 bit floating-point coprocessor - In this case, there are 32 double
// precision register which can also be used as single precision registers.

// When using O32 ABI, floating-point coprocessor is 32 bit.
// When using N32 ABI, floating-point coprocessor is 64 bit.
class FloatRegisters : public FloatRegistersMIPSShared
{
  public:
    static const char* GetName(uint32_t i) {
        MOZ_ASSERT(i < Total);
        return FloatRegistersMIPSShared::GetName(Code(i % 32));
    }

    static Code FromName(const char* name);

    static const uint32_t Total = 64;
    static const uint32_t TotalDouble = 16;
    static const uint32_t RegisterIdLimit = 32;
    // Workarounds: On Loongson CPU-s the odd FP registers behave differently
    // in fp-32 mode than standard MIPS.
#if defined(_MIPS_ARCH_LOONGSON3A)
    static const uint32_t TotalSingle = 16;
    static const uint32_t Allocatable = 28;
    static const SetType AllSingleMask = 0x55555555ULL;
#else
    static const uint32_t TotalSingle = 32;
    static const uint32_t Allocatable = 42;
    static const SetType AllSingleMask = (1ULL << 32) - 1;
#endif
    // When saving all registers we only need to do is save double registers.
    static const uint32_t TotalPhys = 16;

    static_assert(sizeof(SetType) * 8 >= Total,
                  "SetType should be large enough to enumerate all registers.");

    static const SetType AllDoubleMask = 0x55555555ULL << 32;
    static const SetType AllMask = AllDoubleMask | AllSingleMask;

    static const SetType NonVolatileDoubleMask =
        ((1ULL << FloatRegisters::f20) |
         (1ULL << FloatRegisters::f22) |
         (1ULL << FloatRegisters::f24) |
         (1ULL << FloatRegisters::f26) |
         (1ULL << FloatRegisters::f28) |
         (1ULL << FloatRegisters::f30)) << 32;

    // f20-single and f21-single alias f20-double ...
    static const SetType NonVolatileMask =
        NonVolatileDoubleMask |
        (1ULL << FloatRegisters::f20) |
        (1ULL << FloatRegisters::f21) |
        (1ULL << FloatRegisters::f22) |
        (1ULL << FloatRegisters::f23) |
        (1ULL << FloatRegisters::f24) |
        (1ULL << FloatRegisters::f25) |
        (1ULL << FloatRegisters::f26) |
        (1ULL << FloatRegisters::f27) |
        (1ULL << FloatRegisters::f28) |
        (1ULL << FloatRegisters::f29) |
        (1ULL << FloatRegisters::f30) |
        (1ULL << FloatRegisters::f31);

    static const SetType VolatileMask = AllMask & ~NonVolatileMask;
    static const SetType VolatileDoubleMask = AllDoubleMask & ~NonVolatileDoubleMask;

    static const SetType WrapperMask = VolatileMask;

    static const SetType NonAllocatableDoubleMask =
        ((1ULL << FloatRegisters::f16) |
         (1ULL << FloatRegisters::f18)) << 32;
    // f16-single and f17-single alias f16-double ...
    static const SetType NonAllocatableMask =
        NonAllocatableDoubleMask |
        (1ULL << FloatRegisters::f16) |
        (1ULL << FloatRegisters::f17) |
        (1ULL << FloatRegisters::f18) |
        (1ULL << FloatRegisters::f19);

    // Registers that can be allocated without being saved, generally.
    static const SetType TempMask = VolatileMask & ~NonAllocatableMask;

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

class FloatRegister : public FloatRegisterMIPSShared
{
  public:
    enum RegType {
        Single = 0x0,
        Double = 0x1,
    };

    typedef FloatRegisters Codes;
    typedef Codes::Code Code;
    typedef Codes::Encoding Encoding;

    uint32_t code_ : 6;
  protected:
    RegType kind_ : 1;

  public:
    constexpr FloatRegister(uint32_t code, RegType kind = Double)
      : code_ (Code(code)), kind_(kind)
    { }
    constexpr FloatRegister()
      : code_(Code(FloatRegisters::invalid_freg)), kind_(Double)
    { }

    bool operator==(const FloatRegister& other) const {
        MOZ_ASSERT(!isInvalid());
        MOZ_ASSERT(!other.isInvalid());
        return kind_ == other.kind_ && code_ == other.code_;
    }
    bool equiv(const FloatRegister& other) const { return other.kind_ == kind_; }
    size_t size() const { return (kind_ == Double) ? 8 : 4; }
    bool isInvalid() const {
        return code_ == FloatRegisters::invalid_freg;
    }

    bool isSingle() const { return kind_ == Single; }
    bool isDouble() const { return kind_ == Double; }

    FloatRegister doubleOverlay(unsigned int which = 0) const;
    FloatRegister singleOverlay(unsigned int which = 0) const;
    FloatRegister sintOverlay(unsigned int which = 0) const;
    FloatRegister uintOverlay(unsigned int which = 0) const;

    FloatRegister asSingle() const { return singleOverlay(); }
    FloatRegister asDouble() const { return doubleOverlay(); }
    FloatRegister asSimd128() const { MOZ_CRASH("NYI"); }

    Code code() const {
        MOZ_ASSERT(!isInvalid());
        return Code(code_  | (kind_ << 5));
    }
    Encoding encoding() const {
        MOZ_ASSERT(!isInvalid());
        return Encoding(code_);
    }
    uint32_t id() const {
        return code_;
    }
    static FloatRegister FromCode(uint32_t i) {
        uint32_t code = i & 31;
        uint32_t kind = i >> 5;
        return FloatRegister(code, RegType(kind));
    }
    // This is similar to FromCode except for double registers on O32.
    static FloatRegister FromIndex(uint32_t index, RegType kind) {
#if defined(USES_O32_ABI)
        // Only even FP registers are avaiable for Loongson on O32.
# if defined(_MIPS_ARCH_LOONGSON3A)
        return FloatRegister(index * 2, kind);
# else
        if (kind == Double)
            return FloatRegister(index * 2, kind);
# endif
#endif
        return FloatRegister(index, kind);
    }

    bool volatile_() const {
        if (isDouble())
            return !!((1ULL << code_) & FloatRegisters::VolatileMask);
        return !!((1ULL << (code_ & ~1)) & FloatRegisters::VolatileMask);
    }
    const char* name() const {
        return FloatRegisters::GetName(code_);
    }
    bool operator != (const FloatRegister& other) const {
        return other.kind_ != kind_ || code_ != other.code_;
    }
    bool aliases(const FloatRegister& other) {
        if (kind_ == other.kind_)
            return code_ == other.code_;
        return doubleOverlay() == other.doubleOverlay();
    }
    uint32_t numAliased() const {
        if (isDouble()) {
            MOZ_ASSERT((code_ & 1) == 0);
            return 3;
        }
        return 2;
    }
    void aliased(uint32_t aliasIdx, FloatRegister* ret) {
        if (aliasIdx == 0) {
            *ret = *this;
            return;
        }
        if (isDouble()) {
            MOZ_ASSERT((code_ & 1) == 0);
            MOZ_ASSERT(aliasIdx <= 2);
            *ret = singleOverlay(aliasIdx - 1);
            return;
        }
        MOZ_ASSERT(aliasIdx == 1);
        *ret = doubleOverlay(aliasIdx - 1);
    }
    uint32_t numAlignedAliased() const {
        if (isDouble()) {
            MOZ_ASSERT((code_ & 1) == 0);
            return 2;
        }
        // f1-float32 has 0 other aligned aliases, 1 total.
        // f0-float32 has 1 other aligned alias, 2 total.
        return 2 - (code_ & 1);
    }
    // |        f0-double        |
    // | f0-float32 | f1-float32 |
    // We only push double registers on MIPS. So, if we've stored f0-double
    // we also want to f0-float32 is stored there.
    void alignedAliased(uint32_t aliasIdx, FloatRegister* ret) {
        MOZ_ASSERT(isDouble());
        MOZ_ASSERT((code_ & 1) == 0);
        if (aliasIdx == 0) {
            *ret = *this;
            return;
        }
        MOZ_ASSERT(aliasIdx == 1);
        *ret = singleOverlay(aliasIdx - 1);
    }

    SetType alignedOrDominatedAliasedSet() const {
        if (isSingle())
            return SetType(1) << code_;

        MOZ_ASSERT(isDouble());
        return SetType(0b11) << code_;
    }

    static Code FromName(const char* name) {
        return FloatRegisters::FromName(name);
    }
    static TypedRegisterSet<FloatRegister> ReduceSetForPush(const TypedRegisterSet<FloatRegister>& s);
    static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s);
    uint32_t getRegisterDumpOffsetInBytes();
};

// In order to handle functions such as int(*)(int, double) where the first
// argument is a general purpose register, and the second argument is a floating
// point register, we have to store the double content into 2 general purpose
// registers, namely a2 and a3.
#define JS_CODEGEN_REGISTER_PAIR 1

} // namespace jit
} // namespace js

#endif /* jit_mips32_Architecture_mips32_h */