tor-browser

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

CompileInfo.h (14802B)


      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_CompileInfo_h
      8 #define jit_CompileInfo_h
      9 
     10 #include "mozilla/Assertions.h"  // MOZ_ASSERT
     11 #include "mozilla/Maybe.h"       // mozilla::Maybe, mozilla::Some
     12 
     13 #include <algorithm>  // std::max
     14 #include <stdint.h>   // uint32_t
     15 
     16 #include "jit/CompileWrappers.h"  // CompileRuntime
     17 #include "jit/JitFrames.h"        // MinJITStackSize
     18 #include "jit/shared/Assembler-shared.h"
     19 #include "js/TypeDecls.h"    // jsbytecode
     20 #include "vm/BindingKind.h"  // BindingLocation
     21 #include "vm/JSAtomState.h"  // JSAtomState
     22 #include "vm/JSFunction.h"   // JSFunction
     23 #include "vm/JSScript.h"     // JSScript
     24 #include "vm/Opcodes.h"      // JSOp
     25 #include "vm/Scope.h"        // BindingIter
     26 
     27 namespace js {
     28 
     29 class ModuleObject;
     30 
     31 namespace jit {
     32 
     33 class InlineScriptTree;
     34 
     35 inline unsigned StartArgSlot(JSScript* script) {
     36  // Reserved slots:
     37  // Slot 0: Environment chain.
     38  // Slot 1: Return value.
     39 
     40  // When needed:
     41  // Slot 2: Argumentsobject.
     42 
     43  // Note: when updating this, please also update the assert in
     44  // SnapshotWriter::startFrame
     45  return 2 + (script->needsArgsObj() ? 1 : 0);
     46 }
     47 
     48 inline unsigned CountArgSlots(JSScript* script, JSFunction* fun) {
     49  // Slot x + 0: This value.
     50  // Slot x + 1: Argument 1.
     51  // ...
     52  // Slot x + n: Argument n.
     53 
     54  // Note: when updating this, please also update the assert in
     55  // SnapshotWriter::startFrame
     56  return StartArgSlot(script) + (fun ? fun->nargs() + 1 : 0);
     57 }
     58 
     59 inline unsigned CountArgSlots(JSScript* script, bool hasFun,
     60                              uint32_t funArgCount) {
     61  // Same as the previous function, for use when the JSFunction is not
     62  // available.
     63  return StartArgSlot(script) + (hasFun ? funArgCount + 1 : 0);
     64 }
     65 
     66 // Contains information about the compilation source for IR being generated.
     67 class CompileInfo {
     68 public:
     69  CompileInfo(CompileRuntime* runtime, JSScript* script, jsbytecode* osrPc,
     70              bool scriptNeedsArgsObj, InlineScriptTree* inlineScriptTree)
     71      : script_(script),
     72        fun_(script->function()),
     73        osrPc_(osrPc),
     74        scriptNeedsArgsObj_(scriptNeedsArgsObj),
     75        hadEagerTruncationBailout_(script->hadEagerTruncationBailout()),
     76        hadSpeculativePhiBailout_(script->hadSpeculativePhiBailout()),
     77        hadLICMInvalidation_(script->hadLICMInvalidation()),
     78        hadReorderingBailout_(script->hadReorderingBailout()),
     79        hadBoundsCheckBailout_(script->failedBoundsCheck()),
     80        hadUnboxFoldingBailout_(script->hadUnboxFoldingBailout()),
     81        mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()),
     82        anyFormalIsForwarded_(script->anyFormalIsForwarded()),
     83        isDerivedClassConstructor_(script->isDerivedClassConstructor()),
     84        inlineScriptTree_(inlineScriptTree),
     85        hasSeenObjectEmulateUndefinedFuseIntact_(
     86            runtime->hasSeenObjectEmulateUndefinedFuseIntact()),
     87        hasSeenArrayExceedsInt32LengthFuseIntact_(
     88            runtime->hasSeenArrayExceedsInt32LengthFuseIntact()) {
     89    MOZ_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOp::LoopHead);
     90 
     91    nimplicit_ = StartArgSlot(script) /* env chain and argument obj */
     92                 + (fun_ ? 1 : 0);    /* this */
     93    nargs_ = fun_ ? fun_->nargs() : 0;
     94    nlocals_ = script->nfixed();
     95 
     96    // An extra slot is needed for global scopes because InitGLexical (stack
     97    // depth 1) is compiled as a SetProp (stack depth 2) on the global lexical
     98    // scope.
     99    uint32_t extra = script->isGlobalCode() ? 1 : 0;
    100    nstack_ = std::max<unsigned>(script->nslots() - script->nfixed(),
    101                                 MinJITStackSize) +
    102              extra;
    103    nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
    104 
    105    // For derived class constructors, find and cache the frame slot for
    106    // the .this binding. This slot is assumed to be always
    107    // observable. See isObservableFrameSlot.
    108    if (script->isDerivedClassConstructor()) {
    109      MOZ_ASSERT(script->functionHasThisBinding());
    110      for (BindingIter bi(script); bi; bi++) {
    111        if (bi.name() != runtime->names().dot_this_) {
    112          continue;
    113        }
    114        BindingLocation loc = bi.location();
    115        if (loc.kind() == BindingLocation::Kind::Frame) {
    116          thisSlotForDerivedClassConstructor_ =
    117              mozilla::Some(localSlot(loc.slot()));
    118          break;
    119        }
    120      }
    121    }
    122 
    123    // If the script uses an environment in body, the environment chain
    124    // will need to be observable.
    125    needsBodyEnvironmentObject_ = script->needsBodyEnvironment();
    126    funNeedsSomeEnvironmentObject_ =
    127        fun_ ? fun_->needsSomeEnvironmentObject() : false;
    128  }
    129 
    130  explicit CompileInfo(unsigned nlocals)
    131      : script_(nullptr),
    132        fun_(nullptr),
    133        osrPc_(nullptr),
    134        scriptNeedsArgsObj_(false),
    135        hadEagerTruncationBailout_(false),
    136        hadSpeculativePhiBailout_(false),
    137        hadLICMInvalidation_(false),
    138        hadReorderingBailout_(false),
    139        hadBoundsCheckBailout_(false),
    140        hadUnboxFoldingBailout_(false),
    141        branchHintingEnabled_(false),
    142        mayReadFrameArgsDirectly_(false),
    143        anyFormalIsForwarded_(false),
    144        inlineScriptTree_(nullptr),
    145        needsBodyEnvironmentObject_(false),
    146        funNeedsSomeEnvironmentObject_(false),
    147        hasSeenObjectEmulateUndefinedFuseIntact_(false),
    148        hasSeenArrayExceedsInt32LengthFuseIntact_(false) {
    149    nimplicit_ = 0;
    150    nargs_ = 0;
    151    nlocals_ = nlocals;
    152    nstack_ = 1; /* For FunctionCompiler::pushPhiInput/popPhiOutput */
    153    nslots_ = nlocals_ + nstack_;
    154  }
    155 
    156  JSScript* script() const { return script_; }
    157  bool compilingWasm() const { return script() == nullptr; }
    158  ModuleObject* module() const { return script_->module(); }
    159  jsbytecode* osrPc() const { return osrPc_; }
    160  InlineScriptTree* inlineScriptTree() const { return inlineScriptTree_; }
    161 
    162  // It's not safe to access the JSFunction off main thread.
    163  bool hasFunMaybeLazy() const { return fun_; }
    164  ImmGCPtr funMaybeLazy() const { return ImmGCPtr(fun_); }
    165 
    166  const char* filename() const { return script_->filename(); }
    167 
    168  unsigned lineno() const { return script_->lineno(); }
    169 
    170  // Total number of slots: args, locals, and stack.
    171  unsigned nslots() const { return nslots_; }
    172 
    173  // Number of slots needed for env chain, return value,
    174  // maybe argumentsobject and this value.
    175  unsigned nimplicit() const { return nimplicit_; }
    176  // Number of arguments (without counting this value).
    177  unsigned nargs() const { return nargs_; }
    178  // Number of slots needed for all local variables.  This includes "fixed
    179  // vars" (see above) and also block-scoped locals.
    180  unsigned nlocals() const { return nlocals_; }
    181  unsigned ninvoke() const { return nslots_ - nstack_; }
    182 
    183  uint32_t environmentChainSlot() const {
    184    MOZ_ASSERT(script());
    185    return 0;
    186  }
    187  uint32_t returnValueSlot() const {
    188    MOZ_ASSERT(script());
    189    return 1;
    190  }
    191  uint32_t argsObjSlot() const {
    192    MOZ_ASSERT(needsArgsObj());
    193    return 2;
    194  }
    195  uint32_t thisSlot() const {
    196    MOZ_ASSERT(hasFunMaybeLazy());
    197    MOZ_ASSERT(nimplicit_ > 0);
    198    return nimplicit_ - 1;
    199  }
    200  uint32_t firstArgSlot() const { return nimplicit_; }
    201  uint32_t argSlotUnchecked(uint32_t i) const {
    202    // During initialization, some routines need to get at arg
    203    // slots regardless of how regular argument access is done.
    204    MOZ_ASSERT(i < nargs_);
    205    return nimplicit_ + i;
    206  }
    207  uint32_t argSlot(uint32_t i) const {
    208    // This should only be accessed when compiling functions for
    209    // which argument accesses don't need to go through the
    210    // argument object.
    211    MOZ_ASSERT(!argsObjAliasesFormals());
    212    return argSlotUnchecked(i);
    213  }
    214  uint32_t firstLocalSlot() const { return nimplicit_ + nargs_; }
    215  uint32_t localSlot(uint32_t i) const { return firstLocalSlot() + i; }
    216  uint32_t firstStackSlot() const { return firstLocalSlot() + nlocals(); }
    217  uint32_t stackSlot(uint32_t i) const { return firstStackSlot() + i; }
    218 
    219  uint32_t totalSlots() const {
    220    MOZ_ASSERT(script() && hasFunMaybeLazy());
    221    return nimplicit() + nargs() + nlocals();
    222  }
    223 
    224  bool hasMappedArgsObj() const { return script()->hasMappedArgsObj(); }
    225  bool needsArgsObj() const { return scriptNeedsArgsObj_; }
    226  bool argsObjAliasesFormals() const {
    227    return scriptNeedsArgsObj_ && script()->hasMappedArgsObj();
    228  }
    229 
    230  bool needsBodyEnvironmentObject() const {
    231    return needsBodyEnvironmentObject_;
    232  }
    233 
    234  enum class SlotObservableKind {
    235    // This slot must be preserved because it's observable outside SSA uses.
    236    // It can't be recovered before or during bailout.
    237    ObservableNotRecoverable,
    238 
    239    // This slot must be preserved because it's observable, but it can be
    240    // recovered.
    241    ObservableRecoverable,
    242 
    243    // This slot is not observable outside SSA uses.
    244    NotObservable,
    245  };
    246 
    247  inline SlotObservableKind getSlotObservableKind(uint32_t slot) const {
    248    // Locals and expression stack slots.
    249    if (slot >= firstLocalSlot()) {
    250      // The |this| slot for a derived class constructor is a local slot.
    251      // It should never be optimized out, as a Debugger might need to perform
    252      // TDZ checks on it via, e.g., an exceptionUnwind handler. The TDZ check
    253      // is required for correctness if the handler decides to continue
    254      // execution.
    255      if (thisSlotForDerivedClassConstructor_ &&
    256          *thisSlotForDerivedClassConstructor_ == slot) {
    257        return SlotObservableKind::ObservableNotRecoverable;
    258      }
    259      return SlotObservableKind::NotObservable;
    260    }
    261 
    262    // Formal argument slots.
    263    if (slot >= firstArgSlot()) {
    264      MOZ_ASSERT(hasFunMaybeLazy());
    265      MOZ_ASSERT(slot - firstArgSlot() < nargs());
    266 
    267      // Preserve formal arguments if they might be read when creating a rest or
    268      // arguments object. In non-strict scripts, Function.arguments can create
    269      // an arguments object dynamically so we always preserve the arguments.
    270      if (mayReadFrameArgsDirectly_ || !script()->strict()) {
    271        return SlotObservableKind::ObservableRecoverable;
    272      }
    273      return SlotObservableKind::NotObservable;
    274    }
    275 
    276    // |this| slot is observable but it can be recovered.
    277    if (hasFunMaybeLazy() && slot == thisSlot()) {
    278      return SlotObservableKind::ObservableRecoverable;
    279    }
    280 
    281    // Environment chain slot.
    282    if (slot == environmentChainSlot()) {
    283      // If environments can be added in the body (after the prologue) we need
    284      // to preserve the environment chain slot. It can't be recovered.
    285      if (needsBodyEnvironmentObject()) {
    286        return SlotObservableKind::ObservableNotRecoverable;
    287      }
    288      // If the function may need an arguments object, also preserve the
    289      // environment chain because it may be needed to reconstruct the arguments
    290      // object during bailout.
    291      if (funNeedsSomeEnvironmentObject_ || needsArgsObj()) {
    292        return SlotObservableKind::ObservableRecoverable;
    293      }
    294      return SlotObservableKind::NotObservable;
    295    }
    296 
    297    // The arguments object is observable. If it does not escape, it can
    298    // be recovered.
    299    if (needsArgsObj() && slot == argsObjSlot()) {
    300      MOZ_ASSERT(hasFunMaybeLazy());
    301      return SlotObservableKind::ObservableRecoverable;
    302    }
    303 
    304    MOZ_ASSERT(slot == returnValueSlot());
    305    return SlotObservableKind::NotObservable;
    306  }
    307 
    308  // Returns true if a slot can be observed out-side the current frame while
    309  // the frame is active on the stack.  This implies that these definitions
    310  // would have to be executed and that they cannot be removed even if they
    311  // are unused.
    312  inline bool isObservableSlot(uint32_t slot) const {
    313    SlotObservableKind kind = getSlotObservableKind(slot);
    314    return (kind == SlotObservableKind::ObservableNotRecoverable ||
    315            kind == SlotObservableKind::ObservableRecoverable);
    316  }
    317 
    318  // Returns true if a slot can be recovered before or during a bailout.  A
    319  // definition which can be observed and recovered, implies that this
    320  // definition can be optimized away as long as we can compute its values.
    321  bool isRecoverableOperand(uint32_t slot) const {
    322    SlotObservableKind kind = getSlotObservableKind(slot);
    323    return (kind == SlotObservableKind::ObservableRecoverable ||
    324            kind == SlotObservableKind::NotObservable);
    325  }
    326 
    327  // Check previous bailout states to prevent doing the same bailout in the
    328  // next compilation.
    329  bool hadEagerTruncationBailout() const { return hadEagerTruncationBailout_; }
    330  bool hadSpeculativePhiBailout() const { return hadSpeculativePhiBailout_; }
    331  bool hadLICMInvalidation() const { return hadLICMInvalidation_; }
    332  bool hadReorderingBailout() const { return hadReorderingBailout_; }
    333  bool hadBoundsCheckBailout() const { return hadBoundsCheckBailout_; }
    334  bool hadUnboxFoldingBailout() const { return hadUnboxFoldingBailout_; }
    335 
    336  bool branchHintingEnabled() const {
    337    return compilingWasm() && branchHintingEnabled_;
    338  }
    339 
    340  void setBranchHinting(bool value) { branchHintingEnabled_ = value; }
    341 
    342  bool mayReadFrameArgsDirectly() const { return mayReadFrameArgsDirectly_; }
    343  bool anyFormalIsForwarded() const { return anyFormalIsForwarded_; }
    344 
    345  bool isDerivedClassConstructor() const { return isDerivedClassConstructor_; }
    346 
    347  bool hasSeenObjectEmulateUndefinedFuseIntact() const {
    348    return hasSeenObjectEmulateUndefinedFuseIntact_;
    349  }
    350 
    351  bool hasSeenArrayExceedsInt32LengthFuseIntact() const {
    352    return hasSeenArrayExceedsInt32LengthFuseIntact_;
    353  }
    354 
    355 private:
    356  unsigned nimplicit_;
    357  unsigned nargs_;
    358  unsigned nlocals_;
    359  unsigned nstack_;
    360  unsigned nslots_;
    361  mozilla::Maybe<unsigned> thisSlotForDerivedClassConstructor_;
    362  JSScript* script_;
    363  JSFunction* fun_;
    364  jsbytecode* osrPc_;
    365 
    366  bool scriptNeedsArgsObj_;
    367 
    368  // Record the state of previous bailouts in order to prevent compiling the
    369  // same function identically the next time.
    370  bool hadEagerTruncationBailout_;
    371  bool hadSpeculativePhiBailout_;
    372  bool hadLICMInvalidation_;
    373  bool hadReorderingBailout_;
    374  bool hadBoundsCheckBailout_;
    375  bool hadUnboxFoldingBailout_;
    376 
    377  bool branchHintingEnabled_;
    378 
    379  bool mayReadFrameArgsDirectly_;
    380  bool anyFormalIsForwarded_;
    381 
    382  bool isDerivedClassConstructor_;
    383 
    384  InlineScriptTree* inlineScriptTree_;
    385 
    386  // Whether a script needs environments within its body. This informs us
    387  // that the environment chain is not easy to reconstruct.
    388  bool needsBodyEnvironmentObject_;
    389  bool funNeedsSomeEnvironmentObject_;
    390 
    391  bool hasSeenObjectEmulateUndefinedFuseIntact_;
    392  bool hasSeenArrayExceedsInt32LengthFuseIntact_;
    393 };
    394 
    395 }  // namespace jit
    396 }  // namespace js
    397 
    398 #endif /* jit_CompileInfo_h */