summaryrefslogtreecommitdiffstats
path: root/js/src/vm/PIC.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/vm/PIC.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/vm/PIC.cpp')
-rw-r--r--js/src/vm/PIC.cpp330
1 files changed, 330 insertions, 0 deletions
diff --git a/js/src/vm/PIC.cpp b/js/src/vm/PIC.cpp
new file mode 100644
index 000000000..46843b759
--- /dev/null
+++ b/js/src/vm/PIC.cpp
@@ -0,0 +1,330 @@
+/* -*- 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/. */
+
+#include "vm/PIC.h"
+#include "jscntxt.h"
+#include "jscompartment.h"
+#include "jsobj.h"
+#include "gc/Marking.h"
+
+#include "vm/GlobalObject.h"
+#include "vm/SelfHosting.h"
+
+#include "jsobjinlines.h"
+#include "vm/NativeObject-inl.h"
+
+using namespace js;
+using namespace js::gc;
+
+bool
+js::ForOfPIC::Chain::initialize(JSContext* cx)
+{
+ MOZ_ASSERT(!initialized_);
+
+ // Get the canonical Array.prototype
+ RootedNativeObject arrayProto(cx, GlobalObject::getOrCreateArrayPrototype(cx, cx->global()));
+ if (!arrayProto)
+ return false;
+
+ // Get the canonical ArrayIterator.prototype
+ RootedNativeObject arrayIteratorProto(cx,
+ GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
+ if (!arrayIteratorProto)
+ return false;
+
+ // From this point on, we can't fail. Set initialized and fill the fields
+ // for the canonical Array.prototype and ArrayIterator.prototype objects.
+ initialized_ = true;
+ arrayProto_ = arrayProto;
+ arrayIteratorProto_ = arrayIteratorProto;
+
+ // Shortcut returns below means Array for-of will never be optimizable,
+ // do set disabled_ now, and clear it later when we succeed.
+ disabled_ = true;
+
+ // Look up Array.prototype[@@iterator], ensure it's a slotful shape.
+ Shape* iterShape = arrayProto->lookup(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
+ if (!iterShape || !iterShape->hasSlot() || !iterShape->hasDefaultGetter())
+ return true;
+
+ // Get the referred value, and ensure it holds the canonical ArrayValues function.
+ Value iterator = arrayProto->getSlot(iterShape->slot());
+ JSFunction* iterFun;
+ if (!IsFunctionObject(iterator, &iterFun))
+ return true;
+ if (!IsSelfHostedFunctionWithName(iterFun, cx->names().ArrayValues))
+ return true;
+
+ // Look up the 'next' value on ArrayIterator.prototype
+ Shape* nextShape = arrayIteratorProto->lookup(cx, cx->names().next);
+ if (!nextShape || !nextShape->hasSlot())
+ return true;
+
+ // Get the referred value, ensure it holds the canonical ArrayIteratorNext function.
+ Value next = arrayIteratorProto->getSlot(nextShape->slot());
+ JSFunction* nextFun;
+ if (!IsFunctionObject(next, &nextFun))
+ return true;
+ if (!IsSelfHostedFunctionWithName(nextFun, cx->names().ArrayIteratorNext))
+ return true;
+
+ disabled_ = false;
+ arrayProtoShape_ = arrayProto->lastProperty();
+ arrayProtoIteratorSlot_ = iterShape->slot();
+ canonicalIteratorFunc_ = iterator;
+ arrayIteratorProtoShape_ = arrayIteratorProto->lastProperty();
+ arrayIteratorProtoNextSlot_ = nextShape->slot();
+ canonicalNextFunc_ = next;
+ return true;
+}
+
+js::ForOfPIC::Stub*
+js::ForOfPIC::Chain::isArrayOptimized(ArrayObject* obj)
+{
+ Stub* stub = getMatchingStub(obj);
+ if (!stub)
+ return nullptr;
+
+ // Ensure that this is an otherwise optimizable array.
+ if (!isOptimizableArray(obj))
+ return nullptr;
+
+ // Not yet enough! Ensure that the world as we know it remains sane.
+ if (!isArrayStateStillSane())
+ return nullptr;
+
+ return stub;
+}
+
+bool
+js::ForOfPIC::Chain::tryOptimizeArray(JSContext* cx, HandleArrayObject array, bool* optimized)
+{
+ MOZ_ASSERT(optimized);
+
+ *optimized = false;
+
+ if (!initialized_) {
+ // If PIC is not initialized, initialize it.
+ if (!initialize(cx))
+ return false;
+
+ } else if (!disabled_ && !isArrayStateStillSane()) {
+ // Otherwise, if array state is no longer sane, reinitialize.
+ reset(cx);
+
+ if (!initialize(cx))
+ return false;
+ }
+ MOZ_ASSERT(initialized_);
+
+ // If PIC is disabled, don't bother trying to optimize.
+ if (disabled_)
+ return true;
+
+ // By the time we get here, we should have a sane array state to work with.
+ MOZ_ASSERT(isArrayStateStillSane());
+
+ // Check if stub already exists.
+ ForOfPIC::Stub* stub = isArrayOptimized(&array->as<ArrayObject>());
+ if (stub) {
+ *optimized = true;
+ return true;
+ }
+
+ // If the number of stubs is about to exceed the limit, throw away entire
+ // existing cache before adding new stubs. We shouldn't really have heavy
+ // churn on these.
+ if (numStubs() >= MAX_STUBS)
+ eraseChain();
+
+ // Ensure array's prototype is the actual Array.prototype
+ if (!isOptimizableArray(array))
+ return true;
+
+ // Ensure array doesn't define @@iterator directly.
+ if (array->lookup(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)))
+ return true;
+
+ // Good to optimize now, create stub to add.
+ RootedShape shape(cx, array->lastProperty());
+ stub = cx->new_<Stub>(shape);
+ if (!stub)
+ return false;
+
+ // Add the stub.
+ addStub(stub);
+
+ *optimized = true;
+ return true;
+}
+
+js::ForOfPIC::Stub*
+js::ForOfPIC::Chain::getMatchingStub(JSObject* obj)
+{
+ // Ensure PIC is initialized and not disabled.
+ if (!initialized_ || disabled_)
+ return nullptr;
+
+ // Check if there is a matching stub.
+ for (Stub* stub = stubs(); stub != nullptr; stub = stub->next()) {
+ if (stub->shape() == obj->maybeShape())
+ return stub;
+ }
+
+ return nullptr;
+}
+
+bool
+js::ForOfPIC::Chain::isOptimizableArray(JSObject* obj)
+{
+ MOZ_ASSERT(obj->is<ArrayObject>());
+ return obj->staticPrototype() == arrayProto_;
+}
+
+bool
+js::ForOfPIC::Chain::isArrayStateStillSane()
+{
+ // Ensure that canonical Array.prototype has matching shape.
+ if (arrayProto_->lastProperty() != arrayProtoShape_)
+ return false;
+
+ // Ensure that Array.prototype[@@iterator] contains the
+ // canonical iterator function.
+ if (arrayProto_->getSlot(arrayProtoIteratorSlot_) != canonicalIteratorFunc_)
+ return false;
+
+ // Chain to isArrayNextStillSane.
+ return isArrayNextStillSane();
+}
+
+void
+js::ForOfPIC::Chain::reset(JSContext* cx)
+{
+ // Should never reset a disabled_ stub.
+ MOZ_ASSERT(!disabled_);
+
+ // Erase the chain.
+ eraseChain();
+
+ arrayProto_ = nullptr;
+ arrayIteratorProto_ = nullptr;
+
+ arrayProtoShape_ = nullptr;
+ arrayProtoIteratorSlot_ = -1;
+ canonicalIteratorFunc_ = UndefinedValue();
+
+ arrayIteratorProtoShape_ = nullptr;
+ arrayIteratorProtoNextSlot_ = -1;
+ canonicalNextFunc_ = UndefinedValue();
+
+ initialized_ = false;
+}
+
+void
+js::ForOfPIC::Chain::eraseChain()
+{
+ // Should never need to clear the chain of a disabled stub.
+ MOZ_ASSERT(!disabled_);
+
+ // Free all stubs.
+ Stub* stub = stubs_;
+ while (stub) {
+ Stub* next = stub->next();
+ js_delete(stub);
+ stub = next;
+ }
+ stubs_ = nullptr;
+}
+
+
+// Trace the pointers stored directly on the stub.
+void
+js::ForOfPIC::Chain::mark(JSTracer* trc)
+{
+ if (!initialized_ || disabled_)
+ return;
+
+ TraceEdge(trc, &arrayProto_, "ForOfPIC Array.prototype.");
+ TraceEdge(trc, &arrayIteratorProto_, "ForOfPIC ArrayIterator.prototype.");
+
+ TraceEdge(trc, &arrayProtoShape_, "ForOfPIC Array.prototype shape.");
+ TraceEdge(trc, &arrayIteratorProtoShape_, "ForOfPIC ArrayIterator.prototype shape.");
+
+ TraceEdge(trc, &canonicalIteratorFunc_, "ForOfPIC ArrayValues builtin.");
+ TraceEdge(trc, &canonicalNextFunc_, "ForOfPIC ArrayIterator.prototype.next builtin.");
+
+ // Free all the stubs in the chain.
+ while (stubs_)
+ removeStub(stubs_, nullptr);
+}
+
+void
+js::ForOfPIC::Chain::sweep(FreeOp* fop)
+{
+ // Free all the stubs in the chain.
+ while (stubs_) {
+ Stub* next = stubs_->next();
+ fop->delete_(stubs_);
+ stubs_ = next;
+ }
+ fop->delete_(this);
+}
+
+static void
+ForOfPIC_finalize(FreeOp* fop, JSObject* obj)
+{
+ MOZ_ASSERT(fop->maybeOffMainThread());
+ if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
+ chain->sweep(fop);
+}
+
+static void
+ForOfPIC_traceObject(JSTracer* trc, JSObject* obj)
+{
+ if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
+ chain->mark(trc);
+}
+
+static const ClassOps ForOfPICClassOps = {
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, ForOfPIC_finalize,
+ nullptr, /* call */
+ nullptr, /* hasInstance */
+ nullptr, /* construct */
+ ForOfPIC_traceObject
+};
+
+const Class ForOfPIC::class_ = {
+ "ForOfPIC",
+ JSCLASS_HAS_PRIVATE |
+ JSCLASS_BACKGROUND_FINALIZE,
+ &ForOfPICClassOps
+};
+
+/* static */ NativeObject*
+js::ForOfPIC::createForOfPICObject(JSContext* cx, Handle<GlobalObject*> global)
+{
+ assertSameCompartment(cx, global);
+ NativeObject* obj = NewNativeObjectWithGivenProto(cx, &ForOfPIC::class_, nullptr);
+ if (!obj)
+ return nullptr;
+ ForOfPIC::Chain* chain = cx->new_<ForOfPIC::Chain>();
+ if (!chain)
+ return nullptr;
+ obj->setPrivate(chain);
+ return obj;
+}
+
+/* static */ js::ForOfPIC::Chain*
+js::ForOfPIC::create(JSContext* cx)
+{
+ MOZ_ASSERT(!cx->global()->getForOfPICObject());
+ Rooted<GlobalObject*> global(cx, cx->global());
+ NativeObject* obj = GlobalObject::getOrCreateForOfPICObject(cx, global);
+ if (!obj)
+ return nullptr;
+ return fromJSObject(obj);
+}