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/IonFrames-inl.h" michael@0: michael@0: #include "jsfun.h" michael@0: #include "jsobj.h" michael@0: #include "jsscript.h" michael@0: michael@0: #include "gc/Marking.h" michael@0: #include "jit/BaselineDebugModeOSR.h" michael@0: #include "jit/BaselineFrame.h" michael@0: #include "jit/BaselineIC.h" michael@0: #include "jit/BaselineJIT.h" michael@0: #include "jit/Ion.h" michael@0: #include "jit/IonMacroAssembler.h" michael@0: #include "jit/IonSpewer.h" michael@0: #include "jit/JitCompartment.h" michael@0: #include "jit/ParallelFunctions.h" michael@0: #include "jit/PcScriptCache.h" michael@0: #include "jit/Recover.h" michael@0: #include "jit/Safepoints.h" michael@0: #include "jit/Snapshots.h" michael@0: #include "jit/VMFunctions.h" michael@0: #include "vm/ArgumentsObject.h" michael@0: #include "vm/ForkJoin.h" michael@0: #include "vm/Interpreter.h" michael@0: michael@0: #include "jsscriptinlines.h" michael@0: #include "jit/JitFrameIterator-inl.h" michael@0: #include "vm/Probes-inl.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: // Given a slot index, returns the offset, in bytes, of that slot from an michael@0: // IonJSFrameLayout. Slot distances are uniform across architectures, however, michael@0: // the distance does depend on the size of the frame header. michael@0: static inline int32_t michael@0: OffsetOfFrameSlot(int32_t slot) michael@0: { michael@0: return -slot; michael@0: } michael@0: michael@0: static inline uintptr_t michael@0: ReadFrameSlot(IonJSFrameLayout *fp, int32_t slot) michael@0: { michael@0: return *(uintptr_t *)((char *)fp + OffsetOfFrameSlot(slot)); michael@0: } michael@0: michael@0: static inline double michael@0: ReadFrameDoubleSlot(IonJSFrameLayout *fp, int32_t slot) michael@0: { michael@0: return *(double *)((char *)fp + OffsetOfFrameSlot(slot)); michael@0: } michael@0: michael@0: static inline float michael@0: ReadFrameFloat32Slot(IonJSFrameLayout *fp, int32_t slot) michael@0: { michael@0: return *(float *)((char *)fp + OffsetOfFrameSlot(slot)); michael@0: } michael@0: michael@0: static inline int32_t michael@0: ReadFrameInt32Slot(IonJSFrameLayout *fp, int32_t slot) michael@0: { michael@0: return *(int32_t *)((char *)fp + OffsetOfFrameSlot(slot)); michael@0: } michael@0: michael@0: static inline bool michael@0: ReadFrameBooleanSlot(IonJSFrameLayout *fp, int32_t slot) michael@0: { michael@0: return *(bool *)((char *)fp + OffsetOfFrameSlot(slot)); michael@0: } michael@0: michael@0: JitFrameIterator::JitFrameIterator(JSContext *cx) michael@0: : current_(cx->mainThread().ionTop), michael@0: type_(JitFrame_Exit), michael@0: returnAddressToFp_(nullptr), michael@0: frameSize_(0), michael@0: cachedSafepointIndex_(nullptr), michael@0: activation_(nullptr), michael@0: mode_(SequentialExecution) michael@0: { michael@0: } michael@0: michael@0: JitFrameIterator::JitFrameIterator(const ActivationIterator &activations) michael@0: : current_(activations.jitTop()), michael@0: type_(JitFrame_Exit), michael@0: returnAddressToFp_(nullptr), michael@0: frameSize_(0), michael@0: cachedSafepointIndex_(nullptr), michael@0: activation_(activations->asJit()), michael@0: mode_(SequentialExecution) michael@0: { michael@0: } michael@0: michael@0: JitFrameIterator::JitFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode) michael@0: : current_((uint8_t *)fp), michael@0: type_(JitFrame_IonJS), michael@0: returnAddressToFp_(fp->returnAddress()), michael@0: frameSize_(fp->prevFrameLocalSize()), michael@0: mode_(mode) michael@0: { michael@0: } michael@0: michael@0: bool michael@0: JitFrameIterator::checkInvalidation() const michael@0: { michael@0: IonScript *dummy; michael@0: return checkInvalidation(&dummy); michael@0: } michael@0: michael@0: bool michael@0: JitFrameIterator::checkInvalidation(IonScript **ionScriptOut) const michael@0: { michael@0: uint8_t *returnAddr = returnAddressToFp(); michael@0: JSScript *script = this->script(); michael@0: // N.B. the current IonScript is not the same as the frame's michael@0: // IonScript if the frame has since been invalidated. michael@0: bool invalidated; michael@0: if (mode_ == ParallelExecution) { michael@0: // Parallel execution does not have invalidating bailouts. michael@0: invalidated = false; michael@0: } else { michael@0: invalidated = !script->hasIonScript() || michael@0: !script->ionScript()->containsReturnAddress(returnAddr); michael@0: } michael@0: if (!invalidated) michael@0: return false; michael@0: michael@0: int32_t invalidationDataOffset = ((int32_t *) returnAddr)[-1]; michael@0: uint8_t *ionScriptDataOffset = returnAddr + invalidationDataOffset; michael@0: IonScript *ionScript = (IonScript *) Assembler::getPointer(ionScriptDataOffset); michael@0: JS_ASSERT(ionScript->containsReturnAddress(returnAddr)); michael@0: *ionScriptOut = ionScript; michael@0: return true; michael@0: } michael@0: michael@0: CalleeToken michael@0: JitFrameIterator::calleeToken() const michael@0: { michael@0: return ((IonJSFrameLayout *) current_)->calleeToken(); michael@0: } michael@0: michael@0: JSFunction * michael@0: JitFrameIterator::callee() const michael@0: { michael@0: JS_ASSERT(isScripted()); michael@0: JS_ASSERT(isFunctionFrame()); michael@0: return CalleeTokenToFunction(calleeToken()); michael@0: } michael@0: michael@0: JSFunction * michael@0: JitFrameIterator::maybeCallee() const michael@0: { michael@0: if (isScripted() && (isFunctionFrame())) michael@0: return callee(); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: JitFrameIterator::isNative() const michael@0: { michael@0: if (type_ != JitFrame_Exit || isFakeExitFrame()) michael@0: return false; michael@0: return exitFrame()->footer()->jitCode() == nullptr; michael@0: } michael@0: michael@0: bool michael@0: JitFrameIterator::isOOLNative() const michael@0: { michael@0: if (type_ != JitFrame_Exit) michael@0: return false; michael@0: return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_NATIVE; michael@0: } michael@0: michael@0: bool michael@0: JitFrameIterator::isOOLPropertyOp() const michael@0: { michael@0: if (type_ != JitFrame_Exit) michael@0: return false; michael@0: return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_PROPERTY_OP; michael@0: } michael@0: michael@0: bool michael@0: JitFrameIterator::isOOLProxy() const michael@0: { michael@0: if (type_ != JitFrame_Exit) michael@0: return false; michael@0: return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_PROXY; michael@0: } michael@0: michael@0: bool michael@0: JitFrameIterator::isDOMExit() const michael@0: { michael@0: if (type_ != JitFrame_Exit) michael@0: return false; michael@0: return exitFrame()->isDomExit(); michael@0: } michael@0: michael@0: bool michael@0: JitFrameIterator::isFunctionFrame() const michael@0: { michael@0: return CalleeTokenIsFunction(calleeToken()); michael@0: } michael@0: michael@0: JSScript * michael@0: JitFrameIterator::script() const michael@0: { michael@0: JS_ASSERT(isScripted()); michael@0: if (isBaselineJS()) michael@0: return baselineFrame()->script(); michael@0: JSScript *script = ScriptFromCalleeToken(calleeToken()); michael@0: JS_ASSERT(script); michael@0: return script; michael@0: } michael@0: michael@0: void michael@0: JitFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const michael@0: { michael@0: JS_ASSERT(isBaselineJS()); michael@0: JSScript *script = this->script(); michael@0: if (scriptRes) michael@0: *scriptRes = script; michael@0: uint8_t *retAddr = returnAddressToFp(); michael@0: michael@0: // If we are in the middle of a recompile handler, get the real return michael@0: // address as stashed in the RecompileInfo. michael@0: if (BaselineDebugModeOSRInfo *info = baselineFrame()->getDebugModeOSRInfo()) michael@0: retAddr = info->resumeAddr; michael@0: michael@0: if (pcRes) { michael@0: // If the return address is into the prologue entry address or just michael@0: // after the debug prologue, then assume start of script. michael@0: if (retAddr == script->baselineScript()->prologueEntryAddr() || michael@0: retAddr == script->baselineScript()->postDebugPrologueAddr()) michael@0: { michael@0: *pcRes = script->code(); michael@0: return; michael@0: } michael@0: michael@0: // The return address _may_ be a return from a callVM or IC chain call done for michael@0: // some op. michael@0: ICEntry *icEntry = script->baselineScript()->maybeICEntryFromReturnAddress(retAddr); michael@0: if (icEntry) { michael@0: *pcRes = icEntry->pc(script); michael@0: return; michael@0: } michael@0: michael@0: // If not, the return address _must_ be the start address of an op, which can michael@0: // be computed from the pc mapping table. michael@0: *pcRes = script->baselineScript()->pcForReturnAddress(script, retAddr); michael@0: } michael@0: } michael@0: michael@0: Value * michael@0: JitFrameIterator::actualArgs() const michael@0: { michael@0: return jsFrame()->argv() + 1; michael@0: } michael@0: michael@0: static inline size_t michael@0: SizeOfFramePrefix(FrameType type) michael@0: { michael@0: switch (type) { michael@0: case JitFrame_Entry: michael@0: return IonEntryFrameLayout::Size(); michael@0: case JitFrame_BaselineJS: michael@0: case JitFrame_IonJS: michael@0: case JitFrame_Unwound_IonJS: michael@0: return IonJSFrameLayout::Size(); michael@0: case JitFrame_BaselineStub: michael@0: return IonBaselineStubFrameLayout::Size(); michael@0: case JitFrame_Rectifier: michael@0: return IonRectifierFrameLayout::Size(); michael@0: case JitFrame_Unwound_Rectifier: michael@0: return IonUnwoundRectifierFrameLayout::Size(); michael@0: case JitFrame_Exit: michael@0: return IonExitFrameLayout::Size(); michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unknown frame type"); michael@0: } michael@0: } michael@0: michael@0: uint8_t * michael@0: JitFrameIterator::prevFp() const michael@0: { michael@0: size_t currentSize = SizeOfFramePrefix(type_); michael@0: // This quick fix must be removed as soon as bug 717297 land. This is michael@0: // needed because the descriptor size of JS-to-JS frame which is just after michael@0: // a Rectifier frame should not change. (cf EnsureExitFrame function) michael@0: if (isFakeExitFrame()) { michael@0: JS_ASSERT(SizeOfFramePrefix(JitFrame_BaselineJS) == michael@0: SizeOfFramePrefix(JitFrame_IonJS)); michael@0: currentSize = SizeOfFramePrefix(JitFrame_IonJS); michael@0: } michael@0: currentSize += current()->prevFrameLocalSize(); michael@0: return current_ + currentSize; michael@0: } michael@0: michael@0: JitFrameIterator & michael@0: JitFrameIterator::operator++() michael@0: { michael@0: JS_ASSERT(type_ != JitFrame_Entry); michael@0: michael@0: frameSize_ = prevFrameLocalSize(); michael@0: cachedSafepointIndex_ = nullptr; michael@0: michael@0: // If the next frame is the entry frame, just exit. Don't update current_, michael@0: // since the entry and first frames overlap. michael@0: if (current()->prevType() == JitFrame_Entry) { michael@0: type_ = JitFrame_Entry; michael@0: return *this; michael@0: } michael@0: michael@0: // Note: prevFp() needs the current type, so set it after computing the michael@0: // next frame. michael@0: uint8_t *prev = prevFp(); michael@0: type_ = current()->prevType(); michael@0: if (type_ == JitFrame_Unwound_IonJS) michael@0: type_ = JitFrame_IonJS; michael@0: else if (type_ == JitFrame_Unwound_BaselineStub) michael@0: type_ = JitFrame_BaselineStub; michael@0: returnAddressToFp_ = current()->returnAddress(); michael@0: current_ = prev; michael@0: return *this; michael@0: } michael@0: michael@0: uintptr_t * michael@0: JitFrameIterator::spillBase() const michael@0: { michael@0: // Get the base address to where safepoint registers are spilled. michael@0: // Out-of-line calls do not unwind the extra padding space used to michael@0: // aggregate bailout tables, so we use frameSize instead of frameLocals, michael@0: // which would only account for local stack slots. michael@0: return reinterpret_cast(fp() - ionScript()->frameSize()); michael@0: } michael@0: michael@0: MachineState michael@0: JitFrameIterator::machineState() const michael@0: { michael@0: SafepointReader reader(ionScript(), safepoint()); michael@0: uintptr_t *spill = spillBase(); michael@0: michael@0: MachineState machine; michael@0: for (GeneralRegisterBackwardIterator iter(reader.allGprSpills()); iter.more(); iter++) michael@0: machine.setRegisterLocation(*iter, --spill); michael@0: michael@0: double *floatSpill = reinterpret_cast(spill); michael@0: for (FloatRegisterBackwardIterator iter(reader.allFloatSpills()); iter.more(); iter++) michael@0: machine.setRegisterLocation(*iter, --floatSpill); michael@0: michael@0: return machine; michael@0: } michael@0: michael@0: static void michael@0: CloseLiveIterator(JSContext *cx, const InlineFrameIterator &frame, uint32_t localSlot) michael@0: { michael@0: SnapshotIterator si = frame.snapshotIterator(); michael@0: michael@0: // Skip stack slots until we reach the iterator object. michael@0: uint32_t base = CountArgSlots(frame.script(), frame.maybeCallee()) + frame.script()->nfixed(); michael@0: uint32_t skipSlots = base + localSlot - 1; michael@0: michael@0: for (unsigned i = 0; i < skipSlots; i++) michael@0: si.skip(); michael@0: michael@0: Value v = si.read(); michael@0: RootedObject obj(cx, &v.toObject()); michael@0: michael@0: if (cx->isExceptionPending()) michael@0: UnwindIteratorForException(cx, obj); michael@0: else michael@0: UnwindIteratorForUncatchableException(cx, obj); michael@0: } michael@0: michael@0: static void michael@0: HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromException *rfe, michael@0: bool *overrecursed) michael@0: { michael@0: RootedScript script(cx, frame.script()); michael@0: jsbytecode *pc = frame.pc(); michael@0: michael@0: bool bailedOutForDebugMode = false; michael@0: if (cx->compartment()->debugMode()) { michael@0: // If we have an exception from within Ion and the debugger is active, michael@0: // we do the following: michael@0: // michael@0: // 1. Bailout to baseline to reconstruct a baseline frame. michael@0: // 2. Resume immediately into the exception tail afterwards, and michael@0: // handle the exception again with the top frame now a baseline michael@0: // frame. michael@0: // michael@0: // An empty exception info denotes that we're propagating an Ion michael@0: // exception due to debug mode, which BailoutIonToBaseline needs to michael@0: // know. This is because we might not be able to fully reconstruct up michael@0: // to the stack depth at the snapshot, as we could've thrown in the michael@0: // middle of a call. michael@0: ExceptionBailoutInfo propagateInfo; michael@0: uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed); michael@0: bailedOutForDebugMode = retval == BAILOUT_RETURN_OK; michael@0: } michael@0: michael@0: if (!script->hasTrynotes()) michael@0: return; michael@0: michael@0: JSTryNote *tn = script->trynotes()->vector; michael@0: JSTryNote *tnEnd = tn + script->trynotes()->length; michael@0: michael@0: uint32_t pcOffset = uint32_t(pc - script->main()); michael@0: for (; tn != tnEnd; ++tn) { michael@0: if (pcOffset < tn->start) michael@0: continue; michael@0: if (pcOffset >= tn->start + tn->length) michael@0: continue; michael@0: michael@0: switch (tn->kind) { michael@0: case JSTRY_ITER: { michael@0: JS_ASSERT(JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER); michael@0: JS_ASSERT(tn->stackDepth > 0); michael@0: michael@0: uint32_t localSlot = tn->stackDepth; michael@0: CloseLiveIterator(cx, frame, localSlot); michael@0: break; michael@0: } michael@0: michael@0: case JSTRY_LOOP: michael@0: break; michael@0: michael@0: case JSTRY_CATCH: michael@0: if (cx->isExceptionPending() && !bailedOutForDebugMode) { michael@0: // Ion can compile try-catch, but bailing out to catch michael@0: // exceptions is slow. Reset the use count so that if we michael@0: // catch many exceptions we won't Ion-compile the script. michael@0: script->resetUseCount(); michael@0: michael@0: // Bailout at the start of the catch block. michael@0: jsbytecode *catchPC = script->main() + tn->start + tn->length; michael@0: ExceptionBailoutInfo excInfo(frame.frameNo(), catchPC, tn->stackDepth); michael@0: uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed); michael@0: if (retval == BAILOUT_RETURN_OK) michael@0: return; michael@0: michael@0: // Error on bailout clears pending exception. michael@0: MOZ_ASSERT(!cx->isExceptionPending()); michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected try note"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static void michael@0: HandleExceptionBaseline(JSContext *cx, const JitFrameIterator &frame, ResumeFromException *rfe, michael@0: bool *calledDebugEpilogue) michael@0: { michael@0: JS_ASSERT(frame.isBaselineJS()); michael@0: JS_ASSERT(!*calledDebugEpilogue); michael@0: michael@0: RootedScript script(cx); michael@0: jsbytecode *pc; michael@0: frame.baselineScriptAndPc(script.address(), &pc); michael@0: michael@0: if (cx->isExceptionPending() && cx->compartment()->debugMode()) { michael@0: BaselineFrame *baselineFrame = frame.baselineFrame(); michael@0: JSTrapStatus status = DebugExceptionUnwind(cx, baselineFrame, pc); michael@0: switch (status) { michael@0: case JSTRAP_ERROR: michael@0: // Uncatchable exception. michael@0: JS_ASSERT(!cx->isExceptionPending()); michael@0: break; michael@0: michael@0: case JSTRAP_CONTINUE: michael@0: case JSTRAP_THROW: michael@0: JS_ASSERT(cx->isExceptionPending()); michael@0: break; michael@0: michael@0: case JSTRAP_RETURN: michael@0: JS_ASSERT(baselineFrame->hasReturnValue()); michael@0: if (jit::DebugEpilogue(cx, baselineFrame, pc, true)) { michael@0: rfe->kind = ResumeFromException::RESUME_FORCED_RETURN; michael@0: rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset; michael@0: rfe->stackPointer = reinterpret_cast(baselineFrame); michael@0: return; michael@0: } michael@0: michael@0: // DebugEpilogue threw an exception. Propagate to the caller frame. michael@0: *calledDebugEpilogue = true; michael@0: return; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid trap status"); michael@0: } michael@0: } michael@0: michael@0: if (!script->hasTrynotes()) michael@0: return; michael@0: michael@0: JSTryNote *tn = script->trynotes()->vector; michael@0: JSTryNote *tnEnd = tn + script->trynotes()->length; michael@0: michael@0: uint32_t pcOffset = uint32_t(pc - script->main()); michael@0: ScopeIter si(frame.baselineFrame(), pc, cx); michael@0: for (; tn != tnEnd; ++tn) { michael@0: if (pcOffset < tn->start) michael@0: continue; michael@0: if (pcOffset >= tn->start + tn->length) michael@0: continue; michael@0: michael@0: // Skip if the try note's stack depth exceeds the frame's stack depth. michael@0: // See the big comment in TryNoteIter::settle for more info. michael@0: JS_ASSERT(frame.baselineFrame()->numValueSlots() >= script->nfixed()); michael@0: size_t stackDepth = frame.baselineFrame()->numValueSlots() - script->nfixed(); michael@0: if (tn->stackDepth > stackDepth) michael@0: continue; michael@0: michael@0: // Unwind scope chain (pop block objects). michael@0: if (cx->isExceptionPending()) michael@0: UnwindScope(cx, si, script->main() + tn->start); michael@0: michael@0: // Compute base pointer and stack pointer. michael@0: rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset; michael@0: rfe->stackPointer = rfe->framePointer - BaselineFrame::Size() - michael@0: (script->nfixed() + tn->stackDepth) * sizeof(Value); michael@0: michael@0: switch (tn->kind) { michael@0: case JSTRY_CATCH: michael@0: if (cx->isExceptionPending()) { michael@0: // Ion can compile try-catch, but bailing out to catch michael@0: // exceptions is slow. Reset the use count so that if we michael@0: // catch many exceptions we won't Ion-compile the script. michael@0: script->resetUseCount(); michael@0: michael@0: // Resume at the start of the catch block. michael@0: rfe->kind = ResumeFromException::RESUME_CATCH; michael@0: jsbytecode *catchPC = script->main() + tn->start + tn->length; michael@0: rfe->target = script->baselineScript()->nativeCodeForPC(script, catchPC); michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: case JSTRY_FINALLY: michael@0: if (cx->isExceptionPending()) { michael@0: rfe->kind = ResumeFromException::RESUME_FINALLY; michael@0: jsbytecode *finallyPC = script->main() + tn->start + tn->length; michael@0: rfe->target = script->baselineScript()->nativeCodeForPC(script, finallyPC); michael@0: // Drop the exception instead of leaking cross compartment data. michael@0: if (!cx->getPendingException(MutableHandleValue::fromMarkedLocation(&rfe->exception))) michael@0: rfe->exception = UndefinedValue(); michael@0: cx->clearPendingException(); michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: case JSTRY_ITER: { michael@0: Value iterValue(* (Value *) rfe->stackPointer); michael@0: RootedObject iterObject(cx, &iterValue.toObject()); michael@0: if (cx->isExceptionPending()) michael@0: UnwindIteratorForException(cx, iterObject); michael@0: else michael@0: UnwindIteratorForUncatchableException(cx, iterObject); michael@0: break; michael@0: } michael@0: michael@0: case JSTRY_LOOP: michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid try note"); michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: struct AutoDeleteDebugModeOSRInfo michael@0: { michael@0: BaselineFrame *frame; michael@0: AutoDeleteDebugModeOSRInfo(BaselineFrame *frame) : frame(frame) { MOZ_ASSERT(frame); } michael@0: ~AutoDeleteDebugModeOSRInfo() { frame->deleteDebugModeOSRInfo(); } michael@0: }; michael@0: michael@0: void michael@0: HandleException(ResumeFromException *rfe) michael@0: { michael@0: JSContext *cx = GetJSContextFromJitCode(); michael@0: michael@0: rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME; michael@0: michael@0: IonSpew(IonSpew_Invalidate, "handling exception"); michael@0: michael@0: // Clear any Ion return override that's been set. michael@0: // This may happen if a callVM function causes an invalidation (setting the michael@0: // override), and then fails, bypassing the bailout handlers that would michael@0: // otherwise clear the return override. michael@0: if (cx->runtime()->hasIonReturnOverride()) michael@0: cx->runtime()->takeIonReturnOverride(); michael@0: michael@0: JitFrameIterator iter(cx); michael@0: while (!iter.isEntry()) { michael@0: bool overrecursed = false; michael@0: if (iter.isIonJS()) { michael@0: // Search each inlined frame for live iterator objects, and close michael@0: // them. michael@0: InlineFrameIterator frames(cx, &iter); michael@0: michael@0: // Invalidation state will be the same for all inlined scripts in the frame. michael@0: IonScript *ionScript = nullptr; michael@0: bool invalidated = iter.checkInvalidation(&ionScript); michael@0: michael@0: for (;;) { michael@0: HandleExceptionIon(cx, frames, rfe, &overrecursed); michael@0: michael@0: if (rfe->kind == ResumeFromException::RESUME_BAILOUT) { michael@0: if (invalidated) michael@0: ionScript->decref(cx->runtime()->defaultFreeOp()); michael@0: return; michael@0: } michael@0: michael@0: JS_ASSERT(rfe->kind == ResumeFromException::RESUME_ENTRY_FRAME); michael@0: michael@0: // Figure out whether SPS frame was pushed for this frame or not. michael@0: // Even if profiler is enabled, the frame being popped might have michael@0: // been entered prior to SPS being enabled, and thus not have michael@0: // a pushed SPS frame. michael@0: bool popSPSFrame = cx->runtime()->spsProfiler.enabled(); michael@0: if (invalidated) michael@0: popSPSFrame = ionScript->hasSPSInstrumentation(); michael@0: michael@0: // If inline-frames are not profiled, then don't pop an SPS frame michael@0: // for them. michael@0: if (frames.more() && !js_JitOptions.profileInlineFrames) michael@0: popSPSFrame = false; michael@0: michael@0: // When profiling, each frame popped needs a notification that michael@0: // the function has exited, so invoke the probe that a function michael@0: // is exiting. michael@0: JSScript *script = frames.script(); michael@0: probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); michael@0: if (!frames.more()) michael@0: break; michael@0: ++frames; michael@0: } michael@0: michael@0: if (invalidated) michael@0: ionScript->decref(cx->runtime()->defaultFreeOp()); michael@0: michael@0: } else if (iter.isBaselineJS()) { michael@0: // It's invalid to call DebugEpilogue twice for the same frame. michael@0: bool calledDebugEpilogue = false; michael@0: michael@0: HandleExceptionBaseline(cx, iter, rfe, &calledDebugEpilogue); michael@0: michael@0: // If we are propagating an exception through a frame with michael@0: // on-stack recompile info, we should free the allocated michael@0: // RecompileInfo struct before we leave this block, as we will not michael@0: // be returning to the recompile handler. michael@0: // michael@0: // We cannot delete it immediately because of the call to michael@0: // iter.baselineScriptAndPc below. michael@0: AutoDeleteDebugModeOSRInfo deleteDebugModeOSRInfo(iter.baselineFrame()); michael@0: michael@0: if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME) michael@0: return; michael@0: michael@0: // Unwind profiler pseudo-stack michael@0: JSScript *script = iter.script(); michael@0: probes::ExitScript(cx, script, script->functionNonDelazifying(), michael@0: iter.baselineFrame()->hasPushedSPSFrame()); michael@0: // After this point, any pushed SPS frame would have been popped if it needed michael@0: // to be. Unset the flag here so that if we call DebugEpilogue below, michael@0: // it doesn't try to pop the SPS frame again. michael@0: iter.baselineFrame()->unsetPushedSPSFrame(); michael@0: michael@0: if (cx->compartment()->debugMode() && !calledDebugEpilogue) { michael@0: // If DebugEpilogue returns |true|, we have to perform a forced michael@0: // return, e.g. return frame->returnValue() to the caller. michael@0: BaselineFrame *frame = iter.baselineFrame(); michael@0: RootedScript script(cx); michael@0: jsbytecode *pc; michael@0: iter.baselineScriptAndPc(script.address(), &pc); michael@0: if (jit::DebugEpilogue(cx, frame, pc, false)) { michael@0: JS_ASSERT(frame->hasReturnValue()); michael@0: rfe->kind = ResumeFromException::RESUME_FORCED_RETURN; michael@0: rfe->framePointer = iter.fp() - BaselineFrame::FramePointerOffset; michael@0: rfe->stackPointer = reinterpret_cast(frame); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: IonJSFrameLayout *current = iter.isScripted() ? iter.jsFrame() : nullptr; michael@0: michael@0: ++iter; michael@0: michael@0: if (current) { michael@0: // Unwind the frame by updating ionTop. This is necessary so that michael@0: // (1) debugger exception unwind and leave frame hooks don't see this michael@0: // frame when they use ScriptFrameIter, and (2) ScriptFrameIter does michael@0: // not crash when accessing an IonScript that's destroyed by the michael@0: // ionScript->decref call. michael@0: EnsureExitFrame(current); michael@0: cx->mainThread().ionTop = (uint8_t *)current; michael@0: } michael@0: michael@0: if (overrecursed) { michael@0: // We hit an overrecursion error during bailout. Report it now. michael@0: js_ReportOverRecursed(cx); michael@0: } michael@0: } michael@0: michael@0: rfe->stackPointer = iter.fp(); michael@0: } michael@0: michael@0: void michael@0: HandleParallelFailure(ResumeFromException *rfe) michael@0: { michael@0: ForkJoinContext *cx = ForkJoinContext::current(); michael@0: JitFrameIterator iter(cx->perThreadData->ionTop, ParallelExecution); michael@0: michael@0: parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry"); michael@0: michael@0: while (!iter.isEntry()) { michael@0: if (iter.isScripted()) { michael@0: cx->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM, michael@0: iter.script(), iter.script(), nullptr); michael@0: break; michael@0: } michael@0: ++iter; michael@0: } michael@0: michael@0: while (!iter.isEntry()) { michael@0: if (iter.isScripted()) michael@0: PropagateAbortPar(iter.script(), iter.script()); michael@0: ++iter; michael@0: } michael@0: michael@0: rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME; michael@0: rfe->stackPointer = iter.fp(); michael@0: } michael@0: michael@0: void michael@0: EnsureExitFrame(IonCommonFrameLayout *frame) michael@0: { michael@0: if (frame->prevType() == JitFrame_Unwound_IonJS || michael@0: frame->prevType() == JitFrame_Unwound_BaselineStub || michael@0: frame->prevType() == JitFrame_Unwound_Rectifier) michael@0: { michael@0: // Already an exit frame, nothing to do. michael@0: return; michael@0: } michael@0: michael@0: if (frame->prevType() == JitFrame_Entry) { michael@0: // The previous frame type is the entry frame, so there's no actual michael@0: // need for an exit frame. michael@0: return; michael@0: } michael@0: michael@0: if (frame->prevType() == JitFrame_Rectifier) { michael@0: // The rectifier code uses the frame descriptor to discard its stack, michael@0: // so modifying its descriptor size here would be dangerous. Instead, michael@0: // we change the frame type, and teach the stack walking code how to michael@0: // deal with this edge case. bug 717297 would obviate the need michael@0: frame->changePrevType(JitFrame_Unwound_Rectifier); michael@0: return; michael@0: } michael@0: michael@0: if (frame->prevType() == JitFrame_BaselineStub) { michael@0: frame->changePrevType(JitFrame_Unwound_BaselineStub); michael@0: return; michael@0: } michael@0: michael@0: JS_ASSERT(frame->prevType() == JitFrame_IonJS); michael@0: frame->changePrevType(JitFrame_Unwound_IonJS); michael@0: } michael@0: michael@0: CalleeToken michael@0: MarkCalleeToken(JSTracer *trc, CalleeToken token) michael@0: { michael@0: switch (GetCalleeTokenTag(token)) { michael@0: case CalleeToken_Function: michael@0: { michael@0: JSFunction *fun = CalleeTokenToFunction(token); michael@0: MarkObjectRoot(trc, &fun, "ion-callee"); michael@0: return CalleeToToken(fun); michael@0: } michael@0: case CalleeToken_Script: michael@0: { michael@0: JSScript *script = CalleeTokenToScript(token); michael@0: MarkScriptRoot(trc, &script, "ion-entry"); michael@0: return CalleeToToken(script); michael@0: } michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unknown callee token type"); michael@0: } michael@0: } michael@0: michael@0: #ifdef JS_NUNBOX32 michael@0: static inline uintptr_t michael@0: ReadAllocation(const JitFrameIterator &frame, const LAllocation *a) michael@0: { michael@0: if (a->isGeneralReg()) { michael@0: Register reg = a->toGeneralReg()->reg(); michael@0: return frame.machineState().read(reg); michael@0: } michael@0: if (a->isStackSlot()) { michael@0: uint32_t slot = a->toStackSlot()->slot(); michael@0: return *frame.jsFrame()->slotRef(slot); michael@0: } michael@0: uint32_t index = a->toArgument()->index(); michael@0: uint8_t *argv = reinterpret_cast(frame.jsFrame()->argv()); michael@0: return *reinterpret_cast(argv + index); michael@0: } michael@0: #endif michael@0: michael@0: static void michael@0: MarkActualArguments(JSTracer *trc, const JitFrameIterator &frame) michael@0: { michael@0: IonJSFrameLayout *layout = frame.jsFrame(); michael@0: JS_ASSERT(CalleeTokenIsFunction(layout->calleeToken())); michael@0: michael@0: size_t nargs = frame.numActualArgs(); michael@0: michael@0: // Trace function arguments. Note + 1 for thisv. michael@0: Value *argv = layout->argv(); michael@0: for (size_t i = 0; i < nargs + 1; i++) michael@0: gc::MarkValueRoot(trc, &argv[i], "ion-argv"); michael@0: } michael@0: michael@0: #ifdef JS_NUNBOX32 michael@0: static inline void michael@0: WriteAllocation(const JitFrameIterator &frame, const LAllocation *a, uintptr_t value) michael@0: { michael@0: if (a->isGeneralReg()) { michael@0: Register reg = a->toGeneralReg()->reg(); michael@0: frame.machineState().write(reg, value); michael@0: return; michael@0: } michael@0: if (a->isStackSlot()) { michael@0: uint32_t slot = a->toStackSlot()->slot(); michael@0: *frame.jsFrame()->slotRef(slot) = value; michael@0: return; michael@0: } michael@0: uint32_t index = a->toArgument()->index(); michael@0: uint8_t *argv = reinterpret_cast(frame.jsFrame()->argv()); michael@0: *reinterpret_cast(argv + index) = value; michael@0: } michael@0: #endif michael@0: michael@0: static void michael@0: MarkIonJSFrame(JSTracer *trc, const JitFrameIterator &frame) michael@0: { michael@0: IonJSFrameLayout *layout = (IonJSFrameLayout *)frame.fp(); michael@0: michael@0: layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken())); michael@0: michael@0: IonScript *ionScript = nullptr; michael@0: if (frame.checkInvalidation(&ionScript)) { michael@0: // This frame has been invalidated, meaning that its IonScript is no michael@0: // longer reachable through the callee token (JSFunction/JSScript->ion michael@0: // is now nullptr or recompiled). Manually trace it here. michael@0: IonScript::Trace(trc, ionScript); michael@0: } else if (CalleeTokenIsFunction(layout->calleeToken())) { michael@0: ionScript = CalleeTokenToFunction(layout->calleeToken())->nonLazyScript()->ionScript(); michael@0: } else { michael@0: ionScript = CalleeTokenToScript(layout->calleeToken())->ionScript(); michael@0: } michael@0: michael@0: if (CalleeTokenIsFunction(layout->calleeToken())) michael@0: MarkActualArguments(trc, frame); michael@0: michael@0: const SafepointIndex *si = ionScript->getSafepointIndex(frame.returnAddressToFp()); michael@0: michael@0: SafepointReader safepoint(ionScript, si); michael@0: michael@0: // Scan through slots which contain pointers (or on punboxing systems, michael@0: // actual values). michael@0: uint32_t slot; michael@0: while (safepoint.getGcSlot(&slot)) { michael@0: uintptr_t *ref = layout->slotRef(slot); michael@0: gc::MarkGCThingRoot(trc, reinterpret_cast(ref), "ion-gc-slot"); michael@0: } michael@0: michael@0: while (safepoint.getValueSlot(&slot)) { michael@0: Value *v = (Value *)layout->slotRef(slot); michael@0: gc::MarkValueRoot(trc, v, "ion-gc-slot"); michael@0: } michael@0: michael@0: uintptr_t *spill = frame.spillBase(); michael@0: GeneralRegisterSet gcRegs = safepoint.gcSpills(); michael@0: GeneralRegisterSet valueRegs = safepoint.valueSpills(); michael@0: for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); iter++) { michael@0: --spill; michael@0: if (gcRegs.has(*iter)) michael@0: gc::MarkGCThingRoot(trc, reinterpret_cast(spill), "ion-gc-spill"); michael@0: else if (valueRegs.has(*iter)) michael@0: gc::MarkValueRoot(trc, reinterpret_cast(spill), "ion-value-spill"); michael@0: } michael@0: michael@0: #ifdef JS_NUNBOX32 michael@0: LAllocation type, payload; michael@0: while (safepoint.getNunboxSlot(&type, &payload)) { michael@0: jsval_layout layout; michael@0: layout.s.tag = (JSValueTag)ReadAllocation(frame, &type); michael@0: layout.s.payload.uintptr = ReadAllocation(frame, &payload); michael@0: michael@0: Value v = IMPL_TO_JSVAL(layout); michael@0: gc::MarkValueRoot(trc, &v, "ion-torn-value"); michael@0: michael@0: if (v != IMPL_TO_JSVAL(layout)) { michael@0: // GC moved the value, replace the stored payload. michael@0: layout = JSVAL_TO_IMPL(v); michael@0: WriteAllocation(frame, &payload, layout.s.payload.uintptr); michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: static void michael@0: UpdateIonJSFrameForMinorGC(JSTracer *trc, const JitFrameIterator &frame) michael@0: { michael@0: // Minor GCs may move slots/elements allocated in the nursery. Update michael@0: // any slots/elements pointers stored in this frame. michael@0: michael@0: IonJSFrameLayout *layout = (IonJSFrameLayout *)frame.fp(); michael@0: michael@0: IonScript *ionScript = nullptr; michael@0: if (frame.checkInvalidation(&ionScript)) { michael@0: // This frame has been invalidated, meaning that its IonScript is no michael@0: // longer reachable through the callee token (JSFunction/JSScript->ion michael@0: // is now nullptr or recompiled). michael@0: } else if (CalleeTokenIsFunction(layout->calleeToken())) { michael@0: ionScript = CalleeTokenToFunction(layout->calleeToken())->nonLazyScript()->ionScript(); michael@0: } else { michael@0: ionScript = CalleeTokenToScript(layout->calleeToken())->ionScript(); michael@0: } michael@0: michael@0: const SafepointIndex *si = ionScript->getSafepointIndex(frame.returnAddressToFp()); michael@0: SafepointReader safepoint(ionScript, si); michael@0: michael@0: GeneralRegisterSet slotsRegs = safepoint.slotsOrElementsSpills(); michael@0: uintptr_t *spill = frame.spillBase(); michael@0: for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); iter++) { michael@0: --spill; michael@0: if (slotsRegs.has(*iter)) michael@0: trc->runtime()->gcNursery.forwardBufferPointer(reinterpret_cast(spill)); michael@0: } michael@0: michael@0: // Skip to the right place in the safepoint michael@0: uint32_t slot; michael@0: while (safepoint.getGcSlot(&slot)); michael@0: while (safepoint.getValueSlot(&slot)); michael@0: #ifdef JS_NUNBOX32 michael@0: LAllocation type, payload; michael@0: while (safepoint.getNunboxSlot(&type, &payload)); michael@0: #endif michael@0: michael@0: while (safepoint.getSlotsOrElementsSlot(&slot)) { michael@0: HeapSlot **slots = reinterpret_cast(layout->slotRef(slot)); michael@0: trc->runtime()->gcNursery.forwardBufferPointer(slots); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: static void michael@0: MarkBaselineStubFrame(JSTracer *trc, const JitFrameIterator &frame) michael@0: { michael@0: // Mark the ICStub pointer stored in the stub frame. This is necessary michael@0: // so that we don't destroy the stub code after unlinking the stub. michael@0: michael@0: JS_ASSERT(frame.type() == JitFrame_BaselineStub); michael@0: IonBaselineStubFrameLayout *layout = (IonBaselineStubFrameLayout *)frame.fp(); michael@0: michael@0: if (ICStub *stub = layout->maybeStubPtr()) { michael@0: JS_ASSERT(ICStub::CanMakeCalls(stub->kind())); michael@0: stub->trace(trc); michael@0: } michael@0: } michael@0: michael@0: void michael@0: JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end) michael@0: { michael@0: JitFrameIterator frames(jitTop(), SequentialExecution); michael@0: michael@0: if (frames.isFakeExitFrame()) { michael@0: min = reinterpret_cast(frames.fp()); michael@0: } else { michael@0: IonExitFrameLayout *exitFrame = frames.exitFrame(); michael@0: IonExitFooterFrame *footer = exitFrame->footer(); michael@0: const VMFunction *f = footer->function(); michael@0: if (exitFrame->isWrapperExit() && f->outParam == Type_Handle) { michael@0: switch (f->outParamRootType) { michael@0: case VMFunction::RootNone: michael@0: MOZ_ASSUME_UNREACHABLE("Handle outparam must have root type"); michael@0: case VMFunction::RootObject: michael@0: case VMFunction::RootString: michael@0: case VMFunction::RootPropertyName: michael@0: case VMFunction::RootFunction: michael@0: case VMFunction::RootCell: michael@0: // These are all handles to GCThing pointers. michael@0: min = reinterpret_cast(footer->outParam()); michael@0: break; michael@0: case VMFunction::RootValue: michael@0: min = reinterpret_cast(footer->outParam()); michael@0: break; michael@0: } michael@0: } else { michael@0: min = reinterpret_cast(footer); michael@0: } michael@0: } michael@0: michael@0: while (!frames.done()) michael@0: ++frames; michael@0: michael@0: end = reinterpret_cast(frames.prevFp()); michael@0: } michael@0: michael@0: static void michael@0: MarkJitExitFrame(JSTracer *trc, const JitFrameIterator &frame) michael@0: { michael@0: // Ignore fake exit frames created by EnsureExitFrame. michael@0: if (frame.isFakeExitFrame()) michael@0: return; michael@0: michael@0: IonExitFooterFrame *footer = frame.exitFrame()->footer(); michael@0: michael@0: // Mark the code of the code handling the exit path. This is needed because michael@0: // invalidated script are no longer marked because data are erased by the michael@0: // invalidation and relocation data are no longer reliable. So the VM michael@0: // wrapper or the invalidation code may be GC if no JitCode keep reference michael@0: // on them. michael@0: JS_ASSERT(uintptr_t(footer->jitCode()) != uintptr_t(-1)); michael@0: michael@0: // This correspond to the case where we have build a fake exit frame in michael@0: // CodeGenerator.cpp which handle the case of a native function call. We michael@0: // need to mark the argument vector of the function call. michael@0: if (frame.isNative()) { michael@0: IonNativeExitFrameLayout *native = frame.exitFrame()->nativeExit(); michael@0: size_t len = native->argc() + 2; michael@0: Value *vp = native->vp(); michael@0: gc::MarkValueRootRange(trc, len, vp, "ion-native-args"); michael@0: return; michael@0: } michael@0: michael@0: if (frame.isOOLNative()) { michael@0: IonOOLNativeExitFrameLayout *oolnative = frame.exitFrame()->oolNativeExit(); michael@0: gc::MarkJitCodeRoot(trc, oolnative->stubCode(), "ion-ool-native-code"); michael@0: gc::MarkValueRoot(trc, oolnative->vp(), "iol-ool-native-vp"); michael@0: size_t len = oolnative->argc() + 1; michael@0: gc::MarkValueRootRange(trc, len, oolnative->thisp(), "ion-ool-native-thisargs"); michael@0: return; michael@0: } michael@0: michael@0: if (frame.isOOLPropertyOp()) { michael@0: IonOOLPropertyOpExitFrameLayout *oolgetter = frame.exitFrame()->oolPropertyOpExit(); michael@0: gc::MarkJitCodeRoot(trc, oolgetter->stubCode(), "ion-ool-property-op-code"); michael@0: gc::MarkValueRoot(trc, oolgetter->vp(), "ion-ool-property-op-vp"); michael@0: gc::MarkIdRoot(trc, oolgetter->id(), "ion-ool-property-op-id"); michael@0: gc::MarkObjectRoot(trc, oolgetter->obj(), "ion-ool-property-op-obj"); michael@0: return; michael@0: } michael@0: michael@0: if (frame.isOOLProxy()) { michael@0: IonOOLProxyExitFrameLayout *oolproxy = frame.exitFrame()->oolProxyExit(); michael@0: gc::MarkJitCodeRoot(trc, oolproxy->stubCode(), "ion-ool-proxy-code"); michael@0: gc::MarkValueRoot(trc, oolproxy->vp(), "ion-ool-proxy-vp"); michael@0: gc::MarkIdRoot(trc, oolproxy->id(), "ion-ool-proxy-id"); michael@0: gc::MarkObjectRoot(trc, oolproxy->proxy(), "ion-ool-proxy-proxy"); michael@0: gc::MarkObjectRoot(trc, oolproxy->receiver(), "ion-ool-proxy-receiver"); michael@0: return; michael@0: } michael@0: michael@0: if (frame.isDOMExit()) { michael@0: IonDOMExitFrameLayout *dom = frame.exitFrame()->DOMExit(); michael@0: gc::MarkObjectRoot(trc, dom->thisObjAddress(), "ion-dom-args"); michael@0: if (dom->isMethodFrame()) { michael@0: IonDOMMethodExitFrameLayout *method = michael@0: reinterpret_cast(dom); michael@0: size_t len = method->argc() + 2; michael@0: Value *vp = method->vp(); michael@0: gc::MarkValueRootRange(trc, len, vp, "ion-dom-args"); michael@0: } else { michael@0: gc::MarkValueRoot(trc, dom->vp(), "ion-dom-args"); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: MarkJitCodeRoot(trc, footer->addressOfJitCode(), "ion-exit-code"); michael@0: michael@0: const VMFunction *f = footer->function(); michael@0: if (f == nullptr) michael@0: return; michael@0: michael@0: // Mark arguments of the VM wrapper. michael@0: uint8_t *argBase = frame.exitFrame()->argBase(); michael@0: for (uint32_t explicitArg = 0; explicitArg < f->explicitArgs; explicitArg++) { michael@0: switch (f->argRootType(explicitArg)) { michael@0: case VMFunction::RootNone: michael@0: break; michael@0: case VMFunction::RootObject: { michael@0: // Sometimes we can bake in HandleObjects to nullptr. michael@0: JSObject **pobj = reinterpret_cast(argBase); michael@0: if (*pobj) michael@0: gc::MarkObjectRoot(trc, pobj, "ion-vm-args"); michael@0: break; michael@0: } michael@0: case VMFunction::RootString: michael@0: case VMFunction::RootPropertyName: michael@0: gc::MarkStringRoot(trc, reinterpret_cast(argBase), "ion-vm-args"); michael@0: break; michael@0: case VMFunction::RootFunction: michael@0: gc::MarkObjectRoot(trc, reinterpret_cast(argBase), "ion-vm-args"); michael@0: break; michael@0: case VMFunction::RootValue: michael@0: gc::MarkValueRoot(trc, reinterpret_cast(argBase), "ion-vm-args"); michael@0: break; michael@0: case VMFunction::RootCell: michael@0: gc::MarkGCThingRoot(trc, reinterpret_cast(argBase), "ion-vm-args"); michael@0: break; michael@0: } michael@0: michael@0: switch (f->argProperties(explicitArg)) { michael@0: case VMFunction::WordByValue: michael@0: case VMFunction::WordByRef: michael@0: argBase += sizeof(void *); michael@0: break; michael@0: case VMFunction::DoubleByValue: michael@0: case VMFunction::DoubleByRef: michael@0: argBase += 2 * sizeof(void *); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (f->outParam == Type_Handle) { michael@0: switch (f->outParamRootType) { michael@0: case VMFunction::RootNone: michael@0: MOZ_ASSUME_UNREACHABLE("Handle outparam must have root type"); michael@0: case VMFunction::RootObject: michael@0: gc::MarkObjectRoot(trc, footer->outParam(), "ion-vm-out"); michael@0: break; michael@0: case VMFunction::RootString: michael@0: case VMFunction::RootPropertyName: michael@0: gc::MarkStringRoot(trc, footer->outParam(), "ion-vm-out"); michael@0: break; michael@0: case VMFunction::RootFunction: michael@0: gc::MarkObjectRoot(trc, footer->outParam(), "ion-vm-out"); michael@0: break; michael@0: case VMFunction::RootValue: michael@0: gc::MarkValueRoot(trc, footer->outParam(), "ion-vm-outvp"); michael@0: break; michael@0: case VMFunction::RootCell: michael@0: gc::MarkGCThingRoot(trc, footer->outParam(), "ion-vm-out"); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: static void michael@0: MarkRectifierFrame(JSTracer *trc, const JitFrameIterator &frame) michael@0: { michael@0: // Mark thisv. michael@0: // michael@0: // Baseline JIT code generated as part of the ICCall_Fallback stub may use michael@0: // it if we're calling a constructor that returns a primitive value. michael@0: IonRectifierFrameLayout *layout = (IonRectifierFrameLayout *)frame.fp(); michael@0: gc::MarkValueRoot(trc, &layout->argv()[0], "ion-thisv"); michael@0: } michael@0: michael@0: static void michael@0: MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations) michael@0: { michael@0: JitActivation *activation = activations->asJit(); michael@0: michael@0: #ifdef CHECK_OSIPOINT_REGISTERS michael@0: if (js_JitOptions.checkOsiPointRegisters) { michael@0: // GC can modify spilled registers, breaking our register checks. michael@0: // To handle this, we disable these checks for the current VM call michael@0: // when a GC happens. michael@0: activation->setCheckRegs(false); michael@0: } michael@0: #endif michael@0: michael@0: activation->markRematerializedFrames(trc); michael@0: michael@0: for (JitFrameIterator frames(activations); !frames.done(); ++frames) { michael@0: switch (frames.type()) { michael@0: case JitFrame_Exit: michael@0: MarkJitExitFrame(trc, frames); michael@0: break; michael@0: case JitFrame_BaselineJS: michael@0: frames.baselineFrame()->trace(trc, frames); michael@0: break; michael@0: case JitFrame_BaselineStub: michael@0: MarkBaselineStubFrame(trc, frames); michael@0: break; michael@0: case JitFrame_IonJS: michael@0: MarkIonJSFrame(trc, frames); michael@0: break; michael@0: case JitFrame_Unwound_IonJS: michael@0: MOZ_ASSUME_UNREACHABLE("invalid"); michael@0: case JitFrame_Rectifier: michael@0: MarkRectifierFrame(trc, frames); michael@0: break; michael@0: case JitFrame_Unwound_Rectifier: michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected frame type"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: MarkJitActivations(JSRuntime *rt, JSTracer *trc) michael@0: { michael@0: for (JitActivationIterator activations(rt); !activations.done(); ++activations) michael@0: MarkJitActivation(trc, activations); michael@0: } michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: void michael@0: UpdateJitActivationsForMinorGC(JSRuntime *rt, JSTracer *trc) michael@0: { michael@0: JS_ASSERT(trc->runtime()->isHeapMinorCollecting()); michael@0: for (JitActivationIterator activations(rt); !activations.done(); ++activations) { michael@0: for (JitFrameIterator frames(activations); !frames.done(); ++frames) { michael@0: if (frames.type() == JitFrame_IonJS) michael@0: UpdateIonJSFrameForMinorGC(trc, frames); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: AutoTempAllocatorRooter::trace(JSTracer *trc) michael@0: { michael@0: for (CompilerRootNode *root = temp->rootList(); root != nullptr; root = root->next) michael@0: gc::MarkGCThingRoot(trc, root->address(), "ion-compiler-root"); michael@0: } michael@0: michael@0: void michael@0: GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes) michael@0: { michael@0: IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame."); michael@0: michael@0: JSRuntime *rt = cx->runtime(); michael@0: michael@0: // Recover the return address. michael@0: JitFrameIterator it(rt->mainThread.ionTop, SequentialExecution); michael@0: michael@0: // If the previous frame is a rectifier frame (maybe unwound), michael@0: // skip past it. michael@0: if (it.prevType() == JitFrame_Rectifier || it.prevType() == JitFrame_Unwound_Rectifier) { michael@0: ++it; michael@0: JS_ASSERT(it.prevType() == JitFrame_BaselineStub || michael@0: it.prevType() == JitFrame_BaselineJS || michael@0: it.prevType() == JitFrame_IonJS); michael@0: } michael@0: michael@0: // If the previous frame is a stub frame, skip the exit frame so that michael@0: // returnAddress below gets the return address into the BaselineJS michael@0: // frame. michael@0: if (it.prevType() == JitFrame_BaselineStub || it.prevType() == JitFrame_Unwound_BaselineStub) { michael@0: ++it; michael@0: JS_ASSERT(it.prevType() == JitFrame_BaselineJS); michael@0: } michael@0: michael@0: uint8_t *retAddr = it.returnAddress(); michael@0: uint32_t hash = PcScriptCache::Hash(retAddr); michael@0: JS_ASSERT(retAddr != nullptr); michael@0: michael@0: // Lazily initialize the cache. The allocation may safely fail and will not GC. michael@0: if (MOZ_UNLIKELY(rt->ionPcScriptCache == nullptr)) { michael@0: rt->ionPcScriptCache = (PcScriptCache *)js_malloc(sizeof(struct PcScriptCache)); michael@0: if (rt->ionPcScriptCache) michael@0: rt->ionPcScriptCache->clear(rt->gcNumber); michael@0: } michael@0: michael@0: // Attempt to lookup address in cache. michael@0: if (rt->ionPcScriptCache && rt->ionPcScriptCache->get(rt, hash, retAddr, scriptRes, pcRes)) michael@0: return; michael@0: michael@0: // Lookup failed: undertake expensive process to recover the innermost inlined frame. michael@0: ++it; // Skip exit frame. michael@0: jsbytecode *pc = nullptr; michael@0: michael@0: if (it.isIonJS()) { michael@0: InlineFrameIterator ifi(cx, &it); michael@0: *scriptRes = ifi.script(); michael@0: pc = ifi.pc(); michael@0: } else { michael@0: JS_ASSERT(it.isBaselineJS()); michael@0: it.baselineScriptAndPc(scriptRes, &pc); michael@0: } michael@0: michael@0: if (pcRes) michael@0: *pcRes = pc; michael@0: michael@0: // Add entry to cache. michael@0: if (rt->ionPcScriptCache) michael@0: rt->ionPcScriptCache->add(hash, retAddr, pc, *scriptRes); michael@0: } michael@0: michael@0: void michael@0: OsiIndex::fixUpOffset(MacroAssembler &masm) michael@0: { michael@0: callPointDisplacement_ = masm.actualOffset(callPointDisplacement_); michael@0: } michael@0: michael@0: uint32_t michael@0: OsiIndex::returnPointDisplacement() const michael@0: { michael@0: // In general, pointer arithmetic on code is bad, but in this case, michael@0: // getting the return address from a call instruction, stepping over pools michael@0: // would be wrong. michael@0: return callPointDisplacement_ + Assembler::patchWrite_NearCallSize(); michael@0: } michael@0: michael@0: SnapshotIterator::SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshotOffset, michael@0: IonJSFrameLayout *fp, const MachineState &machine) michael@0: : snapshot_(ionScript->snapshots(), michael@0: snapshotOffset, michael@0: ionScript->snapshotsRVATableSize(), michael@0: ionScript->snapshotsListSize()), michael@0: recover_(snapshot_, michael@0: ionScript->recovers(), michael@0: ionScript->recoversSize()), michael@0: fp_(fp), michael@0: machine_(machine), michael@0: ionScript_(ionScript) michael@0: { michael@0: JS_ASSERT(snapshotOffset < ionScript->snapshotsListSize()); michael@0: } michael@0: michael@0: SnapshotIterator::SnapshotIterator(const JitFrameIterator &iter) michael@0: : snapshot_(iter.ionScript()->snapshots(), michael@0: iter.osiIndex()->snapshotOffset(), michael@0: iter.ionScript()->snapshotsRVATableSize(), michael@0: iter.ionScript()->snapshotsListSize()), michael@0: recover_(snapshot_, michael@0: iter.ionScript()->recovers(), michael@0: iter.ionScript()->recoversSize()), michael@0: fp_(iter.jsFrame()), michael@0: machine_(iter.machineState()), michael@0: ionScript_(iter.ionScript()) michael@0: { michael@0: } michael@0: michael@0: SnapshotIterator::SnapshotIterator() michael@0: : snapshot_(nullptr, 0, 0, 0), michael@0: recover_(snapshot_, nullptr, 0), michael@0: fp_(nullptr), michael@0: ionScript_(nullptr) michael@0: { michael@0: } michael@0: michael@0: uintptr_t michael@0: SnapshotIterator::fromStack(int32_t offset) const michael@0: { michael@0: return ReadFrameSlot(fp_, offset); michael@0: } michael@0: michael@0: static Value michael@0: FromObjectPayload(uintptr_t payload) michael@0: { michael@0: return ObjectValue(*reinterpret_cast(payload)); michael@0: } michael@0: michael@0: static Value michael@0: FromStringPayload(uintptr_t payload) michael@0: { michael@0: return StringValue(reinterpret_cast(payload)); michael@0: } michael@0: michael@0: static Value michael@0: FromTypedPayload(JSValueType type, uintptr_t payload) michael@0: { michael@0: switch (type) { michael@0: case JSVAL_TYPE_INT32: michael@0: return Int32Value(payload); michael@0: case JSVAL_TYPE_BOOLEAN: michael@0: return BooleanValue(!!payload); michael@0: case JSVAL_TYPE_STRING: michael@0: return FromStringPayload(payload); michael@0: case JSVAL_TYPE_OBJECT: michael@0: return FromObjectPayload(payload); michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected type - needs payload"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: SnapshotIterator::allocationReadable(const RValueAllocation &alloc) michael@0: { michael@0: switch (alloc.mode()) { michael@0: case RValueAllocation::DOUBLE_REG: michael@0: return hasRegister(alloc.fpuReg()); michael@0: michael@0: case RValueAllocation::TYPED_REG: michael@0: return hasRegister(alloc.reg2()); michael@0: michael@0: #if defined(JS_NUNBOX32) michael@0: case RValueAllocation::UNTYPED_REG_REG: michael@0: return hasRegister(alloc.reg()) && hasRegister(alloc.reg2()); michael@0: case RValueAllocation::UNTYPED_REG_STACK: michael@0: return hasRegister(alloc.reg()) && hasStack(alloc.stackOffset2()); michael@0: case RValueAllocation::UNTYPED_STACK_REG: michael@0: return hasStack(alloc.stackOffset()) && hasRegister(alloc.reg2()); michael@0: case RValueAllocation::UNTYPED_STACK_STACK: michael@0: return hasStack(alloc.stackOffset()) && hasStack(alloc.stackOffset2()); michael@0: #elif defined(JS_PUNBOX64) michael@0: case RValueAllocation::UNTYPED_REG: michael@0: return hasRegister(alloc.reg()); michael@0: case RValueAllocation::UNTYPED_STACK: michael@0: return hasStack(alloc.stackOffset()); michael@0: #endif michael@0: michael@0: default: michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: Value michael@0: SnapshotIterator::allocationValue(const RValueAllocation &alloc) michael@0: { michael@0: switch (alloc.mode()) { michael@0: case RValueAllocation::CONSTANT: michael@0: return ionScript_->getConstant(alloc.index()); michael@0: michael@0: case RValueAllocation::CST_UNDEFINED: michael@0: return UndefinedValue(); michael@0: michael@0: case RValueAllocation::CST_NULL: michael@0: return NullValue(); michael@0: michael@0: case RValueAllocation::DOUBLE_REG: michael@0: return DoubleValue(fromRegister(alloc.fpuReg())); michael@0: michael@0: case RValueAllocation::FLOAT32_REG: michael@0: { michael@0: union { michael@0: double d; michael@0: float f; michael@0: } pun; michael@0: pun.d = fromRegister(alloc.fpuReg()); michael@0: // The register contains the encoding of a float32. We just read michael@0: // the bits without making any conversion. michael@0: return Float32Value(pun.f); michael@0: } michael@0: michael@0: case RValueAllocation::FLOAT32_STACK: michael@0: return Float32Value(ReadFrameFloat32Slot(fp_, alloc.stackOffset())); michael@0: michael@0: case RValueAllocation::TYPED_REG: michael@0: return FromTypedPayload(alloc.knownType(), fromRegister(alloc.reg2())); michael@0: michael@0: case RValueAllocation::TYPED_STACK: michael@0: { michael@0: switch (alloc.knownType()) { michael@0: case JSVAL_TYPE_DOUBLE: michael@0: return DoubleValue(ReadFrameDoubleSlot(fp_, alloc.stackOffset2())); michael@0: case JSVAL_TYPE_INT32: michael@0: return Int32Value(ReadFrameInt32Slot(fp_, alloc.stackOffset2())); michael@0: case JSVAL_TYPE_BOOLEAN: michael@0: return BooleanValue(ReadFrameBooleanSlot(fp_, alloc.stackOffset2())); michael@0: case JSVAL_TYPE_STRING: michael@0: return FromStringPayload(fromStack(alloc.stackOffset2())); michael@0: case JSVAL_TYPE_OBJECT: michael@0: return FromObjectPayload(fromStack(alloc.stackOffset2())); michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected type"); michael@0: } michael@0: } michael@0: michael@0: #if defined(JS_NUNBOX32) michael@0: case RValueAllocation::UNTYPED_REG_REG: michael@0: { michael@0: jsval_layout layout; michael@0: layout.s.tag = (JSValueTag) fromRegister(alloc.reg()); michael@0: layout.s.payload.word = fromRegister(alloc.reg2()); michael@0: return IMPL_TO_JSVAL(layout); michael@0: } michael@0: michael@0: case RValueAllocation::UNTYPED_REG_STACK: michael@0: { michael@0: jsval_layout layout; michael@0: layout.s.tag = (JSValueTag) fromRegister(alloc.reg()); michael@0: layout.s.payload.word = fromStack(alloc.stackOffset2()); michael@0: return IMPL_TO_JSVAL(layout); michael@0: } michael@0: michael@0: case RValueAllocation::UNTYPED_STACK_REG: michael@0: { michael@0: jsval_layout layout; michael@0: layout.s.tag = (JSValueTag) fromStack(alloc.stackOffset()); michael@0: layout.s.payload.word = fromRegister(alloc.reg2()); michael@0: return IMPL_TO_JSVAL(layout); michael@0: } michael@0: michael@0: case RValueAllocation::UNTYPED_STACK_STACK: michael@0: { michael@0: jsval_layout layout; michael@0: layout.s.tag = (JSValueTag) fromStack(alloc.stackOffset()); michael@0: layout.s.payload.word = fromStack(alloc.stackOffset2()); michael@0: return IMPL_TO_JSVAL(layout); michael@0: } michael@0: #elif defined(JS_PUNBOX64) michael@0: case RValueAllocation::UNTYPED_REG: michael@0: { michael@0: jsval_layout layout; michael@0: layout.asBits = fromRegister(alloc.reg()); michael@0: return IMPL_TO_JSVAL(layout); michael@0: } michael@0: michael@0: case RValueAllocation::UNTYPED_STACK: michael@0: { michael@0: jsval_layout layout; michael@0: layout.asBits = fromStack(alloc.stackOffset()); michael@0: return IMPL_TO_JSVAL(layout); michael@0: } michael@0: #endif michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("huh?"); michael@0: } michael@0: } michael@0: michael@0: const RResumePoint * michael@0: SnapshotIterator::resumePoint() const michael@0: { michael@0: return instruction()->toResumePoint(); michael@0: } michael@0: michael@0: uint32_t michael@0: SnapshotIterator::numAllocations() const michael@0: { michael@0: return resumePoint()->numOperands(); michael@0: } michael@0: michael@0: uint32_t michael@0: SnapshotIterator::pcOffset() const michael@0: { michael@0: return resumePoint()->pcOffset(); michael@0: } michael@0: michael@0: void michael@0: SnapshotIterator::skipInstruction() michael@0: { michael@0: MOZ_ASSERT(snapshot_.numAllocationsRead() == 0); michael@0: size_t numOperands = instruction()->numOperands(); michael@0: for (size_t i = 0; i < numOperands; i++) michael@0: skip(); michael@0: nextInstruction(); michael@0: } michael@0: michael@0: void michael@0: SnapshotIterator::nextFrame() michael@0: { michael@0: nextInstruction(); michael@0: while (!instruction()->isResumePoint()) michael@0: skipInstruction(); michael@0: } michael@0: michael@0: IonScript * michael@0: JitFrameIterator::ionScript() const michael@0: { michael@0: JS_ASSERT(type() == JitFrame_IonJS); michael@0: michael@0: IonScript *ionScript = nullptr; michael@0: if (checkInvalidation(&ionScript)) michael@0: return ionScript; michael@0: switch (GetCalleeTokenTag(calleeToken())) { michael@0: case CalleeToken_Function: michael@0: case CalleeToken_Script: michael@0: return mode_ == ParallelExecution ? script()->parallelIonScript() : script()->ionScript(); michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unknown callee token type"); michael@0: } michael@0: } michael@0: michael@0: const SafepointIndex * michael@0: JitFrameIterator::safepoint() const michael@0: { michael@0: if (!cachedSafepointIndex_) michael@0: cachedSafepointIndex_ = ionScript()->getSafepointIndex(returnAddressToFp()); michael@0: return cachedSafepointIndex_; michael@0: } michael@0: michael@0: const OsiIndex * michael@0: JitFrameIterator::osiIndex() const michael@0: { michael@0: SafepointReader reader(ionScript(), safepoint()); michael@0: return ionScript()->getOsiIndex(reader.osiReturnPointOffset()); michael@0: } michael@0: michael@0: template michael@0: void michael@0: InlineFrameIteratorMaybeGC::resetOn(const JitFrameIterator *iter) michael@0: { michael@0: frame_ = iter; michael@0: framesRead_ = 0; michael@0: frameCount_ = UINT32_MAX; michael@0: michael@0: if (iter) { michael@0: start_ = SnapshotIterator(*iter); michael@0: findNextFrame(); michael@0: } michael@0: } michael@0: template void InlineFrameIteratorMaybeGC::resetOn(const JitFrameIterator *iter); michael@0: template void InlineFrameIteratorMaybeGC::resetOn(const JitFrameIterator *iter); michael@0: michael@0: template michael@0: void michael@0: InlineFrameIteratorMaybeGC::findNextFrame() michael@0: { michael@0: JS_ASSERT(more()); michael@0: michael@0: si_ = start_; michael@0: michael@0: // Read the initial frame out of the C stack. michael@0: callee_ = frame_->maybeCallee(); michael@0: script_ = frame_->script(); michael@0: MOZ_ASSERT(script_->hasBaselineScript()); michael@0: michael@0: // Settle on the outermost frame without evaluating any instructions before michael@0: // looking for a pc. michael@0: if (!si_.instruction()->isResumePoint()) michael@0: si_.nextFrame(); michael@0: michael@0: pc_ = script_->offsetToPC(si_.pcOffset()); michael@0: #ifdef DEBUG michael@0: numActualArgs_ = 0xbadbad; michael@0: #endif michael@0: michael@0: // This unfortunately is O(n*m), because we must skip over outer frames michael@0: // before reading inner ones. michael@0: michael@0: // The first time (frameCount_ == UINT32_MAX) we do not know the number of michael@0: // frames that we are going to inspect. So we are iterating until there is michael@0: // no more frames, to settle on the inner most frame and to count the number michael@0: // of frames. michael@0: size_t remaining = (frameCount_ != UINT32_MAX) ? frameNo() - 1 : SIZE_MAX; michael@0: michael@0: size_t i = 1; michael@0: for (; i <= remaining && si_.moreFrames(); i++) { michael@0: JS_ASSERT(IsIonInlinablePC(pc_)); michael@0: michael@0: // Recover the number of actual arguments from the script. michael@0: if (JSOp(*pc_) != JSOP_FUNAPPLY) michael@0: numActualArgs_ = GET_ARGC(pc_); michael@0: if (JSOp(*pc_) == JSOP_FUNCALL) { michael@0: JS_ASSERT(GET_ARGC(pc_) > 0); michael@0: numActualArgs_ = GET_ARGC(pc_) - 1; michael@0: } else if (IsGetPropPC(pc_)) { michael@0: numActualArgs_ = 0; michael@0: } else if (IsSetPropPC(pc_)) { michael@0: numActualArgs_ = 1; michael@0: } michael@0: michael@0: JS_ASSERT(numActualArgs_ != 0xbadbad); michael@0: michael@0: // Skip over non-argument slots, as well as |this|. michael@0: unsigned skipCount = (si_.numAllocations() - 1) - numActualArgs_ - 1; michael@0: for (unsigned j = 0; j < skipCount; j++) michael@0: si_.skip(); michael@0: michael@0: // The JSFunction is a constant, otherwise we would not have inlined it. michael@0: Value funval = si_.read(); michael@0: michael@0: // Skip extra value allocations. michael@0: while (si_.moreAllocations()) michael@0: si_.skip(); michael@0: michael@0: si_.nextFrame(); michael@0: michael@0: callee_ = &funval.toObject().as(); michael@0: michael@0: // Inlined functions may be clones that still point to the lazy script michael@0: // for the executed script, if they are clones. The actual script michael@0: // exists though, just make sure the function points to it. michael@0: script_ = callee_->existingScriptForInlinedFunction(); michael@0: MOZ_ASSERT(script_->hasBaselineScript()); michael@0: michael@0: pc_ = script_->offsetToPC(si_.pcOffset()); michael@0: } michael@0: michael@0: // The first time we do not know the number of frames, we only settle on the michael@0: // last frame, and update the number of frames based on the number of michael@0: // iteration that we have done. michael@0: if (frameCount_ == UINT32_MAX) { michael@0: MOZ_ASSERT(!si_.moreFrames()); michael@0: frameCount_ = i; michael@0: } michael@0: michael@0: framesRead_++; michael@0: } michael@0: template void InlineFrameIteratorMaybeGC::findNextFrame(); michael@0: template void InlineFrameIteratorMaybeGC::findNextFrame(); michael@0: michael@0: template michael@0: bool michael@0: InlineFrameIteratorMaybeGC::isFunctionFrame() const michael@0: { michael@0: return !!callee_; michael@0: } michael@0: template bool InlineFrameIteratorMaybeGC::isFunctionFrame() const; michael@0: template bool InlineFrameIteratorMaybeGC::isFunctionFrame() const; michael@0: michael@0: MachineState michael@0: MachineState::FromBailout(mozilla::Array ®s, michael@0: mozilla::Array &fpregs) michael@0: { michael@0: MachineState machine; michael@0: michael@0: for (unsigned i = 0; i < Registers::Total; i++) michael@0: machine.setRegisterLocation(Register::FromCode(i), ®s[i]); michael@0: for (unsigned i = 0; i < FloatRegisters::Total; i++) michael@0: machine.setRegisterLocation(FloatRegister::FromCode(i), &fpregs[i]); michael@0: michael@0: return machine; michael@0: } michael@0: michael@0: template michael@0: bool michael@0: InlineFrameIteratorMaybeGC::isConstructing() const michael@0: { michael@0: // Skip the current frame and look at the caller's. michael@0: if (more()) { michael@0: InlineFrameIteratorMaybeGC parent(GetJSContextFromJitCode(), this); michael@0: ++parent; michael@0: michael@0: // Inlined Getters and Setters are never constructing. michael@0: if (IsGetPropPC(parent.pc()) || IsSetPropPC(parent.pc())) michael@0: return false; michael@0: michael@0: // In the case of a JS frame, look up the pc from the snapshot. michael@0: JS_ASSERT(IsCallPC(parent.pc())); michael@0: michael@0: return (JSOp)*parent.pc() == JSOP_NEW; michael@0: } michael@0: michael@0: return frame_->isConstructing(); michael@0: } michael@0: template bool InlineFrameIteratorMaybeGC::isConstructing() const; michael@0: template bool InlineFrameIteratorMaybeGC::isConstructing() const; michael@0: michael@0: bool michael@0: JitFrameIterator::isConstructing() const michael@0: { michael@0: JitFrameIterator parent(*this); michael@0: michael@0: // Skip the current frame and look at the caller's. michael@0: do { michael@0: ++parent; michael@0: } while (!parent.done() && !parent.isScripted()); michael@0: michael@0: if (parent.isIonJS()) { michael@0: // In the case of a JS frame, look up the pc from the snapshot. michael@0: InlineFrameIterator inlinedParent(GetJSContextFromJitCode(), &parent); michael@0: michael@0: //Inlined Getters and Setters are never constructing. michael@0: if (IsGetPropPC(inlinedParent.pc()) || IsSetPropPC(inlinedParent.pc())) michael@0: return false; michael@0: michael@0: JS_ASSERT(IsCallPC(inlinedParent.pc())); michael@0: michael@0: return (JSOp)*inlinedParent.pc() == JSOP_NEW; michael@0: } michael@0: michael@0: if (parent.isBaselineJS()) { michael@0: jsbytecode *pc; michael@0: parent.baselineScriptAndPc(nullptr, &pc); michael@0: michael@0: // Inlined Getters and Setters are never constructing. michael@0: // Baseline may call getters from [GET|SET]PROP or [GET|SET]ELEM ops. michael@0: if (IsGetPropPC(pc) || IsSetPropPC(pc) || IsGetElemPC(pc) || IsSetElemPC(pc)) michael@0: return false; michael@0: michael@0: JS_ASSERT(IsCallPC(pc)); michael@0: michael@0: return JSOp(*pc) == JSOP_NEW; michael@0: } michael@0: michael@0: JS_ASSERT(parent.done()); michael@0: return activation_->firstFrameIsConstructing(); michael@0: } michael@0: michael@0: unsigned michael@0: JitFrameIterator::numActualArgs() const michael@0: { michael@0: if (isScripted()) michael@0: return jsFrame()->numActualArgs(); michael@0: michael@0: JS_ASSERT(isNative()); michael@0: return exitFrame()->nativeExit()->argc(); michael@0: } michael@0: michael@0: void michael@0: SnapshotIterator::warnUnreadableAllocation() michael@0: { michael@0: fprintf(stderr, "Warning! Tried to access unreadable value allocation (possible f.arguments).\n"); michael@0: } michael@0: michael@0: struct DumpOp { michael@0: DumpOp(unsigned int i) : i_(i) {} michael@0: michael@0: unsigned int i_; michael@0: void operator()(const Value& v) { michael@0: fprintf(stderr, " actual (arg %d): ", i_); michael@0: #ifdef DEBUG michael@0: js_DumpValue(v); michael@0: #else michael@0: fprintf(stderr, "?\n"); michael@0: #endif michael@0: i_++; michael@0: } michael@0: }; michael@0: michael@0: void michael@0: JitFrameIterator::dumpBaseline() const michael@0: { michael@0: JS_ASSERT(isBaselineJS()); michael@0: michael@0: fprintf(stderr, " JS Baseline frame\n"); michael@0: if (isFunctionFrame()) { michael@0: fprintf(stderr, " callee fun: "); michael@0: #ifdef DEBUG michael@0: js_DumpObject(callee()); michael@0: #else michael@0: fprintf(stderr, "?\n"); michael@0: #endif michael@0: } else { michael@0: fprintf(stderr, " global frame, no callee\n"); michael@0: } michael@0: michael@0: fprintf(stderr, " file %s line %u\n", michael@0: script()->filename(), (unsigned) script()->lineno()); michael@0: michael@0: JSContext *cx = GetJSContextFromJitCode(); michael@0: RootedScript script(cx); michael@0: jsbytecode *pc; michael@0: baselineScriptAndPc(script.address(), &pc); michael@0: michael@0: fprintf(stderr, " script = %p, pc = %p (offset %u)\n", (void *)script, pc, uint32_t(script->pcToOffset(pc))); michael@0: fprintf(stderr, " current op: %s\n", js_CodeName[*pc]); michael@0: michael@0: fprintf(stderr, " actual args: %d\n", numActualArgs()); michael@0: michael@0: BaselineFrame *frame = baselineFrame(); michael@0: michael@0: for (unsigned i = 0; i < frame->numValueSlots(); i++) { michael@0: fprintf(stderr, " slot %u: ", i); michael@0: #ifdef DEBUG michael@0: Value *v = frame->valueSlot(i); michael@0: js_DumpValue(*v); michael@0: #else michael@0: fprintf(stderr, "?\n"); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: template michael@0: void michael@0: InlineFrameIteratorMaybeGC::dump() const michael@0: { michael@0: if (more()) michael@0: fprintf(stderr, " JS frame (inlined)\n"); michael@0: else michael@0: fprintf(stderr, " JS frame\n"); michael@0: michael@0: bool isFunction = false; michael@0: if (isFunctionFrame()) { michael@0: isFunction = true; michael@0: fprintf(stderr, " callee fun: "); michael@0: #ifdef DEBUG michael@0: js_DumpObject(callee()); michael@0: #else michael@0: fprintf(stderr, "?\n"); michael@0: #endif michael@0: } else { michael@0: fprintf(stderr, " global frame, no callee\n"); michael@0: } michael@0: michael@0: fprintf(stderr, " file %s line %u\n", michael@0: script()->filename(), (unsigned) script()->lineno()); michael@0: michael@0: fprintf(stderr, " script = %p, pc = %p\n", (void*) script(), pc()); michael@0: fprintf(stderr, " current op: %s\n", js_CodeName[*pc()]); michael@0: michael@0: if (!more()) { michael@0: numActualArgs(); michael@0: } michael@0: michael@0: SnapshotIterator si = snapshotIterator(); michael@0: fprintf(stderr, " slots: %u\n", si.numAllocations() - 1); michael@0: for (unsigned i = 0; i < si.numAllocations() - 1; i++) { michael@0: if (isFunction) { michael@0: if (i == 0) michael@0: fprintf(stderr, " scope chain: "); michael@0: else if (i == 1) michael@0: fprintf(stderr, " this: "); michael@0: else if (i - 2 < callee()->nargs()) michael@0: fprintf(stderr, " formal (arg %d): ", i - 2); michael@0: else { michael@0: if (i - 2 == callee()->nargs() && numActualArgs() > callee()->nargs()) { michael@0: DumpOp d(callee()->nargs()); michael@0: unaliasedForEachActual(GetJSContextFromJitCode(), d, ReadFrame_Overflown); michael@0: } michael@0: michael@0: fprintf(stderr, " slot %d: ", int(i - 2 - callee()->nargs())); michael@0: } michael@0: } else michael@0: fprintf(stderr, " slot %u: ", i); michael@0: #ifdef DEBUG michael@0: js_DumpValue(si.maybeRead()); michael@0: #else michael@0: fprintf(stderr, "?\n"); michael@0: #endif michael@0: } michael@0: michael@0: fputc('\n', stderr); michael@0: } michael@0: template void InlineFrameIteratorMaybeGC::dump() const; michael@0: template void InlineFrameIteratorMaybeGC::dump() const; michael@0: michael@0: void michael@0: JitFrameIterator::dump() const michael@0: { michael@0: switch (type_) { michael@0: case JitFrame_Entry: michael@0: fprintf(stderr, " Entry frame\n"); michael@0: fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); michael@0: break; michael@0: case JitFrame_BaselineJS: michael@0: dumpBaseline(); michael@0: break; michael@0: case JitFrame_BaselineStub: michael@0: case JitFrame_Unwound_BaselineStub: michael@0: fprintf(stderr, " Baseline stub frame\n"); michael@0: fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); michael@0: break; michael@0: case JitFrame_IonJS: michael@0: { michael@0: InlineFrameIterator frames(GetJSContextFromJitCode(), this); michael@0: for (;;) { michael@0: frames.dump(); michael@0: if (!frames.more()) michael@0: break; michael@0: ++frames; michael@0: } michael@0: break; michael@0: } michael@0: case JitFrame_Rectifier: michael@0: case JitFrame_Unwound_Rectifier: michael@0: fprintf(stderr, " Rectifier frame\n"); michael@0: fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); michael@0: break; michael@0: case JitFrame_Unwound_IonJS: michael@0: fprintf(stderr, "Warning! Unwound JS frames are not observable.\n"); michael@0: break; michael@0: case JitFrame_Exit: michael@0: break; michael@0: }; michael@0: fputc('\n', stderr); michael@0: } michael@0: michael@0: IonJSFrameLayout * michael@0: InvalidationBailoutStack::fp() const michael@0: { michael@0: return (IonJSFrameLayout *) (sp() + ionScript_->frameSize()); michael@0: } michael@0: michael@0: void michael@0: InvalidationBailoutStack::checkInvariants() const michael@0: { michael@0: #ifdef DEBUG michael@0: IonJSFrameLayout *frame = fp(); michael@0: CalleeToken token = frame->calleeToken(); michael@0: JS_ASSERT(token); michael@0: michael@0: uint8_t *rawBase = ionScript()->method()->raw(); michael@0: uint8_t *rawLimit = rawBase + ionScript()->method()->instructionsSize(); michael@0: uint8_t *osiPoint = osiPointReturnAddress(); michael@0: JS_ASSERT(rawBase <= osiPoint && osiPoint <= rawLimit); michael@0: #endif michael@0: } michael@0: michael@0: } // namespace jit michael@0: } // namespace js