tor-browser

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

WarpSnapshot.cpp (15747B)


      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 #include "jit/WarpSnapshot.h"
      8 
      9 #include "mozilla/DebugOnly.h"
     10 
     11 #include <type_traits>
     12 
     13 #include "jit/CacheIRCompiler.h"
     14 #include "jit/CacheIRSpewer.h"
     15 #include "js/Printer.h"
     16 #include "vm/EnvironmentObject.h"
     17 #include "vm/GetterSetter.h"
     18 #include "vm/GlobalObject.h"
     19 #include "vm/JSContext.h"
     20 
     21 using namespace js;
     22 using namespace js::jit;
     23 
     24 static_assert(!std::is_polymorphic_v<WarpOpSnapshot>,
     25              "WarpOpSnapshot should not have any virtual methods");
     26 
     27 WarpSnapshot::WarpSnapshot(JSContext* cx, TempAllocator& alloc,
     28                           WarpScriptSnapshotList&& scriptSnapshots,
     29                           const WarpZoneStubsSnapshot& zoneStubs,
     30                           const WarpBailoutInfo& bailoutInfo,
     31                           bool needsFinalWarmUpCount)
     32    : scriptSnapshots_(std::move(scriptSnapshots)),
     33      zoneStubs_(zoneStubs),
     34      globalLexicalEnv_(&cx->global()->lexicalEnvironment()),
     35      globalLexicalEnvThis_(globalLexicalEnv_->thisObject()),
     36      bailoutInfo_(bailoutInfo),
     37      nurseryObjects_(alloc),
     38      nurseryValues_(alloc) {
     39 #ifdef JS_CACHEIR_SPEW
     40  needsFinalWarmUpCount_ = needsFinalWarmUpCount;
     41 #endif
     42 }
     43 
     44 WarpScriptSnapshot::WarpScriptSnapshot(JSScript* script,
     45                                       const WarpEnvironment& env,
     46                                       WarpOpSnapshotList&& opSnapshots,
     47                                       ModuleObject* moduleObject)
     48    : script_(script),
     49      environment_(env),
     50      opSnapshots_(std::move(opSnapshots)),
     51      moduleObject_(moduleObject),
     52      isArrowFunction_(script->isFunction() && script->function()->isArrow()) {}
     53 
     54 #ifdef JS_JITSPEW
     55 void WarpSnapshot::dump() const {
     56  Fprinter out(stderr);
     57  dump(out);
     58 }
     59 
     60 void WarpSnapshot::dump(GenericPrinter& out) const {
     61  out.printf("WarpSnapshot (0x%p)\n", this);
     62  out.printf("------------------------------\n");
     63  out.printf("globalLexicalEnv: 0x%p\n", globalLexicalEnv());
     64  out.printf("globalLexicalEnvThis: 0x%p\n", globalLexicalEnvThis());
     65  out.printf("failedBoundsCheck: %u\n", bailoutInfo().failedBoundsCheck());
     66  out.printf("failedLexicalCheck: %u\n", bailoutInfo().failedLexicalCheck());
     67  out.printf("\n");
     68 
     69  out.printf("JitZone stubs:\n");
     70  for (const auto& stub : zoneStubs_) {
     71    unsigned index = &stub - zoneStubs_.begin();
     72    out.printf("Stub %u: 0x%p\n", index, stub);
     73  }
     74  out.printf("\n");
     75 
     76  out.printf("Nursery objects (%zu):\n", nurseryObjects_.length());
     77  for (size_t i = 0; i < nurseryObjects_.length(); i++) {
     78    out.printf("  %zu: 0x%p\n", i, nurseryObjects_[i]);
     79  }
     80  out.printf("\n");
     81 
     82  out.printf("Nursery values (%zu):\n", nurseryValues_.length());
     83  for (size_t i = 0; i < nurseryValues_.length(); i++) {
     84    out.printf("  %zu: (gc::Cell*)0x%p\n", i, nurseryValues_[i].toGCThing());
     85  }
     86  out.printf("\n");
     87 
     88  for (auto* scriptSnapshot : scriptSnapshots_) {
     89    scriptSnapshot->dump(out);
     90  }
     91 }
     92 
     93 void WarpScriptSnapshot::dump(GenericPrinter& out) const {
     94  out.printf("WarpScriptSnapshot (0x%p)\n", this);
     95  out.printf("------------------------------\n");
     96  out.printf("Script: %s:%u:%u (0x%p)\n", script_->filename(),
     97             script_->lineno(), script_->column().oneOriginValue(),
     98             static_cast<JSScript*>(script_));
     99  out.printf("  moduleObject: 0x%p\n", moduleObject());
    100  out.printf("  isArrowFunction: %u\n", isArrowFunction());
    101 
    102  out.printf("  environment: ");
    103  environment_.match(
    104      [&](const NoEnvironment&) { out.printf("None\n"); },
    105      [&](JSObject* obj) { out.printf("Object: 0x%p\n", obj); },
    106      [&](const FunctionEnvironment& env) {
    107        out.printf(
    108            "Function: callobject template 0x%p, named lambda template: 0x%p,"
    109            " initial heap %u\n",
    110            static_cast<JSObject*>(env.callObjectTemplate),
    111            static_cast<JSObject*>(env.namedLambdaTemplate),
    112            unsigned(env.initialHeap));
    113      });
    114 
    115  out.printf("\n");
    116  for (const WarpOpSnapshot* snapshot : opSnapshots()) {
    117    snapshot->dump(out, script_);
    118    out.printf("\n");
    119  }
    120 }
    121 
    122 static const char* OpSnapshotKindString(WarpOpSnapshot::Kind kind) {
    123  static const char* const names[] = {
    124 #  define NAME(x) #x,
    125      WARP_OP_SNAPSHOT_LIST(NAME)
    126 #  undef NAME
    127  };
    128  return names[unsigned(kind)];
    129 }
    130 
    131 void WarpOpSnapshot::dump(GenericPrinter& out, JSScript* script) const {
    132  jsbytecode* pc = script->offsetToPC(offset_);
    133  out.printf("  %s (offset %u, JSOp::%s)\n", OpSnapshotKindString(kind_),
    134             offset_, CodeName(JSOp(*pc)));
    135 
    136  // Dispatch to dumpData() methods.
    137  switch (kind_) {
    138 #  define DUMP(kind)             \
    139    case Kind::kind:             \
    140      as<kind>()->dumpData(out); \
    141      break;
    142    WARP_OP_SNAPSHOT_LIST(DUMP)
    143 #  undef DUMP
    144  }
    145 }
    146 
    147 void WarpArguments::dumpData(GenericPrinter& out) const {
    148  out.printf("    template: 0x%p\n", templateObj());
    149 }
    150 
    151 void WarpRegExp::dumpData(GenericPrinter& out) const {
    152  out.printf("    hasShared: %u\n", hasShared());
    153 }
    154 
    155 void WarpBuiltinObject::dumpData(GenericPrinter& out) const {
    156  out.printf("    builtin: 0x%p\n", builtin());
    157 }
    158 
    159 void WarpGetIntrinsic::dumpData(GenericPrinter& out) const {
    160  out.printf("    intrinsic: 0x%016" PRIx64 "\n", intrinsic().asRawBits());
    161 }
    162 
    163 void WarpGetImport::dumpData(GenericPrinter& out) const {
    164  out.printf("    targetEnv: 0x%p\n", targetEnv());
    165  out.printf("    numFixedSlots: %u\n", numFixedSlots());
    166  out.printf("    slot: %u\n", slot());
    167  out.printf("    needsLexicalCheck: %u\n", needsLexicalCheck());
    168 }
    169 
    170 void WarpRest::dumpData(GenericPrinter& out) const {
    171  out.printf("    shape: 0x%p\n", shape());
    172 }
    173 
    174 void WarpBindUnqualifiedGName::dumpData(GenericPrinter& out) const {
    175  out.printf("    globalEnv: 0x%p\n", globalEnv());
    176 }
    177 
    178 void WarpVarEnvironment::dumpData(GenericPrinter& out) const {
    179  out.printf("    template: 0x%p\n", templateObj());
    180 }
    181 
    182 void WarpLexicalEnvironment::dumpData(GenericPrinter& out) const {
    183  out.printf("    template: 0x%p\n", templateObj());
    184 }
    185 
    186 void WarpClassBodyEnvironment::dumpData(GenericPrinter& out) const {
    187  out.printf("    template: 0x%p\n", templateObj());
    188 }
    189 
    190 void WarpBailout::dumpData(GenericPrinter& out) const {
    191  // No fields.
    192 }
    193 
    194 void WarpCacheIRBase::dumpData(GenericPrinter& out) const {
    195  out.printf("    stubCode: 0x%p\n", static_cast<JitCode*>(stubCode_));
    196  out.printf("    stubInfo: 0x%p\n", stubInfo_);
    197  out.printf("    stubData: 0x%p\n", stubData_);
    198 #  ifdef JS_CACHEIR_SPEW
    199  out.printf("    IR:\n");
    200  SpewCacheIROps(out, "      ", stubInfo_);
    201 #  else
    202  out.printf("(CacheIR spew unavailable)\n");
    203 #  endif
    204 }
    205 
    206 void WarpCacheIR::dumpData(GenericPrinter& out) const {
    207  WarpCacheIRBase::dumpData(out);
    208 }
    209 
    210 void WarpCacheIRWithShapeList::dumpData(GenericPrinter& out) const {
    211  WarpCacheIRBase::dumpData(out);
    212  uint32_t index = 0;
    213  for (Shape* shape : shapes_.shapes()) {
    214    out.printf("    shape %u: 0x%p\n", index, shape);
    215    index++;
    216  }
    217 }
    218 
    219 void WarpCacheIRWithShapeListAndOffsets::dumpData(GenericPrinter& out) const {
    220  WarpCacheIRBase::dumpData(out);
    221  uint32_t index = 0;
    222  for (Shape* shape : shapes_.shapes()) {
    223    out.printf("    shape %u: 0x%p\n", index, shape);
    224    index++;
    225  }
    226  index = 0;
    227  for (uint32_t offset : shapes_.offsets()) {
    228    out.printf("    offset %u: %u\n", index, offset);
    229    index++;
    230  }
    231 }
    232 
    233 void WarpInlinedCall::dumpData(GenericPrinter& out) const {
    234  out.printf("    scriptSnapshot: 0x%p\n", scriptSnapshot_);
    235  out.printf("    info: 0x%p\n", info_);
    236  cacheIRSnapshot_->dumpData(out);
    237 }
    238 
    239 void WarpPolymorphicTypes::dumpData(GenericPrinter& out) const {
    240  out.printf("    types:\n");
    241  for (auto& typeData : list_) {
    242    out.printf("      %s\n", ValTypeToString(typeData.type()));
    243  }
    244 }
    245 
    246 #endif  // JS_JITSPEW
    247 
    248 void WarpSnapshot::trace(JSTracer* trc) {
    249  // Nursery objects/values can be tenured in parallel with Warp compilation.
    250  // Note: don't use TraceOffthreadGCPtr here as that asserts non-moving.
    251  for (size_t i = 0; i < nurseryObjects_.length(); i++) {
    252    TraceManuallyBarrieredEdge(trc, &nurseryObjects_[i], "warp-nursery-object");
    253  }
    254  for (size_t i = 0; i < nurseryValues_.length(); i++) {
    255    MOZ_ASSERT(nurseryValues_[i].isGCThing());
    256    TraceManuallyBarrieredEdge(trc, &nurseryValues_[i], "warp-nursery-value");
    257  }
    258 
    259  // Other GC things are not in the nursery.
    260  if (trc->runtime()->heapState() == JS::HeapState::MinorCollecting) {
    261    return;
    262  }
    263 
    264  for (auto* script : scriptSnapshots_) {
    265    script->trace(trc);
    266  }
    267  for (JitCode* stub : zoneStubs_) {
    268    if (stub) {
    269      OffthreadGCPtr<JitCode*> ptr(stub);
    270      TraceOffthreadGCPtr(trc, ptr, "warp-zone-stub");
    271    }
    272  }
    273  TraceOffthreadGCPtr(trc, globalLexicalEnv_, "warp-lexical");
    274  TraceOffthreadGCPtr(trc, globalLexicalEnvThis_, "warp-lexicalthis");
    275 }
    276 
    277 void WarpScriptSnapshot::trace(JSTracer* trc) {
    278  TraceOffthreadGCPtr(trc, script_, "warp-script");
    279 
    280  environment_.match([](const NoEnvironment&) {},
    281                     [trc](OffthreadGCPtr<JSObject*>& obj) {
    282                       TraceOffthreadGCPtr(trc, obj, "warp-env-object");
    283                     },
    284                     [trc](FunctionEnvironment& env) {
    285                       if (env.callObjectTemplate) {
    286                         TraceOffthreadGCPtr(trc, env.callObjectTemplate,
    287                                             "warp-env-callobject");
    288                       }
    289                       if (env.namedLambdaTemplate) {
    290                         TraceOffthreadGCPtr(trc, env.namedLambdaTemplate,
    291                                             "warp-env-namedlambda");
    292                       }
    293                     });
    294 
    295  for (WarpOpSnapshot* snapshot : opSnapshots_) {
    296    snapshot->trace(trc);
    297  }
    298 
    299  if (moduleObject_) {
    300    TraceOffthreadGCPtr(trc, moduleObject_, "warp-module-obj");
    301  }
    302 }
    303 
    304 void WarpOpSnapshot::trace(JSTracer* trc) {
    305  // Dispatch to traceData() methods.
    306  switch (kind_) {
    307 #define TRACE(kind)             \
    308  case Kind::kind:              \
    309    as<kind>()->traceData(trc); \
    310    break;
    311    WARP_OP_SNAPSHOT_LIST(TRACE)
    312 #undef TRACE
    313  }
    314 }
    315 
    316 void WarpArguments::traceData(JSTracer* trc) {
    317  if (templateObj_) {
    318    TraceOffthreadGCPtr(trc, templateObj_, "warp-args-template");
    319  }
    320 }
    321 
    322 void WarpRegExp::traceData(JSTracer* trc) {
    323  // No GC pointers.
    324 }
    325 
    326 void WarpBuiltinObject::traceData(JSTracer* trc) {
    327  TraceOffthreadGCPtr(trc, builtin_, "warp-builtin-object");
    328 }
    329 
    330 void WarpGetIntrinsic::traceData(JSTracer* trc) {
    331  TraceOffthreadGCPtr(trc, intrinsic_, "warp-intrinsic");
    332 }
    333 
    334 void WarpGetImport::traceData(JSTracer* trc) {
    335  TraceOffthreadGCPtr(trc, targetEnv_, "warp-import-env");
    336 }
    337 
    338 void WarpRest::traceData(JSTracer* trc) {
    339  TraceOffthreadGCPtr(trc, shape_, "warp-rest-shape");
    340 }
    341 
    342 void WarpBindUnqualifiedGName::traceData(JSTracer* trc) {
    343  TraceOffthreadGCPtr(trc, globalEnv_, "warp-bindunqualifiedgname-globalenv");
    344 }
    345 
    346 void WarpVarEnvironment::traceData(JSTracer* trc) {
    347  TraceOffthreadGCPtr(trc, templateObj_, "warp-varenv-template");
    348 }
    349 
    350 void WarpLexicalEnvironment::traceData(JSTracer* trc) {
    351  TraceOffthreadGCPtr(trc, templateObj_, "warp-lexenv-template");
    352 }
    353 
    354 void WarpClassBodyEnvironment::traceData(JSTracer* trc) {
    355  TraceOffthreadGCPtr(trc, templateObj_, "warp-classbodyenv-template");
    356 }
    357 
    358 void WarpBailout::traceData(JSTracer* trc) {
    359  // No GC pointers.
    360 }
    361 
    362 void WarpPolymorphicTypes::traceData(JSTracer* trc) {
    363  // No GC pointers.
    364 }
    365 
    366 template <typename T>
    367 static void TraceWarpStubPtr(JSTracer* trc, uintptr_t word, const char* name) {
    368  T* ptr = reinterpret_cast<T*>(word);
    369  TraceOffthreadGCPtr(trc, OffthreadGCPtr<T*>(ptr), name);
    370 }
    371 
    372 void WarpCacheIRBase::traceData(JSTracer* trc) {
    373  TraceOffthreadGCPtr(trc, stubCode_, "warp-stub-code");
    374  if (stubData_) {
    375    uint32_t field = 0;
    376    size_t offset = 0;
    377    while (true) {
    378      StubField::Type fieldType = stubInfo_->fieldType(field);
    379      switch (fieldType) {
    380        case StubField::Type::RawInt32:
    381        case StubField::Type::RawPointer:
    382        case StubField::Type::RawInt64:
    383        case StubField::Type::Double:
    384          break;
    385        case StubField::Type::Shape:
    386        case StubField::Type::WeakShape: {
    387          // WeakShape pointers are traced strongly in this context.
    388          uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset);
    389          TraceWarpStubPtr<Shape>(trc, word, "warp-cacheir-shape");
    390          break;
    391        }
    392        case StubField::Type::JSObject:
    393        case StubField::Type::WeakObject: {
    394          // WeakObject pointers are traced strongly in this context.
    395          uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset);
    396          WarpObjectField field = WarpObjectField::fromData(word);
    397          if (!field.isNurseryIndex()) {
    398            TraceWarpStubPtr<JSObject>(trc, word, "warp-cacheir-object");
    399          }
    400          break;
    401        }
    402        case StubField::Type::Symbol: {
    403          uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset);
    404          TraceWarpStubPtr<JS::Symbol>(trc, word, "warp-cacheir-symbol");
    405          break;
    406        }
    407        case StubField::Type::String: {
    408          uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset);
    409          TraceWarpStubPtr<JSString>(trc, word, "warp-cacheir-string");
    410          break;
    411        }
    412        case StubField::Type::WeakBaseScript: {
    413          // WeakBaseScript pointers are traced strongly in this context.
    414          uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset);
    415          TraceWarpStubPtr<BaseScript>(trc, word, "warp-cacheir-script");
    416          break;
    417        }
    418        case StubField::Type::JitCode: {
    419          uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset);
    420          TraceWarpStubPtr<JitCode>(trc, word, "warp-cacheir-jitcode");
    421          break;
    422        }
    423        case StubField::Type::Id: {
    424          uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset);
    425          jsid id = jsid::fromRawBits(word);
    426          TraceOffthreadGCPtr(trc, OffthreadGCPtr<jsid>(id),
    427                              "warp-cacheir-jsid");
    428          break;
    429        }
    430        case StubField::Type::Value:
    431        case StubField::Type::WeakValue: {
    432          // WeakValues are traced strongly in this context.
    433          uint64_t data = stubInfo_->getStubRawInt64(stubData_, offset);
    434          Value val = Value::fromRawBits(data);
    435          TraceOffthreadGCPtr(trc, OffthreadGCPtr<Value>(val),
    436                              "warp-cacheir-value");
    437          break;
    438        }
    439        case StubField::Type::AllocSite: {
    440          mozilla::DebugOnly<uintptr_t> word =
    441              stubInfo_->getStubRawWord(stubData_, offset);
    442          MOZ_ASSERT(word == uintptr_t(gc::Heap::Default) ||
    443                     word == uintptr_t(gc::Heap::Tenured));
    444          break;
    445        }
    446        case StubField::Type::Limit:
    447          return;  // Done.
    448      }
    449      field++;
    450      offset += StubField::sizeInBytes(fieldType);
    451    }
    452  }
    453 }
    454 
    455 void WarpCacheIR::traceData(JSTracer* trc) { WarpCacheIRBase::traceData(trc); }
    456 
    457 void ShapeListSnapshot::trace(JSTracer* trc) const {
    458  for (auto& shape : shapes_) {
    459    if (shape) {
    460      TraceOffthreadGCPtr(trc, shape, "warp-shape-list-shape");
    461    }
    462  }
    463 }
    464 
    465 void WarpCacheIRWithShapeList::traceData(JSTracer* trc) {
    466  WarpCacheIRBase::traceData(trc);
    467  shapes_.trace(trc);
    468 }
    469 
    470 void WarpCacheIRWithShapeListAndOffsets::traceData(JSTracer* trc) {
    471  WarpCacheIRBase::traceData(trc);
    472  shapes_.trace(trc);
    473 }
    474 
    475 void WarpInlinedCall::traceData(JSTracer* trc) {
    476  // Note: scriptSnapshot_ is traced through WarpSnapshot.
    477  cacheIRSnapshot_->trace(trc);
    478 }