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