blob: d1ed784296f736e7104b8a97bcadcd0cbd3c1747 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jscntxt.h"
#include "jscompartment.h"
#include "Bailouts.h"
#include "SnapshotReader.h"
#include "Ion.h"
#include "IonCompartment.h"
#include "IonSpewer.h"
#include "jsinfer.h"
#include "jsanalyze.h"
#include "jsinferinlines.h"
#include "vm/Interpreter.h"
#include "IonFrames-inl.h"
#include "BaselineJIT.h"
using namespace js;
using namespace js::jit;
// These constructor are exactly the same except for the type of the iterator
// which is given to the SnapshotIterator constructor. Doing so avoid the
// creation of virtual functions for the IonIterator but may introduce some
// weirdness as IonInlineIterator is using an IonFrameIterator reference.
//
// If a function relies on ionScript() or to use OsiIndex(), due to the
// lack of virtual, these functions will use the IonFrameIterator reference
// contained in the InlineFrameIterator and thus are not able to recover
// correctly the data stored in IonBailoutIterator.
//
// Currently, such cases should not happen because our only use case of the
// IonFrameIterator within InlineFrameIterator is to read the frame content, or
// to clone it to find the parent scripted frame. Both use cases are fine and
// should not cause any issue since the only potential issue is to read the
// bailed out frame.
SnapshotIterator::SnapshotIterator(const IonBailoutIterator &iter)
: SnapshotReader(iter.ionScript()->snapshots() + iter.snapshotOffset(),
iter.ionScript()->snapshots() + iter.ionScript()->snapshotsSize()),
fp_(iter.jsFrame()),
machine_(iter.machineState()),
ionScript_(iter.ionScript())
{
}
void
IonBailoutIterator::dump() const
{
if (type_ == IonFrame_OptimizedJS) {
InlineFrameIterator frames(GetIonContext()->cx, this);
for (;;) {
frames.dump();
if (!frames.more())
break;
++frames;
}
} else {
IonFrameIterator::dump();
}
}
uint32_t
jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
{
JS_ASSERT(bailoutInfo);
JSContext *cx = GetIonContext()->cx;
// We don't have an exit frame.
cx->mainThread().ionTop = NULL;
JitActivationIterator jitActivations(cx->runtime());
IonBailoutIterator iter(jitActivations, sp);
JitActivation *activation = jitActivations.activation()->asJit();
IonSpew(IonSpew_Bailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());
JS_ASSERT(IsBaselineEnabled(cx));
*bailoutInfo = NULL;
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 != NULL);
if (retval != BAILOUT_RETURN_OK)
EnsureExitFrame(iter.jsFrame());
return retval;
}
uint32_t
jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
BaselineBailoutInfo **bailoutInfo)
{
sp->checkInvariants();
JSContext *cx = GetIonContext()->cx;
// We don't have an exit frame.
cx->mainThread().ionTop = NULL;
JitActivationIterator jitActivations(cx->runtime());
IonBailoutIterator iter(jitActivations, sp);
JitActivation *activation = jitActivations.activation()->asJit();
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 = NULL;
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 != NULL);
if (retval != BAILOUT_RETURN_OK) {
IonJSFrameLayout *frame = iter.jsFrame();
IonSpew(IonSpew_Invalidate, "converting to exit frame");
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(NULL);
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;
}
// Initialize the decl env Object, call object, and any arguments obj of the current frame.
bool
jit::EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp)
{
if (fp.isFunctionFrame() &&
fp.fun()->isHeavyweight() &&
!fp.hasCallObj())
{
return fp.initFunctionScopeObjects(cx);
}
return true;
}
bool
jit::CheckFrequentBailouts(JSContext *cx, JSScript *script)
{
// Invalidate if this script keeps bailing out without invalidation. Next time
// we compile this script LICM will be disabled.
if (script->hasIonScript() &&
script->ionScript()->numBailouts() >= js_IonOptions.frequentBailoutThreshold &&
!script->hadFrequentBailouts)
{
script->hadFrequentBailouts = true;
IonSpew(IonSpew_Invalidate, "Invalidating due to too many bailouts");
if (!Invalidate(cx, script))
return false;
}
return true;
}