From 3e57f2d6eb85b25531876d47a1a1b6e4cd075f7f Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Fri, 22 Mar 2013 13:42:52 -0400 Subject: [PATCH 1/2] Bug 846363 - Integrate SPS Profiler with Baseline. r=jandem --- js/src/ion/BaselineBailouts.cpp | 12 +- js/src/ion/BaselineCompiler.cpp | 44 +++- js/src/ion/BaselineCompiler.h | 3 + js/src/ion/BaselineFrame.cpp | 3 + js/src/ion/BaselineFrame.h | 17 +- js/src/ion/BaselineIC.cpp | 241 +++++++++++++++++- js/src/ion/BaselineIC.h | 238 +++++++++++++---- js/src/ion/BaselineJIT.cpp | 43 +++- js/src/ion/BaselineJIT.h | 16 +- js/src/ion/IonFrames.cpp | 4 + js/src/ion/IonMacroAssembler.h | 38 ++- js/src/ion/shared/BaselineCompiler-shared.cpp | 6 +- js/src/ion/shared/BaselineCompiler-shared.h | 3 + js/src/vm/SPSProfiler.cpp | 11 + js/src/vm/SPSProfiler.h | 6 +- 15 files changed, 610 insertions(+), 75 deletions(-) diff --git a/js/src/ion/BaselineBailouts.cpp b/js/src/ion/BaselineBailouts.cpp index 1b67a814b93..dd2ad4cf90b 100644 --- a/js/src/ion/BaselineBailouts.cpp +++ b/js/src/ion/BaselineBailouts.cpp @@ -499,6 +499,14 @@ InitFromBailout(JSContext *cx, HandleFunction fun, HandleScript script, Snapshot 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) { @@ -639,7 +647,6 @@ InitFromBailout(JSContext *cx, HandleFunction fun, HandleScript script, Snapshot // If this was the last inline frame, then unpacking is almost done. if (!iter.moreFrames()) { - // 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. @@ -742,6 +749,9 @@ InitFromBailout(JSContext *cx, HandleFunction fun, HandleScript script, Snapshot 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(); } else { opReturnAddr = nativeCodeForPC; } diff --git a/js/src/ion/BaselineCompiler.cpp b/js/src/ion/BaselineCompiler.cpp index 804d8fdc5a2..c66474a4191 100644 --- a/js/src/ion/BaselineCompiler.cpp +++ b/js/src/ion/BaselineCompiler.cpp @@ -114,9 +114,10 @@ BaselineCompiler::compile() return Method_Error; prologueOffset_.fixup(&masm); - uint32_t prologueOffset = uint32_t(prologueOffset_.offset()); + spsPushToggleOffset_.fixup(&masm); - BaselineScript *baselineScript = BaselineScript::New(cx, prologueOffset, + BaselineScript *baselineScript = BaselineScript::New(cx, prologueOffset_.offset(), + spsPushToggleOffset_.offset(), icEntries_.length(), pcMappingIndexEntries.length(), pcEntries.length()); @@ -158,6 +159,10 @@ BaselineCompiler::compile() 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; } @@ -228,6 +233,9 @@ BaselineCompiler::emitPrologue() if (!emitArgumentTypeChecks()) return false; + if (!emitSPSPush()) + return false; + return true; } @@ -236,6 +244,9 @@ BaselineCompiler::emitEpilogue() { masm.bind(return_); + // Pop SPS frame if necessary + emitSPSPop(); + masm.mov(BaselineFrameReg, BaselineStackReg); masm.pop(BaselineFrameReg); @@ -466,6 +477,35 @@ BaselineCompiler::emitDebugTrap() 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() { diff --git a/js/src/ion/BaselineCompiler.h b/js/src/ion/BaselineCompiler.h index 44c0efa296d..12425ab2cc8 100644 --- a/js/src/ion/BaselineCompiler.h +++ b/js/src/ion/BaselineCompiler.h @@ -193,12 +193,15 @@ class BaselineCompiler : public BaselineCompilerSpecific 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(); diff --git a/js/src/ion/BaselineFrame.cpp b/js/src/ion/BaselineFrame.cpp index 8424642a050..1a18e93150b 100644 --- a/js/src/ion/BaselineFrame.cpp +++ b/js/src/ion/BaselineFrame.cpp @@ -134,6 +134,9 @@ BaselineFrame::initForOsr(StackFrame *fp, uint32_t numStackValues) hookData_ = fp->hookData(); } + if (fp->hasPushedSPSFrame()) + flags_ |= BaselineFrame::HAS_PUSHED_SPS_FRAME; + frameSize_ = BaselineFrame::FramePointerOffset + BaselineFrame::Size() + numStackValues * sizeof(Value); diff --git a/js/src/ion/BaselineFrame.h b/js/src/ion/BaselineFrame.h index 186b79a07cf..46e0e62c5bb 100644 --- a/js/src/ion/BaselineFrame.h +++ b/js/src/ion/BaselineFrame.h @@ -56,7 +56,10 @@ class BaselineFrame 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. @@ -296,6 +299,18 @@ class BaselineFrame 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 { diff --git a/js/src/ion/BaselineIC.cpp b/js/src/ion/BaselineIC.cpp index 0587291abfe..88214dddf80 100644 --- a/js/src/ion/BaselineIC.cpp +++ b/js/src/ion/BaselineIC.cpp @@ -77,6 +77,7 @@ TypeFallbackICSpew(JSContext *cx, ICTypeMonitor_Fallback *stub, const char *fmt, #define TypeFallbackICSpew(...) #endif + ICFallbackStub * ICEntry::fallbackStub() const { @@ -240,6 +241,11 @@ ICStub::trace(JSTracer *trc) 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"); @@ -546,6 +552,29 @@ 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 @@ -858,6 +887,100 @@ ICUseCount_Fallback::Compiler::generateStubCode(MacroAssembler &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(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 // @@ -4477,9 +4600,9 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub } 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); @@ -4526,7 +4649,8 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, ICGetProp_Fallbac (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; @@ -4638,7 +4762,7 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub, 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; @@ -4901,6 +5025,28 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm) } 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); @@ -4924,7 +5070,7 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm) // 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) { @@ -4988,7 +5134,7 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, ICSetProp_Fallback *stu (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; @@ -5045,8 +5191,11 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub, } 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; @@ -5295,6 +5444,28 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm) } 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); @@ -5323,8 +5494,8 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm) // 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; @@ -5366,7 +5537,7 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, JSO 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; @@ -5384,7 +5555,7 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, JSO 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; @@ -5406,7 +5577,7 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, JSO 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; @@ -5472,9 +5643,13 @@ DoCallFallback(JSContext *cx, BaselineFrame *frame, ICCall_Fallback *stub, uint3 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; @@ -5792,6 +5967,32 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler &masm) } 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 @@ -5887,6 +6088,18 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler &masm) 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); diff --git a/js/src/ion/BaselineIC.h b/js/src/ion/BaselineIC.h index b8732928384..fdc403c3347 100644 --- a/js/src/ion/BaselineIC.h +++ b/js/src/ion/BaselineIC.h @@ -271,6 +271,9 @@ class ICEntry #define IC_STUB_KIND_LIST(_) \ _(UseCount_Fallback) \ \ + _(Profiler_Fallback) \ + _(Profiler_PushFunction) \ + \ _(TypeMonitor_Fallback) \ _(TypeMonitor_SingleObject) \ _(TypeMonitor_TypeObject) \ @@ -991,6 +994,11 @@ class ICStubCompiler 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)); @@ -1082,6 +1090,95 @@ class ICUseCount_Fallback : public ICFallbackStub }; }; +// 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(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(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. @@ -3893,26 +3990,31 @@ class ICGetProp_CallScripted : public ICMonitoredStub // 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(code, firstMonitorStub, - shape, holder, holderShape, getter); + shape, holder, holderShape, getter, + pcOffset); } HeapPtrShape &shape() { @@ -3927,6 +4029,7 @@ class ICGetProp_CallScripted : public ICMonitoredStub HeapPtrFunction &getter() { return getter_; } + static size_t offsetOfShape() { return offsetof(ICGetProp_CallScripted, shape_); } @@ -3939,31 +4042,35 @@ class ICGetProp_CallScripted : public ICMonitoredStub 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_); } }; }; @@ -4260,23 +4367,29 @@ class ICSetProp_CallScripted : public ICStub // 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(code, shape, holder, holderShape, setter); + return space->allocate(code, shape, holder, holderShape, setter, + pcOffset); } HeapPtrShape &shape() { @@ -4291,6 +4404,7 @@ class ICSetProp_CallScripted : public ICStub HeapPtrFunction &setter() { return setter_; } + static size_t offsetOfShape() { return offsetof(ICSetProp_CallScripted, shape_); } @@ -4303,28 +4417,33 @@ class ICSetProp_CallScripted : public ICStub 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_); } }; }; @@ -4420,26 +4539,34 @@ class ICCall_Scripted : public ICMonitoredStub friend class ICStubSpace; HeapPtrScript calleeScript_; + uint32_t pcOffset_; - ICCall_Scripted(IonCode *stubCode, ICStub *firstMonitorStub, HandleScript calleeScript) + 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(code, firstMonitorStub, calleeScript); + return space->allocate(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_); } }; @@ -4447,17 +4574,24 @@ 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(code, firstMonitorStub); + return space->allocate(code, firstMonitorStub, pcOffset); + } + + static size_t offsetOfPCOffset() { + return offsetof(ICCall_AnyScripted, pcOffset_); } }; @@ -4467,6 +4601,7 @@ class ICCallScriptedCompiler : public ICCallStubCompiler { ICStub *firstMonitorStub_; bool isConstructing_; RootedScript calleeScript_; + uint32_t pcOffset_; bool generateStubCode(MacroAssembler &masm); virtual int32_t getKey() const { @@ -4475,24 +4610,30 @@ class ICCallScriptedCompiler : public ICCallStubCompiler { 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_); } }; @@ -4501,26 +4642,33 @@ class ICCall_Native : public ICMonitoredStub friend class ICStubSpace; HeapPtrFunction callee_; + uint32_t pcOffset_; - ICCall_Native(IonCode *stubCode, ICStub *firstMonitorStub, HandleFunction callee) + 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(code, firstMonitorStub, callee); + return space->allocate(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. @@ -4529,6 +4677,7 @@ class ICCall_Native : public ICMonitoredStub ICStub *firstMonitorStub_; bool isConstructing_; RootedFunction callee_; + uint32_t pcOffset_; bool generateStubCode(MacroAssembler &masm); virtual int32_t getKey() const { @@ -4537,15 +4686,16 @@ class ICCall_Native : public ICMonitoredStub 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_); } }; }; diff --git a/js/src/ion/BaselineJIT.cpp b/js/src/ion/BaselineJIT.cpp index a3f20b27e81..e486e8d977a 100644 --- a/js/src/ion/BaselineJIT.cpp +++ b/js/src/ion/BaselineJIT.cpp @@ -32,10 +32,14 @@ PCMappingSlotInfo::ToSlotLocation(const StackValue *stackVal) 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) { } @@ -284,7 +288,8 @@ ion::CanEnterBaselineJIT(JSContext *cx, JSScript *scriptArg, StackFrame *fp, boo 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); @@ -306,7 +311,7 @@ BaselineScript::New(JSContext *cx, uint32_t prologueOffset, size_t icEntries, return NULL; BaselineScript *script = reinterpret_cast(buffer); - new (script) BaselineScript(prologueOffset); + new (script) BaselineScript(prologueOffset, spsPushToggleOffset); size_t offsetCursor = paddedBaselineScriptSize; @@ -613,6 +618,25 @@ BaselineScript::toggleDebugTraps(RawScript script, jsbytecode *pc) } } +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) { @@ -714,6 +738,19 @@ ion::SizeOfBaselineData(JSScript *script, JSMallocSizeOfFun mallocSizeOf, size_t 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(); + if (!script->hasBaselineScript()) + continue; + script->baselineScript()->toggleSPS(enable); + } + } +} + static void MarkActiveBaselineScripts(JSContext *cx, const IonActivationIterator &activation) { diff --git a/js/src/ion/BaselineJIT.h b/js/src/ion/BaselineJIT.h index 04ac701996a..435d72d749a 100644 --- a/js/src/ion/BaselineJIT.h +++ b/js/src/ion/BaselineJIT.h @@ -108,6 +108,12 @@ struct BaselineScript // 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 spsOn_; +#endif + uint32_t spsPushToggleOffset_; + public: enum Flag { // Flag set by JSScript::argumentsOptimizationFailed. Similar to @@ -136,9 +142,10 @@ struct BaselineScript 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); @@ -235,6 +242,8 @@ struct BaselineScript // toggle traps at |pc|. void toggleDebugTraps(RawScript script, jsbytecode *pc); + void toggleSPS(bool enable); + static size_t offsetOfFlags() { return offsetof(BaselineScript, flags_); } @@ -261,6 +270,9 @@ 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. diff --git a/js/src/ion/IonFrames.cpp b/js/src/ion/IonFrames.cpp index 284af22d297..490ed9abc20 100644 --- a/js/src/ion/IonFrames.cpp +++ b/js/src/ion/IonFrames.cpp @@ -466,6 +466,10 @@ HandleException(ResumeFromException *rfe) 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. diff --git a/js/src/ion/IonMacroAssembler.h b/js/src/ion/IonMacroAssembler.h index db99e60b3e0..5adf752dae1 100644 --- a/js/src/ion/IonMacroAssembler.h +++ b/js/src/ion/IonMacroAssembler.h @@ -748,16 +748,46 @@ class MacroAssembler : public MacroAssemblerSpecific 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); diff --git a/js/src/ion/shared/BaselineCompiler-shared.cpp b/js/src/ion/shared/BaselineCompiler-shared.cpp index 5b356189ba4..7b8bbfe05fc 100644 --- a/js/src/ion/shared/BaselineCompiler-shared.cpp +++ b/js/src/ion/shared/BaselineCompiler-shared.cpp @@ -24,9 +24,9 @@ BaselineCompilerShared::BaselineCompilerShared(JSContext *cx, HandleScript scrip pcMappingEntries_(), icLoadLabels_(), pushedBeforeCall_(0), - inCall_(false) -{ -} + inCall_(false), + spsPushToggleOffset_() +{ } bool BaselineCompilerShared::callVM(const VMFunction &fun) diff --git a/js/src/ion/shared/BaselineCompiler-shared.h b/js/src/ion/shared/BaselineCompiler-shared.h index 750a5f54a8f..d5ae1a5f60e 100644 --- a/js/src/ion/shared/BaselineCompiler-shared.h +++ b/js/src/ion/shared/BaselineCompiler-shared.h @@ -12,6 +12,7 @@ #include "ion/BaselineFrameInfo.h" #include "ion/IonSpewer.h" #include "ion/BaselineIC.h" +#include "ion/IonInstrumentation.h" #include "ion/IonMacroAssembler.h" namespace js { @@ -66,6 +67,8 @@ class BaselineCompilerShared uint32_t pushedBeforeCall_; mozilla::DebugOnly inCall_; + CodeOffsetLabel spsPushToggleOffset_; + BaselineCompilerShared(JSContext *cx, HandleScript script); ICEntry *allocateICEntry(ICStub *stub, bool isForOp) { diff --git a/js/src/vm/SPSProfiler.cpp b/js/src/vm/SPSProfiler.cpp index 37d16b27ada..9d09eef476a 100644 --- a/js/src/vm/SPSProfiler.cpp +++ b/js/src/vm/SPSProfiler.cpp @@ -16,6 +16,8 @@ #include "vm/SPSProfiler.h" #include "vm/StringBuffer.h" +#include "ion/BaselineJIT.h" + using namespace js; using mozilla::DebugOnly; @@ -66,6 +68,15 @@ SPSProfiler::enable(bool enabled) * 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 */ diff --git a/js/src/vm/SPSProfiler.h b/js/src/vm/SPSProfiler.h index 03b5a8b79c1..a8818ad653d 100644 --- a/js/src/vm/SPSProfiler.h +++ b/js/src/vm/SPSProfiler.h @@ -134,7 +134,7 @@ class SPSProfiler uint32_t *size_; uint32_t max_; bool slowAssertions; - bool enabled_; + uint32_t enabled_; const char *allocProfileString(JSContext *cx, RawScript script, RawFunction function); @@ -254,6 +254,10 @@ class SPSProfiler /* 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_; + } }; /* From c8219ba2f8d92565e43e283149a699b0cb0c6114 Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Fri, 22 Mar 2013 17:00:40 -0400 Subject: [PATCH 2/2] Bug 846363 - Fix SPS Profiler frame adjustment when bailing from Ion to Baseline. r=jandem --- js/src/ion/BaselineBailouts.cpp | 59 +++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/js/src/ion/BaselineBailouts.cpp b/js/src/ion/BaselineBailouts.cpp index dd2ad4cf90b..d37c6b66627 100644 --- a/js/src/ion/BaselineBailouts.cpp +++ b/js/src/ion/BaselineBailouts.cpp @@ -443,8 +443,10 @@ struct BaselineStackBuilder // +===============+ // 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)); @@ -509,7 +511,8 @@ InitFromBailout(JSContext *cx, HandleFunction fun, HandleScript script, Snapshot // 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(). @@ -642,11 +645,14 @@ InitFromBailout(JSContext *cx, HandleFunction fun, HandleScript script, Snapshot 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. @@ -752,6 +758,32 @@ InitFromBailout(JSContext *cx, HandleFunction fun, HandleScript script, Snapshot // 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; } @@ -762,6 +794,8 @@ InitFromBailout(JSContext *cx, HandleFunction fun, HandleScript script, Snapshot return true; } + *callPC = pc; + // Write out descriptor of BaselineJS frame. size_t baselineFrameDescr = MakeFrameDescriptor((uint32_t) builder.framePushed(), IonFrame_BaselineJS); @@ -1018,18 +1052,29 @@ ion::BailoutIonToBaseline(JSContext *cx, IonActivation *activation, IonBailoutIt 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();