|
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 #ifndef jit_Bailouts_h |
|
8 #define jit_Bailouts_h |
|
9 |
|
10 #include "jstypes.h" |
|
11 |
|
12 #include "jit/IonFrames.h" |
|
13 #include "jit/JitFrameIterator.h" |
|
14 #include "vm/Stack.h" |
|
15 |
|
16 namespace js { |
|
17 namespace jit { |
|
18 |
|
19 // A "bailout" is a condition in which we need to recover an interpreter frame |
|
20 // from an IonFrame. Bailouts can happen for the following reasons: |
|
21 // (1) A deoptimization guard, for example, an add overflows or a type check |
|
22 // fails. |
|
23 // (2) A check or assumption held by the JIT is invalidated by the VM, and |
|
24 // JIT code must be thrown away. This includes the GC possibly deciding |
|
25 // to evict live JIT code, or a Type Inference reflow. |
|
26 // |
|
27 // Note that bailouts as described here do not include normal Ion frame |
|
28 // inspection, for example, if an exception must be built or the GC needs to |
|
29 // scan an Ion frame for gcthings. |
|
30 // |
|
31 // The second type of bailout needs a different name - "deoptimization" or |
|
32 // "deep bailout". Here we are concerned with eager (or maybe "shallow") |
|
33 // bailouts, that happen from JIT code. These happen from guards, like: |
|
34 // |
|
35 // cmp [obj + shape], 0x50M37TH1NG |
|
36 // jmp _bailout |
|
37 // |
|
38 // The bailout target needs to somehow translate the Ion frame (whose state |
|
39 // will differ at each program point) to an interpreter frame. This state is |
|
40 // captured into the IonScript's snapshot buffer, and for each bailout we know |
|
41 // which snapshot corresponds to its state. |
|
42 // |
|
43 // Roughly, the following needs to happen at the bailout target. |
|
44 // (1) Move snapshot ID into a known stack location (registers cannot be |
|
45 // mutated). |
|
46 // (2) Spill all registers to the stack. |
|
47 // (3) Call a Bailout() routine, whose argument is the stack pointer. |
|
48 // (4) Bailout() will find the IonScript on the stack, use the snapshot ID |
|
49 // to find the structure of the frame, and then use the stack and spilled |
|
50 // registers to perform frame conversion. |
|
51 // (5) Bailout() returns, and the JIT must immediately return to the |
|
52 // interpreter (all frames are converted at once). |
|
53 // |
|
54 // (2) and (3) are implemented by a trampoline held in the compartment. |
|
55 // Naively, we could implement (1) like: |
|
56 // |
|
57 // _bailout_ID_1: |
|
58 // push 1 |
|
59 // jmp _global_bailout_handler |
|
60 // _bailout_ID_2: |
|
61 // push 2 |
|
62 // jmp _global_bailout_handler |
|
63 // |
|
64 // This takes about 10 extra bytes per guard. On some platforms, we can reduce |
|
65 // this overhead to 4 bytes by creating a global jump table, shared again in |
|
66 // the compartment: |
|
67 // |
|
68 // call _global_bailout_handler |
|
69 // call _global_bailout_handler |
|
70 // call _global_bailout_handler |
|
71 // call _global_bailout_handler |
|
72 // ... |
|
73 // _global_bailout_handler: |
|
74 // |
|
75 // In the bailout handler, we can recompute which entry in the table was |
|
76 // selected by subtracting the return addressed pushed by the call, from the |
|
77 // start of the table, and then dividing by the size of a (call X) entry in the |
|
78 // table. This gives us a number in [0, TableSize), which we call a |
|
79 // "BailoutId". |
|
80 // |
|
81 // Then, we can provide a per-script mapping from BailoutIds to snapshots, |
|
82 // which takes only four bytes per entry. |
|
83 // |
|
84 // This strategy does not work as given, because the bailout handler has no way |
|
85 // to compute the location of an IonScript. Currently, we do not use frame |
|
86 // pointers. To account for this we segregate frames into a limited set of |
|
87 // "frame sizes", and create a table for each frame size. We also have the |
|
88 // option of not using bailout tables, for platforms or situations where the |
|
89 // 10 byte cost is more optimal than a bailout table. See IonFrames.h for more |
|
90 // detail. |
|
91 |
|
92 static const BailoutId INVALID_BAILOUT_ID = BailoutId(-1); |
|
93 |
|
94 // Keep this arbitrarily small for now, for testing. |
|
95 static const uint32_t BAILOUT_TABLE_SIZE = 16; |
|
96 |
|
97 // Bailout return codes. |
|
98 // N.B. the relative order of these values is hard-coded into ::GenerateBailoutThunk. |
|
99 static const uint32_t BAILOUT_RETURN_OK = 0; |
|
100 static const uint32_t BAILOUT_RETURN_FATAL_ERROR = 1; |
|
101 static const uint32_t BAILOUT_RETURN_OVERRECURSED = 2; |
|
102 |
|
103 class JitCompartment; |
|
104 |
|
105 // BailoutStack is an architecture specific pointer to the stack, given by the |
|
106 // bailout handler. |
|
107 class BailoutStack; |
|
108 class InvalidationBailoutStack; |
|
109 |
|
110 // Must be implemented by each architecture. |
|
111 |
|
112 // This iterator is constructed at a time where there is no exit frame at the |
|
113 // moment. They must be initialized to the first JS frame instead of the exit |
|
114 // frame as usually done with JitFrameIterator. |
|
115 class IonBailoutIterator : public JitFrameIterator |
|
116 { |
|
117 MachineState machine_; |
|
118 uint32_t snapshotOffset_; |
|
119 size_t topFrameSize_; |
|
120 IonScript *topIonScript_; |
|
121 |
|
122 public: |
|
123 IonBailoutIterator(const JitActivationIterator &activations, BailoutStack *sp); |
|
124 IonBailoutIterator(const JitActivationIterator &activations, InvalidationBailoutStack *sp); |
|
125 IonBailoutIterator(const JitActivationIterator &activations, const JitFrameIterator &frame); |
|
126 |
|
127 SnapshotOffset snapshotOffset() const { |
|
128 JS_ASSERT(topIonScript_); |
|
129 return snapshotOffset_; |
|
130 } |
|
131 const MachineState &machineState() const { |
|
132 return machine_; |
|
133 } |
|
134 size_t topFrameSize() const { |
|
135 JS_ASSERT(topIonScript_); |
|
136 return topFrameSize_; |
|
137 } |
|
138 IonScript *ionScript() const { |
|
139 if (topIonScript_) |
|
140 return topIonScript_; |
|
141 return JitFrameIterator::ionScript(); |
|
142 } |
|
143 |
|
144 void dump() const; |
|
145 }; |
|
146 |
|
147 bool EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp); |
|
148 |
|
149 struct BaselineBailoutInfo; |
|
150 |
|
151 // Called from a bailout thunk. Returns a BAILOUT_* error code. |
|
152 uint32_t Bailout(BailoutStack *sp, BaselineBailoutInfo **info); |
|
153 |
|
154 // Called from the invalidation thunk. Returns a BAILOUT_* error code. |
|
155 uint32_t InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, |
|
156 BaselineBailoutInfo **info); |
|
157 |
|
158 class ExceptionBailoutInfo |
|
159 { |
|
160 size_t frameNo_; |
|
161 jsbytecode *resumePC_; |
|
162 size_t numExprSlots_; |
|
163 |
|
164 public: |
|
165 ExceptionBailoutInfo(size_t frameNo, jsbytecode *resumePC, size_t numExprSlots) |
|
166 : frameNo_(frameNo), |
|
167 resumePC_(resumePC), |
|
168 numExprSlots_(numExprSlots) |
|
169 { } |
|
170 |
|
171 ExceptionBailoutInfo() |
|
172 : frameNo_(0), |
|
173 resumePC_(nullptr), |
|
174 numExprSlots_(0) |
|
175 { } |
|
176 |
|
177 bool catchingException() const { |
|
178 return !!resumePC_; |
|
179 } |
|
180 bool propagatingIonExceptionForDebugMode() const { |
|
181 return !resumePC_; |
|
182 } |
|
183 |
|
184 size_t frameNo() const { |
|
185 MOZ_ASSERT(catchingException()); |
|
186 return frameNo_; |
|
187 } |
|
188 jsbytecode *resumePC() const { |
|
189 MOZ_ASSERT(catchingException()); |
|
190 return resumePC_; |
|
191 } |
|
192 size_t numExprSlots() const { |
|
193 MOZ_ASSERT(catchingException()); |
|
194 return numExprSlots_; |
|
195 } |
|
196 }; |
|
197 |
|
198 // Called from the exception handler to enter a catch or finally block. |
|
199 // Returns a BAILOUT_* error code. |
|
200 uint32_t ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame, |
|
201 ResumeFromException *rfe, |
|
202 const ExceptionBailoutInfo &excInfo, |
|
203 bool *overrecursed); |
|
204 |
|
205 uint32_t FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo); |
|
206 |
|
207 bool CheckFrequentBailouts(JSContext *cx, JSScript *script); |
|
208 |
|
209 } // namespace jit |
|
210 } // namespace js |
|
211 |
|
212 #endif /* jit_Bailouts_h */ |