/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "jit/WarpSnapshot.h" #include "mozilla/DebugOnly.h" #include #include "jit/CacheIRCompiler.h" #include "jit/CacheIRSpewer.h" #include "js/Printer.h" #include "vm/EnvironmentObject.h" #include "vm/GetterSetter.h" #include "vm/GlobalObject.h" #include "vm/JSContext.h" using namespace js; using namespace js::jit; static_assert(!std::is_polymorphic_v, "WarpOpSnapshot should not have any virtual methods"); WarpSnapshot::WarpSnapshot(JSContext* cx, TempAllocator& alloc, WarpScriptSnapshotList&& scriptSnapshots, const WarpBailoutInfo& bailoutInfo, bool needsFinalWarmUpCount) : scriptSnapshots_(std::move(scriptSnapshots)), globalLexicalEnv_(&cx->global()->lexicalEnvironment()), globalLexicalEnvThis_(globalLexicalEnv_->thisObject()), bailoutInfo_(bailoutInfo), nurseryObjects_(alloc) { #ifdef JS_CACHEIR_SPEW needsFinalWarmUpCount_ = needsFinalWarmUpCount; #endif } WarpScriptSnapshot::WarpScriptSnapshot(JSScript* script, const WarpEnvironment& env, WarpOpSnapshotList&& opSnapshots, ModuleObject* moduleObject) : script_(script), environment_(env), opSnapshots_(std::move(opSnapshots)), moduleObject_(moduleObject), isArrowFunction_(script->isFunction() && script->function()->isArrow()), isMonomorphicInlined_(false) {} #ifdef JS_JITSPEW void WarpSnapshot::dump() const { Fprinter out(stderr); dump(out); } void WarpSnapshot::dump(GenericPrinter& out) const { out.printf("WarpSnapshot (0x%p)\n", this); out.printf("------------------------------\n"); out.printf("globalLexicalEnv: 0x%p\n", globalLexicalEnv()); out.printf("globalLexicalEnvThis: 0x%p\n", globalLexicalEnvThis()); out.printf("failedBoundsCheck: %u\n", bailoutInfo().failedBoundsCheck()); out.printf("failedLexicalCheck: %u\n", bailoutInfo().failedLexicalCheck()); out.printf("\n"); out.printf("Nursery objects (%u):\n", unsigned(nurseryObjects_.length())); for (size_t i = 0; i < nurseryObjects_.length(); i++) { out.printf(" %u: 0x%p\n", unsigned(i), nurseryObjects_[i]); } out.printf("\n"); for (auto* scriptSnapshot : scriptSnapshots_) { scriptSnapshot->dump(out); } } void WarpScriptSnapshot::dump(GenericPrinter& out) const { out.printf("WarpScriptSnapshot (0x%p)\n", this); out.printf("------------------------------\n"); out.printf("Script: %s:%u:%u (0x%p)\n", script_->filename(), script_->lineno(), script_->column(), static_cast(script_)); out.printf(" moduleObject: 0x%p\n", moduleObject()); out.printf(" isArrowFunction: %u\n", isArrowFunction()); out.printf(" environment: "); environment_.match( [&](const NoEnvironment&) { out.printf("None\n"); }, [&](JSObject* obj) { out.printf("Object: 0x%p\n", obj); }, [&](const FunctionEnvironment& env) { out.printf( "Function: callobject template 0x%p, named lambda template: 0x%p\n", static_cast(env.callObjectTemplate), static_cast(env.namedLambdaTemplate)); }); out.printf("\n"); for (const WarpOpSnapshot* snapshot : opSnapshots()) { snapshot->dump(out, script_); out.printf("\n"); } } static const char* OpSnapshotKindString(WarpOpSnapshot::Kind kind) { static const char* const names[] = { # define NAME(x) #x, WARP_OP_SNAPSHOT_LIST(NAME) # undef NAME }; return names[unsigned(kind)]; } void WarpOpSnapshot::dump(GenericPrinter& out, JSScript* script) const { jsbytecode* pc = script->offsetToPC(offset_); out.printf(" %s (offset %u, JSOp::%s)\n", OpSnapshotKindString(kind_), offset_, CodeName(JSOp(*pc))); // Dispatch to dumpData() methods. switch (kind_) { # define DUMP(kind) \ case Kind::kind: \ as()->dumpData(out); \ break; WARP_OP_SNAPSHOT_LIST(DUMP) # undef DUMP } } void WarpArguments::dumpData(GenericPrinter& out) const { out.printf(" template: 0x%p\n", templateObj()); } void WarpRegExp::dumpData(GenericPrinter& out) const { out.printf(" hasShared: %u\n", hasShared()); } void WarpBuiltinObject::dumpData(GenericPrinter& out) const { out.printf(" builtin: 0x%p\n", builtin()); } void WarpGetIntrinsic::dumpData(GenericPrinter& out) const { out.printf(" intrinsic: 0x%016" PRIx64 "\n", intrinsic().asRawBits()); } void WarpGetImport::dumpData(GenericPrinter& out) const { out.printf(" targetEnv: 0x%p\n", targetEnv()); out.printf(" numFixedSlots: %u\n", numFixedSlots()); out.printf(" slot: %u\n", slot()); out.printf(" needsLexicalCheck: %u\n", needsLexicalCheck()); } void WarpRest::dumpData(GenericPrinter& out) const { out.printf(" shape: 0x%p\n", shape()); } void WarpBindGName::dumpData(GenericPrinter& out) const { out.printf(" globalEnv: 0x%p\n", globalEnv()); } void WarpVarEnvironment::dumpData(GenericPrinter& out) const { out.printf(" template: 0x%p\n", templateObj()); } void WarpLexicalEnvironment::dumpData(GenericPrinter& out) const { out.printf(" template: 0x%p\n", templateObj()); } void WarpClassBodyEnvironment::dumpData(GenericPrinter& out) const { out.printf(" template: 0x%p\n", templateObj()); } void WarpBailout::dumpData(GenericPrinter& out) const { // No fields. } void WarpCacheIR::dumpData(GenericPrinter& out) const { out.printf(" stubCode: 0x%p\n", static_cast(stubCode_)); out.printf(" stubInfo: 0x%p\n", stubInfo_); out.printf(" stubData: 0x%p\n", stubData_); # ifdef JS_CACHEIR_SPEW out.printf(" IR:\n"); SpewCacheIROps(out, " ", stubInfo_); # else out.printf("(CacheIR spew unavailable)\n"); # endif } void WarpInlinedCall::dumpData(GenericPrinter& out) const { out.printf(" scriptSnapshot: 0x%p\n", scriptSnapshot_); out.printf(" info: 0x%p\n", info_); cacheIRSnapshot_->dumpData(out); } void WarpPolymorphicTypes::dumpData(GenericPrinter& out) const { out.printf(" types:\n"); for (auto& typeData : list_) { out.printf(" %s\n", ValTypeToString(typeData.type())); } } #endif // JS_JITSPEW template static void TraceWarpGCPtr(JSTracer* trc, const WarpGCPtr& thing, const char* name) { T thingRaw = thing; TraceManuallyBarrieredEdge(trc, &thingRaw, name); MOZ_ASSERT(static_cast(thing) == thingRaw, "Unexpected moving GC!"); } void WarpSnapshot::trace(JSTracer* trc) { // Nursery objects can be tenured in parallel with Warp compilation. // Note: don't use TraceWarpGCPtr here as that asserts non-moving. for (size_t i = 0; i < nurseryObjects_.length(); i++) { TraceManuallyBarrieredEdge(trc, &nurseryObjects_[i], "warp-nursery-object"); } // Other GC things are not in the nursery. if (trc->runtime()->heapState() == JS::HeapState::MinorCollecting) { return; } for (auto* script : scriptSnapshots_) { script->trace(trc); } TraceWarpGCPtr(trc, globalLexicalEnv_, "warp-lexical"); TraceWarpGCPtr(trc, globalLexicalEnvThis_, "warp-lexicalthis"); } void WarpScriptSnapshot::trace(JSTracer* trc) { TraceWarpGCPtr(trc, script_, "warp-script"); environment_.match( [](const NoEnvironment&) {}, [trc](WarpGCPtr& obj) { TraceWarpGCPtr(trc, obj, "warp-env-object"); }, [trc](FunctionEnvironment& env) { if (env.callObjectTemplate) { TraceWarpGCPtr(trc, env.callObjectTemplate, "warp-env-callobject"); } if (env.namedLambdaTemplate) { TraceWarpGCPtr(trc, env.namedLambdaTemplate, "warp-env-namedlambda"); } }); for (WarpOpSnapshot* snapshot : opSnapshots_) { snapshot->trace(trc); } if (moduleObject_) { TraceWarpGCPtr(trc, moduleObject_, "warp-module-obj"); } } void WarpOpSnapshot::trace(JSTracer* trc) { // Dispatch to traceData() methods. switch (kind_) { #define TRACE(kind) \ case Kind::kind: \ as()->traceData(trc); \ break; WARP_OP_SNAPSHOT_LIST(TRACE) #undef TRACE } } void WarpArguments::traceData(JSTracer* trc) { if (templateObj_) { TraceWarpGCPtr(trc, templateObj_, "warp-args-template"); } } void WarpRegExp::traceData(JSTracer* trc) { // No GC pointers. } void WarpBuiltinObject::traceData(JSTracer* trc) { TraceWarpGCPtr(trc, builtin_, "warp-builtin-object"); } void WarpGetIntrinsic::traceData(JSTracer* trc) { TraceWarpGCPtr(trc, intrinsic_, "warp-intrinsic"); } void WarpGetImport::traceData(JSTracer* trc) { TraceWarpGCPtr(trc, targetEnv_, "warp-import-env"); } void WarpRest::traceData(JSTracer* trc) { TraceWarpGCPtr(trc, shape_, "warp-rest-shape"); } void WarpBindGName::traceData(JSTracer* trc) { TraceWarpGCPtr(trc, globalEnv_, "warp-bindgname-globalenv"); } void WarpVarEnvironment::traceData(JSTracer* trc) { TraceWarpGCPtr(trc, templateObj_, "warp-varenv-template"); } void WarpLexicalEnvironment::traceData(JSTracer* trc) { TraceWarpGCPtr(trc, templateObj_, "warp-lexenv-template"); } void WarpClassBodyEnvironment::traceData(JSTracer* trc) { TraceWarpGCPtr(trc, templateObj_, "warp-classbodyenv-template"); } void WarpBailout::traceData(JSTracer* trc) { // No GC pointers. } void WarpPolymorphicTypes::traceData(JSTracer* trc) { // No GC pointers. } template static void TraceWarpStubPtr(JSTracer* trc, uintptr_t word, const char* name) { T* ptr = reinterpret_cast(word); TraceWarpGCPtr(trc, WarpGCPtr(ptr), name); } void WarpCacheIR::traceData(JSTracer* trc) { TraceWarpGCPtr(trc, stubCode_, "warp-stub-code"); if (stubData_) { uint32_t field = 0; size_t offset = 0; while (true) { StubField::Type fieldType = stubInfo_->fieldType(field); switch (fieldType) { case StubField::Type::RawInt32: case StubField::Type::RawPointer: case StubField::Type::RawInt64: case StubField::Type::Double: break; case StubField::Type::Shape: { uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); TraceWarpStubPtr(trc, word, "warp-cacheir-shape"); break; } case StubField::Type::GetterSetter: { uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); TraceWarpStubPtr(trc, word, "warp-cacheir-getter-setter"); break; } case StubField::Type::JSObject: { uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); WarpObjectField field = WarpObjectField::fromData(word); if (!field.isNurseryIndex()) { TraceWarpStubPtr(trc, word, "warp-cacheir-object"); } break; } case StubField::Type::Symbol: { uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); TraceWarpStubPtr(trc, word, "warp-cacheir-symbol"); break; } case StubField::Type::String: { uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); TraceWarpStubPtr(trc, word, "warp-cacheir-string"); break; } case StubField::Type::BaseScript: { uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); TraceWarpStubPtr(trc, word, "warp-cacheir-script"); break; } case StubField::Type::JitCode: { uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); TraceWarpStubPtr(trc, word, "warp-cacheir-jitcode"); break; } case StubField::Type::Id: { uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); jsid id = jsid::fromRawBits(word); TraceWarpGCPtr(trc, WarpGCPtr(id), "warp-cacheir-jsid"); break; } case StubField::Type::Value: { uint64_t data = stubInfo_->getStubRawInt64(stubData_, offset); Value val = Value::fromRawBits(data); TraceWarpGCPtr(trc, WarpGCPtr(val), "warp-cacheir-value"); break; } case StubField::Type::AllocSite: { mozilla::DebugOnly word = stubInfo_->getStubRawWord(stubData_, offset); MOZ_ASSERT(word == uintptr_t(gc::Heap::Default) || word == uintptr_t(gc::Heap::Tenured)); break; } case StubField::Type::Limit: return; // Done. } field++; offset += StubField::sizeInBytes(fieldType); } } } void WarpInlinedCall::traceData(JSTracer* trc) { // Note: scriptSnapshot_ is traced through WarpSnapshot. cacheIRSnapshot_->trace(trc); }