Merge.
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 25 Mar 2013 10:29:04 +0100
changeset 127441 63c079713c45354b16cfef357446ffca523ee809
parent 127440 492e87516012a79a258cd6b40c6872510406f1ee (current diff)
parent 127439 f035cd0ee56ea287483fb3b96ab266ac54f87353 (diff)
child 127442 def96e89be7ea0c2cbf6115f729086becf398dbe
push id24503
push userjandemooij@gmail.com
push dateWed, 03 Apr 2013 15:43:00 +0000
treeherdermozilla-central@b5cb88ccd907 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge.
js/src/ion/IonMacroAssembler.h
--- a/js/src/ion/BaselineBailouts.cpp
+++ b/js/src/ion/BaselineBailouts.cpp
@@ -438,18 +438,20 @@ struct BaselineStackBuilder
 //         |            |  CalleeToken  |
 //         |            +---------------+
 //         +------------|  Descr(Rect)  |
 //                      +---------------+
 //                      |  ReturnAddr   | <-- return into ArgumentsRectifier after call
 //                      +===============+
 //
 static bool
-InitFromBailout(JSContext *cx, HandleFunction fun, HandleScript script, SnapshotIterator &iter,
-                bool invalidate, BaselineStackBuilder &builder, MutableHandleFunction nextCallee)
+InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
+                HandleFunction fun, HandleScript script, SnapshotIterator &iter,
+                bool invalidate, BaselineStackBuilder &builder,
+                MutableHandleFunction nextCallee, jsbytecode **callPC)
 {
     uint32_t exprStackSlots = iter.slots() - (script->nfixed + CountArgSlots(fun));
 
     builder.resetFramePushed();
 
     // Build first baseline frame:
     // +===============+
     // | PrevFramePtr  |
@@ -494,19 +496,28 @@ InitFromBailout(JSContext *cx, HandleFun
     // Initialize BaselineFrame::frameSize
     uint32_t frameSize = BaselineFrame::Size() + BaselineFrame::FramePointerOffset +
                          (sizeof(Value) * (script->nfixed + exprStackSlots));
     IonSpew(IonSpew_BaselineBailouts, "      FrameSize=%d", (int) frameSize);
     blFrame->setFrameSize(frameSize);
 
     uint32_t flags = 0;
 
+    // If SPS Profiler is enabled, mark the frame as having pushed an SPS entry.
+    // This may be wrong for the last frame of ArgumentCheck bailout, but
+    // that will be fixed later.
+    if (cx->runtime->spsProfiler.enabled()) {
+        IonSpew(IonSpew_BaselineBailouts, "      Setting SPS flag on frame!");
+        flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
+    }
+
     // Initialize BaselineFrame::scopeChain
     JSObject *scopeChain = NULL;
-    if (iter.bailoutKind() == Bailout_ArgumentCheck) {
+    BailoutKind bailoutKind = iter.bailoutKind();
+    if (bailoutKind == Bailout_ArgumentCheck) {
         // Temporary hack -- skip the (unused) scopeChain, because it could be
         // bogus (we can fail before the scope chain slot is set). Strip the
         // hasScopeChain flag and we'll check this later to run prologue().
         IonSpew(IonSpew_BaselineBailouts, "      Bailout_ArgumentCheck! (no valid scopeChain)");
         iter.skip();
     } else {
         Value v = iter.read();
         if (v.isObject()) {
@@ -629,21 +640,23 @@ InitFromBailout(JSContext *cx, HandleFun
                                                       resumeAfter ? GetNextPc(pc) : pc);
     JS_ASSERT_IF(op != JSOP_FUNAPPLY || !iter.moreFrames() || resumeAfter,
                  exprStackSlots == expectedDepth);
 
     IonSpew(IonSpew_BaselineBailouts, "      Resuming %s pc offset %d (op %s) (line %d) of %s:%d",
                 resumeAfter ? "after" : "at", (int) pcOff, js_CodeName[op],
                 PCToLineNumber(script, pc), script->filename(), (int) script->lineno);
     IonSpew(IonSpew_BaselineBailouts, "      Bailout kind: %s",
-            BailoutKindString(iter.bailoutKind()));
+            BailoutKindString(bailoutKind));
 #endif
 
     // If this was the last inline frame, then unpacking is almost done.
     if (!iter.moreFrames()) {
+        // Last frame, so PC for call to next frame is set to NULL.
+        *callPC = NULL;
 
         // If the bailout was a resumeAfter, and the opcode is monitored,
         // then the bailed out state should be in a position to enter
         // into the ICTypeMonitor chain for the op.
         bool enterMonitorChain = false;
         if (resumeAfter && (js_CodeSpec[op].format & JOF_TYPESET)) {
             // Not every monitored op has a monitored fallback stub, e.g.
             // JSOP_GETINTRINSIC will always return the same value so does
@@ -737,26 +750,57 @@ InitFromBailout(JSContext *cx, HandleFun
             uint8_t *opReturnAddr;
             if (scopeChain == NULL) {
                 // Global and eval scripts expect the scope chain in R1, so only
                 // resume into the prologue for function scripts.
                 JS_ASSERT(fun);
                 JS_ASSERT(numUnsynced == 0);
                 opReturnAddr = baselineScript->prologueEntryAddr();
                 IonSpew(IonSpew_BaselineBailouts, "      Resuming into prologue.");
+
+                // If bailing into prologue, HAS_PUSHED_SPS_FRAME should not be set on frame.
+                blFrame->unsetPushedSPSFrame();
+
+                // Additionally, if SPS is enabled, there are two corner cases to handle:
+                //  1. If resuming into the prologue, and innermost frame is an inlined frame,
+                //     and bailout is because of argument check failure, then:
+                //          Top SPS profiler entry would be for caller frame.
+                //          Ion would not have set the PC index field on that frame
+                //              (since this bailout happens before MFunctionBoundary).
+                //          Make sure that's done now.
+                //  2. If resuming into the prologue, and the bailout is NOT because of an
+                //     argument check, then:
+                //          Top SPS profiler entry would be for callee frame.
+                //          Ion would already have pushed an SPS entry for this frame.
+                //          The pc for this entry would be set to NULL.
+                //          Make sure it's set to script->pc.
+                if (cx->runtime->spsProfiler.enabled()) {
+                    if (caller && bailoutKind == Bailout_ArgumentCheck) {
+                        IonSpew(IonSpew_BaselineBailouts, "      Setting PCidx on innermost "
+                                "inlined frame's parent's SPS entry (%s:%d) (pcIdx=%d)!",
+                                caller->filename(), caller->lineno, callerPC - caller->code);
+                        cx->runtime->spsProfiler.updatePC(caller, callerPC);
+                    } else if (bailoutKind != Bailout_ArgumentCheck) {
+                        IonSpew(IonSpew_BaselineBailouts,
+                                "      Popping SPS entry for innermost inlined frame's SPS entry");
+                        cx->runtime->spsProfiler.exit(cx, script, fun);
+                    }
+                }
             } else {
                 opReturnAddr = nativeCodeForPC;
             }
             builder.setResumeAddr(opReturnAddr);
             IonSpew(IonSpew_BaselineBailouts, "      Set resumeAddr=%p", opReturnAddr);
         }
 
         return true;
     }
 
+    *callPC = pc;
+
     // Write out descriptor of BaselineJS frame.
     size_t baselineFrameDescr = MakeFrameDescriptor((uint32_t) builder.framePushed(),
                                                     IonFrame_BaselineJS);
     if (!builder.writeWord(baselineFrameDescr, "Descriptor"))
         return false;
 
     // Calculate and write out return address.
     // The icEntry in question MUST have a ICCall_Fallback as its fallback stub.
@@ -1003,28 +1047,39 @@ ion::BailoutIonToBaseline(JSContext *cx,
         IonSpew(IonSpew_BaselineBailouts, "  Constructing!");
     else
         IonSpew(IonSpew_BaselineBailouts, "  Not constructing!");
 
     IonSpew(IonSpew_BaselineBailouts, "  Restoring frames:");
     int frameNo = 0;
 
     // Reconstruct baseline frames using the builder.
+    RootedScript caller(cx);
+    jsbytecode *callerPC = NULL;
     RootedFunction fun(cx, callee);
     RootedScript scr(cx, iter.script());
     while (true) {
         IonSpew(IonSpew_BaselineBailouts, "    FrameNo %d", frameNo);
+        jsbytecode *callPC = NULL;
         RootedFunction nextCallee(cx, NULL);
-        if (!InitFromBailout(cx, fun, scr, snapIter, invalidate, builder, &nextCallee))
+        if (!InitFromBailout(cx, caller, callerPC, fun, scr, snapIter, invalidate, builder,
+                             &nextCallee, &callPC))
+        {
             return BAILOUT_RETURN_FATAL_ERROR;
+        }
 
-        if (!snapIter.moreFrames())
-             break;
+        if (!snapIter.moreFrames()) {
+            JS_ASSERT(!callPC);
+            break;
+        }
 
         JS_ASSERT(nextCallee);
+        JS_ASSERT(callPC);
+        caller = scr;
+        callerPC = callPC;
         fun = nextCallee;
         scr = fun->nonLazyScript();
         snapIter.nextFrame();
 
         frameNo++;
     }
     IonSpew(IonSpew_BaselineBailouts, "  Done restoring frames");
     BailoutKind bailoutKind = snapIter.bailoutKind();
--- a/js/src/ion/BaselineCompiler.cpp
+++ b/js/src/ion/BaselineCompiler.cpp
@@ -109,19 +109,20 @@ BaselineCompiler::compile()
 
         previousOffset = entry.nativeOffset;
     }
 
     if (pcEntries.oom())
         return Method_Error;
 
     prologueOffset_.fixup(&masm);
-    uint32_t prologueOffset = uint32_t(prologueOffset_.offset());
-
-    BaselineScript *baselineScript = BaselineScript::New(cx, prologueOffset,
+    spsPushToggleOffset_.fixup(&masm);
+
+    BaselineScript *baselineScript = BaselineScript::New(cx, prologueOffset_.offset(),
+                                                         spsPushToggleOffset_.offset(),
                                                          icEntries_.length(),
                                                          pcMappingIndexEntries.length(),
                                                          pcEntries.length());
     if (!baselineScript)
         return Method_Error;
     script->baseline = baselineScript;
 
     IonSpew(IonSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%d",
@@ -153,16 +154,20 @@ BaselineCompiler::compile()
                                            ImmWord(uintptr_t(entryAddr)),
                                            ImmWord(uintptr_t(-1)));
     }
 
     // All barriers are emitted off-by-default, toggle them on if needed.
     if (cx->zone()->needsBarrier())
         baselineScript->toggleBarriers(true);
 
+    // All SPS instrumentation is emitted toggled off.  Toggle them on if needed.
+    if (cx->runtime->spsProfiler.enabled())
+        baselineScript->toggleSPS(true);
+
     return Method_Compiled;
 }
 
 #ifdef DEBUG
 #define SPEW_OPCODE()                                                         \
     JS_BEGIN_MACRO                                                            \
         if (IsJaegerSpewChannelActive(JSpew_JSOps)) {                         \
             Sprinter sprinter(cx);                                            \
@@ -223,24 +228,30 @@ BaselineCompiler::emitPrologue()
         return false;
 
     if (!emitUseCountIncrement())
         return false;
 
     if (!emitArgumentTypeChecks())
         return false;
 
+    if (!emitSPSPush())
+        return false;
+
     return true;
 }
 
 bool
 BaselineCompiler::emitEpilogue()
 {
     masm.bind(return_);
 
+    // Pop SPS frame if necessary
+    emitSPSPop();
+
     masm.mov(BaselineFrameReg, BaselineStackReg);
     masm.pop(BaselineFrameReg);
 
     masm.ret();
     return true;
 }
 
 bool
@@ -461,16 +472,45 @@ BaselineCompiler::emitDebugTrap()
     ICEntry icEntry(pc - script->code, false);
     icEntry.setReturnOffset(masm.currentOffset());
     if (!icEntries_.append(icEntry))
         return false;
 
     return true;
 }
 
+bool
+BaselineCompiler::emitSPSPush()
+{
+    // Enter the IC, guarded by a toggled jump (initially disabled).
+    Label noPush;
+    CodeOffsetLabel toggleOffset = masm.toggledJump(&noPush);
+    JS_ASSERT(frame.numUnsyncedSlots() == 0);
+    ICProfiler_Fallback::Compiler compiler(cx);
+    if (!emitNonOpIC(compiler.getStub(&stubSpace_)))
+        return false;
+    masm.bind(&noPush);
+
+    // Store the start offset in the appropriate location.
+    JS_ASSERT(spsPushToggleOffset_.offset() == 0);
+    spsPushToggleOffset_ = toggleOffset;
+    return true;
+}
+
+void
+BaselineCompiler::emitSPSPop()
+{
+    // If profiler entry was pushed on this frame, pop it.
+    Label noPop;
+    masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
+                      Imm32(BaselineFrame::HAS_PUSHED_SPS_FRAME), &noPop);
+    masm.spsPopFrame(&cx->runtime->spsProfiler, R1.scratchReg());
+    masm.bind(&noPop);
+}
+
 MethodStatus
 BaselineCompiler::emitBody()
 {
     JS_ASSERT(pc == script->code);
 
     bool lastOpUnreachable = false;
     uint32_t emittedOps = 0;
 
--- a/js/src/ion/BaselineCompiler.h
+++ b/js/src/ion/BaselineCompiler.h
@@ -188,22 +188,25 @@ class BaselineCompiler : public Baseline
     bool emitEpilogue();
     bool emitIC(ICStub *stub, bool isForOp);
     bool emitOpIC(ICStub *stub) {
         return emitIC(stub, true);
     }
     bool emitNonOpIC(ICStub *stub) {
         return emitIC(stub, false);
     }
+
     bool emitStackCheck();
     bool emitInterruptCheck();
     bool emitUseCountIncrement();
     bool emitArgumentTypeChecks();
     bool emitDebugPrologue();
     bool emitDebugTrap();
+    bool emitSPSPush();
+    void emitSPSPop();
 
     bool initScopeChain();
 
     void storeValue(const StackValue *source, const Address &dest,
                     const ValueOperand &scratch);
 
 #define EMIT_OP(op) bool emit_##op();
     OPCODE_LIST(EMIT_OP)
--- a/js/src/ion/BaselineFrame.cpp
+++ b/js/src/ion/BaselineFrame.cpp
@@ -129,16 +129,19 @@ BaselineFrame::initForOsr(StackFrame *fp
         argsObj_ = &fp->argsObj();
     }
 
     if (fp->hasHookData()) {
         flags_ |= BaselineFrame::HAS_HOOK_DATA;
         hookData_ = fp->hookData();
     }
 
+    if (fp->hasPushedSPSFrame())
+        flags_ |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
+
     frameSize_ = BaselineFrame::FramePointerOffset +
         BaselineFrame::Size() +
         numStackValues * sizeof(Value);
 
     JS_ASSERT(numValueSlots() == numStackValues);
 
     for (uint32_t i = 0; i < numStackValues; i++)
         *valueSlot(i) = fp->slots()[i];
--- a/js/src/ion/BaselineFrame.h
+++ b/js/src/ion/BaselineFrame.h
@@ -51,17 +51,20 @@ class BaselineFrame
 
         // See StackFrame::PREV_UP_TO_DATE.
         PREV_UP_TO_DATE  = 1 << 5,
 
         // Eval frame, see the "eval frames" comment.
         EVAL             = 1 << 6,
 
         // Frame has hookData_ set.
-        HAS_HOOK_DATA    = 1 << 7
+        HAS_HOOK_DATA    = 1 << 7,
+
+        // Frame has profiler entry pushed.
+        HAS_PUSHED_SPS_FRAME = 1 << 8
     };
 
   protected: // Silence Clang warning about unused private fields.
     // We need to split the Value into 2 fields of 32 bits, otherwise the C++
     // compiler may add some padding between the fields.
     uint32_t loScratchValue_;
     uint32_t hiScratchValue_;
     uint32_t loReturnValue_;        // If HAS_RVAL, the frame's return value.
@@ -291,16 +294,28 @@ class BaselineFrame
         return hasHookData() ? hookData_ : NULL;
     }
 
     void setHookData(void *v) {
         hookData_ = v;
         flags_ |= HAS_HOOK_DATA;
     }
 
+    bool hasPushedSPSFrame() const {
+        return flags_ & HAS_PUSHED_SPS_FRAME;
+    }
+
+    void setPushedSPSFrame() {
+        flags_ |= HAS_PUSHED_SPS_FRAME;
+    }
+
+    void unsetPushedSPSFrame() {
+        flags_ &= ~HAS_PUSHED_SPS_FRAME;
+    }
+
     void trace(JSTracer *trc);
 
     bool isFunctionFrame() const {
         return CalleeTokenIsFunction(calleeToken());
     }
     bool isGlobalFrame() const {
         return !CalleeTokenIsFunction(calleeToken());
     }
--- a/js/src/ion/BaselineIC.cpp
+++ b/js/src/ion/BaselineIC.cpp
@@ -72,16 +72,17 @@ TypeFallbackICSpew(JSContext *cx, ICType
     }
 }
 
 #else
 #define FallbackICSpew(...)
 #define TypeFallbackICSpew(...)
 #endif
 
