#ifndef jit_JitRealm_h
#define jit_JitRealm_h
#include "mozilla/Array.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/MemoryReporting.h"
#include <utility>
#include "builtin/TypedObject.h"
#include "jit/CompileInfo.h"
#include "jit/ICStubSpace.h"
#include "jit/IonCode.h"
#include "jit/IonControlFlow.h"
#include "jit/JitFrames.h"
#include "jit/shared/Assembler-shared.h"
#include "js/GCHashTable.h"
#include "js/Value.h"
#include "vm/Stack.h"
namespace js {
namespace jit {
class FrameSizeClass;
struct VMFunctionData;
enum class TailCallVMFunctionId;
enum class VMFunctionId;
struct EnterJitData {
explicit EnterJitData(JSContext* cx)
: jitcode(nullptr),
osrFrame(nullptr),
calleeToken(nullptr),
maxArgv(nullptr),
maxArgc(0),
numActualArgs(0),
osrNumStackValues(0),
envChain(cx),
result(cx),
constructing(false) {}
uint8_t* jitcode;
InterpreterFrame* osrFrame;
void* calleeToken;
Value* maxArgv;
unsigned maxArgc;
unsigned numActualArgs;
unsigned osrNumStackValues;
RootedObject envChain;
RootedValue result;
bool constructing;
};
typedef void (*EnterJitCode)(void* code, unsigned argc, Value* argv,
InterpreterFrame* fp, CalleeToken calleeToken,
JSObject* envChain, size_t numStackValues,
Value* vp);
class JitcodeGlobalTable;
class JitRuntime {
private:
friend class JitRealm;
MainThreadData<ExecutableAllocator> execAlloc_;
MainThreadData<uint64_t> nextCompilationId_;
WriteOnceData<uint32_t> exceptionTailOffset_;
WriteOnceData<uint32_t> bailoutTailOffset_;
WriteOnceData<uint32_t> profilerExitFrameTailOffset_;
WriteOnceData<uint32_t> enterJITOffset_;
struct BailoutTable {
uint32_t startOffset;
uint32_t size;
BailoutTable(uint32_t startOffset, uint32_t size)
: startOffset(startOffset), size(size) {}
};
typedef Vector<BailoutTable, 4, SystemAllocPolicy> BailoutTableVector;
WriteOnceData<BailoutTableVector> bailoutTables_;
WriteOnceData<uint32_t> bailoutHandlerOffset_;
WriteOnceData<uint32_t> argumentsRectifierOffset_;
WriteOnceData<uint32_t> argumentsRectifierReturnOffset_;
WriteOnceData<uint32_t> invalidatorOffset_;
WriteOnceData<uint32_t> valuePreBarrierOffset_;
WriteOnceData<uint32_t> stringPreBarrierOffset_;
WriteOnceData<uint32_t> objectPreBarrierOffset_;
WriteOnceData<uint32_t> shapePreBarrierOffset_;
WriteOnceData<uint32_t> objectGroupPreBarrierOffset_;
WriteOnceData<uint32_t> mallocStubOffset_;
WriteOnceData<uint32_t> freeStubOffset_;
WriteOnceData<uint32_t> lazyLinkStubOffset_;
WriteOnceData<uint32_t> interpreterStubOffset_;
WriteOnceData<uint32_t> doubleToInt32ValueStubOffset_;
WriteOnceData<JitCode*> debugTrapHandler_;
WriteOnceData<JitCode*> baselineDebugModeOSRHandler_;
WriteOnceData<void*> baselineDebugModeOSRHandlerNoFrameRegPopAddr_;
WriteOnceData<JitCode*> trampolineCode_;
using VMWrapperMap = HashMap<const VMFunction*, uint32_t, VMFunction>;
WriteOnceData<VMWrapperMap*> functionWrappers_;
using VMWrapperOffsets = Vector<uint32_t, 0, SystemAllocPolicy>;
VMWrapperOffsets functionWrapperOffsets_;
VMWrapperOffsets tailCallFunctionWrapperOffsets_;
UnprotectedData<JitcodeGlobalTable*> jitcodeGlobalTable_;
#ifdef DEBUG
MainThreadData<uint32_t> ionBailAfter_;
#endif
typedef mozilla::Atomic<size_t, mozilla::SequentiallyConsistent,
mozilla::recordreplay::Behavior::DontPreserve>
NumFinishedBuildersType;
NumFinishedBuildersType numFinishedBuilders_;
using IonBuilderList = mozilla::LinkedList<js::jit::IonBuilder>;
MainThreadData<IonBuilderList> ionLazyLinkList_;
MainThreadData<size_t> ionLazyLinkListSize_;
MainThreadData<uint64_t> disambiguationId_;
private:
void generateLazyLinkStub(MacroAssembler& masm);
void generateInterpreterStub(MacroAssembler& masm);
void generateDoubleToInt32ValueStub(MacroAssembler& masm);
void generateProfilerExitFrameTailStub(MacroAssembler& masm,
Label* profilerExitTail);
void generateExceptionTailStub(MacroAssembler& masm, void* handler,
Label* profilerExitTail);
void generateBailoutTailStub(MacroAssembler& masm, Label* bailoutTail);
void generateEnterJIT(JSContext* cx, MacroAssembler& masm);
void generateArgumentsRectifier(MacroAssembler& masm);
BailoutTable generateBailoutTable(MacroAssembler& masm, Label* bailoutTail,
uint32_t frameClass);
void generateBailoutHandler(MacroAssembler& masm, Label* bailoutTail);
void generateInvalidator(MacroAssembler& masm, Label* bailoutTail);
uint32_t generatePreBarrier(JSContext* cx, MacroAssembler& masm,
MIRType type);
void generateMallocStub(MacroAssembler& masm);
void generateFreeStub(MacroAssembler& masm);
JitCode* generateDebugTrapHandler(JSContext* cx);
JitCode* generateBaselineDebugModeOSRHandler(
JSContext* cx, uint32_t* noFrameRegPopOffsetOut);
bool generateVMWrapper(JSContext* cx, MacroAssembler& masm,
const VMFunctionData& f, void* nativeFun,
uint32_t* wrapperOffset);
template <typename IdT>
bool generateVMWrappers(JSContext* cx, MacroAssembler& masm,
VMWrapperOffsets& offsets);
bool generateVMWrappers(JSContext* cx, MacroAssembler& masm);
bool generateTLEventVM(MacroAssembler& masm, const VMFunctionData& f,
bool enter);
inline bool generateTLEnterVM(MacroAssembler& masm, const VMFunctionData& f) {
return generateTLEventVM(masm, f, true);
}
inline bool generateTLExitVM(MacroAssembler& masm, const VMFunctionData& f) {
return generateTLEventVM(masm, f, false);
}
uint32_t startTrampolineCode(MacroAssembler& masm);
TrampolinePtr trampolineCode(uint32_t offset) const {
MOZ_ASSERT(offset > 0);
MOZ_ASSERT(offset < trampolineCode_->instructionsSize());
return TrampolinePtr(trampolineCode_->raw() + offset);
}
public:
JitRuntime();
~JitRuntime();
MOZ_MUST_USE bool initialize(JSContext* cx);
static void Trace(JSTracer* trc, const js::AutoAccessAtomsZone& access);
static void TraceJitcodeGlobalTableForMinorGC(JSTracer* trc);
static MOZ_MUST_USE bool MarkJitcodeGlobalTableIteratively(GCMarker* marker);
static void SweepJitcodeGlobalTable(JSRuntime* rt);
ExecutableAllocator& execAlloc() { return execAlloc_.ref(); }
IonCompilationId nextCompilationId() {
return IonCompilationId(nextCompilationId_++);
}
TrampolinePtr getVMWrapper(const VMFunction& f) const;
TrampolinePtr getVMWrapper(VMFunctionId funId) const {
MOZ_ASSERT(trampolineCode_);
return trampolineCode(functionWrapperOffsets_[size_t(funId)]);
}
TrampolinePtr getVMWrapper(TailCallVMFunctionId funId) const {
MOZ_ASSERT(trampolineCode_);
return trampolineCode(tailCallFunctionWrapperOffsets_[size_t(funId)]);
}
JitCode* debugTrapHandler(JSContext* cx);
JitCode* getBaselineDebugModeOSRHandler(JSContext* cx);
void* getBaselineDebugModeOSRHandlerAddress(JSContext* cx, bool popFrameReg);
TrampolinePtr getGenericBailoutHandler() const {
return trampolineCode(bailoutHandlerOffset_);
}
TrampolinePtr getExceptionTail() const {
return trampolineCode(exceptionTailOffset_);
}
TrampolinePtr getBailoutTail() const {
return trampolineCode(bailoutTailOffset_);
}
TrampolinePtr getProfilerExitFrameTail() const {
return trampolineCode(profilerExitFrameTailOffset_);
}
TrampolinePtr getBailoutTable(const FrameSizeClass& frameClass) const;
uint32_t getBailoutTableSize(const FrameSizeClass& frameClass) const;
TrampolinePtr getArgumentsRectifier() const {
return trampolineCode(argumentsRectifierOffset_);
}
TrampolinePtr getArgumentsRectifierReturnAddr() const {
return trampolineCode(argumentsRectifierReturnOffset_);
}
TrampolinePtr getInvalidationThunk() const {
return trampolineCode(invalidatorOffset_);
}
EnterJitCode enterJit() const {
return JS_DATA_TO_FUNC_PTR(EnterJitCode,
trampolineCode(enterJITOffset_).value);
}
TrampolinePtr preBarrier(MIRType type) const {
switch (type) {
case MIRType::Value:
return trampolineCode(valuePreBarrierOffset_);
case MIRType::String:
return trampolineCode(stringPreBarrierOffset_);
case MIRType::Object:
return trampolineCode(objectPreBarrierOffset_);
case MIRType::Shape:
return trampolineCode(shapePreBarrierOffset_);
case MIRType::ObjectGroup:
return trampolineCode(objectGroupPreBarrierOffset_);
default:
MOZ_CRASH();
}
}
TrampolinePtr mallocStub() const { return trampolineCode(mallocStubOffset_); }
TrampolinePtr freeStub() const { return trampolineCode(freeStubOffset_); }
TrampolinePtr lazyLinkStub() const {
return trampolineCode(lazyLinkStubOffset_);
}
TrampolinePtr interpreterStub() const {
return trampolineCode(interpreterStubOffset_);
}
TrampolinePtr getDoubleToInt32ValueStub() const {
return trampolineCode(doubleToInt32ValueStubOffset_);
}
bool hasJitcodeGlobalTable() const { return jitcodeGlobalTable_ != nullptr; }
JitcodeGlobalTable* getJitcodeGlobalTable() {
MOZ_ASSERT(hasJitcodeGlobalTable());
return jitcodeGlobalTable_;
}
bool isProfilerInstrumentationEnabled(JSRuntime* rt) {
return rt->geckoProfiler().enabled();
}
bool isOptimizationTrackingEnabled(JSRuntime* rt) {
return isProfilerInstrumentationEnabled(rt);
}
#ifdef DEBUG
void* addressOfIonBailAfter() { return &ionBailAfter_; }
void setIonBailAfter(uint32_t after) { ionBailAfter_ = after; }
#endif
size_t numFinishedBuilders() const { return numFinishedBuilders_; }
NumFinishedBuildersType& numFinishedBuildersRef(
const AutoLockHelperThreadState& locked) {
return numFinishedBuilders_;
}
IonBuilderList& ionLazyLinkList(JSRuntime* rt);
size_t ionLazyLinkListSize() const { return ionLazyLinkListSize_; }
void ionLazyLinkListRemove(JSRuntime* rt, js::jit::IonBuilder* builder);
void ionLazyLinkListAdd(JSRuntime* rt, js::jit::IonBuilder* builder);
uint64_t nextDisambiguationId() { return disambiguationId_++; }
};
enum class CacheKind : uint8_t;
class CacheIRStubInfo;
enum class ICStubEngine : uint8_t {
Baseline = 0,
IonIC
};
struct CacheIRStubKey : public DefaultHasher<CacheIRStubKey> {
struct Lookup {
CacheKind kind;
ICStubEngine engine;
const uint8_t* code;
uint32_t length;
Lookup(CacheKind kind, ICStubEngine engine, const uint8_t* code,
uint32_t length)
: kind(kind), engine(engine), code(code), length(length) {}
};
static HashNumber hash(const Lookup& l);
static bool match(const CacheIRStubKey& entry, const Lookup& l);
UniquePtr<CacheIRStubInfo, JS::FreePolicy> stubInfo;
explicit CacheIRStubKey(CacheIRStubInfo* info) : stubInfo(info) {}
CacheIRStubKey(CacheIRStubKey&& other)
: stubInfo(std::move(other.stubInfo)) {}
void operator=(CacheIRStubKey&& other) {
stubInfo = std::move(other.stubInfo);
}
};
template <typename Key>
struct IcStubCodeMapGCPolicy {
static bool needsSweep(Key*, ReadBarrieredJitCode* value) {
return IsAboutToBeFinalized(value);
}
};
class JitZone {
OptimizedICStubSpace optimizedStubSpace_;
CFGSpace cfgSpace_;
using IonCacheIRStubInfoSet =
HashSet<CacheIRStubKey, CacheIRStubKey, SystemAllocPolicy>;
IonCacheIRStubInfoSet ionCacheIRStubInfoSet_;
using BaselineCacheIRStubCodeMap =
GCHashMap<CacheIRStubKey, ReadBarrieredJitCode, CacheIRStubKey,
SystemAllocPolicy, IcStubCodeMapGCPolicy<CacheIRStubKey>>;
BaselineCacheIRStubCodeMap baselineCacheIRStubCodes_;
public:
void sweep();
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t* jitZone, size_t* baselineStubsOptimized,
size_t* cachedCFG) const;
OptimizedICStubSpace* optimizedStubSpace() { return &optimizedStubSpace_; }
CFGSpace* cfgSpace() { return &cfgSpace_; }
JitCode* getBaselineCacheIRStubCode(const CacheIRStubKey::Lookup& key,
CacheIRStubInfo** stubInfo) {
auto p = baselineCacheIRStubCodes_.lookup(key);
if (p) {
*stubInfo = p->key().stubInfo.get();
return p->value();
}
*stubInfo = nullptr;
return nullptr;
}
MOZ_MUST_USE bool putBaselineCacheIRStubCode(
const CacheIRStubKey::Lookup& lookup, CacheIRStubKey& key,
JitCode* stubCode) {
auto p = baselineCacheIRStubCodes_.lookupForAdd(lookup);
MOZ_ASSERT(!p);
return baselineCacheIRStubCodes_.add(p, std::move(key), stubCode);
}
CacheIRStubInfo* getIonCacheIRStubInfo(const CacheIRStubKey::Lookup& key) {
IonCacheIRStubInfoSet::Ptr p = ionCacheIRStubInfoSet_.lookup(key);
return p ? p->stubInfo.get() : nullptr;
}
MOZ_MUST_USE bool putIonCacheIRStubInfo(const CacheIRStubKey::Lookup& lookup,
CacheIRStubKey& key) {
IonCacheIRStubInfoSet::AddPtr p =
ionCacheIRStubInfoSet_.lookupForAdd(lookup);
MOZ_ASSERT(!p);
return ionCacheIRStubInfoSet_.add(p, std::move(key));
}
void purgeIonCacheIRStubInfo() { ionCacheIRStubInfoSet_.clearAndCompact(); }
};
enum class BailoutReturnStub {
GetProp,
GetPropSuper,
SetProp,
GetElem,
GetElemSuper,
Call,
New,
Count
};
class JitRealm {
friend class JitActivation;
using ICStubCodeMap =
GCHashMap<uint32_t, ReadBarrieredJitCode, DefaultHasher<uint32_t>,
ZoneAllocPolicy, IcStubCodeMapGCPolicy<uint32_t>>;
ICStubCodeMap* stubCodes_;
struct BailoutReturnStubInfo {
void* addr;
uint32_t key;
BailoutReturnStubInfo() : addr(nullptr), key(0) {}
BailoutReturnStubInfo(void* addr_, uint32_t key_)
: addr(addr_), key(key_) {}
};
mozilla::EnumeratedArray<BailoutReturnStub, BailoutReturnStub::Count,
BailoutReturnStubInfo>
bailoutReturnStubInfo_;
enum StubIndex : uint32_t {
StringConcat = 0,
RegExpMatcher,
RegExpSearcher,
RegExpTester,
Count
};
mozilla::EnumeratedArray<StubIndex, StubIndex::Count, ReadBarrieredJitCode>
stubs_;
bool stringsCanBeInNursery;
JitCode* generateStringConcatStub(JSContext* cx);
JitCode* generateRegExpMatcherStub(JSContext* cx);
JitCode* generateRegExpSearcherStub(JSContext* cx);
JitCode* generateRegExpTesterStub(JSContext* cx);
JitCode* getStubNoBarrier(StubIndex stub,
uint32_t* requiredBarriersOut) const {
MOZ_ASSERT(CurrentThreadIsIonCompiling());
*requiredBarriersOut |= 1 << uint32_t(stub);
return stubs_[stub].unbarrieredGet();
}
public:
JitCode* getStubCode(uint32_t key) {
ICStubCodeMap::Ptr p = stubCodes_->lookup(key);
if (p) {
return p->value();
}
return nullptr;
}
MOZ_MUST_USE bool putStubCode(JSContext* cx, uint32_t key,
Handle<JitCode*> stubCode) {
MOZ_ASSERT(stubCode);
if (!stubCodes_->putNew(key, stubCode.get())) {
ReportOutOfMemory(cx);
return false;
}
return true;
}
void initBailoutReturnAddr(void* addr, uint32_t key, BailoutReturnStub kind) {
MOZ_ASSERT(bailoutReturnStubInfo_[kind].addr == nullptr);
bailoutReturnStubInfo_[kind] = BailoutReturnStubInfo{addr, key};
}
void* bailoutReturnAddr(BailoutReturnStub kind) {
MOZ_ASSERT(bailoutReturnStubInfo_[kind].addr);
return bailoutReturnStubInfo_[kind].addr;
}
JitRealm();
~JitRealm();
MOZ_MUST_USE bool initialize(JSContext* cx, bool zoneHasNurseryStrings);
MOZ_MUST_USE bool ensureIonStubsExist(JSContext* cx) {
if (stubs_[StringConcat]) {
return true;
}
stubs_[StringConcat] = generateStringConcatStub(cx);
return stubs_[StringConcat];
}
void sweep(JS::Realm* realm);
void discardStubs() {
for (ReadBarrieredJitCode& stubRef : stubs_) {
stubRef = nullptr;
}
}
bool hasStubs() const {
for (const ReadBarrieredJitCode& stubRef : stubs_) {
if (stubRef) {
return true;
}
}
return false;
}
void setStringsCanBeInNursery(bool allow) {
MOZ_ASSERT(!hasStubs());
stringsCanBeInNursery = allow;
}
JitCode* stringConcatStubNoBarrier(uint32_t* requiredBarriersOut) const {
return getStubNoBarrier(StringConcat, requiredBarriersOut);
}
JitCode* regExpMatcherStubNoBarrier(uint32_t* requiredBarriersOut) const {
return getStubNoBarrier(RegExpMatcher, requiredBarriersOut);
}
MOZ_MUST_USE bool ensureRegExpMatcherStubExists(JSContext* cx) {
if (stubs_[RegExpMatcher]) {
return true;
}
stubs_[RegExpMatcher] = generateRegExpMatcherStub(cx);
return stubs_[RegExpMatcher];
}
JitCode* regExpSearcherStubNoBarrier(uint32_t* requiredBarriersOut) const {
return getStubNoBarrier(RegExpSearcher, requiredBarriersOut);
}
MOZ_MUST_USE bool ensureRegExpSearcherStubExists(JSContext* cx) {
if (stubs_[RegExpSearcher]) {
return true;
}
stubs_[RegExpSearcher] = generateRegExpSearcherStub(cx);
return stubs_[RegExpSearcher];
}
JitCode* regExpTesterStubNoBarrier(uint32_t* requiredBarriersOut) const {
return getStubNoBarrier(RegExpTester, requiredBarriersOut);
}
MOZ_MUST_USE bool ensureRegExpTesterStubExists(JSContext* cx) {
if (stubs_[RegExpTester]) {
return true;
}
stubs_[RegExpTester] = generateRegExpTesterStub(cx);
return stubs_[RegExpTester];
}
void performStubReadBarriers(uint32_t stubsToBarrier) const;
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
};
void InvalidateAll(FreeOp* fop, JS::Zone* zone);
void FinishInvalidation(FreeOp* fop, JSScript* script);
class MOZ_RAII AutoWritableJitCodeFallible {
JSRuntime* rt_;
void* addr_;
size_t size_;
public:
AutoWritableJitCodeFallible(JSRuntime* rt, void* addr, size_t size)
: rt_(rt), addr_(addr), size_(size) {
rt_->toggleAutoWritableJitCodeActive(true);
}
AutoWritableJitCodeFallible(void* addr, size_t size)
: AutoWritableJitCodeFallible(TlsContext.get()->runtime(), addr, size) {}
explicit AutoWritableJitCodeFallible(JitCode* code)
: AutoWritableJitCodeFallible(code->runtimeFromMainThread(), code->raw(),
code->bufferSize()) {}
MOZ_MUST_USE bool makeWritable() {
return ExecutableAllocator::makeWritable(addr_, size_);
}
~AutoWritableJitCodeFallible() {
if (!ExecutableAllocator::makeExecutable(addr_, size_)) {
MOZ_CRASH();
}
rt_->toggleAutoWritableJitCodeActive(false);
}
};
class MOZ_RAII AutoWritableJitCode : private AutoWritableJitCodeFallible {
public:
AutoWritableJitCode(JSRuntime* rt, void* addr, size_t size)
: AutoWritableJitCodeFallible(rt, addr, size) {
MOZ_RELEASE_ASSERT(makeWritable());
}
AutoWritableJitCode(void* addr, size_t size)
: AutoWritableJitCode(TlsContext.get()->runtime(), addr, size) {}
explicit AutoWritableJitCode(JitCode* code)
: AutoWritableJitCode(code->runtimeFromMainThread(), code->raw(),
code->bufferSize()) {}
};
class MOZ_STACK_CLASS MaybeAutoWritableJitCode {
mozilla::Maybe<AutoWritableJitCode> awjc_;
public:
MaybeAutoWritableJitCode(void* addr, size_t size, ReprotectCode reprotect) {
if (reprotect) {
awjc_.emplace(addr, size);
}
}
MaybeAutoWritableJitCode(JitCode* code, ReprotectCode reprotect) {
if (reprotect) {
awjc_.emplace(code);
}
}
};
} }
#endif