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_IonTypes_h
8 #define jit_IonTypes_h
9
10 #include "mozilla/HashFunctions.h"
11
12 #include <algorithm>
13 #include <initializer_list>
14 #include <stdint.h>
15
16 #include "jstypes.h"
17
18 #include "js/ScalarType.h" // js::Scalar::Type
19 #include "js/Value.h"
20
21 namespace js {
22
23 // Each IonScript has a unique compilation id. This is used to sweep/ignore
24 // constraints for IonScripts that have been invalidated/destroyed.
25 class IonCompilationId {
26 // Use two 32-bit integers instead of uint64_t to avoid 8-byte alignment on
27 // some 32-bit platforms.
28 uint32_t idLo_;
29 uint32_t idHi_;
30
31 public:
IonCompilationId(uint64_t id)32 explicit IonCompilationId(uint64_t id)
33 : idLo_(id & UINT32_MAX), idHi_(id >> 32) {}
34 bool operator==(const IonCompilationId& other) const {
35 return idLo_ == other.idLo_ && idHi_ == other.idHi_;
36 }
37 bool operator!=(const IonCompilationId& other) const {
38 return !operator==(other);
39 }
40 };
41
42 namespace jit {
43
44 using RecoverOffset = uint32_t;
45 using SnapshotOffset = uint32_t;
46 using BailoutId = uint32_t;
47
48 // The maximum size of any buffer associated with an assembler or code object.
49 // This is chosen to not overflow a signed integer, leaving room for an extra
50 // bit on offsets.
51 static const uint32_t MAX_BUFFER_SIZE = (1 << 30) - 1;
52
53 // Maximum number of scripted arg slots.
54 static const uint32_t SNAPSHOT_MAX_NARGS = 127;
55
56 static const SnapshotOffset INVALID_RECOVER_OFFSET = uint32_t(-1);
57 static const SnapshotOffset INVALID_SNAPSHOT_OFFSET = uint32_t(-1);
58
59 /*
60 * [SMDOC] Avoiding repeated bailouts / invalidations
61 *
62 * To avoid getting trapped in a "compilation -> bailout -> invalidation ->
63 * recompilation -> bailout -> invalidation -> ..." loop, every snapshot in
64 * Warp code is assigned a BailoutKind. If we bail out at that snapshot,
65 * FinishBailoutToBaseline will examine the BailoutKind and take appropriate
66 * action. In general:
67 *
68 * 1. If the bailing instruction comes from transpiled CacheIR, then when we
69 * bail out and continue execution in the baseline interpreter, the
70 * corresponding stub should fail a guard. As a result, we will either
71 * increment the enteredCount for a subsequent stub or attach a new stub,
72 * either of which will prevent WarpOracle from transpiling the failing stub
73 * when we recompile.
74 *
75 * Note: this means that every CacheIR op that can bail out in Warp must
76 * have an equivalent guard in the baseline CacheIR implementation.
77 *
78 * FirstExecution works according to the same principles: we have never hit
79 * this IC before, but after we bail to baseline we will attach a stub and
80 * recompile with better CacheIR information.
81 *
82 * 2. If the bailout occurs because an assumption we made in WarpBuilder was
83 * invalidated, then FinishBailoutToBaseline will set a flag on the script
84 * to avoid that assumption in the future: for example, UninitializedLexical.
85 *
86 * 3. Similarly, if the bailing instruction is generated or modified by a MIR
87 * optimization, then FinishBailoutToBaseline will set a flag on the script
88 * to make that optimization more conservative in the future. Examples
89 * include LICM, EagerTruncation, and HoistBoundsCheck.
90 *
91 * 4. Some bailouts can't be handled in Warp, even after a recompile. For
92 * example, Warp does not support catching exceptions. If this happens
93 * too often, then the cost of bailing out repeatedly outweighs the
94 * benefit of Warp compilation, so we invalidate the script and disable
95 * Warp compilation.
96 *
97 * 5. Some bailouts don't happen in performance-sensitive code: for example,
98 * the |debugger| statement. We just ignore those.
99 */
100 enum class BailoutKind : uint8_t {
101 Unknown,
102
103 // An instruction generated by the transpiler. If this instruction bails out,
104 // attaching a new stub in baseline will invalidate the current Warp script
105 // and avoid a bailout loop.
106 TranspiledCacheIR,
107
108 // An optimistic unbox on the cold path for a non-Value phi failed. If this
109 // instruction bails out, we will invalidate the script and mark the
110 // HadSpeculativePhiBailout flag on the script.
111 SpeculativePhi,
112
113 // A conversion inserted by a type policy. If this instruction bails out,
114 // we expect to throw an error. If this happens too frequently, we will
115 // invalidate the current Warp script and disable recompilation.
116 TypePolicy,
117
118 // An instruction hoisted by LICM. If this instruction bails out, we will
119 // bail out to baseline to see if we attach a new stub. If we do, then the
120 // more than once, we will invalidate the current Warp script and
121 // mark the hadLICMInvalidation flag on the script.
122 LICM,
123
124 // An instruction moved up by InstructionReordering. If this
125 // instruction bails out, we will mark the ReorderingBailout flag on
126 // the script. If this happens too frequently, we will invalidate
127 // the script.
128 InstructionReordering,
129
130 // An instruction created or hoisted by tryHoistBoundsCheck.
131 // If this instruction bails out, we will invalidate the current Warp script
132 // and mark the HoistBoundsCheckBailout flag on the script.
133 HoistBoundsCheck,
134
135 // An eager truncation generated by range analysis.
136 // If this instruction bails out, we will invalidate the current Warp script
137 // and mark the EagerTruncationBailout flag on the script.
138 EagerTruncation,
139
140 // A folded unbox instruction generated by FoldLoadsWithUnbox.
141 // If this instruction bails out, we will invalidate the current Warp script
142 // and mark the UnboxFoldingBailout flag on the script.
143 UnboxFolding,
144
145 // An inevitable bailout (MBail instruction or type barrier that always bails)
146 Inevitable,
147
148 // Bailing out during a VM call. Many possible causes that are hard
149 // to distinguish statically at snapshot construction time.
150 // We just lump them together.
151 DuringVMCall,
152
153 // A spread call or funapply had more than JIT_ARGS_LENGTH_MAX arguments.
154 // We bail out to handle this in the VM. If this happens too frequently,
155 // we will invalidate the current Warp script and disable recompilation.
156 TooManyArguments,
157
158 // We hit an active |debugger;| statement.
159 Debugger,
160
161 // We hit this code for the first time.
162 FirstExecution,
163
164 // A lexical check failed. We will set lexical checks as unmovable.
165 UninitializedLexical,
166
167 // A bailout to baseline from Ion on exception to handle Debugger hooks.
168 IonExceptionDebugMode,
169
170 // We returned to a stack frame after invalidating its IonScript.
171 OnStackInvalidation,
172
173 // We have executed code that should be unreachable, and need to assert.
174 Unreachable,
175
176 Limit
177 };
178
BailoutKindString(BailoutKind kind)179 inline const char* BailoutKindString(BailoutKind kind) {
180 switch (kind) {
181 case BailoutKind::Unknown:
182 return "Unknown";
183 case BailoutKind::TranspiledCacheIR:
184 return "TranspiledCacheIR";
185 case BailoutKind::SpeculativePhi:
186 return "SpeculativePhi";
187 case BailoutKind::TypePolicy:
188 return "TypePolicy";
189 case BailoutKind::LICM:
190 return "LICM";
191 case BailoutKind::InstructionReordering:
192 return "InstructionReordering";
193 case BailoutKind::HoistBoundsCheck:
194 return "HoistBoundsCheck";
195 case BailoutKind::EagerTruncation:
196 return "EagerTruncation";
197 case BailoutKind::UnboxFolding:
198 return "UnboxFolding";
199 case BailoutKind::Inevitable:
200 return "Inevitable";
201 case BailoutKind::DuringVMCall:
202 return "DuringVMCall";
203 case BailoutKind::TooManyArguments:
204 return "TooManyArguments";
205 case BailoutKind::Debugger:
206 return "Debugger";
207 case BailoutKind::FirstExecution:
208 return "FirstExecution";
209 case BailoutKind::UninitializedLexical:
210 return "UninitializedLexical";
211 case BailoutKind::IonExceptionDebugMode:
212 return "IonExceptionDebugMode";
213 case BailoutKind::OnStackInvalidation:
214 return "OnStackInvalidation";
215 case BailoutKind::Unreachable:
216 return "Unreachable";
217
218 case BailoutKind::Limit:
219 break;
220 }
221
222 MOZ_CRASH("Invalid BailoutKind");
223 }
224
225 static const uint32_t ELEMENT_TYPE_BITS = 5;
226 static const uint32_t ELEMENT_TYPE_SHIFT = 0;
227 static const uint32_t ELEMENT_TYPE_MASK = (1 << ELEMENT_TYPE_BITS) - 1;
228 static const uint32_t VECTOR_TYPE_BITS = 1;
229 static const uint32_t VECTOR_TYPE_SHIFT =
230 ELEMENT_TYPE_BITS + ELEMENT_TYPE_SHIFT;
231 static const uint32_t VECTOR_TYPE_MASK = (1 << VECTOR_TYPE_BITS) - 1;
232
233 // The integer SIMD types have a lot of operations that do the exact same thing
234 // for signed and unsigned integer types. Sometimes it is simpler to treat
235 // signed and unsigned integer SIMD types as the same type, using a SimdSign to
236 // distinguish the few cases where there is a difference.
237 enum class SimdSign {
238 // Signedness is not applicable to this type. (i.e., Float or Bool).
239 NotApplicable,
240 // Treat as an unsigned integer with a range 0 .. 2^N-1.
241 Unsigned,
242 // Treat as a signed integer in two's complement encoding.
243 Signed,
244 };
245
246 class SimdConstant {
247 public:
248 enum Type {
249 Int8x16,
250 Int16x8,
251 Int32x4,
252 Int64x2,
253 Float32x4,
254 Float64x2,
255 Undefined = -1
256 };
257
258 typedef int8_t I8x16[16];
259 typedef int16_t I16x8[8];
260 typedef int32_t I32x4[4];
261 typedef int64_t I64x2[2];
262 typedef float F32x4[4];
263 typedef double F64x2[2];
264
265 private:
266 Type type_;
267 union {
268 I8x16 i8x16;
269 I16x8 i16x8;
270 I32x4 i32x4;
271 I64x2 i64x2;
272 F32x4 f32x4;
273 F64x2 f64x2;
274 } u;
275
defined()276 bool defined() const { return type_ != Undefined; }
277
278 public:
279 // Doesn't have a default constructor, as it would prevent it from being
280 // included in unions.
281
CreateX16(const int8_t * array)282 static SimdConstant CreateX16(const int8_t* array) {
283 SimdConstant cst;
284 cst.type_ = Int8x16;
285 memcpy(cst.u.i8x16, array, sizeof(cst.u));
286 return cst;
287 }
SplatX16(int8_t v)288 static SimdConstant SplatX16(int8_t v) {
289 SimdConstant cst;
290 cst.type_ = Int8x16;
291 std::fill_n(cst.u.i8x16, 16, v);
292 return cst;
293 }
CreateX8(const int16_t * array)294 static SimdConstant CreateX8(const int16_t* array) {
295 SimdConstant cst;
296 cst.type_ = Int16x8;
297 memcpy(cst.u.i16x8, array, sizeof(cst.u));
298 return cst;
299 }
SplatX8(int16_t v)300 static SimdConstant SplatX8(int16_t v) {
301 SimdConstant cst;
302 cst.type_ = Int16x8;
303 std::fill_n(cst.u.i16x8, 8, v);
304 return cst;
305 }
CreateX4(const int32_t * array)306 static SimdConstant CreateX4(const int32_t* array) {
307 SimdConstant cst;
308 cst.type_ = Int32x4;
309 memcpy(cst.u.i32x4, array, sizeof(cst.u));
310 return cst;
311 }
SplatX4(int32_t v)312 static SimdConstant SplatX4(int32_t v) {
313 SimdConstant cst;
314 cst.type_ = Int32x4;
315 std::fill_n(cst.u.i32x4, 4, v);
316 return cst;
317 }
CreateX2(const int64_t * array)318 static SimdConstant CreateX2(const int64_t* array) {
319 SimdConstant cst;
320 cst.type_ = Int64x2;
321 memcpy(cst.u.i64x2, array, sizeof(cst.u));
322 return cst;
323 }
SplatX2(int64_t v)324 static SimdConstant SplatX2(int64_t v) {
325 SimdConstant cst;
326 cst.type_ = Int64x2;
327 std::fill_n(cst.u.i64x2, 2, v);
328 return cst;
329 }
CreateX4(const float * array)330 static SimdConstant CreateX4(const float* array) {
331 SimdConstant cst;
332 cst.type_ = Float32x4;
333 memcpy(cst.u.f32x4, array, sizeof(cst.u));
334 return cst;
335 }
SplatX4(float v)336 static SimdConstant SplatX4(float v) {
337 SimdConstant cst;
338 cst.type_ = Float32x4;
339 std::fill_n(cst.u.f32x4, 4, v);
340 return cst;
341 }
CreateX2(const double * array)342 static SimdConstant CreateX2(const double* array) {
343 SimdConstant cst;
344 cst.type_ = Float64x2;
345 memcpy(cst.u.f64x2, array, sizeof(cst.u));
346 return cst;
347 }
SplatX2(double v)348 static SimdConstant SplatX2(double v) {
349 SimdConstant cst;
350 cst.type_ = Float64x2;
351 std::fill_n(cst.u.f64x2, 2, v);
352 return cst;
353 }
354
355 // Overloads for use by templates.
CreateSimd128(const int8_t * array)356 static SimdConstant CreateSimd128(const int8_t* array) {
357 return CreateX16(array);
358 }
CreateSimd128(const int16_t * array)359 static SimdConstant CreateSimd128(const int16_t* array) {
360 return CreateX8(array);
361 }
CreateSimd128(const int32_t * array)362 static SimdConstant CreateSimd128(const int32_t* array) {
363 return CreateX4(array);
364 }
CreateSimd128(const int64_t * array)365 static SimdConstant CreateSimd128(const int64_t* array) {
366 return CreateX2(array);
367 }
CreateSimd128(const float * array)368 static SimdConstant CreateSimd128(const float* array) {
369 return CreateX4(array);
370 }
CreateSimd128(const double * array)371 static SimdConstant CreateSimd128(const double* array) {
372 return CreateX2(array);
373 }
374
type()375 Type type() const {
376 MOZ_ASSERT(defined());
377 return type_;
378 }
379
isFloatingType()380 bool isFloatingType() const {
381 MOZ_ASSERT(defined());
382 return type_ >= Float32x4;
383 }
384
isIntegerType()385 bool isIntegerType() const {
386 MOZ_ASSERT(defined());
387 return type_ <= Int64x2;
388 }
389
390 // Get the raw bytes of the constant.
bytes()391 const void* bytes() const { return u.i8x16; }
392
asInt8x16()393 const I8x16& asInt8x16() const {
394 MOZ_ASSERT(defined() && type_ == Int8x16);
395 return u.i8x16;
396 }
397
asInt16x8()398 const I16x8& asInt16x8() const {
399 MOZ_ASSERT(defined() && type_ == Int16x8);
400 return u.i16x8;
401 }
402
asInt32x4()403 const I32x4& asInt32x4() const {
404 MOZ_ASSERT(defined() && type_ == Int32x4);
405 return u.i32x4;
406 }
407
asInt64x2()408 const I64x2& asInt64x2() const {
409 MOZ_ASSERT(defined() && type_ == Int64x2);
410 return u.i64x2;
411 }
412
asFloat32x4()413 const F32x4& asFloat32x4() const {
414 MOZ_ASSERT(defined() && type_ == Float32x4);
415 return u.f32x4;
416 }
417
asFloat64x2()418 const F64x2& asFloat64x2() const {
419 MOZ_ASSERT(defined() && type_ == Float64x2);
420 return u.f64x2;
421 }
422
bitwiseEqual(const SimdConstant & rhs)423 bool bitwiseEqual(const SimdConstant& rhs) const {
424 MOZ_ASSERT(defined() && rhs.defined());
425 return memcmp(&u, &rhs.u, sizeof(u)) == 0;
426 }
427
isZeroBits()428 bool isZeroBits() const {
429 MOZ_ASSERT(defined());
430 return u.i64x2[0] == 0 && u.i64x2[1] == 0;
431 }
432
isOneBits()433 bool isOneBits() const {
434 MOZ_ASSERT(defined());
435 return ~u.i64x2[0] == 0 && ~u.i64x2[1] == 0;
436 }
437
438 // SimdConstant is a HashPolicy. Currently we discriminate by type, but it
439 // may be that we should only be discriminating by int vs float.
440 using Lookup = SimdConstant;
441
hash(const SimdConstant & val)442 static HashNumber hash(const SimdConstant& val) {
443 uint32_t hash = mozilla::HashBytes(&val.u, sizeof(val.u));
444 return mozilla::AddToHash(hash, val.type_);
445 }
446
match(const SimdConstant & lhs,const SimdConstant & rhs)447 static bool match(const SimdConstant& lhs, const SimdConstant& rhs) {
448 return lhs.type() == rhs.type() && lhs.bitwiseEqual(rhs);
449 }
450 };
451
452 enum class IntConversionBehavior {
453 // These two try to convert the input to an int32 using ToNumber and
454 // will fail if the resulting int32 isn't strictly equal to the input.
455 Normal, // Succeeds on -0: converts to 0.
456 NegativeZeroCheck, // Fails on -0.
457 // These three will convert the input to an int32 with loss of precision.
458 Truncate,
459 TruncateNoWrap,
460 ClampToUint8,
461 };
462
463 enum class IntConversionInputKind { NumbersOnly, NumbersOrBoolsOnly, Any };
464
465 // The ordering of this enumeration is important: Anything < Value is a
466 // specialized type. Furthermore, anything < String has trivial conversion to
467 // a number.
468 enum class MIRType : uint8_t {
469 Undefined,
470 Null,
471 Boolean,
472 Int32,
473 Int64,
474 IntPtr,
475 Double,
476 Float32,
477 // Types above have trivial conversion to a number.
478 String,
479 Symbol,
480 BigInt,
481 Simd128,
482 // Types above are primitive (including undefined and null).
483 Object,
484 MagicOptimizedOut, // JS_OPTIMIZED_OUT magic value.
485 MagicHole, // JS_ELEMENTS_HOLE magic value.
486 MagicIsConstructing, // JS_IS_CONSTRUCTING magic value.
487 MagicUninitializedLexical, // JS_UNINITIALIZED_LEXICAL magic value.
488 // Types above are specialized.
489 Value,
490 None, // Invalid, used as a placeholder.
491 Slots, // A slots vector
492 Elements, // An elements vector
493 Pointer, // An opaque pointer that receives no special treatment
494 RefOrNull, // Wasm Ref/AnyRef/NullRef: a raw JSObject* or a raw (void*)0
495 StackResults, // Wasm multi-value stack result area, which may contain refs
496 Shape, // A Shape pointer.
497 Last = Shape
498 };
499
MIRTypeFromValueType(JSValueType type)500 static inline MIRType MIRTypeFromValueType(JSValueType type) {
501 // This function does not deal with magic types. Magic constants should be
502 // filtered out in MIRTypeFromValue.
503 switch (type) {
504 case JSVAL_TYPE_DOUBLE:
505 return MIRType::Double;
506 case JSVAL_TYPE_INT32:
507 return MIRType::Int32;
508 case JSVAL_TYPE_UNDEFINED:
509 return MIRType::Undefined;
510 case JSVAL_TYPE_STRING:
511 return MIRType::String;
512 case JSVAL_TYPE_SYMBOL:
513 return MIRType::Symbol;
514 case JSVAL_TYPE_BIGINT:
515 return MIRType::BigInt;
516 case JSVAL_TYPE_BOOLEAN:
517 return MIRType::Boolean;
518 case JSVAL_TYPE_NULL:
519 return MIRType::Null;
520 case JSVAL_TYPE_OBJECT:
521 return MIRType::Object;
522 case JSVAL_TYPE_UNKNOWN:
523 return MIRType::Value;
524 default:
525 MOZ_CRASH("unexpected jsval type");
526 }
527 }
528
ValueTypeFromMIRType(MIRType type)529 static inline JSValueType ValueTypeFromMIRType(MIRType type) {
530 switch (type) {
531 case MIRType::Undefined:
532 return JSVAL_TYPE_UNDEFINED;
533 case MIRType::Null:
534 return JSVAL_TYPE_NULL;
535 case MIRType::Boolean:
536 return JSVAL_TYPE_BOOLEAN;
537 case MIRType::Int32:
538 return JSVAL_TYPE_INT32;
539 case MIRType::Float32: // Fall through, there's no JSVAL for Float32
540 case MIRType::Double:
541 return JSVAL_TYPE_DOUBLE;
542 case MIRType::String:
543 return JSVAL_TYPE_STRING;
544 case MIRType::Symbol:
545 return JSVAL_TYPE_SYMBOL;
546 case MIRType::BigInt:
547 return JSVAL_TYPE_BIGINT;
548 case MIRType::MagicOptimizedOut:
549 case MIRType::MagicHole:
550 case MIRType::MagicIsConstructing:
551 case MIRType::MagicUninitializedLexical:
552 return JSVAL_TYPE_MAGIC;
553 default:
554 MOZ_ASSERT(type == MIRType::Object);
555 return JSVAL_TYPE_OBJECT;
556 }
557 }
558
MIRTypeToTag(MIRType type)559 static inline JSValueTag MIRTypeToTag(MIRType type) {
560 return JSVAL_TYPE_TO_TAG(ValueTypeFromMIRType(type));
561 }
562
MIRTypeToSize(MIRType type)563 static inline size_t MIRTypeToSize(MIRType type) {
564 switch (type) {
565 case MIRType::Int32:
566 return 4;
567 case MIRType::Int64:
568 return 8;
569 case MIRType::Float32:
570 return 4;
571 case MIRType::Double:
572 return 8;
573 case MIRType::Simd128:
574 return 16;
575 case MIRType::Pointer:
576 case MIRType::RefOrNull:
577 return sizeof(uintptr_t);
578 default:
579 MOZ_CRASH("MIRTypeToSize - unhandled case");
580 }
581 }
582
StringFromMIRType(MIRType type)583 static inline const char* StringFromMIRType(MIRType type) {
584 switch (type) {
585 case MIRType::Undefined:
586 return "Undefined";
587 case MIRType::Null:
588 return "Null";
589 case MIRType::Boolean:
590 return "Bool";
591 case MIRType::Int32:
592 return "Int32";
593 case MIRType::Int64:
594 return "Int64";
595 case MIRType::IntPtr:
596 return "IntPtr";
597 case MIRType::Double:
598 return "Double";
599 case MIRType::Float32:
600 return "Float32";
601 case MIRType::String:
602 return "String";
603 case MIRType::Symbol:
604 return "Symbol";
605 case MIRType::BigInt:
606 return "BigInt";
607 case MIRType::Object:
608 return "Object";
609 case MIRType::MagicOptimizedOut:
610 return "MagicOptimizedOut";
611 case MIRType::MagicHole:
612 return "MagicHole";
613 case MIRType::MagicIsConstructing:
614 return "MagicIsConstructing";
615 case MIRType::MagicUninitializedLexical:
616 return "MagicUninitializedLexical";
617 case MIRType::Value:
618 return "Value";
619 case MIRType::None:
620 return "None";
621 case MIRType::Slots:
622 return "Slots";
623 case MIRType::Elements:
624 return "Elements";
625 case MIRType::Pointer:
626 return "Pointer";
627 case MIRType::RefOrNull:
628 return "RefOrNull";
629 case MIRType::StackResults:
630 return "StackResults";
631 case MIRType::Shape:
632 return "Shape";
633 case MIRType::Simd128:
634 return "Simd128";
635 }
636 MOZ_CRASH("Unknown MIRType.");
637 }
638
IsIntType(MIRType type)639 static inline bool IsIntType(MIRType type) {
640 return type == MIRType::Int32 || type == MIRType::Int64;
641 }
642
IsNumberType(MIRType type)643 static inline bool IsNumberType(MIRType type) {
644 return type == MIRType::Int32 || type == MIRType::Double ||
645 type == MIRType::Float32 || type == MIRType::Int64;
646 }
647
IsNumericType(MIRType type)648 static inline bool IsNumericType(MIRType type) {
649 return IsNumberType(type) || type == MIRType::BigInt;
650 }
651
IsTypeRepresentableAsDouble(MIRType type)652 static inline bool IsTypeRepresentableAsDouble(MIRType type) {
653 return type == MIRType::Int32 || type == MIRType::Double ||
654 type == MIRType::Float32;
655 }
656
IsFloatType(MIRType type)657 static inline bool IsFloatType(MIRType type) {
658 return type == MIRType::Int32 || type == MIRType::Float32;
659 }
660
IsFloatingPointType(MIRType type)661 static inline bool IsFloatingPointType(MIRType type) {
662 return type == MIRType::Double || type == MIRType::Float32;
663 }
664
IsNullOrUndefined(MIRType type)665 static inline bool IsNullOrUndefined(MIRType type) {
666 return type == MIRType::Null || type == MIRType::Undefined;
667 }
668
IsMagicType(MIRType type)669 static inline bool IsMagicType(MIRType type) {
670 return type == MIRType::MagicHole || type == MIRType::MagicOptimizedOut ||
671 type == MIRType::MagicIsConstructing ||
672 type == MIRType::MagicUninitializedLexical;
673 }
674
ScalarTypeToMIRType(Scalar::Type type)675 static inline MIRType ScalarTypeToMIRType(Scalar::Type type) {
676 switch (type) {
677 case Scalar::Int8:
678 case Scalar::Uint8:
679 case Scalar::Int16:
680 case Scalar::Uint16:
681 case Scalar::Int32:
682 case Scalar::Uint32:
683 case Scalar::Uint8Clamped:
684 return MIRType::Int32;
685 case Scalar::Int64:
686 return MIRType::Int64;
687 case Scalar::Float32:
688 return MIRType::Float32;
689 case Scalar::Float64:
690 return MIRType::Double;
691 case Scalar::BigInt64:
692 case Scalar::BigUint64:
693 MOZ_CRASH("NYI");
694 case Scalar::Simd128:
695 return MIRType::Simd128;
696 case Scalar::MaxTypedArrayViewType:
697 break;
698 }
699 MOZ_CRASH("unexpected kind");
700 }
701
NeedsPostBarrier(MIRType type)702 static constexpr bool NeedsPostBarrier(MIRType type) {
703 MOZ_ASSERT(type != MIRType::Value);
704 return type == MIRType::Object || type == MIRType::String ||
705 type == MIRType::BigInt;
706 }
707
708 #ifdef DEBUG
709
710 // Track the pipeline of opcodes which has produced a snapshot.
711 # define TRACK_SNAPSHOTS 1
712
713 // Make sure registers are not modified between an instruction and
714 // its OsiPoint.
715 # define CHECK_OSIPOINT_REGISTERS 1
716
717 #endif // DEBUG
718
719 enum ABIArgType {
720 // A pointer sized integer
721 ArgType_General = 0x1,
722 // A 32-bit integer
723 ArgType_Int32 = 0x2,
724 // A 64-bit integer
725 ArgType_Int64 = 0x3,
726 // A 32-bit floating point number
727 ArgType_Float32 = 0x4,
728 // A 64-bit floating point number
729 ArgType_Float64 = 0x5,
730
731 RetType_Shift = 0x0,
732 ArgType_Shift = 0x3,
733 ArgType_Mask = 0x7
734 };
735
736 namespace detail {
737
MakeABIFunctionType(ABIArgType ret,std::initializer_list<ABIArgType> args)738 static constexpr int MakeABIFunctionType(
739 ABIArgType ret, std::initializer_list<ABIArgType> args) {
740 int abiType = ret << RetType_Shift;
741 int i = 1;
742 for (auto arg : args) {
743 abiType |= (arg << (ArgType_Shift * i));
744 i++;
745 }
746 return abiType;
747 }
748
749 } // namespace detail
750
751 enum ABIFunctionType : uint32_t {
752 // The enum must be explicitly typed to avoid UB: some validly constructed
753 // members are larger than any explicitly declared members.
754
755 // VM functions that take 0-9 non-double arguments
756 // and return a non-double value.
757 Args_General0 = ArgType_General << RetType_Shift,
758 Args_General1 = Args_General0 | (ArgType_General << (ArgType_Shift * 1)),
759 Args_General2 = Args_General1 | (ArgType_General << (ArgType_Shift * 2)),
760 Args_General3 = Args_General2 | (ArgType_General << (ArgType_Shift * 3)),
761 Args_General4 = Args_General3 | (ArgType_General << (ArgType_Shift * 4)),
762 Args_General5 = Args_General4 | (ArgType_General << (ArgType_Shift * 5)),
763 Args_General6 = Args_General5 | (ArgType_General << (ArgType_Shift * 6)),
764 Args_General7 = Args_General6 | (ArgType_General << (ArgType_Shift * 7)),
765 Args_General8 = Args_General7 | (ArgType_General << (ArgType_Shift * 8)),
766
767 // int64 f(double)
768 Args_Int64_Double =
769 (ArgType_Int64 << RetType_Shift) | (ArgType_Float64 << ArgType_Shift),
770
771 // double f()
772 Args_Double_None = ArgType_Float64 << RetType_Shift,
773
774 // int f(double)
775 Args_Int_Double = Args_General0 | (ArgType_Float64 << ArgType_Shift),
776
777 // int f(float32)
778 Args_Int_Float32 = Args_General0 | (ArgType_Float32 << ArgType_Shift),
779
780 // float f(float)
781 Args_Float32_Float32 =
782 (ArgType_Float32 << RetType_Shift) | (ArgType_Float32 << ArgType_Shift),
783
784 // float f(int, int)
785 Args_Float32_IntInt = (ArgType_Float32 << RetType_Shift) |
786 (ArgType_General << (ArgType_Shift * 1)) |
787 (ArgType_General << (ArgType_Shift * 2)),
788
789 // double f(double)
790 Args_Double_Double = Args_Double_None | (ArgType_Float64 << ArgType_Shift),
791
792 // double f(int)
793 Args_Double_Int = Args_Double_None | (ArgType_General << ArgType_Shift),
794
795 // double f(int, int)
796 Args_Double_IntInt =
797 Args_Double_Int | (ArgType_General << (ArgType_Shift * 2)),
798
799 // double f(double, int)
800 Args_Double_DoubleInt = Args_Double_None |
801 (ArgType_General << (ArgType_Shift * 1)) |
802 (ArgType_Float64 << (ArgType_Shift * 2)),
803
804 // double f(double, double)
805 Args_Double_DoubleDouble =
806 Args_Double_Double | (ArgType_Float64 << (ArgType_Shift * 2)),
807
808 // float f(float, float)
809 Args_Float32_Float32Float32 =
810 Args_Float32_Float32 | (ArgType_Float32 << (ArgType_Shift * 2)),
811
812 // double f(int, double)
813 Args_Double_IntDouble = Args_Double_None |
814 (ArgType_Float64 << (ArgType_Shift * 1)) |
815 (ArgType_General << (ArgType_Shift * 2)),
816
817 // int f(int, double)
818 Args_Int_IntDouble = Args_General0 |
819 (ArgType_Float64 << (ArgType_Shift * 1)) |
820 (ArgType_General << (ArgType_Shift * 2)),
821
822 // int f(double, int)
823 Args_Int_DoubleInt = Args_General0 |
824 (ArgType_General << (ArgType_Shift * 1)) |
825 (ArgType_Float64 << (ArgType_Shift * 2)),
826
827 // double f(double, double, double)
828 Args_Double_DoubleDoubleDouble =
829 Args_Double_DoubleDouble | (ArgType_Float64 << (ArgType_Shift * 3)),
830
831 // double f(double, double, double, double)
832 Args_Double_DoubleDoubleDoubleDouble =
833 Args_Double_DoubleDoubleDouble | (ArgType_Float64 << (ArgType_Shift * 4)),
834
835 // int f(double, int, int)
836 Args_Int_DoubleIntInt = Args_General0 |
837 (ArgType_General << (ArgType_Shift * 1)) |
838 (ArgType_General << (ArgType_Shift * 2)) |
839 (ArgType_Float64 << (ArgType_Shift * 3)),
840
841 // int f(int, double, int, int)
842 Args_Int_IntDoubleIntInt = Args_General0 |
843 (ArgType_General << (ArgType_Shift * 1)) |
844 (ArgType_General << (ArgType_Shift * 2)) |
845 (ArgType_Float64 << (ArgType_Shift * 3)) |
846 (ArgType_General << (ArgType_Shift * 4)),
847
848 Args_Int_GeneralGeneralGeneralInt64 =
849 Args_General0 | (ArgType_General << (ArgType_Shift * 1)) |
850 (ArgType_General << (ArgType_Shift * 2)) |
851 (ArgType_General << (ArgType_Shift * 3)) |
852 (ArgType_Int64 << (ArgType_Shift * 4)),
853
854 Args_Int_GeneralGeneralInt64Int64 = Args_General0 |
855 (ArgType_General << (ArgType_Shift * 1)) |
856 (ArgType_General << (ArgType_Shift * 2)) |
857 (ArgType_Int64 << (ArgType_Shift * 3)) |
858 (ArgType_Int64 << (ArgType_Shift * 4)),
859
860 Args_Int32_General =
861 detail::MakeABIFunctionType(ArgType_Int32, {ArgType_General}),
862 Args_Int32_GeneralInt32 = detail::MakeABIFunctionType(
863 ArgType_Int32, {ArgType_General, ArgType_Int32}),
864 Args_Int32_GeneralInt32Int32 = detail::MakeABIFunctionType(
865 ArgType_Int32, {ArgType_General, ArgType_Int32, ArgType_Int32}),
866 Args_Int32_GeneralInt32Int32Int32Int32 = detail::MakeABIFunctionType(
867 ArgType_Int32, {ArgType_General, ArgType_Int32, ArgType_Int32,
868 ArgType_Int32, ArgType_Int32}),
869 Args_Int32_GeneralInt32Int32Int32Int32Int32 = detail::MakeABIFunctionType(
870 ArgType_Int32, {ArgType_General, ArgType_Int32, ArgType_Int32,
871 ArgType_Int32, ArgType_Int32, ArgType_Int32}),
872 Args_Int32_GeneralInt32Int32Int32General = detail::MakeABIFunctionType(
873 ArgType_Int32, {ArgType_General, ArgType_Int32, ArgType_Int32,
874 ArgType_Int32, ArgType_General}),
875 Args_Int32_GeneralInt32Int32Int64 = detail::MakeABIFunctionType(
876 ArgType_Int32,
877 {ArgType_General, ArgType_Int32, ArgType_Int32, ArgType_Int64}),
878 Args_Int32_GeneralInt32Int32General = detail::MakeABIFunctionType(
879 ArgType_Int32,
880 {ArgType_General, ArgType_Int32, ArgType_Int32, ArgType_General}),
881 Args_Int32_GeneralInt32Int64Int64 = detail::MakeABIFunctionType(
882 ArgType_Int32,
883 {ArgType_General, ArgType_Int32, ArgType_Int64, ArgType_Int64}),
884 Args_Int32_GeneralInt32GeneralInt32 = detail::MakeABIFunctionType(
885 ArgType_Int32,
886 {ArgType_General, ArgType_Int32, ArgType_General, ArgType_Int32}),
887 Args_Int32_GeneralInt32GeneralInt32Int32 = detail::MakeABIFunctionType(
888 ArgType_Int32, {ArgType_General, ArgType_Int32, ArgType_General,
889 ArgType_Int32, ArgType_Int32}),
890 Args_Int32_GeneralGeneral = detail::MakeABIFunctionType(
891 ArgType_Int32, {ArgType_General, ArgType_General}),
892 Args_Int32_GeneralGeneralGeneral = detail::MakeABIFunctionType(
893 ArgType_Int32, {ArgType_General, ArgType_General, ArgType_General}),
894 Args_Int32_GeneralGeneralInt32Int32 = detail::MakeABIFunctionType(
895 ArgType_Int32,
896 {ArgType_General, ArgType_General, ArgType_Int32, ArgType_Int32}),
897 Args_General_GeneralInt32 = detail::MakeABIFunctionType(
898 ArgType_General, {ArgType_General, ArgType_Int32}),
899 Args_General_GeneralInt32Int32 = detail::MakeABIFunctionType(
900 ArgType_General, {ArgType_General, ArgType_Int32, ArgType_Int32}),
901 Args_General_GeneralInt32General = detail::MakeABIFunctionType(
902 ArgType_General, {ArgType_General, ArgType_Int32, ArgType_General}),
903 };
904
MakeABIFunctionType(ABIArgType ret,std::initializer_list<ABIArgType> args)905 static constexpr ABIFunctionType MakeABIFunctionType(
906 ABIArgType ret, std::initializer_list<ABIArgType> args) {
907 return ABIFunctionType(detail::MakeABIFunctionType(ret, args));
908 }
909
910 // Rounding modes for round instructions.
911 enum class RoundingMode { Down, Up, NearestTiesToEven, TowardsZero };
912
913 // If a function contains no calls, we can assume the caller has checked the
914 // stack limit up to this maximum frame size. This works because the jit stack
915 // limit has a generous buffer before the real end of the native stack.
916 static const uint32_t MAX_UNCHECKED_LEAF_FRAME_SIZE = 64;
917
918 // Truncating conversion modifiers.
919 using TruncFlags = uint32_t;
920 static const TruncFlags TRUNC_UNSIGNED = TruncFlags(1) << 0;
921 static const TruncFlags TRUNC_SATURATING = TruncFlags(1) << 1;
922
923 enum BranchDirection { FALSE_BRANCH, TRUE_BRANCH };
924
925 template <typename T>
SplatByteToUInt(uint8_t val,uint8_t x)926 constexpr T SplatByteToUInt(uint8_t val, uint8_t x) {
927 T splatted = val;
928 for (; x > 1; x--) {
929 splatted |= splatted << 8;
930 }
931 return splatted;
932 }
933
934 } // namespace jit
935 } // namespace js
936
937 #endif /* jit_IonTypes_h */
938