+
 ICFallbackStub *
 ICEntry::fallbackStub() const
 {
     return firstStub()->getChainFallback();
 }
 
 
 ICStubConstIterator &
@@ -235,16 +236,21 @@ ICStub::trace(JSTracer *trc)
         MarkObject(trc, &updateStub->object(), "baseline-update-singleobject");
         break;
       }
       case ICStub::TypeUpdate_TypeObject: {
         ICTypeUpdate_TypeObject *updateStub = toTypeUpdate_TypeObject();
         MarkTypeObject(trc, &updateStub->type(), "baseline-update-typeobject");
         break;
       }
+      case ICStub::Profiler_PushFunction: {
+        ICProfiler_PushFunction *pushFunStub = toProfiler_PushFunction();
+        MarkScript(trc, &pushFunStub->script(), "baseline-profilerpushfunction-stub-script");
+        break;
+      }
       case ICStub::GetName_Global: {
         ICGetName_Global *globalStub = toGetName_Global();
         MarkShape(trc, &globalStub->shape(), "baseline-global-stub-shape");
         break;
       }
       case ICStub::GetName_Scope0:
         static_cast<ICGetName_Scope<0>*>(this)->traceScopes(trc);
         break;
@@ -541,16 +547,39 @@ ICStubCompiler::enterStubFrame(MacroAsse
 }
 
 void
 ICStubCompiler::leaveStubFrame(MacroAssembler &masm, bool calledIntoIon)
 {
     JS_ASSERT(entersStubFrame_);
     EmitLeaveStubFrame(masm, calledIntoIon);
 }
