summaryrefslogtreecommitdiffstats
path: root/js/src/jit/Registers.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/jit/Registers.h
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit/Registers.h')
-rw-r--r--js/src/jit/Registers.h311
1 files changed, 311 insertions, 0 deletions
diff --git a/js/src/jit/Registers.h b/js/src/jit/Registers.h
new file mode 100644
index 0000000000..f17f4d5d9c
--- /dev/null
+++ b/js/src/jit/Registers.h
@@ -0,0 +1,311 @@
+/* -*- 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_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() = default;
+
+ 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); }
+};
+
+// 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();
+ }
+};
+
+// Information needed to recover machine register state. This records the
+// location of spilled register and not the content of the spilled
+// registers. Thus we can safely assume that this structure is unchanged, even
+// if the GC pointers mapped by this structure are relocated.
+class MachineState {
+ mozilla::Array<Registers::RegisterContent*, Registers::Total> regs_;
+ mozilla::Array<FloatRegisters::RegisterContent*, FloatRegisters::Total>
+ fpregs_;
+
+ public:
+ MachineState() {
+#ifndef JS_CODEGEN_NONE
+ for (uintptr_t i = 0; i < Registers::Total; i++) {
+ regs_[i] = reinterpret_cast<Registers::RegisterContent*>(i + 0x100);
+ }
+ for (uintptr_t i = 0; i < FloatRegisters::Total; i++) {
+ fpregs_[i] =
+ reinterpret_cast<FloatRegisters::RegisterContent*>(i + 0x200);
+ }
+#endif
+ }
+
+ static MachineState FromBailout(RegisterDump::GPRArray& regs,
+ RegisterDump::FPUArray& fpregs);
+
+ void setRegisterLocation(Register reg, uintptr_t* up) {
+ regs_[reg.code()] = (Registers::RegisterContent*)up;
+ }
+ void setRegisterLocation(FloatRegister reg, float* fp) {
+ MOZ_ASSERT(reg.isSingle());
+ fpregs_[reg.code()] = (FloatRegisters::RegisterContent*)fp;
+ }
+ void setRegisterLocation(FloatRegister reg, double* dp) {
+ fpregs_[reg.code()] = (FloatRegisters::RegisterContent*)dp;
+ }
+ void setRegisterLocation(FloatRegister reg,
+ FloatRegisters::RegisterContent* rp) {
+ fpregs_[reg.code()] = rp;
+ }
+
+ bool has(Register reg) const { return regs_[reg.code()] != nullptr; }
+ bool has(FloatRegister reg) const { return fpregs_[reg.code()] != nullptr; }
+ uintptr_t read(Register reg) const { return regs_[reg.code()]->r; }
+ double read(FloatRegister reg) const { return fpregs_[reg.code()]->d; }
+ void write(Register reg, uintptr_t value) const {
+ regs_[reg.code()]->r = value;
+ }
+ const Registers::RegisterContent* address(Register reg) const {
+ return regs_[reg.code()];
+ }
+ const FloatRegisters::RegisterContent* address(FloatRegister reg) const {
+ return fpregs_[reg.code()];
+ }
+};
+
+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 */