Bug 870514 - Add Ion stubs for optimized shadowed gets on proxy objects. r=h4writer

This commit is contained in:
Kannan Vijayan 2013-05-21 16:40:10 -04:00
parent 7abd17539e
commit e503cd4d2f
7 changed files with 263 additions and 5 deletions

View File

@ -625,7 +625,8 @@ EmitLoadSlot(MacroAssembler &masm, JSObject *holder, Shape *shape, Register hold
static void
GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, JSObject *obj,
PropertyName *name, Register object, Label *stubFailure)
PropertyName *name, Register object, Label *stubFailure,
bool skipExpandoCheck=false)
{
MOZ_ASSERT(IsCacheableListBase(obj));
@ -639,6 +640,9 @@ GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, JSObject *obj,
// Check that object is a ListBase.
masm.branchPrivatePtr(Assembler::NotEqual, handlerAddr, ImmWord(GetProxyHandler(obj)), stubFailure);
if (skipExpandoCheck)
return;
// For the remaining code, we need to reserve some registers to load a value.
// This is ugly, but unvaoidable.
RegisterSet listBaseRegSet(RegisterSet::All());
@ -1015,6 +1019,118 @@ GetPropertyIC::attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSOb
return linkAndAttachStub(cx, masm, attacher, ion, attachKind);
}
bool
GetPropertyIC::attachListBaseShadowed(JSContext *cx, IonScript *ion, JSObject *obj,
void *returnAddr)
{
JS_ASSERT(!idempotent());
JS_ASSERT(IsCacheableListBase(obj));
JS_ASSERT(output().hasValue());
Label failures;
MacroAssembler masm(cx);
RepatchStubAppender attacher(*this);
masm.setFramePushed(ion->frameSize());
// Guard on the shape of the object.
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object(), JSObject::offsetOfShape()),
ImmGCPtr(obj->lastProperty()),
&failures);
// Make sure object is a ListBase proxy
GenerateListBaseChecks(cx, masm, obj, name(), object(), &failures,
/*skipExpandoCheck=*/true);
// saveLive()
masm.PushRegsInMask(liveRegs_);
DebugOnly<uint32_t> initialStack = masm.framePushed();
// Remaining registers should be free, but we need to use |object| still
// so leave it alone.
RegisterSet regSet(RegisterSet::All());
regSet.take(AnyRegister(object()));
// Proxy::get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
// MutableHandleValue vp)
Register argJSContextReg = regSet.takeGeneral();
Register argProxyReg = regSet.takeGeneral();
Register argIdReg = regSet.takeGeneral();
Register argVpReg = regSet.takeGeneral();
Register scratch = regSet.takeGeneral();
// Push args on stack first so we can take pointers to make handles.
masm.Push(UndefinedValue());
masm.movePtr(StackPointer, argVpReg);
RootedId propId(cx, AtomToId(name()));
masm.Push(propId, scratch);
masm.movePtr(StackPointer, argIdReg);
// Pushing object and receiver. Both are same, so Handle to one is equivalent to
// handle to other.
masm.Push(object());
masm.Push(object());
masm.movePtr(StackPointer, argProxyReg);
masm.loadJSContext(argJSContextReg);
if (!masm.buildOOLFakeExitFrame(returnAddr))
return false;
masm.enterFakeExitFrame(ION_FRAME_OOL_PROXY_GET);
// Make the call.
masm.setupUnalignedABICall(5, scratch);
masm.passABIArg(argJSContextReg);
masm.passABIArg(argProxyReg);
masm.passABIArg(argProxyReg);
masm.passABIArg(argIdReg);
masm.passABIArg(argVpReg);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Proxy::get));
// Test for failure.
Label exception;
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &exception);
// Load the outparam vp[0] into output register(s).
masm.loadValue(
Address(StackPointer, IonOOLProxyGetExitFrameLayout::offsetOfResult()),
JSReturnOperand);
Label success;
masm.jump(&success);
// Handle exception case.
masm.bind(&exception);
masm.handleException();
// Handle success case.
masm.bind(&success);
masm.storeCallResultValue(output());
// 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_);
// Success.
attacher.jumpRejoin(masm);
// Failure.
masm.bind(&failures);
attacher.jumpNextStub(masm);
return linkAndAttachStub(cx, masm, attacher, ion, "list base shadowed get");
}
bool
GetPropertyIC::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj,
JSObject *holder, HandleShape shape,
@ -1264,12 +1380,15 @@ TryAttachNativeGetPropStub(JSContext *cx, IonScript *ion,
RootedObject checkObj(cx, obj);
if (IsCacheableListBase(obj)) {
RootedId id(cx, NameToId(name));
ListBaseShadowsResult shadows =
GetListBaseShadowsCheck()(cx, obj, id);
ListBaseShadowsResult shadows = GetListBaseShadowsCheck()(cx, obj, id);
if (shadows == ShadowCheckFailed)
return false;
if (shadows == Shadows)
return true;
if (shadows == Shadows) {
if (cache.idempotent() || !cache.output().hasValue())
return true;
*isCacheable = true;
return cache.attachListBaseShadowed(cx, ion, obj, returnAddr);
}
if (shadows == DoesntShadowUnique)
// We reset the cache to clear out an existing IC for this object
// (if there is one). The generation is a constant in the generated

View File

@ -542,6 +542,7 @@ class GetPropertyIC : public RepatchIonCache
bool attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
HandleShape shape);
bool attachListBaseShadowed(JSContext *cx, IonScript *ion, JSObject *obj, void *returnAddr);
bool attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
HandleShape shape,
const SafepointIndex *safepointIndex, void *returnAddr);