+ 
+void
+ICStubCompiler::guardProfilingEnabled(MacroAssembler &masm, Register scratch, Label *skip)
+{
+    // This should only be called from the following stubs.
+    JS_ASSERT(kind == ICStub::Call_Scripted || kind == ICStub::Call_AnyScripted ||
+              kind == ICStub::Call_Native   || kind == ICStub::GetProp_CallScripted ||
+              kind == ICStub::SetProp_CallScripted);
+
+    // Guard on bit in frame that indicates if the SPS frame was pushed in the first
+    // place.  This code is expected to be called from within a stub that has already
+    // entered a stub frame.
+    JS_ASSERT(entersStubFrame_);
+    masm.loadPtr(Address(BaselineFrameReg, 0), scratch);
+    masm.branchTest32(Assembler::Zero,
+                      Address(scratch, BaselineFrame::reverseOffsetOfFlags()),
+                      Imm32(BaselineFrame::HAS_PUSHED_SPS_FRAME),
+                      skip);
+
+    // Check if profiling is enabled
+    uint32_t *enabledAddr = cx->runtime->spsProfiler.addressOfEnabled();
+    masm.branch32(Assembler::Equal, AbsoluteAddress(enabledAddr), Imm32(0), skip);
+}
 
 //
 // UseCount_Fallback
 //
 static bool
 IsTopFrameConstructing(JSContext *cx)
 {
     IonFrameIterator iter(cx);
@@ -854,16 +883,110 @@ ICUseCount_Fallback::Compiler::generateS
 
     // No jitcode available, do nothing.
     masm.bind(&noCompiledCode);
     EmitReturnFromIC(masm);
     return true;
 }
 
 //
+// ICProfile_Fallback
+//
+
+static bool
+DoProfilerFallback(JSContext *cx, BaselineFrame *frame, ICProfiler_Fallback *stub)
+{
+    RootedScript script(cx, frame->script());
+    RootedFunction func(cx, frame->maybeFun());
+    ICEntry *icEntry = stub->icEntry();
+
+    FallbackICSpew(cx, stub, "Profiler");
+
+    SPSProfiler *profiler = &cx->runtime->spsProfiler;
+
+    // Manually enter SPS this time.
+    JS_ASSERT(profiler->enabled());
+    if (!cx->runtime->spsProfiler.enter(cx, script, func))
+        return false;
+    frame->setPushedSPSFrame();
+
+    // Unlink any existing PushFunction stub (which may hold stale 'const char *' to
+    // the profile string.
+    JS_ASSERT_IF(icEntry->firstStub() != stub,
+                 icEntry->firstStub()->isProfiler_PushFunction() &&
+                 icEntry->firstStub()->next() == stub);
+    stub->unlinkStubsWithKind(cx, ICStub::Profiler_PushFunction);
+    JS_ASSERT(icEntry->firstStub() == stub);
+
+    // Generate the string to use to identify this stack frame.
+    const char *string = profiler->profileString(cx, script, func);
+    if (string == NULL)
+        return false;
+
+    IonSpew(IonSpew_BaselineIC, "  Generating Profiler_PushFunction stub for %s:%d",
+            script->filename(), script->lineno);
+
+    // Create a new optimized stub.
+    ICProfiler_PushFunction::Compiler compiler(cx, string, script);
+    ICStub *optStub = compiler.getStub(compiler.getStubSpace(script));
+    if (!optStub)
+        return false;
+    stub->addNewStub(optStub);
+
+    return true;
+}
+
+typedef bool (*DoProfilerFallbackFn)(JSContext *, BaselineFrame *frame, ICProfiler_Fallback *);
+static const VMFunction DoProfilerFallbackInfo =
+    FunctionInfo<DoProfilerFallbackFn>(DoProfilerFallback);
+
+bool
+ICProfiler_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
+{
+    EmitRestoreTailCallReg(masm);
+
+    masm.push(BaselineStubReg);         // Push stub.
+    masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); // Push frame.
+
+    return tailCallVM(DoProfilerFallbackInfo, masm);
+}
+
+bool
+ICProfiler_PushFunction::Compiler::generateStubCode(MacroAssembler &masm)
+{
+
+    Register scratch = R0.scratchReg();
+    Register scratch2 = R1.scratchReg();
+
+    // Profiling should be enabled if we ever reach here.
+#ifdef DEBUG
+    Label spsEnabled;
+    uint32_t *enabledAddr = cx->runtime->spsProfiler.addressOfEnabled();
+    masm.branch32(Assembler::NotEqual, AbsoluteAddress(enabledAddr), Imm32(0), &spsEnabled);
+    masm.breakpoint();
+    masm.bind(&spsEnabled);
+#endif
+
+    // Push SPS entry.
+    masm.spsPushFrame(&cx->runtime->spsProfiler,
+                      Address(BaselineStubReg, ICProfiler_PushFunction::offsetOfStr()),
+                      Address(BaselineStubReg, ICProfiler_PushFunction::offsetOfScript()),
+                      scratch,
+                      scratch2);
+
+    // Mark frame as having profiler entry pushed.
+    Address flagsOffset(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags());
+    masm.or32(Imm32(BaselineFrame::HAS_PUSHED_SPS_FRAME), flagsOffset);
+
+    EmitReturnFromIC(masm);
+
+    return true;
+}
+
+//
 // TypeMonitor_Fallback
 //
 
 bool
 ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext *cx, HandleScript script, HandleValue val)
 {
     bool wasDetachedMonitorChain = lastMonitorStubPtrAddr_ == NULL;
     JS_ASSERT_IF(wasDetachedMonitorChain, numOptimizedMonitorStubs_ == 0);
@@ -4472,19 +4595,19 @@ TryAttachLengthStub(JSContext *cx, Handl
         stub->addNewStub(newStub);
         return true;
     }
 
     return true;
 }
 
 static bool
-TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub,
-                           HandlePropertyName name, HandleValue val, HandleValue res,
-                           bool *attached)
+TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
+                           ICGetProp_Fallback *stub, HandlePropertyName name,
+                           HandleValue val, HandleValue res, bool *attached)
 {
     JS_ASSERT(!*attached);
 
     if (!val.isObject())
         return true;
 
     RootedObject obj(cx, &val.toObject());
     if (!obj->isNative())
@@ -4521,17 +4644,18 @@ TryAttachNativeGetPropStub(JSContext *cx
         RootedFunction callee(cx, shape->getterObject()->toFunction());
         JS_ASSERT(obj != holder);
         JS_ASSERT(callee->hasScript());
 
         IonSpew(IonSpew_BaselineIC, "  Generating GetProp(Native %s Getter %s:%d) stub",
                     (obj == holder) ? "direct" : "prototype",
                     callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno);
 
-        ICGetProp_CallScripted::Compiler compiler(cx, monitorStub, obj, holder, callee);
+        ICGetProp_CallScripted::Compiler compiler(cx, monitorStub, obj, holder, callee,
+                                                  pc - script->code);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
@@ -4633,17 +4757,17 @@ DoGetPropFallback(JSContext *cx, Baselin
 
     if (op == JSOP_LENGTH) {
         if (!TryAttachLengthStub(cx, script, stub, val, res, &attached))
             return false;
         if (attached)
             return true;
     }
 
-    if (!TryAttachNativeGetPropStub(cx, script, stub, name, val, res, &attached))
+    if (!TryAttachNativeGetPropStub(cx, script, pc, stub, name, val, res, &attached))
         return false;
     if (attached)
         return true;
 
     if (val.isString()) {
         if (!TryAttachStringGetPropStub(cx, script, stub, name, val, res, &attached))
             return false;
         if (attached)
@@ -4896,16 +5020,38 @@ ICGetProp_CallScripted::Compiler::genera
         IonCode *argumentsRectifier = cx->compartment->ionCompartment()->getArgumentsRectifier();
 
         masm.movePtr(ImmGCPtr(argumentsRectifier), code);
         masm.loadPtr(Address(code, IonCode::offsetOfCode()), code);
         masm.mov(Imm32(0), ArgumentsRectifierReg);
     }
 
     masm.bind(&noUnderflow);
+
+    // If needed, update SPS Profiler frame entry.  At this point, callee and scratch can
+    // be clobbered.
+    {
+        Label skipProfilerUpdate;
+
+        // Need to avoid using ArgumentsRectifierReg and code register.
+        GeneralRegisterSet availRegs = availableGeneralRegs(0);
+        availRegs.take(ArgumentsRectifierReg);
+        availRegs.take(code);
+        Register scratch = availRegs.takeAny();
+        Register pcIdx = availRegs.takeAny();
+
+        // Check if profiling is enabled.
+        guardProfilingEnabled(masm, scratch, &skipProfilerUpdate);
+
+        // Update profiling entry before leaving function.
+        masm.load32(Address(BaselineStubReg, ICGetProp_CallScripted::offsetOfPCOffset()), pcIdx);
+        masm.spsUpdatePCIdx(&cx->runtime->spsProfiler, pcIdx, scratch);
+
+        masm.bind(&skipProfilerUpdate);
+    }
     masm.callIon(code);
 
     leaveStubFrame(masm, true);
 
     // Enter type monitor IC to type-check result.
     EmitEnterTypeMonitorIC(masm);
 
     // Leave stub frame and go to next stub.
@@ -4919,17 +5065,17 @@ ICGetProp_CallScripted::Compiler::genera
 }
 
 //
 // SetProp_Fallback
 //
 
 // Attach an optimized stub for a SETPROP/SETGNAME/SETNAME op.
 static bool
-TryAttachSetPropStub(JSContext *cx, HandleScript script, ICSetProp_Fallback *stub,
+TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetProp_Fallback *stub,
                      HandleObject obj, HandleShape oldShape, uint32_t oldSlots,
                      HandlePropertyName name, HandleId id, HandleValue rhs, bool *attached)
 {
     JS_ASSERT(!*attached);
 
     if (!obj->isNative() || obj->watched())
         return true;
 
@@ -4983,17 +5129,17 @@ TryAttachSetPropStub(JSContext *cx, Hand
         RootedFunction callee(cx, shape->setterObject()->toFunction());
         JS_ASSERT(obj != holder);
         JS_ASSERT(callee->hasScript());
 
         IonSpew(IonSpew_BaselineIC, "  Generating SetProp(Native %s Setter %s:%d) stub",
                     (obj == holder) ? "direct" : "prototype",
                     callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno);
 
-        ICSetProp_CallScripted::Compiler compiler(cx, obj, holder, callee);
+        ICSetProp_CallScripted::Compiler compiler(cx, obj, holder, callee, pc - script->code);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
@@ -5040,18 +5186,21 @@ DoSetPropFallback(JSContext *cx, Baselin
     res.set(rhs);
 
     if (stub->numOptimizedStubs() >= ICSetProp_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with generic setprop stub.
         return true;
     }
 
     bool attached = false;
-    if (!TryAttachSetPropStub(cx, script, stub, obj, oldShape, oldSlots, name, id, rhs, &attached))
+    if (!TryAttachSetPropStub(cx, script, pc, stub, obj, oldShape, oldSlots, name, id, rhs,
+         &attached))
+    {
         return false;
+    }
     if (attached)
         return true;
 
     JS_ASSERT(!attached);
     stub->noteUnoptimizableAccess();
 
     return true;
 }
@@ -5290,16 +5439,38 @@ ICSetProp_CallScripted::Compiler::genera
         IonCode *argumentsRectifier = cx->compartment->ionCompartment()->getArgumentsRectifier();
 
         masm.movePtr(ImmGCPtr(argumentsRectifier), code);
         masm.loadPtr(Address(code, IonCode::offsetOfCode()), code);
         masm.mov(Imm32(1), ArgumentsRectifierReg);
     }
 
     masm.bind(&noUnderflow);
+
+    // If needed, update SPS Profiler frame entry.  At this point, callee and scratch can
+    // be clobbered.
+    {
+        Label skipProfilerUpdate;
+
+        // Need to avoid using ArgumentsRectifierReg and code register.
+        GeneralRegisterSet availRegs = availableGeneralRegs(0);
+        availRegs.take(ArgumentsRectifierReg);
+        availRegs.take(code);
+        Register scratch = availRegs.takeAny();
+        Register pcIdx = availRegs.takeAny();
+
+        // Check if profiling is enabled.
+        guardProfilingEnabled(masm, scratch, &skipProfilerUpdate);
+
+        // Update profiling entry before leaving function.
+        masm.load32(Address(BaselineStubReg, ICSetProp_CallScripted::offsetOfPCOffset()), pcIdx);
+        masm.spsUpdatePCIdx(&cx->runtime->spsProfiler, pcIdx, scratch);
+
+        masm.bind(&skipProfilerUpdate);
+    }
     masm.callIon(code);
 
     leaveStubFrame(masm, true);
     // Do not care about return value from function. The original RHS should be returned
     // as the result of this operation.
     EmitUnstowICValues(masm, 2);
     masm.moveValue(R1, R0);
     EmitReturnFromIC(masm);
@@ -5318,18 +5489,18 @@ ICSetProp_CallScripted::Compiler::genera
     return true;
 }
 
 //
 // Call_Fallback
 //
 
 static bool
-TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, JSOp op,
-                  uint32_t argc, Value *vp, bool constructing, bool useNewType)
+TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsbytecode *pc,
+                  JSOp op, uint32_t argc, Value *vp, bool constructing, bool useNewType)
 {
     if (useNewType || op == JSOP_EVAL)
         return true;
 
     if (stub->numOptimizedStubs() >= ICCall_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         return true;
@@ -5361,17 +5532,17 @@ TryAttachCallStub(JSContext *cx, ICCall_
         }
 
         if (stub->scriptedStubCount() >= ICCall_Fallback::MAX_SCRIPTED_STUBS) {
             // Create a Call_AnyScripted stub.
             IonSpew(IonSpew_BaselineIC, "  Generating Call_AnyScripted stub (cons=%s)", 
                     constructing ? "yes" : "no");
 
             ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
-                                            constructing);
+                                            constructing, pc - script->code);
             ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
             if (!newStub)
                 return false;
 
             // Before adding new stub, unlink all previous Call_Scripted.
             stub->unlinkStubsWithKind(cx, ICStub::Call_Scripted);
 
             // Add new generalized stub.
@@ -5379,17 +5550,17 @@ TryAttachCallStub(JSContext *cx, ICCall_
             return true;
         }
 
         IonSpew(IonSpew_BaselineIC,
                 "  Generating Call_Scripted stub (fun=%p, %s:%d, cons=%s)",
                 fun.get(), fun->nonLazyScript()->filename(), fun->nonLazyScript()->lineno,
                 constructing ? "yes" : "no");
         ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
-                                        calleeScript, constructing);
+                                        calleeScript, constructing, pc - script->code);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         return true;
     }
 
@@ -5401,17 +5572,17 @@ TryAttachCallStub(JSContext *cx, ICCall_
         if (stub->nativeStubCount() >= ICCall_Fallback::MAX_NATIVE_STUBS) {
             IonSpew(IonSpew_BaselineIC, "  Too many Call_Native stubs. TODO: add Call_AnyNative!");
             return true;
         }
 
         IonSpew(IonSpew_BaselineIC, "  Generating Call_Native stub (fun=%p, cons=%s)", fun.get(),
                 constructing ? "yes" : "no");
         ICCall_Native::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
-                                         fun, constructing);
+                                         fun, constructing, pc - script->code);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         return true;
     }
 
@@ -5467,19 +5638,23 @@ DoCallFallback(JSContext *cx, BaselineFr
 
     // Compute construcing and useNewType flags.
     bool constructing = (op == JSOP_NEW);
     bool newType = false;
     if (cx->typeInferenceEnabled())
         newType = types::UseNewType(cx, script, pc);
 
     // Try attaching a call stub.
-    if (!TryAttachCallStub(cx, stub, script, op, argc, vp, constructing, newType))
+    if (!TryAttachCallStub(cx, stub, script, pc, op, argc, vp, constructing, newType))
         return false;
 
+    // Maybe update PC in profiler entry before leaving this script by call.
+    if (cx->runtime->spsProfiler.enabled() && frame->hasPushedSPSFrame())
+        cx->runtime->spsProfiler.updatePC(script, pc);
+
     if (!MaybeCloneFunctionAtCallsite(cx, &callee, script, pc))
         return false;
 
     if (op == JSOP_NEW) {
         if (!InvokeConstructor(cx, callee, argc, args, res.address()))
             return false;
     } else if (op == JSOP_EVAL && IsBuiltinEvalForScope(frame->scopeChain(), callee)) {
         if (!DirectEval(cx, CallArgsFromVp(argc, vp)))
@@ -5787,16 +5962,42 @@ ICCallScriptedCompiler::generateStubCode
         IonCode *argumentsRectifier = cx->compartment->ionCompartment()->getArgumentsRectifier();
 
         masm.movePtr(ImmGCPtr(argumentsRectifier), code);
         masm.loadPtr(Address(code, IonCode::offsetOfCode()), code);
         masm.mov(argcReg, ArgumentsRectifierReg);
     }
 
     masm.bind(&noUnderflow);
+
+    // If needed, update SPS Profiler frame entry before and after call.
+    {
+        Label skipProfilerUpdate;
+
+        // Need to avoid using ArgumentsRectifierReg and code register.
+        GeneralRegisterSet availRegs = availableGeneralRegs(0);
+        availRegs.take(ArgumentsRectifierReg);
+        availRegs.take(code);
+        Register scratch = availRegs.takeAny();
+        Register pcIdx = availRegs.takeAny();
+
+        // Check if profiling is enabled.
+        guardProfilingEnabled(masm, scratch, &skipProfilerUpdate);
+
+        // Update profiling entry before leaving function.
+        JS_ASSERT(kind == ICStub::Call_Scripted || kind == ICStub::Call_AnyScripted);
+        if (kind == ICStub::Call_Scripted)
+            masm.load32(Address(BaselineStubReg, ICCall_Scripted::offsetOfPCOffset()), pcIdx);
+        else
+            masm.load32(Address(BaselineStubReg, ICCall_AnyScripted::offsetOfPCOffset()), pcIdx);
+        masm.spsUpdatePCIdx(&cx->runtime->spsProfiler, pcIdx, scratch);
+
+        masm.bind(&skipProfilerUpdate);
+    }
+    // Do call
     masm.callIon(code);
 
     // If this is a constructing call, and the callee returns a non-object, replace it with
     // the |this| object passed in.
     if (isConstructing_) {
         Label skipThisReplace;
         masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
 
@@ -5882,16 +6083,28 @@ ICCall_Native::Compiler::generateStubCod
     masm.push(argcReg);
 
     Register scratch = regs.takeAny();
     EmitCreateStubFrameDescriptor(masm, scratch);
     masm.push(scratch);
     masm.push(BaselineTailCallReg);
     masm.enterFakeExitFrame();
 
+    // If needed, update SPS Profiler frame entry.  At this point, BaselineTailCallReg
+    // and scratch can be clobbered.
+    {
+        Label skipProfilerUpdate;
+        Register pcIdx = BaselineTailCallReg;
+        guardProfilingEnabled(masm, scratch, &skipProfilerUpdate);
+
+        masm.load32(Address(BaselineStubReg, ICCall_Native::offsetOfPCOffset()), pcIdx);
+        masm.spsUpdatePCIdx(&cx->runtime->spsProfiler, pcIdx, scratch);
+
+        masm.bind(&skipProfilerUpdate);
+    }
     // Execute call.
     masm.setupUnalignedABICall(3, scratch);
     masm.loadJSContext(scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(argcReg);
     masm.passABIArg(vpReg);
     masm.callWithABI(Address(callee, JSFunction::offsetOfNativeOrScript()));
 
--- a/js/src/ion/BaselineIC.h
+++ b/js/src/ion/BaselineIC.h
@@ -266,16 +266,19 @@ class ICEntry
         return &firstStub_;
     }
 };
 
 // List of baseline IC stub kinds.
 #define IC_STUB_KIND_LIST(_)    \
     _(UseCount_Fallback)        \
                                 \
+    _(Profiler_Fallback)        \
+    _(Profiler_PushFunction)    \
+                                \
     _(TypeMonitor_Fallback)     \
     _(TypeMonitor_SingleObject) \
     _(TypeMonitor_TypeObject)   \
     _(TypeMonitor_PrimitiveSet) \
                                 \
     _(TypeUpdate_Fallback)      \
     _(TypeUpdate_SingleObject)  \
     _(TypeUpdate_TypeObject)    \
@@ -986,16 +989,21 @@ class ICStubCompiler
     bool callTypeUpdateIC(MacroAssembler &masm, uint32_t objectOffset);
 
     // A stub frame is used when a stub wants to call into the VM without
     // performing a tail call. This is required for the return address
     // to pc mapping to work.
     void enterStubFrame(MacroAssembler &masm, Register scratch);
     void leaveStubFrame(MacroAssembler &masm, bool calledIntoIon = false);
 
+    // Some stubs need to emit SPS profiler updates.  This emits the guarding
+    // jitcode for those stubs.  If profiling is not enabled, jumps to the
+    // given label.
+    void guardProfilingEnabled(MacroAssembler &masm, Register scratch, Label *skip);
+
     inline GeneralRegisterSet availableGeneralRegs(size_t numInputs) const {
         GeneralRegisterSet regs(GeneralRegisterSet::All());
         JS_ASSERT(!regs.has(BaselineStackReg));
 #ifdef JS_CPU_ARM
         JS_ASSERT(!regs.has(BaselineTailCallReg));
 #endif
         regs.take(BaselineFrameReg);
         regs.take(BaselineStubReg);
@@ -1077,16 +1085,105 @@ class ICUseCount_Fallback : public ICFal
         { }
 
         ICUseCount_Fallback *getStub(ICStubSpace *space) {
             return ICUseCount_Fallback::New(space, getStubCode());
         }
     };
 };
 
