tor-browser

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

WarpBuilder.h (12535B)


      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_WarpBuilder_h
      8 #define jit_WarpBuilder_h
      9 
     10 #include <initializer_list>
     11 
     12 #include "ds/InlineTable.h"
     13 #include "jit/JitContext.h"
     14 #include "jit/MIR-wasm.h"
     15 #include "jit/MIR.h"
     16 #include "jit/WarpBuilderShared.h"
     17 #include "jit/WarpSnapshot.h"
     18 #include "vm/Opcodes.h"
     19 
     20 namespace js {
     21 namespace jit {
     22 
     23 // JSOps not yet supported by WarpBuilder. See warning at the end of the list.
     24 #define WARP_UNSUPPORTED_OPCODE_LIST(_)  \
     25  /* Intentionally not implemented */    \
     26  _(ForceInterpreter)                    \
     27  /* With */                             \
     28  _(EnterWith)                           \
     29  _(LeaveWith)                           \
     30  /* Eval */                             \
     31  _(Eval)                                \
     32  _(StrictEval)                          \
     33  _(SpreadEval)                          \
     34  _(StrictSpreadEval)                    \
     35  /* Super */                            \
     36  _(SetPropSuper)                        \
     37  _(SetElemSuper)                        \
     38  _(StrictSetPropSuper)                  \
     39  _(StrictSetElemSuper)                  \
     40  /* Generators / Async (bug 1317690) */ \
     41  _(IsGenClosing)                        \
     42  _(Resume)                              \
     43  /* Misc */                             \
     44  _(DelName)                             \
     45  _(SetIntrinsic)                        \
     46  /* Private Fields */                   \
     47  _(GetAliasedDebugVar)                  \
     48  /* Non-syntactic scope */              \
     49  _(NonSyntacticGlobalThis)              \
     50  // === !! WARNING WARNING WARNING !! ===
     51  // Do you really want to sacrifice performance by not implementing this
     52  // operation in the optimizing compiler?
     53 
     54 class MIRGenerator;
     55 class MIRGraph;
     56 class WarpSnapshot;
     57 
     58 enum class CacheKind : uint8_t;
     59 
     60 // [SMDOC] Control Flow handling in WarpBuilder.
     61 //
     62 // WarpBuilder traverses the script's bytecode and compiles each instruction to
     63 // corresponding MIR instructions. Handling control flow bytecode ops requires
     64 // some special machinery:
     65 //
     66 // Forward branches
     67 // ----------------
     68 // Most branches in the bytecode are forward branches to a JSOp::JumpTarget
     69 // instruction that we have not inspected yet. We compile them in two phases:
     70 //
     71 // 1) When compiling the source instruction: the MBasicBlock is terminated
     72 //    with a control instruction that has a nullptr successor block. We also add
     73 //    a PendingEdge instance to the PendingEdges list for the target bytecode
     74 //    location.
     75 //
     76 // 2) When finally compiling the JSOp::JumpTarget: WarpBuilder::build_JumpTarget
     77 //    creates the target block and uses the list of PendingEdges to 'link' the
     78 //    blocks.
     79 //
     80 // Loops
     81 // -----
     82 // Loops may be nested within other loops, so each WarpBuilder has a LoopState
     83 // stack. This is used to link the backedge to the loop's header block.
     84 //
     85 // Unreachable/dead code
     86 // ---------------------
     87 // Some bytecode instructions never fall through to the next instruction, for
     88 // example JSOp::Return, JSOp::Goto, or JSOp::Throw. Code after such
     89 // instructions is guaranteed to be dead so WarpBuilder skips it until it gets
     90 // to a jump target instruction with pending edges.
     91 //
     92 // Note: The frontend may generate unnecessary JSOp::JumpTarget instructions we
     93 // can ignore when they have no incoming pending edges.
     94 //
     95 // Try-catch
     96 // ---------
     97 // WarpBuilder supports scripts with try-catch by only compiling the try-block
     98 // and bailing out (to the Baseline Interpreter) from the exception handler
     99 // whenever we need to execute the catch-block.
    100 //
    101 // Because we don't compile the catch-block and the code after the try-catch may
    102 // only be reachable via the catch-block, Baseline's BytecodeAnalysis ensures
    103 // Baseline does not attempt OSR into Warp at loops that are only reachable via
    104 // catch/finally blocks.
    105 //
    106 // Finally-blocks are compiled by WarpBuilder, but when we have to enter a
    107 // finally-block from the exception handler, we bail out to the Baseline
    108 // Interpreter.
    109 
    110 // PendingEdge is used whenever a block is terminated with a forward branch in
    111 // the bytecode. When we reach the jump target we use this information to link
    112 // the block to the jump target's block.
    113 class PendingEdge {
    114  MBasicBlock* block_;
    115  uint32_t successor_;
    116  uint8_t numToPop_;
    117 
    118 public:
    119  PendingEdge(MBasicBlock* block, uint32_t successor, uint32_t numToPop)
    120      : block_(block), successor_(successor), numToPop_(numToPop) {
    121    MOZ_ASSERT(numToPop_ == numToPop, "value must fit in field");
    122  }
    123 
    124  MBasicBlock* block() const { return block_; }
    125  uint32_t successor() const { return successor_; }
    126  uint8_t numToPop() const { return numToPop_; }
    127 };
    128 
    129 // PendingEdgesMap maps a bytecode instruction to a Vector of PendingEdges
    130 // targeting it. We use InlineMap<> for this because most of the time there are
    131 // only a few pending edges but there can be many when switch-statements are
    132 // involved.
    133 using PendingEdges = Vector<PendingEdge, 2, SystemAllocPolicy>;
    134 using PendingEdgesMap =
    135    InlineMap<jsbytecode*, PendingEdges, 8, PointerHasher<jsbytecode*>,
    136              SystemAllocPolicy>;
    137 
    138 // LoopState stores information about a loop that's being compiled to MIR.
    139 class LoopState {
    140  MBasicBlock* header_ = nullptr;
    141 
    142 public:
    143  explicit LoopState(MBasicBlock* header) : header_(header) {}
    144 
    145  MBasicBlock* header() const { return header_; }
    146 };
    147 using LoopStateStack = Vector<LoopState, 4, JitAllocPolicy>;
    148 
    149 // Data that is shared across all WarpBuilders for a given compilation.
    150 class MOZ_STACK_CLASS WarpCompilation {
    151  // The total loop depth, including loops in the caller while
    152  // compiling inlined functions.
    153  uint32_t loopDepth_ = 0;
    154 
    155  // Loop phis for iterators that need to be kept alive.
    156  PhiVector iterators_;
    157 
    158 public:
    159  explicit WarpCompilation(TempAllocator& alloc) : iterators_(alloc) {}
    160 
    161  uint32_t loopDepth() const { return loopDepth_; }
    162  void incLoopDepth() { loopDepth_++; }
    163  void decLoopDepth() {
    164    MOZ_ASSERT(loopDepth() > 0);
    165    loopDepth_--;
    166  }
    167 
    168  PhiVector* iterators() { return &iterators_; }
    169 };
    170 
    171 // WarpBuilder builds a MIR graph from WarpSnapshot. Unlike WarpOracle,
    172 // WarpBuilder can run off-thread.
    173 class MOZ_STACK_CLASS WarpBuilder : public WarpBuilderShared {
    174  WarpCompilation* warpCompilation_;
    175  MIRGraph& graph_;
    176  const CompileInfo& info_;
    177  const WarpScriptSnapshot* scriptSnapshot_;
    178  JSScript* script_;
    179 
    180  // Pointer to a WarpOpSnapshot or nullptr if we reached the end of the list.
    181  // Because bytecode is compiled from first to last instruction (and
    182  // WarpOpSnapshot is sorted the same way), the iterator always moves forward.
    183  const WarpOpSnapshot* opSnapshotIter_ = nullptr;
    184 
    185  // Note: loopStack_ is builder-specific. loopStack_.length is the
    186  // depth relative to the current script.  The overall loop depth is
    187  // stored in the WarpCompilation.
    188  LoopStateStack loopStack_;
    189  PendingEdgesMap pendingEdges_;
    190 
    191  // These are only initialized when building an inlined script.
    192  WarpBuilder* callerBuilder_ = nullptr;
    193  MResumePoint* callerResumePoint_ = nullptr;
    194  CallInfo* inlineCallInfo_ = nullptr;
    195 
    196  WarpCompilation* warpCompilation() const { return warpCompilation_; }
    197  MIRGraph& graph() { return graph_; }
    198  const WarpScriptSnapshot* scriptSnapshot() const { return scriptSnapshot_; }
    199 
    200  uint32_t loopDepth() const { return warpCompilation_->loopDepth(); }
    201  void incLoopDepth() { warpCompilation_->incLoopDepth(); }
    202  void decLoopDepth() { warpCompilation_->decLoopDepth(); }
    203  PhiVector* iterators() { return warpCompilation_->iterators(); }
    204 
    205  WarpBuilder* callerBuilder() const { return callerBuilder_; }
    206  MResumePoint* callerResumePoint() const { return callerResumePoint_; }
    207 
    208  BytecodeSite* newBytecodeSite(BytecodeLocation loc);
    209 
    210  const WarpOpSnapshot* getOpSnapshotImpl(BytecodeLocation loc,
    211                                          WarpOpSnapshot::Kind kind);
    212 
    213  template <typename T>
    214  const T* getOpSnapshot(BytecodeLocation loc) {
    215    const WarpOpSnapshot* snapshot = getOpSnapshotImpl(loc, T::ThisKind);
    216    return snapshot ? snapshot->as<T>() : nullptr;
    217  }
    218 
    219  void initBlock(MBasicBlock* block);
    220  [[nodiscard]] bool startNewEntryBlock(size_t stackDepth,
    221                                        BytecodeLocation loc);
    222  [[nodiscard]] bool startNewBlock(MBasicBlock* predecessor,
    223                                   BytecodeLocation loc, size_t numToPop = 0);
    224  [[nodiscard]] bool startNewLoopHeaderBlock(BytecodeLocation loopHead);
    225  [[nodiscard]] bool startNewOsrPreHeaderBlock(BytecodeLocation loopHead);
    226 
    227  bool hasTerminatedBlock() const { return current == nullptr; }
    228  void setTerminatedBlock() { current = nullptr; }
    229 
    230  [[nodiscard]] bool addPendingEdge(BytecodeLocation target, MBasicBlock* block,
    231                                    uint32_t successor, uint32_t numToPop = 0);
    232  [[nodiscard]] bool buildForwardGoto(BytecodeLocation target);
    233  [[nodiscard]] bool buildBackedge();
    234  [[nodiscard]] bool buildTestBackedge(BytecodeLocation loc);
    235 
    236  [[nodiscard]] bool addIteratorLoopPhis(BytecodeLocation loopHead);
    237 
    238  [[nodiscard]] bool buildPrologue();
    239  [[nodiscard]] bool buildBody();
    240 
    241  [[nodiscard]] bool buildInlinePrologue();
    242 
    243  [[nodiscard]] bool buildIC(BytecodeLocation loc, CacheKind kind,
    244                             std::initializer_list<MDefinition*> inputs);
    245  [[nodiscard]] bool buildBailoutForColdIC(BytecodeLocation loc,
    246                                           CacheKind kind);
    247 
    248  [[nodiscard]] bool buildEnvironmentChain();
    249  MInstruction* buildNamedLambdaEnv(MDefinition* callee, MDefinition* env,
    250                                    NamedLambdaObject* templateObj,
    251                                    gc::Heap initialHeap);
    252  MInstruction* buildCallObject(MDefinition* callee, MDefinition* env,
    253                                CallObject* templateObj, gc::Heap initialHeap);
    254  MInstruction* buildLoadSlot(MDefinition* obj, uint32_t numFixedSlots,
    255                              uint32_t slot);
    256 
    257  MConstant* globalLexicalEnvConstant();
    258  MDefinition* getCallee();
    259 
    260  [[nodiscard]] bool buildUnaryOp(BytecodeLocation loc);
    261  [[nodiscard]] bool buildBinaryOp(BytecodeLocation loc);
    262  [[nodiscard]] bool buildCompareOp(BytecodeLocation loc);
    263  [[nodiscard]] bool buildStrictConstantEqOp(BytecodeLocation loc, JSOp op);
    264  [[nodiscard]] bool buildTestOp(BytecodeLocation loc);
    265  [[nodiscard]] bool buildCallOp(BytecodeLocation loc);
    266 
    267  [[nodiscard]] bool buildInitPropGetterSetterOp(BytecodeLocation loc);
    268  [[nodiscard]] bool buildInitElemGetterSetterOp(BytecodeLocation loc);
    269 
    270  [[nodiscard]] bool buildSuspend(BytecodeLocation loc, MDefinition* gen,
    271                                  MDefinition* retVal);
    272 
    273  void buildCheckLexicalOp(BytecodeLocation loc);
    274 
    275  bool usesEnvironmentChain() const;
    276  MDefinition* walkEnvironmentChain(uint32_t numHops);
    277 
    278  void buildCreateThis(CallInfo& callInfo);
    279 
    280  [[nodiscard]] bool transpileCall(BytecodeLocation loc,
    281                                   const WarpCacheIR* cacheIRSnapshot,
    282                                   CallInfo* callInfo);
    283 
    284  [[nodiscard]] bool buildInlinedCall(BytecodeLocation loc,
    285                                      const WarpInlinedCall* snapshot,
    286                                      CallInfo& callInfo);
    287 
    288  MDefinition* patchInlinedReturns(CompileInfo* calleeCompileInfo,
    289                                   CallInfo& callInfo, MIRGraphReturns& exits,
    290                                   MBasicBlock* returnBlock);
    291  MDefinition* patchInlinedReturn(CompileInfo* calleeCompileInfo,
    292                                  CallInfo& callInfo, MBasicBlock* exit,
    293                                  MBasicBlock* returnBlock);
    294 
    295 #define BUILD_OP(OP, ...) [[nodiscard]] bool build_##OP(BytecodeLocation loc);
    296  FOR_EACH_OPCODE(BUILD_OP)
    297 #undef BUILD_OP
    298 
    299 public:
    300  WarpBuilder(WarpSnapshot& snapshot, MIRGenerator& mirGen,
    301              WarpCompilation* warpCompilation);
    302  WarpBuilder(WarpBuilder* caller, WarpScriptSnapshot* snapshot,
    303              CompileInfo& compileInfo, CallInfo* inlineCallInfo,
    304              MResumePoint* callerResumePoint);
    305 
    306  [[nodiscard]] bool build();
    307  [[nodiscard]] bool buildInline();
    308 
    309  const CompileInfo& info() const { return info_; }
    310  CallInfo* inlineCallInfo() const { return inlineCallInfo_; }
    311 };
    312 
    313 }  // namespace jit
    314 }  // namespace js
    315 
    316 #endif /* jit_WarpBuilder_h */