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/. */
6
7 #ifndef jit_x86_shared_Architecture_x86_h
8 #define jit_x86_shared_Architecture_x86_h
9
10 #if !defined(JS_CODEGEN_X86) && !defined(JS_CODEGEN_X64)
11 # error "Unsupported architecture!"
12 #endif
13
14 #include "mozilla/MathAlgorithms.h"
15
16 #include <string.h>
17
18 #include "jit/shared/Architecture-shared.h"
19
20 #include "jit/x86-shared/Constants-x86-shared.h"
21
22 namespace js {
23 namespace jit {
24
25 #if defined(JS_CODEGEN_X86)
26 // These offsets are specific to nunboxing, and capture offsets into the
27 // components of a js::Value.
28 static const int32_t NUNBOX32_TYPE_OFFSET = 4;
29 static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0;
30
31 // Size of each bailout table entry. On x86 this is a 5-byte relative call.
32 static const uint32_t BAILOUT_TABLE_ENTRY_SIZE = 5;
33 #endif
34
35 #if defined(JS_CODEGEN_X64) && defined(_WIN64)
36 static const uint32_t ShadowStackSpace = 32;
37 #else
38 static const uint32_t ShadowStackSpace = 0;
39 #endif
40
41 static const uint32_t JumpImmediateRange = INT32_MAX;
42
43 class Registers {
44 public:
45 using Code = uint8_t;
46 using Encoding = X86Encoding::RegisterID;
47
48 // Content spilled during bailouts.
49 union RegisterContent {
50 uintptr_t r;
51 };
52
53 #if defined(JS_CODEGEN_X86)
54 using SetType = uint8_t;
55
GetName(Code code)56 static const char* GetName(Code code) {
57 return X86Encoding::GPRegName(Encoding(code));
58 }
59
60 static const uint32_t Total = 8;
61 static const uint32_t TotalPhys = 8;
62 static const uint32_t Allocatable = 7;
63
64 #elif defined(JS_CODEGEN_X64)
65 using SetType = uint16_t;
66
GetName(Code code)67 static const char* GetName(Code code) {
68 static const char* const Names[] = {
69 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
70 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"};
71 return Names[code];
72 }
73
74 static const uint32_t Total = 16;
75 static const uint32_t TotalPhys = 16;
76 static const uint32_t Allocatable = 14;
77 #endif
78
SetSize(SetType x)79 static uint32_t SetSize(SetType x) {
80 static_assert(sizeof(SetType) <= 4, "SetType must be, at most, 32 bits");
81 return mozilla::CountPopulation32(x);
82 }
FirstBit(SetType x)83 static uint32_t FirstBit(SetType x) {
84 return mozilla::CountTrailingZeroes32(x);
85 }
LastBit(SetType x)86 static uint32_t LastBit(SetType x) {
87 return 31 - mozilla::CountLeadingZeroes32(x);
88 }
89
FromName(const char * name)90 static Code FromName(const char* name) {
91 for (size_t i = 0; i < Total; i++) {
92 if (strcmp(GetName(Code(i)), name) == 0) {
93 return Code(i);
94 }
95 }
96 return Invalid;
97 }
98
99 static const Encoding StackPointer = X86Encoding::rsp;
100 static const Encoding Invalid = X86Encoding::invalid_reg;
101
102 static const SetType AllMask = (1 << Total) - 1;
103
104 #if defined(JS_CODEGEN_X86)
105 static const SetType ArgRegMask = 0;
106
107 static const SetType VolatileMask = (1 << X86Encoding::rax) |
108 (1 << X86Encoding::rcx) |
109 (1 << X86Encoding::rdx);
110
111 static const SetType WrapperMask = VolatileMask | (1 << X86Encoding::rbx);
112
113 static const SetType SingleByteRegs =
114 (1 << X86Encoding::rax) | (1 << X86Encoding::rcx) |
115 (1 << X86Encoding::rdx) | (1 << X86Encoding::rbx);
116
117 static const SetType NonAllocatableMask = (1 << X86Encoding::rsp);
118
119 // Registers returned from a JS -> JS call.
120 static const SetType JSCallMask =
121 (1 << X86Encoding::rcx) | (1 << X86Encoding::rdx);
122
123 // Registers returned from a JS -> C call.
124 static const SetType CallMask = (1 << X86Encoding::rax);
125
126 #elif defined(JS_CODEGEN_X64)
127 static const SetType ArgRegMask =
128 # if !defined(_WIN64)
129 (1 << X86Encoding::rdi) | (1 << X86Encoding::rsi) |
130 # endif
131 (1 << X86Encoding::rdx) | (1 << X86Encoding::rcx) |
132 (1 << X86Encoding::r8) | (1 << X86Encoding::r9);
133
134 static const SetType VolatileMask = (1 << X86Encoding::rax) | ArgRegMask |
135 (1 << X86Encoding::r10) |
136 (1 << X86Encoding::r11);
137
138 static const SetType WrapperMask = VolatileMask;
139
140 static const SetType SingleByteRegs = AllMask & ~(1 << X86Encoding::rsp);
141
142 static const SetType NonAllocatableMask =
143 (1 << X86Encoding::rsp) | (1 << X86Encoding::r11); // This is ScratchReg.
144
145 // Registers returned from a JS -> JS call.
146 static const SetType JSCallMask = (1 << X86Encoding::rcx);
147
148 // Registers returned from a JS -> C call.
149 static const SetType CallMask = (1 << X86Encoding::rax);
150
151 #endif
152
153 static const SetType NonVolatileMask =
154 AllMask & ~VolatileMask & ~(1 << X86Encoding::rsp);
155
156 static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
157 };
158
159 using PackedRegisterMask = Registers::SetType;
160
161 class FloatRegisters {
162 public:
163 using Encoding = X86Encoding::XMMRegisterID;
164
165 // Observe that there is a Simd128 type on both x86 and x64 whether SIMD is
166 // implemented/enabled or not, and that the RegisterContent union is large
167 // enough for a V128 datum always. Producers and consumers of a register dump
168 // must be aware of this even if they don't need to save/restore values in the
169 // high lanes of the SIMD registers. See the DumpAllRegs() implementations,
170 // for example.
171
172 enum ContentType {
173 Single, // 32-bit float.
174 Double, // 64-bit double.
175 Simd128, // 128-bit Wasm SIMD type.
176 NumTypes
177 };
178
179 // Content spilled during bailouts.
180 union RegisterContent {
181 float s;
182 double d;
183 uint8_t v128[16];
184 };
185
GetName(Encoding code)186 static const char* GetName(Encoding code) {
187 return X86Encoding::XMMRegName(code);
188 }
189
FromName(const char * name)190 static Encoding FromName(const char* name) {
191 for (size_t i = 0; i < Total; i++) {
192 if (strcmp(GetName(Encoding(i)), name) == 0) {
193 return Encoding(i);
194 }
195 }
196 return Invalid;
197 }
198
199 static const Encoding Invalid = X86Encoding::invalid_xmm;
200
201 #if defined(JS_CODEGEN_X86)
202 static const uint32_t Total = 8 * NumTypes;
203 static const uint32_t TotalPhys = 8;
204 static const uint32_t Allocatable = 7;
205 using SetType = uint32_t;
206 #elif defined(JS_CODEGEN_X64)
207 static const uint32_t Total = 16 * NumTypes;
208 static const uint32_t TotalPhys = 16;
209 static const uint32_t Allocatable = 15;
210 using SetType = uint64_t;
211 #endif
212
213 static_assert(sizeof(SetType) * 8 >= Total,
214 "SetType should be large enough to enumerate all registers.");
215
216 // Magic values which are used to duplicate a mask of physical register for
217 // a specific type of register. A multiplication is used to copy and shift
218 // the bits of the physical register mask.
219 static const SetType SpreadSingle = SetType(1)
220 << (uint32_t(Single) * TotalPhys);
221 static const SetType SpreadDouble = SetType(1)
222 << (uint32_t(Double) * TotalPhys);
223 static const SetType SpreadSimd128 = SetType(1)
224 << (uint32_t(Simd128) * TotalPhys);
225 static const SetType SpreadScalar = SpreadSingle | SpreadDouble;
226 static const SetType SpreadVector = SpreadSimd128;
227 static const SetType Spread = SpreadScalar | SpreadVector;
228
229 static const SetType AllPhysMask = ((1 << TotalPhys) - 1);
230 static const SetType AllMask = AllPhysMask * Spread;
231 static const SetType AllDoubleMask = AllPhysMask * SpreadDouble;
232 static const SetType AllSingleMask = AllPhysMask * SpreadSingle;
233 static const SetType AllVector128Mask = AllPhysMask * SpreadSimd128;
234
235 #if defined(JS_CODEGEN_X86)
236 static const SetType NonAllocatableMask =
237 Spread * (1 << X86Encoding::xmm7); // This is ScratchDoubleReg.
238
239 #elif defined(JS_CODEGEN_X64)
240 static const SetType NonAllocatableMask =
241 Spread * (1 << X86Encoding::xmm15); // This is ScratchDoubleReg.
242 #endif
243
244 #if defined(JS_CODEGEN_X64) && defined(_WIN64)
245 static const SetType VolatileMask =
246 ((1 << X86Encoding::xmm0) | (1 << X86Encoding::xmm1) |
247 (1 << X86Encoding::xmm2) | (1 << X86Encoding::xmm3) |
248 (1 << X86Encoding::xmm4) | (1 << X86Encoding::xmm5)) *
249 SpreadScalar |
250 AllPhysMask * SpreadVector;
251 #else
252 static const SetType VolatileMask = AllMask;
253 #endif
254
255 static const SetType NonVolatileMask = AllMask & ~VolatileMask;
256 static const SetType WrapperMask = VolatileMask;
257 static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
258 };
259
260 template <typename T>
261 class TypedRegisterSet;
262
263 struct FloatRegister {
264 using Codes = FloatRegisters;
265 using Code = size_t;
266 using Encoding = Codes::Encoding;
267 using SetType = Codes::SetType;
SetSizeFloatRegister268 static uint32_t SetSize(SetType x) {
269 // Count the number of non-aliased registers, for the moment.
270 //
271 // Copy the set bits of each typed register to the low part of the of
272 // the Set, and count the number of registers. This is made to avoid
273 // registers which are allocated twice with different types (such as in
274 // AllMask).
275 x |= x >> (2 * Codes::TotalPhys);
276 x |= x >> Codes::TotalPhys;
277 x &= Codes::AllPhysMask;
278 static_assert(Codes::AllPhysMask <= 0xffff,
279 "We can safely use CountPopulation32");
280 return mozilla::CountPopulation32(x);
281 }
282
283 #if defined(JS_CODEGEN_X86)
FirstBitFloatRegister284 static uint32_t FirstBit(SetType x) {
285 static_assert(sizeof(SetType) == 4, "SetType must be 32 bits");
286 return mozilla::CountTrailingZeroes32(x);
287 }
LastBitFloatRegister288 static uint32_t LastBit(SetType x) {
289 return 31 - mozilla::CountLeadingZeroes32(x);
290 }
291
292 #elif defined(JS_CODEGEN_X64)
FirstBitFloatRegister293 static uint32_t FirstBit(SetType x) {
294 static_assert(sizeof(SetType) == 8, "SetType must be 64 bits");
295 return mozilla::CountTrailingZeroes64(x);
296 }
LastBitFloatRegister297 static uint32_t LastBit(SetType x) {
298 return 63 - mozilla::CountLeadingZeroes64(x);
299 }
300 #endif
301
302 private:
303 // Note: These fields are using one extra bit to make the invalid enumerated
304 // values fit, and thus prevent a warning.
305 Codes::Encoding reg_ : 5;
306 Codes::ContentType type_ : 3;
307 bool isInvalid_ : 1;
308
309 // Constants used for exporting/importing the float register code.
310 #if defined(JS_CODEGEN_X86)
311 static const size_t RegSize = 3;
312 #elif defined(JS_CODEGEN_X64)
313 static const size_t RegSize = 4;
314 #endif
315 static const size_t RegMask = (1 << RegSize) - 1;
316
317 public:
FloatRegisterFloatRegister318 constexpr FloatRegister()
319 : reg_(Codes::Encoding(0)), type_(Codes::Single), isInvalid_(true) {}
FloatRegisterFloatRegister320 constexpr FloatRegister(uint32_t r, Codes::ContentType k)
321 : reg_(Codes::Encoding(r)), type_(k), isInvalid_(false) {}
FloatRegisterFloatRegister322 constexpr FloatRegister(Codes::Encoding r, Codes::ContentType k)
323 : reg_(r), type_(k), isInvalid_(false) {}
324
FromCodeFloatRegister325 static FloatRegister FromCode(uint32_t i) {
326 MOZ_ASSERT(i < Codes::Total);
327 return FloatRegister(i & RegMask, Codes::ContentType(i >> RegSize));
328 }
329
isSingleFloatRegister330 bool isSingle() const {
331 MOZ_ASSERT(!isInvalid());
332 return type_ == Codes::Single;
333 }
isDoubleFloatRegister334 bool isDouble() const {
335 MOZ_ASSERT(!isInvalid());
336 return type_ == Codes::Double;
337 }
isSimd128FloatRegister338 bool isSimd128() const {
339 MOZ_ASSERT(!isInvalid());
340 return type_ == Codes::Simd128;
341 }
isInvalidFloatRegister342 bool isInvalid() const { return isInvalid_; }
343
asSingleFloatRegister344 FloatRegister asSingle() const {
345 MOZ_ASSERT(!isInvalid());
346 return FloatRegister(reg_, Codes::Single);
347 }
asDoubleFloatRegister348 FloatRegister asDouble() const {
349 MOZ_ASSERT(!isInvalid());
350 return FloatRegister(reg_, Codes::Double);
351 }
asSimd128FloatRegister352 FloatRegister asSimd128() const {
353 MOZ_ASSERT(!isInvalid());
354 return FloatRegister(reg_, Codes::Simd128);
355 }
356
sizeFloatRegister357 uint32_t size() const {
358 MOZ_ASSERT(!isInvalid());
359 if (isSingle()) {
360 return sizeof(float);
361 }
362 if (isDouble()) {
363 return sizeof(double);
364 }
365 MOZ_ASSERT(isSimd128());
366 return 4 * sizeof(int32_t);
367 }
368
codeFloatRegister369 Code code() const {
370 MOZ_ASSERT(!isInvalid());
371 MOZ_ASSERT(uint32_t(reg_) < Codes::TotalPhys);
372 // :TODO: ARM is doing the same thing, but we should avoid this, except
373 // that the RegisterSets depends on this.
374 return Code(reg_ | (type_ << RegSize));
375 }
encodingFloatRegister376 Encoding encoding() const {
377 MOZ_ASSERT(!isInvalid());
378 MOZ_ASSERT(uint32_t(reg_) < Codes::TotalPhys);
379 return reg_;
380 }
381 // defined in Assembler-x86-shared.cpp
382 const char* name() const;
volatile_FloatRegister383 bool volatile_() const {
384 return !!((SetType(1) << code()) & FloatRegisters::VolatileMask);
385 }
386 bool operator!=(FloatRegister other) const {
387 return other.reg_ != reg_ || other.type_ != type_;
388 }
389 bool operator==(FloatRegister other) const {
390 return other.reg_ == reg_ && other.type_ == type_;
391 }
aliasesFloatRegister392 bool aliases(FloatRegister other) const { return other.reg_ == reg_; }
393 // Check if two floating point registers have the same type.
equivFloatRegister394 bool equiv(FloatRegister other) const { return other.type_ == type_; }
395
numAliasedFloatRegister396 uint32_t numAliased() const { return Codes::NumTypes; }
numAlignedAliasedFloatRegister397 uint32_t numAlignedAliased() const { return numAliased(); }
398
aliasedFloatRegister399 FloatRegister aliased(uint32_t aliasIdx) const {
400 MOZ_ASSERT(aliasIdx < Codes::NumTypes);
401 return FloatRegister(
402 reg_, Codes::ContentType((aliasIdx + type_) % Codes::NumTypes));
403 }
alignedAliasedFloatRegister404 FloatRegister alignedAliased(uint32_t aliasIdx) const {
405 return aliased(aliasIdx);
406 }
407
alignedOrDominatedAliasedSetFloatRegister408 SetType alignedOrDominatedAliasedSet() const { return Codes::Spread << reg_; }
409
410 static constexpr RegTypeName DefaultType = RegTypeName::Float64;
411
412 template <RegTypeName = DefaultType>
LiveAsIndexableSetFloatRegister413 static SetType LiveAsIndexableSet(SetType s) {
414 return SetType(0);
415 }
416
417 template <RegTypeName Name = DefaultType>
AllocatableAsIndexableSetFloatRegister418 static SetType AllocatableAsIndexableSet(SetType s) {
419 static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable");
420 return LiveAsIndexableSet<Name>(s);
421 }
422
423 static TypedRegisterSet<FloatRegister> ReduceSetForPush(
424 const TypedRegisterSet<FloatRegister>& s);
425 static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s);
426 uint32_t getRegisterDumpOffsetInBytes();
427 };
428
429 template <>
430 inline FloatRegister::SetType
431 FloatRegister::LiveAsIndexableSet<RegTypeName::Float32>(SetType set) {
432 return set & FloatRegisters::AllSingleMask;
433 }
434
435 template <>
436 inline FloatRegister::SetType
437 FloatRegister::LiveAsIndexableSet<RegTypeName::Float64>(SetType set) {
438 return set & FloatRegisters::AllDoubleMask;
439 }
440
441 template <>
442 inline FloatRegister::SetType
443 FloatRegister::LiveAsIndexableSet<RegTypeName::Vector128>(SetType set) {
444 return set & FloatRegisters::AllVector128Mask;
445 }
446
447 template <>
448 inline FloatRegister::SetType
449 FloatRegister::LiveAsIndexableSet<RegTypeName::Any>(SetType set) {
450 return set;
451 }
452
453 // Arm/D32 has double registers that can NOT be treated as float32
454 // and this requires some dances in lowering.
hasUnaliasedDouble()455 inline bool hasUnaliasedDouble() { return false; }
456
457 // On ARM, Dn aliases both S2n and S2n+1, so if you need to convert a float32
458 // to a double as a temporary, you need a temporary double register.
hasMultiAlias()459 inline bool hasMultiAlias() { return false; }
460
461 } // namespace jit
462 } // namespace js
463
464 #endif /* jit_x86_shared_Architecture_x86_h */
465