View File

@ -138,6 +138,7 @@ class IonFrameIterator
bool isNative() const;
bool isOOLNativeGetter() const;
bool isOOLPropertyOp() const;
bool isOOLProxyGet() const;
bool isDOMExit() const;
bool isEntry() const {
return type_ == IonFrame_Entry;

View File

@ -127,6 +127,14 @@ IonFrameIterator::isOOLPropertyOp() const
return exitFrame()->footer()->ionCode() == ION_FRAME_OOL_PROPERTY_OP;
}
bool
IonFrameIterator::isOOLProxyGet() const
{
if (type_ != IonFrame_Exit)
return false;
return exitFrame()->footer()->ionCode() == ION_FRAME_OOL_PROXY_GET;
}
bool
IonFrameIterator::isDOMExit() const
{
@ -829,6 +837,16 @@ MarkIonExitFrame(JSTracer *trc, const IonFrameIterator &frame)
return;
}
if (frame.isOOLProxyGet()) {
IonOOLProxyGetExitFrameLayout *oolproxyget = frame.exitFrame()->oolProxyGetExit();
gc::MarkIonCodeRoot(trc, oolproxyget->stubCode(), "ion-ool-proxy-get-code");
gc::MarkValueRoot(trc, oolproxyget->vp(), "ion-ool-proxy-get-vp");
gc::MarkIdRoot(trc, oolproxyget->id(), "ion-ool-proxy-get-id");
gc::MarkObjectRoot(trc, oolproxyget->proxy(), "ion-ool-proxy-get-proxy");
gc::MarkObjectRoot(trc, oolproxyget->receiver(), "ion-ool-proxy-get-receiver");
return;
}
if (frame.isDOMExit()) {
IonDOMExitFrameLayout *dom = frame.exitFrame()->DOMExit();
gc::MarkObjectRoot(trc, dom->thisObjAddress(), "ion-dom-args");

View File

@ -192,6 +192,7 @@ class IonBaselineStubFrameLayout : public IonCommonFrameLayout
class IonNativeExitFrameLayout;
class IonOOLNativeGetterExitFrameLayout;
class IonOOLPropertyOpExitFrameLayout;
class IonOOLProxyGetExitFrameLayout;
class IonDOMExitFrameLayout;
// this is the frame layout when we are exiting ion code, and about to enter EABI code
@ -234,6 +235,9 @@ class IonExitFrameLayout : public IonCommonFrameLayout
inline bool isOOLPropertyOpExit() {
return footer()->ionCode() == ION_FRAME_OOL_PROPERTY_OP;
}
inline bool isOOLProxyGetExit() {
return footer()->ionCode() == ION_FRAME_OOL_PROXY_GET;
}
inline bool isDomExit() {
IonCode *code = footer()->ionCode();
return
@ -255,6 +259,10 @@ class IonExitFrameLayout : public IonCommonFrameLayout
JS_ASSERT(isOOLPropertyOpExit());
return reinterpret_cast<IonOOLPropertyOpExitFrameLayout *>(footer());
}
inline IonOOLProxyGetExitFrameLayout *oolProxyGetExit() {
JS_ASSERT(isOOLProxyGetExit());
return reinterpret_cast<IonOOLProxyGetExitFrameLayout *>(footer());
}
inline IonDOMExitFrameLayout *DOMExit() {
JS_ASSERT(isDomExit());
return reinterpret_cast<IonDOMExitFrameLayout *>(footer());
@ -372,6 +380,57 @@ class IonOOLPropertyOpExitFrameLayout
}
};
// Proxy::get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
// MutableHandleValue vp)
class IonOOLProxyGetExitFrameLayout
{
protected: // only to silence a clang warning about unused private fields
IonExitFooterFrame footer_;
IonExitFrameLayout exit_;
// The proxy object.
JSObject *proxy_;
// Object for JSHandleObject
JSObject *receiver_;
// id for JSHandleId
jsid id_;
// space for JSMutableHandleValue result
// use two uint32_t so compiler doesn't align.
uint32_t vp0_;
uint32_t vp1_;
// pointer to root the stub's IonCode
IonCode *stubCode_;
public:
static inline size_t Size() {
return sizeof(IonOOLProxyGetExitFrameLayout);
}
static size_t offsetOfResult() {
return offsetof(IonOOLProxyGetExitFrameLayout, vp0_);
}
inline IonCode **stubCode() {
return &stubCode_;
}
inline Value *vp() {
return reinterpret_cast<Value*>(&vp0_);
}
inline jsid *id() {
return &id_;
}
inline JSObject **receiver() {
return &receiver_;
}
inline JSObject **proxy() {
return &proxy_;
}
};
class IonDOMExitFrameLayout
{
IonExitFooterFrame footer_;

View File

@ -12,5 +12,6 @@
#define ION_FRAME_DOMMETHOD ((IonCode *)0x3)
#define ION_FRAME_OOL_NATIVE_GETTER ((IonCode *)0x4)
#define ION_FRAME_OOL_PROPERTY_OP ((IonCode *)0x5)
#define ION_FRAME_OOL_PROXY_GET ((IonCode *)0x6)
#endif

View File

@ -155,6 +155,7 @@ class IonExitFooterFrame
class IonNativeExitFrameLayout;
class IonOOLNativeGetterExitFrameLayout;
class IonOOLPropertyOpExitFrameLayout;
class IonOOLProxyGetExitFrameLayout;
class IonDOMExitFrameLayout;
class IonExitFrameLayout : public IonCommonFrameLayout
@ -196,6 +197,9 @@ class IonExitFrameLayout : public IonCommonFrameLayout
inline bool isOOLPropertyOpExit() {
return footer()->ionCode() == ION_FRAME_OOL_PROPERTY_OP;
}
inline bool isOOLProxyGetExit() {
return footer()->ionCode() == ION_FRAME_OOL_PROXY_GET;
}
inline bool isDomExit() {
IonCode *code = footer()->ionCode();
return
@ -217,6 +221,10 @@ class IonExitFrameLayout : public IonCommonFrameLayout
JS_ASSERT(isOOLPropertyOpExit());
return reinterpret_cast<IonOOLPropertyOpExitFrameLayout *>(footer());
}
inline IonOOLProxyGetExitFrameLayout *oolProxyGetExit() {
JS_ASSERT(isOOLProxyGetExit());
return reinterpret_cast<IonOOLProxyGetExitFrameLayout *>(footer());
}
inline IonDOMExitFrameLayout *DOMExit() {
JS_ASSERT(isDomExit());
return reinterpret_cast<IonDOMExitFrameLayout *>(footer());
@ -336,6 +344,57 @@ class IonOOLPropertyOpExitFrameLayout
}
};
// Proxy::get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
// MutableHandleValue vp)
class IonOOLProxyGetExitFrameLayout
{
protected: // only to silence a clang warning about unused private fields
IonExitFooterFrame footer_;
IonExitFrameLayout exit_;
// The proxy object.
JSObject *proxy_;
// Object for JSHandleObject
JSObject *receiver_;
// id for JSHandleId
jsid id_;
// space for JSMutableHandleValue result
// use two uint32_t so compiler doesn't align.
uint32_t vp0_;
uint32_t vp1_;
// pointer to root the stub's IonCode
IonCode *stubCode_;
public:
static inline size_t Size() {
return sizeof(IonOOLProxyGetExitFrameLayout);
}
static size_t offsetOfResult() {
return offsetof(IonOOLProxyGetExitFrameLayout, vp0_);
}
inline IonCode **stubCode() {
return &stubCode_;
}
inline Value *vp() {
return reinterpret_cast<Value*>(&vp0_);
}
inline jsid *id() {
return &id_;
}
inline JSObject **receiver() {
return &receiver_;
}
inline JSObject **proxy() {
return &proxy_;
}
};
class IonDOMExitFrameLayout
{
protected: // only to silence a clang warning about unused private fields