mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-09-26 23:54:56 +00:00
1335 lines
47 KiB
C++
1335 lines
47 KiB
C++
/* -*- 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/. */
|
|
|
|
/* Definitions related to javascript type inference. */
|
|
|
|
#ifndef vm_TypeInference_h
|
|
#define vm_TypeInference_h
|
|
|
|
#include "mozilla/MemoryReporting.h"
|
|
|
|
#include "jsalloc.h"
|
|
#include "jsfriendapi.h"
|
|
#include "jstypes.h"
|
|
|
|
#include "ds/IdValuePair.h"
|
|
#include "ds/LifoAlloc.h"
|
|
#include "gc/Barrier.h"
|
|
#include "gc/Marking.h"
|
|
#include "jit/IonTypes.h"
|
|
#include "js/UbiNode.h"
|
|
#include "js/Utility.h"
|
|
#include "js/Vector.h"
|
|
#include "vm/TaggedProto.h"
|
|
|
|
namespace js {
|
|
|
|
namespace jit {
|
|
struct IonScript;
|
|
class JitAllocPolicy;
|
|
class TempAllocator;
|
|
} // namespace jit
|
|
|
|
struct TypeZone;
|
|
class TypeConstraint;
|
|
class TypeNewScript;
|
|
class CompilerConstraintList;
|
|
class HeapTypeSetKey;
|
|
|
|
/*
|
|
* Type inference memory management overview.
|
|
*
|
|
* Type information about the values observed within scripts and about the
|
|
* contents of the heap is accumulated as the program executes. Compilation
|
|
* accumulates constraints relating type information on the heap with the
|
|
* compilations that should be invalidated when those types change. Type
|
|
* information and constraints are allocated in the zone's typeLifoAlloc,
|
|
* and on GC all data referring to live things is copied into a new allocator.
|
|
* Thus, type set and constraints only hold weak references.
|
|
*/
|
|
|
|
/* Flags and other state stored in TypeSet::flags */
|
|
enum : uint32_t {
|
|
TYPE_FLAG_UNDEFINED = 0x1,
|
|
TYPE_FLAG_NULL = 0x2,
|
|
TYPE_FLAG_BOOLEAN = 0x4,
|
|
TYPE_FLAG_INT32 = 0x8,
|
|
TYPE_FLAG_DOUBLE = 0x10,
|
|
TYPE_FLAG_STRING = 0x20,
|
|
TYPE_FLAG_SYMBOL = 0x40,
|
|
TYPE_FLAG_LAZYARGS = 0x80,
|
|
TYPE_FLAG_ANYOBJECT = 0x100,
|
|
|
|
/* Mask containing all primitives */
|
|
TYPE_FLAG_PRIMITIVE = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_BOOLEAN |
|
|
TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_STRING |
|
|
TYPE_FLAG_SYMBOL,
|
|
|
|
/* Mask/shift for the number of objects in objectSet */
|
|
TYPE_FLAG_OBJECT_COUNT_MASK = 0x3e00,
|
|
TYPE_FLAG_OBJECT_COUNT_SHIFT = 9,
|
|
TYPE_FLAG_OBJECT_COUNT_LIMIT = 7,
|
|
TYPE_FLAG_DOMOBJECT_COUNT_LIMIT =
|
|
TYPE_FLAG_OBJECT_COUNT_MASK >> TYPE_FLAG_OBJECT_COUNT_SHIFT,
|
|
|
|
/* Whether the contents of this type set are totally unknown. */
|
|
TYPE_FLAG_UNKNOWN = 0x00004000,
|
|
|
|
/* Mask of normal type flags on a type set. */
|
|
TYPE_FLAG_BASE_MASK = 0x000041ff,
|
|
|
|
/* Additional flags for HeapTypeSet sets. */
|
|
|
|
/*
|
|
* Whether the property has ever been deleted or reconfigured to behave
|
|
* differently from a plain data property, other than making the property
|
|
* non-writable.
|
|
*/
|
|
TYPE_FLAG_NON_DATA_PROPERTY = 0x00008000,
|
|
|
|
/* Whether the property has ever been made non-writable. */
|
|
TYPE_FLAG_NON_WRITABLE_PROPERTY = 0x00010000,
|
|
|
|
/* Whether the property might not be constant. */
|
|
TYPE_FLAG_NON_CONSTANT_PROPERTY = 0x00020000,
|
|
|
|
/*
|
|
* Whether the property is definitely in a particular slot on all objects
|
|
* from which it has not been deleted or reconfigured. For singletons
|
|
* this may be a fixed or dynamic slot, and for other objects this will be
|
|
* a fixed slot.
|
|
*
|
|
* If the property is definite, mask and shift storing the slot + 1.
|
|
* Otherwise these bits are clear.
|
|
*/
|
|
TYPE_FLAG_DEFINITE_MASK = 0xfffc0000,
|
|
TYPE_FLAG_DEFINITE_SHIFT = 18
|
|
};
|
|
typedef uint32_t TypeFlags;
|
|
|
|
/* Flags and other state stored in ObjectGroup::Flags */
|
|
enum : uint32_t {
|
|
/* Whether this group is associated with some allocation site. */
|
|
OBJECT_FLAG_FROM_ALLOCATION_SITE = 0x1,
|
|
|
|
/* Whether this group is associated with a single object. */
|
|
OBJECT_FLAG_SINGLETON = 0x2,
|
|
|
|
/*
|
|
* Whether this group is used by objects whose singleton groups have not
|
|
* been created yet.
|
|
*/
|
|
OBJECT_FLAG_LAZY_SINGLETON = 0x4,
|
|
|
|
/* Mask/shift for the number of properties in propertySet */
|
|
OBJECT_FLAG_PROPERTY_COUNT_MASK = 0xfff8,
|
|
OBJECT_FLAG_PROPERTY_COUNT_SHIFT = 3,
|
|
OBJECT_FLAG_PROPERTY_COUNT_LIMIT =
|
|
OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT,
|
|
|
|
/* Whether any objects this represents may have sparse indexes. */
|
|
OBJECT_FLAG_SPARSE_INDEXES = 0x00010000,
|
|
|
|
/* Whether any objects this represents may not have packed dense elements. */
|
|
OBJECT_FLAG_NON_PACKED = 0x00020000,
|
|
|
|
/*
|
|
* Whether any objects this represents may be arrays whose length does not
|
|
* fit in an int32.
|
|
*/
|
|
OBJECT_FLAG_LENGTH_OVERFLOW = 0x00040000,
|
|
|
|
/* Whether any objects have been iterated over. */
|
|
OBJECT_FLAG_ITERATED = 0x00080000,
|
|
|
|
/* For a global object, whether flags were set on the RegExpStatics. */
|
|
OBJECT_FLAG_REGEXP_FLAGS_SET = 0x00100000,
|
|
|
|
/*
|
|
* For the function on a run-once script, whether the function has actually
|
|
* run multiple times.
|
|
*/
|
|
OBJECT_FLAG_RUNONCE_INVALIDATED = 0x00200000,
|
|
|
|
/*
|
|
* For a global object, whether any array buffers in this compartment with
|
|
* typed object views have been neutered.
|
|
*/
|
|
OBJECT_FLAG_TYPED_OBJECT_NEUTERED = 0x00400000,
|
|
|
|
/*
|
|
* Whether objects with this type should be allocated directly in the
|
|
* tenured heap.
|
|
*/
|
|
OBJECT_FLAG_PRE_TENURE = 0x00800000,
|
|
|
|
/* Whether objects with this type might have copy on write elements. */
|
|
OBJECT_FLAG_COPY_ON_WRITE = 0x01000000,
|
|
|
|
/* Whether this type has had its 'new' script cleared in the past. */
|
|
OBJECT_FLAG_NEW_SCRIPT_CLEARED = 0x02000000,
|
|
|
|
/*
|
|
* Whether all properties of this object are considered unknown.
|
|
* If set, all other flags in DYNAMIC_MASK will also be set.
|
|
*/
|
|
OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x04000000,
|
|
|
|
/* Flags which indicate dynamic properties of represented objects. */
|
|
OBJECT_FLAG_DYNAMIC_MASK = 0x07ff0000,
|
|
|
|
// Mask/shift for the kind of addendum attached to this group.
|
|
OBJECT_FLAG_ADDENDUM_MASK = 0x38000000,
|
|
OBJECT_FLAG_ADDENDUM_SHIFT = 27,
|
|
|
|
// Mask/shift for this group's generation. If out of sync with the
|
|
// TypeZone's generation, this group hasn't been swept yet.
|
|
OBJECT_FLAG_GENERATION_MASK = 0x40000000,
|
|
OBJECT_FLAG_GENERATION_SHIFT = 30,
|
|
};
|
|
typedef uint32_t ObjectGroupFlags;
|
|
|
|
class StackTypeSet;
|
|
class HeapTypeSet;
|
|
class TemporaryTypeSet;
|
|
|
|
/*
|
|
* Information about the set of types associated with an lvalue. There are
|
|
* three kinds of type sets:
|
|
*
|
|
* - StackTypeSet are associated with TypeScripts, for arguments and values
|
|
* observed at property reads. These are implicitly frozen on compilation
|
|
* and only have constraints added to them which can trigger invalidation of
|
|
* TypeNewScript information.
|
|
*
|
|
* - HeapTypeSet are associated with the properties of ObjectGroups. These
|
|
* may have constraints added to them to trigger invalidation of either
|
|
* compiled code or TypeNewScript information.
|
|
*
|
|
* - TemporaryTypeSet are created during compilation and do not outlive
|
|
* that compilation.
|
|
*
|
|
* The contents of a type set completely describe the values that a particular
|
|
* lvalue might have, except for the following cases:
|
|
*
|
|
* - If an object's prototype or class is dynamically mutated, its group will
|
|
* change. Type sets containing the old group will not necessarily contain
|
|
* the new group. When this occurs, the properties of the old and new group
|
|
* will both be marked as unknown, which will prevent Ion from optimizing
|
|
* based on the object's type information.
|
|
*
|
|
* - If an unboxed object is converted to a native object, its group will also
|
|
* change and type sets containing the old group will not necessarily contain
|
|
* the new group. Unlike the above case, this will not degrade property type
|
|
* information, but Ion will no longer optimize unboxed objects with the old
|
|
* group.
|
|
*/
|
|
class TypeSet
|
|
{
|
|
public:
|
|
// Type set entry for either a JSObject with singleton type or a
|
|
// non-singleton ObjectGroup.
|
|
class ObjectKey {
|
|
public:
|
|
static intptr_t keyBits(ObjectKey* obj) { return (intptr_t) obj; }
|
|
static ObjectKey* getKey(ObjectKey* obj) { return obj; }
|
|
|
|
static inline ObjectKey* get(JSObject* obj);
|
|
static inline ObjectKey* get(ObjectGroup* group);
|
|
|
|
bool isGroup() {
|
|
return (uintptr_t(this) & 1) == 0;
|
|
}
|
|
bool isSingleton() {
|
|
return (uintptr_t(this) & 1) != 0;
|
|
}
|
|
|
|
inline ObjectGroup* group();
|
|
inline JSObject* singleton();
|
|
|
|
inline ObjectGroup* groupNoBarrier();
|
|
inline JSObject* singletonNoBarrier();
|
|
|
|
const Class* clasp();
|
|
TaggedProto proto();
|
|
TypeNewScript* newScript();
|
|
|
|
bool unknownProperties();
|
|
bool hasFlags(CompilerConstraintList* constraints, ObjectGroupFlags flags);
|
|
bool hasStableClassAndProto(CompilerConstraintList* constraints);
|
|
void watchStateChangeForInlinedCall(CompilerConstraintList* constraints);
|
|
void watchStateChangeForTypedArrayData(CompilerConstraintList* constraints);
|
|
void watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints);
|
|
HeapTypeSetKey property(jsid id);
|
|
void ensureTrackedProperty(JSContext* cx, jsid id);
|
|
|
|
ObjectGroup* maybeGroup();
|
|
};
|
|
|
|
// Information about a single concrete type. We pack this into one word,
|
|
// where small values are particular primitive or other singleton types and
|
|
// larger values are either specific JS objects or object groups.
|
|
class Type : public JS::Traceable
|
|
{
|
|
friend class TypeSet;
|
|
|
|
uintptr_t data;
|
|
explicit Type(uintptr_t data) : data(data) {}
|
|
|
|
public:
|
|
|
|
uintptr_t raw() const { return data; }
|
|
|
|
bool isPrimitive() const {
|
|
return data < JSVAL_TYPE_OBJECT;
|
|
}
|
|
|
|
bool isPrimitive(JSValueType type) const {
|
|
MOZ_ASSERT(type < JSVAL_TYPE_OBJECT);
|
|
return (uintptr_t) type == data;
|
|
}
|
|
|
|
JSValueType primitive() const {
|
|
MOZ_ASSERT(isPrimitive());
|
|
return (JSValueType) data;
|
|
}
|
|
|
|
bool isMagicArguments() const {
|
|
return primitive() == JSVAL_TYPE_MAGIC;
|
|
}
|
|
|
|
bool isSomeObject() const {
|
|
return data == JSVAL_TYPE_OBJECT || data > JSVAL_TYPE_UNKNOWN;
|
|
}
|
|
|
|
bool isAnyObject() const {
|
|
return data == JSVAL_TYPE_OBJECT;
|
|
}
|
|
|
|
bool isUnknown() const {
|
|
return data == JSVAL_TYPE_UNKNOWN;
|
|
}
|
|
|
|
/* Accessors for types that are either JSObject or ObjectGroup. */
|
|
|
|
bool isObject() const {
|
|
MOZ_ASSERT(!isAnyObject() && !isUnknown());
|
|
return data > JSVAL_TYPE_UNKNOWN;
|
|
}
|
|
|
|
bool isObjectUnchecked() const {
|
|
return data > JSVAL_TYPE_UNKNOWN;
|
|
}
|
|
|
|
inline ObjectKey* objectKey() const;
|
|
|
|
/* Accessors for JSObject types */
|
|
|
|
bool isSingleton() const {
|
|
return isObject() && !!(data & 1);
|
|
}
|
|
bool isSingletonUnchecked() const {
|
|
return isObjectUnchecked() && !!(data & 1);
|
|
}
|
|
|
|
inline JSObject* singleton() const;
|
|
inline JSObject* singletonNoBarrier() const;
|
|
|
|
/* Accessors for ObjectGroup types */
|
|
|
|
bool isGroup() const {
|
|
return isObject() && !(data & 1);
|
|
}
|
|
bool isGroupUnchecked() const {
|
|
return isObjectUnchecked() && !(data & 1);
|
|
}
|
|
|
|
inline ObjectGroup* group() const;
|
|
inline ObjectGroup* groupNoBarrier() const;
|
|
|
|
static void trace(Type* v, JSTracer* trc) {
|
|
MarkTypeUnbarriered(trc, v, "TypeSet::Type");
|
|
}
|
|
|
|
bool operator == (Type o) const { return data == o.data; }
|
|
bool operator != (Type o) const { return data != o.data; }
|
|
};
|
|
|
|
static inline Type UndefinedType() { return Type(JSVAL_TYPE_UNDEFINED); }
|
|
static inline Type NullType() { return Type(JSVAL_TYPE_NULL); }
|
|
static inline Type BooleanType() { return Type(JSVAL_TYPE_BOOLEAN); }
|
|
static inline Type Int32Type() { return Type(JSVAL_TYPE_INT32); }
|
|
static inline Type DoubleType() { return Type(JSVAL_TYPE_DOUBLE); }
|
|
static inline Type StringType() { return Type(JSVAL_TYPE_STRING); }
|
|
static inline Type SymbolType() { return Type(JSVAL_TYPE_SYMBOL); }
|
|
static inline Type MagicArgType() { return Type(JSVAL_TYPE_MAGIC); }
|
|
static inline Type AnyObjectType() { return Type(JSVAL_TYPE_OBJECT); }
|
|
static inline Type UnknownType() { return Type(JSVAL_TYPE_UNKNOWN); }
|
|
|
|
static inline Type PrimitiveType(JSValueType type) {
|
|
MOZ_ASSERT(type < JSVAL_TYPE_UNKNOWN);
|
|
return Type(type);
|
|
}
|
|
|
|
static inline Type ObjectType(JSObject* obj);
|
|
static inline Type ObjectType(ObjectGroup* group);
|
|
static inline Type ObjectType(ObjectKey* key);
|
|
|
|
static const char* NonObjectTypeString(Type type);
|
|
|
|
#ifdef DEBUG
|
|
static const char* TypeString(Type type);
|
|
static const char* ObjectGroupString(ObjectGroup* group);
|
|
#else
|
|
static const char* TypeString(Type type) { return nullptr; }
|
|
static const char* ObjectGroupString(ObjectGroup* group) { return nullptr; }
|
|
#endif
|
|
|
|
protected:
|
|
/* Flags for this type set. */
|
|
TypeFlags flags;
|
|
|
|
/* Possible objects this type set can represent. */
|
|
ObjectKey** objectSet;
|
|
|
|
public:
|
|
|
|
TypeSet()
|
|
: flags(0), objectSet(nullptr)
|
|
{}
|
|
|
|
void print(FILE* fp = stderr);
|
|
|
|
/* Whether this set contains a specific type. */
|
|
inline bool hasType(Type type) const;
|
|
|
|
TypeFlags baseFlags() const { return flags & TYPE_FLAG_BASE_MASK; }
|
|
bool unknown() const { return !!(flags & TYPE_FLAG_UNKNOWN); }
|
|
bool unknownObject() const { return !!(flags & (TYPE_FLAG_UNKNOWN | TYPE_FLAG_ANYOBJECT)); }
|
|
bool empty() const { return !baseFlags() && !baseObjectCount(); }
|
|
|
|
bool hasAnyFlag(TypeFlags flags) const {
|
|
MOZ_ASSERT((flags & TYPE_FLAG_BASE_MASK) == flags);
|
|
return !!(baseFlags() & flags);
|
|
}
|
|
|
|
bool nonDataProperty() const {
|
|
return flags & TYPE_FLAG_NON_DATA_PROPERTY;
|
|
}
|
|
bool nonWritableProperty() const {
|
|
return flags & TYPE_FLAG_NON_WRITABLE_PROPERTY;
|
|
}
|
|
bool nonConstantProperty() const {
|
|
return flags & TYPE_FLAG_NON_CONSTANT_PROPERTY;
|
|
}
|
|
bool definiteProperty() const { return flags & TYPE_FLAG_DEFINITE_MASK; }
|
|
unsigned definiteSlot() const {
|
|
MOZ_ASSERT(definiteProperty());
|
|
return (flags >> TYPE_FLAG_DEFINITE_SHIFT) - 1;
|
|
}
|
|
|
|
/* Join two type sets into a new set. The result should not be modified further. */
|
|
static TemporaryTypeSet* unionSets(TypeSet* a, TypeSet* b, LifoAlloc* alloc);
|
|
/* Return the intersection of the 2 TypeSets. The result should not be modified further */
|
|
static TemporaryTypeSet* intersectSets(TemporaryTypeSet* a, TemporaryTypeSet* b, LifoAlloc* alloc);
|
|
/*
|
|
* Returns a copy of TypeSet a excluding/removing the types in TypeSet b.
|
|
* TypeSet b can only contain primitives or be any object. No support for
|
|
* specific objects. The result should not be modified further.
|
|
*/
|
|
static TemporaryTypeSet* removeSet(TemporaryTypeSet* a, TemporaryTypeSet* b, LifoAlloc* alloc);
|
|
|
|
/* Add a type to this set using the specified allocator. */
|
|
void addType(Type type, LifoAlloc* alloc);
|
|
|
|
/* Get a list of all types in this set. */
|
|
typedef Vector<Type, 1, SystemAllocPolicy> TypeList;
|
|
template <class TypeListT> bool enumerateTypes(TypeListT* list) const;
|
|
|
|
/*
|
|
* Iterate through the objects in this set. getObjectCount overapproximates
|
|
* in the hash case (see SET_ARRAY_SIZE in TypeInference-inl.h), and
|
|
* getObject may return nullptr.
|
|
*/
|
|
inline unsigned getObjectCount() const;
|
|
inline ObjectKey* getObject(unsigned i) const;
|
|
inline JSObject* getSingleton(unsigned i) const;
|
|
inline ObjectGroup* getGroup(unsigned i) const;
|
|
inline JSObject* getSingletonNoBarrier(unsigned i) const;
|
|
inline ObjectGroup* getGroupNoBarrier(unsigned i) const;
|
|
|
|
/* The Class of an object in this set. */
|
|
inline const Class* getObjectClass(unsigned i) const;
|
|
|
|
bool canSetDefinite(unsigned slot) {
|
|
// Note: the cast is required to work around an MSVC issue.
|
|
return (slot + 1) <= (unsigned(TYPE_FLAG_DEFINITE_MASK) >> TYPE_FLAG_DEFINITE_SHIFT);
|
|
}
|
|
void setDefinite(unsigned slot) {
|
|
MOZ_ASSERT(canSetDefinite(slot));
|
|
flags |= ((slot + 1) << TYPE_FLAG_DEFINITE_SHIFT);
|
|
MOZ_ASSERT(definiteSlot() == slot);
|
|
}
|
|
|
|
/* Whether any values in this set might have the specified type. */
|
|
bool mightBeMIRType(jit::MIRType type) const;
|
|
|
|
/*
|
|
* Get whether this type set is known to be a subset of other.
|
|
* This variant doesn't freeze constraints. That variant is called knownSubset
|
|
*/
|
|
bool isSubset(const TypeSet* other) const;
|
|
|
|
/*
|
|
* Get whether the objects in this TypeSet are a subset of the objects
|
|
* in other.
|
|
*/
|
|
bool objectsAreSubset(TypeSet* other);
|
|
|
|
/* Whether this TypeSet contains exactly the same types as other. */
|
|
bool equals(const TypeSet* other) const {
|
|
return this->isSubset(other) && other->isSubset(this);
|
|
}
|
|
|
|
bool objectsIntersect(const TypeSet* other) const;
|
|
|
|
/* Forward all types in this set to the specified constraint. */
|
|
bool addTypesToConstraint(JSContext* cx, TypeConstraint* constraint);
|
|
|
|
// Clone a type set into an arbitrary allocator.
|
|
TemporaryTypeSet* clone(LifoAlloc* alloc) const;
|
|
bool clone(LifoAlloc* alloc, TemporaryTypeSet* result) const;
|
|
|
|
// Create a new TemporaryTypeSet where undefined and/or null has been filtered out.
|
|
TemporaryTypeSet* filter(LifoAlloc* alloc, bool filterUndefined, bool filterNull) const;
|
|
// Create a new TemporaryTypeSet where the type has been set to object.
|
|
TemporaryTypeSet* cloneObjectsOnly(LifoAlloc* alloc);
|
|
TemporaryTypeSet* cloneWithoutObjects(LifoAlloc* alloc);
|
|
|
|
// Trigger a read barrier on all the contents of a type set.
|
|
static void readBarrier(const TypeSet* types);
|
|
|
|
protected:
|
|
uint32_t baseObjectCount() const {
|
|
return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT;
|
|
}
|
|
inline void setBaseObjectCount(uint32_t count);
|
|
|
|
void clearObjects();
|
|
|
|
public:
|
|
static inline Type GetValueType(const Value& val);
|
|
|
|
static inline bool IsUntrackedValue(const Value& val);
|
|
|
|
// Get the type of a possibly optimized out or uninitialized let value.
|
|
// This generally only happens on unconditional type monitors on bailing
|
|
// out of Ion, such as for argument and local types.
|
|
static inline Type GetMaybeUntrackedValueType(const Value& val);
|
|
|
|
static void MarkTypeRoot(JSTracer* trc, Type* v, const char* name);
|
|
static void MarkTypeUnbarriered(JSTracer* trc, Type* v, const char* name);
|
|
static bool IsTypeMarked(JSRuntime* rt, Type* v);
|
|
static bool IsTypeAllocatedDuringIncremental(Type v);
|
|
static bool IsTypeAboutToBeFinalized(Type* v);
|
|
};
|
|
|
|
/*
|
|
* A constraint which listens to additions to a type set and propagates those
|
|
* changes to other type sets.
|
|
*/
|
|
class TypeConstraint
|
|
{
|
|
public:
|
|
/* Next constraint listening to the same type set. */
|
|
TypeConstraint* next;
|
|
|
|
TypeConstraint()
|
|
: next(nullptr)
|
|
{}
|
|
|
|
/* Debugging name for this kind of constraint. */
|
|
virtual const char* kind() = 0;
|
|
|
|
/* Register a new type for the set this constraint is listening to. */
|
|
virtual void newType(JSContext* cx, TypeSet* source, TypeSet::Type type) = 0;
|
|
|
|
/*
|
|
* For constraints attached to an object property's type set, mark the
|
|
* property as having changed somehow.
|
|
*/
|
|
virtual void newPropertyState(JSContext* cx, TypeSet* source) {}
|
|
|
|
/*
|
|
* For constraints attached to the JSID_EMPTY type set on an object,
|
|
* indicate a change in one of the object's dynamic property flags or other
|
|
* state.
|
|
*/
|
|
virtual void newObjectState(JSContext* cx, ObjectGroup* group) {}
|
|
|
|
/*
|
|
* If the data this constraint refers to is still live, copy it into the
|
|
* zone's new allocator. Type constraints only hold weak references.
|
|
*/
|
|
virtual bool sweep(TypeZone& zone, TypeConstraint** res) = 0;
|
|
};
|
|
|
|
// If there is an OOM while sweeping types, the type information is deoptimized
|
|
// so that it stays correct (i.e. overapproximates the possible types in the
|
|
// zone), but constraints might not have been triggered on the deoptimization
|
|
// or even copied over completely. In this case, destroy all JIT code and new
|
|
// script information in the zone, the only things whose correctness depends on
|
|
// the type constraints.
|
|
class AutoClearTypeInferenceStateOnOOM
|
|
{
|
|
Zone* zone;
|
|
bool oom;
|
|
|
|
public:
|
|
explicit AutoClearTypeInferenceStateOnOOM(Zone* zone)
|
|
: zone(zone), oom(false)
|
|
{}
|
|
|
|
~AutoClearTypeInferenceStateOnOOM();
|
|
|
|
void setOOM() {
|
|
oom = true;
|
|
}
|
|
bool hadOOM() const {
|
|
return oom;
|
|
}
|
|
};
|
|
|
|
/* Superclass common to stack and heap type sets. */
|
|
class ConstraintTypeSet : public TypeSet
|
|
{
|
|
public:
|
|
/* Chain of constraints which propagate changes out from this type set. */
|
|
TypeConstraint* constraintList;
|
|
|
|
ConstraintTypeSet() : constraintList(nullptr) {}
|
|
|
|
/*
|
|
* Add a type to this set, calling any constraint handlers if this is a new
|
|
* possible type.
|
|
*/
|
|
void addType(ExclusiveContext* cx, Type type);
|
|
|
|
// Trigger a post barrier when writing to this set, if necessary.
|
|
// addType(cx, type) takes care of this automatically.
|
|
void postWriteBarrier(ExclusiveContext* cx, Type type);
|
|
|
|
/* Add a new constraint to this set. */
|
|
bool addConstraint(JSContext* cx, TypeConstraint* constraint, bool callExisting = true);
|
|
|
|
inline void sweep(JS::Zone* zone, AutoClearTypeInferenceStateOnOOM& oom);
|
|
inline void trace(JS::Zone* zone, JSTracer* trc);
|
|
};
|
|
|
|
class StackTypeSet : public ConstraintTypeSet
|
|
{
|
|
public:
|
|
};
|
|
|
|
class HeapTypeSet : public ConstraintTypeSet
|
|
{
|
|
inline void newPropertyState(ExclusiveContext* cx);
|
|
|
|
public:
|
|
/* Mark this type set as representing a non-data property. */
|
|
inline void setNonDataProperty(ExclusiveContext* cx);
|
|
|
|
/* Mark this type set as representing a non-writable property. */
|
|
inline void setNonWritableProperty(ExclusiveContext* cx);
|
|
|
|
// Mark this type set as being non-constant.
|
|
inline void setNonConstantProperty(ExclusiveContext* cx);
|
|
};
|
|
|
|
CompilerConstraintList*
|
|
NewCompilerConstraintList(jit::TempAllocator& alloc);
|
|
|
|
class TemporaryTypeSet : public TypeSet
|
|
{
|
|
public:
|
|
TemporaryTypeSet() {}
|
|
TemporaryTypeSet(LifoAlloc* alloc, Type type);
|
|
|
|
TemporaryTypeSet(uint32_t flags, ObjectKey** objectSet) {
|
|
this->flags = flags;
|
|
this->objectSet = objectSet;
|
|
}
|
|
|
|
/*
|
|
* Constraints for JIT compilation.
|
|
*
|
|
* Methods for JIT compilation. These must be used when a script is
|
|
* currently being compiled (see AutoEnterCompilation) and will add
|
|
* constraints ensuring that if the return value change in the future due
|
|
* to new type information, the script's jitcode will be discarded.
|
|
*/
|
|
|
|
/* Get any type tag which all values in this set must have. */
|
|
jit::MIRType getKnownMIRType();
|
|
|
|
bool isMagicArguments() { return getKnownMIRType() == jit::MIRType_MagicOptimizedArguments; }
|
|
|
|
/* Whether this value may be an object. */
|
|
bool maybeObject() { return unknownObject() || baseObjectCount() > 0; }
|
|
|
|
/*
|
|
* Whether this typeset represents a potentially sentineled object value:
|
|
* the value may be an object or null or undefined.
|
|
* Returns false if the value cannot ever be an object.
|
|
*/
|
|
bool objectOrSentinel() {
|
|
TypeFlags flags = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_ANYOBJECT;
|
|
if (baseFlags() & (~flags & TYPE_FLAG_BASE_MASK))
|
|
return false;
|
|
|
|
return hasAnyFlag(TYPE_FLAG_ANYOBJECT) || baseObjectCount() > 0;
|
|
}
|
|
|
|
/* Whether the type set contains objects with any of a set of flags. */
|
|
bool hasObjectFlags(CompilerConstraintList* constraints, ObjectGroupFlags flags);
|
|
|
|
/* Get the class shared by all objects in this set, or nullptr. */
|
|
const Class* getKnownClass(CompilerConstraintList* constraints);
|
|
|
|
/* Result returned from forAllClasses */
|
|
enum ForAllResult {
|
|
EMPTY=1, // Set empty
|
|
ALL_TRUE, // Set not empty and predicate returned true for all classes
|
|
ALL_FALSE, // Set not empty and predicate returned false for all classes
|
|
MIXED, // Set not empty and predicate returned false for some classes
|
|
// and true for others, or set contains an unknown or non-object
|
|
// type
|
|
};
|
|
|
|
/* Apply func to the members of the set and return an appropriate result.
|
|
* The iteration may end early if the result becomes known early.
|
|
*/
|
|
ForAllResult forAllClasses(CompilerConstraintList* constraints,
|
|
bool (*func)(const Class* clasp));
|
|
|
|
/*
|
|
* Returns true if all objects in this set have the same prototype, and
|
|
* assigns this object to *proto. The proto can be nullptr.
|
|
*/
|
|
bool getCommonPrototype(CompilerConstraintList* constraints, JSObject** proto);
|
|
|
|
/* Whether the buffer mapped by a TypedArray is shared memory or not */
|
|
enum TypedArraySharedness {
|
|
UnknownSharedness=1, // We can't determine sharedness
|
|
KnownShared, // We know for sure the buffer is shared
|
|
KnownUnshared // We know for sure the buffer is unshared
|
|
};
|
|
|
|
/* Get the typed array type of all objects in this set, or Scalar::MaxTypedArrayViewType.
|
|
* If there is such a common type and sharedness is not nullptr then
|
|
* *sharedness is set to what we know about the sharedness of the memory.
|
|
*/
|
|
Scalar::Type getTypedArrayType(CompilerConstraintList* constraints,
|
|
TypedArraySharedness* sharedness = nullptr);
|
|
|
|
/* Whether all objects have JSCLASS_IS_DOMJSCLASS set. */
|
|
bool isDOMClass(CompilerConstraintList* constraints);
|
|
|
|
/* Whether clasp->isCallable() is true for one or more objects in this set. */
|
|
bool maybeCallable(CompilerConstraintList* constraints);
|
|
|
|
/* Whether clasp->emulatesUndefined() is true for one or more objects in this set. */
|
|
bool maybeEmulatesUndefined(CompilerConstraintList* constraints);
|
|
|
|
/* Get the single value which can appear in this type set, otherwise nullptr. */
|
|
JSObject* maybeSingleton();
|
|
|
|
/* Whether any objects in the type set needs a barrier on id. */
|
|
bool propertyNeedsBarrier(CompilerConstraintList* constraints, jsid id);
|
|
|
|
/*
|
|
* Whether this set contains all types in other, except (possibly) the
|
|
* specified type.
|
|
*/
|
|
bool filtersType(const TemporaryTypeSet* other, Type type) const;
|
|
|
|
enum DoubleConversion {
|
|
/* All types in the set should use eager double conversion. */
|
|
AlwaysConvertToDoubles,
|
|
|
|
/* Some types in the set should use eager double conversion. */
|
|
MaybeConvertToDoubles,
|
|
|
|
/* No types should use eager double conversion. */
|
|
DontConvertToDoubles,
|
|
|
|
/* Some types should use eager double conversion, others cannot. */
|
|
AmbiguousDoubleConversion
|
|
};
|
|
|
|
/*
|
|
* Whether known double optimizations are possible for element accesses on
|
|
* objects in this type set.
|
|
*/
|
|
DoubleConversion convertDoubleElements(CompilerConstraintList* constraints);
|
|
|
|
private:
|
|
void getTypedArraySharedness(CompilerConstraintList* constraints,
|
|
TypedArraySharedness* sharedness);
|
|
};
|
|
|
|
bool
|
|
AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* group, HandleId id);
|
|
|
|
bool
|
|
AddClearDefiniteFunctionUsesInScript(JSContext* cx, ObjectGroup* group,
|
|
JSScript* script, JSScript* calleeScript);
|
|
|
|
// For groups where only a small number of objects have been allocated, this
|
|
// structure keeps track of all objects in the group. Once COUNT objects have
|
|
// been allocated, this structure is cleared and the objects are analyzed, to
|
|
// perform the new script properties analyses or determine if an unboxed
|
|
// representation can be used.
|
|
class PreliminaryObjectArray
|
|
{
|
|
public:
|
|
static const uint32_t COUNT = 20;
|
|
|
|
private:
|
|
// All objects with the type which have been allocated. The pointers in
|
|
// this array are weak.
|
|
JSObject* objects[COUNT];
|
|
|
|
public:
|
|
PreliminaryObjectArray() {
|
|
mozilla::PodZero(this);
|
|
}
|
|
|
|
void registerNewObject(JSObject* res);
|
|
void unregisterObject(JSObject* obj);
|
|
|
|
JSObject* get(size_t i) const {
|
|
MOZ_ASSERT(i < COUNT);
|
|
return objects[i];
|
|
}
|
|
|
|
bool full() const;
|
|
bool empty() const;
|
|
void sweep();
|
|
};
|
|
|
|
class PreliminaryObjectArrayWithTemplate : public PreliminaryObjectArray
|
|
{
|
|
RelocatablePtrShape shape_;
|
|
|
|
public:
|
|
explicit PreliminaryObjectArrayWithTemplate(Shape* shape)
|
|
: shape_(shape)
|
|
{}
|
|
|
|
void clear() {
|
|
shape_.init(nullptr);
|
|
}
|
|
|
|
Shape* shape() {
|
|
return shape_;
|
|
}
|
|
|
|
void maybeAnalyze(ExclusiveContext* cx, ObjectGroup* group, bool force = false);
|
|
|
|
void trace(JSTracer* trc);
|
|
|
|
static void writeBarrierPre(PreliminaryObjectArrayWithTemplate* preliminaryObjects);
|
|
};
|
|
|
|
// New script properties analyses overview.
|
|
//
|
|
// When constructing objects using 'new' on a script, we attempt to determine
|
|
// the properties which that object will eventually have. This is done via two
|
|
// analyses. One of these, the definite properties analysis, is static, and the
|
|
// other, the acquired properties analysis, is dynamic. As objects are
|
|
// constructed using 'new' on some script to create objects of group G, our
|
|
// analysis strategy is as follows:
|
|
//
|
|
// - When the first objects are created, no analysis is immediately performed.
|
|
// Instead, all objects of group G are accumulated in an array.
|
|
//
|
|
// - After a certain number of such objects have been created, the definite
|
|
// properties analysis is performed. This analyzes the body of the
|
|
// constructor script and any other functions it calls to look for properties
|
|
// which will definitely be added by the constructor in a particular order,
|
|
// creating an object with shape S.
|
|
//
|
|
// - The properties in S are compared with the greatest common prefix P of the
|
|
// shapes of the objects that have been created. If P has more properties
|
|
// than S, the acquired properties analysis is performed.
|
|
//
|
|
// - The acquired properties analysis marks all properties in P as definite
|
|
// in G, and creates a new group IG for objects which are partially
|
|
// initialized. Objects of group IG are initially created with shape S, and if
|
|
// they are later given shape P, their group can be changed to G.
|
|
//
|
|
// For objects which are rarely created, the definite properties analysis can
|
|
// be triggered after only one or a few objects have been allocated, when code
|
|
// being Ion compiled might access them. In this case type information in the
|
|
// constructor might not be good enough for the definite properties analysis to
|
|
// compute useful information, but the acquired properties analysis will still
|
|
// be able to identify definite properties in this case.
|
|
//
|
|
// This layered approach is designed to maximize performance on easily
|
|
// analyzable code, while still allowing us to determine definite properties
|
|
// robustly when code consistently adds the same properties to objects, but in
|
|
// complex ways which can't be understood statically.
|
|
class TypeNewScript
|
|
{
|
|
public:
|
|
struct Initializer {
|
|
enum Kind {
|
|
SETPROP,
|
|
SETPROP_FRAME,
|
|
DONE
|
|
} kind;
|
|
uint32_t offset;
|
|
Initializer(Kind kind, uint32_t offset)
|
|
: kind(kind), offset(offset)
|
|
{}
|
|
};
|
|
|
|
private:
|
|
// Scripted function which this information was computed for.
|
|
RelocatablePtrFunction function_;
|
|
|
|
// Any preliminary objects with the type. The analyses are not performed
|
|
// until this array is cleared.
|
|
PreliminaryObjectArray* preliminaryObjects;
|
|
|
|
// After the new script properties analyses have been performed, a template
|
|
// object to use for newly constructed objects. The shape of this object
|
|
// reflects all definite properties the object will have, and the
|
|
// allocation kind to use. This is null if the new objects have an unboxed
|
|
// layout, in which case the UnboxedLayout provides the initial structure
|
|
// of the object.
|
|
RelocatablePtrPlainObject templateObject_;
|
|
|
|
// Order in which definite properties become initialized. We need this in
|
|
// case the definite properties are invalidated (such as by adding a setter
|
|
// to an object on the prototype chain) while an object is in the middle of
|
|
// being initialized, so we can walk the stack and fixup any objects which
|
|
// look for in-progress objects which were prematurely set with an incorrect
|
|
// shape. Property assignments in inner frames are preceded by a series of
|
|
// SETPROP_FRAME entries specifying the stack down to the frame containing
|
|
// the write.
|
|
Initializer* initializerList;
|
|
|
|
// If there are additional properties found by the acquired properties
|
|
// analysis which were not found by the definite properties analysis, this
|
|
// shape contains all such additional properties (plus the definite
|
|
// properties). When an object of this group acquires this shape, it is
|
|
// fully initialized and its group can be changed to initializedGroup.
|
|
RelocatablePtrShape initializedShape_;
|
|
|
|
// Group with definite properties set for all properties found by
|
|
// both the definite and acquired properties analyses.
|
|
RelocatablePtrObjectGroup initializedGroup_;
|
|
|
|
public:
|
|
TypeNewScript() { mozilla::PodZero(this); }
|
|
~TypeNewScript() {
|
|
js_delete(preliminaryObjects);
|
|
js_free(initializerList);
|
|
}
|
|
|
|
void clear() {
|
|
function_.init(nullptr);
|
|
templateObject_.init(nullptr);
|
|
initializedShape_.init(nullptr);
|
|
initializedGroup_.init(nullptr);
|
|
}
|
|
|
|
static void writeBarrierPre(TypeNewScript* newScript);
|
|
|
|
bool analyzed() const {
|
|
return preliminaryObjects == nullptr;
|
|
}
|
|
|
|
PlainObject* templateObject() const {
|
|
return templateObject_;
|
|
}
|
|
|
|
Shape* initializedShape() const {
|
|
return initializedShape_;
|
|
}
|
|
|
|
ObjectGroup* initializedGroup() const {
|
|
return initializedGroup_;
|
|
}
|
|
|
|
JSFunction* function() const {
|
|
return function_;
|
|
}
|
|
|
|
void trace(JSTracer* trc);
|
|
void sweep();
|
|
|
|
void registerNewObject(PlainObject* res);
|
|
bool maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, bool force = false);
|
|
|
|
bool rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* group);
|
|
|
|
static bool make(JSContext* cx, ObjectGroup* group, JSFunction* fun);
|
|
static TypeNewScript* makeNativeVersion(JSContext* cx, TypeNewScript* newScript,
|
|
PlainObject* templateObject);
|
|
|
|
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
|
};
|
|
|
|
/* Is this a reasonable PC to be doing inlining on? */
|
|
inline bool isInlinableCall(jsbytecode* pc);
|
|
|
|
bool
|
|
ClassCanHaveExtraProperties(const Class* clasp);
|
|
|
|
/* Persistent type information for a script, retained across GCs. */
|
|
class TypeScript
|
|
{
|
|
friend class ::JSScript;
|
|
|
|
// Variable-size array
|
|
StackTypeSet typeArray_[1];
|
|
|
|
public:
|
|
/* Array of type sets for variables and JOF_TYPESET ops. */
|
|
StackTypeSet* typeArray() const {
|
|
// Ensure typeArray_ is the last data member of TypeScript.
|
|
JS_STATIC_ASSERT(sizeof(TypeScript) ==
|
|
sizeof(typeArray_) + offsetof(TypeScript, typeArray_));
|
|
return const_cast<StackTypeSet*>(typeArray_);
|
|
}
|
|
|
|
static inline size_t SizeIncludingTypeArray(size_t arraySize) {
|
|
// Ensure typeArray_ is the last data member of TypeScript.
|
|
JS_STATIC_ASSERT(sizeof(TypeScript) ==
|
|
sizeof(StackTypeSet) + offsetof(TypeScript, typeArray_));
|
|
return offsetof(TypeScript, typeArray_) + arraySize * sizeof(StackTypeSet);
|
|
}
|
|
|
|
static inline unsigned NumTypeSets(JSScript* script);
|
|
|
|
static inline StackTypeSet* ThisTypes(JSScript* script);
|
|
static inline StackTypeSet* ArgTypes(JSScript* script, unsigned i);
|
|
|
|
/* Get the type set for values observed at an opcode. */
|
|
static inline StackTypeSet* BytecodeTypes(JSScript* script, jsbytecode* pc);
|
|
|
|
template <typename TYPESET>
|
|
static inline TYPESET* BytecodeTypes(JSScript* script, jsbytecode* pc, uint32_t* bytecodeMap,
|
|
uint32_t* hint, TYPESET* typeArray);
|
|
|
|
/*
|
|
* Monitor a bytecode pushing any value. This must be called for any opcode
|
|
* which is JOF_TYPESET, and where either the script has not been analyzed
|
|
* by type inference or where the pc has type barriers. For simplicity, we
|
|
* always monitor JOF_TYPESET opcodes in the interpreter and stub calls,
|
|
* and only look at barriers when generating JIT code for the script.
|
|
*/
|
|
static inline void Monitor(JSContext* cx, JSScript* script, jsbytecode* pc,
|
|
const js::Value& val);
|
|
static inline void Monitor(JSContext* cx, JSScript* script, jsbytecode* pc,
|
|
TypeSet::Type type);
|
|
static inline void Monitor(JSContext* cx, const js::Value& rval);
|
|
|
|
/* Monitor an assignment at a SETELEM on a non-integer identifier. */
|
|
static inline void MonitorAssign(JSContext* cx, HandleObject obj, jsid id);
|
|
|
|
/* Add a type for a variable in a script. */
|
|
static inline void SetThis(JSContext* cx, JSScript* script, TypeSet::Type type);
|
|
static inline void SetThis(JSContext* cx, JSScript* script, const js::Value& value);
|
|
static inline void SetArgument(JSContext* cx, JSScript* script, unsigned arg,
|
|
TypeSet::Type type);
|
|
static inline void SetArgument(JSContext* cx, JSScript* script, unsigned arg,
|
|
const js::Value& value);
|
|
|
|
/*
|
|
* Freeze all the stack type sets in a script, for a compilation. Returns
|
|
* copies of the type sets which will be checked against the actual ones
|
|
* under FinishCompilation, to detect any type changes.
|
|
*/
|
|
static bool FreezeTypeSets(CompilerConstraintList* constraints, JSScript* script,
|
|
TemporaryTypeSet** pThisTypes,
|
|
TemporaryTypeSet** pArgTypes,
|
|
TemporaryTypeSet** pBytecodeTypes);
|
|
|
|
static void Purge(JSContext* cx, HandleScript script);
|
|
|
|
void destroy();
|
|
|
|
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
|
return mallocSizeOf(this);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void printTypes(JSContext* cx, HandleScript script) const;
|
|
#endif
|
|
};
|
|
|
|
void
|
|
FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap);
|
|
|
|
class RecompileInfo;
|
|
|
|
// Allocate a CompilerOutput for a finished compilation and generate the type
|
|
// constraints for the compilation. Sets |isValidOut| based on whether the type
|
|
// constraints still hold.
|
|
bool
|
|
FinishCompilation(JSContext* cx, HandleScript script, CompilerConstraintList* constraints,
|
|
RecompileInfo* precompileInfo, bool* isValidOut);
|
|
|
|
// Reset any CompilerOutput present for a script.
|
|
void
|
|
InvalidateCompilerOutputsForScript(JSContext* cx, HandleScript script);
|
|
|
|
// Update the actual types in any scripts queried by constraints with any
|
|
// speculative types added during the definite properties analysis.
|
|
void
|
|
FinishDefinitePropertiesAnalysis(JSContext* cx, CompilerConstraintList* constraints);
|
|
|
|
// Representation of a heap type property which may or may not be instantiated.
|
|
// Heap properties for singleton types are instantiated lazily as they are used
|
|
// by the compiler, but this is only done on the main thread. If we are
|
|
// compiling off thread and use a property which has not yet been instantiated,
|
|
// it will be treated as empty and non-configured and will be instantiated when
|
|
// rejoining to the main thread. If it is in fact not empty, the compilation
|
|
// will fail; to avoid this, we try to instantiate singleton property types
|
|
// during generation of baseline caches.
|
|
class HeapTypeSetKey
|
|
{
|
|
friend class TypeSet::ObjectKey;
|
|
|
|
// Object and property being accessed.
|
|
TypeSet::ObjectKey* object_;
|
|
jsid id_;
|
|
|
|
// If instantiated, the underlying heap type set.
|
|
HeapTypeSet* maybeTypes_;
|
|
|
|
public:
|
|
HeapTypeSetKey()
|
|
: object_(nullptr), id_(JSID_EMPTY), maybeTypes_(nullptr)
|
|
{}
|
|
|
|
TypeSet::ObjectKey* object() const { return object_; }
|
|
jsid id() const { return id_; }
|
|
HeapTypeSet* maybeTypes() const { return maybeTypes_; }
|
|
|
|
bool instantiate(JSContext* cx);
|
|
|
|
void freeze(CompilerConstraintList* constraints);
|
|
jit::MIRType knownMIRType(CompilerConstraintList* constraints);
|
|
bool nonData(CompilerConstraintList* constraints);
|
|
bool nonWritable(CompilerConstraintList* constraints);
|
|
bool isOwnProperty(CompilerConstraintList* constraints, bool allowEmptyTypesForGlobal = false);
|
|
bool knownSubset(CompilerConstraintList* constraints, const HeapTypeSetKey& other);
|
|
JSObject* singleton(CompilerConstraintList* constraints);
|
|
bool needsBarrier(CompilerConstraintList* constraints);
|
|
bool constant(CompilerConstraintList* constraints, Value* valOut);
|
|
bool couldBeConstant(CompilerConstraintList* constraints);
|
|
};
|
|
|
|
/*
|
|
* Information about the result of the compilation of a script. This structure
|
|
* stored in the TypeCompartment is indexed by the RecompileInfo. This
|
|
* indirection enables the invalidation of all constraints related to the same
|
|
* compilation.
|
|
*/
|
|
class CompilerOutput
|
|
{
|
|
// If this compilation has not been invalidated, the associated script and
|
|
// kind of compilation being performed.
|
|
JSScript* script_;
|
|
|
|
// Whether this compilation is about to be invalidated.
|
|
bool pendingInvalidation_ : 1;
|
|
|
|
// During sweeping, the list of compiler outputs is compacted and invalidated
|
|
// outputs are removed. This gives the new index for a valid compiler output.
|
|
uint32_t sweepIndex_ : 31;
|
|
|
|
public:
|
|
static const uint32_t INVALID_SWEEP_INDEX = static_cast<uint32_t>(1 << 31) - 1;
|
|
|
|
CompilerOutput()
|
|
: script_(nullptr),
|
|
pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
|
|
{}
|
|
|
|
explicit CompilerOutput(JSScript* script)
|
|
: script_(script),
|
|
pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
|
|
{}
|
|
|
|
JSScript* script() const { return script_; }
|
|
|
|
inline jit::IonScript* ion() const;
|
|
|
|
bool isValid() const {
|
|
return script_ != nullptr;
|
|
}
|
|
void invalidate() {
|
|
script_ = nullptr;
|
|
}
|
|
|
|
void setPendingInvalidation() {
|
|
pendingInvalidation_ = true;
|
|
}
|
|
bool pendingInvalidation() {
|
|
return pendingInvalidation_;
|
|
}
|
|
|
|
void setSweepIndex(uint32_t index) {
|
|
if (index >= INVALID_SWEEP_INDEX)
|
|
MOZ_CRASH();
|
|
sweepIndex_ = index;
|
|
}
|
|
uint32_t sweepIndex() {
|
|
MOZ_ASSERT(sweepIndex_ != INVALID_SWEEP_INDEX);
|
|
return sweepIndex_;
|
|
}
|
|
};
|
|
|
|
class RecompileInfo
|
|
{
|
|
// Index in the TypeZone's compilerOutputs or sweepCompilerOutputs arrays,
|
|
// depending on the generation value.
|
|
uint32_t outputIndex : 31;
|
|
|
|
// If out of sync with the TypeZone's generation, this index is for the
|
|
// zone's sweepCompilerOutputs rather than compilerOutputs.
|
|
uint32_t generation : 1;
|
|
|
|
public:
|
|
RecompileInfo(uint32_t outputIndex, uint32_t generation)
|
|
: outputIndex(outputIndex), generation(generation)
|
|
{}
|
|
|
|
RecompileInfo()
|
|
: outputIndex(JS_BITMASK(31)), generation(0)
|
|
{}
|
|
|
|
CompilerOutput* compilerOutput(TypeZone& types) const;
|
|
CompilerOutput* compilerOutput(JSContext* cx) const;
|
|
bool shouldSweep(TypeZone& types);
|
|
};
|
|
|
|
typedef Vector<RecompileInfo, 0, SystemAllocPolicy> RecompileInfoVector;
|
|
|
|
struct AutoEnterAnalysis;
|
|
|
|
struct TypeZone
|
|
{
|
|
JS::Zone* zone_;
|
|
|
|
/* Pool for type information in this zone. */
|
|
static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024;
|
|
LifoAlloc typeLifoAlloc;
|
|
|
|
// Current generation for sweeping.
|
|
uint32_t generation : 1;
|
|
|
|
/*
|
|
* All Ion compilations that have occured in this zone, for indexing via
|
|
* RecompileInfo. This includes both valid and invalid compilations, though
|
|
* invalidated compilations are swept on GC.
|
|
*/
|
|
typedef Vector<CompilerOutput, 4, SystemAllocPolicy> CompilerOutputVector;
|
|
CompilerOutputVector* compilerOutputs;
|
|
|
|
// During incremental sweeping, allocator holding the old type information
|
|
// for the zone.
|
|
LifoAlloc sweepTypeLifoAlloc;
|
|
|
|
// During incremental sweeping, the old compiler outputs for use by
|
|
// recompile indexes with a stale generation.
|
|
CompilerOutputVector* sweepCompilerOutputs;
|
|
|
|
// During incremental sweeping, whether to try to destroy all type
|
|
// information attached to scripts.
|
|
bool sweepReleaseTypes;
|
|
|
|
// The topmost AutoEnterAnalysis on the stack, if there is one.
|
|
AutoEnterAnalysis* activeAnalysis;
|
|
|
|
explicit TypeZone(JS::Zone* zone);
|
|
~TypeZone();
|
|
|
|
JS::Zone* zone() const { return zone_; }
|
|
|
|
void beginSweep(FreeOp* fop, bool releaseTypes, AutoClearTypeInferenceStateOnOOM& oom);
|
|
void endSweep(JSRuntime* rt);
|
|
void clearAllNewScriptsOnOOM();
|
|
|
|
/* Mark a script as needing recompilation once inference has finished. */
|
|
void addPendingRecompile(JSContext* cx, const RecompileInfo& info);
|
|
void addPendingRecompile(JSContext* cx, JSScript* script);
|
|
|
|
void processPendingRecompiles(FreeOp* fop, RecompileInfoVector& recompiles);
|
|
};
|
|
|
|
enum SpewChannel {
|
|
ISpewOps, /* ops: New constraints and types. */
|
|
ISpewResult, /* result: Final type sets. */
|
|
SPEW_COUNT
|
|
};
|
|
|
|
#ifdef DEBUG
|
|
|
|
const char * InferSpewColorReset();
|
|
const char * InferSpewColor(TypeConstraint* constraint);
|
|
const char * InferSpewColor(TypeSet* types);
|
|
|
|
void InferSpew(SpewChannel which, const char* fmt, ...);
|
|
|
|
/* Check that the type property for id in group contains value. */
|
|
bool ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Value& value);
|
|
|
|
#else
|
|
|
|
inline const char * InferSpewColorReset() { return nullptr; }
|
|
inline const char * InferSpewColor(TypeConstraint* constraint) { return nullptr; }
|
|
inline const char * InferSpewColor(TypeSet* types) { return nullptr; }
|
|
inline void InferSpew(SpewChannel which, const char* fmt, ...) {}
|
|
|
|
#endif
|
|
|
|
/* Print a warning, dump state and abort the program. */
|
|
MOZ_NORETURN MOZ_COLD void TypeFailure(JSContext* cx, const char* fmt, ...);
|
|
|
|
// Prints type information for a context if spew is enabled or force is set.
|
|
void
|
|
PrintTypes(JSContext* cx, JSCompartment* comp, bool force);
|
|
|
|
} /* namespace js */
|
|
|
|
// JS::ubi::Nodes can point to object groups; they're js::gc::Cell instances
|
|
// with no associated compartment.
|
|
namespace JS {
|
|
namespace ubi {
|
|
|
|
template<>
|
|
struct Concrete<js::ObjectGroup> : TracerConcrete<js::ObjectGroup> {
|
|
Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
|
|
|
|
protected:
|
|
explicit Concrete(js::ObjectGroup *ptr) : TracerConcrete<js::ObjectGroup>(ptr) { }
|
|
|
|
public:
|
|
static void construct(void *storage, js::ObjectGroup *ptr) { new (storage) Concrete(ptr); }
|
|
};
|
|
|
|
} // namespace ubi
|
|
} // namespace JS
|
|
|
|
#endif /* vm_TypeInference_h */
|