tor-browser

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

JitInfo.h (11047B)


      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 js_experimental_JitInfo_h
      8 #define js_experimental_JitInfo_h
      9 
     10 #include "mozilla/Assertions.h"  // MOZ_ASSERT
     11 #include "mozilla/Attributes.h"  // MOZ_NON_PARAM
     12 
     13 #include <stddef.h>  // size_t
     14 #include <stdint.h>  // uint16_t, uint32_t
     15 
     16 #include "js/CallArgs.h"    // JS::CallArgs, JS::detail::CallArgsBase, JSNative
     17 #include "js/RootingAPI.h"  // JS::{,Mutable}Handle, JS::Rooted
     18 #include "js/Value.h"       // JS::Value, JSValueType
     19 
     20 namespace js {
     21 
     22 namespace jit {
     23 
     24 enum class InlinableNative : uint16_t;
     25 enum class TrampolineNative : uint16_t;
     26 
     27 }  // namespace jit
     28 
     29 }  // namespace js
     30 
     31 /**
     32 * A class, expected to be passed by value, which represents the CallArgs for a
     33 * JSJitGetterOp.
     34 */
     35 class JSJitGetterCallArgs : protected JS::MutableHandle<JS::Value> {
     36 public:
     37  explicit JSJitGetterCallArgs(const JS::CallArgs& args)
     38      : JS::MutableHandle<JS::Value>(args.rval()) {}
     39 
     40  explicit JSJitGetterCallArgs(JS::Rooted<JS::Value>* rooted)
     41      : JS::MutableHandle<JS::Value>(rooted) {}
     42 
     43  explicit JSJitGetterCallArgs(JS::MutableHandle<JS::Value> handle)
     44      : JS::MutableHandle<JS::Value>(handle) {}
     45 
     46  JS::MutableHandle<JS::Value> rval() { return *this; }
     47 };
     48 
     49 /**
     50 * A class, expected to be passed by value, which represents the CallArgs for a
     51 * JSJitSetterOp.
     52 */
     53 class JSJitSetterCallArgs : protected JS::MutableHandle<JS::Value> {
     54 public:
     55  explicit JSJitSetterCallArgs(const JS::CallArgs& args)
     56      : JS::MutableHandle<JS::Value>(args[0]) {}
     57 
     58  explicit JSJitSetterCallArgs(JS::Rooted<JS::Value>* rooted)
     59      : JS::MutableHandle<JS::Value>(rooted) {}
     60 
     61  JS::MutableHandle<JS::Value> operator[](unsigned i) {
     62    MOZ_ASSERT(i == 0);
     63    return *this;
     64  }
     65 
     66  unsigned length() const { return 1; }
     67 
     68  // Add get() or maybe hasDefined() as needed
     69 };
     70 
     71 struct JSJitMethodCallArgsTraits;
     72 
     73 /**
     74 * A class, expected to be passed by reference, which represents the CallArgs
     75 * for a JSJitMethodOp.
     76 */
     77 class MOZ_NON_PARAM JSJitMethodCallArgs
     78    : protected JS::detail::CallArgsBase<JS::detail::NoUsedRval> {
     79 private:
     80  using Base = JS::detail::CallArgsBase<JS::detail::NoUsedRval>;
     81  friend struct JSJitMethodCallArgsTraits;
     82 
     83 public:
     84  explicit JSJitMethodCallArgs(const JS::CallArgs& args) {
     85    argv_ = args.array();
     86    argc_ = args.length();
     87  }
     88 
     89  JS::MutableHandle<JS::Value> rval() const { return Base::rval(); }
     90 
     91  unsigned length() const { return Base::length(); }
     92 
     93  JS::MutableHandle<JS::Value> operator[](unsigned i) const {
     94    return Base::operator[](i);
     95  }
     96 
     97  bool hasDefined(unsigned i) const { return Base::hasDefined(i); }
     98 
     99  JSObject& callee() const {
    100    // We can't use Base::callee() because that will try to poke at
    101    // this->usedRval_, which we don't have.
    102    return argv_[-2].toObject();
    103  }
    104 
    105  JS::Handle<JS::Value> get(unsigned i) const { return Base::get(i); }
    106 
    107  bool requireAtLeast(JSContext* cx, const char* fnname,
    108                      unsigned required) const {
    109    // Can just forward to Base, since it only needs the length and we
    110    // forward that already.
    111    return Base::requireAtLeast(cx, fnname, required);
    112  }
    113 };
    114 
    115 struct JSJitMethodCallArgsTraits {
    116  static constexpr size_t offsetOfArgv = offsetof(JSJitMethodCallArgs, argv_);
    117  static constexpr size_t offsetOfArgc = offsetof(JSJitMethodCallArgs, argc_);
    118 };
    119 
    120 using JSJitGetterOp = bool (*)(JSContext*, JS::Handle<JSObject*>, void*,
    121                               JSJitGetterCallArgs);
    122 using JSJitSetterOp = bool (*)(JSContext*, JS::Handle<JSObject*>, void*,
    123                               JSJitSetterCallArgs);
    124 using JSJitMethodOp = bool (*)(JSContext*, JS::Handle<JSObject*>, void*,
    125                               const JSJitMethodCallArgs&);
    126 
    127 /**
    128 * This struct contains metadata passed from the DOM to the JS Engine for JIT
    129 * optimizations on DOM property accessors.
    130 *
    131 * Eventually, this should be made available to general JSAPI users as *not*
    132 * experimental and *not* a friend API, but we're not ready to do so yet.
    133 */
    134 class JSJitInfo {
    135 public:
    136  enum OpType {
    137    Getter,
    138    Setter,
    139    Method,
    140    StaticMethod,
    141    InlinableNative,
    142    TrampolineNative,
    143    IgnoresReturnValueNative,
    144    // Must be last
    145    OpTypeCount
    146  };
    147 
    148  enum ArgType {
    149    // Basic types
    150    String = (1 << 0),
    151    Integer = (1 << 1),  // Only 32-bit or less
    152    Double = (1 << 2),   // Maybe we want to add Float sometime too
    153    Boolean = (1 << 3),
    154    Object = (1 << 4),
    155    Null = (1 << 5),
    156 
    157    // And derived types
    158    Numeric = Integer | Double,
    159    // Should "Primitive" use the WebIDL definition, which
    160    // excludes string and null, or the typical JS one that includes them?
    161    Primitive = Numeric | Boolean | Null | String,
    162    ObjectOrNull = Object | Null,
    163    Any = ObjectOrNull | Primitive,
    164 
    165    // Our sentinel value.
    166    ArgTypeListEnd = (1 << 31)
    167  };
    168 
    169  static_assert(Any & String, "Any must include String");
    170  static_assert(Any & Integer, "Any must include Integer");
    171  static_assert(Any & Double, "Any must include Double");
    172  static_assert(Any & Boolean, "Any must include Boolean");
    173  static_assert(Any & Object, "Any must include Object");
    174  static_assert(Any & Null, "Any must include Null");
    175 
    176  /**
    177   * An enum that describes what this getter/setter/method aliases.  This
    178   * determines what things can be hoisted past this call, and if this
    179   * call is movable what it can be hoisted past.
    180   */
    181  enum AliasSet {
    182    /**
    183     * Alias nothing: a constant value, getting it can't affect any other
    184     * values, nothing can affect it.
    185     */
    186    AliasNone,
    187 
    188    /**
    189     * Alias things that can modify the DOM but nothing else.  Doing the
    190     * call can't affect the behavior of any other function.
    191     */
    192    AliasDOMSets,
    193 
    194    /**
    195     * Alias the world.  Calling this can change arbitrary values anywhere
    196     * in the system.  Most things fall in this bucket.
    197     */
    198    AliasEverything,
    199 
    200    /** Must be last. */
    201    AliasSetCount
    202  };
    203 
    204  bool needsOuterizedThisObject() const {
    205    return type() != Getter && type() != Setter;
    206  }
    207 
    208  bool isTypedMethodJitInfo() const { return isTypedMethod; }
    209 
    210  OpType type() const { return OpType(type_); }
    211 
    212  AliasSet aliasSet() const { return AliasSet(aliasSet_); }
    213 
    214  JSValueType returnType() const { return JSValueType(returnType_); }
    215 
    216  union {
    217    JSJitGetterOp getter;
    218    JSJitSetterOp setter;
    219    JSJitMethodOp method;
    220    /** A DOM static method, used for Promise wrappers */
    221    JSNative staticMethod;
    222    JSNative ignoresReturnValueMethod;
    223  };
    224 
    225  static unsigned offsetOfIgnoresReturnValueNative() {
    226    return offsetof(JSJitInfo, ignoresReturnValueMethod);
    227  }
    228 
    229  union {
    230    uint16_t protoID;
    231    js::jit::InlinableNative inlinableNative;
    232    js::jit::TrampolineNative trampolineNative;
    233  };
    234 
    235  union {
    236    uint16_t depth;
    237 
    238    // Additional opcode for some InlinableNative functions.
    239    uint16_t nativeOp;
    240  };
    241 
    242  // These fields are carefully packed to take up 4 bytes.  If you need more
    243  // bits for whatever reason, please see if you can steal bits from existing
    244  // fields before adding more members to this structure.
    245  static constexpr size_t OpTypeBits = 4;
    246  static constexpr size_t AliasSetBits = 4;
    247  static constexpr size_t ReturnTypeBits = 8;
    248  static constexpr size_t SlotIndexBits = 10;
    249 
    250  /** The OpType that says what sort of function we are. */
    251  uint32_t type_ : OpTypeBits;
    252 
    253  /**
    254   * The alias set for this op.  This is a _minimal_ alias set; in
    255   * particular for a method it does not include whatever argument
    256   * conversions might do.  That's covered by argTypes and runtime
    257   * analysis of the actual argument types being passed in.
    258   */
    259  uint32_t aliasSet_ : AliasSetBits;
    260 
    261  /** The return type tag.  Might be JSVAL_TYPE_UNKNOWN. */
    262  uint32_t returnType_ : ReturnTypeBits;
    263 
    264  static_assert(OpTypeCount <= (1 << OpTypeBits),
    265                "Not enough space for OpType");
    266  static_assert(AliasSetCount <= (1 << AliasSetBits),
    267                "Not enough space for AliasSet");
    268  static_assert((sizeof(JSValueType) * 8) <= ReturnTypeBits,
    269                "Not enough space for JSValueType");
    270 
    271  /** Is op fallible? False in setters. */
    272  uint32_t isInfallible : 1;
    273 
    274  /**
    275   * Is op movable?  To be movable the op must
    276   * not AliasEverything, but even that might
    277   * not be enough (e.g. in cases when it can
    278   * throw or is explicitly not movable).
    279   */
    280  uint32_t isMovable : 1;
    281 
    282  /**
    283   * Can op be dead-code eliminated? Again, this
    284   * depends on whether the op can throw, in
    285   * addition to the alias set.
    286   */
    287  uint32_t isEliminatable : 1;
    288 
    289  // XXXbz should we have a JSValueType for the type of the member?
    290  /**
    291   * True if this is a getter that can always
    292   * get the value from a slot of the "this" object.
    293   */
    294  uint32_t isAlwaysInSlot : 1;
    295 
    296  /**
    297   * True if this is a getter that can sometimes (if the slot doesn't contain
    298   * UndefinedValue()) get the value from a slot of the "this" object.
    299   */
    300  uint32_t isLazilyCachedInSlot : 1;
    301 
    302  /** True if this is an instance of JSTypedMethodJitInfo. */
    303  uint32_t isTypedMethod : 1;
    304 
    305  /**
    306   * If isAlwaysInSlot or isSometimesInSlot is true,
    307   * the index of the slot to get the value from.
    308   * Otherwise 0.
    309   */
    310  uint32_t slotIndex : SlotIndexBits;
    311 
    312  static constexpr size_t maxSlotIndex = (1 << SlotIndexBits) - 1;
    313 };
    314 
    315 static_assert(sizeof(JSJitInfo) == (sizeof(void*) + 2 * sizeof(uint32_t)),
    316              "There are several thousand instances of JSJitInfo stored in "
    317              "a binary. Please don't increase its space requirements without "
    318              "verifying that there is no other way forward (better packing, "
    319              "smaller datatypes for fields, subclassing, etc.).");
    320 
    321 struct JSTypedMethodJitInfo {
    322  // We use C-style inheritance here, rather than C++ style inheritance
    323  // because not all compilers support brace-initialization for non-aggregate
    324  // classes. Using C++ style inheritance and constructors instead of
    325  // brace-initialization would also force the creation of static
    326  // constructors (on some compilers) when JSJitInfo and JSTypedMethodJitInfo
    327  // structures are declared. Since there can be several thousand of these
    328  // structures present and we want to have roughly equivalent performance
    329  // across a range of compilers, we do things manually.
    330  JSJitInfo base;
    331 
    332  const JSJitInfo::ArgType* const argTypes; /* For a method, a list of sets of
    333                                               types that the function
    334                                               expects.  This can be used,
    335                                               for example, to figure out
    336                                               when argument coercions can
    337                                               have side-effects. */
    338 };
    339 
    340 #endif  // js_experimental_JitInfo_h