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