Bug 1129382 - Add Ion ICs for scripted getters/setters. r=efaust,nbp,djvj

This commit is contained in:
Jan de Mooij 2015-02-11 14:42:01 +01:00
parent 0fcdc6c58e
commit c62c6dfd2a
13 changed files with 552 additions and 54 deletions

View File

@ -0,0 +1,59 @@
if (getJitCompilerOptions()["ion.warmup.trigger"] > 50)
setJitCompilerOption("ion.warmup.trigger", 50);
function getObjects() {
var objs = [];
// Own scripted getter/setter.
objs.push({x: 0, get prop() {
assertJitStackInvariants();
return ++this.x;
}, set prop(v) {
assertJitStackInvariants();
this.x += v;
}});
// Scripted getter/setter on prototype. Also verify extra formal args are
// handled correctly.
function getter(a, b, c) {
assertEq(arguments.length, 0);
assertEq(a, undefined);
assertEq(b, undefined);
assertEq(c, undefined);
assertJitStackInvariants();
bailout();
return ++this.y;
}
function setter1(a, b) {
assertEq(arguments.length, 1);
assertEq(b, undefined);
assertJitStackInvariants();
this.y = a;
bailout();
return "unused";
}
var proto = {};
Object.defineProperty(proto, "prop", {get: getter, set: setter1});
objs.push(Object.create(proto));
function setter2() {
assertEq(arguments.length, 1);
assertJitStackInvariants();
this.y = arguments[0];
}
proto = {};
Object.defineProperty(proto, "prop", {get: getter, set: setter2});
objs.push(Object.create(proto));
return objs;
}
function f() {
var objs = getObjects();
var res = 0;
for (var i=0; i<200; i++) {
var o = objs[i % objs.length];
o.prop = 2;
res += o.prop;
}
assertEq(res, 7233);
}
f();

View File

@ -0,0 +1,32 @@
// Ensure readSPSProfilingStack() doesn't crash with Ion
// getter/setter ICs on the stack.
function getObjects() {
var objs = [];
objs.push({x: 0, get prop() {
readSPSProfilingStack();
return ++this.x;
}, set prop(v) {
readSPSProfilingStack();
this.x = v + 2;
}});
objs.push({x: 0, y: 0, get prop() {
readSPSProfilingStack();
return this.y;
}, set prop(v) {
readSPSProfilingStack();
this.y = v;
}});
return objs;
}
function f() {
var objs = getObjects();
var res = 0;
for (var i=0; i<100; i++) {
var o = objs[i % objs.length];
res += o.prop;
o.prop = i;
}
assertEq(res, 4901);
}
enableSPSProfiling();
f();

View File

@ -329,10 +329,11 @@ struct BaselineStackBuilder
BufferPointer<JitFrameLayout> topFrame = topFrameAddress();
FrameType type = topFrame->prevType();
// For IonJS and Entry frames, the "saved" frame pointer in the baseline
// frame is meaningless, since Ion saves all registers before calling other ion
// frames, and the entry frame saves all registers too.
if (type == JitFrame_IonJS || type == JitFrame_Entry)
// For IonJS, IonAccessorIC and Entry frames, the "saved" frame pointer
// in the baseline frame is meaningless, since Ion saves all registers
// before calling other ion frames, and the entry frame saves all
// registers too.
if (type == JitFrame_IonJS || type == JitFrame_Entry || type == JitFrame_IonAccessorIC)
return nullptr;
// BaselineStub - Baseline calling into Ion.
@ -1348,7 +1349,8 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIter
MOZ_ASSERT(prevFrameType == JitFrame_IonJS ||
prevFrameType == JitFrame_BaselineStub ||
prevFrameType == JitFrame_Entry ||
prevFrameType == JitFrame_Rectifier);
prevFrameType == JitFrame_Rectifier ||
prevFrameType == JitFrame_IonAccessorIC);
// All incoming frames are going to look like this:
//

View File

