diff options
Diffstat (limited to 'js/src/vm/PIC.h')
-rw-r--r-- | js/src/vm/PIC.h | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/js/src/vm/PIC.h b/js/src/vm/PIC.h new file mode 100644 index 0000000000..4f1258dd58 --- /dev/null +++ b/js/src/vm/PIC.h @@ -0,0 +1,268 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * 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 vm_PIC_h +#define vm_PIC_h + +#include "vm/GlobalObject.h" +#include "vm/NativeObject.h" + +namespace js { + +class Shape; + +template <typename Category> +class PICChain; + +/* + * The basic PICStub just has a pointer to the next stub. + */ +template <typename Category> +class PICStub { + friend class PICChain<Category>; + + private: + using CatStub = typename Category::Stub; + using CatChain = typename Category::Chain; + + protected: + CatStub* next_; + + PICStub() : next_(nullptr) {} + explicit PICStub(const CatStub* next) : next_(next) { MOZ_ASSERT(next_); } + explicit PICStub(const CatStub& other) : next_(other.next_) {} + + public: + CatStub* next() const { return next_; } + + protected: + void append(CatStub* stub) { + MOZ_ASSERT(!next_); + MOZ_ASSERT(!stub->next_); + next_ = stub; + } +}; + +/* + * The basic PIC just has a pointer to the list of stubs. + */ +template <typename Category> +class PICChain { + private: + using CatStub = typename Category::Stub; + using CatChain = typename Category::Chain; + + protected: + CatStub* stubs_; + + PICChain() : stubs_(nullptr) {} + // PICs should never be copy constructed. + PICChain(const PICChain<Category>& other) = delete; + + public: + CatStub* stubs() const { return stubs_; } + + void addStub(JSObject* obj, CatStub* stub); + + unsigned numStubs() const { + unsigned count = 0; + for (CatStub* stub = stubs_; stub; stub = stub->next()) { + count++; + } + return count; + } +}; + +// Class for object that holds ForOfPIC chain. +class ForOfPICObject : public NativeObject { + public: + enum { ChainSlot, SlotCount }; + + static const JSClass class_; +}; + +/* + * ForOfPIC defines a PIC category for optimizing for-of operations. + */ +struct ForOfPIC { + /* Forward declarations so template-substitution works. */ + class Stub; + class Chain; + + ForOfPIC() = delete; + ForOfPIC(const ForOfPIC& other) = delete; + + using BaseStub = PICStub<ForOfPIC>; + using BaseChain = PICChain<ForOfPIC>; + + /* + * A ForOfPIC has only one kind of stub for now: one that holds the shape + * of an array object that does not override its @@iterator property. + */ + class Stub : public BaseStub { + private: + // Shape of matching array object. + const HeapPtr<Shape*> shape_; + + public: + explicit Stub(Shape* shape) : shape_(shape) { MOZ_ASSERT(shape_); } + + Shape* shape() { return shape_; } + + void trace(JSTracer* trc); + }; + + /* + * A ForOfPIC chain holds the following: + * + * Array.prototype (arrayProto_) + * To ensure that the incoming array has the standard proto. + * + * Array.prototype's shape (arrayProtoShape_) + * To ensure that Array.prototype has not been modified. + * + * ArrayIterator.prototype + * ArrayIterator.prototype's shape + * (arrayIteratorProto_, arrayIteratorProtoShape_) + * To ensure that an ArrayIterator.prototype has not been modified. + * + * Array.prototype's slot number for @@iterator + * Array.prototype's canonical value for @@iterator + * (arrayProtoIteratorSlot_, canonicalIteratorFunc_) + * To quickly retrieve and ensure that the iterator constructor + * stored in the slot has not changed. + * + * ArrayIterator.prototype's slot number for 'next' + * ArrayIterator.prototype's canonical value for 'next' + * (arrayIteratorProtoNextSlot_, canonicalNextFunc_) + * To quickly retrieve and ensure that the 'next' method for + * ArrayIterator objects has not changed. + */ + class Chain : public BaseChain { + private: + // Pointer to owning JSObject for memory accounting purposes. + const GCPtr<JSObject*> picObject_; + + // Pointer to canonical Array.prototype, ArrayIterator.prototype, + // Iterator.prototype, and Object.prototype + GCPtr<NativeObject*> arrayProto_; + GCPtr<NativeObject*> arrayIteratorProto_; + GCPtr<NativeObject*> iteratorProto_; + GCPtr<NativeObject*> objectProto_; + + // Shape of matching Array.prototype object, and slot containing + // the @@iterator for it, and the canonical value. + GCPtr<Shape*> arrayProtoShape_; + uint32_t arrayProtoIteratorSlot_; + GCPtr<Value> canonicalIteratorFunc_; + + // Shape of matching ArrayIteratorProto, and slot containing + // the 'next' property, and the canonical value. + GCPtr<Shape*> arrayIteratorProtoShape_; + uint32_t arrayIteratorProtoNextSlot_; + GCPtr<Value> canonicalNextFunc_; + + // Shape of matching Iterator.prototype object. + GCPtr<Shape*> iteratorProtoShape_; + // Shape of matching Object.prototype object. + GCPtr<Shape*> objectProtoShape_; + + // Initialization flag marking lazy initialization of above fields. + bool initialized_; + + // Disabled flag is set when we don't want to try optimizing anymore + // because core objects were changed. + bool disabled_; + + static const unsigned MAX_STUBS = 10; + + public: + explicit Chain(JSObject* picObject) + : picObject_(picObject), + arrayProto_(nullptr), + arrayIteratorProto_(nullptr), + arrayProtoShape_(nullptr), + arrayProtoIteratorSlot_(-1), + canonicalIteratorFunc_(UndefinedValue()), + arrayIteratorProtoShape_(nullptr), + arrayIteratorProtoNextSlot_(-1), + initialized_(false), + disabled_(false) {} + + // Initialize the canonical iterator function. + bool initialize(JSContext* cx); + + // Try to optimize this chain for a newly allocated array. + bool tryOptimizeArray(JSContext* cx, bool* optimized); + + // Try to optimize this chain for an object. + bool tryOptimizeArray(JSContext* cx, Handle<ArrayObject*> array, + bool* optimized); + + // Check if %ArrayIteratorPrototype% still uses the default "next" method. + bool tryOptimizeArrayIteratorNext(JSContext* cx, bool* optimized); + + void trace(JSTracer* trc); + void finalize(JS::GCContext* gcx, JSObject* obj); + + void freeAllStubs(JS::GCContext* gcx); + + private: + // Check if the global array-related objects have not been messed with + // in a way that would disable this PIC. + bool isArrayStateStillSane(); + + // Check if ArrayIterator.next and ArrayIterator.return are still + // optimizable. + inline bool isArrayIteratorStateStillSane() { + // Ensure the prototype chain is intact, which will ensure that "return" + // has not been defined. + if (arrayIteratorProto_->shape() != arrayIteratorProtoShape_) { + return false; + } + + if (iteratorProto_->shape() != iteratorProtoShape_) { + return false; + } + + if (objectProto_->shape() != objectProtoShape_) { + return false; + } + + return arrayIteratorProto_->getSlot(arrayIteratorProtoNextSlot_) == + canonicalNextFunc_; + } + + // Check if a matching optimized stub for the given object exists. + bool hasMatchingStub(ArrayObject* obj); + + // Reset the PIC and all info associated with it. + void reset(JSContext* cx); + + // Erase the stub chain. + void eraseChain(JSContext* cx); + }; + + static NativeObject* createForOfPICObject(JSContext* cx, + Handle<GlobalObject*> global); + + static inline Chain* fromJSObject(NativeObject* obj) { + MOZ_ASSERT(obj->is<ForOfPICObject>()); + return obj->maybePtrFromReservedSlot<Chain>(ForOfPICObject::ChainSlot); + } + static inline Chain* getOrCreate(JSContext* cx) { + NativeObject* obj = cx->global()->getForOfPICObject(); + if (obj) { + return fromJSObject(obj); + } + return create(cx); + } + static Chain* create(JSContext* cx); +}; + +} // namespace js + +#endif /* vm_PIC_h */ |