Bug 781979 - Part 2: Track the line number for profiling in IonMonkey. r=nbp,djvj
authorAlex Crichton <acrichton@mozilla.com>
Wed, 15 Aug 2012 01:16:41 -0700
changeset 113017 01c69de69dc30d1bf47950820f782d073ac7c998
parent 113016 2d190481b4a40ca6f9ce4c3b159e6f30ec0141b1
child 113018 bd706422296fd7d514e4b73e4843c3fc984c7ce1
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp, djvj
bugs781979
milestone17.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
Bug 781979 - Part 2: Track the line number for profiling in IonMonkey. r=nbp,djvj
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/IonMacroAssembler.h
js/src/ion/LIR-Common.h
js/src/ion/LIR.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MIR.h
js/src/ion/MIRGenerator.h
js/src/ion/MIRGraph.cpp
js/src/ion/MIRGraph.h
js/src/ion/MOpcodes.h
js/src/ion/VMFunctions.cpp
js/src/ion/VMFunctions.h
js/src/ion/arm/Architecture-arm.h
js/src/ion/arm/MacroAssembler-arm.h
js/src/ion/shared/CodeGenerator-shared.cpp
js/src/ion/shared/CodeGenerator-shared.h
js/src/ion/shared/Lowering-x86-shared.cpp
js/src/ion/x64/MacroAssembler-x64.h
js/src/ion/x86/MacroAssembler-x86.h
js/src/vm/SPSProfiler.h
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -743,19 +743,21 @@ CodeGenerator::visitCallGeneric(LCallGen
         JS_ASSERT(ArgumentsRectifierReg != objreg);
         masm.movePtr(ImmGCPtr(argumentsRectifier), objreg); // Necessary for GC marking.
         masm.movePtr(Address(objreg, IonCode::OffsetOfCode()), objreg);
         masm.move32(Imm32(call->numStackArgs()), ArgumentsRectifierReg);
     }
 
     // Finally call the function in objreg.
     masm.bind(&makeCall);
+    masm.leaveBeforeCall();
     masm.callIon(objreg);
     if (!markSafepoint(call))
         return false;
+    masm.reenterAfterCall();
 
     // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
     // The return address has already been removed from the Ion frame.
     int prefixGarbage = sizeof(IonJSFrameLayout) - sizeof(void *);
     masm.adjustStack(prefixGarbage - unusedStack);
     masm.jump(&end);
 
     // Handle uncompiled or native functions.
@@ -814,21 +816,23 @@ CodeGenerator::visitCallKnown(LCallKnown
     // Nestle the StackPointer up to the argument vector.
     masm.freeStack(unusedStack);
 
     // Construct the IonFramePrefix.
     uint32 descriptor = MakeFrameDescriptor(masm.framePushed(), IonFrame_JS);
     masm.Push(Imm32(call->numActualArgs()));
     masm.Push(calleereg);
     masm.Push(Imm32(descriptor));
+    masm.leaveBeforeCall();
 
     // Finally call the function in objreg.
     masm.callIon(objreg);
     if (!markSafepoint(call))
         return false;
+    masm.reenterAfterCall();
 
     // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
     // The return address has already been removed from the Ion frame.
     int prefixGarbage = sizeof(IonJSFrameLayout) - sizeof(void *);
     masm.adjustStack(prefixGarbage - unusedStack);
     masm.jump(&end);
 
     // Handle uncompiled functions.
@@ -1070,21 +1074,23 @@ CodeGenerator::visitApplyArgsGeneric(LAp
                 return false;
 
             JS_ASSERT(ArgumentsRectifierReg != objreg);
             masm.movePtr(argcreg, ArgumentsRectifierReg);
             masm.movePtr(ImmWord(argumentsRectifier->raw()), objreg);
         }
 
         masm.bind(&rejoin);
+        masm.leaveBeforeCall();
 
         // Finally call the function in objreg, as assigned by one of the paths above.
         masm.callIon(objreg);
         if (!markSafepoint(apply))
             return false;
+        masm.reenterAfterCall();
 
         // Recover the number of arguments from the frame descriptor.
         masm.movePtr(Address(StackPointer, 0), copyreg);
         masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), copyreg);
         masm.subPtr(Imm32(pushed), copyreg);
 
         // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
         // The return address has already been removed from the Ion frame.
@@ -4036,61 +4042,86 @@ CodeGenerator::visitSetDOMProperty(LSetD
     masm.bind(&success);
     masm.adjustStack(IonDOMExitFrameLayout::Size());
 
     JS_ASSERT(masm.framePushed() == initialStack);
     return true;
 }
 
 bool
