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