tor-browser

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

BaselineFrame.h (14962B)


      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_BaselineFrame_h
      8 #define jit_BaselineFrame_h
      9 
     10 #include <algorithm>
     11 
     12 #include "jit/CalleeToken.h"
     13 #include "jit/JitFrames.h"
     14 #include "jit/ScriptFromCalleeToken.h"
     15 #include "vm/Stack.h"
     16 
     17 namespace js {
     18 namespace jit {
     19 
     20 class ICEntry;
     21 class ICScript;
     22 class JSJitFrameIter;
     23 
     24 // The stack looks like this, fp is the frame pointer:
     25 //
     26 // fp+y   arguments
     27 // fp  => JitFrameLayout (frame header)
     28 // fp-x   BaselineFrame
     29 //        locals
     30 //        stack values
     31 
     32 class BaselineFrame {
     33 public:
     34  enum Flags : uint32_t {
     35    // The frame has a valid return value. See also InterpreterFrame::HAS_RVAL.
     36    HAS_RVAL = 1 << 0,
     37 
     38    // The frame is running in the Baseline interpreter instead of JIT.
     39    RUNNING_IN_INTERPRETER = 1 << 1,
     40 
     41    // An initial environment has been pushed on the environment chain for
     42    // function frames that need a CallObject or eval frames that need a
     43    // VarEnvironmentObject.
     44    HAS_INITIAL_ENV = 1 << 2,
     45 
     46    // Frame has an arguments object, argsObj_.
     47    HAS_ARGS_OBJ = 1 << 4,
     48 
     49    // See InterpreterFrame::PREV_UP_TO_DATE.
     50    PREV_UP_TO_DATE = 1 << 5,
     51 
     52    // Frame has execution observed by a Debugger.
     53    //
     54    // See comment above 'isDebuggee' in vm/Realm.h for explanation
     55    // of invariants of debuggee compartments, scripts, and frames.
     56    DEBUGGEE = 1 << 6,
     57 
     58    // Frame is executing Realm-independent Jitcode, which requires
     59    // a valid interpreterScript_ field.
     60    REALM_INDEPENDENT = 1 << 7,
     61  };
     62 
     63 protected:  // Silence Clang warning about unused private fields.
     64  // The fields below are only valid if RUNNING_IN_INTERPRETER or
     65  // isRealmIndependent().
     66  JSScript* interpreterScript_;
     67  // The fields below are only valid if RUNNING_IN_INTERPRETER.
     68  jsbytecode* interpreterPC_;
     69  ICEntry* interpreterICEntry_;
     70 
     71  JSObject* envChain_;        // Environment chain (always initialized).
     72  ICScript* icScript_;        // IC script (initialized if Warp is enabled).
     73  ArgumentsObject* argsObj_;  // If HAS_ARGS_OBJ, the arguments object.
     74 
     75  // We need to split the Value into 2 fields of 32 bits, otherwise the C++
     76  // compiler may add some padding between the fields.
     77  uint32_t loScratchValue_;
     78  uint32_t hiScratchValue_;
     79  uint32_t flags_;
     80 #ifdef DEBUG
     81  // Size of the frame. Stored in DEBUG builds when calling into C++. This is
     82  // BaselineFrame::Size() + the size of the local and expression stack Values.
     83  //
     84  // We don't store this in release builds because it's redundant with the frame
     85  // size computed from the frame pointers. In debug builds it's still useful
     86  // for assertions.
     87  uint32_t debugFrameSize_;
     88 #else
     89  uint32_t unused_;
     90 #endif
     91  uint32_t loReturnValue_;  // If HAS_RVAL, the frame's return value.
     92  uint32_t hiReturnValue_;
     93 
     94 public:
     95  [[nodiscard]] bool initForOsr(InterpreterFrame* fp, uint32_t numStackValues);
     96 
     97 #ifdef DEBUG
     98  uint32_t debugFrameSize() const { return debugFrameSize_; }
     99  void setDebugFrameSize(uint32_t frameSize) { debugFrameSize_ = frameSize; }
    100 #endif
    101 
    102  JSObject* environmentChain() const { return envChain_; }
    103  void setEnvironmentChain(JSObject* envChain) { envChain_ = envChain; }
    104 
    105  template <typename SpecificEnvironment>
    106  inline void pushOnEnvironmentChain(SpecificEnvironment& env);
    107  template <typename SpecificEnvironment>
    108  inline void popOffEnvironmentChain();
    109  inline void replaceInnermostEnvironment(EnvironmentObject& env);
    110 
    111  CalleeToken calleeToken() const { return framePrefix()->calleeToken(); }
    112  void replaceCalleeToken(CalleeToken token) {
    113    framePrefix()->replaceCalleeToken(token);
    114  }
    115  bool isConstructing() const {
    116    return CalleeTokenIsConstructing(calleeToken());
    117  }
    118  JSScript* script() const {
    119    return MaybeForwardedScriptFromCalleeToken(calleeToken());
    120  }
    121  JSFunction* callee() const { return CalleeTokenToFunction(calleeToken()); }
    122  Value calleev() const { return ObjectValue(*callee()); }
    123 
    124  size_t numValueSlots(size_t frameSize) const {
    125    MOZ_ASSERT(frameSize == debugFrameSize());
    126 
    127    MOZ_ASSERT(frameSize >= BaselineFrame::Size());
    128    frameSize -= BaselineFrame::Size();
    129 
    130    MOZ_ASSERT((frameSize % sizeof(Value)) == 0);
    131    return frameSize / sizeof(Value);
    132  }
    133 
    134  Value newTarget() const {
    135    MOZ_ASSERT(isFunctionFrame());
    136    MOZ_ASSERT(!callee()->isArrow());
    137 
    138    if (isConstructing()) {
    139      unsigned pushedArgs = std::max(numFormalArgs(), numActualArgs());
    140      return argv()[pushedArgs];
    141    }
    142    return UndefinedValue();
    143  }
    144 
    145 #ifdef DEBUG
    146  size_t debugNumValueSlots() const { return numValueSlots(debugFrameSize()); }
    147 #endif
    148 
    149  Value* valueSlot(size_t slot) const {
    150 #ifndef ENABLE_PORTABLE_BASELINE_INTERP
    151    // Assert that we're within the frame, but only if the "debug
    152    // frame size" has been set. Ordinarily if we are in C++ code
    153    // looking upward at a baseline frame, it will be, because it is
    154    // set for the *previous* frame when we push an exit frame and
    155    // call back into C++ from generated baseline code. However, the
    156    // portable baseline interpreter uses accessors on BaselineFrame
    157    // directly within the active frame and so the "debug frame size"
    158    // hasn't been set (and it would be expensive to constantly update
    159    // it). Because this is only used for assertions, and is not
    160    // needed for correctness, we can disable this check below when
    161    // PBL is enabled.
    162    MOZ_ASSERT(slot < debugNumValueSlots());
    163 #endif
    164    return (Value*)this - (slot + 1);
    165  }
    166 
    167  static size_t frameSizeForNumValueSlots(size_t numValueSlots) {
    168    return BaselineFrame::Size() + numValueSlots * sizeof(Value);
    169  }
    170 
    171  Value& unaliasedFormal(
    172      unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
    173    MOZ_ASSERT(i < numFormalArgs());
    174    MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals() &&
    175                                     !script()->formalIsAliased(i));
    176    return argv()[i];
    177  }
    178 
    179  Value& unaliasedActual(
    180      unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
    181    MOZ_ASSERT(i < numActualArgs());
    182    MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
    183    MOZ_ASSERT_IF(checkAliasing && i < numFormalArgs(),
    184                  !script()->formalIsAliased(i));
    185    return argv()[i];
    186  }
    187 
    188  Value& unaliasedLocal(uint32_t i) const {
    189    MOZ_ASSERT(i < script()->nfixed());
    190    return *valueSlot(i);
    191  }
    192 
    193  unsigned numActualArgs() const { return framePrefix()->numActualArgs(); }
    194  unsigned numFormalArgs() const { return script()->function()->nargs(); }
    195  Value& thisArgument() const {
    196    MOZ_ASSERT(isFunctionFrame());
    197    return framePrefix()->thisv();
    198  }
    199  Value* argv() const { return framePrefix()->actualArgs(); }
    200 
    201  [[nodiscard]] bool saveGeneratorSlots(JSContext* cx, unsigned nslots,
    202                                        ArrayObject* dest) const;
    203 
    204 public:
    205  void prepareForBaselineInterpreterToJitOSR() {
    206    // Clearing the RUNNING_IN_INTERPRETER flag is sufficient, but we also null
    207    // out the interpreter fields to ensure we don't use stale values.
    208    flags_ &= ~RUNNING_IN_INTERPRETER;
    209    if (JS::Prefs::experimental_self_hosted_cache() && script()->selfHosted()) {
    210      flags_ |= REALM_INDEPENDENT;
    211    }
    212    interpreterPC_ = nullptr;
    213  }
    214 
    215 private:
    216  bool uninlineIsProfilerSamplingEnabled(JSContext* cx);
    217 
    218 public:
    219  // Switch a JIT frame on the stack to Interpreter mode. The caller is
    220  // responsible for patching the return address into this frame to a location
    221  // in the interpreter code. Also assert profiler sampling has been suppressed
    222  // so the sampler thread doesn't see an inconsistent state while we are
    223  // patching frames.
    224  void switchFromJitToInterpreter(JSContext* cx, jsbytecode* pc) {
    225    MOZ_ASSERT(!uninlineIsProfilerSamplingEnabled(cx));
    226    MOZ_ASSERT(!runningInInterpreter());
    227    flags_ |= RUNNING_IN_INTERPRETER;
    228    setInterpreterFields(pc);
    229  }
    230  void switchFromJitToInterpreterAtPrologue(JSContext* cx) {
    231    MOZ_ASSERT(!uninlineIsProfilerSamplingEnabled(cx));
    232    MOZ_ASSERT(!runningInInterpreter());
    233    flags_ |= RUNNING_IN_INTERPRETER;
    234    setInterpreterFieldsForPrologue(script());
    235  }
    236 
    237  // Like switchFromJitToInterpreter, but set the interpreterICEntry_ field to
    238  // nullptr. Initializing this field requires a binary search on the
    239  // JitScript's ICEntry list but the exception handler never returns to this
    240  // pc anyway so we can avoid the overhead.
    241  void switchFromJitToInterpreterForExceptionHandler(JSContext* cx,
    242                                                     jsbytecode* pc) {
    243    MOZ_ASSERT(!uninlineIsProfilerSamplingEnabled(cx));
    244    MOZ_ASSERT(!runningInInterpreter());
    245    flags_ |= RUNNING_IN_INTERPRETER;
    246    interpreterScript_ = script();
    247    interpreterPC_ = pc;
    248    interpreterICEntry_ = nullptr;
    249  }
    250 
    251  bool runningInInterpreter() const { return flags_ & RUNNING_IN_INTERPRETER; }
    252  bool isRealmIndependent() const { return flags_ & REALM_INDEPENDENT; }
    253 
    254  JSScript* interpreterScript() const {
    255    MOZ_ASSERT(runningInInterpreter());
    256    return interpreterScript_;
    257  }
    258 
    259  jsbytecode* interpreterPC() const {
    260    MOZ_ASSERT(runningInInterpreter());
    261    return interpreterPC_;
    262  }
    263  jsbytecode*& interpreterPC() {
    264    MOZ_ASSERT(runningInInterpreter());
    265    return interpreterPC_;
    266  }
    267 
    268  ICEntry* interpreterICEntry() const {
    269    MOZ_ASSERT(runningInInterpreter());
    270    return interpreterICEntry_;
    271  }
    272  ICEntry*& interpreterICEntry() {
    273    MOZ_ASSERT(runningInInterpreter());
    274    return interpreterICEntry_;
    275  }
    276 
    277  void setInterpreterFields(JSScript* script, jsbytecode* pc);
    278 
    279  void setInterpreterFields(jsbytecode* pc) {
    280    setInterpreterFields(script(), pc);
    281  }
    282 
    283  // Initialize interpreter fields for resuming in the prologue (before the
    284  // argument type check ICs).
    285  void setInterpreterFieldsForPrologue(JSScript* script);
    286 
    287  ICScript* icScript() const { return icScript_; }
    288  void setICScript(ICScript* icScript) { icScript_ = icScript; }
    289 
    290  // The script that owns the current ICScript.
    291  JSScript* outerScript() const;
    292 
    293  bool hasReturnValue() const { return flags_ & HAS_RVAL; }
    294  MutableHandleValue returnValue() {
    295    if (!hasReturnValue()) {
    296      addressOfReturnValue()->setUndefined();
    297    }
    298    return MutableHandleValue::fromMarkedLocation(addressOfReturnValue());
    299  }
    300  void setReturnValue(const Value& v) {
    301    returnValue().set(v);
    302    flags_ |= HAS_RVAL;
    303  }
    304  inline Value* addressOfReturnValue() {
    305    return reinterpret_cast<Value*>(&loReturnValue_);
    306  }
    307 
    308  bool hasInitialEnvironment() const { return flags_ & HAS_INITIAL_ENV; }
    309 
    310  inline CallObject& callObj() const;
    311 
    312  void setFlag(uint32_t flag) { flags_ |= flag; }
    313  void setFlags(uint32_t flags) { flags_ = flags; }
    314 
    315  [[nodiscard]] inline bool pushLexicalEnvironment(JSContext* cx,
    316                                                   Handle<LexicalScope*> scope);
    317  template <bool IsDebuggee>
    318  [[nodiscard]] inline bool freshenLexicalEnvironment(
    319      JSContext* cx, const jsbytecode* pc = nullptr);
    320  template <bool IsDebuggee>
    321  [[nodiscard]] inline bool recreateLexicalEnvironment(
    322      JSContext* cx, const jsbytecode* pc = nullptr);
    323 
    324  [[nodiscard]] bool initFunctionEnvironmentObjects(JSContext* cx);
    325  [[nodiscard]] bool pushClassBodyEnvironment(JSContext* cx,
    326                                              Handle<ClassBodyScope*> scope);
    327  [[nodiscard]] bool pushVarEnvironment(JSContext* cx, Handle<Scope*> scope);
    328 
    329  void initArgsObjUnchecked(ArgumentsObject& argsobj) {
    330    flags_ |= HAS_ARGS_OBJ;
    331    argsObj_ = &argsobj;
    332  }
    333  void initArgsObj(ArgumentsObject& argsobj) {
    334    MOZ_ASSERT(script()->needsArgsObj());
    335    initArgsObjUnchecked(argsobj);
    336  }
    337  bool hasArgsObj() const { return flags_ & HAS_ARGS_OBJ; }
    338  ArgumentsObject& argsObj() const {
    339    MOZ_ASSERT(hasArgsObj());
    340    MOZ_ASSERT(script()->needsArgsObj());
    341    return *argsObj_;
    342  }
    343 
    344  bool prevUpToDate() const { return flags_ & PREV_UP_TO_DATE; }
    345  void setPrevUpToDate() { flags_ |= PREV_UP_TO_DATE; }
    346  void unsetPrevUpToDate() { flags_ &= ~PREV_UP_TO_DATE; }
    347 
    348  bool isDebuggee() const { return flags_ & DEBUGGEE; }
    349  void setIsDebuggee() { flags_ |= DEBUGGEE; }
    350  inline void unsetIsDebuggee();
    351 
    352  void trace(JSTracer* trc, const JSJitFrameIter& frame);
    353 
    354  bool isGlobalFrame() const { return script()->isGlobalCode(); }
    355  bool isModuleFrame() const { return script()->isModule(); }
    356  bool isEvalFrame() const { return script()->isForEval(); }
    357  bool isFunctionFrame() const {
    358    return CalleeTokenIsFunction(calleeToken()) && !isModuleFrame();
    359  }
    360  bool isDebuggerEvalFrame() const { return false; }
    361 
    362  JitFrameLayout* framePrefix() const {
    363    uint8_t* fp = (uint8_t*)this + Size();
    364    return (JitFrameLayout*)fp;
    365  }
    366 
    367  static size_t Size() { return sizeof(BaselineFrame); }
    368 
    369  // The reverseOffsetOf methods below compute the offset relative to the
    370  // frame's base pointer. Since the stack grows down, these offsets are
    371  // negative.
    372 
    373 #ifdef DEBUG
    374  static int reverseOffsetOfDebugFrameSize() {
    375    return -int(Size()) + offsetof(BaselineFrame, debugFrameSize_);
    376  }
    377 #endif
    378 
    379  // The scratch value slot can either be used as a Value slot or as two
    380  // separate 32-bit integer slots.
    381  static int reverseOffsetOfScratchValueLow32() {
    382    return -int(Size()) + offsetof(BaselineFrame, loScratchValue_);
    383  }
    384  static int reverseOffsetOfScratchValueHigh32() {
    385    return -int(Size()) + offsetof(BaselineFrame, hiScratchValue_);
    386  }
    387  static int reverseOffsetOfScratchValue() {
    388    return reverseOffsetOfScratchValueLow32();
    389  }
    390 
    391  static int reverseOffsetOfEnvironmentChain() {
    392    return -int(Size()) + offsetof(BaselineFrame, envChain_);
    393  }
    394  static int reverseOffsetOfArgsObj() {
    395    return -int(Size()) + offsetof(BaselineFrame, argsObj_);
    396  }
    397  static int reverseOffsetOfFlags() {
    398    return -int(Size()) + offsetof(BaselineFrame, flags_);
    399  }
    400  static int reverseOffsetOfReturnValue() {
    401    return -int(Size()) + offsetof(BaselineFrame, loReturnValue_);
    402  }
    403  static int reverseOffsetOfInterpreterScript() {
    404    return -int(Size()) + offsetof(BaselineFrame, interpreterScript_);
    405  }
    406  static int reverseOffsetOfInterpreterPC() {
    407    return -int(Size()) + offsetof(BaselineFrame, interpreterPC_);
    408  }
    409  static int reverseOffsetOfInterpreterICEntry() {
    410    return -int(Size()) + offsetof(BaselineFrame, interpreterICEntry_);
    411  }
    412  static int reverseOffsetOfICScript() {
    413    return -int(Size()) + offsetof(BaselineFrame, icScript_);
    414  }
    415  static int reverseOffsetOfLocal(size_t index) {
    416    return -int(Size()) - (index + 1) * sizeof(Value);
    417  }
    418 };
    419 
    420 // Ensure the frame is 8-byte aligned (required on ARM).
    421 static_assert((sizeof(BaselineFrame) % 8) == 0, "frame must be 8-byte aligned");
    422 
    423 }  // namespace jit
    424 }  // namespace js
    425 
    426 #endif /* jit_BaselineFrame_h */