Bug 952891 - Add PIC fast-path for ForOfIterator iteration over arrays. r=jimb r=jorendorff DONTBUILD

This commit is contained in:
Kannan Vijayan 2014-02-13 14:29:00 -05:00
Родитель 158468a425
Коммит 21e7995fb4
11 изменённых файлов: 181 добавлений и 14 удалений

Просмотреть файл

@ -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_ */