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 }