-CodeGenerator::visitProfilingEnter(LProfilingEnter *lir)
+CodeGenerator::visitFunctionBoundary(LFunctionBoundary *lir)
 {
-#if 0
-    SPSProfiler *profiler = &gen->compartment->rt->spsProfiler;
-    JS_ASSERT(profiler->enabled());
-
-    const char *string = lir->profileString();
-
-    Register size = ToRegister(lir->temp1()->output());
-    Register base = ToRegister(lir->temp2()->output());
-
-    // Check if there's still space on the stack
-    masm.movePtr(ImmWord(profiler->sizePointer()), size);
-    masm.load32(Address(size, 0), size);
-    Label stackFull;
-    masm.branch32(Assembler::GreaterThanOrEqual, size, Imm32(profiler->maxSize()),
-                  &stackFull);
-
-    // With room, store our string onto the stack
-    masm.movePtr(ImmWord(profiler->stack()), base);
-    JS_STATIC_ASSERT(sizeof(ProfileEntry) == 2 * sizeof(void*));
-    masm.lshiftPtr(Imm32(sizeof(void*) == 4 ? 3 : 4), size);
-    masm.addPtr(size, base);
-
-    masm.storePtr(ImmWord(string), Address(base, offsetof(ProfileEntry, string)));
-    masm.storePtr(ImmWord((uintptr_t) 0), Address(base, offsetof(ProfileEntry, sp)));
-
-    // Always increment the stack size (paired with a decrement in pop)
-    masm.bind(&stackFull);
-    masm.movePtr(ImmWord(profiler->sizePointer()), size);
-    Address addr(size, 0);
-    masm.add32(Imm32(1), addr);
-#endif
-    return true;
-}
-
-bool
-CodeGenerator::visitProfilingExit(LProfilingExit *exit)
-{
-#if 0
-    SPSProfiler *profiler = &gen->compartment->rt->spsProfiler;
-    JS_ASSERT(profiler->enabled());
-    Register temp = ToRegister(exit->temp());
-    masm.movePtr(ImmWord(profiler->sizePointer()), temp);
-    Address addr(temp, 0);
-    masm.add32(Imm32(-1), addr);
-#endif
-    return true;
+    Register temp = ToRegister(lir->temp()->output());
+
+    switch (lir->type()) {
+        case MFunctionBoundary::Inline_Enter:
+            // Multiple scripts can be inlined at one depth, but there is only
+            // one Inline_Exit node to signify this. To deal with this, if we
+            // reach the entry of another inline script on the same level, then
+            // just reset the sps metadata about the frame. We must balance
+            // calls to leave()/reenter(), so perform the balance without
+            // emitting any instrumentation. Technically the previous inline
+            // call at this same depth has reentered, but the instrumentation
+            // will be emitted at the common join point for all inlines at the
+            // same depth.
+            if (sps.inliningDepth() == lir->inlineLevel()) {
+                sps.leaveInlineFrame();
+                sps.skipNextReenter();
+                sps.reenter(masm, temp);
+            }
+
+            sps.leave(lastPC, masm, temp);
+            if (!sps.enterInlineFrame())
+                return false;
+            // fallthrough
+
+        case MFunctionBoundary::Enter:
+            if (sps.slowAssertions()) {
+                typedef bool(*pf)(JSContext *, HandleScript);
+                static const VMFunction SPSEnterInfo = FunctionInfo<pf>(SPSEnter);
+
+                saveLive(lir);
+                pushArg(ImmGCPtr(lir->script()));
+                if (!callVM(SPSEnterInfo, lir))
+                    return false;
+                restoreLive(lir);
+                sps.pushManual(lir->script(), masm, temp);
+                return true;
+            }
+
+            return sps.push(GetIonContext()->cx, lir->script(), masm, temp);
+
+        case MFunctionBoundary::Inline_Exit:
+            // all inline returns were covered with ::Exit, so we just need to
+            // maintain the state of inline frames currently active and then
+            // reenter the caller
+            sps.leaveInlineFrame();
+            sps.reenter(masm, temp);
+            return true;
+
+        case MFunctionBoundary::Exit:
+            if (sps.slowAssertions()) {
+                typedef bool(*pf)(JSContext *, HandleScript);
+                static const VMFunction SPSExitInfo = FunctionInfo<pf>(SPSExit);
+
+                saveLive(lir);
+                pushArg(ImmGCPtr(lir->script()));
+                // Once we've exited, then we shouldn't emit instrumentation for
+                // the corresponding reenter() because we no longer have a
+                // frame.
+                sps.skipNextReenter();
+                if (!callVM(SPSExitInfo, lir))
+                    return false;
+                restoreLive(lir);
+                return true;
+            }
+
+            sps.pop(masm, temp);
+            return true;
+
+        default:
+            JS_NOT_REACHED("invalid LFunctionBoundary type");
+    }
 }
 
 } // namespace ion
 } // namespace js
 
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -158,18 +158,17 @@ class CodeGenerator : public CodeGenerat
     bool visitGetArgument(LGetArgument *lir);
     bool visitCallSetProperty(LCallSetProperty *ins);
     bool visitCallDeleteProperty(LCallDeleteProperty *lir);
     bool visitBitNotV(LBitNotV *lir);
     bool visitBitOpV(LBitOpV *lir);
     bool emitInstanceOf(LInstruction *ins, Register rhs);
     bool visitInstanceOfO(LInstanceOfO *ins);
     bool visitInstanceOfV(LInstanceOfV *ins);
-    bool visitProfilingEnter(LProfilingEnter *lir);
-    bool visitProfilingExit(LProfilingExit *lir);
+    bool visitFunctionBoundary(LFunctionBoundary *lir);
     bool visitGetDOMProperty(LGetDOMProperty *lir);
     bool visitSetDOMProperty(LSetDOMProperty *lir);
     bool visitCallDOMNative(LCallDOMNative *lir);
 
     bool visitCheckOverRecursed(LCheckOverRecursed *lir);
     bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool);
 
     bool visitUnboxDouble(LUnboxDouble *lir);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -284,23 +284,18 @@ IonBuilder::build()
     {
         MInstruction *scope = MConstant::New(UndefinedValue());
         current->add(scope);
         current->initSlot(info().scopeChainSlot(), scope);
     }
 
     // Emit the start instruction, so we can begin real instructions.
     current->makeStart(MStart::New(MStart::StartType_Default));
-    if (instrumentedProfiling()) {
-        SPSProfiler *profiler = &cx->runtime->spsProfiler;
-        const char *string = profiler->profileString(cx, script, script->function());
-        if (!string)
-            return false;
-        current->add(MProfilingEnter::New(string));
-    }
+    if (instrumentedProfiling())
+        current->add(MFunctionBoundary::New(script, MFunctionBoundary::Enter));
 
     // Parameters have been checked to correspond to the typeset, now we unbox
     // what we can in an infallible manner.
     rewriteParameters();
 
     // It's safe to start emitting actual IR, so now build the scope chain.
     if (!initScopeChain())
         return false;
@@ -402,28 +397,29 @@ IonBuilder::buildInline(IonBuilder *call
 
     // Generate single entrance block.
     current = newBlock(pc);
     if (!current)
         return false;
 
     current->setCallerResumePoint(callerResumePoint);
 
-    // Flag the entry into an inlined function with a special MStart block
-    if (instrumentedProfiling()) {
-        SPSProfiler *profiler = &cx->runtime->spsProfiler;
-        const char *string = profiler->profileString(cx, script, script->function());
-        if (!string)
-            return false;
-        current->add(MProfilingEnter::New(string));
-    }
-
     // Connect the entrance block to the last block in the caller's graph.
     MBasicBlock *predecessor = callerBuilder->current;
     JS_ASSERT(predecessor == callerResumePoint->block());
+
+    // All further instructions generated in from this scope should be
+    // considered as part of the function that we're inlining. We also need to
+    // keep track of the inlining depth because all scripts inlined on the same
+    // level contiguously have only one Inline_Exit node.
+    if (instrumentedProfiling())
+        predecessor->add(MFunctionBoundary::New(script,
+                                                MFunctionBoundary::Inline_Enter,
+                                                inliningDepth));
+
     predecessor->end(MGoto::New(current));
     if (!current->addPredecessorWithoutPhis(predecessor))
         return false;
 
     // Explicitly pass Undefined for missing arguments.
     const size_t numActualArgs = argv.length() - 1;
     const size_t nargs = info().nargs();
 
@@ -2556,17 +2552,17 @@ IonBuilder::processReturn(JSOp op)
 
       default:
         def = NULL;
         JS_NOT_REACHED("unknown return op");
         break;
     }
 
     if (instrumentedProfiling())
-        current->add(MProfilingExit::New());
+        current->add(MFunctionBoundary::New(script, MFunctionBoundary::Exit));
     MReturn *ret = MReturn::New(def);
     current->end(ret);
 
     if (!graph().addExit(current))
         return ControlStatus_Error;
 
     // Make sure no one tries to use this block now.
     current = NULL;
