summaryrefslogtreecommitdiffstats
path: root/js/src/jit/Registers.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/Registers.h')
-rw-r--r--js/src/jit/Registers.h299
1 files changed, 299 insertions, 0 deletions
diff --git a/js/src/jit/Registers.h b/js/src/jit/Registers.h
new file mode 100644
index 0000000000..1ae9c1954c
--- /dev/null
+++ b/js/src/jit/Registers.h
@@ -0,0 +1,299 @@
+/* -*- 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_MIPS32)
+# include "jit/mips32/Architecture-mips32.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 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; }
+ static Register64 Invalid() {
+ return Register64(Register::Invalid(), Register::Invalid());
+ }
+#endif
+};
+
+class RegisterDump {
+ public:
+ typedef mozilla::Array<Registers::RegisterContent, Registers::Total> GPRArray;
+ typedef mozilla::Array<FloatRegisters::RegisterContent,
+ FloatRegisters::TotalPhys>
+ FPUArray;
+
+ 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 */