tor-browser

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

TrialInlining.h (6068B)


      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_TrialInlining_h
      8 #define jit_TrialInlining_h
      9 
     10 #include "mozilla/Attributes.h"
     11 #include "mozilla/Maybe.h"
     12 
     13 #include <stddef.h>
     14 #include <stdint.h>
     15 
     16 #include "jstypes.h"
     17 #include "NamespaceImports.h"
     18 
     19 #include "gc/Barrier.h"
     20 #include "jit/CacheIR.h"
     21 #include "js/RootingAPI.h"
     22 #include "js/TypeDecls.h"
     23 #include "js/UniquePtr.h"
     24 #include "js/Vector.h"
     25 #include "vm/JSScript.h"
     26 
     27 /*
     28 * [SMDOC] Trial Inlining
     29 *
     30 * WarpBuilder relies on transpiling CacheIR. When inlining scripted
     31 * functions in WarpBuilder, we want our ICs to be as monomorphic as
     32 * possible. Functions with multiple callers complicate this. An IC in
     33 * such a function might be monomorphic for any given caller, but
     34 * polymorphic overall. This make the input to WarpBuilder less precise.
     35 *
     36 * To solve this problem, we do trial inlining. During baseline
     37 * execution, we identify call sites for which it would be useful to
     38 * have more precise inlining data. For each such call site, we
     39 * allocate a fresh ICScript and replace the existing call IC with a
     40 * new specialized IC that invokes the callee using the new
     41 * ICScript. Other callers of the callee will continue using the
     42 * default ICScript. When we eventually Warp-compile the script, we
     43 * can generate code for the callee using the IC information in our
     44 * private ICScript, which is specialized for its caller.
     45 *
     46 * The same approach can be used to inline recursively.
     47 */
     48 
     49 class JS_PUBLIC_API JSTracer;
     50 struct JS_PUBLIC_API JSContext;
     51 
     52 namespace JS {
     53 class Zone;
     54 }
     55 
     56 namespace js {
     57 
     58 class BytecodeLocation;
     59 
     60 namespace jit {
     61 
     62 class BaselineFrame;
     63 class CacheIRWriter;
     64 class ICCacheIRStub;
     65 class ICEntry;
     66 class ICFallbackStub;
     67 class ICScript;
     68 class ICStubSpace;
     69 
     70 /*
     71 * An InliningRoot is owned by a JitScript. In turn, it owns the set
     72 * of ICScripts that are candidates for being inlined in that JitScript.
     73 */
     74 class InliningRoot {
     75 public:
     76  explicit InliningRoot(JSContext* cx, JSScript* owningScript)
     77      : owningScript_(owningScript),
     78        inlinedScripts_(cx),
     79        totalBytecodeSize_(owningScript->length()) {}
     80 
     81  void trace(JSTracer* trc);
     82  bool traceWeak(JSTracer* trc);
     83 
     84  bool addInlinedScript(js::UniquePtr<ICScript> icScript);
     85 
     86  uint32_t numInlinedScripts() const { return inlinedScripts_.length(); }
     87 
     88  void purgeInactiveICScripts();
     89 
     90  JSScript* owningScript() const { return owningScript_; }
     91 
     92  size_t totalBytecodeSize() const { return totalBytecodeSize_; }
     93 
     94  void addToTotalBytecodeSize(size_t size) { totalBytecodeSize_ += size; }
     95 
     96  template <typename F>
     97  void forEachInlinedScript(const F& f) const {
     98    for (auto& script : inlinedScripts_) {
     99      f(script.get());
    100    }
    101  }
    102 
    103 private:
    104  HeapPtr<JSScript*> owningScript_;
    105  js::Vector<js::UniquePtr<ICScript>> inlinedScripts_;
    106 
    107  // Bytecode size of outer script and all inlined scripts.
    108  size_t totalBytecodeSize_;
    109 };
    110 
    111 class InlinableOpData {
    112 public:
    113  JSScript* target = nullptr;
    114  ICScript* icScript = nullptr;
    115  const uint8_t* endOfSharedPrefix = nullptr;
    116 };
    117 
    118 class InlinableCallData : public InlinableOpData {
    119 public:
    120  ObjOperandId calleeOperand;
    121  CallFlags callFlags;
    122 };
    123 
    124 class InlinableGetterData : public InlinableOpData {
    125 public:
    126  ValOperandId receiverOperand;
    127  ObjOperandId calleeOperand;
    128  bool sameRealm = false;
    129 };
    130 
    131 class InlinableSetterData : public InlinableOpData {
    132 public:
    133  ObjOperandId receiverOperand;
    134  ObjOperandId calleeOperand;
    135  ValOperandId rhsOperand;
    136  bool sameRealm = false;
    137 };
    138 
    139 mozilla::Maybe<InlinableOpData> FindInlinableOpData(ICCacheIRStub* stub,
    140                                                    BytecodeLocation loc);
    141 
    142 mozilla::Maybe<InlinableCallData> FindInlinableCallData(ICCacheIRStub* stub);
    143 mozilla::Maybe<InlinableGetterData> FindInlinableGetterData(
    144    ICCacheIRStub* stub);
    145 mozilla::Maybe<InlinableSetterData> FindInlinableSetterData(
    146    ICCacheIRStub* stub);
    147 
    148 enum class TrialInliningDecision {
    149  NoInline,
    150  Inline,
    151  MonomorphicInline,
    152 };
    153 
    154 class MOZ_RAII TrialInliner {
    155 public:
    156  TrialInliner(JSContext* cx, HandleScript script, ICScript* icScript)
    157      : cx_(cx), script_(script), icScript_(icScript) {}
    158 
    159  JSContext* cx() { return cx_; }
    160 
    161  [[nodiscard]] bool tryInlining();
    162  [[nodiscard]] bool maybeInlineCall(ICEntry& entry, ICFallbackStub* fallback,
    163                                     BytecodeLocation loc);
    164  [[nodiscard]] bool maybeInlineGetter(ICEntry& entry, ICFallbackStub* fallback,
    165                                       BytecodeLocation loc, CacheKind kind);
    166  [[nodiscard]] bool maybeInlineSetter(ICEntry& entry, ICFallbackStub* fallback,
    167                                       BytecodeLocation loc, CacheKind kind);
    168 
    169  static bool canInline(JSContext* cx, JSScript* target, HandleScript caller,
    170                        BytecodeLocation loc);
    171 
    172  static bool IsValidInliningOp(JSOp op);
    173 
    174 private:
    175  ICCacheIRStub* maybeSingleStub(const ICEntry& entry);
    176  void cloneSharedPrefix(ICCacheIRStub* stub, const uint8_t* endOfPrefix,
    177                         CacheIRWriter& writer);
    178  ICScript* createInlinedICScript(JSScript* target, BytecodeLocation loc);
    179  [[nodiscard]] bool replaceICStub(ICEntry& entry, ICFallbackStub* fallback,
    180                                   CacheIRWriter& writer, CacheKind kind);
    181 
    182  TrialInliningDecision getInliningDecision(JSScript* target,
    183                                            ICCacheIRStub* stub,
    184                                            BytecodeLocation loc);
    185 
    186  InliningRoot* getOrCreateInliningRoot();
    187  InliningRoot* maybeGetInliningRoot() const;
    188  size_t inliningRootTotalBytecodeSize() const;
    189 
    190  JSContext* cx_;
    191  HandleScript script_;
    192  ICScript* icScript_;
    193 };
    194 
    195 bool DoTrialInlining(JSContext* cx, BaselineFrame* frame);
    196 
    197 }  // namespace jit
    198 }  // namespace js
    199 
    200 #endif /* jit_TrialInlining_h */