@ -2507,10 +2507,14 @@ InvalidateActivation(FreeOp *fop, const JitActivationIterator &activations, bool
case JitFrame_Unwound_IonJS:
case JitFrame_Unwound_BaselineJS:
case JitFrame_Unwound_BaselineStub:
case JitFrame_Unwound_IonAccessorIC:
MOZ_CRASH("invalid");
case JitFrame_Unwound_Rectifier:
JitSpew(JitSpew_IonInvalidate, "#%d unwound rectifier frame @ %p", frameno, it.fp());
break;
case JitFrame_IonAccessorIC:
JitSpew(JitSpew_IonInvalidate, "#%d ion IC getter/setter frame @ %p", frameno, it.fp());
break;
case JitFrame_Entry:
JitSpew(JitSpew_IonInvalidate, "#%d entry frame @ %p", frameno, it.fp());
break;

View File

@ -652,6 +652,26 @@ IsCacheableGetPropCallNative(JSObject *obj, JSObject *holder, Shape *shape)
return !obj->getClass()->ext.outerObject;
}
static bool
IsCacheableGetPropCallScripted(JSObject *obj, JSObject *holder, Shape *shape)
{
if (!shape || !IsCacheableProtoChainForIon(obj, holder))
return false;
if (!shape->hasGetterValue() || !shape->getterValue().isObject())
return false;
if (!shape->getterValue().toObject().is<JSFunction>())
return false;
JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
if (!getter.hasJITCode())
return false;
// See IsCacheableGetPropCallNative.
return !obj->getClass()->ext.outerObject;
}
static bool
IsCacheableGetPropCallPropertyOp(JSObject *obj, JSObject *holder, Shape *shape)
{
@ -920,20 +940,15 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm,
// 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 argUintNReg = regSet.takeGeneral();
Register argVpReg = regSet.takeGeneral();
Register scratchReg = regSet.takeGeneral();
// Shape has a getter function.
bool callNative = IsCacheableGetPropCallNative(obj, holder, shape);
MOZ_ASSERT_IF(!callNative, IsCacheableGetPropCallPropertyOp(obj, holder, shape));
// Shape has a JSNative, PropertyOp or scripted getter function.
if (IsCacheableGetPropCallNative(obj, holder, shape)) {
Register argJSContextReg = regSet.takeGeneral();
Register argUintNReg = regSet.takeGeneral();
Register argVpReg = regSet.takeGeneral();
if (callNative) {
MOZ_ASSERT(shape->hasGetterValue() && shape->getterValue().isObject() &&
shape->getterValue().toObject().is<JSFunction>());
JSFunction *target = &shape->getterValue().toObject().as<JSFunction>();
MOZ_ASSERT(target);
MOZ_ASSERT(target->isNative());
@ -977,7 +992,10 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm,
// masm.leaveExitFrame & pop locals
masm.adjustStack(IonOOLNativeExitFrameLayout::Size(0));
} else {
} else if (IsCacheableGetPropCallPropertyOp(obj, holder, shape)) {
Register argJSContextReg = regSet.takeGeneral();
Register argUintNReg = regSet.takeGeneral();
Register argVpReg = regSet.takeGeneral();
Register argObjReg = argUintNReg;
Register argIdReg = regSet.takeGeneral();
@ -1023,6 +1041,51 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm,
// masm.leaveExitFrame & pop locals.
masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
} else {
MOZ_ASSERT(IsCacheableGetPropCallScripted(obj, holder, shape));
JSFunction *target = &shape->getterValue().toObject().as<JSFunction>();
uint32_t framePushedBefore = masm.framePushed();
// Construct IonAccessorICFrameLayout.
uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS);
attacher.pushStubCodePointer(masm);
masm.Push(Imm32(descriptor));
masm.Push(ImmPtr(returnAddr));
// The JitFrameLayout pushed below will be aligned to JitStackAlignment,
// so we just have to make sure the stack is aligned after we push the
// |this| + argument Values.
uint32_t argSize = (target->nargs() + 1) * sizeof(Value);
uint32_t padding = ComputeByteAlignment(masm.framePushed() + argSize, JitStackAlignment);
MOZ_ASSERT(padding % sizeof(uintptr_t) == 0);
MOZ_ASSERT(padding < JitStackAlignment);
masm.reserveStack(padding);
for (size_t i = 0; i < target->nargs(); i++)
masm.Push(UndefinedValue());
masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object)));
masm.movePtr(ImmMaybeNurseryPtr(target), scratchReg);
descriptor = MakeFrameDescriptor(argSize + padding, JitFrame_IonAccessorIC);
masm.Push(Imm32(0)); // argc
masm.Push(scratchReg);
masm.Push(Imm32(descriptor));
// Check stack alignment. Add sizeof(uintptr_t) for the return address.
MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
// The getter has JIT code now and we will only discard the getter's JIT
// code when discarding all JIT code in the Zone, so we can assume it'll
// still have JIT code.
MOZ_ASSERT(target->hasJITCode());
masm.loadPtr(Address(scratchReg, JSFunction::offsetOfNativeOrScript()), scratchReg);
masm.loadBaselineOrIonRaw(scratchReg, scratchReg, nullptr);
masm.callJit(scratchReg);
masm.storeCallResultValue(output);
masm.freeStack(masm.framePushed() - framePushedBefore);
}
masm.icRestoreLive(liveRegs, aic);
@ -1240,12 +1303,13 @@ CanAttachNativeGetProp(typename GetPropCache::Context cx, const GetPropCache &ca
//
// Be careful when adding support for other getters here: for outer window
// proxies, IonBuilder can innerize and pass us the inner window (the global),
// see IonBuilder::getPropTryInnerize. This is fine for native getters because
// IsCacheableGetPropCallNative checks they can handle both the inner and
// outer object, but scripted getters would need a similar mechanism.
// see IonBuilder::getPropTryInnerize. This is fine for native/scripted getters
// because IsCacheableGetPropCallNative and IsCacheableGetPropCallScripted
// handle this.
if (cache.allowGetters() &&
(IsCacheableGetPropCallNative(obj, holder, shape) ||
IsCacheableGetPropCallPropertyOp(obj, holder, shape)))
IsCacheableGetPropCallPropertyOp(obj, holder, shape) ||
IsCacheableGetPropCallScripted(obj, holder, shape)))
{
// Don't enable getter call if cache is idempotent, since they can be
// effectful. This is handled by allowGetters()
@ -1995,6 +2059,19 @@ IsCacheableSetPropCallNative(HandleObject obj, HandleObject holder, HandleShape
shape->setterObject()->as<JSFunction>().isNative();
}
static bool
IsCacheableSetPropCallScripted(HandleObject obj, HandleObject holder, HandleShape shape)
{
MOZ_ASSERT(obj->isNative());
if (!shape || !IsCacheableProtoChainForIon(obj, holder))
return false;
return shape->hasSetterValue() && shape->setterObject() &&
shape->setterObject()->is<JSFunction>() &&
shape->setterObject()->as<JSFunction>().hasJITCode();
}
static bool
IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder, HandleShape shape)
{
@ -2238,14 +2315,12 @@ GenerateCallSetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
//
// Be very careful not to use any of these before value is pushed, since they
// might shadow.
Register scratchReg = regSet.takeGeneral();
Register argJSContextReg = regSet.takeGeneral();
Register argVpReg = regSet.takeGeneral();
Register scratchReg = regSet.takeGeneral();
bool callNative = IsCacheableSetPropCallNative(obj, holder, shape);
MOZ_ASSERT_IF(!callNative, IsCacheableSetPropCallPropertyOp(obj, holder, shape));
if (IsCacheableSetPropCallNative(obj, holder, shape)) {
Register argJSContextReg = regSet.takeGeneral();
Register argVpReg = regSet.takeGeneral();
if (callNative) {
MOZ_ASSERT(shape->hasSetterValue() && shape->setterObject() &&
shape->setterObject()->is<JSFunction>());
JSFunction *target = &shape->setterObject()->as<JSFunction>();
@ -2290,7 +2365,9 @@ GenerateCallSetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
// masm.leaveExitFrame & pop locals.
masm.adjustStack(IonOOLNativeExitFrameLayout::Size(1));
} else {
} else if (IsCacheableSetPropCallPropertyOp(obj, holder, shape)) {
Register argJSContextReg = regSet.takeGeneral();
Register argVpReg = regSet.takeGeneral();
Register argObjReg = regSet.takeGeneral();
Register argIdReg = regSet.takeGeneral();
Register argStrictReg = regSet.takeGeneral();
@ -2338,6 +2415,52 @@ GenerateCallSetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
// masm.leaveExitFrame & pop locals.
masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
} else {
MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, shape));
JSFunction *target = &shape->setterValue().toObject().as<JSFunction>();
uint32_t framePushedBefore = masm.framePushed();
// Construct IonAccessorICFrameLayout.
uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS);
attacher.pushStubCodePointer(masm);
masm.Push(Imm32(descriptor));
masm.Push(ImmPtr(returnAddr));
// The JitFrameLayout pushed below will be aligned to JitStackAlignment,
// so we just have to make sure the stack is aligned after we push the
// |this| + argument Values.
uint32_t numArgs = Max(size_t(1), target->nargs());
uint32_t argSize = (numArgs + 1) * sizeof(Value);
uint32_t padding = ComputeByteAlignment(masm.framePushed() + argSize, JitStackAlignment);
MOZ_ASSERT(padding % sizeof(uintptr_t) == 0);
MOZ_ASSERT(padding < JitStackAlignment);
masm.reserveStack(padding);
for (size_t i = 1; i < target->nargs(); i++)
masm.Push(UndefinedValue());
masm.Push(value);
masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object)));
masm.movePtr(ImmMaybeNurseryPtr(target), scratchReg);
descriptor = MakeFrameDescriptor(argSize + padding, JitFrame_IonAccessorIC);
masm.Push(Imm32(1)); // argc
masm.Push(scratchReg);
masm.Push(Imm32(descriptor));
// Check stack alignment. Add sizeof(uintptr_t) for the return address.
MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
// The setter has JIT code now and we will only discard the setter's JIT
// code when discarding all JIT code in the Zone, so we can assume it'll
// still have JIT code.
MOZ_ASSERT(target->hasJITCode());
masm.loadPtr(Address(scratchReg, JSFunction::offsetOfNativeOrScript()), scratchReg);
masm.loadBaselineOrIonRaw(scratchReg, scratchReg, nullptr);
masm.callJit(scratchReg);
masm.freeStack(masm.framePushed() - framePushedBefore);
}
masm.icRestoreLive(liveRegs, aic);
@ -2364,7 +2487,8 @@ IsCacheableDOMProxyUnshadowedSetterCall(JSContext *cx, HandleObject obj, HandleP
return true;
if (!IsCacheableSetPropCallNative(checkObj, holder, shape) &&
!IsCacheableSetPropCallPropertyOp(checkObj, holder, shape))
!IsCacheableSetPropCallPropertyOp(checkObj, holder, shape) &&
!IsCacheableSetPropCallScripted(checkObj, holder, shape))
{
return true;
}
@ -2729,7 +2853,8 @@ CanAttachNativeSetProp(JSContext *cx, HandleObject obj, HandleId id, ConstantOrR
return SetPropertyIC::MaybeCanAttachAddSlot;
if (IsCacheableSetPropCallPropertyOp(obj, holder, shape) ||
IsCacheableSetPropCallNative(obj, holder, shape))
IsCacheableSetPropCallNative(obj, holder, shape) ||
IsCacheableSetPropCallScripted(obj, holder, shape))
{
return SetPropertyIC::CanAttachCallSetter;
}
@ -4095,7 +4220,8 @@ IsCacheableNameCallGetter(HandleObject scopeChain, HandleObject obj, HandleObjec
return false;
return IsCacheableGetPropCallNative(obj, holder, shape) ||
IsCacheableGetPropCallPropertyOp(obj, holder, shape);
IsCacheableGetPropCallPropertyOp(obj, holder, shape) ||
IsCacheableGetPropCallScripted(obj, holder, shape);
}
bool

View File

@ -44,18 +44,17 @@ enum FrameType
// mismatches in calls.
JitFrame_Rectifier,
// Ion IC calling a scripted getter/setter.
JitFrame_IonAccessorIC,
// An unwound JS frame is a JS frame signalling that its callee frame has been
// turned into an exit frame (see EnsureExitFrame). Used by Ion bailouts and
// Baseline exception unwinding.
JitFrame_Unwound_BaselineJS,
JitFrame_Unwound_IonJS,
// Like Unwound_IonJS, but the caller is a baseline stub frame.
JitFrame_Unwound_BaselineStub,
// An unwound rectifier frame is a rectifier frame signalling that its callee
// frame has been turned into an exit frame (see EnsureExitFrame).
JitFrame_Unwound_Rectifier,
JitFrame_Unwound_IonAccessorIC,
// An exit frame is necessary for transitioning from a JS frame into C++.
// From within C++, an exit frame is always the last frame in any

View File

@ -249,6 +249,7 @@ SizeOfFramePrefix(FrameType type)
case JitFrame_Unwound_IonJS:
return JitFrameLayout::Size();
case JitFrame_BaselineStub:
case JitFrame_Unwound_BaselineStub:
return BaselineStubFrameLayout::Size();
case JitFrame_Rectifier:
return RectifierFrameLayout::Size();
@ -256,9 +257,12 @@ SizeOfFramePrefix(FrameType type)
return IonUnwoundRectifierFrameLayout::Size();
case JitFrame_Exit:
return ExitFrameLayout::Size();
default:
MOZ_CRASH("unknown frame type");
case JitFrame_IonAccessorIC:
case JitFrame_Unwound_IonAccessorIC:
return IonAccessorICFrameLayout::Size();
}
MOZ_CRASH("unknown frame type");
}
uint8_t *
@ -302,6 +306,8 @@ JitFrameIterator::operator++()
type_ = JitFrame_BaselineJS;
else if (type_ == JitFrame_Unwound_BaselineStub)
type_ = JitFrame_BaselineStub;
else if (type_ == JitFrame_Unwound_IonAccessorIC)
type_ = JitFrame_IonAccessorIC;
returnAddressToFp_ = current()->returnAddress();
current_ = prev;
@ -889,43 +895,51 @@ HandleException(ResumeFromException *rfe)
void
EnsureExitFrame(CommonFrameLayout *frame)
{
if (frame->prevType() == JitFrame_Unwound_IonJS ||
frame->prevType() == JitFrame_Unwound_BaselineJS ||
frame->prevType() == JitFrame_Unwound_BaselineStub ||
frame->prevType() == JitFrame_Unwound_Rectifier)
{
switch (frame->prevType()) {
case JitFrame_Unwound_IonJS:
case JitFrame_Unwound_BaselineJS:
case JitFrame_Unwound_BaselineStub:
case JitFrame_Unwound_Rectifier:
case JitFrame_Unwound_IonAccessorIC:
// Already an exit frame, nothing to do.
return;
}
if (frame->prevType() == JitFrame_Entry) {
case JitFrame_Entry:
// The previous frame type is the entry frame, so there's no actual
// need for an exit frame.
return;
}
if (frame->prevType() == JitFrame_Rectifier) {
case JitFrame_Rectifier:
// The rectifier code uses the frame descriptor to discard its stack,
// so modifying its descriptor size here would be dangerous. Instead,
// we change the frame type, and teach the stack walking code how to
// deal with this edge case. bug 717297 would obviate the need
frame->changePrevType(JitFrame_Unwound_Rectifier);
return;
}
if (frame->prevType() == JitFrame_BaselineStub) {
case JitFrame_BaselineStub:
frame->changePrevType(JitFrame_Unwound_BaselineStub);
return;
}
if (frame->prevType() == JitFrame_BaselineJS) {
case JitFrame_BaselineJS:
frame->changePrevType(JitFrame_Unwound_BaselineJS);
return;
case JitFrame_IonJS:
frame->changePrevType(JitFrame_Unwound_IonJS);
return;
case JitFrame_IonAccessorIC:
frame->changePrevType(JitFrame_Unwound_IonAccessorIC);
return;
case JitFrame_Exit:
case JitFrame_Bailout:
// Fall-through to MOZ_CRASH below.
break;
}
MOZ_ASSERT(frame->prevType() == JitFrame_IonJS);
frame->changePrevType(JitFrame_Unwound_IonJS);
MOZ_CRASH("Unexpected frame type");
}
CalleeToken
@ -1169,6 +1183,14 @@ MarkBaselineStubFrame(JSTracer *trc, const JitFrameIterator &frame)
}
}
static void
MarkIonAccessorICFrame(JSTracer *trc, const JitFrameIterator &frame)
{
MOZ_ASSERT(frame.type() == JitFrame_IonAccessorIC);
IonAccessorICFrameLayout *layout = (IonAccessorICFrameLayout *)frame.fp();
gc::MarkJitCodeRoot(trc, layout->stubCode(), "ion-ic-accessor-code");
}
void
JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end)
{
@ -1441,12 +1463,17 @@ MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations)
break;
case JitFrame_Unwound_IonJS:
case JitFrame_Unwound_BaselineJS:
case JitFrame_Unwound_BaselineStub:
case JitFrame_Unwound_IonAccessorIC:
MOZ_CRASH("invalid");
case JitFrame_Rectifier:
MarkRectifierFrame(trc, frames);
break;
case JitFrame_Unwound_Rectifier:
break;
case JitFrame_IonAccessorIC:
MarkIonAccessorICFrame(trc, frames);
break;
default:
MOZ_CRASH("unexpected frame type");
}
@ -2660,6 +2687,11 @@ JitFrameIterator::dump() const
fprintf(stderr, " Rectifier frame\n");
fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
break;
case JitFrame_IonAccessorIC:
case JitFrame_Unwound_IonAccessorIC:
fprintf(stderr, " Ion scripted accessor IC\n");
fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
break;
case JitFrame_Unwound_IonJS:
case JitFrame_Unwound_BaselineJS:
fprintf(stderr, "Warning! Unwound JS frames are not observable.\n");
@ -3012,6 +3044,18 @@ JitProfilingFrameIterator::operator++()
MOZ_CRASH("Bad frame type prior to rectifier frame.");
}
if (prevType == JitFrame_IonAccessorIC || prevType == JitFrame_Unwound_IonAccessorIC) {
IonAccessorICFrameLayout *accessorFrame =
GetPreviousRawFrame<JitFrameLayout, IonAccessorICFrameLayout *>(frame);
MOZ_ASSERT(accessorFrame->prevType() == JitFrame_IonJS);
returnAddressToFp_ = accessorFrame->returnAddress();
fp_ = GetPreviousRawFrame<IonAccessorICFrameLayout, uint8_t *>(accessorFrame);
type_ = JitFrame_IonJS;
return;
}
if (prevType == JitFrame_Entry) {
// No previous frame, set to null to indicate that JitFrameIterator is done()
returnAddressToFp_ = nullptr;

View File

@ -428,6 +428,21 @@ class RectifierFrameLayout : public JitFrameLayout
}
};
class IonAccessorICFrameLayout : public CommonFrameLayout
{
protected:
// Pointer to root the stub's JitCode.
JitCode *stubCode_;
public:
JitCode **stubCode() {
return &stubCode_;
}
static size_t Size() {
return sizeof(IonAccessorICFrameLayout);
}
};
// The callee token is now dead.
class IonUnwoundRectifierFrameLayout : public RectifierFrameLayout
{

View File

@ -1130,6 +1130,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
Label handle_IonJS;
Label handle_BaselineStub;
Label handle_Rectifier;
Label handle_IonAccessorIC;
Label handle_Entry;
Label end;
@ -1137,6 +1138,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonAccessorIC), &handle_IonAccessorIC);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
@ -1307,6 +1309,54 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
masm.ret();
}
// JitFrame_IonAccessorIC
//
// The caller is always an IonJS frame.
//
// Ion-Descriptor
// Ion-ReturnAddr
// ... ion frame data ... |- AccFrame-Descriptor.Size
// StubCode |
// AccFrame-Descriptor |- IonAccessorICFrameLayout::Size()
// AccFrame-ReturnAddr |
// ... accessor frame data & args ... |- Descriptor.Size
// ActualArgc |
// CalleeToken |- JitFrameLayout::Size()
// Descriptor |
// FP -----> ReturnAddr |
masm.bind(&handle_IonAccessorIC);
{
// scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size()
masm.ma_add(StackPointer, scratch1, scratch2);
masm.addPtr(Imm32(JitFrameLayout::Size()), scratch2);
// scratch3 := AccFrame-Descriptor.Size
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfDescriptor()), scratch3);
#ifdef DEBUG
// Assert previous frame is an IonJS frame.
masm.movePtr(scratch3, scratch1);
masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1);
{
Label checkOk;
masm.branch32(Assembler::Equal, scratch1, Imm32(JitFrame_IonJS), &checkOk);
masm.assumeUnreachable("IonAccessorIC frame must be preceded by IonJS frame");
masm.bind(&checkOk);
}
#endif
masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch3);
// lastProfilingCallSite := AccFrame-ReturnAddr
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfReturnAddress()), scratch1);
masm.storePtr(scratch1, lastProfilingCallSite);
// lastProfilingFrame := AccessorFrame + AccFrame-Descriptor.Size +
// IonAccessorICFrameLayout::Size()
masm.ma_add(scratch2, scratch3, scratch1);
masm.addPtr(Imm32(IonAccessorICFrameLayout::Size()), scratch1);
masm.storePtr(scratch1, lastProfilingFrame);
masm.ret();
}
//
// JitFrame_Entry
//