+// Profiler_Fallback
+
+class ICProfiler_Fallback : public ICFallbackStub
+{
+    friend class ICStubSpace;
+
+    ICProfiler_Fallback(IonCode *stubCode)
+      : ICFallbackStub(ICStub::Profiler_Fallback, stubCode)
+    { }
+
+  public:
+    static inline ICProfiler_Fallback *New(ICStubSpace *space, IonCode *code) {
+        if (!code)
+            return NULL;
+        return space->allocate<ICProfiler_Fallback>(code);
+    }
+
+    // Compiler for this stub kind.
+    class Compiler : public ICStubCompiler {
+      protected:
+        bool generateStubCode(MacroAssembler &masm);
+
+      public:
+        Compiler(JSContext *cx)
+          : ICStubCompiler(cx, ICStub::Profiler_Fallback)
+        { }
+
+        ICProfiler_Fallback *getStub(ICStubSpace *space) {
+            return ICProfiler_Fallback::New(space, getStubCode());
+        }
+    };
+};
+
+// Profiler_PushFunction
+
+class ICProfiler_PushFunction : public ICStub
+{
+    friend class ICStubSpace;
+
+    const char *str_;
+    HeapPtrScript script_;
+
+    ICProfiler_PushFunction(IonCode *stubCode, const char *str, HandleScript script)
+      : ICStub(ICStub::Profiler_PushFunction, stubCode),
+        str_(str),
+        script_(script)
+    { }
+
+  public:
+    static inline ICProfiler_PushFunction *New(ICStubSpace *space, IonCode *code,
+                                               const char *str, HandleScript script)
+    {
+        if (!code)
+            return NULL;
+        return space->allocate<ICProfiler_PushFunction>(code, str, script);
+    }
+
+    HeapPtrScript &script() {
+        return script_;
+    }
+
+    static size_t offsetOfStr() {
+        return offsetof(ICProfiler_PushFunction, str_);
+    }
+    static size_t offsetOfScript() {
+        return offsetof(ICProfiler_PushFunction, script_);
+    }
+
+    // Compiler for this stub kind.
+    class Compiler : public ICStubCompiler {
+      protected:
+        const char *str_;
+        RootedScript script_;
+        bool generateStubCode(MacroAssembler &masm);
+
+      public:
+        Compiler(JSContext *cx, const char *str, HandleScript script)
+          : ICStubCompiler(cx, ICStub::Profiler_PushFunction),
+            str_(str),
+            script_(cx, script)
+        { }
+
+        ICProfiler_PushFunction *getStub(ICStubSpace *space) {
+            return ICProfiler_PushFunction::New(space, getStubCode(), str_, script_);
+        }
+    };
+};
+
+
 // TypeCheckPrimitiveSetStub
 //   Base class for IC stubs (TypeUpdate or TypeMonitor) that check that a given
 //   value's type falls within a set of primitive types.
 
 class TypeCheckPrimitiveSetStub : public ICStub
 {
     friend class ICStubSpace;
   protected:
@@ -3888,87 +3985,97 @@ class ICGetProp_CallScripted : public IC
 
     // Holder and shape.
     HeapPtrObject holder_;
     HeapPtrShape holderShape_;
 
     // Function to call.
     HeapPtrFunction getter_;
 
+    // PC offset of call
+    uint32_t pcOffset_;
+
     ICGetProp_CallScripted(IonCode *stubCode, ICStub *firstMonitorStub,
                            HandleShape shape, HandleObject holder, HandleShape holderShape,
-                           HandleFunction getter)
+                           HandleFunction getter, uint32_t pcOffset)
       : ICMonitoredStub(GetProp_CallScripted, stubCode, firstMonitorStub),
         shape_(shape),
         holder_(holder),
         holderShape_(holderShape),
-        getter_(getter)
+        getter_(getter),
+        pcOffset_(pcOffset)
     {}
 
   public:
     static inline ICGetProp_CallScripted *New(
                 ICStubSpace *space, IonCode *code, ICStub *firstMonitorStub,
                 HandleShape shape, HandleObject holder, HandleShape holderShape,
-                HandleFunction getter)
+                HandleFunction getter, uint32_t pcOffset)
     {
         if (!code)
             return NULL;
         return space->allocate<ICGetProp_CallScripted>(code, firstMonitorStub,
-                                                           shape, holder, holderShape, getter);
+                                                       shape, holder, holderShape, getter,
+                                                       pcOffset);
     }
 
     HeapPtrShape &shape() {
         return shape_;
     }
     HeapPtrObject &holder() {
         return holder_;
     }
     HeapPtrShape &holderShape() {
         return holderShape_;
     }
     HeapPtrFunction &getter() {
         return getter_;
     }
+
     static size_t offsetOfShape() {
         return offsetof(ICGetProp_CallScripted, shape_);
     }
     static size_t offsetOfHolder() {
         return offsetof(ICGetProp_CallScripted, holder_);
     }
     static size_t offsetOfHolderShape() {
         return offsetof(ICGetProp_CallScripted, holderShape_);
     }
     static size_t offsetOfGetter() {
         return offsetof(ICGetProp_CallScripted, getter_);
     }
-
+    static size_t offsetOfPCOffset() {
+        return offsetof(ICGetProp_CallScripted, pcOffset_);
+    }
 
     class Compiler : public ICStubCompiler {
         ICStub *firstMonitorStub_;
         RootedObject obj_;
         RootedObject holder_;
         RootedFunction getter_;
+        uint32_t pcOffset_;
 
         bool generateStubCode(MacroAssembler &masm);
 
       public:
         Compiler(JSContext *cx, ICStub *firstMonitorStub, HandleObject obj,
-                 HandleObject holder, HandleFunction getter)
+                 HandleObject holder, HandleFunction getter, uint32_t pcOffset)
           : ICStubCompiler(cx, ICStub::GetProp_CallScripted),
             firstMonitorStub_(firstMonitorStub),
             obj_(cx, obj),
             holder_(cx, holder),
-            getter_(cx, getter)
+            getter_(cx, getter),
+            pcOffset_(pcOffset)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             RootedShape shape(cx, obj_->lastProperty());
             RootedShape holderShape(cx, holder_->lastProperty());
-            return ICGetProp_CallScripted::New(space, getStubCode(), firstMonitorStub_,
-                                               shape, holder_, holderShape, getter_); 
+            return ICGetProp_CallScripted::New(space, getStubCode(), firstMonitorStub_, shape,
+                                               holder_, holderShape, getter_, pcOffset_); 
         }
     };
 };
 
 // SetProp
 //     JSOP_SETPROP
 //     JSOP_SETNAME
 //     JSOP_SETGNAME
@@ -4255,81 +4362,93 @@ class ICSetProp_CallScripted : public IC
 
     // Holder and shape.
     HeapPtrObject holder_;
     HeapPtrShape holderShape_;
 
     // Function to call.
     HeapPtrFunction setter_;
 
