js/src/jit/Bailouts.h

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:1db8335fe0e8
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 */

mercurial