@@ -3224,16 +3220,22 @@ IonBuilder::inlineScriptedCall(AutoObjec
         JSFunction *func = targets[0]->toFunction();
         MConstant *constFun = MConstant::New(ObjectValue(*func));
         current->add(constFun);
 
         // Monomorphic case is simple - no guards.
         RootedFunction target(cx, func);
         if (!jsop_call_inline(target, argc, constructing, constFun, bottom, retvalDefns))
             return false;
+
+        // The Inline_Enter node is handled by buildInline, we're responsible
+        // for the Inline_Exit node (mostly for the case below)
+        if (instrumentedProfiling())
+            bottom->add(MFunctionBoundary::New(NULL, MFunctionBoundary::Inline_Exit));
+
     } else {
         // In the polymorphic case, we end the current block with a MPolyInlineDispatch instruction.
 
         // Create a PolyInlineDispatch instruction for this call site
         MPolyInlineDispatch *disp = makePolyInlineDispatch(cx, targets, argc, getPropCache,
                                                            types, barrier, bottom, retvalDefns);
         if (!disp)
             return false;
@@ -3249,25 +3251,77 @@ IonBuilder::inlineScriptedCall(AutoObjec
                 return false;
 
             // Add case to PolyInlineDispatch
             entryBlock->add(constFun);
             disp->addCallee(constFun, entryBlock);
         }
         top->end(disp);
 
+        // If profiling is enabled, then we need a clear-cut boundary of all of
+        // the inlined functions which is distinct from the fallback path where
+        // no inline functions are entered. In the case that there's a fallback
+        // path and a set of inline functions, we create a new block as a join
+        // point for all of the inline paths which will then go to the real end
+        // block: 'bottom'. This 'inlineBottom' block is never different from
+        // 'bottom' except for this one case where profiling is turned on.
+        MBasicBlock *inlineBottom = bottom;
+        if (instrumentedProfiling() && disp->inlinePropertyTable()) {
+            inlineBottom = newBlock(NULL, pc);
+            if (inlineBottom == NULL)
+                return false;
+        }
+
         for (size_t i = 0; i < disp->numCallees(); i++) {
             // Do the inline function build.
             MConstant *constFun = disp->getFunctionConstant(i);
             RootedFunction target(cx, constFun->value().toObject().toFunction());
             MBasicBlock *block = disp->getSuccessor(i);
             graph().moveBlockToEnd(block);
             current = block;
-            
-            if (!jsop_call_inline(target, argc, constructing, constFun, bottom, retvalDefns))
+
+            if (!jsop_call_inline(target, argc, constructing, constFun, inlineBottom, retvalDefns))
+                return false;
+        }
+
+        // Regardless of whether inlineBottom != bottom, demarcate these exits
+        // with an Inline_Exit instruction signifying that the inlined functions
+        // on this level have all ceased running.
+        if (instrumentedProfiling())
+            inlineBottom->add(MFunctionBoundary::New(NULL, MFunctionBoundary::Inline_Exit));
+
+        // In the case where we had to create a new block, all of the returns of
+        // the inline functions need to be merged together with a phi node. This
+        // phi node resident in the 'inlineBottom' block is then an input to the
+        // phi node for this entire call sequence in the 'bottom' block.
+        if (inlineBottom != bottom) {
+            graph().moveBlockToEnd(inlineBottom);
+            inlineBottom->inheritSlots(top);
+            if (!inlineBottom->initEntrySlots())
+                return false;
+
+            // Only need to phi returns together if there's more than one
+            if (retvalDefns.length() > 1) {
+                // This is the same depth as the phi node of the 'bottom' block
+                // after all of the 'pops' happen (see pop() sequence below)
+                MPhi *phi = MPhi::New(inlineBottom->stackDepth() - argc - 2);
+                inlineBottom->addPhi(phi);
+
+                for (MDefinition **it = retvalDefns.begin(), **end = retvalDefns.end(); it != end; ++it) {
+                    if (!phi->addInput(*it))
+                        return false;
+                }
+                // retvalDefns should become a singleton vector of 'phi'
+                retvalDefns.clear();
+                if (!retvalDefns.append(phi))
+                    return false;
+            }
+
+            inlineBottom->end(MGoto::New(bottom));
+            if (!bottom->addPredecessorWithoutPhis(inlineBottom))
                 return false;
         }
 
         // If inline property table is set on the dispatch instruction, then there is
         // a fallback case to consider.  Move the fallback blocks to the end of the graph
         // and link them to the bottom block.
         if (disp->inlinePropertyTable()) {
             graph().moveBlockToEnd(disp->fallbackPrepBlock());
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -178,20 +178,16 @@ class IonBuilder : public MIRGenerator
     JSAtom *readAtom(jsbytecode *pc);
     bool abort(const char *message, ...);
     void spew(const char *message);
 
     static bool inliningEnabled() {
         return js_IonOptions.inlining;
     }
 
-    bool instrumentedProfiling() {
-        return cx->runtime->spsProfiler.enabled();
-    }
-
     JSFunction *getSingleCallTarget(uint32 argc, jsbytecode *pc);
     unsigned getPolyCallTargets(uint32 argc, jsbytecode *pc,
                                 AutoObjectVector &targets, uint32_t maxTargets);
     bool canInlineTarget(JSFunction *target);
 
     void popCfgStack();
     bool processDeferredContinues(CFGState &state);
     ControlStatus processControlEnd();
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -19,16 +19,20 @@
 #include "ion/TypeOracle.h"
 
 #include "jsscope.h"
 #include "jstypedarray.h"
 #include "jscompartment.h"
 
 namespace js {
 namespace ion {
+
+class MacroAssembler;
+
+typedef SPSInstrumentation<MacroAssembler, Register> IonInstrumentation;
  
 // The public entrypoint for emitting assembly. Note that a MacroAssembler can
 // use cx->lifoAlloc, so take care not to interleave masm use with other
 // lifoAlloc use if one will be destroyed before the other.
 class MacroAssembler : public MacroAssemblerSpecific
 {
     MacroAssembler *thisFromCtor() {
         return this;
@@ -50,33 +54,41 @@ class MacroAssembler : public MacroAssem
         }
     };
 
     AutoRooter autoRooter_;
     Maybe<IonContext> ionContext_;
     Maybe<AutoIonContextAlloc> alloc_;
     bool enoughMemory_;
 
+  private:
+    IonInstrumentation *sps;
+    jsbytecode **pc_;
+
   public:
-    MacroAssembler()
+    MacroAssembler(IonInstrumentation *sps = NULL, jsbytecode **pc = NULL)
       : autoRooter_(GetIonContext()->cx, thisFromCtor()),
-        enoughMemory_(true)
+        enoughMemory_(true),
+        sps(sps),
+        pc_(pc)
     {
         if (!GetIonContext()->temp)
             alloc_.construct(GetIonContext()->cx);
 #ifdef JS_CPU_ARM
         m_buffer.id = GetIonContext()->getNextAssemblerId();
 #endif
     }
 
     // This constructor should only be used when there is no IonContext active
     // (for example, Trampoline-$(ARCH).cpp).
     MacroAssembler(JSContext *cx)
       : autoRooter_(cx, thisFromCtor()),
-        enoughMemory_(true)
+        enoughMemory_(true),
+        sps(NULL),
+        pc_(NULL)
     {
         ionContext_.construct(cx, cx->compartment, (js::ion::TempAllocator *)NULL);
         alloc_.construct(cx);
 #ifdef JS_CPU_ARM
         m_buffer.id = GetIonContext()->getNextAssemblerId();
 #endif
     }
 
@@ -460,15 +472,119 @@ class MacroAssembler : public MacroAssem
     // replacement. The stack frame must be at a valid OSR entry-point.
     void performOsr();
 
     // Checks if an OSR frame is the previous frame, and if so, removes it.
     void maybeRemoveOsrFrame(Register scratch);
 
     // Generates code used to complete a bailout.
     void generateBailoutTail(Register scratch);
+
+    // These functions exist as small wrappers around sites where execution can
+    // leave the currently running stream of instructions. They exist so that
+    // instrumentation may be put in place around them if necessary and the
+    // instrumentation is enabled.
+
+    void callWithABI(void *fun, Result result = GENERAL) {
+        leaveBeforeCall();
+        MacroAssemblerSpecific::callWithABI(fun, result);
+        reenterAfterCall();
+    }
+
+    void handleException() {
+        // Re-entry code is irrelevant because the exception will leave the
+        // running function and never come back
+        if (sps)
+            sps->skipNextReenter();
+        leaveBeforeCall();
+        MacroAssemblerSpecific::handleException();
+        // Doesn't actually emit code, but balances the leave()
+        if (sps)
+            sps->reenter(*this, InvalidReg);
+    }
+
+  private:
+    void spsProfileEntryAddress(SPSProfiler *p, int offset, Register temp,
+                                Label *full)
+    {
+        movePtr(ImmWord(p->sizePointer()), temp);
+        load32(Address(temp, 0), temp);
+        if (offset != 0)
+            add32(Imm32(offset), temp);
+        branch32(Assembler::GreaterThanOrEqual, temp, Imm32(p->maxSize()), full);
+
+        // 4 * sizeof(void*) * idx = idx << (2 + log(sizeof(void*)))
+        JS_STATIC_ASSERT(sizeof(ProfileEntry) == 4 * sizeof(void*));
+        lshiftPtr(Imm32(2 + (sizeof(void*) == 4 ? 2 : 3)), temp);
+        addPtr(ImmWord(p->stack()), temp);
+    }
+
+  public:
+
+    // These functions are needed by the IonInstrumentation interface defined in
+    // vm/SPSProfiler.h.  They will modify the pseudostack provided to SPS to
+    // perform the actual instrumentation.
+
+    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 spsPushFrame(SPSProfiler *p, const char *str, JSScript *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()));
+
+        /* Always increment the stack size, tempardless if we actually pushed */
+        bind(&stackFull);
+        movePtr(ImmWord(p->sizePointer()), temp);
+        add32(Imm32(1), Address(temp, 0));
+    }
+
+    void spsPopFrame(SPSProfiler *p, Register temp) {
+        movePtr(ImmWord(p->sizePointer()), temp);
+        add32(Imm32(-1), Address(temp, 0));
+    }
+
+    // These two functions are helpers used around call sites throughout the
+    // assembler. They are mainly called from the call wrappers above, but can
+    // also be found within callVM and around callIon.
+
+    void leaveBeforeCall() {
+        if (!sps || !sps->enabled())
+            return;
+        GeneralRegisterSet regs(Registers::TempMask);
+        Register r = regs.getAny();
+        push(r);
+        sps->leave(*pc_, *this, r);
+        pop(r);
+    }
+
+    void reenterAfterCall() {
+        if (!sps || !sps->enabled())
+            return;
+        GeneralRegisterSet regs(Registers::TempMask & ~Registers::JSCallMask &
+                                                      ~Registers::CallMask);
+        if (regs.empty()) {
+            regs = GeneralRegisterSet(Registers::TempMask);
+            Register r = regs.getAny();
+            push(r);
+            sps->reenter(*this, r);
+            pop(r);
+        } else {
+            sps->reenter(*this, regs.getAny());
+        }
+    }
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_macro_assembler_h__
 
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -2974,51 +2974,40 @@ class LInstanceOfV : public LInstruction
     const LAllocation *rhs() {
         return getOperand(RHS);
     }
 
     static const size_t LHS = 0;
     static const size_t RHS = BOX_PIECES;
 };
 
-class LProfilingEnter : public LInstructionHelper<0, 0, 2>
+class LFunctionBoundary : public LInstructionHelper<0, 0, 1>
 {
   public:
-    LIR_HEADER(ProfilingEnter);
-
-    LProfilingEnter(const LDefinition &temp1, const LDefinition &temp2) {
-        setTemp(0, temp1);
-        setTemp(1, temp2);
-    }
-
-    const LDefinition *temp1() {
-        return getTemp(0);
-    }
-
-    const LDefinition *temp2() {
-        return getTemp(1);
-    }
-
-    const char *profileString() const {
-        return mir_->toProfilingEnter()->profileString();
-    }
-};
-
-class LProfilingExit : public LInstructionHelper<0, 0, 1>
-{
-  public:
-    LIR_HEADER(ProfilingExit);
-
-    LProfilingExit(const LDefinition &temp) {
+    LIR_HEADER(FunctionBoundary);
+
+    LFunctionBoundary(const LDefinition &temp) {
         setTemp(0, temp);
     }
 
     const LDefinition *temp() {
         return getTemp(0);
     }
+
+    JSScript *script() {
+        return mir_->toFunctionBoundary()->script();
+    }
+
+    MFunctionBoundary::Type type() {
+        return mir_->toFunctionBoundary()->type();
+    }
+
+    unsigned inlineLevel() {
+        return mir_->toFunctionBoundary()->inlineLevel();
+    }
 };
 
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_lir_common_h__
 
--- a/js/src/ion/LIR.h
+++ b/js/src/ion/LIR.h
@@ -652,36 +652,37 @@ class LInstruction
     LIR_OPCODE_LIST(LIROP)
 #   undef LIROP
 
     virtual bool accept(LInstructionVisitor *visitor) = 0;
 };
 
 class LInstructionVisitor
 {
-#ifdef TRACK_SNAPSHOTS
     LInstruction *ins_;
 
   protected:
+    jsbytecode *lastPC;
+
     LInstruction *instruction() {
         return ins_;
     }
-#endif
 
   public:
     void setInstruction(LInstruction *ins) {
-#ifdef TRACK_SNAPSHOTS
         ins_ = ins;
-#endif
+        if (ins->mirRaw()) {
+            lastPC = ins->mirRaw()->trackedPc();
+            JS_ASSERT(lastPC != NULL);
+        }
     }
 
     LInstructionVisitor()
-#ifdef TRACK_SNAPSHOTS
-      : ins_(NULL)
-#endif
+      : ins_(NULL),
+        lastPC(NULL)
     {}
 
   public:
 #define VISIT_INS(op) virtual bool visit##op(L##op *) { JS_NOT_REACHED("NYI: " #op); return false; }
     LIR_OPCODE_LIST(VISIT_INS)
 #undef VISIT_INS
 };
 
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -156,18 +156,17 @@
     _(GetArgument)                  \
     _(TypeOfV)                      \
     _(ToIdV)                        \
     _(Floor)                        \
     _(Round)                        \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(InterruptCheck)               \
-    _(ProfilingEnter)               \
-    _(ProfilingExit)                \
+    _(FunctionBoundary)             \
     _(GetDOMProperty)               \
     _(SetDOMProperty)               \
     _(CallDOMNative)
 
 #if defined(JS_CPU_X86)
 # include "x86/LOpcodes-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/LOpcodes-x64.h"
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -1803,25 +1803,25 @@ LIRGenerator::visitInstanceOf(MInstanceO
         return define(lir, ins) && assignSafepoint(lir, ins);
     } else {
         LInstanceOfV *lir = new LInstanceOfV(useRegister(rhs), temp(), temp());
         return useBox(lir, LInstanceOfV::LHS, lhs) && define(lir, ins) && assignSafepoint(lir, ins);
     }
 }
 
 bool
