Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 "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>,
"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().oneOriginValue(),
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:
case StubField::Type::WeakShape: {
// WeakShape pointers are traced strongly in this context.
uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset);
TraceWarpStubPtr<Shape>(trc, word, "warp-cacheir-shape");
break;
}
case StubField::Type::WeakGetterSetter: {
// WeakGetterSetter pointers are traced strongly in this context.
uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset);
TraceWarpStubPtr<GetterSetter>(trc, word,
"warp-cacheir-getter-setter");
break;
}
case StubField::Type::JSObject:
case StubField::Type::WeakObject: {
// WeakObject pointers are traced strongly in this context.
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::WeakBaseScript: {
// WeakBaseScript pointers are traced strongly in this context.
uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset);
TraceWarpStubPtr<BaseScript>(trc, word, "warp-cacheir-script");
break;
}
case StubField::Type::JitCode: {
uintptr_t word = stubInfo_->getStubRawWord(stubData_, offset);
TraceWarpStubPtr<JitCode>(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<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::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);
}