Bug 1129382 - Add Ion ICs for scripted getters/setters. r=efaust,nbp,djvj
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 11 Feb 2015 14:42:01 +0100
changeset 228671 d96d552ff899418b2a6324ee1ca662df18d586e9
parent 228670 f5128665af10d3d13ccb5edc4ad061f968b98ca9
child 228672 8dfe4f1d46f3773ebbcb600a2b8b17e7905449e1
push idunknown
push userunknown
push dateunknown
reviewersefaust, nbp, djvj
bugs1129382
milestone38.0a1
Bug 1129382 - Add Ion ICs for scripted getters/setters. r=efaust,nbp,djvj
js/src/jit-test/tests/ion/scripted-getter-setter.js
js/src/jit-test/tests/profiler/getter-setter-ic.js
js/src/jit/BaselineBailouts.cpp
js/src/jit/Ion.cpp
js/src/jit/IonCaches.cpp
js/src/jit/JitFrameIterator.h
js/src/jit/JitFrames.cpp
js/src/jit/JitFrames.h
js/src/jit/arm/Trampoline-arm.cpp
js/src/jit/mips/Trampoline-mips.cpp
js/src/jit/x64/Trampoline-x64.cpp
js/src/jit/x86/Trampoline-x86.cpp
js/src/tests/ecma_5/misc/getter-setter-outerize-this.js
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/scripted-getter-setter.js
@@ -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();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/profiler/getter-setter-ic.js
@@ -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();
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -324,20 +324,21 @@ struct BaselineStackBuilder
     // cases, this value can be bogus since we can ensure that the caller would have saved
     // it anyway.
     //
     void *calculatePrevFramePtr() {
         // Get the incoming frame.
         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.
         //  PrevFramePtr needs to point to the BaselineStubFrame's saved frame pointer.
         //      STACK_START_ADDR + JitFrameLayout::Size() + PREV_FRAME_SIZE
         //                      - BaselineStubFrameLayout::reverseOffsetOfSavedFramePtr()
         if (type == JitFrame_BaselineStub) {
             size_t offset = JitFrameLayout::Size() + topFrame->prevFrameLocalSize() +
@@ -1343,17 +1344,18 @@ jit::BailoutIonToBaseline(JSContext *cx,
     //      BaselineStub - Baseline calling into Ion.
     //      Entry - Interpreter or other calling into Ion.
     //      Rectifier - Arguments rectifier calling into Ion.
     MOZ_ASSERT(iter.isBailoutJS());
     FrameType prevFrameType = iter.prevType();
     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:
     //
     //      +---------------+
     //      |     ...       |
     //      +---------------+
     //      |     Args      |
     //      |     ...       |
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2502,20 +2502,24 @@ InvalidateActivation(FreeOp *fop, const 
             JitSpew(JitSpew_IonInvalidate, "#%d baseline stub frame @ %p", frameno, it.fp());
             break;
           case JitFrame_Rectifier:
             JitSpew(JitSpew_IonInvalidate, "#%d rectifier frame @ %p", frameno, it.fp());
             break;
           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;
         }
 #endif
 
         if (!it.isIonScripted())
             continue;
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -648,16 +648,36 @@ IsCacheableGetPropCallNative(JSObject *o
 
     // For getters that need an outerized this object, don't cache if
     // obj has an outerObject hook, since our cache will pass obj
     // itself without outerizing.
     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)
 {
     if (!shape || !IsCacheableProtoChainForIon(obj, holder))
         return false;
 
     if (shape->hasSlot() || shape->hasGetterValue() || shape->hasDefaultGetter())
         return false;
 
@@ -915,30 +935,25 @@ EmitGetterCall(JSContext *cx, MacroAssem
     // 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 argUintNReg     = regSet.takeGeneral();
-    Register argVpReg        = regSet.takeGeneral();
-
-    // Shape has a getter function.
-    bool callNative = IsCacheableGetPropCallNative(obj, holder, shape);
-    MOZ_ASSERT_IF(!callNative, IsCacheableGetPropCallPropertyOp(obj, holder, shape));
-
-    if (callNative) {
-        MOZ_ASSERT(shape->hasGetterValue() && shape->getterValue().isObject() &&
-                   shape->getterValue().toObject().is<JSFunction>());
+    Register scratchReg = regSet.takeGeneral();
+
+    // 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();
+
         JSFunction *target = &shape->getterValue().toObject().as<JSFunction>();
-
         MOZ_ASSERT(target);
         MOZ_ASSERT(target->isNative());
 
         // Native functions have the signature:
         //  bool (*)(JSContext *, unsigned, Value *vp)
         // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
         // are the function arguments.
 
@@ -972,17 +987,20 @@ EmitGetterCall(JSContext *cx, MacroAssem
         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
         // Load the outparam vp[0] into output register(s).
         Address outparam(StackPointer, IonOOLNativeExitFrameLayout::offsetOfResult());
         masm.loadTypedOrValue(outparam, output);
 
         // 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();
 
         PropertyOp target = shape->getterOp();
         MOZ_ASSERT(target);
 
         // Push stubCode for marking.
         attacher.pushStubCodePointer(masm);
@@ -1018,16 +1036,61 @@ EmitGetterCall(JSContext *cx, MacroAssem
         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
         // Load the outparam vp[0] into output register(s).
         Address outparam(StackPointer, IonOOLPropertyOpExitFrameLayout::offsetOfResult());
         masm.loadTypedOrValue(outparam, output);
 
         // 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);
     return true;
 }
 
 static bool
 GenerateCallGetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
@@ -1235,22 +1298,23 @@ CanAttachNativeGetProp(typename GetPropC
     }
 
     // IonBuilder guarantees that it's impossible to generate a GetPropertyIC with
     // allowGetters() true and cache.output().hasValue() false. If this isn't true,
     // we will quickly assert during stub generation.
     //
     // 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()
         return GetPropertyIC::CanAttachCallGetter;
     }
 
     return GetPropertyIC::CanAttachNone;
 }
@@ -1991,16 +2055,29 @@ IsCacheableSetPropCallNative(HandleObjec
         return false;
 
     return shape->hasSetterValue() && shape->setterObject() &&
            shape->setterObject()->is<JSFunction>() &&
            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)
 {
     MOZ_ASSERT(obj->isNative());
 
     if (!shape)
         return false;
 
     if (!IsCacheableProtoChainForIon(obj, holder))
@@ -2233,24 +2310,22 @@ GenerateCallSetter(JSContext *cx, IonScr
 
     // 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.
     //
     // 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();
-
-    bool callNative = IsCacheableSetPropCallNative(obj, holder, shape);
-    MOZ_ASSERT_IF(!callNative, IsCacheableSetPropCallPropertyOp(obj, holder, shape));
-
-    if (callNative) {
+    Register scratchReg = regSet.takeGeneral();
+
+    if (IsCacheableSetPropCallNative(obj, holder, shape)) {
+        Register argJSContextReg = regSet.takeGeneral();
+        Register argVpReg        = regSet.takeGeneral();
+
         MOZ_ASSERT(shape->hasSetterValue() && shape->setterObject() &&
                    shape->setterObject()->is<JSFunction>());
         JSFunction *target = &shape->setterObject()->as<JSFunction>();
 
         MOZ_ASSERT(target->isNative());
 
         Register argUintNReg = regSet.takeGeneral();
 
@@ -2285,17 +2360,19 @@ GenerateCallSetter(JSContext *cx, IonScr
         masm.passABIArg(argVpReg);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->native()));
 
         // Test for failure.
         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
         // 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();
 
         attacher.pushStubCodePointer(masm);
 
         StrictPropertyOp target = shape->setterOp();
         MOZ_ASSERT(target);
@@ -2333,16 +2410,62 @@ GenerateCallSetter(JSContext *cx, IonScr
         masm.passABIArg(argVpReg);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target));
 
         // Test for failure.
         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
         // 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);
     return true;
 }
 
 static bool
 IsCacheableDOMProxyUnshadowedSetterCall(JSContext *cx, HandleObject obj, HandlePropertyName name,
@@ -2359,17 +2482,18 @@ IsCacheableDOMProxyUnshadowedSetterCall(
 
     if (!LookupProperty(cx, obj, name, holder, shape))
         return false;
 
     if (!holder)
         return true;
 
     if (!IsCacheableSetPropCallNative(checkObj, holder, shape) &&
-        !IsCacheableSetPropCallPropertyOp(checkObj, holder, shape))
+        !IsCacheableSetPropCallPropertyOp(checkObj, holder, shape) &&
+        !IsCacheableSetPropCallScripted(checkObj, holder, shape))
     {
         return true;
     }
 
     *isSetter = true;
     return true;
 }
 
@@ -2724,17 +2848,18 @@ CanAttachNativeSetProp(JSContext *cx, Ha
     // If the object doesn't have the property, we don't know if we can attach
     // a stub to add the property until we do the VM call to add. If the
     // property exists as a data property on the prototype, we should add
     // a new, shadowing property.
     if (!shape || (obj != holder && shape->hasDefaultSetter() && shape->hasSlot()))
         return SetPropertyIC::MaybeCanAttachAddSlot;
 
     if (IsCacheableSetPropCallPropertyOp(obj, holder, shape) ||
-        IsCacheableSetPropCallNative(obj, holder, shape))
+        IsCacheableSetPropCallNative(obj, holder, shape) ||
+        IsCacheableSetPropCallScripted(obj, holder, shape))
     {
         return SetPropertyIC::CanAttachCallSetter;
     }
 
     return SetPropertyIC::CanAttachNone;
 }
 
 static void
@@ -4090,17 +4215,18 @@ IsCacheableNameCallGetter(HandleObject s
         return false;
     if (!obj->is<GlobalObject>())
         return false;
 
     if (!IsCacheableScopeChain(scopeChain, obj))
         return false;
 
     return IsCacheableGetPropCallNative(obj, holder, shape) ||
-        IsCacheableGetPropCallPropertyOp(obj, holder, shape);
+        IsCacheableGetPropCallPropertyOp(obj, holder, shape) ||
+        IsCacheableGetPropCallScripted(obj, holder, shape);
 }
 
 bool
 NameIC::update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain,
                MutableHandleValue vp)
 {
     void *returnAddr;
     RootedScript outerScript(cx, GetTopJitJSScript(cx, &returnAddr));
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -39,28 +39,27 @@ enum FrameType
     // The entry frame is the initial prologue block transitioning from the VM
     // into the Ion world.
     JitFrame_Entry,
 
     // A rectifier frame sits in between two JS frames, adapting argc != nargs
     // 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
     // JitActivation.
     JitFrame_Exit,
 
     // A bailout frame is a special IonJS jit frame after a bailout, and before
     // the reconstruction of the BaselineJS frame. From within C++, a bailout
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -244,26 +244,30 @@ SizeOfFramePrefix(FrameType type)
         return EntryFrameLayout::Size();
       case JitFrame_BaselineJS:
       case JitFrame_IonJS:
       case JitFrame_Bailout:
       case JitFrame_Unwound_BaselineJS:
       case JitFrame_Unwound_IonJS:
         return JitFrameLayout::Size();
       case JitFrame_BaselineStub:
+      case JitFrame_Unwound_BaselineStub:
         return BaselineStubFrameLayout::Size();
       case JitFrame_Rectifier:
         return RectifierFrameLayout::Size();
       case JitFrame_Unwound_Rectifier:
         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 *
 JitFrameIterator::prevFp() const
 {
     size_t currentSize = SizeOfFramePrefix(type_);
     // This quick fix must be removed as soon as bug 717297 land.  This is
     // needed because the descriptor size of JS-to-JS frame which is just after
@@ -297,16 +301,18 @@ JitFrameIterator::operator++()
     uint8_t *prev = prevFp();
     type_ = current()->prevType();
     if (type_ == JitFrame_Unwound_IonJS)
         type_ = JitFrame_IonJS;
     else if (type_ == JitFrame_Unwound_BaselineJS)
         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;
 
     return *this;
 }
 
 uintptr_t *
 JitFrameIterator::spillBase() const
@@ -884,53 +890,61 @@ HandleException(ResumeFromException *rfe
     }
 
     rfe->stackPointer = iter.fp();
 }
 
 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
 MarkCalleeToken(JSTracer *trc, CalleeToken token)
 {
     switch (CalleeTokenTag tag = GetCalleeTokenTag(token)) {
       case CalleeToken_Function:
       case CalleeToken_FunctionConstructing:
@@ -1164,16 +1178,24 @@ MarkBaselineStubFrame(JSTracer *trc, con
     BaselineStubFrameLayout *layout = (BaselineStubFrameLayout *)frame.fp();
 
     if (ICStub *stub = layout->maybeStubPtr()) {
         MOZ_ASSERT(ICStub::CanMakeCalls(stub->kind()));
         stub->trace(trc);
     }
 }
 
+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)
 {
     JitFrameIterator frames(*this);
 
     if (frames.isFakeExitFrame()) {
         min = reinterpret_cast<uintptr_t *>(frames.fp());
     } else {
@@ -1436,22 +1458,27 @@ MarkJitActivation(JSTracer *trc, const J
           case JitFrame_IonJS:
             MarkIonJSFrame(trc, frames);
             break;
           case JitFrame_Bailout:
             MarkBailoutFrame(trc, frames);
             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");
         }
     }
 }
 
 void
 MarkJitActivations(JSRuntime *rt, JSTracer *trc)
@@ -2655,16 +2682,21 @@ JitFrameIterator::dump() const
         }
         break;
       }
       case JitFrame_Rectifier:
       case JitFrame_Unwound_Rectifier:
         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");
         break;
       case JitFrame_Exit:
         break;
     };
     fputc('\n', stderr);
@@ -3007,16 +3039,28 @@ JitProfilingFrameIterator::operator++()
                     + jit::BaselineFrame::FramePointerOffset;
             type_ = JitFrame_BaselineJS;
             return;
         }
 
         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;
         fp_ = nullptr;
         type_ = JitFrame_Entry;
         return;
     }
 
