js/src/jit/Bailouts.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "jit/Bailouts.h"
michael@0 8
michael@0 9 #include "jscntxt.h"
michael@0 10
michael@0 11 #include "jit/BaselineJIT.h"
michael@0 12 #include "jit/Ion.h"
michael@0 13 #include "jit/IonSpewer.h"
michael@0 14 #include "jit/JitCompartment.h"
michael@0 15 #include "jit/Snapshots.h"
michael@0 16 #include "vm/TraceLogging.h"
michael@0 17
michael@0 18 #include "jit/JitFrameIterator-inl.h"
michael@0 19 #include "vm/Stack-inl.h"
michael@0 20
michael@0 21 using namespace js;
michael@0 22 using namespace js::jit;
michael@0 23
michael@0 24 // These constructor are exactly the same except for the type of the iterator
michael@0 25 // which is given to the SnapshotIterator constructor. Doing so avoid the
michael@0 26 // creation of virtual functions for the IonIterator but may introduce some
michael@0 27 // weirdness as IonInlineIterator is using a JitFrameIterator reference.
michael@0 28 //
michael@0 29 // If a function relies on ionScript() or to use OsiIndex(), due to the
michael@0 30 // lack of virtual, these functions will use the JitFrameIterator reference
michael@0 31 // contained in the InlineFrameIterator and thus are not able to recover
michael@0 32 // correctly the data stored in IonBailoutIterator.
michael@0 33 //
michael@0 34 // Currently, such cases should not happen because our only use case of the
michael@0 35 // JitFrameIterator within InlineFrameIterator is to read the frame content, or
michael@0 36 // to clone it to find the parent scripted frame. Both use cases are fine and
michael@0 37 // should not cause any issue since the only potential issue is to read the
michael@0 38 // bailed out frame.
michael@0 39
michael@0 40 SnapshotIterator::SnapshotIterator(const IonBailoutIterator &iter)
michael@0 41 : snapshot_(iter.ionScript()->snapshots(),
michael@0 42 iter.snapshotOffset(),
michael@0 43 iter.ionScript()->snapshotsRVATableSize(),
michael@0 44 iter.ionScript()->snapshotsListSize()),
michael@0 45 recover_(snapshot_,
michael@0 46 iter.ionScript()->recovers(),
michael@0 47 iter.ionScript()->recoversSize()),
michael@0 48 fp_(iter.jsFrame()),
michael@0 49 machine_(iter.machineState()),
michael@0 50 ionScript_(iter.ionScript())
michael@0 51 {
michael@0 52 }
michael@0 53
michael@0 54 void
michael@0 55 IonBailoutIterator::dump() const
michael@0 56 {
michael@0 57 if (type_ == JitFrame_IonJS) {
michael@0 58 InlineFrameIterator frames(GetJSContextFromJitCode(), this);
michael@0 59 for (;;) {
michael@0 60 frames.dump();
michael@0 61 if (!frames.more())
michael@0 62 break;
michael@0 63 ++frames;
michael@0 64 }
michael@0 65 } else {
michael@0 66 JitFrameIterator::dump();
michael@0 67 }
michael@0 68 }
michael@0 69
michael@0 70 uint32_t
michael@0 71 jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
michael@0 72 {
michael@0 73 JSContext *cx = GetJSContextFromJitCode();
michael@0 74 JS_ASSERT(bailoutInfo);
michael@0 75
michael@0 76 // We don't have an exit frame.
michael@0 77 cx->mainThread().ionTop = nullptr;
michael@0 78 JitActivationIterator jitActivations(cx->runtime());
michael@0 79 IonBailoutIterator iter(jitActivations, sp);
michael@0 80 JitActivation *activation = jitActivations->asJit();
michael@0 81
michael@0 82 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
michael@0 83 TraceLogTimestamp(logger, TraceLogger::Bailout);
michael@0 84
michael@0 85 IonSpew(IonSpew_Bailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());
michael@0 86
michael@0 87 JS_ASSERT(IsBaselineEnabled(cx));
michael@0 88
michael@0 89 *bailoutInfo = nullptr;
michael@0 90 uint32_t retval = BailoutIonToBaseline(cx, activation, iter, false, bailoutInfo);
michael@0 91 JS_ASSERT(retval == BAILOUT_RETURN_OK ||
michael@0 92 retval == BAILOUT_RETURN_FATAL_ERROR ||
michael@0 93 retval == BAILOUT_RETURN_OVERRECURSED);
michael@0 94 JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);
michael@0 95
michael@0 96 if (retval != BAILOUT_RETURN_OK)
michael@0 97 EnsureExitFrame(iter.jsFrame());
michael@0 98
michael@0 99 return retval;
michael@0 100 }
michael@0 101
michael@0 102 uint32_t
michael@0 103 jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
michael@0 104 BaselineBailoutInfo **bailoutInfo)
michael@0 105 {
michael@0 106 sp->checkInvariants();
michael@0 107
michael@0 108 JSContext *cx = GetJSContextFromJitCode();
michael@0 109
michael@0 110 // We don't have an exit frame.
michael@0 111 cx->mainThread().ionTop = nullptr;
michael@0 112 JitActivationIterator jitActivations(cx->runtime());
michael@0 113 IonBailoutIterator iter(jitActivations, sp);
michael@0 114 JitActivation *activation = jitActivations->asJit();
michael@0 115
michael@0 116 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
michael@0 117 TraceLogTimestamp(logger, TraceLogger::Invalidation);
michael@0 118
michael@0 119 IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());
michael@0 120
michael@0 121 // Note: the frame size must be computed before we return from this function.
michael@0 122 *frameSizeOut = iter.topFrameSize();
michael@0 123
michael@0 124 JS_ASSERT(IsBaselineEnabled(cx));
michael@0 125
michael@0 126 *bailoutInfo = nullptr;
michael@0 127 uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo);
michael@0 128 JS_ASSERT(retval == BAILOUT_RETURN_OK ||
michael@0 129 retval == BAILOUT_RETURN_FATAL_ERROR ||
michael@0 130 retval == BAILOUT_RETURN_OVERRECURSED);
michael@0 131 JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);
michael@0 132
michael@0 133 if (retval != BAILOUT_RETURN_OK) {
michael@0 134 IonJSFrameLayout *frame = iter.jsFrame();
michael@0 135 IonSpew(IonSpew_Invalidate, "converting to exit frame");
michael@0 136 IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken());
michael@0 137 IonSpew(IonSpew_Invalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize()));
michael@0 138 IonSpew(IonSpew_Invalidate, " orig ra %p", (void *) frame->returnAddress());
michael@0 139
michael@0 140 frame->replaceCalleeToken(nullptr);
michael@0 141 EnsureExitFrame(frame);
michael@0 142
michael@0 143 IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken());
michael@0 144 IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize()));
michael@0 145 IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress());
michael@0 146 }
michael@0 147
michael@0 148 iter.ionScript()->decref(cx->runtime()->defaultFreeOp());
michael@0 149
michael@0 150 return retval;
michael@0 151 }
michael@0 152
michael@0 153 IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
michael@0 154 const JitFrameIterator &frame)
michael@0 155 : JitFrameIterator(activations),
michael@0 156 machine_(frame.machineState())
michael@0 157 {
michael@0 158 returnAddressToFp_ = frame.returnAddressToFp();
michael@0 159 topIonScript_ = frame.ionScript();
michael@0 160 const OsiIndex *osiIndex = frame.osiIndex();
michael@0 161
michael@0 162 current_ = (uint8_t *) frame.fp();
michael@0 163 type_ = JitFrame_IonJS;
michael@0 164 topFrameSize_ = frame.frameSize();
michael@0 165 snapshotOffset_ = osiIndex->snapshotOffset();
michael@0 166 }
michael@0 167
michael@0 168 uint32_t
michael@0 169 jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame,
michael@0 170 ResumeFromException *rfe,
michael@0 171 const ExceptionBailoutInfo &excInfo,
michael@0 172 bool *overrecursed)
michael@0 173 {
michael@0 174 // We can be propagating debug mode exceptions without there being an
michael@0 175 // actual exception pending. For instance, when we return false from an
michael@0 176 // operation callback like a timeout handler.
michael@0 177 MOZ_ASSERT_IF(!excInfo.propagatingIonExceptionForDebugMode(), cx->isExceptionPending());
michael@0 178
michael@0 179 cx->mainThread().ionTop = nullptr;
michael@0 180 JitActivationIterator jitActivations(cx->runtime());
michael@0 181 IonBailoutIterator iter(jitActivations, frame.frame());
michael@0 182 JitActivation *activation = jitActivations->asJit();
michael@0 183
michael@0 184 BaselineBailoutInfo *bailoutInfo = nullptr;
michael@0 185 uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, &bailoutInfo, &excInfo);
michael@0 186
michael@0 187 if (retval == BAILOUT_RETURN_OK) {
michael@0 188 MOZ_ASSERT(bailoutInfo);
michael@0 189
michael@0 190 // Overwrite the kind so HandleException after the bailout returns
michael@0 191 // false, jumping directly to the exception tail.
michael@0 192 if (excInfo.propagatingIonExceptionForDebugMode())
michael@0 193 bailoutInfo->bailoutKind = Bailout_IonExceptionDebugMode;
michael@0 194
michael@0 195 rfe->kind = ResumeFromException::RESUME_BAILOUT;
michael@0 196 rfe->target = cx->runtime()->jitRuntime()->getBailoutTail()->raw();
michael@0 197 rfe->bailoutInfo = bailoutInfo;
michael@0 198 } else {
michael@0 199 // Bailout failed. If there was a fatal error, clear the
michael@0 200 // exception to turn this into an uncatchable error. If the
michael@0 201 // overrecursion check failed, continue popping all inline
michael@0 202 // frames and have the caller report an overrecursion error.
michael@0 203 MOZ_ASSERT(!bailoutInfo);
michael@0 204
michael@0 205 if (!excInfo.propagatingIonExceptionForDebugMode())
michael@0 206 cx->clearPendingException();
michael@0 207
michael@0 208 if (retval == BAILOUT_RETURN_OVERRECURSED)
michael@0 209 *overrecursed = true;
michael@0 210 else
michael@0 211 MOZ_ASSERT(retval == BAILOUT_RETURN_FATAL_ERROR);
michael@0 212 }
michael@0 213
michael@0 214 return retval;
michael@0 215 }
michael@0 216
michael@0 217 // Initialize the decl env Object, call object, and any arguments obj of the current frame.
michael@0 218 bool
michael@0 219 jit::EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp)
michael@0 220 {
michael@0 221 if (fp.isFunctionFrame() &&
michael@0 222 fp.fun()->isHeavyweight() &&
michael@0 223 !fp.hasCallObj())
michael@0 224 {
michael@0 225 return fp.initFunctionScopeObjects(cx);
michael@0 226 }
michael@0 227 return true;
michael@0 228 }
michael@0 229
michael@0 230 bool
michael@0 231 jit::CheckFrequentBailouts(JSContext *cx, JSScript *script)
michael@0 232 {
michael@0 233 if (script->hasIonScript()) {
michael@0 234 // Invalidate if this script keeps bailing out without invalidation. Next time
michael@0 235 // we compile this script LICM will be disabled.
michael@0 236 IonScript *ionScript = script->ionScript();
michael@0 237
michael@0 238 if (ionScript->numBailouts() >= js_JitOptions.frequentBailoutThreshold &&
michael@0 239 !script->hadFrequentBailouts())
michael@0 240 {
michael@0 241 script->setHadFrequentBailouts();
michael@0 242
michael@0 243 IonSpew(IonSpew_Invalidate, "Invalidating due to too many bailouts");
michael@0 244
michael@0 245 if (!Invalidate(cx, script))
michael@0 246 return false;
michael@0 247 }
michael@0 248 }
michael@0 249
michael@0 250 return true;
michael@0 251 }

mercurial