tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */