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