+    // PC of call, for profiler
+    uint32_t pcOffset_;
+
     ICSetProp_CallScripted(IonCode *stubCode, HandleShape shape, HandleObject holder,
-                           HandleShape holderShape, HandleFunction setter)
+                           HandleShape holderShape, HandleFunction setter, uint32_t pcOffset)
       : ICStub(SetProp_CallScripted, stubCode),
         shape_(shape),
         holder_(holder),
         holderShape_(holderShape),
-        setter_(setter)
+        setter_(setter),
+        pcOffset_(pcOffset)
     {}
 
   public:
     static inline ICSetProp_CallScripted *New(ICStubSpace *space, IonCode *code,
                                               HandleShape shape, HandleObject holder,
-                                              HandleShape holderShape, HandleFunction setter)
+                                              HandleShape holderShape, HandleFunction setter,
+                                              uint32_t pcOffset)
     {
         if (!code)
             return NULL;
-        return space->allocate<ICSetProp_CallScripted>(code, shape, holder, holderShape, setter);
+        return space->allocate<ICSetProp_CallScripted>(code, shape, holder, holderShape, setter,
+                                                       pcOffset);
     }
 
     HeapPtrShape &shape() {
         return shape_;
     }
     HeapPtrObject &holder() {
         return holder_;
     }
     HeapPtrShape &holderShape() {
         return holderShape_;
     }
     HeapPtrFunction &setter() {
         return setter_;
     }
+
     static size_t offsetOfShape() {
         return offsetof(ICSetProp_CallScripted, shape_);
     }
     static size_t offsetOfHolder() {
         return offsetof(ICSetProp_CallScripted, holder_);
     }
     static size_t offsetOfHolderShape() {
         return offsetof(ICSetProp_CallScripted, holderShape_);
     }
     static size_t offsetOfSetter() {
         return offsetof(ICSetProp_CallScripted, setter_);
     }
-
+    static size_t offsetOfPCOffset() {
+        return offsetof(ICSetProp_CallScripted, pcOffset_);
+    }
 
     class Compiler : public ICStubCompiler {
         RootedObject obj_;
         RootedObject holder_;
         RootedFunction setter_;
+        uint32_t pcOffset_;
 
         bool generateStubCode(MacroAssembler &masm);
 
       public:
-        Compiler(JSContext *cx, HandleObject obj, HandleObject holder, HandleFunction setter)
+        Compiler(JSContext *cx, HandleObject obj, HandleObject holder, HandleFunction setter,
+                 uint32_t pcOffset)
           : ICStubCompiler(cx, ICStub::SetProp_CallScripted),
             obj_(cx, obj),
             holder_(cx, holder),
-            setter_(cx, setter)
+            setter_(cx, setter),
+            pcOffset_(pcOffset)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             RootedShape shape(cx, obj_->lastProperty());
             RootedShape holderShape(cx, holder_->lastProperty());
-            return ICSetProp_CallScripted::New(space, getStubCode(), shape, holder_,
-                                               holderShape, setter_); 
+            return ICSetProp_CallScripted::New(space, getStubCode(), shape, holder_, holderShape,
+                                               setter_, pcOffset_); 
         }
     };
 };
 
 // Call
 //      JSOP_CALL
 //      JSOP_FUNAPPLY
 //      JSOP_FUNCALL
@@ -4415,142 +4534,173 @@ class ICCall_Fallback : public ICMonitor
     };
 };
 
 class ICCall_Scripted : public ICMonitoredStub
 {
     friend class ICStubSpace;
 
     HeapPtrScript calleeScript_;
-
-    ICCall_Scripted(IonCode *stubCode, ICStub *firstMonitorStub, HandleScript calleeScript)
+    uint32_t pcOffset_;
+
+    ICCall_Scripted(IonCode *stubCode, ICStub *firstMonitorStub, HandleScript calleeScript,
+                    uint32_t pcOffset)
       : ICMonitoredStub(ICStub::Call_Scripted, stubCode, firstMonitorStub),
-        calleeScript_(calleeScript)
+        calleeScript_(calleeScript),
+        pcOffset_(pcOffset)
     { }
 
   public:
     static inline ICCall_Scripted *New(
-            ICStubSpace *space, IonCode *code, ICStub *firstMonitorStub, HandleScript calleeScript)
+            ICStubSpace *space, IonCode *code, ICStub *firstMonitorStub, HandleScript calleeScript,
+            uint32_t pcOffset)
     {
         if (!code)
             return NULL;
-        return space->allocate<ICCall_Scripted>(code, firstMonitorStub, calleeScript);
+        return space->allocate<ICCall_Scripted>(code, firstMonitorStub, calleeScript, pcOffset);
+    }
+
+    HeapPtrScript &calleeScript() {
+        return calleeScript_;
     }
 
     static size_t offsetOfCalleeScript() {
         return offsetof(ICCall_Scripted, calleeScript_);
     }
-    HeapPtrScript &calleeScript() {
-        return calleeScript_;
+    static size_t offsetOfPCOffset() {
+        return offsetof(ICCall_Scripted, pcOffset_);
     }
 };
 
 class ICCall_AnyScripted : public ICMonitoredStub
 {
     friend class ICStubSpace;
 
-    ICCall_AnyScripted(IonCode *stubCode, ICStub *firstMonitorStub)
-      : ICMonitoredStub(ICStub::Call_AnyScripted, stubCode, firstMonitorStub)
+    uint32_t pcOffset_;
+
+    ICCall_AnyScripted(IonCode *stubCode, ICStub *firstMonitorStub, uint32_t pcOffset)
+      : ICMonitoredStub(ICStub::Call_AnyScripted, stubCode, firstMonitorStub),
+        pcOffset_(pcOffset)
     { }
 
   public:
     static inline ICCall_AnyScripted *New(ICStubSpace *space, IonCode *code,
-                                          ICStub *firstMonitorStub)
+                                          ICStub *firstMonitorStub, uint32_t pcOffset)
     {
         if (!code)
             return NULL;
-        return space->allocate<ICCall_AnyScripted>(code, firstMonitorStub);
+        return space->allocate<ICCall_AnyScripted>(code, firstMonitorStub, pcOffset);
+    }
+
+    static size_t offsetOfPCOffset() {
+        return offsetof(ICCall_AnyScripted, pcOffset_);
     }
 };
 
 // Compiler for Call_Scripted and Call_AnyScripted stubs.
 class ICCallScriptedCompiler : public ICCallStubCompiler {
   protected:
     ICStub *firstMonitorStub_;
     bool isConstructing_;
     RootedScript calleeScript_;
+    uint32_t pcOffset_;
     bool generateStubCode(MacroAssembler &masm);
 
     virtual int32_t getKey() const {
         return static_cast<int32_t>(kind) | (static_cast<int32_t>(isConstructing_) << 16);
     }
 
   public:
     ICCallScriptedCompiler(JSContext *cx, ICStub *firstMonitorStub, HandleScript calleeScript,
-                            bool isConstructing)
+                            bool isConstructing, uint32_t pcOffset)
       : ICCallStubCompiler(cx, ICStub::Call_Scripted),
         firstMonitorStub_(firstMonitorStub),
         isConstructing_(isConstructing),
-        calleeScript_(cx, calleeScript)
+        calleeScript_(cx, calleeScript),
+        pcOffset_(pcOffset)
     { }
 
-    ICCallScriptedCompiler(JSContext *cx, ICStub *firstMonitorStub, bool isConstructing)
+    ICCallScriptedCompiler(JSContext *cx, ICStub *firstMonitorStub, bool isConstructing,
+                           uint32_t pcOffset)
       : ICCallStubCompiler(cx, ICStub::Call_AnyScripted),
         firstMonitorStub_(firstMonitorStub),
         isConstructing_(isConstructing),
-        calleeScript_(cx, NULL)
+        calleeScript_(cx, NULL),
+        pcOffset_(pcOffset)
     { }
 
     ICStub *getStub(ICStubSpace *space) {
-        if (calleeScript_)
-            return ICCall_Scripted::New(space, getStubCode(), firstMonitorStub_, calleeScript_);
-        return ICCall_AnyScripted::New(space, getStubCode(), firstMonitorStub_);
+        ICStub *stub = NULL;
+        if (calleeScript_) {
+            return ICCall_Scripted::New(space, getStubCode(), firstMonitorStub_, calleeScript_,
+                                        pcOffset_);
+        }
+        return ICCall_AnyScripted::New(space, getStubCode(), firstMonitorStub_, pcOffset_);
     }
 };
 
 class ICCall_Native : public ICMonitoredStub
 {
     friend class ICStubSpace;
 
     HeapPtrFunction callee_;
-
-    ICCall_Native(IonCode *stubCode, ICStub *firstMonitorStub, HandleFunction callee)
+    uint32_t pcOffset_;
+
+    ICCall_Native(IonCode *stubCode, ICStub *firstMonitorStub, HandleFunction callee,
+                  uint32_t pcOffset)
       : ICMonitoredStub(ICStub::Call_Native, stubCode, firstMonitorStub),
-        callee_(callee)
+        callee_(callee),
+        pcOffset_(pcOffset)
     { }
 
   public:
     static inline ICCall_Native *New(ICStubSpace *space, IonCode *code, ICStub *firstMonitorStub,
-                                     HandleFunction callee)
+                                     HandleFunction callee, uint32_t pcOffset)
     {
         if (!code)
             return NULL;
-        return space->allocate<ICCall_Native>(code, firstMonitorStub, callee);
+        return space->allocate<ICCall_Native>(code, firstMonitorStub, callee, pcOffset);
+    }
+
+    HeapPtrFunction &callee() {
+        return callee_;
     }
 
     static size_t offsetOfCallee() {
         return offsetof(ICCall_Native, callee_);
     }
