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