1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/Bailouts.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,212 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef jit_Bailouts_h 1.11 +#define jit_Bailouts_h 1.12 + 1.13 +#include "jstypes.h" 1.14 + 1.15 +#include "jit/IonFrames.h" 1.16 +#include "jit/JitFrameIterator.h" 1.17 +#include "vm/Stack.h" 1.18 + 1.19 +namespace js { 1.20 +namespace jit { 1.21 + 1.22 +// A "bailout" is a condition in which we need to recover an interpreter frame 1.23 +// from an IonFrame. Bailouts can happen for the following reasons: 1.24 +// (1) A deoptimization guard, for example, an add overflows or a type check 1.25 +// fails. 1.26 +// (2) A check or assumption held by the JIT is invalidated by the VM, and 1.27 +// JIT code must be thrown away. This includes the GC possibly deciding 1.28 +// to evict live JIT code, or a Type Inference reflow. 1.29 +// 1.30 +// Note that bailouts as described here do not include normal Ion frame 1.31 +// inspection, for example, if an exception must be built or the GC needs to 1.32 +// scan an Ion frame for gcthings. 1.33 +// 1.34 +// The second type of bailout needs a different name - "deoptimization" or 1.35 +// "deep bailout". Here we are concerned with eager (or maybe "shallow") 1.36 +// bailouts, that happen from JIT code. These happen from guards, like: 1.37 +// 1.38 +// cmp [obj + shape], 0x50M37TH1NG 1.39 +// jmp _bailout 1.40 +// 1.41 +// The bailout target needs to somehow translate the Ion frame (whose state 1.42 +// will differ at each program point) to an interpreter frame. This state is 1.43 +// captured into the IonScript's snapshot buffer, and for each bailout we know 1.44 +// which snapshot corresponds to its state. 1.45 +// 1.46 +// Roughly, the following needs to happen at the bailout target. 1.47 +// (1) Move snapshot ID into a known stack location (registers cannot be 1.48 +// mutated). 1.49 +// (2) Spill all registers to the stack. 1.50 +// (3) Call a Bailout() routine, whose argument is the stack pointer. 1.51 +// (4) Bailout() will find the IonScript on the stack, use the snapshot ID 1.52 +// to find the structure of the frame, and then use the stack and spilled 1.53 +// registers to perform frame conversion. 1.54 +// (5) Bailout() returns, and the JIT must immediately return to the 1.55 +// interpreter (all frames are converted at once). 1.56 +// 1.57 +// (2) and (3) are implemented by a trampoline held in the compartment. 1.58 +// Naively, we could implement (1) like: 1.59 +// 1.60 +// _bailout_ID_1: 1.61 +// push 1 1.62 +// jmp _global_bailout_handler 1.63 +// _bailout_ID_2: 1.64 +// push 2 1.65 +// jmp _global_bailout_handler 1.66 +// 1.67 +// This takes about 10 extra bytes per guard. On some platforms, we can reduce 1.68 +// this overhead to 4 bytes by creating a global jump table, shared again in 1.69 +// the compartment: 1.70 +// 1.71 +// call _global_bailout_handler 1.72 +// call _global_bailout_handler 1.73 +// call _global_bailout_handler 1.74 +// call _global_bailout_handler 1.75 +// ... 1.76 +// _global_bailout_handler: 1.77 +// 1.78 +// In the bailout handler, we can recompute which entry in the table was 1.79 +// selected by subtracting the return addressed pushed by the call, from the 1.80 +// start of the table, and then dividing by the size of a (call X) entry in the 1.81 +// table. This gives us a number in [0, TableSize), which we call a 1.82 +// "BailoutId". 1.83 +// 1.84 +// Then, we can provide a per-script mapping from BailoutIds to snapshots, 1.85 +// which takes only four bytes per entry. 1.86 +// 1.87 +// This strategy does not work as given, because the bailout handler has no way 1.88 +// to compute the location of an IonScript. Currently, we do not use frame 1.89 +// pointers. To account for this we segregate frames into a limited set of 1.90 +// "frame sizes", and create a table for each frame size. We also have the 1.91 +// option of not using bailout tables, for platforms or situations where the 1.92 +// 10 byte cost is more optimal than a bailout table. See IonFrames.h for more 1.93 +// detail. 1.94 + 1.95 +static const BailoutId INVALID_BAILOUT_ID = BailoutId(-1); 1.96 + 1.97 +// Keep this arbitrarily small for now, for testing. 1.98 +static const uint32_t BAILOUT_TABLE_SIZE = 16; 1.99 + 1.100 +// Bailout return codes. 1.101 +// N.B. the relative order of these values is hard-coded into ::GenerateBailoutThunk. 1.102 +static const uint32_t BAILOUT_RETURN_OK = 0; 1.103 +static const uint32_t BAILOUT_RETURN_FATAL_ERROR = 1; 1.104 +static const uint32_t BAILOUT_RETURN_OVERRECURSED = 2; 1.105 + 1.106 +class JitCompartment; 1.107 + 1.108 +// BailoutStack is an architecture specific pointer to the stack, given by the 1.109 +// bailout handler. 1.110 +class BailoutStack; 1.111 +class InvalidationBailoutStack; 1.112 + 1.113 +// Must be implemented by each architecture. 1.114 + 1.115 +// This iterator is constructed at a time where there is no exit frame at the 1.116 +// moment. They must be initialized to the first JS frame instead of the exit 1.117 +// frame as usually done with JitFrameIterator. 1.118 +class IonBailoutIterator : public JitFrameIterator 1.119 +{ 1.120 + MachineState machine_; 1.121 + uint32_t snapshotOffset_; 1.122 + size_t topFrameSize_; 1.123 + IonScript *topIonScript_; 1.124 + 1.125 + public: 1.126 + IonBailoutIterator(const JitActivationIterator &activations, BailoutStack *sp); 1.127 + IonBailoutIterator(const JitActivationIterator &activations, InvalidationBailoutStack *sp); 1.128 + IonBailoutIterator(const JitActivationIterator &activations, const JitFrameIterator &frame); 1.129 + 1.130 + SnapshotOffset snapshotOffset() const { 1.131 + JS_ASSERT(topIonScript_); 1.132 + return snapshotOffset_; 1.133 + } 1.134 + const MachineState &machineState() const { 1.135 + return machine_; 1.136 + } 1.137 + size_t topFrameSize() const { 1.138 + JS_ASSERT(topIonScript_); 1.139 + return topFrameSize_; 1.140 + } 1.141 + IonScript *ionScript() const { 1.142 + if (topIonScript_) 1.143 + return topIonScript_; 1.144 + return JitFrameIterator::ionScript(); 1.145 + } 1.146 + 1.147 + void dump() const; 1.148 +}; 1.149 + 1.150 +bool EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp); 1.151 + 1.152 +struct BaselineBailoutInfo; 1.153 + 1.154 +// Called from a bailout thunk. Returns a BAILOUT_* error code. 1.155 +uint32_t Bailout(BailoutStack *sp, BaselineBailoutInfo **info); 1.156 + 1.157 +// Called from the invalidation thunk. Returns a BAILOUT_* error code. 1.158 +uint32_t InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, 1.159 + BaselineBailoutInfo **info); 1.160 + 1.161 +class ExceptionBailoutInfo 1.162 +{ 1.163 + size_t frameNo_; 1.164 + jsbytecode *resumePC_; 1.165 + size_t numExprSlots_; 1.166 + 1.167 + public: 1.168 + ExceptionBailoutInfo(size_t frameNo, jsbytecode *resumePC, size_t numExprSlots) 1.169 + : frameNo_(frameNo), 1.170 + resumePC_(resumePC), 1.171 + numExprSlots_(numExprSlots) 1.172 + { } 1.173 + 1.174 + ExceptionBailoutInfo() 1.175 + : frameNo_(0), 1.176 + resumePC_(nullptr), 1.177 + numExprSlots_(0) 1.178 + { } 1.179 + 1.180 + bool catchingException() const { 1.181 + return !!resumePC_; 1.182 + } 1.183 + bool propagatingIonExceptionForDebugMode() const { 1.184 + return !resumePC_; 1.185 + } 1.186 + 1.187 + size_t frameNo() const { 1.188 + MOZ_ASSERT(catchingException()); 1.189 + return frameNo_; 1.190 + } 1.191 + jsbytecode *resumePC() const { 1.192 + MOZ_ASSERT(catchingException()); 1.193 + return resumePC_; 1.194 + } 1.195 + size_t numExprSlots() const { 1.196 + MOZ_ASSERT(catchingException()); 1.197 + return numExprSlots_; 1.198 + } 1.199 +}; 1.200 + 1.201 +// Called from the exception handler to enter a catch or finally block. 1.202 +// Returns a BAILOUT_* error code. 1.203 +uint32_t ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame, 1.204 + ResumeFromException *rfe, 1.205 + const ExceptionBailoutInfo &excInfo, 1.206 + bool *overrecursed); 1.207 + 1.208 +uint32_t FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo); 1.209 + 1.210 +bool CheckFrequentBailouts(JSContext *cx, JSScript *script); 1.211 + 1.212 +} // namespace jit 1.213 +} // namespace js 1.214 + 1.215 +#endif /* jit_Bailouts_h */