-    HeapPtrFunction &callee() {
-        return callee_;
+    static size_t offsetOfPCOffset() {
+        return offsetof(ICCall_Native, pcOffset_);
     }
 
     // Compiler for this stub kind.
     class Compiler : public ICCallStubCompiler {
       protected:
         ICStub *firstMonitorStub_;
         bool isConstructing_;
         RootedFunction callee_;
+        uint32_t pcOffset_;
         bool generateStubCode(MacroAssembler &masm);
 
         virtual int32_t getKey() const {
             return static_cast<int32_t>(kind) | (static_cast<int32_t>(isConstructing_) << 16);
         }
 
       public:
         Compiler(JSContext *cx, ICStub *firstMonitorStub, HandleFunction callee,
-                 bool isConstructing)
+                 bool isConstructing, uint32_t pcOffset)
           : ICCallStubCompiler(cx, ICStub::Call_Native),
             firstMonitorStub_(firstMonitorStub),
             isConstructing_(isConstructing),
-            callee_(cx, callee)
+            callee_(cx, callee),
+            pcOffset_(pcOffset)
         { }
 
         ICStub *getStub(ICStubSpace *space) {
-            return ICCall_Native::New(space, getStubCode(), firstMonitorStub_, callee_);
+            return ICCall_Native::New(space, getStubCode(), firstMonitorStub_, callee_, pcOffset_);
         }
     };
 };
 
 // Stub for performing a TableSwitch, updating the IC's return address to jump
 // to whatever point the switch is branching to.
 class ICTableSwitch : public ICStub
 {
--- a/js/src/ion/BaselineJIT.cpp
+++ b/js/src/ion/BaselineJIT.cpp
@@ -27,20 +27,24 @@ PCMappingSlotInfo::ToSlotLocation(const 
             return SlotInR0;
         JS_ASSERT(stackVal->reg() == R1);
         return SlotInR1;
     }
     JS_ASSERT(stackVal->kind() != StackValue::Stack);
     return SlotIgnore;
 }
 
-BaselineScript::BaselineScript(uint32_t prologueOffset)
+BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t spsPushToggleOffset)
   : method_(NULL),
     fallbackStubSpace_(),
     prologueOffset_(prologueOffset),