View File

@ -1125,6 +1125,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
Label handle_IonJS;
Label handle_BaselineStub;
Label handle_Rectifier;
Label handle_IonAccessorIC;
Label handle_Entry;
Label end;
@ -1132,6 +1133,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonAccessorIC), &handle_IonAccessorIC);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
@ -1302,6 +1304,54 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
masm.ret();
}
// JitFrame_IonAccessorIC
//
// The caller is always an IonJS frame.
//
// Ion-Descriptor
// Ion-ReturnAddr
// ... ion frame data ... |- AccFrame-Descriptor.Size
// StubCode |
// AccFrame-Descriptor |- IonAccessorICFrameLayout::Size()
// AccFrame-ReturnAddr |
// ... accessor frame data & args ... |- Descriptor.Size
// ActualArgc |
// CalleeToken |- JitFrameLayout::Size()
// Descriptor |
// FP -----> ReturnAddr |
masm.bind(&handle_IonAccessorIC);
{
// scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size()
masm.ma_addu(StackPointer, scratch1, scratch2);
masm.addPtr(Imm32(JitFrameLayout::Size()), scratch2);
// scratch3 := AccFrame-Descriptor.Size
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfDescriptor()), scratch3);
#ifdef DEBUG
// Assert previous frame is an IonJS frame.
masm.movePtr(scratch3, scratch1);
masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1);
{
Label checkOk;
masm.branch32(Assembler::Equal, scratch1, Imm32(JitFrame_IonJS), &checkOk);
masm.assumeUnreachable("IonAccessorIC frame must be preceded by IonJS frame");
masm.bind(&checkOk);
}
#endif
masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch3);
// lastProfilingCallSite := AccFrame-ReturnAddr
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfReturnAddress()), scratch1);
masm.storePtr(scratch1, lastProfilingCallSite);
// lastProfilingFrame := AccessorFrame + AccFrame-Descriptor.Size +
// IonAccessorICFrameLayout::Size()
masm.ma_addu(scratch2, scratch3, scratch1);
masm.addPtr(Imm32(IonAccessorICFrameLayout::Size()), scratch1);
masm.storePtr(scratch1, lastProfilingFrame);
masm.ret();
}
//
// JitFrame_Entry
//