-LIRGenerator::visitProfilingEnter(MProfilingEnter *ins)
+LIRGenerator::visitFunctionBoundary(MFunctionBoundary *ins)
 {
-    return add(new LProfilingEnter(temp(), temp()), ins);
-}
-
-bool
-LIRGenerator::visitProfilingExit(MProfilingExit *ins)
-{
-    return add(new LProfilingExit(temp()), ins);
+    LFunctionBoundary *lir = new LFunctionBoundary(temp());
+    if (!add(lir, ins))
+        return false;
+    // If slow assertions are enabled, then this node will result in a callVM
+    // out to a C++ function for the assertions, so we will need a safepoint.
+    return !gen->compartment->rt->spsProfiler.slowAssertionsEnabled() ||
+           assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitSetDOMProperty(MSetDOMProperty *ins)
 {
     MDefinition *val = ins->value();
 
     LSetDOMProperty *lir = new LSetDOMProperty(tempFixed(CallTempReg0),
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -171,18 +171,17 @@ class LIRGenerator : public LIRGenerator
     bool visitIteratorNext(MIteratorNext *ins);
     bool visitIteratorMore(MIteratorMore *ins);
     bool visitIteratorEnd(MIteratorEnd *ins);
     bool visitStringLength(MStringLength *ins);
     bool visitArgumentsLength(MArgumentsLength *ins);
     bool visitGetArgument(MGetArgument *ins);
     bool visitThrow(MThrow *ins);
     bool visitInstanceOf(MInstanceOf *ins);
-    bool visitProfilingEnter(MProfilingEnter *ins);
-    bool visitProfilingExit(MProfilingExit *ins);
+    bool visitFunctionBoundary(MFunctionBoundary *ins);
     bool visitSetDOMProperty(MSetDOMProperty *ins);
     bool visitGetDOMProperty(MGetDOMProperty *ins);
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ion_lowering_h__
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -234,16 +234,20 @@ class MDefinition : public MNode
     MIRType resultType_;           // Representation of result type.
     uint32 flags_;                 // Bit flags.
     union {
         MDefinition *dependency_;  // Implicit dependency (store, call, etc.) of this instruction.
                                    // Used by alias analysis, GVN and LICM.
         uint32 virtualRegister_;   // Used by lowering to map definitions to virtual registers.
     };
 
+    // Track bailouts by storing the current pc in MIR instruction. Also used
+    // for profiling and keeping track of what the last known pc was.
+    jsbytecode *trackedPc_;
+
   private:
     enum Flag {
         None = 0,
 #   define DEFINE_FLAG(flag) flag,
         MIR_FLAG_LIST(DEFINE_FLAG)
 #   undef DEFINE_FLAG
         Total
     };
@@ -257,49 +261,40 @@ class MDefinition : public MNode
     }
     void removeFlags(uint32 flags) {
         flags_ &= ~flags;
     }
     void setFlags(uint32 flags) {
         flags_ |= flags;
     }
 
-#ifdef TRACK_SNAPSHOTS
-    // Track bailouts by storing the current pc in MIR instruction.
-    jsbytecode *trackedPc_;
-
-  public:
-    void setTrackedPc(jsbytecode *pc) {
-        if (!trackedPc_)
-            trackedPc_ = pc;
-    }
-
-    jsbytecode *trackedPc() {
-        return trackedPc_;
-    }
-#endif
-
   public:
     MDefinition()
       : id_(0),
         valueNumber_(NULL),
         range_(),
         resultType_(MIRType_None),
         flags_(0),
-        dependency_(NULL)
-#ifdef TRACK_SNAPSHOTS
-      , trackedPc_(NULL)
-#endif
+        dependency_(NULL),
+        trackedPc_(NULL)
     { }
 
     virtual Opcode op() const = 0;
     void printName(FILE *fp);
     static void PrintOpcodeName(FILE *fp, Opcode op);
     virtual void printOpcode(FILE *fp);
 
+    void setTrackedPc(jsbytecode *pc) {
+        trackedPc_ = pc;
+    }
+
+    jsbytecode *trackedPc() {
+        return trackedPc_;
+    }
+
     Range *range() {
         return &range_;
     }
 
     virtual HashNumber valueHash() const;
     virtual bool congruentTo(MDefinition* const &ins) const {
         return false;
     }
@@ -5287,54 +5282,60 @@ class MNewCallObject : public MUnaryInst
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 // Node that represents that a script has begun executing. This comes at the
 // start of the function and is called once per function (including inline
 // ones)
-class MProfilingEnter : public MNullaryInstruction
+class MFunctionBoundary : public MNullaryInstruction
 {
-    const char *string_;
-
-    MProfilingEnter(const char *string) : string_(string) {
-        JS_ASSERT(string != NULL);
+  public:
+    enum Type {
+        Enter,        // a function has begun executing and it is not inline
+        Exit,         // any function has exited (inlined or normal)
+        Inline_Enter, // an inline function has begun executing
+
+        Inline_Exit   // all instructions of an inline function are done, a
+                      // return from the inline function could have occurred
+                      // before this boundary
+    };
+
+  private:
+    JSScript *script_;
+    Type type_;
+    unsigned inlineLevel_;
+
+    MFunctionBoundary(JSScript *script, Type type, unsigned inlineLevel)
+      : script_(script), type_(type), inlineLevel_(inlineLevel)
+    {
+        JS_ASSERT_IF(type != Inline_Exit, script != NULL);
+        JS_ASSERT_IF(type == Inline_Enter, inlineLevel != 0);
         setGuard();
     }
 
   public:
-    INSTRUCTION_HEADER(ProfilingEnter);
-
-    static MProfilingEnter *New(const char *string) {
-        return new MProfilingEnter(string);
-    }
-
-    const char *profileString() {
-        return string_;
-    }
-
-    AliasSet getAliasSet() const {
-        return AliasSet::None();
-    }
-};
-
-// Pairing of MProfilingEnter. Each Enter is paired with an eventual
-// Exit. This includes inline functions.
-class MProfilingExit : public MNullaryInstruction
-{
-    MProfilingExit() {
-        setGuard();
-    }
-
-  public:
-    INSTRUCTION_HEADER(ProfilingExit);
-
-    static MProfilingExit *New() {
-        return new MProfilingExit();
+    INSTRUCTION_HEADER(FunctionBoundary);
+
+    static MFunctionBoundary *New(JSScript *script, Type type,
+                                  unsigned inlineLevel = 0) {
+        return new MFunctionBoundary(script, type, inlineLevel);
+    }
+
+    JSScript *script() {
+        return script_;
+    }
+
+    Type type() {
+        return type_;
+    }
+
+    unsigned inlineLevel() {
+        return inlineLevel_;
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 // This is an alias for MLoadFixedSlot.
--- a/js/src/ion/MIRGenerator.h
+++ b/js/src/ion/MIRGenerator.h
@@ -55,16 +55,20 @@ class MIRGenerator
     // propagated up.
     bool abort(const char *message, ...);
     bool abortFmt(const char *message, va_list ap);
 
     bool errored() const {
         return error_;
     }
 
+    bool instrumentedProfiling() {
+        return compartment->rt->spsProfiler.enabled();
+    }
+
   public:
     JSCompartment *compartment;
 
   protected:
     CompileInfo *info_;
     TempAllocator *temp_;
     JSFunction *fun_;
     uint32 nslots_;
--- a/js/src/ion/MIRGraph.cpp
+++ b/js/src/ion/MIRGraph.cpp
@@ -127,20 +127,18 @@ MBasicBlock::MBasicBlock(MIRGraph &graph
     entryResumePoint_(NULL),
     successorWithPhis_(NULL),
     positionInPhiSuccessor_(0),
     kind_(kind),
     loopDepth_(0),
     mark_(false),
     immediateDominator_(NULL),
     numDominated_(0),
-    loopHeader_(NULL)
-#ifdef TRACK_SNAPSHOTS
-  , trackedPc_(pc)
-#endif
+    loopHeader_(NULL),
+    trackedPc_(pc)
 {
 }
 
 bool
 MBasicBlock::init()
 {
     if (!slots_.init(info_.nslots()))
         return false;
@@ -480,42 +478,36 @@ MBasicBlock::discardDefAt(MDefinitionIte
 }
 
 void
 MBasicBlock::insertBefore(MInstruction *at, MInstruction *ins)
 {
     ins->setBlock(this);
     graph().allocDefinitionId(ins);
     instructions_.insertBefore(at, ins);
-#ifdef TRACK_SNAPSHOTS
     ins->setTrackedPc(at->trackedPc());
-#endif
 }
 
 void
 MBasicBlock::insertAfter(MInstruction *at, MInstruction *ins)
 {
     ins->setBlock(this);
     graph().allocDefinitionId(ins);
     instructions_.insertAfter(at, ins);
-#ifdef TRACK_SNAPSHOTS
     ins->setTrackedPc(at->trackedPc());
-#endif
 }
 
 void
 MBasicBlock::add(MInstruction *ins)
 {
     JS_ASSERT(!lastIns_);
     ins->setBlock(this);
     graph().allocDefinitionId(ins);
     instructions_.pushBack(ins);
-#ifdef TRACK_SNAPSHOTS
     ins->setTrackedPc(trackedPc_);
-#endif
 }
 
 void
 MBasicBlock::end(MControlInstruction *ins)
 {
     JS_ASSERT(!lastIns_); // Existing control instructions should be removed first.
     JS_ASSERT(ins);
     add(ins);
--- a/js/src/ion/MIRGraph.h
+++ b/js/src/ion/MIRGraph.h
@@ -381,23 +381,25 @@ class MBasicBlock : public TempObject, p
     }
 
     bool strictModeCode() const {
         return info_.script()->strictModeCode;
     }
 
     void dumpStack(FILE *fp);
 
-#ifdef TRACK_SNAPSHOTS
     // Track bailouts by storing the current pc in MIR instruction added at this
-    // cycle.
+    // cycle. This is also used for tracking calls when profiling.
     void updateTrackedPc(jsbytecode *pc) {
         trackedPc_ = pc;
     }
-#endif
+
+    jsbytecode *trackedPc() {
+        return trackedPc_;
+    }
 
   private:
     MIRGraph &graph_;
     CompileInfo &info_; // Each block originates from a particular script.
     InlineList<MInstruction> instructions_;
     Vector<MBasicBlock *, 1, IonAllocPolicy> predecessors_;
     InlineForwardList<MPhi> phis_;
     FixedList<MDefinition *> slots_;
@@ -417,21 +419,17 @@ class MBasicBlock : public TempObject, p
     // Utility mark for traversal algorithms.
     bool mark_;
 
     Vector<MBasicBlock *, 1, IonAllocPolicy> immediatelyDominated_;
     MBasicBlock *immediateDominator_;
     size_t numDominated_;
     MBasicBlock *loopHeader_;
 
-#ifdef TRACK_SNAPSHOTS
-    // Track bailouts by storing the current pc in MIR instruction added at this
-    // cycle.
     jsbytecode *trackedPc_;
-#endif
 };
 
 typedef InlineListIterator<MBasicBlock> MBasicBlockIterator;
 typedef InlineListIterator<MBasicBlock> ReversePostorderIterator;
 typedef InlineListReverseIterator<MBasicBlock> PostorderIterator;
 
 typedef Vector<MBasicBlock *, 1, IonAllocPolicy> MIRGraphExits;
 
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -122,18 +122,17 @@ namespace ion {
     _(IteratorEnd)                                                          \
     _(StringLength)                                                         \
     _(ArgumentsLength)                                                      \
     _(GetArgument)                                                          \
     _(Floor)                                                                \
     _(Round)                                                                \
     _(InstanceOf)                                                           \
     _(InterruptCheck)                                                       \
-    _(ProfilingEnter)                                                       \
-    _(ProfilingExit)                                                        \
+    _(FunctionBoundary)                                                     \
     _(GetDOMProperty)                                                       \
     _(SetDOMProperty)
 
 // Forward declarations of MIR types.
 #define FORWARD_DECLARE(op) class M##op;
  MIR_OPCODE_LIST(FORWARD_DECLARE)
 #undef FORWARD_DECLARE
 
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -353,10 +353,21 @@ NewSlots(JSRuntime *rt, unsigned nslots)
 
 JSObject *
 NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots,
               HandleObject global)
 {
     return CallObject::create(cx, shape, type, slots, global);
 }
 
+bool SPSEnter(JSContext *cx, HandleScript script)
+{
+    return cx->runtime->spsProfiler.enter(cx, script, script->function());
+}
+
+bool SPSExit(JSContext *cx, HandleScript script)
+{
+    cx->runtime->spsProfiler.exit(cx, script, script->function());
+    return true;
+}
+
 } // namespace ion
 } // namespace js
--- a/js/src/ion/VMFunctions.h
+++ b/js/src/ion/VMFunctions.h
@@ -434,14 +434,17 @@ bool SetProperty(JSContext *cx, HandleOb
                  bool strict, bool isSetName);
 
 bool InterruptCheck(JSContext *cx);
 
 HeapSlot *NewSlots(JSRuntime *rt, unsigned nslots);
 JSObject *NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots,
                         HandleObject global);
 
+bool SPSEnter(JSContext *cx, HandleScript script);
+bool SPSExit(JSContext *cx, HandleScript script);
+
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_vm_functions_h_
 
--- a/js/src/ion/arm/Architecture-arm.h
+++ b/js/src/ion/arm/Architecture-arm.h
@@ -127,17 +127,18 @@ class Registers
 
     // Registers returned from a JS -> JS call.
     static const uint32 JSCallMask =
         (1 << Registers::r2) |
         (1 << Registers::r3);
 
     // Registers returned from a JS -> C call.
     static const uint32 CallMask =
-        (1 << Registers::r0);
+        (1 << Registers::r0) |
+        (1 << Registers::r1);  // used for double-size returns
 
     static const uint32 AllocatableMask = AllMask & ~NonAllocatableMask;
 };
 
 // Smallest integer type that can hold a register bitmask.
 typedef uint16 PackedRegisterMask;
 
 class FloatRegisters
--- a/js/src/ion/arm/MacroAssembler-arm.h
+++ b/js/src/ion/arm/MacroAssembler-arm.h
@@ -985,16 +985,19 @@ class MacroAssemblerARMCompat : public M
     void cmpPtr(const Register &lhs, const Register &rhs);
     void cmpPtr(const Register &lhs, const ImmGCPtr &rhs);
     void cmpPtr(const Address &lhs, const Register &rhs);
     void cmpPtr(const Address &lhs, const ImmWord &rhs);
 
     void subPtr(Imm32 imm, const Register dest);
     void addPtr(Imm32 imm, const Register dest);
     void addPtr(Imm32 imm, const Address &dest);
+    void addPtr(ImmWord imm, const Register dest) {
+        addPtr(Imm32(imm.value), dest);
+    }
 
     void setStackArg(const Register &reg, uint32 arg);
 
     void breakpoint();
 
     void compareDouble(FloatRegister lhs, FloatRegister rhs);
     void branchDouble(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs,
                       Label *label);
--- a/js/src/ion/shared/CodeGenerator-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-shared.cpp
@@ -14,50 +14,65 @@
 #include "ion/IonMacroAssembler.h"
 using namespace js;
 using namespace js::ion;
 
 namespace js {
 namespace ion {
 
 CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph &graph)
-  : gen(gen),
+  : oolIns(NULL),
+    masm(&sps, &lastPC),
+    gen(gen),
     graph(graph),
+    current(NULL),
     deoptTable_(NULL),
 #ifdef DEBUG
     pushedArgs_(0),
 #endif
     lastOsiPointOffset_(0),
+    sps(&gen->compartment->rt->spsProfiler),
     osrEntryOffset_(0),
     frameDepth_(graph.localSlotCount() * sizeof(STACK_SLOT_SIZE) +
                 graph.argumentSlotCount() * sizeof(Value))
 {
     frameClass_ = FrameSizeClass::FromDepth(frameDepth_);
 }
 
 bool
 CodeGeneratorShared::generateOutOfLineCode()
 {
     for (size_t i = 0; i < outOfLineCode_.length(); i++) {
         if (!gen->temp().ensureBallast())
             return false;
         masm.setFramePushed(outOfLineCode_[i]->framePushed());
+        lastPC = outOfLineCode_[i]->pc();
+        sps.setPushed(outOfLineCode_[i]->script());
         outOfLineCode_[i]->bind(&masm);
 
+        oolIns = outOfLineCode_[i];
         if (!outOfLineCode_[i]->generate(this))
             return false;
     }
+    oolIns = NULL;
 
     return true;
 }
 
 bool
 CodeGeneratorShared::addOutOfLineCode(OutOfLineCode *code)
 {
     code->setFramePushed(masm.framePushed());
+    // If an OOL instruction adds another OOL instruction, then use the original
+    // instruction's script/pc instead of the basic block's that we're on
+    // because they're probably not relevant any more.
+    if (oolIns)
+        code->setSource(oolIns->script(), oolIns->pc());
+    else
+        code->setSource(current ? current->mir()->info().script() : NULL, lastPC);
     return outOfLineCode_.append(code);
 }
 
 static inline int32
 ToStackIndex(LAllocation *a)
 {
     if (a->isStackSlot()) {
         JS_ASSERT(a->toStackSlot()->slot() >= 1);
@@ -338,28 +353,30 @@ CodeGeneratorShared::callVM(const VMFunc
 #endif
 
     // Generate the wrapper of the VM function.
     JSContext *cx = GetIonContext()->cx;
     IonCompartment *ion = cx->compartment->ionCompartment();
     IonCode *wrapper = ion->generateVMWrapper(cx, fun);
     if (!wrapper)
         return false;
+    masm.leaveBeforeCall();
 
     // Call the wrapper function.  The wrapper is in charge to unwind the stack
     // when returning from the call.  Failures are handled with exceptions based
     // on the return value of the C functions.  To guard the outcome of the
     // returned value, use another LIR instruction.
     if (dynStack)
         masm.callWithExitFrame(wrapper, *dynStack);
     else
         masm.callWithExitFrame(wrapper);
 
     if (!markSafepoint(ins))
         return false;
+    masm.reenterAfterCall();
 
     // Remove rest of the frame left on the stack. We remove the return address
     // which is implicitly poped when returning.
     int framePop = sizeof(IonExitFrameLayout) - sizeof(void*);
 
     // Pop arguments from framePushed.
     masm.implicitPop(fun.explicitStackSlots() * sizeof(void *) + framePop);
 
--- a/js/src/ion/shared/CodeGenerator-shared.h
+++ b/js/src/ion/shared/CodeGenerator-shared.h
@@ -28,16 +28,17 @@ class MacroAssembler;
 
 template <class ArgSeq, class StoreOutputTo>
 class OutOfLineCallVM;
 class OutOfLineTruncateSlow;
 
 class CodeGeneratorShared : public LInstructionVisitor
 {
     js::Vector<OutOfLineCode *, 0, SystemAllocPolicy> outOfLineCode_;
+    OutOfLineCode *oolIns;
 
   protected:
     MacroAssembler masm;
     MIRGenerator *gen;
     LIRGraph &graph;
     LBlock *current;
     SnapshotWriter snapshots_;
     IonCode *deoptTable_;
@@ -59,16 +60,18 @@ class CodeGeneratorShared : public LInst
     js::Vector<IonCache, 0, SystemAllocPolicy> cacheList_;
 
     // Vector of all patchable write pre-barrier offsets.
     js::Vector<CodeOffsetLabel, 0, SystemAllocPolicy> barrierOffsets_;
 
     // List of stack slots that have been pushed as arguments to an MCall.
     js::Vector<uint32, 0, SystemAllocPolicy> pushedArgumentSlots_;
 
+    IonInstrumentation sps;
+
   protected:
     // The offset of the first instruction of the OSR entry block from the
     // beginning of the code buffer.
     size_t osrEntryOffset_;
 
     inline void setOsrEntryOffset(size_t offset) {
         JS_ASSERT(osrEntryOffset_ == 0);
         osrEntryOffset_ = offset;
@@ -285,20 +288,24 @@ struct HeapLabel
 };
 
 // An out-of-line path is generated at the end of the function.
 class OutOfLineCode : public TempObject
 {
     Label entry_;
     Label rejoin_;
     uint32 framePushed_;
+    jsbytecode *pc_;
+    JSScript *script_;
 
   public:
     OutOfLineCode()
-      : framePushed_(0)
+      : framePushed_(0),
+        pc_(NULL),
+        script_(NULL)
     { }
 
     virtual bool generate(CodeGeneratorShared *codegen) = 0;
 
     Label *entry() {
         return &entry_;
     }
     virtual void bind(MacroAssembler *masm) {
@@ -308,16 +315,26 @@ class OutOfLineCode : public TempObject
         return &rejoin_;
     }
     void setFramePushed(uint32 framePushed) {
         framePushed_ = framePushed;
     }
     uint32 framePushed() const {
         return framePushed_;
     }
+    void setSource(JSScript *script, jsbytecode *pc) {
+        script_ = script;
+        pc_ = pc;
+    }
+    jsbytecode *pc() {
+        return pc_;
+    }
+    JSScript *script() {
+        return script_;
+    }
 };
 
 // For OOL paths that want a specific-typed code generator.
 template <typename T>
 class OutOfLineCodeBase : public OutOfLineCode
 {
   public:
     virtual bool generate(CodeGeneratorShared *codegen) {
--- a/js/src/ion/shared/Lowering-x86-shared.cpp
+++ b/js/src/ion/shared/Lowering-x86-shared.cpp
@@ -48,17 +48,17 @@ LIRGeneratorX86Shared::visitRecompileChe
     LRecompileCheck *lir = new LRecompileCheck();
     return assignSnapshot(lir, Bailout_RecompileCheck) && add(lir, ins);
 }
 
 bool
 LIRGeneratorX86Shared::visitInterruptCheck(MInterruptCheck *ins)
 {
     LInterruptCheck *lir = new LInterruptCheck();
-    if (!add(lir))
+    if (!add(lir, ins))
         return false;
     if (!assignSafepoint(lir, ins))
         return false;
     return true;
 }
 
 bool
 LIRGeneratorX86Shared::visitGuardShape(MGuardShape *ins)
--- a/js/src/ion/x64/MacroAssembler-x64.h
+++ b/js/src/ion/x64/MacroAssembler-x64.h
@@ -346,16 +346,21 @@ class MacroAssemblerX64 : public MacroAs
         addq(src, dest);
     }
     void addPtr(Imm32 imm, const Register &dest) {
         addq(imm, dest);
     }
     void addPtr(Imm32 imm, const Address &dest) {
         addq(imm, Operand(dest));
     }
+    void addPtr(ImmWord imm, const Register &dest) {
+        JS_ASSERT(dest != ScratchReg);
+        movq(imm, ScratchReg);
+        addq(ScratchReg, dest);
+    }
     void subPtr(Imm32 imm, const Register &dest) {
         subq(imm, dest);
     }
     void subPtr(const Register &src, const Register &dest) {
         subq(src, dest);
     }
 
     // Specialization for AbsoluteAddress.
--- a/js/src/ion/x86/MacroAssembler-x86.h
+++ b/js/src/ion/x86/MacroAssembler-x86.h
@@ -376,16 +376,19 @@ class MacroAssemblerX86 : public MacroAs
     }
 
     void addPtr(const Register &src, const Register &dest) {
         addl(src, dest);
     }
     void addPtr(Imm32 imm, const Register &dest) {
         addl(imm, dest);
     }
+    void addPtr(ImmWord imm, const Register &dest) {
+        addl(Imm32(imm.value), dest);
+    }
     void addPtr(Imm32 imm, const Address &dest) {
         addl(imm, Operand(dest));
     }
     void subPtr(Imm32 imm, const Register &dest) {
         subl(imm, dest);
     }
 
     template <typename T, typename S>
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -293,51 +293,16 @@ class SPSInstrumentation
 
     SPSProfiler *profiler_; // Instrumentation location management
 
     Vector<FrameState, 1, SystemAllocPolicy> frames;
     FrameState *frame;
 
   public:
     /*
-     * SPS instruments calls to track leaving and reentering a function. A call
-     * is one made to C++ other other JS. This is a helper class to assist with
-     * the calls to leave()/reenter() to an SPSInstrumentation instance. It uses
-     * RAII to invoke leave() on construction and reenter() on destruction.
-     */
-    class CallScope
-    {
-        SPSInstrumentation *sps;
-        jsbytecode *pc;
-        Assembler &masm;
-        Register reg;
-        JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-      public:
-
-        /*
-         * Each parameter will be passed along to the instrumentation's
-         * leave()/reenter() methods. The given instrumentation can be NULL, in
-         * which case this object will do nothing.
-         */
-        CallScope(SPSInstrumentation *sps, jsbytecode *pc, Assembler &masm,
-                  Register reg JS_GUARD_OBJECT_NOTIFIER_PARAM)
-          : sps(sps), pc(pc), masm(masm), reg(reg)
-        {
-            JS_GUARD_OBJECT_NOTIFIER_INIT;
-            if (sps)
-                sps->leave(pc, masm, reg);
-        }
-
-        ~CallScope() {
-            if (sps)
-                sps->reenter(masm, reg);
-        }
-    };
-
-    /*
      * Creates instrumentation which writes information out the the specified
      * profiler's stack and constituent fields.
      */
     SPSInstrumentation(SPSProfiler *profiler)
       : profiler_(profiler), frame(NULL)
     {
         enterInlineFrame();
     }
@@ -398,17 +363,16 @@ class SPSInstrumentation
     /*
      * In some cases, a frame needs to be flagged as having been pushed, but no
      * instrumentation should be emitted. This updates internal state to flag
      * that further instrumentation should actually be emitted.
      */
     void setPushed(JSScript *script) {
         if (!enabled())
             return;
-        JS_ASSERT(frame->script == NULL);
         JS_ASSERT(frame->left == 0);
         frame->script = script;
     }
 
     /*
      * Flags entry into a JS function for the first time. Before this is called,
      * no instrumentation is emitted, but after this instrumentation is emitted.
      */
@@ -439,18 +403,21 @@ class SPSInstrumentation
     /*
      * Signals that the current function is leaving for a function call. This
      * can happen both on JS function calls and also calls to C++. This
      * internally manages how many leave() calls have been seen, and only the
      * first leave() emits instrumentation. Similarly, only the last
      * corresponding reenter() actually emits instrumentation.
      */
     void leave(jsbytecode *pc, Assembler &masm, Register scratch) {
-        if (enabled() && frame->script && frame->left++ == 0)
+        if (enabled() && frame->script && frame->left++ == 0) {
+            JS_ASSERT(frame->script->code <= pc &&
+                      pc < frame->script->code + frame->script->length);
             masm.spsUpdatePCIdx(profiler_, pc - frame->script->code, scratch);
+        }
     }
 
     /*
      * Flags that the leaving of the current function has returned. This tracks
      * state with leave() to only emit instrumentation at proper times.
      */
     void reenter(Assembler &masm, Register scratch) {
         if (!enabled() || !frame->script || frame->left-- != 1)