michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/BaselineFrame-inl.h" michael@0: michael@0: #include "jit/BaselineJIT.h" michael@0: #include "jit/Ion.h" michael@0: #include "vm/Debugger.h" michael@0: #include "vm/ScopeObject.h" michael@0: michael@0: #include "jit/IonFrames-inl.h" michael@0: #include "vm/Stack-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: static void michael@0: MarkLocals(BaselineFrame *frame, JSTracer *trc, unsigned start, unsigned end) michael@0: { michael@0: if (start < end) { michael@0: // Stack grows down. michael@0: Value *last = frame->valueSlot(end - 1); michael@0: gc::MarkValueRootRange(trc, end - start, last, "baseline-stack"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: BaselineFrame::trace(JSTracer *trc, JitFrameIterator &frameIterator) michael@0: { michael@0: replaceCalleeToken(MarkCalleeToken(trc, calleeToken())); michael@0: michael@0: gc::MarkValueRoot(trc, &thisValue(), "baseline-this"); michael@0: michael@0: // Mark actual and formal args. michael@0: if (isNonEvalFunctionFrame()) { michael@0: unsigned numArgs = js::Max(numActualArgs(), numFormalArgs()); michael@0: gc::MarkValueRootRange(trc, numArgs, argv(), "baseline-args"); michael@0: } michael@0: michael@0: // Mark scope chain, if it exists. michael@0: if (scopeChain_) michael@0: gc::MarkObjectRoot(trc, &scopeChain_, "baseline-scopechain"); michael@0: michael@0: // Mark return value. michael@0: if (hasReturnValue()) michael@0: gc::MarkValueRoot(trc, returnValue().address(), "baseline-rval"); michael@0: michael@0: if (isEvalFrame()) michael@0: gc::MarkScriptRoot(trc, &evalScript_, "baseline-evalscript"); michael@0: michael@0: if (hasArgsObj()) michael@0: gc::MarkObjectRoot(trc, &argsObj_, "baseline-args-obj"); michael@0: michael@0: // Mark locals and stack values. michael@0: JSScript *script = this->script(); michael@0: size_t nfixed = script->nfixed(); michael@0: size_t nlivefixed = script->nfixedvars(); michael@0: michael@0: if (nfixed != nlivefixed) { michael@0: jsbytecode *pc; michael@0: NestedScopeObject *staticScope; michael@0: michael@0: frameIterator.baselineScriptAndPc(nullptr, &pc); michael@0: staticScope = script->getStaticScope(pc); michael@0: while (staticScope && !staticScope->is()) michael@0: staticScope = staticScope->enclosingNestedScope(); michael@0: michael@0: if (staticScope) { michael@0: StaticBlockObject &blockObj = staticScope->as(); michael@0: nlivefixed = blockObj.localOffset() + blockObj.numVariables(); michael@0: } michael@0: } michael@0: michael@0: JS_ASSERT(nlivefixed <= nfixed); michael@0: JS_ASSERT(nlivefixed >= script->nfixedvars()); michael@0: michael@0: // NB: It is possible that numValueSlots() could be zero, even if nfixed is michael@0: // nonzero. This is the case if the function has an early stack check. michael@0: if (numValueSlots() == 0) michael@0: return; michael@0: michael@0: JS_ASSERT(nfixed <= numValueSlots()); michael@0: michael@0: if (nfixed == nlivefixed) { michael@0: // All locals are live. michael@0: MarkLocals(this, trc, 0, numValueSlots()); michael@0: } else { michael@0: // Mark operand stack. michael@0: MarkLocals(this, trc, nfixed, numValueSlots()); michael@0: michael@0: // Clear dead locals. michael@0: while (nfixed > nlivefixed) michael@0: unaliasedLocal(--nfixed, DONT_CHECK_ALIASING).setUndefined(); michael@0: michael@0: // Mark live locals. michael@0: MarkLocals(this, trc, 0, nlivefixed); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: BaselineFrame::copyRawFrameSlots(AutoValueVector *vec) const michael@0: { michael@0: unsigned nfixed = script()->nfixed(); michael@0: unsigned nformals = numFormalArgs(); michael@0: michael@0: if (!vec->resize(nformals + nfixed)) michael@0: return false; michael@0: michael@0: mozilla::PodCopy(vec->begin(), argv(), nformals); michael@0: for (unsigned i = 0; i < nfixed; i++) michael@0: (*vec)[nformals + i] = *valueSlot(i); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineFrame::strictEvalPrologue(JSContext *cx) michael@0: { michael@0: JS_ASSERT(isStrictEvalFrame()); michael@0: michael@0: CallObject *callobj = CallObject::createForStrictEval(cx, this); michael@0: if (!callobj) michael@0: return false; michael@0: michael@0: pushOnScopeChain(*callobj); michael@0: flags_ |= HAS_CALL_OBJ; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineFrame::heavyweightFunPrologue(JSContext *cx) michael@0: { michael@0: return initFunctionScopeObjects(cx); michael@0: } michael@0: michael@0: bool michael@0: BaselineFrame::initFunctionScopeObjects(JSContext *cx) michael@0: { michael@0: JS_ASSERT(isNonEvalFunctionFrame()); michael@0: JS_ASSERT(fun()->isHeavyweight()); michael@0: michael@0: CallObject *callobj = CallObject::createForFunction(cx, this); michael@0: if (!callobj) michael@0: return false; michael@0: michael@0: pushOnScopeChain(*callobj); michael@0: flags_ |= HAS_CALL_OBJ; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineFrame::initForOsr(InterpreterFrame *fp, uint32_t numStackValues) michael@0: { michael@0: mozilla::PodZero(this); michael@0: michael@0: scopeChain_ = fp->scopeChain(); michael@0: michael@0: if (fp->hasCallObjUnchecked()) michael@0: flags_ |= BaselineFrame::HAS_CALL_OBJ; michael@0: michael@0: if (fp->isEvalFrame()) { michael@0: flags_ |= BaselineFrame::EVAL; michael@0: evalScript_ = fp->script(); michael@0: } michael@0: michael@0: if (fp->script()->needsArgsObj() && fp->hasArgsObj()) { michael@0: flags_ |= BaselineFrame::HAS_ARGS_OBJ; michael@0: argsObj_ = &fp->argsObj(); michael@0: } michael@0: michael@0: if (fp->hasHookData()) { michael@0: flags_ |= BaselineFrame::HAS_HOOK_DATA; michael@0: hookData_ = fp->hookData(); michael@0: } michael@0: michael@0: if (fp->hasReturnValue()) michael@0: setReturnValue(fp->returnValue()); michael@0: michael@0: // If the interpreter pushed an SPS frame when it entered the function, the michael@0: // interpreter will pop it after the OSR trampoline returns. In order for michael@0: // the Baseline frame to have its SPS flag set, it must have its own SPS michael@0: // frame, which the Baseline code will pop on return. Note that the michael@0: // profiler may have been enabled or disabled after the function was entered michael@0: // but before OSR. michael@0: JSContext *cx = GetJSContextFromJitCode(); michael@0: SPSProfiler *p = &(cx->runtime()->spsProfiler); michael@0: if (p->enabled()) { michael@0: p->enter(fp->script(), fp->maybeFun()); michael@0: flags_ |= BaselineFrame::HAS_PUSHED_SPS_FRAME; michael@0: } michael@0: michael@0: frameSize_ = BaselineFrame::FramePointerOffset + michael@0: BaselineFrame::Size() + michael@0: numStackValues * sizeof(Value); michael@0: michael@0: JS_ASSERT(numValueSlots() == numStackValues); michael@0: michael@0: for (uint32_t i = 0; i < numStackValues; i++) michael@0: *valueSlot(i) = fp->slots()[i]; michael@0: michael@0: if (cx->compartment()->debugMode()) { michael@0: // In debug mode, update any Debugger.Frame objects for the michael@0: // InterpreterFrame to point to the BaselineFrame. michael@0: michael@0: // The caller pushed a fake return address. ScriptFrameIter, used by the michael@0: // debugger, wants a valid return address, but it's okay to just pick one. michael@0: // In debug mode there's always at least 1 ICEntry (since there are always michael@0: // debug prologue/epilogue calls). michael@0: JitFrameIterator iter(cx); michael@0: JS_ASSERT(iter.returnAddress() == nullptr); michael@0: BaselineScript *baseline = fp->script()->baselineScript(); michael@0: iter.current()->setReturnAddress(baseline->returnAddressForIC(baseline->icEntry(0))); michael@0: michael@0: if (!Debugger::handleBaselineOsr(cx, fp, this)) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: }