Line data Source code
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/JitFrameIterator.h"
13 : #include "jit/JitFrames.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 JitFrames.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 : // This address is a magic number made to cause crashes while indicating that we
104 : // are making an attempt to mark the stack during a bailout.
105 : static uint8_t* const FAKE_EXITFP_FOR_BAILOUT = reinterpret_cast<uint8_t*>(0xba1);
106 :
107 : // BailoutStack is an architecture specific pointer to the stack, given by the
108 : // bailout handler.
109 : class BailoutStack;
110 : class InvalidationBailoutStack;
111 :
112 : // Must be implemented by each architecture.
113 :
114 : // This structure is constructed before recovering the baseline frames for a
115 : // bailout. It records all information extracted from the stack, and which are
116 : // needed for the JitFrameIterator.
117 : class BailoutFrameInfo
118 : {
119 : MachineState machine_;
120 : uint8_t* framePointer_;
121 : size_t topFrameSize_;
122 : IonScript* topIonScript_;
123 : uint32_t snapshotOffset_;
124 : JitActivation* activation_;
125 :
126 : void attachOnJitActivation(const JitActivationIterator& activations);
127 :
128 : public:
129 : BailoutFrameInfo(const JitActivationIterator& activations, BailoutStack* sp);
130 : BailoutFrameInfo(const JitActivationIterator& activations, InvalidationBailoutStack* sp);
131 : BailoutFrameInfo(const JitActivationIterator& activations, const JitFrameIterator& frame);
132 : ~BailoutFrameInfo();
133 :
134 0 : uint8_t* fp() const {
135 0 : return framePointer_;
136 : }
137 0 : SnapshotOffset snapshotOffset() const {
138 0 : return snapshotOffset_;
139 : }
140 0 : const MachineState* machineState() const {
141 0 : return &machine_;
142 : }
143 0 : size_t topFrameSize() const {
144 0 : return topFrameSize_;
145 : }
146 0 : IonScript* ionScript() const {
147 0 : return topIonScript_;
148 : }
149 0 : JitActivation* activation() const {
150 0 : return activation_;
151 : }
152 : };
153 :
154 : MOZ_MUST_USE bool EnsureHasEnvironmentObjects(JSContext* cx, AbstractFramePtr fp);
155 :
156 : struct BaselineBailoutInfo;
157 :
158 : // Called from a bailout thunk. Returns a BAILOUT_* error code.
159 : uint32_t Bailout(BailoutStack* sp, BaselineBailoutInfo** info);
160 :
161 : // Called from the invalidation thunk. Returns a BAILOUT_* error code.
162 : uint32_t InvalidationBailout(InvalidationBailoutStack* sp, size_t* frameSizeOut,
163 : BaselineBailoutInfo** info);
164 :
165 : class ExceptionBailoutInfo
166 : {
167 : size_t frameNo_;
168 : jsbytecode* resumePC_;
169 : size_t numExprSlots_;
170 :
171 : public:
172 0 : ExceptionBailoutInfo(size_t frameNo, jsbytecode* resumePC, size_t numExprSlots)
173 0 : : frameNo_(frameNo),
174 : resumePC_(resumePC),
175 0 : numExprSlots_(numExprSlots)
176 0 : { }
177 :
178 0 : ExceptionBailoutInfo()
179 0 : : frameNo_(0),
180 : resumePC_(nullptr),
181 0 : numExprSlots_(0)
182 0 : { }
183 :
184 0 : bool catchingException() const {
185 0 : return !!resumePC_;
186 : }
187 0 : bool propagatingIonExceptionForDebugMode() const {
188 0 : return !resumePC_;
189 : }
190 :
191 0 : size_t frameNo() const {
192 0 : MOZ_ASSERT(catchingException());
193 0 : return frameNo_;
194 : }
195 0 : jsbytecode* resumePC() const {
196 0 : MOZ_ASSERT(catchingException());
197 0 : return resumePC_;
198 : }
199 0 : size_t numExprSlots() const {
200 0 : MOZ_ASSERT(catchingException());
201 0 : return numExprSlots_;
202 : }
203 : };
204 :
205 : // Called from the exception handler to enter a catch or finally block.
206 : // Returns a BAILOUT_* error code.
207 : uint32_t ExceptionHandlerBailout(JSContext* cx, const InlineFrameIterator& frame,
208 : ResumeFromException* rfe,
209 : const ExceptionBailoutInfo& excInfo,
210 : bool* overrecursed);
211 :
212 : uint32_t FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfo);
213 :
214 : void CheckFrequentBailouts(JSContext* cx, JSScript* script, BailoutKind bailoutKind);
215 :
216 : } // namespace jit
217 : } // namespace js
218 :
219 : #endif /* jit_Bailouts_h */
|