author | Nicolas B. Pierron <nicolas.b.pierron@mozilla.com> |
Thu, 20 Dec 2012 04:14:05 -0800 | |
changeset 116617 | 259aa51773334998c074fa8676acbd50bab9d673 |
parent 116616 | d08057e095a2e78e8e102fb022100be0efbe8608 |
child 116618 | 8b94cd856c5959d7464ee7ed1d8e75fb9bb152e0 |
push id | 20032 |
push user | npierron@mozilla.com |
push date | Thu, 20 Dec 2012 12:18:20 +0000 |
treeherder | mozilla-inbound@259aa5177333 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jandem |
bugs | 823061 |
milestone | 20.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/ion/IonCaches.cpp +++ b/js/src/ion/IonCaches.cpp @@ -701,16 +701,153 @@ IonCacheGetProperty::attachCallGetter(JS updateLastJump(exitJump); IonSpew(IonSpew_InlineCaches, "Generated native GETPROP stub at %p %s", code->raw(), idempotent() ? "(idempotent)" : "(not idempotent)"); return true; } +bool +IonCacheGetProperty::attachDenseArrayLength(JSContext *cx, IonScript *ion, JSObject *obj) +{ + JS_ASSERT(obj->isDenseArray()); + JS_ASSERT(!idempotent()); + + Label failures; + MacroAssembler masm; + + // Guard object is a dense array. + RootedObject globalObj(cx, &script->global()); + RootedShape shape(cx, GetDenseArrayShape(cx, globalObj)); + if (!shape) + return false; + masm.branchTestObjShape(Assembler::NotEqual, object(), shape, &failures); + + // Load length. + Register outReg; + if (output().hasValue()) { + outReg = output().valueReg().scratchReg(); + } else { + JS_ASSERT(output().type() == MIRType_Int32); + outReg = output().typedReg().gpr(); + } + + masm.loadPtr(Address(object(), JSObject::offsetOfElements()), outReg); + masm.load32(Address(outReg, ObjectElements::offsetOfLength()), outReg); + + // The length is an unsigned int, but the value encodes a signed int. + JS_ASSERT(object() != outReg); + masm.branchTest32(Assembler::Signed, outReg, outReg, &failures); + + if (output().hasValue()) + masm.tagValue(JSVAL_TYPE_INT32, outReg, output().valueReg()); + + u.getprop.hasDenseArrayLengthStub = true; + incrementStubCount(); + + /* Success. */ + RepatchLabel rejoin_; + CodeOffsetJump rejoinOffset = masm.jumpWithPatch(&rejoin_); + masm.bind(&rejoin_); + + /* Failure. */ + masm.bind(&failures); + RepatchLabel exit_; + CodeOffsetJump exitOffset = masm.jumpWithPatch(&exit_); + masm.bind(&exit_); + + Linker linker(masm); + IonCode *code = linker.newCode(cx); + if (!code) + return false; + + rejoinOffset.fixup(&masm); + exitOffset.fixup(&masm); + + if (ion->invalidated()) + return true; + + CodeLocationJump rejoinJump(code, rejoinOffset); + CodeLocationJump exitJump(code, exitOffset); + CodeLocationJump lastJump_ = lastJump(); + PatchJump(lastJump_, CodeLocationLabel(code)); + PatchJump(rejoinJump, rejoinLabel()); + PatchJump(exitJump, cacheLabel()); + updateLastJump(exitJump); + + IonSpew(IonSpew_InlineCaches, "Generated GETPROP dense array length stub at %p", code->raw()); + + return true; +} + +bool +IonCacheGetProperty::attachTypedArrayLength(JSContext *cx, IonScript *ion, JSObject *obj) +{ + JS_ASSERT(obj->isTypedArray()); + JS_ASSERT(!idempotent()); + + Label failures; + MacroAssembler masm; + + Register tmpReg; + if (output().hasValue()) { + tmpReg = output().valueReg().scratchReg(); + } else { + JS_ASSERT(output().type() == MIRType_Int32); + tmpReg = output().typedReg().gpr(); + } + JS_ASSERT(object() != tmpReg); + + // Implement the negated version of JSObject::isTypedArray predicate. + masm.loadObjClass(object(), tmpReg); + masm.branchPtr(Assembler::Below, tmpReg, ImmWord(&TypedArray::classes[0]), &failures); + masm.branchPtr(Assembler::AboveOrEqual, tmpReg, ImmWord(&TypedArray::classes[TypedArray::TYPE_MAX]), &failures); + + // Load length. + masm.loadTypedOrValue(Address(object(), TypedArray::lengthOffset()), output()); + + u.getprop.hasTypedArrayLengthStub = true; + incrementStubCount(); + + /* Success. */ + RepatchLabel rejoin_; + CodeOffsetJump rejoinOffset = masm.jumpWithPatch(&rejoin_); + masm.bind(&rejoin_); + + /* Failure. */ + masm.bind(&failures); + RepatchLabel exit_; + CodeOffsetJump exitOffset = masm.jumpWithPatch(&exit_); + masm.bind(&exit_); + + Linker linker(masm); + IonCode *code = linker.newCode(cx); + if (!code) + return false; + + rejoinOffset.fixup(&masm); + exitOffset.fixup(&masm); + + if (ion->invalidated()) + return true; + + CodeLocationJump rejoinJump(code, rejoinOffset); + CodeLocationJump exitJump(code, exitOffset); + CodeLocationJump lastJump_ = lastJump(); + PatchJump(lastJump_, CodeLocationLabel(code)); + PatchJump(rejoinJump, rejoinLabel()); + PatchJump(exitJump, cacheLabel()); + updateLastJump(exitJump); + + IonSpew(IonSpew_InlineCaches, "Generated GETPROP typed array length stub at %p", code->raw()); + + return true; +} + static bool TryAttachNativeGetPropStub(JSContext *cx, IonScript *ion, IonCacheGetProperty &cache, HandleObject obj, HandlePropertyName name, const SafepointIndex *safepointIndex, void *returnAddr, bool *isCacheable) { JS_ASSERT(!*isCacheable); @@ -820,16 +957,32 @@ js::ion::GetPropertyCache(JSContext *cx, bool isCacheable = false; if (!TryAttachNativeGetPropStub(cx, ion, cache, obj, name, safepointIndex, returnAddr, &isCacheable)) { return false; } + if (!isCacheable && !cache.idempotent() && cx->names().length == name) { + if (cache.output().type() != MIRType_Value && cache.output().type() != MIRType_Int32) { + // The next execution should cause an invalidation because the type + // does not fit. + isCacheable = false; + } else if (obj->isDenseArray() && !cache.hasDenseArrayLengthStub()) { + isCacheable = true; + if (!cache.attachDenseArrayLength(cx, ion, obj)) + return false; + } else if (obj->isTypedArray() && !cache.hasTypedArrayLengthStub()) { + isCacheable = true; + if (!cache.attachTypedArrayLength(cx, ion, obj)) + return false; + } + } + if (cache.idempotent() && !isCacheable) { // Invalidate the cache if the property was not found, or was found on // a non-native object. This ensures: // 1) The property read has no observable side-effects. // 2) There's no need to dynamically monitor the return type. This would // be complicated since (due to GVN) there can be multiple pc's // associated with a single idempotent cache. IonSpew(IonSpew_InlineCaches, "Invalidating from idempotent cache %s:%d",
--- a/js/src/ion/IonCaches.h +++ b/js/src/ion/IonCaches.h @@ -106,17 +106,19 @@ class IonCache #else static const size_t REJOIN_LABEL_OFFSET = 0; #endif union { struct { Register object; PropertyName *name; TypedOrValueRegisterSpace output; - bool allowGetters; + bool allowGetters : 1; + bool hasDenseArrayLengthStub : 1; + bool hasTypedArrayLengthStub : 1; } getprop; struct { Register object; PropertyName *name; ConstantOrRegisterSpace value; bool strict; } setprop; struct { @@ -260,28 +262,34 @@ class IonCacheGetProperty : public IonCa TypedOrValueRegister output, bool allowGetters) { init(GetProperty, liveRegs, initialJump, rejoinLabel, cacheLabel); u.getprop.object = object; u.getprop.name = name; u.getprop.output.data() = output; u.getprop.allowGetters = allowGetters; + u.getprop.hasDenseArrayLengthStub = false; + u.getprop.hasTypedArrayLengthStub = false; } Register object() const { return u.getprop.object; } PropertyName *name() const { return u.getprop.name; } TypedOrValueRegister output() const { return u.getprop.output.data(); } bool allowGetters() const { return u.getprop.allowGetters; } + bool hasDenseArrayLengthStub() const { return u.getprop.hasDenseArrayLengthStub; } + bool hasTypedArrayLengthStub() const { return u.getprop.hasTypedArrayLengthStub; } bool attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder, HandleShape shape); bool attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder, HandleShape shape, const SafepointIndex *safepointIndex, void *returnAddr); + bool attachDenseArrayLength(JSContext *cx, IonScript *ion, JSObject *obj); + bool attachTypedArrayLength(JSContext *cx, IonScript *ion, JSObject *obj); }; class IonCacheSetProperty : public IonCache { public: IonCacheSetProperty(CodeOffsetJump initialJump, CodeOffsetLabel rejoinLabel, CodeOffsetLabel cacheLabel,
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/ion/ArrayLengthGetPropertyIC.js @@ -0,0 +1,54 @@ +function intLength (a, l) { + var res = 0; + for (var i = 0; i < l; i++) + res += a.length; + return res / l; +} + +function valueLength (a, l) { + var res = 0; + for (var i = 0; i < l; i++) + res += a.length; + return res / l; +} + +var denseArray = [0,1,2,3,4,5,6,7,8,9]; +var typedArray = new Uint8Array(10); +var hugeArray = new Array(4294967295); +var fakeArray1 = { length: 10 }; +var fakeArray2 = { length: 10.5 }; + +// Check the interpreter result and play with TI type objects. +assertEq(intLength(denseArray, 10), 10); +assertEq(intLength(typedArray, 10), 10); +// assertEq(intLength(fakeArray1, 10), 10); + +assertEq(valueLength(denseArray, 10), 10); +assertEq(valueLength(typedArray, 10), 10); +assertEq(valueLength(hugeArray , 10), 4294967295); +assertEq(valueLength(fakeArray2, 10), 10.5); + +// Heat up to compile (either JM / Ion) +assertEq(intLength(denseArray, 100), 10); +assertEq(valueLength(denseArray, 100), 10); + +// No bailout should occur during any of the following checks: + +// Check get-property length IC with dense array. +assertEq(intLength(denseArray, 1), 10); +assertEq(valueLength(denseArray, 1), 10); + +// Check get-property length IC with typed array. +assertEq(intLength(typedArray, 1), 10); +assertEq(valueLength(typedArray, 1), 10); + +// Check length which do not fit on non-double value. +assertEq(valueLength(hugeArray, 1), 4294967295); + +// Check object length property. +assertEq(intLength(fakeArray1, 1), 10); +assertEq(valueLength(fakeArray2, 1), 10.5); + +// Cause invalidation of intLength by returning a double. +assertEq(intLength(hugeArray, 1), 4294967295); +assertEq(intLength(fakeArray2, 1), 10.5);