Bug 1890513: Directly invoke variadic native functions. r=jandem
[gecko.git] / js / src / jit / Registers.h
blob43e94e6bff5864ec8adbeb33183f1f012b64b90c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef jit_Registers_h
8 #define jit_Registers_h
10 #include "mozilla/Array.h"
12 #include "jit/IonTypes.h"
13 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
14 # include "jit/x86-shared/Architecture-x86-shared.h"
15 #elif defined(JS_CODEGEN_ARM)
16 # include "jit/arm/Architecture-arm.h"
17 #elif defined(JS_CODEGEN_ARM64)
18 # include "jit/arm64/Architecture-arm64.h"
19 #elif defined(JS_CODEGEN_MIPS32)
20 # include "jit/mips32/Architecture-mips32.h"
21 #elif defined(JS_CODEGEN_MIPS64)
22 # include "jit/mips64/Architecture-mips64.h"
23 #elif defined(JS_CODEGEN_LOONG64)
24 # include "jit/loong64/Architecture-loong64.h"
25 #elif defined(JS_CODEGEN_RISCV64)
26 # include "jit/riscv64/Architecture-riscv64.h"
27 #elif defined(JS_CODEGEN_WASM32)
28 # include "jit/wasm32/Architecture-wasm32.h"
29 #elif defined(JS_CODEGEN_NONE)
30 # include "jit/none/Architecture-none.h"
31 #else
32 # error "Unknown architecture!"
33 #endif
35 namespace js {
36 namespace jit {
38 struct Register {
39 using Codes = Registers;
40 using Encoding = Codes::Encoding;
41 using Code = Codes::Code;
42 using SetType = Codes::SetType;
44 Encoding reg_;
45 explicit constexpr Register(Encoding e) : reg_(e) {}
46 Register() : reg_(Encoding(Codes::Invalid)) {}
48 static Register FromCode(Code i) {
49 MOZ_ASSERT(i < Registers::Total);
50 Register r{Encoding(i)};
51 return r;
53 static Register FromName(const char* name) {
54 Code code = Registers::FromName(name);
55 Register r{Encoding(code)};
56 return r;
58 constexpr static Register Invalid() {
59 Register r{Encoding(Codes::Invalid)};
60 return r;
62 constexpr Code code() const { return Code(reg_); }
63 Encoding encoding() const {
64 MOZ_ASSERT(Code(reg_) < Registers::Total);
65 return reg_;
67 const char* name() const { return Registers::GetName(code()); }
68 constexpr bool operator==(Register other) const { return reg_ == other.reg_; }
69 constexpr bool operator!=(Register other) const { return reg_ != other.reg_; }
70 bool volatile_() const {
71 return !!((SetType(1) << code()) & Registers::VolatileMask);
73 bool aliases(const Register& other) const { return reg_ == other.reg_; }
74 uint32_t numAliased() const { return 1; }
76 Register aliased(uint32_t aliasIdx) const {
77 MOZ_ASSERT(aliasIdx == 0);
78 return *this;
81 SetType alignedOrDominatedAliasedSet() const { return SetType(1) << code(); }
83 static constexpr RegTypeName DefaultType = RegTypeName::GPR;
85 template <RegTypeName = DefaultType>
86 static SetType LiveAsIndexableSet(SetType s) {
87 return SetType(0);
90 template <RegTypeName Name = DefaultType>
91 static SetType AllocatableAsIndexableSet(SetType s) {
92 static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable");
93 return SetType(0);
96 static uint32_t SetSize(SetType x) { return Codes::SetSize(x); }
97 static uint32_t FirstBit(SetType x) { return Codes::FirstBit(x); }
98 static uint32_t LastBit(SetType x) { return Codes::LastBit(x); }
100 // Returns the offset of |reg| on the stack, assuming all registers in |set|
101 // were pushed in order (e.g. by |PushRegsInMask|). This is computed by
102 // clearing the lower bits (registers that were pushed later).
103 static size_t OffsetOfPushedRegister(SetType set, Register reg) {
104 return sizeof(Codes::RegisterContent) * Codes::SetSize(set >> reg.code());
108 // Architectures where the stack pointer is not a plain register with a standard
109 // register encoding must define JS_HAS_HIDDEN_SP and HiddenSPEncoding.
111 #ifdef JS_HAS_HIDDEN_SP
112 struct RegisterOrSP {
113 // The register code -- but possibly one that cannot be represented as a bit
114 // position in a 32-bit vector.
115 uint32_t code;
117 explicit RegisterOrSP(uint32_t code) : code(code) {}
118 explicit RegisterOrSP(Register r) : code(r.code()) {}
121 static inline bool IsHiddenSP(RegisterOrSP r) {
122 return r.code == HiddenSPEncoding;
125 static inline Register AsRegister(RegisterOrSP r) {
126 MOZ_ASSERT(!IsHiddenSP(r));
127 return Register::FromCode(r.code);
130 static inline Register AsRegister(Register r) { return r; }
132 inline bool operator==(Register r, RegisterOrSP e) {
133 return r.code() == e.code;
136 inline bool operator!=(Register r, RegisterOrSP e) { return !(r == e); }
138 inline bool operator==(RegisterOrSP e, Register r) { return r == e; }
140 inline bool operator!=(RegisterOrSP e, Register r) { return r != e; }
142 inline bool operator==(RegisterOrSP lhs, RegisterOrSP rhs) {
143 return lhs.code == rhs.code;
146 inline bool operator!=(RegisterOrSP lhs, RegisterOrSP rhs) {
147 return !(lhs == rhs);
149 #else
150 // On platforms where there's nothing special about SP, make RegisterOrSP be
151 // just Register, and return false for IsHiddenSP(r) for any r so that we use
152 // "normal" code for handling the SP. This reduces ifdeffery throughout the
153 // jit.
154 using RegisterOrSP = Register;
156 static inline bool IsHiddenSP(RegisterOrSP r) { return false; }
158 static inline Register AsRegister(RegisterOrSP r) { return r; }
159 #endif
161 template <>
162 inline Register::SetType Register::LiveAsIndexableSet<RegTypeName::GPR>(
163 SetType set) {
164 return set;
167 template <>
168 inline Register::SetType Register::LiveAsIndexableSet<RegTypeName::Any>(
169 SetType set) {
170 return set;
173 template <>
174 inline Register::SetType Register::AllocatableAsIndexableSet<RegTypeName::GPR>(
175 SetType set) {
176 return set;
179 #if JS_BITS_PER_WORD == 32
180 // Note, some platform code depends on INT64LOW_OFFSET being zero.
181 static const uint32_t INT64LOW_OFFSET = 0 * sizeof(int32_t);
182 static const uint32_t INT64HIGH_OFFSET = 1 * sizeof(int32_t);
183 #endif
185 struct Register64 {
186 #ifdef JS_PUNBOX64
187 Register reg;
188 #else
189 Register high;
190 Register low;
191 #endif
193 #ifdef JS_PUNBOX64
194 explicit constexpr Register64(Register r) : reg(r) {}
195 constexpr bool operator==(Register64 other) const { return reg == other.reg; }
196 constexpr bool operator!=(Register64 other) const { return reg != other.reg; }
197 Register scratchReg() { return reg; }
198 static Register64 Invalid() { return Register64(Register::Invalid()); }
199 #else
200 constexpr Register64(Register h, Register l) : high(h), low(l) {}
201 constexpr bool operator==(Register64 other) const {
202 return high == other.high && low == other.low;
204 constexpr bool operator!=(Register64 other) const {
205 return high != other.high || low != other.low;
207 Register scratchReg() { return high; }
208 Register secondScratchReg() { return low; }
209 static Register64 Invalid() {
210 return Register64(Register::Invalid(), Register::Invalid());
212 #endif
215 class RegisterDump {
216 public:
217 typedef mozilla::Array<Registers::RegisterContent, Registers::Total> GPRArray;
218 typedef mozilla::Array<FloatRegisters::RegisterContent,
219 FloatRegisters::TotalPhys>
220 FPUArray;
222 protected: // Silence Clang warning.
223 GPRArray regs_;
224 FPUArray fpregs_;
226 public:
227 static size_t offsetOfRegister(Register reg) {
228 return offsetof(RegisterDump, regs_) + reg.code() * sizeof(uintptr_t);
230 static size_t offsetOfRegister(FloatRegister reg) {
231 return offsetof(RegisterDump, fpregs_) + reg.getRegisterDumpOffsetInBytes();
235 // Class for mapping each register to an offset.
236 class RegisterOffsets {
237 mozilla::Array<uint32_t, Registers::Total> offsets_;
239 // Sentinel value representing an uninitialized offset.
240 static constexpr uint32_t InvalidOffset = UINT32_MAX;
242 public:
243 RegisterOffsets() {
244 for (size_t i = 0; i < Registers::Total; i++) {
245 offsets_[i] = InvalidOffset;
249 RegisterOffsets(const RegisterOffsets&) = delete;
250 void operator=(const RegisterOffsets&) = delete;
252 bool hasOffset(Register reg) const {
253 return offsets_[reg.code()] != InvalidOffset;
255 uint32_t getOffset(Register reg) const {
256 MOZ_ASSERT(hasOffset(reg));
257 return offsets_[reg.code()];
259 void setOffset(Register reg, size_t offset) {
260 MOZ_ASSERT(offset < InvalidOffset);
261 offsets_[reg.code()] = uint32_t(offset);
265 class MacroAssembler;
267 // Declares a register as owned within the scope of the object.
268 // In debug mode, owned register state is tracked within the MacroAssembler,
269 // and an assert will fire if ownership is conflicting.
270 // In contrast to ARM64's UseScratchRegisterScope, this class has no overhead
271 // in non-debug builds.
272 template <class RegisterType>
273 struct AutoGenericRegisterScope : public RegisterType {
274 // Prevent MacroAssembler templates from creating copies,
275 // which causes the destructor to fire more than once.
276 AutoGenericRegisterScope(const AutoGenericRegisterScope& other) = delete;
278 #ifdef DEBUG
279 MacroAssembler& masm_;
280 bool released_;
281 explicit AutoGenericRegisterScope(MacroAssembler& masm, RegisterType reg);
282 ~AutoGenericRegisterScope();
283 void release();
284 void reacquire();
285 #else
286 constexpr explicit AutoGenericRegisterScope(MacroAssembler& masm,
287 RegisterType reg)
288 : RegisterType(reg) {}
289 void release() {}
290 void reacquire() {}
291 #endif
294 using AutoRegisterScope = AutoGenericRegisterScope<Register>;
295 using AutoFloatRegisterScope = AutoGenericRegisterScope<FloatRegister>;
297 } // namespace jit
298 } // namespace js
300 #endif /* jit_Registers_h */