Coverage Report

Created: 2018-09-25 14:53

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