Пример #1
0
uint32_t
jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
{
    JSContext *cx = GetJSContextFromJitCode();
    JS_ASSERT(bailoutInfo);

    // We don't have an exit frame.
    MOZ_ASSERT(IsInRange(FAKE_JIT_TOP_FOR_BAILOUT, 0, 0x1000) &&
               IsInRange(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(IonCommonFrameLayout), 0, 0x1000),
               "Fake jitTop pointer should be within the first page.");
    cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
    gc::AutoSuppressGC suppress(cx);

    JitActivationIterator jitActivations(cx->runtime());
    IonBailoutIterator iter(jitActivations, sp);
    JitActivation *activation = jitActivations->asJit();

    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
    TraceLogTimestamp(logger, TraceLogger::Bailout);

    JitSpew(JitSpew_IonBailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());

    JS_ASSERT(IsBaselineEnabled(cx));

    *bailoutInfo = nullptr;
    uint32_t retval = BailoutIonToBaseline(cx, activation, iter, false, bailoutInfo);
    JS_ASSERT(retval == BAILOUT_RETURN_OK ||
              retval == BAILOUT_RETURN_FATAL_ERROR ||
              retval == BAILOUT_RETURN_OVERRECURSED);
    JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);

    if (retval != BAILOUT_RETURN_OK) {
        // If the bailout failed, then bailout trampoline will pop the
        // current frame and jump straight to exception handling code when
        // this function returns.  Any SPS entry pushed for this frame will
        // be silently forgotten.
        //
        // We call ExitScript here to ensure that if the ionScript had SPS
        // instrumentation, then the SPS entry for it is popped.
        //
        // However, if the bailout was during argument check, then a
        // pseudostack frame would not have been pushed in the first
        // place, so don't pop anything in that case.
        bool popSPSFrame = iter.ionScript()->hasSPSInstrumentation() &&
                           (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck);
        JSScript *script = iter.script();
        probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame);

        EnsureExitFrame(iter.jsFrame());
    }

    return retval;
}
Пример #2
0
uint32_t
jit::Bailout(BailoutStack* sp, BaselineBailoutInfo** bailoutInfo)
{
    JSContext* cx = GetJSContextFromJitCode();
    MOZ_ASSERT(bailoutInfo);

    // We don't have an exit frame.
    MOZ_ASSERT(IsInRange(FAKE_JIT_TOP_FOR_BAILOUT, 0, 0x1000) &&
               IsInRange(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000),
               "Fake jitTop pointer should be within the first page.");
    cx->runtime()->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;

    JitActivationIterator jitActivations(cx->runtime());
    BailoutFrameInfo bailoutData(jitActivations, sp);
    JitFrameIterator iter(jitActivations);
    MOZ_ASSERT(!iter.ionScript()->invalidated());
    CommonFrameLayout* currentFramePtr = iter.current();

    TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
    TraceLogTimestamp(logger, TraceLogger_Bailout);

    JitSpew(JitSpew_IonBailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());

    MOZ_ASSERT(IsBaselineEnabled(cx));

    *bailoutInfo = nullptr;
    uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo,
                                           /* excInfo = */ nullptr);
    MOZ_ASSERT(retval == BAILOUT_RETURN_OK ||
               retval == BAILOUT_RETURN_FATAL_ERROR ||
               retval == BAILOUT_RETURN_OVERRECURSED);
    MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);

    if (retval != BAILOUT_RETURN_OK) {
        JSScript* script = iter.script();
        probes::ExitScript(cx, script, script->functionNonDelazifying(),
                           /* popSPSFrame = */ false);

        EnsureExitFrame(iter.jsFrame());
    }

    // This condition was wrong when we entered this bailout function, but it
    // might be true now. A GC might have reclaimed all the Jit code and
    // invalidated all frames which are currently on the stack. As we are
    // already in a bailout, we could not switch to an invalidation
    // bailout. When the code of an IonScript which is on the stack is
    // invalidated (see InvalidateActivation), we remove references to it and
    // increment the reference counter for each activation that appear on the
    // stack. As the bailed frame is one of them, we have to decrement it now.
    if (iter.ionScript()->invalidated())
        iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp());

    // NB: Commentary on how |lastProfilingFrame| is set from bailouts.
    //
    // Once we return to jitcode, any following frames might get clobbered,
    // but the current frame will not (as it will be clobbered "in-place"
    // with a baseline frame that will share the same frame prefix).
    // However, there may be multiple baseline frames unpacked from this
    // single Ion frame, which means we will need to once again reset
    // |lastProfilingFrame| to point to the correct unpacked last frame
    // in |FinishBailoutToBaseline|.
    //
    // In the case of error, the jitcode will jump immediately to an
    // exception handler, which will unwind the frames and properly set
    // the |lastProfilingFrame| to point to the frame being resumed into
    // (see |AutoResetLastProfilerFrameOnReturnFromException|).
    //
    // In both cases, we want to temporarily set the |lastProfilingFrame|
    // to the current frame being bailed out, and then fix it up later.
    if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
        cx->runtime()->jitActivation->setLastProfilingFrame(currentFramePtr);

    return retval;
}
Пример #3
0
uint32_t
jit::InvalidationBailout(InvalidationBailoutStack* sp, size_t* frameSizeOut,
                         BaselineBailoutInfo** bailoutInfo)
{
    sp->checkInvariants();

    JSContext* cx = GetJSContextFromJitCode();

    // We don't have an exit frame.
    cx->runtime()->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;

    JitActivationIterator jitActivations(cx->runtime());
    BailoutFrameInfo bailoutData(jitActivations, sp);
    JitFrameIterator iter(jitActivations);
    CommonFrameLayout* currentFramePtr = iter.current();

    TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
    TraceLogTimestamp(logger, TraceLogger_Invalidation);

    JitSpew(JitSpew_IonBailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());

    // Note: the frame size must be computed before we return from this function.
    *frameSizeOut = iter.frameSize();

    MOZ_ASSERT(IsBaselineEnabled(cx));

    *bailoutInfo = nullptr;
    uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, bailoutInfo,
                                           /* excInfo = */ nullptr);
    MOZ_ASSERT(retval == BAILOUT_RETURN_OK ||
               retval == BAILOUT_RETURN_FATAL_ERROR ||
               retval == BAILOUT_RETURN_OVERRECURSED);
    MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);

    if (retval != BAILOUT_RETURN_OK) {
        // If the bailout failed, then bailout trampoline will pop the
        // current frame and jump straight to exception handling code when
        // this function returns.  Any SPS entry pushed for this frame will
        // be silently forgotten.
        //
        // We call ExitScript here to ensure that if the ionScript had SPS
        // instrumentation, then the SPS entry for it is popped.
        //
        // However, if the bailout was during argument check, then a
        // pseudostack frame would not have been pushed in the first
        // place, so don't pop anything in that case.
        JSScript* script = iter.script();
        probes::ExitScript(cx, script, script->functionNonDelazifying(),
                           /* popSPSFrame = */ false);

        JitFrameLayout* frame = iter.jsFrame();
        JitSpew(JitSpew_IonInvalidate, "Bailout failed (%s): converting to exit frame",
                (retval == BAILOUT_RETURN_FATAL_ERROR) ? "Fatal Error" : "Over Recursion");
        JitSpew(JitSpew_IonInvalidate, "   orig calleeToken %p", (void*) frame->calleeToken());
        JitSpew(JitSpew_IonInvalidate, "   orig frameSize %u", unsigned(frame->prevFrameLocalSize()));
        JitSpew(JitSpew_IonInvalidate, "   orig ra %p", (void*) frame->returnAddress());

        frame->replaceCalleeToken(nullptr);
        EnsureExitFrame(frame);

        JitSpew(JitSpew_IonInvalidate, "   new  calleeToken %p", (void*) frame->calleeToken());
        JitSpew(JitSpew_IonInvalidate, "   new  frameSize %u", unsigned(frame->prevFrameLocalSize()));
        JitSpew(JitSpew_IonInvalidate, "   new  ra %p", (void*) frame->returnAddress());
    }

    iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp());

    // Make the frame being bailed out the top profiled frame.
    if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
        cx->runtime()->jitActivation->setLastProfilingFrame(currentFramePtr);

    return retval;
}
Пример #4
0
static EnterJitStatus JS_HAZ_JSNATIVE_CALLER EnterJit(JSContext* cx,
                                                      RunState& state,
                                                      uint8_t* code) {
  MOZ_ASSERT(state.script()->hasBaselineScript());
  MOZ_ASSERT(code);
  MOZ_ASSERT(IsBaselineEnabled(cx));

  if (!CheckRecursionLimit(cx)) {
    return EnterJitStatus::Error;
  }

#ifdef DEBUG
  // Assert we don't GC before entering JIT code. A GC could discard JIT code
  // or move the function stored in the CalleeToken (it won't be traced at
  // this point). We use Maybe<> here so we can call reset() to call the
  // AutoAssertNoGC destructor before we enter JIT code.
  mozilla::Maybe<JS::AutoAssertNoGC> nogc;
  nogc.emplace(cx);
#endif

  JSScript* script = state.script();
  size_t numActualArgs;
  bool constructing;
  size_t maxArgc;
  Value* maxArgv;
  JSObject* envChain;
  CalleeToken calleeToken;

  if (state.isInvoke()) {
    const CallArgs& args = state.asInvoke()->args();
    numActualArgs = args.length();

    if (TooManyActualArguments(numActualArgs)) {
      // Too many arguments for Ion. Baseline supports more actual
      // arguments, so in that case force Baseline code.
      if (numActualArgs > BASELINE_MAX_ARGS_LENGTH) {
        return EnterJitStatus::NotEntered;
      }
      code = script->baselineScript()->method()->raw();
    }

    constructing = state.asInvoke()->constructing();
    maxArgc = args.length() + 1;
    maxArgv = args.array() - 1;  // -1 to include |this|
    envChain = nullptr;
    calleeToken = CalleeToToken(&args.callee().as<JSFunction>(), constructing);

    unsigned numFormals = script->functionNonDelazifying()->nargs();
    if (numFormals > numActualArgs) {
      code = cx->runtime()->jitRuntime()->getArgumentsRectifier().value;
    }
  } else {
    numActualArgs = 0;
    constructing = false;
    if (script->isDirectEvalInFunction()) {
      if (state.asExecute()->newTarget().isNull()) {
        ScriptFrameIter iter(cx);
        state.asExecute()->setNewTarget(iter.newTarget());
      }
      maxArgc = 1;
      maxArgv = state.asExecute()->addressOfNewTarget();
    } else {
      maxArgc = 0;
      maxArgv = nullptr;
    }
    envChain = state.asExecute()->environmentChain();
    calleeToken = CalleeToToken(state.script());
  }

  // Caller must construct |this| before invoking the function.
  MOZ_ASSERT_IF(constructing, maxArgv[0].isObject() ||
                                  maxArgv[0].isMagic(JS_UNINITIALIZED_LEXICAL));

  RootedValue result(cx, Int32Value(numActualArgs));
  {
    AssertRealmUnchanged aru(cx);
    ActivationEntryMonitor entryMonitor(cx, calleeToken);
    JitActivation activation(cx);
    EnterJitCode enter = cx->runtime()->jitRuntime()->enterJit();

#ifdef DEBUG
    nogc.reset();
#endif
    CALL_GENERATED_CODE(enter, code, maxArgc, maxArgv, /* osrFrame = */ nullptr,
                        calleeToken, envChain, /* osrNumStackValues = */ 0,
                        result.address());
  }

  MOZ_ASSERT(!cx->hasIonReturnOverride());

  // Release temporary buffer used for OSR into Ion.
  cx->freeOsrTempData();

  if (result.isMagic()) {
    MOZ_ASSERT(result.isMagic(JS_ION_ERROR));
    return EnterJitStatus::Error;
  }

  // Jit callers wrap primitive constructor return, except for derived
  // class constructors, which are forced to do it themselves.
  if (constructing && result.isPrimitive()) {
    MOZ_ASSERT(maxArgv[0].isObject());
    result = maxArgv[0];
  }

  state.setReturnValue(result);
  return EnterJitStatus::Ok;
}
Пример #5
0
uint32_t
jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
                         BaselineBailoutInfo **bailoutInfo)
{
    sp->checkInvariants();

    JSContext *cx = GetJSContextFromJitCode();

    // We don't have an exit frame.
    cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
    gc::AutoSuppressGC suppress(cx);

    JitActivationIterator jitActivations(cx->runtime());
    IonBailoutIterator iter(jitActivations, sp);
    JitActivation *activation = jitActivations->asJit();

    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
    TraceLogTimestamp(logger, TraceLogger::Invalidation);

    IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());

    // Note: the frame size must be computed before we return from this function.
    *frameSizeOut = iter.topFrameSize();

    JS_ASSERT(IsBaselineEnabled(cx));

    *bailoutInfo = nullptr;
    uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo);
    JS_ASSERT(retval == BAILOUT_RETURN_OK ||
              retval == BAILOUT_RETURN_FATAL_ERROR ||
              retval == BAILOUT_RETURN_OVERRECURSED);
    JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);

    if (retval != BAILOUT_RETURN_OK) {
        // If the bailout failed, then bailout trampoline will pop the
        // current frame and jump straight to exception handling code when
        // this function returns.  Any SPS entry pushed for this frame will
        // be silently forgotten.
        //
        // We call ExitScript here to ensure that if the ionScript had SPS
        // instrumentation, then the SPS entry for it is popped.
        JSScript *script = iter.script();
        probes::ExitScript(cx, script, script->functionNonDelazifying(),
                           iter.ionScript()->hasSPSInstrumentation());

        IonJSFrameLayout *frame = iter.jsFrame();
        IonSpew(IonSpew_Invalidate, "Bailout failed (%s): converting to exit frame",
                (retval == BAILOUT_RETURN_FATAL_ERROR) ? "Fatal Error" : "Over Recursion");
        IonSpew(IonSpew_Invalidate, "   orig calleeToken %p", (void *) frame->calleeToken());
        IonSpew(IonSpew_Invalidate, "   orig frameSize %u", unsigned(frame->prevFrameLocalSize()));
        IonSpew(IonSpew_Invalidate, "   orig ra %p", (void *) frame->returnAddress());

        frame->replaceCalleeToken(nullptr);
        EnsureExitFrame(frame);

        IonSpew(IonSpew_Invalidate, "   new  calleeToken %p", (void *) frame->calleeToken());
        IonSpew(IonSpew_Invalidate, "   new  frameSize %u", unsigned(frame->prevFrameLocalSize()));
        IonSpew(IonSpew_Invalidate, "   new  ra %p", (void *) frame->returnAddress());
    }

    iter.ionScript()->decref(cx->runtime()->defaultFreeOp());

    return retval;
}
Пример #6
0
uint32_t
jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
{
    JSContext *cx = GetJSContextFromJitCode();
    MOZ_ASSERT(bailoutInfo);

    // We don't have an exit frame.
    MOZ_ASSERT(IsInRange(FAKE_JIT_TOP_FOR_BAILOUT, 0, 0x1000) &&
               IsInRange(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000),
               "Fake jitTop pointer should be within the first page.");
    cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT;

    JitActivationIterator jitActivations(cx->runtime());
    BailoutFrameInfo bailoutData(jitActivations, sp);
    JitFrameIterator iter(jitActivations);
    MOZ_ASSERT(!iter.ionScript()->invalidated());

    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
    TraceLogTimestamp(logger, TraceLogger::Bailout);

    JitSpew(JitSpew_IonBailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());

    MOZ_ASSERT(IsBaselineEnabled(cx));

    *bailoutInfo = nullptr;
    bool poppedLastSPSFrame = false;
    uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo,
                                           /* excInfo = */ nullptr, &poppedLastSPSFrame);
    MOZ_ASSERT(retval == BAILOUT_RETURN_OK ||
               retval == BAILOUT_RETURN_FATAL_ERROR ||
               retval == BAILOUT_RETURN_OVERRECURSED);
    MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);

    if (retval != BAILOUT_RETURN_OK) {
        // If the bailout failed, then bailout trampoline will pop the
        // current frame and jump straight to exception handling code when
        // this function returns.  Any SPS entry pushed for this frame will
        // be silently forgotten.
        //
        // We call ExitScript here to ensure that if the ionScript had SPS
        // instrumentation, then the SPS entry for it is popped.
        //
        // However, if the bailout was during argument check, then a
        // pseudostack frame would not have been pushed in the first
        // place, so don't pop anything in that case.
        bool popSPSFrame = iter.ionScript()->hasSPSInstrumentation() &&
                           (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck) &&
                           !poppedLastSPSFrame;
        JSScript *script = iter.script();
        probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame);

        EnsureExitFrame(iter.jsFrame());
    }

    // This condition was wrong when we entered this bailout function, but it
    // might be true now. A GC might have reclaimed all the Jit code and
    // invalidated all frames which are currently on the stack. As we are
    // already in a bailout, we could not switch to an invalidation
    // bailout. When the code of an IonScript which is on the stack is
    // invalidated (see InvalidateActivation), we remove references to it and
    // increment the reference counter for each activation that appear on the
    // stack. As the bailed frame is one of them, we have to decrement it now.
    if (iter.ionScript()->invalidated())
        iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp());

    return retval;
}