/* -*- 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 static SetType LiveAsIndexableSet(SetType s) { return SetType(0); } template 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( SetType set) { return set; } template <> inline Register::SetType Register::LiveAsIndexableSet( SetType set) { return set; } template <> inline Register::SetType Register::AllocatableAsIndexableSet( 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 GPRArray; typedef mozilla::Array 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 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 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; using AutoFloatRegisterScope = AutoGenericRegisterScope; } // namespace jit } // namespace js #endif /* jit_Registers_h */