Bug 1224389 - Odin: simplify AsmJSModule global data allocation (r=bbouvier)

--HG--
extra : rebase_source : 20f174a9593293674e20e768f6d19f6ede861083
This commit is contained in:
Luke Wagner 2015-11-24 23:35:20 -06:00
Родитель f7c5ebaa66
Коммит 4ef42db6e3
7 изменённых файлов: 223 добавлений и 365 удалений

Просмотреть файл

@ -130,9 +130,7 @@ static bool
ValidateGlobalVariable(JSContext* cx, const AsmJSModule& module, AsmJSModule::Global& global,
HandleValue importVal)
{
MOZ_ASSERT(global.which() == AsmJSModule::Global::Variable);
void* datum = module.globalVarToGlobalDatum(global);
void* datum = module.globalData() + global.varGlobalDataOffset();
switch (global.varInitKind()) {
case AsmJSModule::Global::InitConstant: {
@ -588,10 +586,10 @@ DynamicallyLinkModule(JSContext* cx, const CallArgs& args, AsmJSModule& module)
}
}
for (unsigned i = 0; i < module.numExits(); i++)
module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();
module.initGlobalNaN();
for (unsigned i = 0; i < module.numExits(); i++) {
const AsmJSModule::Exit& exit = module.exit(i);
exit.datum(module).fun = &ffis[exit.ffiIndex()]->as<JSFunction>();
}
// See the comment in AllocateExecutableMemory.
ExecutableAllocator::makeExecutable(module.codeBase(), module.codeBytes());
@ -957,12 +955,7 @@ SendModuleToAttachedProfiler(JSContext* cx, AsmJSModule& module)
if (IsVTuneProfilingActive() && !SendFunctionsToVTune(cx, module))
return false;
#endif
#if defined(JS_ION_PERF)
if (module.numExportedFunctions() > 0) {
size_t firstEntryCode = size_t(module.codeBase() + module.functionBytes());
writePerfSpewerAsmJSEntriesAndExits(firstEntryCode, module.codeBytes() - module.functionBytes());
}
if (!SendFunctionsToPerf(cx, module))
return false;
#endif

Просмотреть файл

@ -88,8 +88,7 @@ AsmJSModule::AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t
interrupted_(false)
{
mozilla::PodZero(&pod);
pod.funcPtrTableAndExitBytes_ = SIZE_MAX;
pod.functionBytes_ = UINT32_MAX;
pod.globalBytes_ = sInitialGlobalDataBytes;
pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
pod.maxHeapLength_ = 0x80000000;
pod.strict_ = strict;
@ -110,7 +109,7 @@ AsmJSModule::~AsmJSModule()
if (code_) {
for (unsigned i = 0; i < numExits(); i++) {
AsmJSModule::ExitDatum& exitDatum = exitIndexToGlobalDatum(i);
AsmJSModule::ExitDatum& exitDatum = exit(i).datum(*this);
if (!exitDatum.baselineScript)
continue;
@ -130,19 +129,19 @@ AsmJSModule::~AsmJSModule()
void
AsmJSModule::trace(JSTracer* trc)
{
for (unsigned i = 0; i < globals_.length(); i++)
globals_[i].trace(trc);
for (unsigned i = 0; i < exits_.length(); i++) {
if (exitIndexToGlobalDatum(i).fun)
TraceEdge(trc, &exitIndexToGlobalDatum(i).fun, "asm.js imported function");
for (Global& global : globals_)
global.trace(trc);
for (Exit& exit : exits_) {
if (exit.datum(*this).fun)
TraceEdge(trc, &exit.datum(*this).fun, "asm.js imported function");
}
for (unsigned i = 0; i < exports_.length(); i++)
exports_[i].trace(trc);
for (unsigned i = 0; i < names_.length(); i++)
TraceManuallyBarrieredEdge(trc, &names_[i].name(), "asm.js module function name");
for (ExportedFunction& exp : exports_)
exp.trace(trc);
for (Name& name : names_)
TraceManuallyBarrieredEdge(trc, &name.name(), "asm.js module function name");
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
for (unsigned i = 0; i < profiledFunctions_.length(); i++)
profiledFunctions_[i].trace(trc);
for (ProfiledFunction& profiledFunction : profiledFunctions_)
profiledFunction.trace(trc);
#endif
if (globalArgumentName_)
TraceManuallyBarrieredEdge(trc, &globalArgumentName_, "asm.js global argument name");
@ -269,7 +268,7 @@ bool
AsmJSModule::finish(ExclusiveContext* cx, TokenStream& tokenStream, MacroAssembler& masm,
const Label& interruptLabel, const Label& outOfBoundsLabel)
{
MOZ_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
MOZ_ASSERT(!isFinished());
uint32_t endBeforeCurly = tokenStream.currentToken().pos.end;
TokenPos pos;
@ -284,10 +283,11 @@ AsmJSModule::finish(ExclusiveContext* cx, TokenStream& tokenStream, MacroAssembl
// Start global data on a new page so JIT code may be given independent
// protection flags.
pod.codeBytes_ = AlignBytes(masm.bytesNeeded(), AsmJSPageSize);
MOZ_ASSERT(pod.functionBytes_ <= pod.codeBytes_);
// The entire region is allocated via mmap/VirtualAlloc which requires
// units of pages.
pod.totalBytes_ = AlignBytes(pod.codeBytes_ + globalDataBytes(), AsmJSPageSize);
pod.totalBytes_ = AlignBytes(pod.codeBytes_ + pod.globalBytes_, AsmJSPageSize);
MOZ_ASSERT(!code_);
code_ = AllocateExecutableMemory(cx, pod.totalBytes_);
@ -506,14 +506,15 @@ TryEnablingJit(JSContext* cx, AsmJSModule& module, HandleFunction fun, uint32_t
}
// The exit may have become optimized while executing the FFI.
if (module.exitIsOptimized(exitIndex))
AsmJSModule::Exit& exit = module.exit(exitIndex);
if (exit.isOptimized(module))
return true;
BaselineScript* baselineScript = script->baselineScript();
if (!baselineScript->addDependentAsmJSModule(cx, DependentAsmJSModuleExit(&module, exitIndex)))
return false;
module.optimizeExit(exitIndex, baselineScript);
exit.optimize(module, baselineScript);
return true;
}
@ -524,7 +525,7 @@ InvokeFromAsmJS(AsmJSActivation* activation, int32_t exitIndex, int32_t argc, Va
JSContext* cx = activation->cx();
AsmJSModule& module = activation->module();
RootedFunction fun(cx, module.exitIndexToGlobalDatum(exitIndex).fun);
RootedFunction fun(cx, module.exit(exitIndex).datum(module).fun);
RootedValue fval(cx, ObjectValue(*fun));
if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval))
return false;
@ -710,7 +711,6 @@ void
AsmJSModule::staticallyLink(ExclusiveContext* cx)
{
MOZ_ASSERT(isFinished());
MOZ_ASSERT(!isStaticallyLinked());
// Process staticLinkData_
@ -762,14 +762,11 @@ AsmJSModule::staticallyLink(ExclusiveContext* cx)
// Initialize global data segment
for (size_t i = 0; i < exits_.length(); i++) {
ExitDatum& exitDatum = exitIndexToGlobalDatum(i);
exitDatum.exit = interpExitTrampoline(exits_[i]);
exitDatum.fun = nullptr;
exitDatum.baselineScript = nullptr;
}
*(double*)(globalData() + AsmJSNaN64GlobalDataOffset) = GenericNaN();
*(float*)(globalData() + AsmJSNaN32GlobalDataOffset) = GenericNaN();
MOZ_ASSERT(isStaticallyLinked());
for (AsmJSModule::Exit& exit : exits_)
exit.initDatum(*this);
}
void
@ -1801,7 +1798,7 @@ AsmJSModule::setProfilingEnabled(bool enabled, JSContext* cx)
// profiling prologues:
for (size_t i = 0; i < funcPtrTables_.length(); i++) {
FuncPtrTable& funcPtrTable = funcPtrTables_[i];
uint8_t** array = globalDataOffsetToFuncPtrTable(funcPtrTable.globalDataOffset());
auto array = reinterpret_cast<uint8_t**>(globalData() + funcPtrTable.globalDataOffset());
for (size_t j = 0; j < funcPtrTable.numElems(); j++) {
void* callee = array[j];
const CodeRange* codeRange = lookupCodeRange(callee);

Просмотреть файл

@ -114,7 +114,7 @@ class AsmJSModule
Which which_;
union {
struct {
uint32_t index_;
uint32_t globalDataOffset_;
VarInitKind initKind_;
union {
wasm::ValType importType_;
@ -157,9 +157,9 @@ class AsmJSModule
Which which() const {
return pod.which_;
}
uint32_t varIndex() const {
uint32_t varGlobalDataOffset() const {
MOZ_ASSERT(pod.which_ == Variable);
return pod.u.var.index_;
return pod.u.var.globalDataOffset_;
}
VarInitKind varInitKind() const {
MOZ_ASSERT(pod.which_ == Variable);
@ -259,6 +259,17 @@ class AsmJSModule
bool clone(ExclusiveContext* cx, Global* out) const;
};
// An Exit holds bookkeeping information about an exit; the ExitDatum
// struct overlays the actual runtime data stored in the global data
// section.
struct ExitDatum
{
uint8_t* exit;
jit::BaselineScript* baselineScript;
HeapPtrFunction fun;
};
class Exit
{
unsigned ffiIndex_;
@ -266,8 +277,6 @@ class AsmJSModule
unsigned interpCodeOffset_;
unsigned jitCodeOffset_;
friend class AsmJSModule;
public:
Exit() {}
Exit(unsigned ffiIndex, unsigned globalDataOffset)
@ -288,6 +297,29 @@ class AsmJSModule
MOZ_ASSERT(!jitCodeOffset_);
jitCodeOffset_ = off;
}
ExitDatum& datum(const AsmJSModule& module) const {
return *reinterpret_cast<ExitDatum*>(module.globalData() + globalDataOffset_);
}
void initDatum(const AsmJSModule& module) const {
MOZ_ASSERT(interpCodeOffset_);
ExitDatum& d = datum(module);
d.exit = module.codeBase() + interpCodeOffset_;
d.baselineScript = nullptr;
d.fun = nullptr;
}
bool isOptimized(const AsmJSModule& module) const {
return datum(module).exit == module.codeBase() + jitCodeOffset_;
}
void optimize(const AsmJSModule& module, jit::BaselineScript* baselineScript) const {
ExitDatum& d = datum(module);
d.exit = module.codeBase() + jitCodeOffset_;
d.baselineScript = baselineScript;
}
void deoptimize(const AsmJSModule& module) const {
ExitDatum& d = datum(module);
d.exit = module.codeBase() + interpCodeOffset_;
d.baselineScript = nullptr;
}
size_t serializedSize() const;
uint8_t* serialize(uint8_t* cursor) const;
@ -302,16 +334,6 @@ class AsmJSModule
typedef int32_t (*CodePtr)(EntryArg* args, uint8_t* global);
// An Exit holds bookkeeping information about an exit; the ExitDatum
// struct overlays the actual runtime data stored in the global data
// section.
struct ExitDatum
{
uint8_t* exit;
jit::BaselineScript* baselineScript;
HeapPtrFunction fun;
};
class ExportedFunction
{
PropertyName* name_;
@ -679,15 +701,13 @@ class AsmJSModule
private:
struct Pod {
size_t funcPtrTableAndExitBytes_;
size_t functionBytes_; // just the function bodies, no stubs
size_t codeBytes_; // function bodies and stubs
size_t totalBytes_; // function bodies, stubs, and global data
uint32_t functionBytes_;
uint32_t codeBytes_;
uint32_t globalBytes_;
uint32_t totalBytes_;
uint32_t minHeapLength_;
uint32_t maxHeapLength_;
uint32_t heapLengthMask_;
uint32_t numGlobalScalarVars_;
uint32_t numGlobalSimdVars_;
uint32_t numFFIs_;
uint32_t srcLength_;
uint32_t srcLengthWithRightBrace_;
@ -744,11 +764,8 @@ class AsmJSModule
void trace(JSTracer* trc);
~AsmJSModule();
// An AsmJSModule transitions monotonically through these states:
bool isFinishedWithModulePrologue() const { return pod.funcPtrTableAndExitBytes_ != SIZE_MAX; }
bool isFinishedWithFunctionBodies() const { return pod.functionBytes_ != UINT32_MAX; }
// An AsmJSModule transitions from !finished to finished to dynamically linked.
bool isFinished() const { return !!code_; }
bool isStaticallyLinked() const { return !!interruptExit_; }
bool isDynamicallyLinked() const { return dynamicallyLinked_; }
/*************************************************************************/
@ -812,17 +829,17 @@ class AsmJSModule
// module prologue (before the function bodies):
void initGlobalArgumentName(PropertyName* n) {
MOZ_ASSERT(!isFinishedWithModulePrologue());
MOZ_ASSERT(!isFinished());
MOZ_ASSERT_IF(n, n->isTenured());
globalArgumentName_ = n;
}
void initImportArgumentName(PropertyName* n) {
MOZ_ASSERT(!isFinishedWithModulePrologue());
MOZ_ASSERT(!isFinished());
MOZ_ASSERT_IF(n, n->isTenured());
importArgumentName_ = n;
}
void initBufferArgumentName(PropertyName* n) {
MOZ_ASSERT(!isFinishedWithModulePrologue());
MOZ_ASSERT(!isFinished());
MOZ_ASSERT_IF(n, n->isTenured());
bufferArgumentName_ = n;
}
@ -835,36 +852,54 @@ class AsmJSModule
PropertyName* bufferArgumentName() const {
return bufferArgumentName_;
}
bool addGlobalVarInit(const wasm::Val& v, uint32_t* globalIndex) {
MOZ_ASSERT(!isFinishedWithModulePrologue());
/*************************************************************************/
// These functions may only be called before finish():
private:
bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset) {
MOZ_ASSERT(!isFinished());
uint32_t pad = ComputeByteAlignment(pod.globalBytes_, align);
if (UINT32_MAX - pod.globalBytes_ < pad + bytes)
return false;
pod.globalBytes_ += pad;
*globalDataOffset = pod.globalBytes_;
pod.globalBytes_ += bytes;
return true;
}
bool addGlobalVar(wasm::ValType type, uint32_t* globalDataOffset) {
MOZ_ASSERT(!isFinished());
unsigned width = 0;
switch (type) {
case wasm::ValType::I32: case wasm::ValType::F32: width = 4; break;
case wasm::ValType::I64: case wasm::ValType::F64: width = 8; break;
case wasm::ValType::I32x4: case wasm::ValType::F32x4: width = 16; break;
}
return allocateGlobalBytes(width, width, globalDataOffset);
}
public:
bool addGlobalVarInit(const wasm::Val& v, uint32_t* globalDataOffset) {
MOZ_ASSERT(!isFinished());
if (!addGlobalVar(v.type(), globalDataOffset))
return false;
Global g(Global::Variable, nullptr);
g.pod.u.var.initKind_ = Global::InitConstant;
g.pod.u.var.u.val_ = v;
if (v.isSimd()) {
if (pod.numGlobalSimdVars_ == UINT32_MAX)
return false;
*globalIndex = pod.numGlobalSimdVars_++;
} else {
if (pod.numGlobalScalarVars_ == UINT32_MAX)
return false;
*globalIndex = pod.numGlobalScalarVars_++;
}
g.pod.u.var.index_ = *globalIndex;
g.pod.u.var.globalDataOffset_ = *globalDataOffset;
return globals_.append(g);
}
bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t* globalIndex) {
MOZ_ASSERT(!isFinishedWithModulePrologue());
bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t* globalDataOffset) {
MOZ_ASSERT(!isFinished());
if (!addGlobalVar(importType, globalDataOffset))
return false;
Global g(Global::Variable, name);
g.pod.u.var.initKind_ = Global::InitImport;
g.pod.u.var.u.importType_ = importType;
*globalIndex = IsSimdType(importType)
? pod.numGlobalSimdVars_++
: pod.numGlobalScalarVars_++;
g.pod.u.var.index_ = *globalIndex;
g.pod.u.var.globalDataOffset_ = *globalDataOffset;
return globals_.append(g);
}
bool addFFI(PropertyName* field, uint32_t* ffiIndex) {
MOZ_ASSERT(!isFinishedWithModulePrologue());
MOZ_ASSERT(!isFinished());
if (pod.numFFIs_ == UINT32_MAX)
return false;
Global g(Global::FFI, field);
@ -872,7 +907,7 @@ class AsmJSModule
return globals_.append(g);
}
bool addArrayView(Scalar::Type vt, PropertyName* maybeField, bool isSharedView) {
MOZ_ASSERT(!isFinishedWithModulePrologue());
MOZ_ASSERT(!isFinished());
MOZ_ASSERT(!pod.hasArrayView_ || (pod.isSharedView_ == isSharedView));
pod.hasArrayView_ = true;
pod.isSharedView_ = isSharedView;
@ -881,7 +916,7 @@ class AsmJSModule
return globals_.append(g);
}
bool addArrayViewCtor(Scalar::Type vt, PropertyName* field, bool isSharedView) {
MOZ_ASSERT(!isFinishedWithModulePrologue());
MOZ_ASSERT(!isFinished());
MOZ_ASSERT(field);
MOZ_ASSERT(!pod.isSharedView_ || isSharedView);
pod.isSharedView_ = isSharedView;
@ -890,42 +925,44 @@ class AsmJSModule
return globals_.append(g);
}
bool addByteLength() {
MOZ_ASSERT(!isFinishedWithModulePrologue());
MOZ_ASSERT(!isFinished());
Global g(Global::ByteLength, nullptr);
return globals_.append(g);
}
bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName* field) {
MOZ_ASSERT(!isFinishedWithModulePrologue());
MOZ_ASSERT(!isFinished());
Global g(Global::MathBuiltinFunction, field);
g.pod.u.mathBuiltinFunc_ = func;
return globals_.append(g);
}
bool addMathBuiltinConstant(double value, PropertyName* field) {
MOZ_ASSERT(!isFinishedWithModulePrologue());
MOZ_ASSERT(!isFinished());
Global g(Global::Constant, field);
g.pod.u.constant.value_ = value;
g.pod.u.constant.kind_ = Global::MathConstant;
return globals_.append(g);
}
bool addAtomicsBuiltinFunction(AsmJSAtomicsBuiltinFunction func, PropertyName* field) {
MOZ_ASSERT(!isFinishedWithModulePrologue());
MOZ_ASSERT(!isFinished());
Global g(Global::AtomicsBuiltinFunction, field);
g.pod.u.atomicsBuiltinFunc_ = func;
return globals_.append(g);
}
bool addSimdCtor(AsmJSSimdType type, PropertyName* field) {
MOZ_ASSERT(!isFinished());
Global g(Global::SimdCtor, field);
g.pod.u.simdCtorType_ = type;
return globals_.append(g);
}
bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName* field) {
MOZ_ASSERT(!isFinished());
Global g(Global::SimdOperation, field);
g.pod.u.simdOp.type_ = type;
g.pod.u.simdOp.which_ = op;
return globals_.append(g);
}
bool addGlobalConstant(double value, PropertyName* name) {
MOZ_ASSERT(!isFinishedWithModulePrologue());
MOZ_ASSERT(!isFinished());
Global g(Global::Constant, name);
g.pod.u.constant.value_ = value;
g.pod.u.constant.kind_ = Global::GlobalConstant;
@ -952,19 +989,10 @@ class AsmJSModule
}
}
/*************************************************************************/
void startFunctionBodies() {
MOZ_ASSERT(!isFinishedWithModulePrologue());
pod.funcPtrTableAndExitBytes_ = 0;
MOZ_ASSERT(isFinishedWithModulePrologue());
}
/*************************************************************************/
// These functions are called while parsing/compiling function bodies:
bool hasArrayView() const {
MOZ_ASSERT(isFinishedWithModulePrologue());
return pod.hasArrayView_;
}
bool isSharedView() const {
@ -972,7 +1000,7 @@ class AsmJSModule
return pod.isSharedView_;
}
void addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) {
MOZ_ASSERT(isFinishedWithModulePrologue());
MOZ_ASSERT(!isFinished());
MOZ_ASSERT(!pod.hasFixedMinHeapLength_);
MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1));
MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0));
@ -984,7 +1012,7 @@ class AsmJSModule
pod.hasFixedMinHeapLength_ = true;
}
bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
MOZ_ASSERT(!isFinished());
if (pod.hasFixedMinHeapLength_ && len > pod.minHeapLength_)
return false;
if (len > pod.maxHeapLength_)
@ -1013,69 +1041,43 @@ class AsmJSModule
codeRanges_.append(CodeRange(builtin, offsets));
}
bool addExit(unsigned ffiIndex, unsigned* exitIndex) {
MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
if (SIZE_MAX - pod.funcPtrTableAndExitBytes_ < sizeof(ExitDatum))
MOZ_ASSERT(!isFinished());
static_assert(sizeof(ExitDatum) % sizeof(void*) == 0, "word aligned");
uint32_t globalDataOffset;
if (!allocateGlobalBytes(sizeof(ExitDatum), sizeof(void*), &globalDataOffset))
return false;
uint32_t globalDataOffset = globalDataBytes();
JS_STATIC_ASSERT(sizeof(ExitDatum) % sizeof(void*) == 0);
pod.funcPtrTableAndExitBytes_ += sizeof(ExitDatum);
*exitIndex = unsigned(exits_.length());
return exits_.append(Exit(ffiIndex, globalDataOffset));
}
unsigned numExits() const {
MOZ_ASSERT(isFinishedWithModulePrologue());
return exits_.length();
}
Exit& exit(unsigned i) {
MOZ_ASSERT(isFinishedWithModulePrologue());
return exits_[i];
}
const Exit& exit(unsigned i) const {
MOZ_ASSERT(isFinishedWithModulePrologue());
return exits_[i];
}
bool addFuncPtrTable(unsigned numElems, uint32_t* globalDataOffset) {
MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinished());
MOZ_ASSERT(!isFinished());
MOZ_ASSERT(IsPowerOfTwo(numElems));
if (SIZE_MAX - pod.funcPtrTableAndExitBytes_ < numElems * sizeof(void*))
if (!allocateGlobalBytes(numElems * sizeof(void*), sizeof(void*), globalDataOffset))
return false;
*globalDataOffset = globalDataBytes();
if (!funcPtrTables_.append(FuncPtrTable(*globalDataOffset, numElems)))
return false;
pod.funcPtrTableAndExitBytes_ += numElems * sizeof(void*);
return true;
return funcPtrTables_.append(FuncPtrTable(*globalDataOffset, numElems));
}
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
bool addProfiledFunction(ProfiledFunction&& func)
{
MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
return profiledFunctions_.append(mozilla::Move(func));
bool addProfiledFunction(ProfiledFunction func) {
MOZ_ASSERT(!isFinished());
return profiledFunctions_.append(func);
}
unsigned numProfiledFunctions() const {
MOZ_ASSERT(isFinishedWithModulePrologue());
return profiledFunctions_.length();
}
ProfiledFunction& profiledFunction(unsigned i) {
MOZ_ASSERT(isFinishedWithModulePrologue());
return profiledFunctions_[i];
}
#endif
/*************************************************************************/
// This function is called after compiling the function bodies (before
// compiling entries/exits) to record the extent of compiled function code.
void finishFunctionBodies(size_t functionBytes) {
MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
pod.functionBytes_ = functionBytes;
MOZ_ASSERT(isFinishedWithFunctionBodies());
}
/*************************************************************************/
// Exported functions are added after finishFunctionBodies() and before
// finish(). The list of exported functions can be accessed any time after
// the exported functions have been added.
bool addExportedFunction(PropertyName* name,
uint32_t funcSrcBegin,
uint32_t funcSrcEnd,
@ -1085,7 +1087,7 @@ class AsmJSModule
// NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource
// (the entire file) and ExportedFunctions store offsets relative to
// the beginning of the module (so that they are caching-invariant).
MOZ_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
MOZ_ASSERT(!isFinished());
MOZ_ASSERT(srcStart_ < funcSrcBegin);
MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
ExportedFunction func(name, funcSrcBegin - srcStart_, funcSrcEnd - srcStart_,
@ -1098,7 +1100,7 @@ class AsmJSModule
PropertyName* maybeFieldName)
{
// See addExportedFunction.
MOZ_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
MOZ_ASSERT(!isFinished());
MOZ_ASSERT(srcStart_ < funcSrcBegin);
MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
ExportedFunction func(name, funcSrcBegin - srcStart_, funcSrcEnd - srcStart_,
@ -1106,15 +1108,12 @@ class AsmJSModule
return exports_.length() < UINT32_MAX && exports_.append(mozilla::Move(func));
}
unsigned numExportedFunctions() const {
MOZ_ASSERT(isFinishedWithFunctionBodies());
return exports_.length();
}
const ExportedFunction& exportedFunction(unsigned i) const {
MOZ_ASSERT(isFinishedWithFunctionBodies());
return exports_[i];
}
ExportedFunction& exportedFunction(unsigned i) {
MOZ_ASSERT(isFinishedWithFunctionBodies());
return exports_[i];
}
@ -1133,6 +1132,44 @@ class AsmJSModule
/*************************************************************************/
// These accessor functions can be used after finish():
uint8_t* codeBase() const {
MOZ_ASSERT(isFinished());
MOZ_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
return code_;
}
uint32_t codeBytes() const {
MOZ_ASSERT(isFinished());
return pod.codeBytes_;
}
bool containsCodePC(void* pc) const {
MOZ_ASSERT(isFinished());
return pc >= code_ && pc < (code_ + codeBytes());
}
// The range [0, functionBytes) is a subrange of [0, codeBytes) that
// contains only function body code, not the stub code. This distinction is
// used by the async interrupt handler to only interrupt when the pc is in
// function code which, in turn, simplifies reasoning about how stubs
// enter/exit.
void setFunctionBytes(uint32_t functionBytes) {
MOZ_ASSERT(!isFinished());
MOZ_ASSERT(!pod.functionBytes_);
pod.functionBytes_ = functionBytes;
}
uint32_t functionBytes() const {
MOZ_ASSERT(isFinished());
return pod.functionBytes_;
}
bool containsFunctionPC(void* pc) const {
MOZ_ASSERT(isFinished());
return pc >= code_ && pc < (code_ + functionBytes());
}
uint32_t globalBytes() const {
MOZ_ASSERT(isFinished());
return pod.globalBytes_;
}
unsigned numFFIs() const {
MOZ_ASSERT(isFinished());
return pod.numFFIs_;
@ -1145,39 +1182,6 @@ class AsmJSModule
MOZ_ASSERT(isFinished());
return srcStart_ + pod.srcLengthWithRightBrace_;
}
uint8_t* codeBase() const {
MOZ_ASSERT(isFinished());
MOZ_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
return code_;
}
size_t functionBytes() const {
MOZ_ASSERT(isFinished());
return pod.functionBytes_;
}
size_t codeBytes() const {
MOZ_ASSERT(isFinished());
return pod.codeBytes_;
}
bool containsFunctionPC(void* pc) const {
MOZ_ASSERT(isFinished());
return pc >= code_ && pc < (code_ + functionBytes());
}
bool containsCodePC(void* pc) const {
MOZ_ASSERT(isFinished());
return pc >= code_ && pc < (code_ + codeBytes());
}
private:
uint8_t* interpExitTrampoline(const Exit& exit) const {
MOZ_ASSERT(isFinished());
MOZ_ASSERT(exit.interpCodeOffset_);
return code_ + exit.interpCodeOffset_;
}
uint8_t* jitExitTrampoline(const Exit& exit) const {
MOZ_ASSERT(isFinished());
MOZ_ASSERT(exit.jitCodeOffset_);
return code_ + exit.jitCodeOffset_;
}
public:
// Lookup a callsite by the return pc (from the callee to the caller).
// Return null if no callsite was found.
@ -1193,145 +1197,51 @@ class AsmJSModule
// The global data section is placed after the executable code (i.e., at
// offset codeBytes_) in the module's linear allocation. The global data
// are laid out in this order:
// 0. a pointer to the current AsmJSActivation
// 1. a pointer to the heap that was linked to the module
// 2. the double float constant NaN
// 3. the float32 constant NaN, padded to Simd128DataSize
// 4. global SIMD variable state (elements are Simd128DataSize)
// 5. global variable state (elements are sizeof(uint64_t))
// 6. interleaved function-pointer tables and exits. These are allocated
// while type checking function bodies (as exits and uses of
// function-pointer tables are encountered).
size_t offsetOfGlobalData() const {
// starts with some fixed allocations followed by interleaved global,
// function-pointer table and exit allocations.
uint32_t offsetOfGlobalData() const {
MOZ_ASSERT(isFinished());
return pod.codeBytes_;
}
uint8_t* globalData() const {
MOZ_ASSERT(isFinished());
return code_ + offsetOfGlobalData();
return codeBase() + offsetOfGlobalData();
}
size_t globalSimdVarsOffset() const {
return AlignBytes(/* 0 */ sizeof(void*) +
/* 1 */ sizeof(void*) +
/* 2 */ sizeof(double) +
/* 3 */ sizeof(float),
jit::Simd128DataSize);
}
size_t globalDataBytes() const {
return globalSimdVarsOffset() +
/* 4 */ pod.numGlobalSimdVars_ * jit::Simd128DataSize +
/* 5 */ pod.numGlobalScalarVars_ * sizeof(uint64_t) +
/* 6 */ pod.funcPtrTableAndExitBytes_;
}
static unsigned activationGlobalDataOffset() {
JS_STATIC_ASSERT(jit::AsmJSActivationGlobalDataOffset == 0);
return 0;
static void assertGlobalDataOffsets() {
static_assert(jit::AsmJSActivationGlobalDataOffset == 0,
"global data goes first");
static_assert(jit::AsmJSHeapGlobalDataOffset == jit::AsmJSActivationGlobalDataOffset + sizeof(void*),
"then an AsmJSActivation*");
static_assert(jit::AsmJSNaN64GlobalDataOffset == jit::AsmJSHeapGlobalDataOffset + sizeof(uint8_t*),
"then a pointer to the heap");
static_assert(jit::AsmJSNaN32GlobalDataOffset == jit::AsmJSNaN64GlobalDataOffset + sizeof(double),
"then a 32-bit NaN");
static_assert(sInitialGlobalDataBytes == jit::AsmJSNaN32GlobalDataOffset + sizeof(float),
"then a 64-bit NaN");
}
static const uint32_t sInitialGlobalDataBytes = jit::AsmJSNaN32GlobalDataOffset + sizeof(float);
AsmJSActivation*& activation() const {
return *(AsmJSActivation**)(globalData() + activationGlobalDataOffset());
MOZ_ASSERT(isFinished());
return *(AsmJSActivation**)(globalData() + jit::AsmJSActivationGlobalDataOffset);
}
bool active() const {
return activation() != nullptr;
}
static unsigned heapGlobalDataOffset() {
JS_STATIC_ASSERT(jit::AsmJSHeapGlobalDataOffset == sizeof(void*));
return sizeof(void*);
}
private:
// The pointer may reference shared memory, use with care.
// Generally you want to use maybeHeap(), not heapDatum().
uint8_t*& heapDatum() const {
MOZ_ASSERT(isFinished());
return *(uint8_t**)(globalData() + heapGlobalDataOffset());
return *(uint8_t**)(globalData() + jit::AsmJSHeapGlobalDataOffset);
}
public:
static unsigned nan64GlobalDataOffset() {
static_assert(jit::AsmJSNaN64GlobalDataOffset % sizeof(double) == 0,
"Global data NaN should be aligned");
return heapGlobalDataOffset() + sizeof(void*);
}
static unsigned nan32GlobalDataOffset() {
static_assert(jit::AsmJSNaN32GlobalDataOffset % sizeof(double) == 0,
"Global data NaN should be aligned");
return nan64GlobalDataOffset() + sizeof(double);
}
void initGlobalNaN() {
MOZ_ASSERT(jit::AsmJSNaN64GlobalDataOffset == nan64GlobalDataOffset());
MOZ_ASSERT(jit::AsmJSNaN32GlobalDataOffset == nan32GlobalDataOffset());
*(double*)(globalData() + nan64GlobalDataOffset()) = GenericNaN();
*(float*)(globalData() + nan32GlobalDataOffset()) = GenericNaN();
}
unsigned globalSimdVarIndexToGlobalDataOffset(unsigned i) const {
MOZ_ASSERT(isFinishedWithModulePrologue());
MOZ_ASSERT(i < pod.numGlobalSimdVars_);
return globalSimdVarsOffset() +
i * jit::Simd128DataSize;
}
unsigned globalScalarVarIndexToGlobalDataOffset(unsigned i) const {
MOZ_ASSERT(isFinishedWithModulePrologue());
MOZ_ASSERT(i < pod.numGlobalScalarVars_);
return globalSimdVarsOffset() +
pod.numGlobalSimdVars_ * jit::Simd128DataSize +
i * sizeof(uint64_t);
}
void* globalScalarVarIndexToGlobalDatum(unsigned i) const {
MOZ_ASSERT(isFinished());
return (void*)(globalData() + globalScalarVarIndexToGlobalDataOffset(i));
}
void* globalSimdVarIndexToGlobalDatum(unsigned i) const {
MOZ_ASSERT(isFinished());
return (void*)(globalData() + globalSimdVarIndexToGlobalDataOffset(i));
}
void* globalVarToGlobalDatum(const Global& g) const {
unsigned index = g.varIndex();
if (g.varInitKind() == Global::VarInitKind::InitConstant) {
return g.varInitVal().isSimd()
? globalSimdVarIndexToGlobalDatum(index)
: globalScalarVarIndexToGlobalDatum(index);
}
MOZ_ASSERT(g.varInitKind() == Global::VarInitKind::InitImport);
return IsSimdType(g.varInitImportType())
? globalSimdVarIndexToGlobalDatum(index)
: globalScalarVarIndexToGlobalDatum(index);
}
uint8_t** globalDataOffsetToFuncPtrTable(unsigned globalDataOffset) const {
MOZ_ASSERT(isFinished());
MOZ_ASSERT(globalDataOffset < globalDataBytes());
return (uint8_t**)(globalData() + globalDataOffset);
}
unsigned exitIndexToGlobalDataOffset(unsigned exitIndex) const {
MOZ_ASSERT(isFinishedWithModulePrologue());
return exits_[exitIndex].globalDataOffset();
}
ExitDatum& exitIndexToGlobalDatum(unsigned exitIndex) const {
MOZ_ASSERT(isFinished());
return *(ExitDatum*)(globalData() + exitIndexToGlobalDataOffset(exitIndex));
}
bool exitIsOptimized(unsigned exitIndex) const {
MOZ_ASSERT(isFinished());
ExitDatum& exitDatum = exitIndexToGlobalDatum(exitIndex);
return exitDatum.exit != interpExitTrampoline(exit(exitIndex));
}
void optimizeExit(unsigned exitIndex, jit::BaselineScript* baselineScript) const {
MOZ_ASSERT(!exitIsOptimized(exitIndex));
ExitDatum& exitDatum = exitIndexToGlobalDatum(exitIndex);
exitDatum.exit = jitExitTrampoline(exit(exitIndex));
exitDatum.baselineScript = baselineScript;
}
void detachJitCompilation(size_t exitIndex) const {
MOZ_ASSERT(isFinished());
ExitDatum& exitDatum = exitIndexToGlobalDatum(exitIndex);
exitDatum.exit = interpExitTrampoline(exit(exitIndex));
exitDatum.baselineScript = nullptr;
}
/*************************************************************************/
// These functions are called after finish() but before staticallyLink():
bool addRelativeLink(RelativeLink link) {
MOZ_ASSERT(isFinished() && !isStaticallyLinked());
MOZ_ASSERT(isFinished());
return staticLinkData_.relativeLinks.append(link);
}
@ -1349,7 +1259,7 @@ class AsmJSModule
/*************************************************************************/
// After a module isFinished compiling or deserializing, it is "statically
// After a module is finished compiling or deserializing, it is "statically
// linked" which specializes the code to its current address (this allows
// code to be relocated between serialization and deserialization).
void staticallyLink(ExclusiveContext* cx);
@ -1362,6 +1272,7 @@ class AsmJSModule
// is kept in a list so that it can be updated if the linked buffer is
// detached.
void setIsDynamicallyLinked(JSRuntime* rt) {
MOZ_ASSERT(isFinished());
MOZ_ASSERT(!isDynamicallyLinked());
dynamicallyLinked_ = true;
nextLinked_ = rt->linkedAsmJSModules;

Просмотреть файл

@ -1022,7 +1022,7 @@ class MOZ_STACK_CLASS ModuleValidator
union {
struct {
Type::Which type_;
uint32_t index_;
uint32_t globalDataOffset_;
NumLit literalValue_;
} varOrConst;
uint32_t funcIndex_;
@ -1058,9 +1058,9 @@ class MOZ_STACK_CLASS ModuleValidator
MOZ_ASSERT(which_ == Variable || which_ == ConstantLiteral || which_ == ConstantImport);
return u.varOrConst.type_;
}
uint32_t varOrConstIndex() const {
uint32_t varOrConstGlobalDataOffset() const {
MOZ_ASSERT(which_ == Variable || which_ == ConstantImport);
return u.varOrConst.index_;
return u.varOrConst.globalDataOffset_;
}
bool isConst() const {
return which_ == ConstantLiteral || which_ == ConstantImport;
@ -1258,8 +1258,6 @@ class MOZ_STACK_CLASS ModuleValidator
NonAssertingLabel asyncInterruptLabel_;
NonAssertingLabel onDetachedLabel_;
DebugOnly<bool> finishedFunctionBodies_;
public:
ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser)
: cx_(cx),
@ -1285,8 +1283,7 @@ class MOZ_STACK_CLASS ModuleValidator
masm_(MacroAssembler::AsmJSToken()),
usecBefore_(PRMJ_Now()),
functionEntryOffsets_(cx),
slowFunctions_(cx),
finishedFunctionBodies_(false)
slowFunctions_(cx)
{
MOZ_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox());
}
@ -1416,14 +1413,14 @@ class MOZ_STACK_CLASS ModuleValidator
// The type of a const is the exact type of the literal (since its value
// cannot change) which is more precise than the corresponding vartype.
Type type = isConst ? Type::lit(lit) : Type::var(lit.type());
uint32_t globalIndex;
if (!module_->addGlobalVarInit(lit.value(), &globalIndex))
uint32_t globalDataOffset;
if (!module_->addGlobalVarInit(lit.value(), &globalDataOffset))
return false;
Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
Global* global = moduleLifo_.new_<Global>(which);
if (!global)
return false;
global->u.varOrConst.index_ = globalIndex;
global->u.varOrConst.globalDataOffset_ = globalDataOffset;
global->u.varOrConst.type_ = type.which();
if (isConst)
global->u.varOrConst.literalValue_ = lit;
@ -1432,14 +1429,14 @@ class MOZ_STACK_CLASS ModuleValidator
bool addGlobalVarImport(PropertyName* varName, PropertyName* fieldName, ValType importType,
bool isConst)
{
uint32_t globalIndex;
if (!module_->addGlobalVarImport(fieldName, importType, &globalIndex))
uint32_t globalDataOffset;
if (!module_->addGlobalVarImport(fieldName, importType, &globalDataOffset))
return false;
Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
Global* global = moduleLifo_.new_<Global>(which);
if (!global)
return false;
global->u.varOrConst.index_ = globalIndex;
global->u.varOrConst.globalDataOffset_ = globalDataOffset;
global->u.varOrConst.type_ = Type::var(importType).which();
return globals_.putNew(varName, global);
}
@ -1830,34 +1827,38 @@ class MOZ_STACK_CLASS ModuleValidator
}
bool finishGeneratingEntry(unsigned exportIndex, AsmJSOffsets offsets) {
MOZ_ASSERT(finishedFunctionBodies_);
module_->exportedFunction(exportIndex).initCodeOffset(offsets.begin);
return module_->addCodeRange(AsmJSModule::CodeRange::Entry, offsets);
}
bool finishGeneratingInlineStub(AsmJSOffsets offsets) {
MOZ_ASSERT(finishedFunctionBodies_);
return module_->addCodeRange(AsmJSModule::CodeRange::Inline, offsets);
}
bool finishGeneratingInterpExit(unsigned exitIndex, AsmJSProfilingOffsets offsets) {
MOZ_ASSERT(finishedFunctionBodies_);
module_->exit(exitIndex).initInterpOffset(offsets.begin);
return module_->addCodeRange(AsmJSModule::CodeRange::SlowFFI, offsets);
}
bool finishGeneratingJitExit(unsigned exitIndex, AsmJSProfilingOffsets offsets) {
MOZ_ASSERT(finishedFunctionBodies_);
module_->exit(exitIndex).initJitOffset(offsets.begin);
return module_->addCodeRange(AsmJSModule::CodeRange::JitFFI, offsets);
}
bool finishGeneratingInterrupt(AsmJSProfilingOffsets offsets) {
MOZ_ASSERT(finishedFunctionBodies_);
return module_->addCodeRange(AsmJSModule::CodeRange::Interrupt, offsets);
}
bool finishGeneratingBuiltinThunk(AsmJSExit::BuiltinKind builtin, AsmJSProfilingOffsets offsets) {
MOZ_ASSERT(finishedFunctionBodies_);
return module_->addBuiltinThunkCodeRange(builtin, offsets);
}
bool finish(ScopedJSDeletePtr<AsmJSModule>* module) {
// Patch internal calls to their final positions
for (auto& cs : masm().callSites()) {
if (!cs.isInternal())
continue;
MOZ_ASSERT(cs.kind() == CallSiteDesc::Relative);
uint32_t callerOffset = cs.returnAddressOffset();
uint32_t calleeOffset = functionEntryOffset(cs.targetIndex());
masm().patchCall(callerOffset, calleeOffset);
}
masm().finish();
if (masm().oom())
return false;
@ -1892,22 +1893,9 @@ class MOZ_STACK_CLASS ModuleValidator
}
module_->setViewsAreShared();
}
module_->startFunctionBodies();
}
bool finishFunctionBodies() {
// Patch internal calls to their final positions
for (auto& cs : masm().callSites()) {
if (!cs.isInternal())
continue;
MOZ_ASSERT(cs.kind() == CallSiteDesc::Relative);
uint32_t callerOffset = cs.returnAddressOffset();
uint32_t calleeOffset = functionEntryOffset(cs.targetIndex());
masm().patchCall(callerOffset, calleeOffset);
}
MOZ_ASSERT(!finishedFunctionBodies_);
module_->finishFunctionBodies(masm().currentOffset());
finishedFunctionBodies_ = true;
module_->setFunctionBytes(masm().currentOffset());
return true;
}
@ -3245,12 +3233,7 @@ CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type)
default: MOZ_CRASH("unexpected global type");
}
uint32_t globalIndex = global->varOrConstIndex();
// Write the global data offset
if (global->varOrConstType().isSimd())
f.writeU32(f.module().globalSimdVarIndexToGlobalDataOffset(globalIndex));
else
f.writeU32(f.module().globalScalarVarIndexToGlobalDataOffset(globalIndex));
f.writeU32(global->varOrConstGlobalDataOffset());
f.writeU8(uint8_t(global->isConst()));
*type = global->varOrConstType();
break;
@ -3636,12 +3619,7 @@ CheckAssignName(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type
default: MOZ_CRASH("unexpected global type");
}
unsigned globalIndex = global->varOrConstIndex();
// Global data offset
if (global->varOrConstType().isSimd())
f.patch32(indexAt, f.module().globalSimdVarIndexToGlobalDataOffset(globalIndex));
else
f.patch32(indexAt, f.module().globalScalarVarIndexToGlobalDataOffset(globalIndex));
f.patch32(indexAt, global->varOrConstGlobalDataOffset());
*type = rhsType;
return true;
}
@ -4365,7 +4343,7 @@ CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, ExprT
return false;
JS_STATIC_ASSERT(offsetof(AsmJSModule::ExitDatum, exit) == 0);
f.patch32(offsetAt, f.module().exitIndexToGlobalDataOffset(exitIndex));
f.patch32(offsetAt, f.module().exit(exitIndex).globalDataOffset());
f.patchSig(sigAt, lifoSig);
*type = Type::ret(ret);
return true;
@ -7725,7 +7703,7 @@ GenerateFFIIonExit(ModuleValidator& m, const LifoSig& sig, unsigned exitIndex, L
Register scratch = ABIArgGenerator::NonArgReturnReg1; // repeatedly clobbered
// 2.1. Get ExitDatum
unsigned globalDataOffset = m.module().exitIndexToGlobalDataOffset(exitIndex);
unsigned globalDataOffset = m.module().exit(exitIndex).globalDataOffset();
#if defined(JS_CODEGEN_X64)
m.masm().append(AsmJSGlobalAccess(masm.leaRipRelative(callee), globalDataOffset));
#elif defined(JS_CODEGEN_X86)

Просмотреть файл

@ -497,7 +497,7 @@ BaselineScript::clearDependentAsmJSModules()
if (dependentAsmJSModules_) {
for (size_t i = 0; i < dependentAsmJSModules_->length(); i++) {
DependentAsmJSModuleExit exit = (*dependentAsmJSModules_)[i];
exit.module->detachJitCompilation(exit.exitIndex);
exit.module->exit(exit.exitIndex).deoptimize(*exit.module);
}
dependentAsmJSModules_->clear();
@ -509,12 +509,8 @@ BaselineScript::unlinkDependentAsmJSModules(FreeOp* fop)
{
// Remove any links from AsmJSModules that contain optimized FFI calls into
// this BaselineScript.
clearDependentAsmJSModules();
if (dependentAsmJSModules_) {
for (size_t i = 0; i < dependentAsmJSModules_->length(); i++) {
DependentAsmJSModuleExit exit = (*dependentAsmJSModules_)[i];
exit.module->detachJitCompilation(exit.exitIndex);
}
fop->delete_(dependentAsmJSModules_);
dependentAsmJSModules_ = nullptr;
}

Просмотреть файл

@ -336,19 +336,4 @@ js::jit::writePerfSpewerAsmJSFunctionMap(uintptr_t base, uintptr_t size,
unlockPerfMap();
}
void
js::jit::writePerfSpewerAsmJSEntriesAndExits(uintptr_t base, size_t size)
{
if (size == 0)
return;
if (!lockPerfMap())
return;
fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxSIZE " AsmJS Entries and Exits (0x%" PRIxPTR " 0x%" PRIxSIZE ")\n",
base, size, base, size);
unlockPerfMap();
}
#endif // defined (JS_ION_PERF)

Просмотреть файл

@ -87,8 +87,6 @@ class AsmJSPerfSpewer : public PerfSpewer
void writePerfSpewerAsmJSFunctionMap(uintptr_t base, uintptr_t size, const char* filename,
unsigned lineno, unsigned colIndex, const char* funcName);
void writePerfSpewerAsmJSEntriesAndExits(uintptr_t base, size_t size);
#endif // JS_ION_PERF
} // namespace jit