Bug 795801 - IC StrictPropertyOp setters in IonMonkey. (r=dvander)

This commit is contained in:
Kannan Vijayan 2012-10-23 22:18:11 -04:00
parent 4139eb035b
commit 84f5850fcd
2 changed files with 257 additions and 28 deletions

View File

@ -401,10 +401,7 @@ struct GetNativePropertyStub
// WARNING: if the IonCode object ever moved, since we'd be rooting a nonsense
// WARNING: value here.
// WARNING:
stubCodePatchOffset = masm.pushWithPatch(ImmWord(uintptr_t(-1)));
// Manually adjust framePushed to account for this push which is not otherwise
// accounted for.
masm.setFramePushed(masm.framePushed() + sizeof(uintptr_t));
stubCodePatchOffset = masm.PushWithPatch(ImmWord(uintptr_t(-1)));
if (callNative) {
JS_ASSERT(shape->hasGetterValue() && shape->getterValue().isObject() &&
@ -629,10 +626,11 @@ IonCacheGetProperty::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *o
}
static bool
TryAttachNativeStub(JSContext *cx, IonScript *ion,
IonCacheGetProperty &cache, HandleObject obj,
HandlePropertyName name, const SafepointIndex *safepointIndex,
void *returnAddr, bool *isCacheable)
TryAttachNativeGetPropStub(JSContext *cx, IonScript *ion,
IonCacheGetProperty &cache, HandleObject obj,
HandlePropertyName name,
const SafepointIndex *safepointIndex,
void *returnAddr, bool *isCacheable)
{
JS_ASSERT(!*isCacheable);
@ -732,8 +730,12 @@ js::ion::GetPropertyCache(JSContext *cx, size_t cacheIndex, HandleObject obj, Mu
// limit. Once we can make calls from within generated stubs, a new call
// stub will be generated instead and the previous stubs unlinked.
bool isCacheable = false;
if (!TryAttachNativeStub(cx, ion, cache, obj, name, safepointIndex, returnAddr, &isCacheable))
if (!TryAttachNativeGetPropStub(cx, ion, cache, obj, name,
safepointIndex, returnAddr,
&isCacheable))
{
return false;
}
if (cache.idempotent() && !isCacheable) {
// Invalidate the cache if the property was not found, or was found on
@ -800,7 +802,8 @@ IonCache::reset()
}
bool
IonCacheSetProperty::attachNativeExisting(JSContext *cx, IonScript *ion, JSObject *obj, const Shape *shape)
IonCacheSetProperty::attachNativeExisting(JSContext *cx, IonScript *ion,
HandleObject obj, HandleShape shape)
{
MacroAssembler masm;
@ -859,6 +862,194 @@ IonCacheSetProperty::attachNativeExisting(JSContext *cx, IonScript *ion, JSObjec
return true;
}
bool
IonCacheSetProperty::attachSetterCall(JSContext *cx, IonScript *ion,
HandleObject obj, HandleObject holder, HandleShape shape,
void *returnAddr)
{
MacroAssembler masm;
// Need to set correct framePushed on the masm so that exit frame descriptors are
// properly constructed.
masm.setFramePushed(ion->frameSize());
Label failure;
masm.branchPtr(Assembler::NotEqual,
Address(object(), JSObject::offsetOfShape()),
ImmGCPtr(obj->lastProperty()),
&failure);
// Generate prototype guards if needed.
// Take a scratch register for use, save on stack.
{
RegisterSet regSet(RegisterSet::All());
regSet.take(AnyRegister(object()));
if (!value().constant())
regSet.maybeTake(value().reg());
Register scratchReg = regSet.takeGeneral();
masm.push(scratchReg);
Label protoFailure;
Label protoSuccess;
// Generate prototype/shape guards.
if (obj != holder)
GeneratePrototypeGuards(cx, masm, obj, holder, object(), scratchReg, &protoFailure);
masm.movePtr(ImmGCPtr(holder), scratchReg);
masm.branchPtr(Assembler::NotEqual,
Address(scratchReg, JSObject::offsetOfShape()),
ImmGCPtr(holder->lastProperty()),
&protoFailure);
masm.jump(&protoSuccess);
masm.bind(&protoFailure);
masm.pop(scratchReg);
masm.jump(&failure);
masm.bind(&protoSuccess);
masm.pop(scratchReg);
}
// Good to go for invoking setter.
// saveLive()
masm.PushRegsInMask(liveRegs);
// Remaining registers should basically be free, but we need to use |object| still
// so leave it alone.
RegisterSet regSet(RegisterSet::All());
regSet.take(AnyRegister(object()));
// This is a slower stub path, and we're going to be doing a call anyway. Don't need
// to try so hard to not use the stack. Scratch regs are just taken from the register
// set not including the input, current value saved on the stack, and restored when
// we're done with it.
Register scratchReg = regSet.takeGeneral();
Register argJSContextReg = regSet.takeGeneral();
Register argObjReg = regSet.takeGeneral();
Register argIdReg = regSet.takeGeneral();
Register argStrictReg = regSet.takeGeneral();
Register argVpReg = regSet.takeGeneral();
// Ensure stack is aligned.
DebugOnly<uint32> initialStack = masm.framePushed();
masm.checkStackAlignment();
Label success, exception;
// Push the IonCode pointer for the stub we're generating.
// WARNING:
// WARNING: If IonCode ever becomes relocatable, the following code is incorrect.
// WARNING: Note that we're not marking the pointer being pushed as an ImmGCPtr.
// WARNING: This is not a marking issue since the stub IonCode won't be collected
// WARNING: between the time it's called and when we get here, but it would fail
// WARNING: if the IonCode object ever moved, since we'd be rooting a nonsense
// WARNING: value here.
// WARNING:
CodeOffsetLabel stubCodePatchOffset = masm.PushWithPatch(ImmWord(uintptr_t(-1)));
StrictPropertyOp target = shape->setterOp();
JS_ASSERT(target);
// JSStrictPropertyOp: JSBool fn(JSContext *cx, JSHandleObject obj,
// JSHandleId id, JSBool strict, JSMutableHandleValue vp);
// Push args on stack first so we can take pointers to make handles.
if (value().constant())
masm.Push(value().value());
else
masm.Push(value().reg());
masm.movePtr(StackPointer, argVpReg);
masm.move32(Imm32(strict() ? 1 : 0), argStrictReg);
// push canonical jsid from shape instead of propertyname.
jsid propId;
if (!shape->getUserId(cx, &propId))
return false;
masm.Push(propId, argIdReg);
masm.movePtr(StackPointer, argIdReg);
masm.Push(object());
masm.movePtr(StackPointer, argObjReg);
masm.loadJSContext(argJSContextReg);
if (!masm.buildOOLFakeExitFrame(returnAddr))
return false;
masm.enterFakeExitFrame(ION_FRAME_OOL_PROPERTY_OP);
// Make the call.
masm.setupUnalignedABICall(5, scratchReg);
masm.passABIArg(argJSContextReg);
masm.passABIArg(argObjReg);
masm.passABIArg(argIdReg);
masm.passABIArg(argStrictReg);
masm.passABIArg(argVpReg);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target));
// Test for failure.
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &exception);
masm.jump(&success);
// Handle exception case.
masm.bind(&exception);
masm.handleException();
// Handle success case.
masm.bind(&success);
// The next instruction is removing the footer of the exit frame, so there
// is no need for leaveFakeExitFrame.
// Move the StackPointer back to its original location, unwinding the exit frame.
masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
JS_ASSERT(masm.framePushed() == initialStack);
// restoreLive()
masm.PopRegsInMask(liveRegs);
// Rejoin jump.
RepatchLabel rejoin;
CodeOffsetJump rejoinOffset = masm.jumpWithPatch(&rejoin);
masm.bind(&rejoin);
// Exit jump.
masm.bind(&failure);
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);
stubCodePatchOffset.fixup(&masm);
if (ion->invalidated())
return true;
Assembler::patchDataWithValueCheck(CodeLocationLabel(code, stubCodePatchOffset),
ImmWord(uintptr_t(code)), ImmWord(uintptr_t(-1)));
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 SETPROP calling case stub at %p", code->raw());
return true;
}
bool
IonCacheSetProperty::attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj,
const Shape *oldShape, const Shape *newShape,
@ -968,13 +1159,9 @@ IsPropertyInlineable(JSObject *obj, IonCacheSetProperty &cache)
}
static bool
IsPropertySetInlineable(JSContext *cx, HandleObject obj, JSAtom *atom, jsid *pId, const Shape **pShape)
IsPropertySetInlineable(JSContext *cx, HandleObject obj, HandleId id, MutableHandleShape pshape)
{
jsid id = AtomToId(atom);
*pId = id;
const Shape *shape = obj->nativeLookup(cx, id);
*pShape = shape;
Shape *shape = obj->nativeLookup(cx, id);
if (!shape)
return false;
@ -988,18 +1175,41 @@ IsPropertySetInlineable(JSContext *cx, HandleObject obj, JSAtom *atom, jsid *pId
if (!shape->writable())
return false;
pshape.set(shape);
return true;
}
static bool
IsPropertySetterCallInlineable(JSContext *cx, HandleObject obj, HandleObject holder,
jsid id, HandleShape shape)
{
if (!shape)
return false;
if (shape->hasSlot())
return false;
if (shape->hasDefaultSetter())
return false;
// We only handle propertyOps for now, so fail if we have SetterValue
// (which implies JSNative setter).
if (shape->hasSetterValue())
return false;
return true;
}
static bool
IsPropertyAddInlineable(JSContext *cx, HandleObject obj, jsid id, uint32_t oldSlots,
const Shape **pShape)
MutableHandleShape pShape)
{
// This is not a Add, the property exists.
if (*pShape)
if (pShape.get())
return false;
const Shape *shape = obj->nativeLookup(cx, id);
Shape *shape = obj->nativeLookup(cx, id);
if (!shape || shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter())
return false;
@ -1032,7 +1242,7 @@ IsPropertyAddInlineable(JSContext *cx, HandleObject obj, jsid id, uint32_t oldSl
if (obj->numDynamicSlots() != oldSlots)
return false;
*pShape = shape;
pShape.set(shape);
return true;
}
@ -1042,17 +1252,34 @@ js::ion::SetPropertyCache(JSContext *cx, size_t cacheIndex, HandleObject obj, Ha
{
AutoFlushCache afc ("SetPropertyCache");
IonScript *ion = GetTopIonJSScript(cx)->ion;
void *returnAddr;
const SafepointIndex *safepointIndex;
JSScript *script = GetTopIonJSScript(cx, &safepointIndex, &returnAddr);
IonScript *ion = script->ion;
IonCacheSetProperty &cache = ion->getCache(cacheIndex).toSetProperty();
RootedPropertyName name(cx, cache.name());
jsid id;
const Shape *shape = NULL;
RootedId id(cx, AtomToId(name));
RootedShape shape(cx);
RootedObject holder(cx);
bool inlinable = IsPropertyInlineable(obj, cache);
if (inlinable && IsPropertySetInlineable(cx, obj, name, &id, &shape)) {
cache.incrementStubCount();
if (!cache.attachNativeExisting(cx, ion, obj, shape))
return false;
if (inlinable) {
RootedShape shape(cx);
if (IsPropertySetInlineable(cx, obj, id, &shape)) {
cache.incrementStubCount();
if (!cache.attachNativeExisting(cx, ion, obj, shape))
return false;
} else {
RootedObject holder(cx);
if (!JSObject::lookupProperty(cx, obj, name, &holder, &shape))
return false;
if (IsPropertySetterCallInlineable(cx, obj, holder, id, shape)) {
cache.incrementStubCount();
if (!cache.attachSetterCall(cx, ion, obj, holder, shape, returnAddr))
return false;
}
}
}
uint32_t oldSlots = obj->numDynamicSlots();

View File

@ -302,7 +302,9 @@ class IonCacheSetProperty : public IonCache
ConstantOrRegister value() const { return u.setprop.value.data(); }
bool strict() const { return u.setprop.strict; }
bool attachNativeExisting(JSContext *cx, IonScript *ion, JSObject *obj, const Shape *shape);
bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape);
bool attachSetterCall(JSContext *cx, IonScript *ion, HandleObject obj,
HandleObject holder, HandleShape shape, void *returnAddr);
bool attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, const Shape *oldshape,
const Shape *newshape, const Shape *propshape);
};