diff options
Diffstat (limited to 'js/src/jit/WarpSnapshot.cpp')
-rw-r--r-- | js/src/jit/WarpSnapshot.cpp | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/js/src/jit/WarpSnapshot.cpp b/js/src/jit/WarpSnapshot.cpp new file mode 100644 index 0000000000..ceb024f96c --- /dev/null +++ b/js/src/jit/WarpSnapshot.cpp @@ -0,0 +1,402 @@ +/* -*- 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 <type_traits> + +#include "jit/CacheIRCompiler.h" +#include "jit/CacheIRSpewer.h" +#include "vm/EnvironmentObject.h" +#include "vm/GetterSetter.h" +#include "vm/GlobalObject.h" +#include "vm/JSContext.h" +#include "vm/Printer.h" + +using namespace js; +using namespace js::jit; + +static_assert(!std::is_polymorphic_v<WarpOpSnapshot>, + "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()) {} + +#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<JSScript*>(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<JSObject*>(env.callObjectTemplate), + static_cast<JSObject*>(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<kind>()->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<JitCode*>(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 <typename T> +static void TraceWarpGCPtr(JSTracer* trc, const WarpGCPtr<T>& thing, + const char* name) { + T thingRaw = thing; + TraceManuallyBarrieredEdge(trc, &thingRaw, name); + MOZ_ASSERT(static_cast<T>(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<JSObject*>& 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<kind>()->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 <typename T> +static void TraceWarpStubPtr(JSTracer* trc, uintptr_t word, const char* name) { + T* ptr = reinterpret_cast<T*>(word); + TraceWarpGCPtr(trc, WarpGCPtr<T*>(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<Shape>(trc, word, "warp-cacheir-shape"); + break; + } + case StubField::Type::GetterSetter: { + uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); + TraceWarpStubPtr<GetterSetter>(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<JSObject>(trc, word, "warp-cacheir-object"); + } + break; + } + case StubField::Type::Symbol: { + uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); + TraceWarpStubPtr<JS::Symbol>(trc, word, "warp-cacheir-symbol"); + break; + } + case StubField::Type::String: { + uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); + TraceWarpStubPtr<JSString>(trc, word, "warp-cacheir-string"); + break; + } + case StubField::Type::BaseScript: { + uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); + TraceWarpStubPtr<BaseScript>(trc, word, "warp-cacheir-script"); + break; + } + case StubField::Type::Id: { + uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset); + jsid id = jsid::fromRawBits(word); + TraceWarpGCPtr(trc, WarpGCPtr<jsid>(id), "warp-cacheir-jsid"); + break; + } + case StubField::Type::Value: { + uint64_t data = stubInfo_->getStubRawInt64(stubData_, offset); + Value val = Value::fromRawBits(data); + TraceWarpGCPtr(trc, WarpGCPtr<Value>(val), "warp-cacheir-value"); + break; + } + case StubField::Type::AllocSite: { + mozilla::DebugOnly<uintptr_t> word = + stubInfo_->getStubRawWord(stubData_, offset); + MOZ_ASSERT(word == uintptr_t(gc::DefaultHeap) || + word == uintptr_t(gc::TenuredHeap)); + 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); +} |