LCOV - code coverage report
Current view: top level - js/src/jit - Bailouts.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 129 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 9 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "jit/Bailouts.h"
       8             : 
       9             : #include "mozilla/ScopeExit.h"
      10             : 
      11             : #include "jscntxt.h"
      12             : 
      13             : #include "jit/BaselineJIT.h"
      14             : #include "jit/Ion.h"
      15             : #include "jit/JitCompartment.h"
      16             : #include "jit/JitSpewer.h"
      17             : #include "jit/Snapshots.h"
      18             : #include "vm/TraceLogging.h"
      19             : 
      20             : #include "jit/JitFrameIterator-inl.h"
      21             : #include "vm/Probes-inl.h"
      22             : #include "vm/Stack-inl.h"
      23             : 
      24             : using namespace js;
      25             : using namespace js::jit;
      26             : 
      27             : using mozilla::IsInRange;
      28             : 
      29             : uint32_t
      30           0 : jit::Bailout(BailoutStack* sp, BaselineBailoutInfo** bailoutInfo)
      31             : {
      32           0 :     JSContext* cx = TlsContext.get();
      33           0 :     MOZ_ASSERT(bailoutInfo);
      34             : 
      35             :     // We don't have an exit frame.
      36           0 :     MOZ_ASSERT(IsInRange(FAKE_EXITFP_FOR_BAILOUT, 0, 0x1000) &&
      37             :                IsInRange(FAKE_EXITFP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000),
      38             :                "Fake exitfp pointer should be within the first page.");
      39             : 
      40           0 :     cx->activation()->asJit()->setExitFP(FAKE_EXITFP_FOR_BAILOUT);
      41             : 
      42           0 :     JitActivationIterator jitActivations(cx);
      43           0 :     BailoutFrameInfo bailoutData(jitActivations, sp);
      44           0 :     JitFrameIterator iter(jitActivations);
      45           0 :     MOZ_ASSERT(!iter.ionScript()->invalidated());
      46           0 :     CommonFrameLayout* currentFramePtr = iter.current();
      47             : 
      48           0 :     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
      49           0 :     TraceLogTimestamp(logger, TraceLogger_Bailout);
      50             : 
      51           0 :     JitSpew(JitSpew_IonBailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());
      52             : 
      53           0 :     MOZ_ASSERT(IsBaselineEnabled(cx));
      54             : 
      55           0 :     *bailoutInfo = nullptr;
      56           0 :     uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo,
      57           0 :                                            /* excInfo = */ nullptr);
      58           0 :     MOZ_ASSERT(retval == BAILOUT_RETURN_OK ||
      59             :                retval == BAILOUT_RETURN_FATAL_ERROR ||
      60             :                retval == BAILOUT_RETURN_OVERRECURSED);
      61           0 :     MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);
      62             : 
      63           0 :     if (retval != BAILOUT_RETURN_OK) {
      64           0 :         JSScript* script = iter.script();
      65           0 :         probes::ExitScript(cx, script, script->functionNonDelazifying(),
      66           0 :                            /* popProfilerFrame = */ false);
      67             :     }
      68             : 
      69             :     // This condition was wrong when we entered this bailout function, but it
      70             :     // might be true now. A GC might have reclaimed all the Jit code and
      71             :     // invalidated all frames which are currently on the stack. As we are
      72             :     // already in a bailout, we could not switch to an invalidation
      73             :     // bailout. When the code of an IonScript which is on the stack is
      74             :     // invalidated (see InvalidateActivation), we remove references to it and
      75             :     // increment the reference counter for each activation that appear on the
      76             :     // stack. As the bailed frame is one of them, we have to decrement it now.
      77           0 :     if (iter.ionScript()->invalidated())
      78           0 :         iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp());
      79             : 
      80             :     // NB: Commentary on how |lastProfilingFrame| is set from bailouts.
      81             :     //
      82             :     // Once we return to jitcode, any following frames might get clobbered,
      83             :     // but the current frame will not (as it will be clobbered "in-place"
      84             :     // with a baseline frame that will share the same frame prefix).
      85             :     // However, there may be multiple baseline frames unpacked from this
      86             :     // single Ion frame, which means we will need to once again reset
      87             :     // |lastProfilingFrame| to point to the correct unpacked last frame
      88             :     // in |FinishBailoutToBaseline|.
      89             :     //
      90             :     // In the case of error, the jitcode will jump immediately to an
      91             :     // exception handler, which will unwind the frames and properly set
      92             :     // the |lastProfilingFrame| to point to the frame being resumed into
      93             :     // (see |AutoResetLastProfilerFrameOnReturnFromException|).
      94             :     //
      95             :     // In both cases, we want to temporarily set the |lastProfilingFrame|
      96             :     // to the current frame being bailed out, and then fix it up later.
      97           0 :     if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
      98           0 :         cx->jitActivation->setLastProfilingFrame(currentFramePtr);
      99             : 
     100           0 :     return retval;
     101             : }
     102             : 
     103             : uint32_t
     104           0 : jit::InvalidationBailout(InvalidationBailoutStack* sp, size_t* frameSizeOut,
     105             :                          BaselineBailoutInfo** bailoutInfo)
     106             : {
     107           0 :     sp->checkInvariants();
     108             : 
     109           0 :     JSContext* cx = TlsContext.get();
     110             : 
     111             :     // We don't have an exit frame.
     112           0 :     cx->activation()->asJit()->setExitFP(FAKE_EXITFP_FOR_BAILOUT);
     113             : 
     114           0 :     JitActivationIterator jitActivations(cx);
     115           0 :     BailoutFrameInfo bailoutData(jitActivations, sp);
     116           0 :     JitFrameIterator iter(jitActivations);
     117           0 :     CommonFrameLayout* currentFramePtr = iter.current();
     118             : 
     119           0 :     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
     120           0 :     TraceLogTimestamp(logger, TraceLogger_Invalidation);
     121             : 
     122           0 :     JitSpew(JitSpew_IonBailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());
     123             : 
     124             :     // Note: the frame size must be computed before we return from this function.
     125           0 :     *frameSizeOut = iter.frameSize();
     126             : 
     127           0 :     MOZ_ASSERT(IsBaselineEnabled(cx));
     128             : 
     129           0 :     *bailoutInfo = nullptr;
     130           0 :     uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, bailoutInfo,
     131           0 :                                            /* excInfo = */ nullptr);
     132           0 :     MOZ_ASSERT(retval == BAILOUT_RETURN_OK ||
     133             :                retval == BAILOUT_RETURN_FATAL_ERROR ||
     134             :                retval == BAILOUT_RETURN_OVERRECURSED);
     135           0 :     MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);
     136             : 
     137           0 :     if (retval != BAILOUT_RETURN_OK) {
     138             :         // If the bailout failed, then bailout trampoline will pop the
     139             :         // current frame and jump straight to exception handling code when
     140             :         // this function returns.  Any Gecko Profiler entry pushed for this
     141             :         // frame will be silently forgotten.
     142             :         //
     143             :         // We call ExitScript here to ensure that if the ionScript had Gecko
     144             :         // Profiler instrumentation, then the entry for it is popped.
     145             :         //
     146             :         // However, if the bailout was during argument check, then a
     147             :         // pseudostack frame would not have been pushed in the first
     148             :         // place, so don't pop anything in that case.
     149           0 :         JSScript* script = iter.script();
     150           0 :         probes::ExitScript(cx, script, script->functionNonDelazifying(),
     151           0 :                            /* popProfilerFrame = */ false);
     152             : 
     153             : #ifdef JS_JITSPEW
     154           0 :         JitFrameLayout* frame = iter.jsFrame();
     155           0 :         JitSpew(JitSpew_IonInvalidate, "Bailout failed (%s)",
     156           0 :                 (retval == BAILOUT_RETURN_FATAL_ERROR) ? "Fatal Error" : "Over Recursion");
     157           0 :         JitSpew(JitSpew_IonInvalidate, "   calleeToken %p", (void*) frame->calleeToken());
     158           0 :         JitSpew(JitSpew_IonInvalidate, "   frameSize %u", unsigned(frame->prevFrameLocalSize()));
     159           0 :         JitSpew(JitSpew_IonInvalidate, "   ra %p", (void*) frame->returnAddress());
     160             : #endif
     161             :     }
     162             : 
     163           0 :     iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp());
     164             : 
     165             :     // Make the frame being bailed out the top profiled frame.
     166           0 :     if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
     167           0 :         cx->jitActivation->setLastProfilingFrame(currentFramePtr);
     168             : 
     169           0 :     return retval;
     170             : }
     171             : 
     172           0 : BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& activations,
     173           0 :                                    const JitFrameIterator& frame)
     174           0 :   : machine_(frame.machineState())
     175             : {
     176           0 :     framePointer_ = (uint8_t*) frame.fp();
     177           0 :     topFrameSize_ = frame.frameSize();
     178           0 :     topIonScript_ = frame.ionScript();
     179           0 :     attachOnJitActivation(activations);
     180             : 
     181           0 :     const OsiIndex* osiIndex = frame.osiIndex();
     182           0 :     snapshotOffset_ = osiIndex->snapshotOffset();
     183           0 : }
     184             : 
     185             : uint32_t
     186           0 : jit::ExceptionHandlerBailout(JSContext* cx, const InlineFrameIterator& frame,
     187             :                              ResumeFromException* rfe,
     188             :                              const ExceptionBailoutInfo& excInfo,
     189             :                              bool* overrecursed)
     190             : {
     191             :     // We can be propagating debug mode exceptions without there being an
     192             :     // actual exception pending. For instance, when we return false from an
     193             :     // operation callback like a timeout handler.
     194           0 :     MOZ_ASSERT_IF(!excInfo.propagatingIonExceptionForDebugMode(), cx->isExceptionPending());
     195             : 
     196           0 :     JitActivation* act = cx->activation()->asJit();
     197           0 :     uint8_t* prevExitFP = act->exitFP();
     198           0 :     auto restoreExitFP = mozilla::MakeScopeExit([&]() { act->setExitFP(prevExitFP); });
     199           0 :     act->setExitFP(FAKE_EXITFP_FOR_BAILOUT);
     200             : 
     201           0 :     gc::AutoSuppressGC suppress(cx);
     202             : 
     203           0 :     JitActivationIterator jitActivations(cx);
     204           0 :     BailoutFrameInfo bailoutData(jitActivations, frame.frame());
     205           0 :     JitFrameIterator iter(jitActivations);
     206           0 :     CommonFrameLayout* currentFramePtr = iter.current();
     207             : 
     208           0 :     BaselineBailoutInfo* bailoutInfo = nullptr;
     209             :     uint32_t retval;
     210             : 
     211             :     {
     212             :         // Currently we do not tolerate OOM here so as not to complicate the
     213             :         // exception handling code further.
     214           0 :         AutoEnterOOMUnsafeRegion oomUnsafe;
     215             : 
     216           0 :         retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true,
     217           0 :                                       &bailoutInfo, &excInfo);
     218           0 :         if (retval == BAILOUT_RETURN_FATAL_ERROR && cx->isThrowingOutOfMemory())
     219           0 :             oomUnsafe.crash("ExceptionHandlerBailout");
     220             :     }
     221             : 
     222           0 :     if (retval == BAILOUT_RETURN_OK) {
     223           0 :         MOZ_ASSERT(bailoutInfo);
     224             : 
     225             :         // Overwrite the kind so HandleException after the bailout returns
     226             :         // false, jumping directly to the exception tail.
     227           0 :         if (excInfo.propagatingIonExceptionForDebugMode())
     228           0 :             bailoutInfo->bailoutKind = Bailout_IonExceptionDebugMode;
     229             : 
     230           0 :         rfe->kind = ResumeFromException::RESUME_BAILOUT;
     231           0 :         rfe->target = cx->runtime()->jitRuntime()->getBailoutTail()->raw();
     232           0 :         rfe->bailoutInfo = bailoutInfo;
     233             :     } else {
     234             :         // Bailout failed. If the overrecursion check failed, clear the
     235             :         // exception to turn this into an uncatchable error, continue popping
     236             :         // all inline frames and have the caller report the error.
     237           0 :         MOZ_ASSERT(!bailoutInfo);
     238             : 
     239           0 :         if (retval == BAILOUT_RETURN_OVERRECURSED) {
     240           0 :             *overrecursed = true;
     241           0 :             if (!excInfo.propagatingIonExceptionForDebugMode())
     242           0 :                 cx->clearPendingException();
     243             :         } else {
     244           0 :             MOZ_ASSERT(retval == BAILOUT_RETURN_FATAL_ERROR);
     245             : 
     246             :             // Crash for now so as not to complicate the exception handling code
     247             :             // further.
     248           0 :             MOZ_CRASH();
     249             :         }
     250             :     }
     251             : 
     252             :     // Make the frame being bailed out the top profiled frame.
     253           0 :     if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
     254           0 :         cx->jitActivation->setLastProfilingFrame(currentFramePtr);
     255             : 
     256           0 :     return retval;
     257             : }
     258             : 
     259             : // Initialize the decl env Object, call object, and any arguments obj of the
     260             : // current frame.
     261             : bool
     262           0 : jit::EnsureHasEnvironmentObjects(JSContext* cx, AbstractFramePtr fp)
     263             : {
     264             :     // Ion does not compile eval scripts.
     265           0 :     MOZ_ASSERT(!fp.isEvalFrame());
     266             : 
     267           0 :     if (fp.isFunctionFrame()) {
     268             :         // Ion does not handle extra var environments due to parameter
     269             :         // expressions yet.
     270           0 :         MOZ_ASSERT(!fp.callee()->needsExtraBodyVarEnvironment());
     271             : 
     272           0 :         if (!fp.hasInitialEnvironment() && fp.callee()->needsFunctionEnvironmentObjects()) {
     273           0 :             if (!fp.initFunctionEnvironmentObjects(cx))
     274           0 :                 return false;
     275             :         }
     276             :     }
     277             : 
     278           0 :     return true;
     279             : }
     280             : 
     281             : void
     282           0 : jit::CheckFrequentBailouts(JSContext* cx, JSScript* script, BailoutKind bailoutKind)
     283             : {
     284           0 :     if (script->hasIonScript()) {
     285             :         // Invalidate if this script keeps bailing out without invalidation. Next time
     286             :         // we compile this script LICM will be disabled.
     287           0 :         IonScript* ionScript = script->ionScript();
     288             : 
     289           0 :         if (ionScript->bailoutExpected()) {
     290             :             // If we bailout because of the first execution of a basic block,
     291             :             // then we should record which basic block we are returning in,
     292             :             // which should prevent this from happening again.  Also note that
     293             :             // the first execution bailout can be related to an inlined script,
     294             :             // so there is no need to penalize the caller.
     295           0 :             if (bailoutKind != Bailout_FirstExecution && !script->hadFrequentBailouts())
     296           0 :                 script->setHadFrequentBailouts();
     297             : 
     298           0 :             JitSpew(JitSpew_IonInvalidate, "Invalidating due to too many bailouts");
     299             : 
     300           0 :             Invalidate(cx, script);
     301             :         }
     302             :     }
     303           0 : }
     304             : 
     305             : void
     306           0 : BailoutFrameInfo::attachOnJitActivation(const JitActivationIterator& jitActivations)
     307             : {
     308           0 :     MOZ_ASSERT(jitActivations.exitFP() == FAKE_EXITFP_FOR_BAILOUT);
     309           0 :     activation_ = jitActivations->asJit();
     310           0 :     activation_->setBailoutData(this);
     311           0 : }
     312             : 
     313           0 : BailoutFrameInfo::~BailoutFrameInfo()
     314             : {
     315           0 :     activation_->cleanBailoutData();
     316           0 : }

Generated by: LCOV version 1.13