1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/Bailouts.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,251 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jit/Bailouts.h" 1.11 + 1.12 +#include "jscntxt.h" 1.13 + 1.14 +#include "jit/BaselineJIT.h" 1.15 +#include "jit/Ion.h" 1.16 +#include "jit/IonSpewer.h" 1.17 +#include "jit/JitCompartment.h" 1.18 +#include "jit/Snapshots.h" 1.19 +#include "vm/TraceLogging.h" 1.20 + 1.21 +#include "jit/JitFrameIterator-inl.h" 1.22 +#include "vm/Stack-inl.h" 1.23 + 1.24 +using namespace js; 1.25 +using namespace js::jit; 1.26 + 1.27 +// These constructor are exactly the same except for the type of the iterator 1.28 +// which is given to the SnapshotIterator constructor. Doing so avoid the 1.29 +// creation of virtual functions for the IonIterator but may introduce some 1.30 +// weirdness as IonInlineIterator is using a JitFrameIterator reference. 1.31 +// 1.32 +// If a function relies on ionScript() or to use OsiIndex(), due to the 1.33 +// lack of virtual, these functions will use the JitFrameIterator reference 1.34 +// contained in the InlineFrameIterator and thus are not able to recover 1.35 +// correctly the data stored in IonBailoutIterator. 1.36 +// 1.37 +// Currently, such cases should not happen because our only use case of the 1.38 +// JitFrameIterator within InlineFrameIterator is to read the frame content, or 1.39 +// to clone it to find the parent scripted frame. Both use cases are fine and 1.40 +// should not cause any issue since the only potential issue is to read the 1.41 +// bailed out frame. 1.42 + 1.43 +SnapshotIterator::SnapshotIterator(const IonBailoutIterator &iter) 1.44 + : snapshot_(iter.ionScript()->snapshots(), 1.45 + iter.snapshotOffset(), 1.46 + iter.ionScript()->snapshotsRVATableSize(), 1.47 + iter.ionScript()->snapshotsListSize()), 1.48 + recover_(snapshot_, 1.49 + iter.ionScript()->recovers(), 1.50 + iter.ionScript()->recoversSize()), 1.51 + fp_(iter.jsFrame()), 1.52 + machine_(iter.machineState()), 1.53 + ionScript_(iter.ionScript()) 1.54 +{ 1.55 +} 1.56 + 1.57 +void 1.58 +IonBailoutIterator::dump() const 1.59 +{ 1.60 + if (type_ == JitFrame_IonJS) { 1.61 + InlineFrameIterator frames(GetJSContextFromJitCode(), this); 1.62 + for (;;) { 1.63 + frames.dump(); 1.64 + if (!frames.more()) 1.65 + break; 1.66 + ++frames; 1.67 + } 1.68 + } else { 1.69 + JitFrameIterator::dump(); 1.70 + } 1.71 +} 1.72 + 1.73 +uint32_t 1.74 +jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo) 1.75 +{ 1.76 + JSContext *cx = GetJSContextFromJitCode(); 1.77 + JS_ASSERT(bailoutInfo); 1.78 + 1.79 + // We don't have an exit frame. 1.80 + cx->mainThread().ionTop = nullptr; 1.81 + JitActivationIterator jitActivations(cx->runtime()); 1.82 + IonBailoutIterator iter(jitActivations, sp); 1.83 + JitActivation *activation = jitActivations->asJit(); 1.84 + 1.85 + TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); 1.86 + TraceLogTimestamp(logger, TraceLogger::Bailout); 1.87 + 1.88 + IonSpew(IonSpew_Bailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset()); 1.89 + 1.90 + JS_ASSERT(IsBaselineEnabled(cx)); 1.91 + 1.92 + *bailoutInfo = nullptr; 1.93 + uint32_t retval = BailoutIonToBaseline(cx, activation, iter, false, bailoutInfo); 1.94 + JS_ASSERT(retval == BAILOUT_RETURN_OK || 1.95 + retval == BAILOUT_RETURN_FATAL_ERROR || 1.96 + retval == BAILOUT_RETURN_OVERRECURSED); 1.97 + JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr); 1.98 + 1.99 + if (retval != BAILOUT_RETURN_OK) 1.100 + EnsureExitFrame(iter.jsFrame()); 1.101 + 1.102 + return retval; 1.103 +} 1.104 + 1.105 +uint32_t 1.106 +jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, 1.107 + BaselineBailoutInfo **bailoutInfo) 1.108 +{ 1.109 + sp->checkInvariants(); 1.110 + 1.111 + JSContext *cx = GetJSContextFromJitCode(); 1.112 + 1.113 + // We don't have an exit frame. 1.114 + cx->mainThread().ionTop = nullptr; 1.115 + JitActivationIterator jitActivations(cx->runtime()); 1.116 + IonBailoutIterator iter(jitActivations, sp); 1.117 + JitActivation *activation = jitActivations->asJit(); 1.118 + 1.119 + TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); 1.120 + TraceLogTimestamp(logger, TraceLogger::Invalidation); 1.121 + 1.122 + IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset()); 1.123 + 1.124 + // Note: the frame size must be computed before we return from this function. 1.125 + *frameSizeOut = iter.topFrameSize(); 1.126 + 1.127 + JS_ASSERT(IsBaselineEnabled(cx)); 1.128 + 1.129 + *bailoutInfo = nullptr; 1.130 + uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo); 1.131 + JS_ASSERT(retval == BAILOUT_RETURN_OK || 1.132 + retval == BAILOUT_RETURN_FATAL_ERROR || 1.133 + retval == BAILOUT_RETURN_OVERRECURSED); 1.134 + JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr); 1.135 + 1.136 + if (retval != BAILOUT_RETURN_OK) { 1.137 + IonJSFrameLayout *frame = iter.jsFrame(); 1.138 + IonSpew(IonSpew_Invalidate, "converting to exit frame"); 1.139 + IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken()); 1.140 + IonSpew(IonSpew_Invalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize())); 1.141 + IonSpew(IonSpew_Invalidate, " orig ra %p", (void *) frame->returnAddress()); 1.142 + 1.143 + frame->replaceCalleeToken(nullptr); 1.144 + EnsureExitFrame(frame); 1.145 + 1.146 + IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken()); 1.147 + IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); 1.148 + IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress()); 1.149 + } 1.150 + 1.151 + iter.ionScript()->decref(cx->runtime()->defaultFreeOp()); 1.152 + 1.153 + return retval; 1.154 +} 1.155 + 1.156 +IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations, 1.157 + const JitFrameIterator &frame) 1.158 + : JitFrameIterator(activations), 1.159 + machine_(frame.machineState()) 1.160 +{ 1.161 + returnAddressToFp_ = frame.returnAddressToFp(); 1.162 + topIonScript_ = frame.ionScript(); 1.163 + const OsiIndex *osiIndex = frame.osiIndex(); 1.164 + 1.165 + current_ = (uint8_t *) frame.fp(); 1.166 + type_ = JitFrame_IonJS; 1.167 + topFrameSize_ = frame.frameSize(); 1.168 + snapshotOffset_ = osiIndex->snapshotOffset(); 1.169 +} 1.170 + 1.171 +uint32_t 1.172 +jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame, 1.173 + ResumeFromException *rfe, 1.174 + const ExceptionBailoutInfo &excInfo, 1.175 + bool *overrecursed) 1.176 +{ 1.177 + // We can be propagating debug mode exceptions without there being an 1.178 + // actual exception pending. For instance, when we return false from an 1.179 + // operation callback like a timeout handler. 1.180 + MOZ_ASSERT_IF(!excInfo.propagatingIonExceptionForDebugMode(), cx->isExceptionPending()); 1.181 + 1.182 + cx->mainThread().ionTop = nullptr; 1.183 + JitActivationIterator jitActivations(cx->runtime()); 1.184 + IonBailoutIterator iter(jitActivations, frame.frame()); 1.185 + JitActivation *activation = jitActivations->asJit(); 1.186 + 1.187 + BaselineBailoutInfo *bailoutInfo = nullptr; 1.188 + uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, &bailoutInfo, &excInfo); 1.189 + 1.190 + if (retval == BAILOUT_RETURN_OK) { 1.191 + MOZ_ASSERT(bailoutInfo); 1.192 + 1.193 + // Overwrite the kind so HandleException after the bailout returns 1.194 + // false, jumping directly to the exception tail. 1.195 + if (excInfo.propagatingIonExceptionForDebugMode()) 1.196 + bailoutInfo->bailoutKind = Bailout_IonExceptionDebugMode; 1.197 + 1.198 + rfe->kind = ResumeFromException::RESUME_BAILOUT; 1.199 + rfe->target = cx->runtime()->jitRuntime()->getBailoutTail()->raw(); 1.200 + rfe->bailoutInfo = bailoutInfo; 1.201 + } else { 1.202 + // Bailout failed. If there was a fatal error, clear the 1.203 + // exception to turn this into an uncatchable error. If the 1.204 + // overrecursion check failed, continue popping all inline 1.205 + // frames and have the caller report an overrecursion error. 1.206 + MOZ_ASSERT(!bailoutInfo); 1.207 + 1.208 + if (!excInfo.propagatingIonExceptionForDebugMode()) 1.209 + cx->clearPendingException(); 1.210 + 1.211 + if (retval == BAILOUT_RETURN_OVERRECURSED) 1.212 + *overrecursed = true; 1.213 + else 1.214 + MOZ_ASSERT(retval == BAILOUT_RETURN_FATAL_ERROR); 1.215 + } 1.216 + 1.217 + return retval; 1.218 +} 1.219 + 1.220 +// Initialize the decl env Object, call object, and any arguments obj of the current frame. 1.221 +bool 1.222 +jit::EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp) 1.223 +{ 1.224 + if (fp.isFunctionFrame() && 1.225 + fp.fun()->isHeavyweight() && 1.226 + !fp.hasCallObj()) 1.227 + { 1.228 + return fp.initFunctionScopeObjects(cx); 1.229 + } 1.230 + return true; 1.231 +} 1.232 + 1.233 +bool 1.234 +jit::CheckFrequentBailouts(JSContext *cx, JSScript *script) 1.235 +{ 1.236 + if (script->hasIonScript()) { 1.237 + // Invalidate if this script keeps bailing out without invalidation. Next time 1.238 + // we compile this script LICM will be disabled. 1.239 + IonScript *ionScript = script->ionScript(); 1.240 + 1.241 + if (ionScript->numBailouts() >= js_JitOptions.frequentBailoutThreshold && 1.242 + !script->hadFrequentBailouts()) 1.243 + { 1.244 + script->setHadFrequentBailouts(); 1.245 + 1.246 + IonSpew(IonSpew_Invalidate, "Invalidating due to too many bailouts"); 1.247 + 1.248 + if (!Invalidate(cx, script)) 1.249 + return false; 1.250 + } 1.251 + } 1.252 + 1.253 + return true; 1.254 +}