зеркало из https://github.com/mozilla/gecko-dev.git
Bug 952891 - Add PIC fast-path for ForOfIterator iteration over arrays. r=jimb r=jorendorff DONTBUILD
This commit is contained in:
Родитель
158468a425
Коммит
21e7995fb4
|
@ -583,7 +583,7 @@ struct JSClass {
|
|||
// with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
|
||||
// previously allowed, but is now an ES5 violation and thus unsupported.
|
||||
//
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT (3 + JSProto_LIMIT * 3 + 30)
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT (3 + JSProto_LIMIT * 3 + 31)
|
||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||
#define JSCLASS_GLOBAL_FLAGS \
|
||||
|
|
|
@ -498,14 +498,17 @@ function ArrayFindIndex(predicate/*, thisArg*/) {
|
|||
#define ITEM_KIND_KEY 2
|
||||
|
||||
// ES6 draft specification, section 22.1.5.1, version 2013-09-05.
|
||||
function CreateArrayIterator(obj, kind) {
|
||||
function CreateArrayIteratorAt(obj, kind, n) {
|
||||
var iteratedObject = ToObject(obj);
|
||||
var iterator = NewArrayIterator();
|
||||
UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_ITERATED_OBJECT, iteratedObject);
|
||||
UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_NEXT_INDEX, 0);
|
||||
UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_NEXT_INDEX, n);
|
||||
UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_ITEM_KIND, kind);
|
||||
return iterator;
|
||||
}
|
||||
function CreateArrayIterator(obj, kind) {
|
||||
return CreateArrayIteratorAt(obj, kind, 0);
|
||||
}
|
||||
|
||||
function ArrayIteratorIdentity() {
|
||||
return this;
|
||||
|
@ -544,6 +547,10 @@ function ArrayIteratorNext() {
|
|||
return { value: index, done: false };
|
||||
}
|
||||
|
||||
function ArrayValuesAt(n) {
|
||||
return CreateArrayIteratorAt(this, ITEM_KIND_VALUE, n);
|
||||
}
|
||||
|
||||
function ArrayValues() {
|
||||
return CreateArrayIterator(this, ITEM_KIND_VALUE);
|
||||
}
|
||||
|
|
|
@ -4861,13 +4861,30 @@ SetAsmJSCacheOps(JSRuntime *rt, const AsmJSCacheOps *callbacks);
|
|||
class MOZ_STACK_CLASS JS_PUBLIC_API(ForOfIterator) {
|
||||
protected:
|
||||
JSContext *cx_;
|
||||
/*
|
||||
* Use the ForOfPIC on the global object (see vm/GlobalObject.h) to try
|
||||
* to optimize iteration across arrays.
|
||||
*
|
||||
* Case 1: Regular Iteration
|
||||
* iterator - pointer to the iterator object.
|
||||
* index - fixed to NOT_ARRAY (== UINT32_MAX)
|
||||
*
|
||||
* Case 2: Optimized Array Iteration
|
||||
* iterator - pointer to the array object.
|
||||
* index - current position in array.
|
||||
*
|
||||
* The cases are distinguished by whether or not |index| is equal to NOT_ARRAY.
|
||||
*/
|
||||
JS::RootedObject iterator;
|
||||
uint32_t index;
|
||||
|
||||
static const uint32_t NOT_ARRAY = UINT32_MAX;
|
||||
|
||||
ForOfIterator(const ForOfIterator &) MOZ_DELETE;
|
||||
ForOfIterator &operator=(const ForOfIterator &) MOZ_DELETE;
|
||||
|
||||
public:
|
||||
ForOfIterator(JSContext *cx) : cx_(cx), iterator(cx) { }
|
||||
ForOfIterator(JSContext *cx) : cx_(cx), iterator(cx_), index(NOT_ARRAY) { }
|
||||
|
||||
enum NonIterableBehavior {
|
||||
ThrowOnNonIterable,
|
||||
|
@ -4896,6 +4913,10 @@ class MOZ_STACK_CLASS JS_PUBLIC_API(ForOfIterator) {
|
|||
bool valueIsIterable() const {
|
||||
return iterator;
|
||||
}
|
||||
|
||||
private:
|
||||
inline bool nextFromOptimizedArray(MutableHandleValue val, bool *done);
|
||||
bool materializeArrayIterator();
|
||||
};
|
||||
|
||||
} /* namespace JS */
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "builtin/TypeRepresentation.h"
|
||||
#include "gc/Zone.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/PIC.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
|
|
|
@ -1278,6 +1278,28 @@ ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavio
|
|||
if (!iterableObj)
|
||||
return false;
|
||||
|
||||
JS_ASSERT(index == NOT_ARRAY);
|
||||
|
||||
// Check the PIC first for a match.
|
||||
if (iterableObj->is<ArrayObject>()) {
|
||||
ForOfPIC::Chain *stubChain = ForOfPIC::getOrCreate(cx);
|
||||
if (!stubChain)
|
||||
return false;
|
||||
|
||||
bool optimized;
|
||||
if (!stubChain->tryOptimizeArray(cx, iterableObj, &optimized))
|
||||
return false;
|
||||
|
||||
if (optimized) {
|
||||
// Got optimized stub. Array is optimizable.
|
||||
index = 0;
|
||||
iterator = iterableObj;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
JS_ASSERT(index == NOT_ARRAY);
|
||||
|
||||
// The iterator is the result of calling obj[@@iterator]().
|
||||
InvokeArgs args(cx);
|
||||
if (!args.init(0))
|
||||
|
@ -1314,37 +1336,110 @@ ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavio
|
|||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
ForOfIterator::nextFromOptimizedArray(MutableHandleValue vp, bool *done)
|
||||
{
|
||||
JS_ASSERT(index != NOT_ARRAY);
|
||||
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx_))
|
||||
return false;
|
||||
|
||||
JS_ASSERT(iterator->isNative());
|
||||
JS_ASSERT(iterator->is<ArrayObject>());
|
||||
|
||||
if (index >= iterator->as<ArrayObject>().length()) {
|
||||
vp.setUndefined();
|
||||
*done = true;
|
||||
return true;
|
||||
}
|
||||
*done = false;
|
||||
|
||||
// Try to get array element via direct access.
|
||||
if (index < iterator->getDenseInitializedLength()) {
|
||||
vp.set(iterator->getDenseElement(index));
|
||||
if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
|
||||
++index;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return JSObject::getElement(cx_, iterator, iterator, index++, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
ForOfIterator::next(MutableHandleValue vp, bool *done)
|
||||
{
|
||||
JS_ASSERT(iterator);
|
||||
|
||||
JSContext *cx = cx_;
|
||||
RootedValue method(cx);
|
||||
if (!JSObject::getProperty(cx, iterator, iterator, cx->names().next, &method))
|
||||
if (index != NOT_ARRAY) {
|
||||
ForOfPIC::Chain *stubChain = ForOfPIC::getOrCreate(cx_);
|
||||
if (!stubChain)
|
||||
return false;
|
||||
|
||||
if (stubChain->isArrayNextStillSane())
|
||||
return nextFromOptimizedArray(vp, done);
|
||||
|
||||
// ArrayIterator.prototype.next changed, materialize a proper
|
||||
// ArrayIterator instance and fall through to slowpath case.
|
||||
if (!materializeArrayIterator())
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue method(cx_);
|
||||
if (!JSObject::getProperty(cx_, iterator, iterator, cx_->names().next, &method))
|
||||
return false;
|
||||
|
||||
InvokeArgs args(cx);
|
||||
InvokeArgs args(cx_);
|
||||
if (!args.init(1))
|
||||
return false;
|
||||
args.setCallee(method);
|
||||
args.setThis(ObjectValue(*iterator));
|
||||
args[0].setUndefined();
|
||||
if (!Invoke(cx, args))
|
||||
if (!Invoke(cx_, args))
|
||||
return false;
|
||||
|
||||
RootedObject resultObj(cx, ToObject(cx, args.rval()));
|
||||
RootedObject resultObj(cx_, ToObject(cx_, args.rval()));
|
||||
if (!resultObj)
|
||||
return false;
|
||||
RootedValue doneVal(cx);
|
||||
if (!JSObject::getProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
|
||||
RootedValue doneVal(cx_);
|
||||
if (!JSObject::getProperty(cx_, resultObj, resultObj, cx_->names().done, &doneVal))
|
||||
return false;
|
||||
*done = ToBoolean(doneVal);
|
||||
if (*done) {
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
return JSObject::getProperty(cx, resultObj, resultObj, cx->names().value, vp);
|
||||
return JSObject::getProperty(cx_, resultObj, resultObj, cx_->names().value, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
ForOfIterator::materializeArrayIterator()
|
||||
{
|
||||
JS_ASSERT(index != NOT_ARRAY);
|
||||
|
||||
const char *nameString = "ArrayValuesAt";
|
||||
|
||||
RootedAtom name(cx_, Atomize(cx_, nameString, strlen(nameString)));
|
||||
if (!name)
|
||||
return false;
|
||||
|
||||
RootedValue val(cx_);
|
||||
if (!cx_->global()->getSelfHostedFunction(cx_, name, name, 1, &val))
|
||||
return false;
|
||||
|
||||
InvokeArgs args(cx_);
|
||||
if (!args.init(1))
|
||||
return false;
|
||||
args.setCallee(val);
|
||||
args.setThis(ObjectValue(*iterator));
|
||||
args[0].set(Int32Value(index));
|
||||
if (!Invoke(cx_, args))
|
||||
return false;
|
||||
|
||||
index = NOT_ARRAY;
|
||||
// Result of call to ArrayValuesAt must be an object.
|
||||
iterator = &args.rval().toObject();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*** Generators **********************************************************************************/
|
||||
|
|
|
@ -169,6 +169,7 @@ UNIFIED_SOURCES += [
|
|||
'vm/Monitor.cpp',
|
||||
'vm/ObjectImpl.cpp',
|
||||
'vm/OldDebugAPI.cpp',
|
||||
'vm/PIC.cpp',
|
||||
'vm/Probes.cpp',
|
||||
'vm/PropertyKey.cpp',
|
||||
'vm/ProxyObject.cpp',
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
macro(apply, apply, "apply") \
|
||||
macro(arguments, arguments, "arguments") \
|
||||
macro(as, as, "as") \
|
||||
macro(ArrayIteratorNext, ArrayIteratorNext, "ArrayIteratorNext") \
|
||||
macro(ArrayType, ArrayType, "ArrayType") \
|
||||
macro(ArrayValues, ArrayValues, "ArrayValues") \
|
||||
macro(buffer, buffer, "buffer") \
|
||||
macro(builder, builder, "builder") \
|
||||
macro(byteLength, byteLength, "byteLength") \
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "builtin/RegExp.h"
|
||||
#include "builtin/SIMD.h"
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "vm/PIC.h"
|
||||
#include "vm/RegExpStatics.h"
|
||||
#include "vm/StopIterationObject.h"
|
||||
#include "vm/WeakMapObject.h"
|
||||
|
@ -741,6 +742,21 @@ GlobalObject::addDebugger(JSContext *cx, Handle<GlobalObject*> global, Debugger
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */ JSObject *
|
||||
GlobalObject::getOrCreateForOfPICObject(JSContext *cx, Handle<GlobalObject *> global)
|
||||
{
|
||||
assertSameCompartment(cx, global);
|
||||
JSObject *forOfPIC = global->getForOfPICObject();
|
||||
if (forOfPIC)
|
||||
return forOfPIC;
|
||||
|
||||
forOfPIC = ForOfPIC::createForOfPICObject(cx, global);
|
||||
if (!forOfPIC)
|
||||
return nullptr;
|
||||
global->setReservedSlot(FOR_OF_PIC_CHAIN, ObjectValue(*forOfPIC));
|
||||
return forOfPIC;
|
||||
}
|
||||
|
||||
bool
|
||||
GlobalObject::getSelfHostedFunction(JSContext *cx, HandleAtom selfHostedName, HandleAtom name,
|
||||
unsigned nargs, MutableHandleValue funVal)
|
||||
|
|
|
@ -111,9 +111,10 @@ class GlobalObject : public JSObject
|
|||
static const unsigned INTRINSICS = DEBUGGERS + 1;
|
||||
static const unsigned FLOAT32X4_TYPE_DESCR = INTRINSICS + 1;
|
||||
static const unsigned INT32X4_TYPE_DESCR = FLOAT32X4_TYPE_DESCR + 1;
|
||||
static const unsigned FOR_OF_PIC_CHAIN = INT32X4_TYPE_DESCR + 1;
|
||||
|
||||
/* Total reserved-slot count for global objects. */
|
||||
static const unsigned RESERVED_SLOTS = INT32X4_TYPE_DESCR + 1;
|
||||
static const unsigned RESERVED_SLOTS = FOR_OF_PIC_CHAIN + 1;
|
||||
|
||||
/*
|
||||
* The slot count must be in the public API for JSCLASS_GLOBAL_FLAGS, and
|
||||
|
@ -675,6 +676,14 @@ class GlobalObject : public JSObject
|
|||
*/
|
||||
static DebuggerVector *getOrCreateDebuggers(JSContext *cx, Handle<GlobalObject*> global);
|
||||
|
||||
inline JSObject *getForOfPICObject() {
|
||||
Value forOfPIC = getReservedSlot(FOR_OF_PIC_CHAIN);
|
||||
if (forOfPIC.isUndefined())
|
||||
return nullptr;
|
||||
return &forOfPIC.toObject();
|
||||
}
|
||||
static JSObject *getOrCreateForOfPICObject(JSContext *cx, Handle<GlobalObject*> global);
|
||||
|
||||
static bool addDebugger(JSContext *cx, Handle<GlobalObject*> global, Debugger *dbg);
|
||||
};
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "vm/Compression.h"
|
||||
#include "vm/ForkJoin.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/String.h"
|
||||
|
||||
#include "jsfuninlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
@ -1166,3 +1167,9 @@ js::SelfHostedFunction(JSContext *cx, HandlePropertyName propName)
|
|||
JS_ASSERT(func.toObject().is<JSFunction>());
|
||||
return &func.toObject().as<JSFunction>();
|
||||
}
|
||||
|
||||
bool
|
||||
js::IsSelfHostedFunctionWithName(JSFunction *fun, JSAtom *name)
|
||||
{
|
||||
return fun->isSelfHostedBuiltin() && fun->getExtendedSlot(0).toString() == name;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include "jsapi.h"
|
||||
|
||||
class JSAtom;
|
||||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
|
@ -20,6 +22,12 @@ namespace js {
|
|||
*/
|
||||
extern const JSWrapObjectCallbacks SelfHostingWrapObjectCallbacks;
|
||||
|
||||
/*
|
||||
* Check whether the given JSFunction is a self-hosted function whose
|
||||
* self-hosted name is the given name.
|
||||
*/
|
||||
bool IsSelfHostedFunctionWithName(JSFunction *fun, JSAtom *name);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* vm_SelfHosting_h_ */
|
||||
|
|
Загрузка…
Ссылка в новой задаче