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