--- a/js/src/jit/JitFrames.h
+++ b/js/src/jit/JitFrames.h
@@ -423,16 +423,31 @@ class EntryFrameLayout : public JitFrame
 class RectifierFrameLayout : public JitFrameLayout
 {
   public:
     static inline size_t Size() {
         return sizeof(RectifierFrameLayout);
     }
 };
 
+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
 {
   public:
     static inline size_t Size() {
         // It is not necessary to accout for an extra callee token here because
         // sizeof(ExitFrameLayout) == sizeof(RectifierFrameLayout) due to
         // extra padding.
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -1125,23 +1125,25 @@ JitRuntime::generateProfilerExitFrameTai
     //      FrameDescriptor.type in scratch2
     masm.ma_and(Imm32((1 << FRAMESIZE_SHIFT) - 1), scratch1, scratch2);
     masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1);
 
     // Handling of each case is dependent on FrameDescriptor.type
     Label handle_IonJS;
     Label handle_BaselineStub;
     Label handle_Rectifier;
+    Label handle_IonAccessorIC;
     Label handle_Entry;
     Label end;
 
     masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonJS), &handle_IonJS);
     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.");
 
     //
     // JitFrame_IonJS
     //
     // Stack layout:
@@ -1302,16 +1304,64 @@ JitRuntime::generateProfilerExitFrameTai
         Address stubFrameSavedFramePtr(scratch3,
                                        RectifierFrameLayout::Size() - (2 * sizeof(void *)));
         masm.loadPtr(stubFrameSavedFramePtr, scratch2);
         masm.addPtr(Imm32(sizeof(void *)), scratch2);
         masm.storePtr(scratch2, lastProfilingFrame);
         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
     //
     // If at an entry frame, store null into both fields.
     //
     masm.bind(&handle_Entry);
     {
         masm.movePtr(ImmPtr(nullptr), scratch1);
--- a/js/src/jit/mips/Trampoline-mips.cpp
+++ b/js/src/jit/mips/Trampoline-mips.cpp
@@ -1120,23 +1120,25 @@ JitRuntime::generateProfilerExitFrameTai
     //      FrameDescriptor.type in scratch2
     masm.ma_and(scratch2, scratch1, Imm32((1 << FRAMESIZE_SHIFT) - 1));
     masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1);
 
     // Handling of each case is dependent on FrameDescriptor.type
     Label handle_IonJS;
     Label handle_BaselineStub;
     Label handle_Rectifier;
