Bailouts.h (8583B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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 "mozilla/Assertions.h" // MOZ_ASSERT 11 12 #include <stddef.h> // size_t 13 #include <stdint.h> // uint8_t, uint32_t 14 15 #include "jstypes.h" 16 17 #include "jit/IonTypes.h" // js::jit::Bailout{Id,Kind}, js::jit::SnapshotOffset 18 #include "jit/MachineState.h" // js::jit::MachineState 19 #include "js/TypeDecls.h" // jsbytecode 20 #include "vm/JSContext.h" // JSContext 21 22 namespace js { 23 24 class AbstractFramePtr; 25 26 namespace jit { 27 28 // [SMDOC] IonMonkey Bailouts 29 // 30 // A "bailout" is the process of recovering a baseline interpreter frame from an 31 // IonFrame. Bailouts are implemented in js::jit::BailoutIonToBaseline, which 32 // has the following callers: 33 // 34 // * js::jit::Bailout - This is used when a guard fails in the Ion code 35 // itself; for example, an LGuardShape fails or an LAddI overflows. See 36 // callers of CodeGenerator::bailoutFrom() for more examples. 37 // 38 // * js::jit::ExceptionHandlerBailout - Ion doesn't implement `catch` or 39 // `finally`. If an exception is thrown and would be caught by an Ion frame, 40 // we bail out instead. 41 // 42 // * js::jit::InvalidationBailout - We returned to Ion code that was 43 // invalidated while it was on the stack. See "OSI" below. Ion code can be 44 // invalidated for several reasons: when GC evicts Ion code to save memory, 45 // for example, or when assumptions baked into the jitted code are 46 // invalidated by the VM. 47 // 48 // (Some stack inspection can be done without bailing out, including GC stack 49 // marking, Error object construction, and Gecko profiler sampling.) 50 // 51 // Consider the first case. When an Ion guard fails, we can't continue in 52 // Ion. There's no IC fallback case coming to save us; we've got a broken 53 // assumption baked into the code we're running. So we jump to an out-of-line 54 // code path that's responsible for abandoning Ion execution and resuming in 55 // the baseline interpreter: the bailout path. 56 // 57 // We were in the midst of optimized Ion code, so bits of program state may be 58 // in registers or spilled to the native stack; values may be unboxed; some 59 // objects may have been optimized away; thanks to inlining, whole call frames 60 // may be missing. The bailout path must put all these pieces back together 61 // into the structure the baseline interpreter expects. 62 // 63 // The data structure that makes this possible is called a *snapshot*. 64 // Snapshots are created during Ion codegen and associated with the IonScript; 65 // they tell how to recover each value in a BaselineFrame from the current 66 // machine state at a given point in the Ion JIT code. This is potentially 67 // different at every place in an Ion script where we might bail out. (See 68 // Snapshots.h.) 69 // 70 // The bailout path performs roughly the following steps: 71 // 72 // 1. Push a snapshot index and the frame size to the native stack. 73 // 2. Spill all registers. 74 // 3. Call js::jit::Bailout to reconstruct the baseline frame(s). 75 // 4. memmove() those to the right place on the native stack. 76 // 5. Jump into the baseline interpreter. 77 // 78 // When C++ code invalidates Ion code, we do on-stack invalidation, or OSI, to 79 // arrange for every affected Ion frame on the stack to bail out as soon as 80 // control returns to it. OSI patches every instruction in the JIT code that's 81 // at a return address currently on the stack. See InvalidateActivation. 82 // 83 // 84 // ## Bailout path implementation details 85 // 86 // Ion code has a lot of guards, so each bailout path must be small. Steps 2 87 // and 3 above are therefore implemented by a shared per-Runtime trampoline, 88 // rt->jitRuntime()->getGenericBailoutHandler(). 89 // 90 // We implement step 1 like this: 91 // 92 // _bailout_ID_1: 93 // push 1 94 // jmp _deopt 95 // _bailout_ID_2: 96 // push 2 97 // jmp _deopt 98 // ... 99 // _deopt: 100 // push imm(FrameSize) 101 // call _global_bailout_handler 102 103 // BailoutStack is an architecture specific pointer to the stack, given by the 104 // bailout handler. 105 class BailoutStack; 106 class InvalidationBailoutStack; 107 108 class IonScript; 109 class InlineFrameIterator; 110 class JitActivation; 111 class JitActivationIterator; 112 class JSJitFrameIter; 113 struct ResumeFromException; 114 115 // Must be implemented by each architecture. 116 117 // This structure is constructed before recovering the baseline frames for a 118 // bailout. It records all information extracted from the stack, and which are 119 // needed for the JSJitFrameIter. 120 class BailoutFrameInfo { 121 MachineState machine_; 122 uint8_t* framePointer_; 123 IonScript* topIonScript_; 124 uint32_t snapshotOffset_; 125 JitActivation* activation_; 126 127 void attachOnJitActivation(const JitActivationIterator& activations); 128 129 public: 130 BailoutFrameInfo(const JitActivationIterator& activations, BailoutStack* sp); 131 BailoutFrameInfo(const JitActivationIterator& activations, 132 InvalidationBailoutStack* sp); 133 BailoutFrameInfo(const JitActivationIterator& activations, 134 const JSJitFrameIter& frame); 135 ~BailoutFrameInfo(); 136 137 uint8_t* fp() const { return framePointer_; } 138 SnapshotOffset snapshotOffset() const { return snapshotOffset_; } 139 const MachineState* machineState() const { return &machine_; } 140 IonScript* ionScript() const { return topIonScript_; } 141 JitActivation* activation() const { return activation_; } 142 }; 143 144 [[nodiscard]] bool EnsureHasEnvironmentObjects(JSContext* cx, 145 AbstractFramePtr fp); 146 147 struct BaselineBailoutInfo; 148 149 // Called from a bailout thunk. 150 [[nodiscard]] bool Bailout(BailoutStack* sp, BaselineBailoutInfo** info); 151 152 // Called from the invalidation thunk. 153 [[nodiscard]] bool InvalidationBailout(InvalidationBailoutStack* sp, 154 BaselineBailoutInfo** info); 155 156 class ExceptionBailoutInfo { 157 size_t frameNo_; 158 jsbytecode* resumePC_; 159 size_t numExprSlots_; 160 bool isFinally_ = false; 161 RootedValue finallyException_; 162 RootedValue finallyExceptionStack_; 163 bool forcedReturn_; 164 165 public: 166 ExceptionBailoutInfo(JSContext* cx, size_t frameNo, jsbytecode* resumePC, 167 size_t numExprSlots) 168 : frameNo_(frameNo), 169 resumePC_(resumePC), 170 numExprSlots_(numExprSlots), 171 finallyException_(cx), 172 finallyExceptionStack_(cx), 173 forcedReturn_(cx->isPropagatingForcedReturn()) {} 174 175 explicit ExceptionBailoutInfo(JSContext* cx) 176 : frameNo_(0), 177 resumePC_(nullptr), 178 numExprSlots_(0), 179 finallyException_(cx), 180 finallyExceptionStack_(cx), 181 forcedReturn_(cx->isPropagatingForcedReturn()) {} 182 183 bool catchingException() const { return !!resumePC_; } 184 bool propagatingIonExceptionForDebugMode() const { return !resumePC_; } 185 186 size_t frameNo() const { 187 MOZ_ASSERT(catchingException()); 188 return frameNo_; 189 } 190 jsbytecode* resumePC() const { 191 MOZ_ASSERT(catchingException()); 192 return resumePC_; 193 } 194 size_t numExprSlots() const { 195 MOZ_ASSERT(catchingException()); 196 return numExprSlots_; 197 } 198 199 bool isFinally() const { return isFinally_; } 200 void setFinallyException(const JS::Value& exception, 201 const JS::Value& exceptionStack) { 202 MOZ_ASSERT(!isFinally()); 203 isFinally_ = true; 204 finallyException_ = exception; 205 finallyExceptionStack_ = exceptionStack; 206 } 207 HandleValue finallyException() const { 208 MOZ_ASSERT(isFinally()); 209 return finallyException_; 210 } 211 HandleValue finallyExceptionStack() const { 212 MOZ_ASSERT(isFinally()); 213 return finallyExceptionStack_; 214 } 215 216 bool forcedReturn() const { return forcedReturn_; } 217 }; 218 219 // Called from the exception handler to enter a catch or finally block. 220 [[nodiscard]] bool ExceptionHandlerBailout(JSContext* cx, 221 const InlineFrameIterator& frame, 222 ResumeFromException* rfe, 223 const ExceptionBailoutInfo& excInfo); 224 225 [[nodiscard]] bool FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfoArg); 226 227 #ifdef DEBUG 228 [[nodiscard]] bool AssertBailoutStackDepth(JSContext* cx, JSScript* script, 229 jsbytecode* pc, ResumeMode mode, 230 uint32_t exprStackSlots); 231 #endif 232 233 } // namespace jit 234 } // namespace js 235 236 #endif /* jit_Bailouts_h */