+#ifdef DEBUG
+    spsOn_(false),
+#endif
+    spsPushToggleOffset_(spsPushToggleOffset),
     flags_(0)
 { }
 
 static const size_t BASELINE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4096;
 
 static bool
 CheckFrame(StackFrame *fp)
 {
@@ -279,17 +283,18 @@ ion::CanEnterBaselineJIT(JSContext *cx, 
 
     return BaselineCompile(cx, script);
 }
 
 // Be safe, align IC entry list to 8 in all cases.
 static const unsigned DataAlignment = sizeof(uintptr_t);
 
 BaselineScript *
-BaselineScript::New(JSContext *cx, uint32_t prologueOffset, size_t icEntries,
+BaselineScript::New(JSContext *cx, uint32_t prologueOffset,
+                    uint32_t spsPushToggleOffset, size_t icEntries,
                     size_t pcMappingIndexEntries, size_t pcMappingSize)
 {
     size_t paddedBaselineScriptSize = AlignBytes(sizeof(BaselineScript), DataAlignment);
 
     size_t icEntriesSize = icEntries * sizeof(ICEntry);
     size_t pcMappingIndexEntriesSize = pcMappingIndexEntries * sizeof(PCMappingIndexEntry);
 
     size_t paddedICEntriesSize = AlignBytes(icEntriesSize, DataAlignment);
@@ -301,17 +306,17 @@ BaselineScript::New(JSContext *cx, uint3
         paddedPCMappingIndexEntriesSize +
         paddedPCMappingSize;
 
     uint8_t *buffer = (uint8_t *)cx->malloc_(allocBytes);
     if (!buffer)
         return NULL;
 
     BaselineScript *script = reinterpret_cast<BaselineScript *>(buffer);
-    new (script) BaselineScript(prologueOffset);
+    new (script) BaselineScript(prologueOffset, spsPushToggleOffset);
 
     size_t offsetCursor = paddedBaselineScriptSize;
 
     script->icEntriesOffset_ = offsetCursor;
     script->icEntries_ = icEntries;
     offsetCursor += paddedICEntriesSize;
 
     script->pcMappingIndexOffset_ = offsetCursor;
@@ -609,16 +614,35 @@ BaselineScript::toggleDebugTraps(RawScri
             }
 
             curPC += GetBytecodeLength(curPC);
         }
     }
 }
 
 void
+BaselineScript::toggleSPS(bool enable)
+{
+    JS_ASSERT(enable == !(bool)spsOn_);
+
+    IonSpew(IonSpew_BaselineIC, "  toggling SPS %s for BaselineScript %p",
+            enable ? "on" : "off", this);
+
+    // Toggle the jump
+    CodeLocationLabel pushToggleLocation(method_, CodeOffsetLabel(spsPushToggleOffset_));
+    if (enable)
+        Assembler::ToggleToCmp(pushToggleLocation);
+    else
+        Assembler::ToggleToJmp(pushToggleLocation);
+#ifdef DEBUG
+    spsOn_ = enable;
+#endif
+}
+
+void
 BaselineScript::purgeOptimizedStubs(Zone *zone)
 {
     IonSpew(IonSpew_BaselineIC, "Purging optimized stubs");
 
     for (size_t i = 0; i < numICEntries(); i++) {
         ICEntry &entry = icEntry(i);
         if (!entry.hasStub())
             continue;
@@ -709,16 +733,29 @@ ion::SizeOfBaselineData(JSScript *script
 {
     *data = 0;
     *fallbackStubs = 0;
 
     if (script->hasBaselineScript())
         script->baseline->sizeOfIncludingThis(mallocSizeOf, data, fallbackStubs);
 }
 
+void
+ion::ToggleBaselineSPS(JSRuntime *runtime, bool enable)
+{
+    for (ZonesIter zone(runtime); !zone.done(); zone.next()) {
+        for (gc::CellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+            JSScript *script = i.get<JSScript>();
+            if (!script->hasBaselineScript())
+                continue;
+            script->baselineScript()->toggleSPS(enable);
+        }
+    }
+}
+
 static void
 MarkActiveBaselineScripts(JSContext *cx, const IonActivationIterator &activation)
 {
     for (ion::IonFrameIterator iter(activation); !iter.done(); ++iter) {
         switch (iter.type()) {
           case IonFrame_BaselineJS:
             iter.script()->baselineScript()->setActive();
             break;
--- a/js/src/ion/BaselineJIT.h
+++ b/js/src/ion/BaselineJIT.h
@@ -103,16 +103,22 @@ struct BaselineScript
     HeapPtr<IonCode> method_;
 
     // Allocated space for fallback stubs.
     FallbackICStubSpace fallbackStubSpace_;
 
     // Native code offset right before the scope chain is initialized.
     uint32_t prologueOffset_;
 
+    // The offsets for the toggledJump instructions for SPS update ICs.
+#ifdef DEBUG
+    mozilla::DebugOnly<bool> spsOn_;
+#endif
+    uint32_t spsPushToggleOffset_;
+
   public:
     enum Flag {
         // Flag set by JSScript::argumentsOptimizationFailed. Similar to
         // JSScript::needsArgsObj_, but can be read from JIT code.
         NEEDS_ARGS_OBJ = 1 << 0,
 
         // Flag set when discarding JIT code, to indicate this script is
         // on the stack and should not be discarded.
@@ -131,19 +137,20 @@ struct BaselineScript
     uint32_t pcMappingIndexOffset_;
     uint32_t pcMappingIndexEntries_;
 
     uint32_t pcMappingOffset_;
     uint32_t pcMappingSize_;
 
   public:
     // Do not call directly, use BaselineScript::New. This is public for cx->new_.
-    BaselineScript(uint32_t prologueOffset);
+    BaselineScript(uint32_t prologueOffset, uint32_t spsPushToggleOffset);
 
-    static BaselineScript *New(JSContext *cx, uint32_t prologueOffset, size_t icEntries,
+    static BaselineScript *New(JSContext *cx, uint32_t prologueOffset,
+                               uint32_t spsPushToggleOffset, size_t icEntries,
                                size_t pcMappingIndexEntries, size_t pcMappingSize);
     static void Trace(JSTracer *trc, BaselineScript *script);
     static void Destroy(FreeOp *fop, BaselineScript *script);
 
     void purgeOptimizedStubs(Zone *zone);
 
     static inline size_t offsetOfMethod() {
         return offsetof(BaselineScript, method_);
@@ -230,16 +237,18 @@ struct BaselineScript
     void copyPCMappingEntries(const CompactBufferWriter &entries);
     uint8_t *nativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo = NULL);
 
     // Toggle debug traps (used for breakpoints and step mode) in the script.
     // If |pc| is NULL, toggle traps for all ops in the script. Else, only
     // toggle traps at |pc|.
     void toggleDebugTraps(RawScript script, jsbytecode *pc);
 
+    void toggleSPS(bool enable);
+
     static size_t offsetOfFlags() {
         return offsetof(BaselineScript, flags_);
     }
 };
 
 inline bool IsBaselineEnabled(JSContext *cx)
 {
     return cx->hasOption(JSOPTION_BASELINE);
@@ -256,16 +265,19 @@ EnterBaselineAtBranch(JSContext *cx, Sta
 
 void
 FinishDiscardBaselineScript(FreeOp *fop, RawScript script);
 
 void
 SizeOfBaselineData(JSScript *script, JSMallocSizeOfFun mallocSizeOf, size_t *data,
                    size_t *fallbackStubs);
 
+void
+ToggleBaselineSPS(JSRuntime *runtime, bool enable);
+
 struct BaselineBailoutInfo
 {
     // Pointer into the current C stack, where overwriting will start.
     uint8_t *incomingStack;
 
     // The top and bottom heapspace addresses of the reconstructed stack
     // which will be copied to the bottom.
     uint8_t *copyStackTop;
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -461,16 +461,20 @@ HandleException(ResumeFromException *rfe
         } else if (iter.isBaselineJS()) {
             // It's invalid to call DebugEpilogue twice for the same frame.
             bool calledDebugEpilogue = false;
 
             HandleException(cx, iter, rfe, &calledDebugEpilogue);
             if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME)
                 return;
 
+            // Unwind profiler pseudo-stack
+            RawScript script = iter.script();
+            Probes::exitScript(cx, script, script->function(), NULL);
+
             if (cx->compartment->debugMode() && !calledDebugEpilogue) {
                 // If DebugEpilogue returns |true|, we have to perform a forced
                 // return, e.g. return frame->returnValue() to the caller.
                 BaselineFrame *frame = iter.baselineFrame();
                 if (ion::DebugEpilogue(cx, frame, false)) {
                     JS_ASSERT(frame->hasReturnValue());
                     rfe->kind = ResumeFromException::RESUME_FORCED_RETURN;
                     rfe->framePointer = iter.fp() - BaselineFrame::FramePointerOffset;
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -746,26 +746,56 @@ class MacroAssembler : public MacroAssem
 
     void spsUpdatePCIdx(SPSProfiler *p, int32_t idx, Register temp) {
         Label stackFull;
         spsProfileEntryAddress(p, -1, temp, &stackFull);
         store32(Imm32(idx), Address(temp, ProfileEntry::offsetOfPCIdx()));
         bind(&stackFull);
     }
 
+    void spsUpdatePCIdx(SPSProfiler *p, Register idx, Register temp) {
+        Label stackFull;
+        spsProfileEntryAddress(p, -1, temp, &stackFull);
+        store32(idx, Address(temp, ProfileEntry::offsetOfPCIdx()));
+        bind(&stackFull);
+    }
+
     void spsPushFrame(SPSProfiler *p, const char *str, RawScript s, Register temp) {
         Label stackFull;
         spsProfileEntryAddress(p, 0, temp, &stackFull);
 
         storePtr(ImmWord(str),    Address(temp, ProfileEntry::offsetOfString()));
         storePtr(ImmGCPtr(s),     Address(temp, ProfileEntry::offsetOfScript()));
-        storePtr(ImmWord((void*) NULL),
-                 Address(temp, ProfileEntry::offsetOfStackAddress()));
-        store32(Imm32(ProfileEntry::NullPCIndex),
-                Address(temp, ProfileEntry::offsetOfPCIdx()));
+        storePtr(ImmWord((void*) NULL), Address(temp, ProfileEntry::offsetOfStackAddress()));
+        store32(Imm32(ProfileEntry::NullPCIndex), Address(temp, ProfileEntry::offsetOfPCIdx()));
+
+        /* Always increment the stack size, whether or not we actually pushed. */
+        bind(&stackFull);
+        movePtr(ImmWord(p->sizePointer()), temp);
+        add32(Imm32(1), Address(temp, 0));
+    }
+
+    void spsPushFrame(SPSProfiler *p, const Address &str, const Address &script,
+                      Register temp, Register temp2)
+    {
+        Label stackFull;
+        spsProfileEntryAddress(p, 0, temp, &stackFull);
+
+        loadPtr(str, temp2);
+        storePtr(temp2, Address(temp, ProfileEntry::offsetOfString()));
+
+        loadPtr(script, temp2);
+        storePtr(temp2, Address(temp, ProfileEntry::offsetOfScript()));
+
+        storePtr(ImmWord((void*) 0), Address(temp, ProfileEntry::offsetOfStackAddress()));
+
+        // Store 0 for PCIdx because that's what interpreter does.
+        // (See Probes::enterScript, which calls spsProfiler.enter, which pushes an entry
+        //  with 0 pcIdx).
+        store32(Imm32(0), Address(temp, ProfileEntry::offsetOfPCIdx()));
 
         /* Always increment the stack size, whether or not we actually pushed. */
         bind(&stackFull);
         movePtr(ImmWord(p->sizePointer()), temp);
         add32(Imm32(1), Address(temp, 0));
     }
 
     void spsPopFrame(SPSProfiler *p, Register temp) {
--- a/js/src/ion/shared/BaselineCompiler-shared.cpp
+++ b/js/src/ion/shared/BaselineCompiler-shared.cpp
@@ -19,19 +19,19 @@ BaselineCompilerShared::BaselineCompiler
     ionCompileable_(ion::IsEnabled(cx) && CanIonCompileScript(cx, script)),
     debugMode_(cx->compartment->debugMode()),
     frame(cx, script, masm),
     stubSpace_(),
     icEntries_(),
     pcMappingEntries_(),
     icLoadLabels_(),
     pushedBeforeCall_(0),
-    inCall_(false)
-{
-}
+    inCall_(false),
+    spsPushToggleOffset_()
+{ }
 
 bool
 BaselineCompilerShared::callVM(const VMFunction &fun)
 {
     IonCompartment *ion = cx->compartment->ionCompartment();
     IonCode *code = ion->getVMWrapper(fun);
     if (!code)
         return false;
--- a/js/src/ion/shared/BaselineCompiler-shared.h
+++ b/js/src/ion/shared/BaselineCompiler-shared.h
@@ -7,16 +7,17 @@
 
 #ifndef jsion_baselinecompiler_shared_h__
 #define jsion_baselinecompiler_shared_h__
 
 #include "jscntxt.h"
 #include "ion/BaselineFrameInfo.h"
 #include "ion/IonSpewer.h"
 #include "ion/BaselineIC.h"
+#include "ion/IonInstrumentation.h"
 #include "ion/IonMacroAssembler.h"
 
 namespace js {
 namespace ion {
 
 class BaselineCompilerShared
 {
   protected:
@@ -61,16 +62,18 @@ class BaselineCompilerShared
         size_t icEntry;
         CodeOffsetLabel label;
     };
     js::Vector<ICLoadLabel, 16, SystemAllocPolicy> icLoadLabels_;
 
     uint32_t pushedBeforeCall_;
     mozilla::DebugOnly<bool> inCall_;
 
+    CodeOffsetLabel spsPushToggleOffset_;
+
     BaselineCompilerShared(JSContext *cx, HandleScript script);
 
     ICEntry *allocateICEntry(ICStub *stub, bool isForOp) {
         if (!stub)
             return NULL;
 
         // Create the entry and add it to the vector.
         if (!icEntries_.append(ICEntry((uint32_t) (pc - script->code), isForOp)))
--- a/js/src/vm/SPSProfiler.cpp
+++ b/js/src/vm/SPSProfiler.cpp
@@ -11,16 +11,18 @@
 #include "jsscript.h"
 
 #include "methodjit/MethodJIT.h"
 #include "methodjit/Compiler.h"
 
 #include "vm/SPSProfiler.h"
 #include "vm/StringBuffer.h"
 
+#include "ion/BaselineJIT.h"
+
 using namespace js;
 
 using mozilla::DebugOnly;
 
 SPSProfiler::SPSProfiler(JSRuntime *rt)
   : rt(rt),
     stack_(NULL),
     size_(NULL),
@@ -61,16 +63,25 @@ SPSProfiler::enable(bool enabled)
 {
     JS_ASSERT(installed());
     enabled_ = enabled;
     /*
      * Ensure all future generated code will be instrumented, or that all
      * currently instrumented code is discarded
      */
     ReleaseAllJITCode(rt->defaultFreeOp());
+
+#ifdef JS_ION
+    /* Toggle SPS-related jumps on baseline jitcode.
+     * The call to |ReleaseAllJITCode| above will release most baseline jitcode, but not
+     * jitcode for scripts with active frames on the stack.  These scripts need to have
+     * their profiler state toggled so they behave properly.
+     */
+    ion::ToggleBaselineSPS(rt, enabled);
+#endif
 }
 
 /* Lookup the string for the function/script, creating one if necessary */
 const char*
 SPSProfiler::profileString(JSContext *cx, RawScript script, RawFunction maybeFun)
 {
     JS_ASSERT(strings.initialized());
     ProfileStringMap::AddPtr s = strings.lookupForAdd(script);
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -129,17 +129,17 @@ class SPSProfiler
     friend class SPSEntryMarker;
 
     JSRuntime            *rt;
     ProfileStringMap     strings;
     ProfileEntry         *stack_;
     uint32_t             *size_;
     uint32_t             max_;
     bool                 slowAssertions;
-    bool                 enabled_;
+    uint32_t             enabled_;
 
     const char *allocProfileString(JSContext *cx, RawScript script,
                                    RawFunction function);
     void push(const char *string, void *sp, RawScript script, jsbytecode *pc);
     void pop();
 
   public:
     SPSProfiler(JSRuntime *rt);
@@ -249,16 +249,20 @@ class SPSProfiler
 
     void setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max);
     const char *profileString(JSContext *cx, RawScript script, RawFunction maybeFun);
     void onScriptFinalized(RawScript script);
 
     /* meant to be used for testing, not recommended to call in normal code */
     size_t stringsCount() { return strings.count(); }
     void stringsReset() { strings.clear(); }
+
+    uint32_t *addressOfEnabled() {
+        return &enabled_;
+    }
 };
 
 /*
  * This class is used in RunScript() to push the marker onto the sampling stack
  * that we're about to enter JS function calls. This is the only time in which a
  * valid stack pointer is pushed to the sampling stack.
  */
 class SPSEntryMarker