View File

@ -981,6 +981,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
Label handle_IonJS;
Label handle_BaselineStub;
Label handle_Rectifier;
Label handle_IonAccessorIC;
Label handle_Entry;
Label end;
@ -988,6 +989,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonAccessorIC), &handle_IonAccessorIC);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
@ -1153,6 +1155,52 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
masm.ret();
}
// JitFrame_IonAccessorIC
//
// The caller is always an IonJS frame.
//
// Ion-Descriptor
// Ion-ReturnAddr
// ... ion frame data ... |- AccFrame-Descriptor.Size
// StubCode |
// AccFrame-Descriptor |- IonAccessorICFrameLayout::Size()
// AccFrame-ReturnAddr |
// ... accessor frame data & args ... |- Descriptor.Size
// ActualArgc |
// CalleeToken |- JitFrameLayout::Size()
// Descriptor |
// FP -----> ReturnAddr |
masm.bind(&handle_IonAccessorIC);
{
// scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size()
masm.lea(Operand(StackPointer, scratch1, TimesOne, JitFrameLayout::Size()), scratch2);
// scratch3 := AccFrame-Descriptor.Size
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfDescriptor()), scratch3);
#ifdef DEBUG
// Assert previous frame is an IonJS frame.
masm.movePtr(scratch3, scratch1);
masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1);
{
Label checkOk;
masm.branch32(Assembler::Equal, scratch1, Imm32(JitFrame_IonJS), &checkOk);
masm.assumeUnreachable("IonAccessorIC frame must be preceded by IonJS frame");
masm.bind(&checkOk);
}
#endif
masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch3);
// lastProfilingCallSite := AccFrame-ReturnAddr
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfReturnAddress()), scratch1);
masm.storePtr(scratch1, lastProfilingCallSite);
// lastProfilingFrame := AccessorFrame + AccFrame-Descriptor.Size +
// IonAccessorICFrameLayout::Size()
masm.lea(Operand(scratch2, scratch3, TimesOne, IonAccessorICFrameLayout::Size()), scratch1);
masm.storePtr(scratch1, lastProfilingFrame);
masm.ret();
}
//
// JitFrame_Entry
//

