summaryrefslogtreecommitdiffstats
path: root/js/src/jit/WarpSnapshot.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/WarpSnapshot.cpp')
-rw-r--r--js/src/jit/WarpSnapshot.cpp402
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);
+}