js/src/jit/Bailouts.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial