/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_IonTypes_h #define jit_IonTypes_h #include "mozilla/HashFunctions.h" #include "jsfriendapi.h" #include "jstypes.h" #include "js/Value.h" namespace js { namespace jit { typedef uint32_t RecoverOffset; typedef uint32_t SnapshotOffset; typedef uint32_t BailoutId; // The maximum size of any buffer associated with an assembler or code object. // This is chosen to not overflow a signed integer, leaving room for an extra // bit on offsets. static const uint32_t MAX_BUFFER_SIZE = (1 << 30) - 1; // Maximum number of scripted arg slots. static const uint32_t SNAPSHOT_MAX_NARGS = 127; static const SnapshotOffset INVALID_RECOVER_OFFSET = uint32_t(-1); static const SnapshotOffset INVALID_SNAPSHOT_OFFSET = uint32_t(-1); // Different kinds of bailouts. When extending this enum, make sure to check // the bits reserved for bailout kinds in Bailouts.h enum BailoutKind { // Normal bailouts, that don't need to be handled specially when restarting // in baseline. // An inevitable bailout (MBail instruction or type barrier that always bails) Bailout_Inevitable, // Bailing out during a VM call. Many possible causes that are hard // to distinguish statically at snapshot construction time. // We just lump them together. Bailout_DuringVMCall, // Call to a non-JSFunction (problem for |apply|) Bailout_NonJSFunctionCallee, // Dynamic scope chain lookup produced |undefined| Bailout_DynamicNameNotFound, // Input string contains 'arguments' or 'eval' Bailout_StringArgumentsEval, // Bailout on overflow, but don't immediately invalidate. // Used for abs, sub and LoadUnboxedScalar (when loading a uint32 that // doesn't fit in an int32). Bailout_Overflow, // floor, ceiling and round bail if input is NaN, if output would be -0 or // doesn't fit in int32 range Bailout_Round, // Non-primitive value used as input for ToDouble, ToInt32, ToString, etc. // For ToInt32, can also mean that input can't be converted without precision // loss (e.g. 5.5). Bailout_NonPrimitiveInput, // For ToInt32, would lose precision when converting (e.g. 5.5). Bailout_PrecisionLoss, // We tripped a type barrier (object was not in the expected TypeSet) Bailout_TypeBarrierO, // We tripped a type barrier (value was not in the expected TypeSet) Bailout_TypeBarrierV, // We tripped a type monitor (wrote an unexpected type in a property) Bailout_MonitorTypes, // We hit a hole in an array. Bailout_Hole, // Array access with negative index Bailout_NegativeIndex, // Pretty specific case: // - need a type barrier on a property write // - all but one of the observed types have property types that reflect the value // - we need to guard that we're not given an object of that one other type // also used for the unused GuardClass instruction Bailout_ObjectIdentityOrTypeGuard, // Unbox expects a given type, bails out if it doesn't get it. Bailout_NonInt32Input, Bailout_NonNumericInput, // unboxing a double works with int32 too Bailout_NonBooleanInput, Bailout_NonObjectInput, Bailout_NonStringInput, Bailout_NonSymbolInput, // SIMD Unbox expects a given type, bails out if it doesn't match. Bailout_NonSimdInt32x4Input, Bailout_NonSimdFloat32x4Input, // Atomic operations require shared memory, bail out if the typed array // maps unshared memory. Bailout_NonSharedTypedArrayInput, // For the initial snapshot when entering a function. Bailout_InitialState, // We hit a |debugger;| statement. Bailout_Debugger, // |this| used uninitialized in a derived constructor Bailout_UninitializedThis, // Derived constructors must return object or undefined Bailout_BadDerivedConstructorReturn, // We hit this code for the first time. Bailout_FirstExecution, // END Normal bailouts // Bailouts caused by invalid assumptions based on Baseline code. // Causes immediate invalidation. // Like Bailout_Overflow, but causes immediate invalidation. Bailout_OverflowInvalidate, // Like NonStringInput, but should cause immediate invalidation. // Used for jsop_iternext. Bailout_NonStringInputInvalidate, // Used for integer division, multiplication and modulo. // If there's a remainder, bails to return a double. // Can also signal overflow or result of -0. // Can also signal division by 0 (returns inf, a double). Bailout_DoubleOutput, // END Invalid assumptions bailouts // A bailout at the very start of a function indicates that there may be // a type mismatch in the arguments that necessitates a reflow. Bailout_ArgumentCheck, // A bailout triggered by a bounds-check failure. Bailout_BoundsCheck, // A bailout triggered by a neutered typed object. Bailout_Neutered, // A shape guard based on TI information failed. // (We saw an object whose shape does not match that / any of those observed // by the baseline IC.) Bailout_ShapeGuard, // When we're trying to use an uninitialized lexical. Bailout_UninitializedLexical, // A bailout to baseline from Ion on exception to handle Debugger hooks. Bailout_IonExceptionDebugMode }; inline const char* BailoutKindString(BailoutKind kind) { switch (kind) { // Normal bailouts. case Bailout_Inevitable: return "Bailout_Inevitable"; case Bailout_DuringVMCall: return "Bailout_DuringVMCall"; case Bailout_NonJSFunctionCallee: return "Bailout_NonJSFunctionCallee"; case Bailout_DynamicNameNotFound: return "Bailout_DynamicNameNotFound"; case Bailout_StringArgumentsEval: return "Bailout_StringArgumentsEval"; case Bailout_Overflow: return "Bailout_Overflow"; case Bailout_Round: return "Bailout_Round"; case Bailout_NonPrimitiveInput: return "Bailout_NonPrimitiveInput"; case Bailout_PrecisionLoss: return "Bailout_PrecisionLoss"; case Bailout_TypeBarrierO: return "Bailout_TypeBarrierO"; case Bailout_TypeBarrierV: return "Bailout_TypeBarrierV"; case Bailout_MonitorTypes: return "Bailout_MonitorTypes"; case Bailout_Hole: return "Bailout_Hole"; case Bailout_NegativeIndex: return "Bailout_NegativeIndex"; case Bailout_ObjectIdentityOrTypeGuard: return "Bailout_ObjectIdentityOrTypeGuard"; case Bailout_NonInt32Input: return "Bailout_NonInt32Input"; case Bailout_NonNumericInput: return "Bailout_NonNumericInput"; case Bailout_NonBooleanInput: return "Bailout_NonBooleanInput"; case Bailout_NonObjectInput: return "Bailout_NonObjectInput"; case Bailout_NonStringInput: return "Bailout_NonStringInput"; case Bailout_NonSymbolInput: return "Bailout_NonSymbolInput"; case Bailout_NonSimdInt32x4Input: return "Bailout_NonSimdInt32x4Input"; case Bailout_NonSimdFloat32x4Input: return "Bailout_NonSimdFloat32x4Input"; case Bailout_NonSharedTypedArrayInput: return "Bailout_NonSharedTypedArrayInput"; case Bailout_InitialState: return "Bailout_InitialState"; case Bailout_Debugger: return "Bailout_Debugger"; case Bailout_UninitializedThis: return "Bailout_UninitializedThis"; case Bailout_BadDerivedConstructorReturn: return "Bailout_BadDerivedConstructorReturn"; case Bailout_FirstExecution: return "Bailout_FirstExecution"; // Bailouts caused by invalid assumptions. case Bailout_OverflowInvalidate: return "Bailout_OverflowInvalidate"; case Bailout_NonStringInputInvalidate: return "Bailout_NonStringInputInvalidate"; case Bailout_DoubleOutput: return "Bailout_DoubleOutput"; // Other bailouts. case Bailout_ArgumentCheck: return "Bailout_ArgumentCheck"; case Bailout_BoundsCheck: return "Bailout_BoundsCheck"; case Bailout_Neutered: return "Bailout_Neutered"; case Bailout_ShapeGuard: return "Bailout_ShapeGuard"; case Bailout_UninitializedLexical: return "Bailout_UninitializedLexical"; case Bailout_IonExceptionDebugMode: return "Bailout_IonExceptionDebugMode"; default: MOZ_CRASH("Invalid BailoutKind"); } } static const uint32_t ELEMENT_TYPE_BITS = 5; static const uint32_t ELEMENT_TYPE_SHIFT = 0; static const uint32_t ELEMENT_TYPE_MASK = (1 << ELEMENT_TYPE_BITS) - 1; static const uint32_t VECTOR_SCALE_BITS = 2; static const uint32_t VECTOR_SCALE_SHIFT = ELEMENT_TYPE_BITS + ELEMENT_TYPE_SHIFT; static const uint32_t VECTOR_SCALE_MASK = (1 << VECTOR_SCALE_BITS) - 1; class SimdConstant { public: enum Type { Int32x4, Float32x4, Undefined = -1 }; typedef int32_t I32x4[4]; typedef float F32x4[4]; private: Type type_; union { I32x4 i32x4; F32x4 f32x4; } u; bool defined() const { return type_ != Undefined; } void fillInt32x4(int32_t x, int32_t y, int32_t z, int32_t w) { type_ = Int32x4; u.i32x4[0] = x; u.i32x4[1] = y; u.i32x4[2] = z; u.i32x4[3] = w; } void fillFloat32x4(float x, float y, float z, float w) { type_ = Float32x4; u.f32x4[0] = x; u.f32x4[1] = y; u.f32x4[2] = z; u.f32x4[3] = w; } public: // Doesn't have a default constructor, as it would prevent it from being // included in unions. static SimdConstant CreateX4(int32_t x, int32_t y, int32_t z, int32_t w) { SimdConstant cst; cst.fillInt32x4(x, y, z, w); return cst; } static SimdConstant CreateX4(const int32_t* array) { SimdConstant cst; cst.fillInt32x4(array[0], array[1], array[2], array[3]); return cst; } static SimdConstant SplatX4(int32_t v) { SimdConstant cst; cst.fillInt32x4(v, v, v, v); return cst; } static SimdConstant CreateX4(float x, float y, float z, float w) { SimdConstant cst; cst.fillFloat32x4(x, y, z, w); return cst; } static SimdConstant CreateX4(const float* array) { SimdConstant cst; cst.fillFloat32x4(array[0], array[1], array[2], array[3]); return cst; } static SimdConstant SplatX4(float v) { SimdConstant cst; cst.fillFloat32x4(v, v, v, v); return cst; } uint32_t length() const { MOZ_ASSERT(defined()); switch(type_) { case Int32x4: case Float32x4: return 4; case Undefined: break; } MOZ_CRASH("Unexpected SIMD kind"); } Type type() const { MOZ_ASSERT(defined()); return type_; } const I32x4& asInt32x4() const { MOZ_ASSERT(defined() && type_ == Int32x4); return u.i32x4; } const F32x4& asFloat32x4() const { MOZ_ASSERT(defined() && type_ == Float32x4); return u.f32x4; } bool operator==(const SimdConstant& rhs) const { MOZ_ASSERT(defined() && rhs.defined()); if (type() != rhs.type()) return false; return memcmp(&u, &rhs.u, sizeof(u)) == 0; } // SimdConstant is a HashPolicy typedef SimdConstant Lookup; static HashNumber hash(const SimdConstant& val) { uint32_t hash = mozilla::HashBytes(&val.u, sizeof(val.u)); return mozilla::AddToHash(hash, val.type_); } static bool match(const SimdConstant& lhs, const SimdConstant& rhs) { return lhs == rhs; } }; // The ordering of this enumeration is important: Anything < Value is a // specialized type. Furthermore, anything < String has trivial conversion to // a number. enum MIRType { MIRType_Undefined, MIRType_Null, MIRType_Boolean, MIRType_Int32, MIRType_Double, MIRType_Float32, MIRType_String, MIRType_Symbol, MIRType_Object, MIRType_MagicOptimizedArguments, // JS_OPTIMIZED_ARGUMENTS magic value. MIRType_MagicOptimizedOut, // JS_OPTIMIZED_OUT magic value. MIRType_MagicHole, // JS_ELEMENTS_HOLE magic value. MIRType_MagicIsConstructing, // JS_IS_CONSTRUCTING magic value. MIRType_MagicUninitializedLexical, // JS_UNINITIALIZED_LEXICAL magic value. MIRType_Value, MIRType_SinCosDouble, // Optimizing a sin/cos to sincos. MIRType_ObjectOrNull, MIRType_None, // Invalid, used as a placeholder. MIRType_Slots, // A slots vector MIRType_Elements, // An elements vector MIRType_Pointer, // An opaque pointer that receives no special treatment MIRType_Shape, // A Shape pointer. MIRType_ObjectGroup, // An ObjectGroup pointer. MIRType_Last = MIRType_ObjectGroup, MIRType_Float32x4 = MIRType_Float32 | (2 << VECTOR_SCALE_SHIFT), MIRType_Int32x4 = MIRType_Int32 | (2 << VECTOR_SCALE_SHIFT), MIRType_Doublex2 = MIRType_Double | (1 << VECTOR_SCALE_SHIFT) }; static inline MIRType MIRTypeFromValueType(JSValueType type) { // This function does not deal with magic types. Magic constants should be // filtered out in MIRTypeFromValue. switch (type) { case JSVAL_TYPE_DOUBLE: return MIRType_Double; case JSVAL_TYPE_INT32: return MIRType_Int32; case JSVAL_TYPE_UNDEFINED: return MIRType_Undefined; case JSVAL_TYPE_STRING: return MIRType_String; case JSVAL_TYPE_SYMBOL: return MIRType_Symbol; case JSVAL_TYPE_BOOLEAN: return MIRType_Boolean; case JSVAL_TYPE_NULL: return MIRType_Null; case JSVAL_TYPE_OBJECT: return MIRType_Object; case JSVAL_TYPE_UNKNOWN: return MIRType_Value; default: MOZ_CRASH("unexpected jsval type"); } } static inline JSValueType ValueTypeFromMIRType(MIRType type) { switch (type) { case MIRType_Undefined: return JSVAL_TYPE_UNDEFINED; case MIRType_Null: return JSVAL_TYPE_NULL; case MIRType_Boolean: return JSVAL_TYPE_BOOLEAN; case MIRType_Int32: return JSVAL_TYPE_INT32; case MIRType_Float32: // Fall through, there's no JSVAL for Float32 case MIRType_Double: return JSVAL_TYPE_DOUBLE; case MIRType_String: return JSVAL_TYPE_STRING; case MIRType_Symbol: return JSVAL_TYPE_SYMBOL; case MIRType_MagicOptimizedArguments: case MIRType_MagicOptimizedOut: case MIRType_MagicHole: case MIRType_MagicIsConstructing: case MIRType_MagicUninitializedLexical: return JSVAL_TYPE_MAGIC; default: MOZ_ASSERT(type == MIRType_Object); return JSVAL_TYPE_OBJECT; } } static inline JSValueTag MIRTypeToTag(MIRType type) { return JSVAL_TYPE_TO_TAG(ValueTypeFromMIRType(type)); } static inline const char* StringFromMIRType(MIRType type) { switch (type) { case MIRType_Undefined: return "Undefined"; case MIRType_Null: return "Null"; case MIRType_Boolean: return "Bool"; case MIRType_Int32: return "Int32"; case MIRType_Double: return "Double"; case MIRType_Float32: return "Float32"; case MIRType_String: return "String"; case MIRType_Symbol: return "Symbol"; case MIRType_Object: return "Object"; case MIRType_MagicOptimizedArguments: return "MagicOptimizedArguments"; case MIRType_MagicOptimizedOut: return "MagicOptimizedOut"; case MIRType_MagicHole: return "MagicHole"; case MIRType_MagicIsConstructing: return "MagicIsConstructing"; case MIRType_MagicUninitializedLexical: return "MagicUninitializedLexical"; case MIRType_Value: return "Value"; case MIRType_SinCosDouble: return "SinCosDouble"; case MIRType_ObjectOrNull: return "ObjectOrNull"; case MIRType_None: return "None"; case MIRType_Slots: return "Slots"; case MIRType_Elements: return "Elements"; case MIRType_Pointer: return "Pointer"; case MIRType_Shape: return "Shape"; case MIRType_ObjectGroup: return "ObjectGroup"; case MIRType_Float32x4: return "Float32x4"; case MIRType_Int32x4: return "Int32x4"; case MIRType_Doublex2: return "Doublex2"; default: MOZ_CRASH("Unknown MIRType."); } } static inline bool IsNumberType(MIRType type) { return type == MIRType_Int32 || type == MIRType_Double || type == MIRType_Float32; } static inline bool IsFloatType(MIRType type) { return type == MIRType_Int32 || type == MIRType_Float32; } static inline bool IsFloatingPointType(MIRType type) { return type == MIRType_Double || type == MIRType_Float32; } static inline bool IsNullOrUndefined(MIRType type) { return type == MIRType_Null || type == MIRType_Undefined; } static inline bool IsSimdType(MIRType type) { return type == MIRType_Int32x4 || type == MIRType_Float32x4; } static inline bool IsFloatingPointSimdType(MIRType type) { return type == MIRType_Float32x4; } static inline bool IsIntegerSimdType(MIRType type) { return type == MIRType_Int32x4; } static inline bool IsMagicType(MIRType type) { return type == MIRType_MagicHole || type == MIRType_MagicOptimizedOut || type == MIRType_MagicIsConstructing || type == MIRType_MagicOptimizedArguments || type == MIRType_MagicUninitializedLexical; } // Returns the number of vector elements (hereby called "length") for a given // SIMD kind. It is the Y part of the name "Foo x Y". static inline unsigned SimdTypeToLength(MIRType type) { MOZ_ASSERT(IsSimdType(type)); return 1 << ((type >> VECTOR_SCALE_SHIFT) & VECTOR_SCALE_MASK); } static inline MIRType ScalarTypeToMIRType(Scalar::Type type) { switch (type) { case Scalar::Int8: case Scalar::Uint8: case Scalar::Int16: case Scalar::Uint16: case Scalar::Int32: case Scalar::Uint32: case Scalar::Uint8Clamped: return MIRType_Int32; case Scalar::Float32: return MIRType_Float32; case Scalar::Float64: return MIRType_Double; case Scalar::Float32x4: return MIRType_Float32x4; case Scalar::Int32x4: return MIRType_Int32x4; case Scalar::MaxTypedArrayViewType: break; } MOZ_CRASH("unexpected SIMD kind"); } static inline unsigned ScalarTypeToLength(Scalar::Type type) { switch (type) { case Scalar::Int8: case Scalar::Uint8: case Scalar::Int16: case Scalar::Uint16: case Scalar::Int32: case Scalar::Uint32: case Scalar::Float32: case Scalar::Float64: case Scalar::Uint8Clamped: return 1; case Scalar::Float32x4: case Scalar::Int32x4: return 4; case Scalar::MaxTypedArrayViewType: break; } MOZ_CRASH("unexpected SIMD kind"); } // Get the type of the individual lanes in a SIMD type. // For example, Int32x4 -> Int32, FLoat32x4 -> Float32 etc. static inline MIRType SimdTypeToLaneType(MIRType type) { MOZ_ASSERT(IsSimdType(type)); static_assert(MIRType_Last <= ELEMENT_TYPE_MASK, "ELEMENT_TYPE_MASK should be larger than the last MIRType"); return MIRType((type >> ELEMENT_TYPE_SHIFT) & ELEMENT_TYPE_MASK); } // Indicates a lane in a SIMD register: X for the first lane, Y for the second, // Z for the third (if any), W for the fourth (if any). enum SimdLane { LaneX = 0x0, LaneY = 0x1, LaneZ = 0x2, LaneW = 0x3 }; #ifdef DEBUG // Track the pipeline of opcodes which has produced a snapshot. #define TRACK_SNAPSHOTS 1 // Make sure registers are not modified between an instruction and // its OsiPoint. #define CHECK_OSIPOINT_REGISTERS 1 #endif // DEBUG enum { ArgType_General = 0x1, ArgType_Double = 0x2, ArgType_Float32 = 0x3, RetType_Shift = 0x0, ArgType_Shift = 0x2, ArgType_Mask = 0x3 }; enum ABIFunctionType { // VM functions that take 0-9 non-double arguments // and return a non-double value. Args_General0 = ArgType_General << RetType_Shift, Args_General1 = Args_General0 | (ArgType_General << (ArgType_Shift * 1)), Args_General2 = Args_General1 | (ArgType_General << (ArgType_Shift * 2)), Args_General3 = Args_General2 | (ArgType_General << (ArgType_Shift * 3)), Args_General4 = Args_General3 | (ArgType_General << (ArgType_Shift * 4)), Args_General5 = Args_General4 | (ArgType_General << (ArgType_Shift * 5)), Args_General6 = Args_General5 | (ArgType_General << (ArgType_Shift * 6)), Args_General7 = Args_General6 | (ArgType_General << (ArgType_Shift * 7)), Args_General8 = Args_General7 | (ArgType_General << (ArgType_Shift * 8)), // double f() Args_Double_None = ArgType_Double << RetType_Shift, // int f(double) Args_Int_Double = Args_General0 | (ArgType_Double << ArgType_Shift), // float f(float) Args_Float32_Float32 = (ArgType_Float32 << RetType_Shift) | (ArgType_Float32 << ArgType_Shift), // double f(double) Args_Double_Double = Args_Double_None | (ArgType_Double << ArgType_Shift), // double f(int) Args_Double_Int = Args_Double_None | (ArgType_General << ArgType_Shift), // double f(double, int) Args_Double_DoubleInt = Args_Double_None | (ArgType_General << (ArgType_Shift * 1)) | (ArgType_Double << (ArgType_Shift * 2)), // double f(double, double) Args_Double_DoubleDouble = Args_Double_Double | (ArgType_Double << (ArgType_Shift * 2)), // double f(int, double) Args_Double_IntDouble = Args_Double_None | (ArgType_Double << (ArgType_Shift * 1)) | (ArgType_General << (ArgType_Shift * 2)), // int f(int, double) Args_Int_IntDouble = Args_General0 | (ArgType_Double << (ArgType_Shift * 1)) | (ArgType_General << (ArgType_Shift * 2)), // double f(double, double, double) Args_Double_DoubleDoubleDouble = Args_Double_DoubleDouble | (ArgType_Double << (ArgType_Shift * 3)), // double f(double, double, double, double) Args_Double_DoubleDoubleDoubleDouble = Args_Double_DoubleDoubleDouble | (ArgType_Double << (ArgType_Shift * 4)), // int f(double, int, int) Args_Int_DoubleIntInt = Args_General0 | (ArgType_General << (ArgType_Shift * 1)) | (ArgType_General << (ArgType_Shift * 2)) | (ArgType_Double << (ArgType_Shift * 3)), // int f(int, double, int, int) Args_Int_IntDoubleIntInt = Args_General0 | (ArgType_General << (ArgType_Shift * 1)) | (ArgType_General << (ArgType_Shift * 2)) | (ArgType_Double << (ArgType_Shift * 3)) | (ArgType_General << (ArgType_Shift * 4)) }; enum class BarrierKind : uint32_t { // No barrier is needed. NoBarrier, // The barrier only has to check the value's type tag is in the TypeSet. // Specific object types don't have to be checked. TypeTagOnly, // Check if the value is in the TypeSet, including the object type if it's // an object. TypeSet }; } // namespace jit } // namespace js #endif /* jit_IonTypes_h */