View File

@ -1008,6 +1008,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
Label handle_IonJS;
Label handle_BaselineStub;
Label handle_Rectifier;
Label handle_IonAccessorIC;
Label handle_Entry;
Label end;
@ -1015,6 +1016,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonAccessorIC), &handle_IonAccessorIC);
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
@ -1182,6 +1184,52 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
masm.ret();
}
// JitFrame_IonAccessorIC
//
// The caller is always an IonJS frame.
//
// Ion-Descriptor
// Ion-ReturnAddr
// ... ion frame data ... |- AccFrame-Descriptor.Size
// StubCode |
// AccFrame-Descriptor |- IonAccessorICFrameLayout::Size()
// AccFrame-ReturnAddr |
// ... accessor frame data & args ... |- Descriptor.Size
// ActualArgc |
// CalleeToken |- JitFrameLayout::Size()
// Descriptor |
// FP -----> ReturnAddr |
masm.bind(&handle_IonAccessorIC);
{
// scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size()
masm.lea(Operand(StackPointer, scratch1, TimesOne, JitFrameLayout::Size()), scratch2);
// scratch3 := AccFrame-Descriptor.Size
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfDescriptor()), scratch3);
#ifdef DEBUG
// Assert previous frame is an IonJS frame.
masm.movePtr(scratch3, scratch1);
masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1);
{
Label checkOk;
masm.branch32(Assembler::Equal, scratch1, Imm32(JitFrame_IonJS), &checkOk);
masm.assumeUnreachable("IonAccessorIC frame must be preceded by IonJS frame");
masm.bind(&checkOk);
}
#endif
masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch3);
// lastProfilingCallSite := AccFrame-ReturnAddr
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfReturnAddress()), scratch1);
masm.storePtr(scratch1, lastProfilingCallSite);
// lastProfilingFrame := AccessorFrame + AccFrame-Descriptor.Size +
// IonAccessorICFrameLayout::Size()
masm.lea(Operand(scratch2, scratch3, TimesOne, IonAccessorICFrameLayout::Size()), scratch1);
masm.storePtr(scratch1, lastProfilingFrame);
masm.ret();
}
//
// JitFrame_Entry
//

View File

@ -0,0 +1,21 @@
if (typeof window === "undefined") {
// This test is meant to run in the browser, but it's easy to
// run it in the shell as well, even though it has no inner/outer
// windows.
window = this;
}
var res = false;
Object.defineProperty(this, "foo", {configurable: true,
get: function() { return this === window; },
set: function(v) { res = this === window; }});
(function() {
for (var i = 0; i < 3000; ++i) {
window.foo = i;
assertEq(res, true, "setter");
assertEq(window.foo, true, "getter");
}
})();
reportCompare(true, true);