summaryrefslogtreecommitdiffstats
path: root/js/src/jit/mips32/Architecture-mips32.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/mips32/Architecture-mips32.h')
-rw-r--r--js/src/jit/mips32/Architecture-mips32.h286
1 files changed, 286 insertions, 0 deletions
diff --git a/js/src/jit/mips32/Architecture-mips32.h b/js/src/jit/mips32/Architecture-mips32.h
new file mode 100644
index 0000000000..b8b3869adc
--- /dev/null
+++ b/js/src/jit/mips32/Architecture-mips32.h
@@ -0,0 +1,286 @@
+/* -*- 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_mips32_Architecture_mips32_h
+#define jit_mips32_Architecture_mips32_h
+
+#include "mozilla/EndianUtils.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 {
+
+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.
+#if MOZ_LITTLE_ENDIAN()
+static const int32_t NUNBOX32_TYPE_OFFSET = 4;
+static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0;
+#else
+static const int32_t NUNBOX32_TYPE_OFFSET = 0;
+static const int32_t NUNBOX32_PAYLOAD_OFFSET = 4;
+#endif
+
+// 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 modes:
+// - FR=0 mode/ 32-bit FPRs - Historical default, 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). Loongson3A FPU running in this mode doesn't allow
+// use of odd registers for single precision arithmetic.
+// - FR=1 mode/ 64-bit FPRs - In this case, there are 32 double precision
+// register which can also be used as single precision registers. More info
+// https://dmz-portal.imgtec.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking
+
+// Currently we enable 16 even single precision registers which can be also can
+// be used as double precision registers. It enables jit code to run even on
+// Loongson3A. It does not support FR=1 mode because MacroAssembler threats odd
+// single precision registers as high parts of even double precision registers.
+#ifdef __mips_fpr
+static_assert(__mips_fpr == 32, "MIPS32 jit only supports FR=0 fpu mode.");
+#endif
+
+class FloatRegisters : public FloatRegistersMIPSShared {
+ public:
+ static const char* GetName(uint32_t i) {
+ MOZ_ASSERT(i < RegisterIdLimit);
+ return FloatRegistersMIPSShared::GetName(Encoding(i % 32));
+ }
+
+ static Encoding FromName(const char* name);
+
+ static const uint32_t Total = 32;
+ static const uint32_t TotalDouble = 16;
+ static const uint32_t TotalSingle = 16;
+
+ static const uint32_t Allocatable = 30;
+ static const SetType AllSingleMask = (1ULL << TotalSingle) - 1;
+
+ static const SetType AllDoubleMask = ((1ULL << TotalDouble) - 1)
+ << TotalSingle;
+ static const SetType AllMask = AllDoubleMask | AllSingleMask;
+
+ // When saving all registers we only need to do is save double registers.
+ static const uint32_t TotalPhys = 16;
+ static const uint32_t RegisterIdLimit = 32;
+
+ static_assert(sizeof(SetType) * 8 >= Total,
+ "SetType should be large enough to enumerate all registers.");
+
+ static const SetType NonVolatileMask =
+ ((SetType(1) << (FloatRegisters::f20 >> 1)) |
+ (SetType(1) << (FloatRegisters::f22 >> 1)) |
+ (SetType(1) << (FloatRegisters::f24 >> 1)) |
+ (SetType(1) << (FloatRegisters::f26 >> 1)) |
+ (SetType(1) << (FloatRegisters::f28 >> 1)) |
+ (SetType(1) << (FloatRegisters::f30 >> 1))) *
+ ((1 << TotalSingle) + 1);
+
+ static const SetType VolatileMask = AllMask & ~NonVolatileMask;
+
+ static const SetType WrapperMask = VolatileMask;
+
+ static const SetType NonAllocatableMask =
+ (SetType(1) << (FloatRegisters::f18 >> 1)) * ((1 << TotalSingle) + 1);
+
+ 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;
+
+ Encoding code_ : 6;
+
+ protected:
+ RegType kind_ : 1;
+
+ public:
+ constexpr FloatRegister(uint32_t code, RegType kind = Double)
+ : code_(Encoding(code)), kind_(kind) {}
+ constexpr FloatRegister()
+ : 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; }
+ size_t pushSize() const { return size(); }
+
+ bool isNotOdd() const { return !isInvalid() && ((code_ & 1) == 0); }
+
+ bool isSingle() const { return kind_ == Single; }
+ bool isDouble() const { return kind_ == Double; }
+ bool isInvalid() const { return code_ == FloatRegisters::invalid_freg; }
+ bool isSimd128() const { return false; }
+
+ FloatRegister doubleOverlay() const;
+ FloatRegister singleOverlay() const;
+
+ FloatRegister asSingle() const { return singleOverlay(); }
+ FloatRegister asDouble() const { return doubleOverlay(); }
+ FloatRegister asSimd128() const { MOZ_CRASH("NYI"); }
+
+ Code code() const {
+ MOZ_ASSERT(isNotOdd());
+ return Code((code_ >> 1) | (kind_ << 4));
+ }
+ Encoding encoding() const {
+ MOZ_ASSERT(!isInvalid());
+ return code_;
+ }
+ uint32_t id() const {
+ MOZ_ASSERT(!isInvalid());
+ return code_;
+ }
+ static FloatRegister FromCode(uint32_t i) {
+ uint32_t code = i & 15;
+ uint32_t kind = i >> 4;
+ return FloatRegister(Encoding(code << 1), RegType(kind));
+ }
+
+ static FloatRegister FromIndex(uint32_t index, RegType kind) {
+ MOZ_ASSERT(index < 16);
+ return FloatRegister(Encoding(index << 1), kind);
+ }
+
+ bool volatile_() const {
+ return !!((SetType(1) << code()) & 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) {
+ MOZ_ASSERT(isNotOdd());
+ return code_ == other.code_;
+ }
+ uint32_t numAliased() const {
+ MOZ_ASSERT(isNotOdd());
+ return 2;
+ }
+ FloatRegister aliased(uint32_t aliasIdx) {
+ MOZ_ASSERT(isNotOdd());
+
+ if (aliasIdx == 0) {
+ return *this;
+ }
+ MOZ_ASSERT(aliasIdx == 1);
+ if (isDouble()) {
+ return singleOverlay();
+ }
+ return doubleOverlay();
+ }
+ uint32_t numAlignedAliased() const {
+ MOZ_ASSERT(isNotOdd());
+ return 2;
+ }
+ FloatRegister alignedAliased(uint32_t aliasIdx) {
+ MOZ_ASSERT(isNotOdd());
+
+ if (aliasIdx == 0) {
+ return *this;
+ }
+ MOZ_ASSERT(aliasIdx == 1);
+ if (isDouble()) {
+ return singleOverlay();
+ }
+ return doubleOverlay();
+ }
+
+ SetType alignedOrDominatedAliasedSet() const {
+ MOZ_ASSERT(isNotOdd());
+ return (SetType(1) << (code_ >> 1)) *
+ ((1 << FloatRegisters::TotalSingle) + 1);
+ }
+
+ static constexpr RegTypeName DefaultType = RegTypeName::Float64;
+
+ 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 LiveAsIndexableSet<Name>(s);
+ }
+
+ 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();
+};
+
+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;
+}
+
+template <>
+inline FloatRegister::SetType
+FloatRegister::AllocatableAsIndexableSet<RegTypeName::Float32>(SetType set) {
+ // Single registers are not dominating any smaller registers, thus masking
+ // is enough to convert an allocatable set into a set of register list all
+ // single register available.
+ return set & FloatRegisters::AllSingleMask;
+}
+
+template <>
+inline FloatRegister::SetType
+FloatRegister::AllocatableAsIndexableSet<RegTypeName::Float64>(SetType set) {
+ return set & FloatRegisters::AllDoubleMask;
+}
+
+// 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 */