+    Label handle_IonAccessorIC;
     Label handle_Entry;
     Label end;
 
     masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonJS), &handle_IonJS);
     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.");
 
     //
     // JitFrame_IonJS
     //
     // Stack layout:
@@ -1297,16 +1299,64 @@ JitRuntime::generateProfilerExitFrameTai
         Address stubFrameSavedFramePtr(scratch3,
                                        RectifierFrameLayout::Size() - (2 * sizeof(void *)));
         masm.loadPtr(stubFrameSavedFramePtr, scratch2);
         masm.addPtr(Imm32(sizeof(void *)), scratch2);
         masm.storePtr(scratch2, lastProfilingFrame);
         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
     //
     // If at an entry frame, store null into both fields.
     //
     masm.bind(&handle_Entry);
     {
         masm.movePtr(ImmPtr(nullptr), scratch1);
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -976,23 +976,25 @@ JitRuntime::generateProfilerExitFrameTai
     masm.movePtr(scratch1, scratch2);
     masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1);
     masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch2);
 
     // Handling of each case is dependent on FrameDescriptor.type
     Label handle_IonJS;
     Label handle_BaselineStub;
     Label handle_Rectifier;
+    Label handle_IonAccessorIC;
     Label handle_Entry;
     Label end;
 
     masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonJS), &handle_IonJS);
     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.");
 
     //
     // JitFrame_IonJS
     //
     // Stack layout:
@@ -1148,16 +1150,62 @@ JitRuntime::generateProfilerExitFrameTai
         BaseIndex stubFrameSavedFramePtr(scratch2, scratch1, TimesOne,
                                          RectifierFrameLayout::Size() - (2 * sizeof(void *)));
         masm.loadPtr(stubFrameSavedFramePtr, scratch3);
         masm.addPtr(Imm32(sizeof(void *)), scratch3);
         masm.storePtr(scratch3, lastProfilingFrame);
         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
     //
     // If at an entry frame, store null into both fields.
     //
     masm.bind(&handle_Entry);
     {
         masm.movePtr(ImmPtr(nullptr), scratch1);
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -1003,23 +1003,25 @@ JitRuntime::generateProfilerExitFrameTai
     masm.movePtr(scratch1, scratch2);
     masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1);
     masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch2);
 
     // Handling of each case is dependent on FrameDescriptor.type
     Label handle_IonJS;
     Label handle_BaselineStub;
     Label handle_Rectifier;
+    Label handle_IonAccessorIC;
     Label handle_Entry;
     Label end;
 
     masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonJS), &handle_IonJS);
     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.");
 
     //
     // JitFrame_IonJS
     //
     // Stack layout:
@@ -1177,16 +1179,62 @@ JitRuntime::generateProfilerExitFrameTai
         BaseIndex stubFrameSavedFramePtr(scratch2, scratch1, TimesOne,
                                          RectifierFrameLayout::Size() - (2 * sizeof(void *)));
         masm.loadPtr(stubFrameSavedFramePtr, scratch3);
         masm.addPtr(Imm32(sizeof(void *)), scratch3);
         masm.storePtr(scratch3, lastProfilingFrame);
         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
     //
     // If at an entry frame, store null into both fields.
     //
     masm.bind(&handle_Entry);
     {
         masm.movePtr(ImmPtr(nullptr), scratch1);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/misc/getter-setter-outerize-this.js
@@ -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);