/* -*- 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/. */ #ifndef jit_WarpSnapshot_h #define jit_WarpSnapshot_h #include "mozilla/LinkedList.h" #include "mozilla/Variant.h" #include "builtin/ModuleObject.h" #include "gc/Policy.h" #include "jit/JitAllocPolicy.h" #include "jit/JitContext.h" #include "jit/TypeData.h" #include "vm/EnvironmentObject.h" #include "vm/FunctionFlags.h" // js::FunctionFlags namespace js { class ArgumentsObject; class CallObject; class GlobalLexicalEnvironmentObject; class LexicalEnvironmentObject; class ModuleEnvironmentObject; class NamedLambdaObject; namespace jit { class CacheIRStubInfo; class CompileInfo; class WarpScriptSnapshot; #define WARP_OP_SNAPSHOT_LIST(_) \ _(WarpArguments) \ _(WarpRegExp) \ _(WarpBuiltinObject) \ _(WarpGetIntrinsic) \ _(WarpGetImport) \ _(WarpRest) \ _(WarpBindGName) \ _(WarpVarEnvironment) \ _(WarpLexicalEnvironment) \ _(WarpClassBodyEnvironment) \ _(WarpBailout) \ _(WarpCacheIR) \ _(WarpInlinedCall) \ _(WarpPolymorphicTypes) // Wrapper for GC things stored in WarpSnapshot. Asserts the GC pointer is not // nursery-allocated. These pointers must be traced using TraceWarpGCPtr. template class WarpGCPtr { // Note: no pre-barrier is needed because this is a constant. No post-barrier // is needed because the value is always tenured. const T ptr_; public: explicit WarpGCPtr(const T& ptr) : ptr_(ptr) { MOZ_ASSERT(JS::GCPolicy::isTenured(ptr), "WarpSnapshot pointers must be tenured"); } WarpGCPtr(const WarpGCPtr& other) = default; operator T() const { return ptr_; } T operator->() const { return ptr_; } private: WarpGCPtr() = delete; void operator=(WarpGCPtr& other) = delete; }; // WarpOpSnapshot is the base class for data attached to a single bytecode op by // WarpOracle. This is typically data that WarpBuilder can't read off-thread // without racing. class WarpOpSnapshot : public TempObject, public mozilla::LinkedListElement { public: enum class Kind : uint16_t { #define DEF_KIND(KIND) KIND, WARP_OP_SNAPSHOT_LIST(DEF_KIND) #undef DEF_KIND }; private: // Bytecode offset. uint32_t offset_ = 0; Kind kind_; protected: WarpOpSnapshot(Kind kind, uint32_t offset) : offset_(offset), kind_(kind) {} public: uint32_t offset() const { return offset_; } Kind kind() const { return kind_; } template const T* as() const { MOZ_ASSERT(kind_ == T::ThisKind); return static_cast(this); } template T* as() { MOZ_ASSERT(kind_ == T::ThisKind); return static_cast(this); } void trace(JSTracer* trc); #ifdef JS_JITSPEW void dump(GenericPrinter& out, JSScript* script) const; #endif }; using WarpOpSnapshotList = mozilla::LinkedList; // Template object for JSOp::Arguments. class WarpArguments : public WarpOpSnapshot { // Note: this can be nullptr if the realm has no template object yet. WarpGCPtr templateObj_; public: static constexpr Kind ThisKind = Kind::WarpArguments; WarpArguments(uint32_t offset, ArgumentsObject* templateObj) : WarpOpSnapshot(ThisKind, offset), templateObj_(templateObj) {} ArgumentsObject* templateObj() const { return templateObj_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // The "has RegExpShared" state for JSOp::RegExp's template object. class WarpRegExp : public WarpOpSnapshot { bool hasShared_; public: static constexpr Kind ThisKind = Kind::WarpRegExp; WarpRegExp(uint32_t offset, bool hasShared) : WarpOpSnapshot(ThisKind, offset), hasShared_(hasShared) {} bool hasShared() const { return hasShared_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // The object for JSOp::BuiltinObject if it exists at compile-time. class WarpBuiltinObject : public WarpOpSnapshot { WarpGCPtr builtin_; public: static constexpr Kind ThisKind = Kind::WarpBuiltinObject; WarpBuiltinObject(uint32_t offset, JSObject* builtin) : WarpOpSnapshot(ThisKind, offset), builtin_(builtin) { MOZ_ASSERT(builtin); } JSObject* builtin() const { return builtin_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // The intrinsic for JSOp::GetIntrinsic if it exists at compile-time. class WarpGetIntrinsic : public WarpOpSnapshot { WarpGCPtr intrinsic_; public: static constexpr Kind ThisKind = Kind::WarpGetIntrinsic; WarpGetIntrinsic(uint32_t offset, const Value& intrinsic) : WarpOpSnapshot(ThisKind, offset), intrinsic_(intrinsic) {} Value intrinsic() const { return intrinsic_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // Target module environment and slot information for JSOp::GetImport. class WarpGetImport : public WarpOpSnapshot { WarpGCPtr targetEnv_; uint32_t numFixedSlots_; uint32_t slot_; bool needsLexicalCheck_; public: static constexpr Kind ThisKind = Kind::WarpGetImport; WarpGetImport(uint32_t offset, ModuleEnvironmentObject* targetEnv, uint32_t numFixedSlots, uint32_t slot, bool needsLexicalCheck) : WarpOpSnapshot(ThisKind, offset), targetEnv_(targetEnv), numFixedSlots_(numFixedSlots), slot_(slot), needsLexicalCheck_(needsLexicalCheck) {} ModuleEnvironmentObject* targetEnv() const { return targetEnv_; } uint32_t numFixedSlots() const { return numFixedSlots_; } uint32_t slot() const { return slot_; } bool needsLexicalCheck() const { return needsLexicalCheck_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // Informs WarpBuilder that an IC site is cold and execution should bail out. class WarpBailout : public WarpOpSnapshot { public: static constexpr Kind ThisKind = Kind::WarpBailout; explicit WarpBailout(uint32_t offset) : WarpOpSnapshot(ThisKind, offset) {} void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // Information from a Baseline IC stub. class WarpCacheIR : public WarpOpSnapshot { // Baseline stub code. Stored here to keep the CacheIRStubInfo alive. WarpGCPtr stubCode_; const CacheIRStubInfo* stubInfo_; // Copied Baseline stub data. Allocated in the same LifoAlloc. const uint8_t* stubData_; public: static constexpr Kind ThisKind = Kind::WarpCacheIR; WarpCacheIR(uint32_t offset, JitCode* stubCode, const CacheIRStubInfo* stubInfo, const uint8_t* stubData) : WarpOpSnapshot(ThisKind, offset), stubCode_(stubCode), stubInfo_(stubInfo), stubData_(stubData) {} const CacheIRStubInfo* stubInfo() const { return stubInfo_; } const uint8_t* stubData() const { return stubData_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // [SMDOC] Warp Nursery Object support // // CacheIR stub data can contain nursery allocated objects. This can happen for // example for GuardSpecificObject/GuardSpecificFunction or GuardProto. // // To support nursery GCs in parallel with off-thread compilation, we use the // following mechanism: // // * When WarpOracle copies stub data, it builds a Vector of nursery objects. // The nursery object pointers in the stub data are replaced with the // corresponding index into this Vector. // See WarpScriptOracle::replaceNurseryPointers. // // * The Vector is copied to the snapshot and, at the end of compilation, to // the IonScript. The Vector is only accessed on the main thread. // // * The MIR backend never accesses the raw JSObject*. Instead, it uses // MNurseryObject which will load the object at runtime from the IonScript. // // WarpObjectField is a helper class to encode/decode a stub data field that // either stores an object or a nursery index. class WarpObjectField { // This is a nursery index if the low bit is set. Else it's a JSObject*. static constexpr uintptr_t NurseryIndexTag = 0x1; static constexpr uintptr_t NurseryIndexShift = 1; uintptr_t data_; explicit WarpObjectField(uintptr_t data) : data_(data) {} public: static WarpObjectField fromData(uintptr_t data) { return WarpObjectField(data); } static WarpObjectField fromObject(JSObject* obj) { return WarpObjectField(uintptr_t(obj)); } static WarpObjectField fromNurseryIndex(uint32_t index) { uintptr_t data = (uintptr_t(index) << NurseryIndexShift) | NurseryIndexTag; return WarpObjectField(data); } uintptr_t rawData() const { return data_; } bool isNurseryIndex() const { return (data_ & NurseryIndexTag) != 0; } uint32_t toNurseryIndex() const { MOZ_ASSERT(isNurseryIndex()); return data_ >> NurseryIndexShift; } JSObject* toObject() const { MOZ_ASSERT(!isNurseryIndex()); return reinterpret_cast(data_); } }; // Information for inlining a scripted call IC. class WarpInlinedCall : public WarpOpSnapshot { // Used for generating the correct guards. WarpCacheIR* cacheIRSnapshot_; // Used for generating the inlined code. WarpScriptSnapshot* scriptSnapshot_; CompileInfo* info_; public: static constexpr Kind ThisKind = Kind::WarpInlinedCall; WarpInlinedCall(uint32_t offset, WarpCacheIR* cacheIRSnapshot, WarpScriptSnapshot* scriptSnapshot, CompileInfo* info) : WarpOpSnapshot(ThisKind, offset), cacheIRSnapshot_(cacheIRSnapshot), scriptSnapshot_(scriptSnapshot), info_(info) {} WarpCacheIR* cacheIRSnapshot() const { return cacheIRSnapshot_; } WarpScriptSnapshot* scriptSnapshot() const { return scriptSnapshot_; } CompileInfo* info() const { return info_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // Information for inlining an ordered set of types class WarpPolymorphicTypes : public WarpOpSnapshot { TypeDataList list_; public: static constexpr Kind ThisKind = Kind::WarpPolymorphicTypes; WarpPolymorphicTypes(uint32_t offset, TypeDataList list) : WarpOpSnapshot(ThisKind, offset), list_(list) {} const TypeDataList& list() const { return list_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // Shape for JSOp::Rest. class WarpRest : public WarpOpSnapshot { WarpGCPtr shape_; public: static constexpr Kind ThisKind = Kind::WarpRest; WarpRest(uint32_t offset, Shape* shape) : WarpOpSnapshot(ThisKind, offset), shape_(shape) {} Shape* shape() const { return shape_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // Global environment for BindGName class WarpBindGName : public WarpOpSnapshot { WarpGCPtr globalEnv_; public: static constexpr Kind ThisKind = Kind::WarpBindGName; WarpBindGName(uint32_t offset, JSObject* globalEnv) : WarpOpSnapshot(ThisKind, offset), globalEnv_(globalEnv) {} JSObject* globalEnv() const { return globalEnv_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // Block environment for PushVarEnv class WarpVarEnvironment : public WarpOpSnapshot { WarpGCPtr templateObj_; public: static constexpr Kind ThisKind = Kind::WarpVarEnvironment; WarpVarEnvironment(uint32_t offset, VarEnvironmentObject* templateObj) : WarpOpSnapshot(ThisKind, offset), templateObj_(templateObj) {} VarEnvironmentObject* templateObj() const { return templateObj_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // Block environment for PushLexicalEnv, FreshenLexicalEnv, RecreateLexicalEnv class WarpLexicalEnvironment : public WarpOpSnapshot { WarpGCPtr templateObj_; public: static constexpr Kind ThisKind = Kind::WarpLexicalEnvironment; WarpLexicalEnvironment(uint32_t offset, BlockLexicalEnvironmentObject* templateObj) : WarpOpSnapshot(ThisKind, offset), templateObj_(templateObj) {} BlockLexicalEnvironmentObject* templateObj() const { return templateObj_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; // Class body lexical environment for PushClassBodyEnv class WarpClassBodyEnvironment : public WarpOpSnapshot { WarpGCPtr templateObj_; public: static constexpr Kind ThisKind = Kind::WarpClassBodyEnvironment; WarpClassBodyEnvironment(uint32_t offset, ClassBodyLexicalEnvironmentObject* templateObj) : WarpOpSnapshot(ThisKind, offset), templateObj_(templateObj) {} ClassBodyLexicalEnvironmentObject* templateObj() const { return templateObj_; } void traceData(JSTracer* trc); #ifdef JS_JITSPEW void dumpData(GenericPrinter& out) const; #endif }; struct NoEnvironment {}; using ConstantObjectEnvironment = WarpGCPtr; struct FunctionEnvironment { WarpGCPtr callObjectTemplate; WarpGCPtr namedLambdaTemplate; public: FunctionEnvironment(CallObject* callObjectTemplate, NamedLambdaObject* namedLambdaTemplate) : callObjectTemplate(callObjectTemplate), namedLambdaTemplate(namedLambdaTemplate) {} }; // Snapshot data for the environment object(s) created in the script's prologue. // // One of: // // * NoEnvironment: No environment object should be set. Leave the slot // initialized to |undefined|. // // * ConstantObjectEnvironment: Use this JSObject* as environment object. // // * FunctionEnvironment: Use the callee's environment chain. Optionally // allocate a new NamedLambdaObject and/or CallObject based on // namedLambdaTemplate and callObjectTemplate. using WarpEnvironment = mozilla::Variant; // Snapshot data for a single JSScript. class WarpScriptSnapshot : public TempObject, public mozilla::LinkedListElement { WarpGCPtr script_; WarpEnvironment environment_; WarpOpSnapshotList opSnapshots_; // If the script has a JSOp::ImportMeta op, this is the module to bake in. WarpGCPtr moduleObject_; // Whether this script is for an arrow function. bool isArrowFunction_; bool isMonomorphicInlined_; public: WarpScriptSnapshot(JSScript* script, const WarpEnvironment& env, WarpOpSnapshotList&& opSnapshots, ModuleObject* moduleObject); JSScript* script() const { return script_; } const WarpEnvironment& environment() const { return environment_; } const WarpOpSnapshotList& opSnapshots() const { return opSnapshots_; } ModuleObject* moduleObject() const { return moduleObject_; } bool isArrowFunction() const { return isArrowFunction_; } bool isMonomorphicInlined() const { return isMonomorphicInlined_; } void markIsMonomorphicInlined() { isMonomorphicInlined_ = true; } void trace(JSTracer* trc); #ifdef JS_JITSPEW void dump(GenericPrinter& out) const; #endif }; // Captures information from previous bailouts to prevent bailout/recompile // loops. class WarpBailoutInfo { // True if any script in the compilation has the failedBoundsCheck flag. In // this case mark bounds checks as non-movable to prevent hoisting them in // TryEliminateBoundsCheck. bool failedBoundsCheck_ = false; // True if any script in the compilation has the failedLexicalCheck flag. In // this case mark lexical checks as non-movable. bool failedLexicalCheck_ = false; public: bool failedBoundsCheck() const { return failedBoundsCheck_; } void setFailedBoundsCheck() { failedBoundsCheck_ = true; } bool failedLexicalCheck() const { return failedLexicalCheck_; } void setFailedLexicalCheck() { failedLexicalCheck_ = true; } }; using WarpScriptSnapshotList = mozilla::LinkedList; // Data allocated by WarpOracle on the main thread that's used off-thread by // WarpBuilder to build the MIR graph. class WarpSnapshot : public TempObject { // The scripts being compiled. WarpScriptSnapshotList scriptSnapshots_; // The global lexical environment and its thisObject(). We don't inline // cross-realm calls so this can be stored once per snapshot. WarpGCPtr globalLexicalEnv_; WarpGCPtr globalLexicalEnvThis_; const WarpBailoutInfo bailoutInfo_; // List of (originally) nursery-allocated objects. Must only be accessed on // the main thread. See also WarpObjectField. using NurseryObjectVector = Vector; NurseryObjectVector nurseryObjects_; #ifdef JS_CACHEIR_SPEW bool needsFinalWarmUpCount_ = false; #endif #ifdef DEBUG // A hash of the stub pointers and entry counts for each of the ICs // in this snapshot. mozilla::HashNumber icHash_ = 0; #endif public: explicit WarpSnapshot(JSContext* cx, TempAllocator& alloc, WarpScriptSnapshotList&& scriptSnapshots, const WarpBailoutInfo& bailoutInfo, bool recordWarmUpCount); WarpScriptSnapshot* rootScript() { return scriptSnapshots_.getFirst(); } const WarpScriptSnapshotList& scripts() const { return scriptSnapshots_; } GlobalLexicalEnvironmentObject* globalLexicalEnv() const { return globalLexicalEnv_; } JSObject* globalLexicalEnvThis() const { return globalLexicalEnvThis_; } void trace(JSTracer* trc); const WarpBailoutInfo& bailoutInfo() const { return bailoutInfo_; } NurseryObjectVector& nurseryObjects() { return nurseryObjects_; } const NurseryObjectVector& nurseryObjects() const { return nurseryObjects_; } #ifdef DEBUG mozilla::HashNumber icHash() const { return icHash_; } void setICHash(mozilla::HashNumber hash) { icHash_ = hash; } #endif #ifdef JS_JITSPEW void dump() const; void dump(GenericPrinter& out) const; #endif #ifdef JS_CACHEIR_SPEW bool needsFinalWarmUpCount() const { return needsFinalWarmUpCount_; } #endif }; } // namespace jit } // namespace js #endif /* jit_WarpSnapshot_h */