tor-browser

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

WasmInstanceData.h (13906B)


      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 *
      4 * Copyright 2021 Mozilla Foundation
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *     http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 #ifndef wasm_instance_data_h
     20 #define wasm_instance_data_h
     21 
     22 #include <stdint.h>
     23 
     24 #include "NamespaceImports.h"
     25 
     26 #include "gc/Pretenuring.h"
     27 #include "js/Utility.h"
     28 #include "vm/JSFunction.h"
     29 #include "wasm/WasmInstance.h"
     30 #include "wasm/WasmMemory.h"
     31 #include "wasm/WasmTypeDecls.h"
     32 
     33 namespace js {
     34 namespace wasm {
     35 
     36 // ExportArg holds the unboxed operands to the wasm entry trampoline which can
     37 // be called through an ExportFuncPtr.
     38 
     39 struct ExportArg {
     40  uint64_t lo;
     41  uint64_t hi;
     42 };
     43 
     44 using ExportFuncPtr = int32_t (*)(ExportArg*, Instance*);
     45 
     46 // TypeDefInstanceData describes the runtime information associated with a
     47 // module's type definition. This is accessed directly from JIT code and the
     48 // Instance.
     49 
     50 struct TypeDefInstanceData {
     51  TypeDefInstanceData()
     52      : typeDef(nullptr),
     53        superTypeVector(nullptr),
     54        shape(nullptr),
     55        clasp(nullptr) {
     56    memset(&cached, 0, sizeof(cached));
     57    cached.strukt.allocKind = gc::AllocKind::INVALID;
     58  }
     59 
     60  // The canonicalized pointer to this type definition. This is kept alive by
     61  // the type context associated with the instance.
     62  const wasm::TypeDef* typeDef;
     63 
     64  // The supertype vector for this type definition.  This is also kept alive
     65  // by the type context associated with the instance.
     66  const wasm::SuperTypeVector* superTypeVector;
     67 
     68  // The next three fields are only meaningful for, and used by, structs and
     69  // arrays.
     70  GCPtr<Shape*> shape;
     71  const JSClass* clasp;
     72 
     73  // This union is only meaningful for structs and arrays, and should otherwise
     74  // be zeroed out.  It exists so that allocators of structs and arrays don't
     75  // need to chase through `typeDef` to find this info.
     76  union {
     77    struct {
     78      // When `typeDef` refers to a struct type, these are copied unchanged
     79      // from fields of the same name in StructType.
     80      uint32_t payloadOffsetIL;
     81      uint32_t totalSizeIL;
     82      uint32_t totalSizeOOL;
     83      uint32_t oolPointerOffset;
     84      // Copied from StructType, and updated by GetFinalizedAllocKindForClass
     85      // (see comment on StructType::allocKind_).
     86      gc::AllocKind allocKind;
     87    } strukt;
     88    struct {
     89      // When `typeDef` refers to an array type, this caches the value of
     90      // `typeDef->arrayType().fieldType_.size()` (a size in bytes).
     91      uint32_t elemSize;
     92    } array;
     93  } cached;
     94 
     95  static constexpr size_t offsetOfShape() {
     96    return offsetof(TypeDefInstanceData, shape);
     97  }
     98  static constexpr size_t offsetOfSuperTypeVector() {
     99    return offsetof(TypeDefInstanceData, superTypeVector);
    100  }
    101  static constexpr size_t offsetOfArrayElemSize() {
    102    return offsetof(TypeDefInstanceData, cached.array.elemSize);
    103  }
    104 };
    105 
    106 // FuncDefInstanceData maintains the per-instance hotness state for a locally
    107 // defined wasm function.  This is a signed-int32 value that counts downwards
    108 // from an initially non-negative value.  At the point where the value
    109 // transitions below zero (not *to* zero), we deem the owning function to
    110 // have become hot.  Transitions from one negative value to any other (even
    111 // more) negative value are meaningless and should not happen.
    112 struct FuncDefInstanceData {
    113  int32_t hotnessCounter;
    114 };
    115 
    116 // FuncExportInstanceData maintains the exported function JS wrapper for an
    117 // exported function.
    118 struct FuncExportInstanceData {
    119  GCPtr<JSFunction*> func;
    120 };
    121 
    122 // FuncImportInstanceData describes the region of wasm global memory allocated
    123 // in the instance's thread-local storage for a function import. This is
    124 // accessed directly from JIT code and mutated by Instance as exits become
    125 // optimized and deoptimized.
    126 
    127 struct FuncImportInstanceData {
    128  // The code to call at an import site: a wasm callee, a thunk into C++, or a
    129  // thunk into JIT code.
    130  void* code;
    131 
    132  // The callee's Instance pointer, which must be loaded to InstanceReg
    133  // (along with any pinned registers) before calling 'code'.
    134  Instance* instance;
    135 
    136  // The callee function's realm.
    137  JS::Realm* realm;
    138 
    139  // A GC pointer which keeps the callee alive and is used to recover import
    140  // values for lazy table initialization.
    141  GCPtr<JSObject*> callable;
    142  static_assert(sizeof(GCPtr<JSObject*>) == sizeof(void*), "for JIT access");
    143 
    144  // See "Wasm Function.prototype.call.bind optimization" in WasmInstance.cpp
    145  // for more information.
    146  bool isFunctionCallBind;
    147 };
    148 
    149 struct MemoryInstanceData {
    150  // Pointer to the memory object.
    151  GCPtr<WasmMemoryObject*> memory;
    152 
    153  // Pointer to the base of the memory.
    154  uint8_t* base;
    155 
    156  // Bounds check limit in bytes. This is 64 bits on 64-bit systems so as to
    157  // allow for heap lengths up to and beyond 4GB, and 32 bits on 32-bit systems,
    158  // where heaps are limited to 2GB.
    159  //
    160  // See "Linear memory addresses and bounds checking" in WasmMemory.cpp.
    161  uintptr_t boundsCheckLimit;
    162 
    163  // The default boundsCheckLimit is used for standard page sizes and also 8-bit
    164  // memory accesses on custom page sizes. These other limits are only used for
    165  // accesses on memories with custom page sizes.
    166 #ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES
    167  uintptr_t boundsCheckLimit16;
    168  uintptr_t boundsCheckLimit32;
    169  uintptr_t boundsCheckLimit64;
    170  uintptr_t boundsCheckLimit128;
    171 #endif
    172 
    173  // Whether this memory is shared or not.
    174  bool isShared;
    175 };
    176 
    177 // TableInstanceData describes the region of wasm global memory allocated in the
    178 // instance's thread-local storage which is accessed directly from JIT code
    179 // to bounds-check and index the table.
    180 
    181 struct TableInstanceData {
    182  // Length of the table in number of elements (not bytes). Although the type is
    183  // uint64_t, the maximum value fits in 32 bits -- this value can safely be
    184  // loaded as either a 32-bit value or a 64-bit value.
    185  uint64_t length;
    186 
    187  // Pointer to the array of elements (which can have various representations).
    188  // For tables of anyref this is null.
    189  // For tables of functions, this is a pointer to the array of code pointers.
    190  void* elements;
    191 };
    192 
    193 // TagInstanceData describes the instance state associated with a tag.
    194 
    195 struct TagInstanceData {
    196  GCPtr<WasmTagObject*> object;
    197 };
    198 
    199 // Table element for TableRepr::Func which carries both the code pointer and
    200 // a instance pointer (and thus anything reachable through the instance).
    201 
    202 struct FunctionTableElem {
    203  // The code to call when calling this element. The table ABI is the system
    204  // ABI with the additional ABI requirements that:
    205  //  - InstanceReg and any pinned registers have been loaded appropriately
    206  //  - if this is a heterogeneous table that requires a signature check,
    207  //    WasmTableCallSigReg holds the signature id.
    208  void* code;
    209 
    210  // The pointer to the callee's instance's Instance. This must be loaded into
    211  // InstanceReg before calling 'code'.
    212  Instance* instance;
    213 };
    214 
    215 // A collection of metrics for a `call_ref` instruction. This is tracked by
    216 // baseline when we are using lazy tiering to perform speculative inlining.
    217 //
    218 // See MacroAssembler::updateCallRefMetrics for how this is written into.
    219 //
    220 // Because it contains thread-local data and is written into without
    221 // synchronization, we cannot access this directly from our function compilers
    222 // and so we use CallRefHints for that (see WasmModuleTypes.h).
    223 struct CallRefMetrics {
    224  // We track up to NUM_SLOTS targets with associated count, plus a count for
    225  // "all other" targets, including cross-instance calls.  This facilitates
    226  // knowing the total number of calls made by the instruction, which is
    227  // required in order to know whether the hottest tracked function is more
    228  // than some percentage (eg, 50%) of all calls.
    229  //
    230  // In order to keep search costs low (in the baseline code), we rely on the
    231  // fact that most call sites have distributions which are heavily skewed
    232  // towards one target.  This struct is updated by the code generated in
    233  // GenerateUpdateCallRefMetricsStub.  That tries to make
    234  // `targets[0]`/`counts[0]` be the hottest target, so that for most calls,
    235  // the monitoring code will only check `targets[0]` for a match.
    236  //
    237  // For NUM_SLOTS <= 2, GenerateUpdateCallRefMetricsStub's incremental-sort
    238  // heuristic maintains the `counts` array in strictly non-decreasing order.
    239  // For NUM_SLOTS > 2, in the worst case we will have counts[N+1] at most
    240  // (NUM_SLOTS - 2) larger than counts[N].  In practice the incremental
    241  // sorting heuristic is very effective and so counts are in decreasing order,
    242  // as we desire.
    243  //
    244  // Once NUM_SLOTS targets are being tracked, all new targets will be lumped
    245  // together in the `countOther` bucket.  This can lead to the unfortunate
    246  // case of having NUM_SLOTS different cold targets show up first, after which
    247  // follows a different target that is hot, but cannot be inlined because it
    248  // goes in the `countOther` bucket, so its identity is unknown.  This is
    249  // unlikely but could happen.  The only known fix is to increase NUM_SLOTS.
    250  //
    251  // The `targets` values may be nullptr only to indicate that the slot is not
    252  // in use.  No legitimate target can be nullptr.  Given that the state is
    253  // updated by generated code and that code isn't entirely simple, we place
    254  // emphasis on checking invariants carefully.
    255  //
    256  // Stores of funcrefs in `targets[]`: These CallRefMetrics structs logically
    257  // belong to the Instance data, and do not require any GC barriers for two
    258  // reasons:
    259  //
    260  // 1. The pre-write barrier protects against an unmarked object being stored
    261  //    into a marked object during an incremental GC.  However this funcref is
    262  //    from the Instance we're storing it into (see above) and so if the
    263  //    instance has already been traced, this function will already have been
    264  //    traced (all exported functions are kept alive by an instance cache).
    265  //
    266  // 2. The post-write barrier tracks edges from tenured objects to nursery
    267  //    objects.  However wasm exported functions are not nursery allocated and
    268  //    so no new edge can be created.
    269  //
    270  // Overflows in `counts[]` and `countOther`: increments of these values are
    271  // not checked for overflow and so could wrap around from 2^32-1 to zero.
    272  // We ignore but tolerate this, because:
    273  //
    274  // 1. This is extremely unlikely to happen in practice, since the function
    275  //    containing the call site is almost certain to get tiered up long before
    276  //    any of these counters gets anywhere near the limit.
    277  //
    278  // 2. Performing saturating increments is possible, but has a minimum extra
    279  //    cost of two instructions, and given (1.) it is pointless.
    280  //
    281  // This does however require that interpretation of the `counts[]` and
    282  // `countOther` values needs to be aware that zeroes could mean 2^32 or any
    283  // multiple of it.  Hence a zero in `counts[]` does not necessarily mean that
    284  // the corresponding `target[]` was never called, nor is it the case that a
    285  // `countsOther` of zero means no "other" targets were observed.
    286 
    287  static constexpr size_t NUM_SLOTS = 3;
    288  static_assert(NUM_SLOTS >= 1);  // 1 slot + others is the minimal config
    289 
    290  // An array of pairs of (target, count) ..
    291  GCPtr<JSFunction*> targets[NUM_SLOTS];
    292  uint32_t counts[NUM_SLOTS];
    293  // .. and a count for all other targets.
    294  uint32_t countOther;
    295 
    296  // Generated code assumes this
    297  static_assert(sizeof(GCPtr<JSFunction*>) == sizeof(void*));
    298  static_assert(sizeof(uint32_t) == 4);
    299 
    300  CallRefMetrics() {
    301    for (size_t i = 0; i < NUM_SLOTS; i++) {
    302      targets[i] = nullptr;
    303      counts[i] = 0;
    304    }
    305    countOther = 0;
    306    MOZ_ASSERT(checkInvariants());
    307  }
    308 
    309  [[nodiscard]] bool checkInvariants() const {
    310    // If targets[N] is null, then this slot is not in use and so counts[N]
    311    // must be zero.  Per comments above about overflow, the implication in the
    312    // other direction does not hold.
    313    size_t i;
    314    for (i = 0; i < NUM_SLOTS; i++) {
    315      if (targets[i] == nullptr && counts[i] != 0) {
    316        return false;
    317      }
    318    }
    319    // The targets/counts slots must be filled in in sequence.
    320    for (i = 0; i < NUM_SLOTS; i++) {
    321      if (targets[i] == nullptr) {
    322        break;
    323      }
    324    }
    325    size_t numUsed = i;
    326    for (/*keepgoing*/; i < NUM_SLOTS; i++) {
    327      if (targets[i] != nullptr) {
    328        return false;
    329      }
    330    }
    331    // For the slots in use, the target values must be different
    332    for (i = 0; i < numUsed; i++) {
    333      for (size_t j = i + 1; j < numUsed; j++) {
    334        if (targets[j] == targets[i]) {
    335          return false;
    336        }
    337      }
    338    }
    339    // Note we don't say anything about `countOther`.  This gets incremented in
    340    // the cases when we (1) see a new target when all slots are already in
    341    // use, or (2) have a cross-instance call.  The effect of (2) is that
    342    // `countOther` can be non-zero regardless of how many slots are in use.
    343    return true;
    344  }
    345 
    346  static size_t offsetOfTarget(size_t n) {
    347    MOZ_ASSERT(n < NUM_SLOTS);
    348    return offsetof(CallRefMetrics, targets) + n * sizeof(GCPtr<JSFunction*>);
    349  }
    350  static size_t offsetOfCount(size_t n) {
    351    MOZ_ASSERT(n < NUM_SLOTS);
    352    return offsetof(CallRefMetrics, counts) + n * sizeof(uint32_t);
    353  }
    354  static size_t offsetOfCountOther() {
    355    return offsetof(CallRefMetrics, countOther);
    356  }
    357 };
    358 
    359 }  // namespace wasm
    360 }  // namespace js
    361 
    362 #endif  // wasm_instance_data_h