☠☠ backed out by f8dd6bbde6ce ☠ ☠ | |
author | Benjamin Bouvier <benj@benj.me> |
Sat, 15 Aug 2015 14:41:43 +0200 | |
changeset 257953 | 4612c09141ae63f93b7d4dc042a18f521692f661 |
parent 257952 | 94c7d36f29ba70f22776a4cdeb71b770ce30e854 |
child 257954 | 51eebda696d4d9a2e03808b813daa3151513e9f4 |
push id | 29238 |
push user | ryanvm@gmail.com |
push date | Mon, 17 Aug 2015 13:06:57 +0000 |
treeherder | mozilla-central@a6eeb28458fd [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | luke |
bugs | 1186424 |
milestone | 43.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/asmjs/AsmJSValidate.cpp | file | annotate | diff | comparison | revisions | |
js/src/devtools/rootAnalysis/annotations.js | file | annotate | diff | comparison | revisions |
--- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -24,17 +24,16 @@ #ifdef MOZ_VTUNE # include "vtune/VTuneWrapper.h" #endif #include "jsmath.h" #include "jsprf.h" #include "jsutil.h" - #include "asmjs/AsmJSLink.h" #include "asmjs/AsmJSModule.h" #include "asmjs/AsmJSSignalHandlers.h" #include "builtin/SIMD.h" #include "frontend/Parser.h" #include "jit/AtomicOperations.h" #include "jit/CodeGenerator.h" #include "jit/CompileWrappers.h" @@ -1392,18 +1391,16 @@ enum class F32X4 : uint8_t { Load, Store, // asm.js specific Id, Bad }; -} // namespace - class AsmFunction { typedef Vector<uint8_t, 4096> Bytecode; Bytecode bytecode_; public: typedef Vector<AsmJSNumLit> VarInitializerVector; @@ -1527,131 +1524,145 @@ class AsmFunction // Read-only interface size_t size() const { return bytecode_.length(); } RetType returnedType() const { MOZ_ASSERT(returnedType_ != RetType::Which(-1)); return returnedType_; } const VarInitializerVector& varInitializers() const { return varInitializers_; } size_t numLocals() const { MOZ_ASSERT(numLocals_ != size_t(-1)); return numLocals_; } }; -namespace { - -class ModuleGlobals +class Func +{ + Signature sig_; + AsmFunction* bytecode_; + PropertyName* name_; + Label* entry_; + uint32_t funcIndex_; + uint32_t srcBegin_; + uint32_t srcEnd_; + uint32_t compileTime_; + bool defined_; + + public: + Func(PropertyName* name, Signature&& sig, Label* entry, uint32_t funcIndex) + : sig_(Move(sig)), bytecode_(nullptr), name_(name), entry_(entry), + funcIndex_(funcIndex), srcBegin_(0), srcEnd_(0), compileTime_(0), defined_(false) + {} + + PropertyName* name() const { return name_; } + bool defined() const { return defined_; } + uint32_t funcIndex() const { return funcIndex_; } + + void define(ParseNode* fn, AsmFunction* bytecode) { + MOZ_ASSERT(!defined_); + defined_ = true; + srcBegin_ = fn->pn_pos.begin; + srcEnd_ = fn->pn_pos.end; + bytecode_ = bytecode; + } + + AsmFunction* bytecode() const { MOZ_ASSERT(defined_); return bytecode_; } + uint32_t srcBegin() const { MOZ_ASSERT(defined_); return srcBegin_; } + uint32_t srcEnd() const { MOZ_ASSERT(defined_); return srcEnd_; } + Signature& sig() { return sig_; } + const Signature& sig() const { return sig_; } + Label& entry() const { return *entry_; } + uint32_t compileTime() const { return compileTime_; } + void accumulateCompileTime(uint32_t ms) { compileTime_ += ms; } +}; + +typedef Vector<const Func*> FuncPtrVector; + +class FuncPtrTable +{ + Signature sig_; + uint32_t mask_; + uint32_t globalDataOffset_; + uint32_t tableIndex_; + FuncPtrVector elems_; + + FuncPtrTable(FuncPtrTable&& rhs) = delete; + + public: + FuncPtrTable(ExclusiveContext* cx, Signature&& sig, uint32_t mask, uint32_t gdo, + uint32_t tableIndex) + : sig_(Move(sig)), mask_(mask), globalDataOffset_(gdo), tableIndex_(tableIndex), + elems_(cx) + {} + + Signature& sig() { return sig_; } + const Signature& sig() const { return sig_; } + unsigned mask() const { return mask_; } + unsigned globalDataOffset() const { return globalDataOffset_; } + unsigned tableIndex() const { return tableIndex_; } + + bool initialized() const { return !elems_.empty(); } + void initElems(FuncPtrVector&& elems) { elems_ = Move(elems); MOZ_ASSERT(initialized()); } + unsigned numElems() const { MOZ_ASSERT(initialized()); return elems_.length(); } + const Func& elem(unsigned i) const { return *elems_[i]; } +}; + +typedef Vector<FuncPtrTable*> FuncPtrTableVector; +typedef Vector<Func*> FuncVector; + +} // namespace + +class ModuleCompileResults { public: - - class Func - { - Signature sig_; - AsmFunction* bytecode_; - PropertyName* name_; - Label* entry_; - uint32_t funcIndex_; - uint32_t srcBegin_; - uint32_t srcEnd_; - uint32_t compileTime_; - bool defined_; - - public: - Func(PropertyName* name, Signature&& sig, Label* entry, uint32_t funcIndex) - : sig_(Move(sig)), bytecode_(nullptr), name_(name), entry_(entry), - funcIndex_(funcIndex), srcBegin_(0), srcEnd_(0), compileTime_(0), defined_(false) + struct SlowFunction + { + SlowFunction(PropertyName* name, unsigned ms, unsigned line, unsigned column) + : name(name), ms(ms), line(line), column(column) {} - PropertyName* name() const { return name_; } - bool defined() const { return defined_; } - uint32_t funcIndex() const { return funcIndex_; } - - void define(ParseNode* fn, AsmFunction* bytecode) { - MOZ_ASSERT(!defined_); - defined_ = true; - srcBegin_ = fn->pn_pos.begin; - srcEnd_ = fn->pn_pos.end; - bytecode_ = bytecode; - } - - AsmFunction* bytecode() const { MOZ_ASSERT(defined_); return bytecode_; } - uint32_t srcBegin() const { MOZ_ASSERT(defined_); return srcBegin_; } - uint32_t srcEnd() const { MOZ_ASSERT(defined_); return srcEnd_; } - Signature& sig() { return sig_; } - const Signature& sig() const { return sig_; } - Label& entry() const { return *entry_; } - uint32_t compileTime() const { return compileTime_; } - void accumulateCompileTime(uint32_t ms) { compileTime_ += ms; } + PropertyName* name; + unsigned ms; + unsigned line; + unsigned column; }; - typedef Vector<const Func*> FuncPtrVector; - - class FuncPtrTable - { - Signature sig_; - uint32_t mask_; - uint32_t globalDataOffset_; - uint32_t tableIndex_; - FuncPtrVector elems_; - - FuncPtrTable(FuncPtrTable&& rhs) = delete; - - public: - FuncPtrTable(ExclusiveContext* cx, Signature&& sig, uint32_t mask, uint32_t gdo, - uint32_t tableIndex) - : sig_(Move(sig)), mask_(mask), globalDataOffset_(gdo), tableIndex_(tableIndex), - elems_(cx) - {} - - Signature& sig() { return sig_; } - const Signature& sig() const { return sig_; } - unsigned mask() const { return mask_; } - unsigned globalDataOffset() const { return globalDataOffset_; } - unsigned tableIndex() const { return tableIndex_; } - - bool initialized() const { return !elems_.empty(); } - void initElems(FuncPtrVector&& elems) { elems_ = Move(elems); MOZ_ASSERT(initialized()); } - unsigned numElems() const { MOZ_ASSERT(initialized()); return elems_.length(); } - const Func& elem(unsigned i) const { return *elems_[i]; } - }; - - typedef Vector<FuncPtrTable*> FuncPtrTableVector; - typedef Vector<Func*> FuncVector; + typedef Vector<SlowFunction> SlowFunctionVector; private: - FuncVector functions_; - FuncPtrTableVector funcPtrTables_; + MacroAssembler masm_; + SlowFunctionVector slowFunctions_; + NonAssertingLabel stackOverflowLabel_; + NonAssertingLabel asyncInterruptLabel_; + NonAssertingLabel syncInterruptLabel_; + NonAssertingLabel onDetachedLabel_; + NonAssertingLabel onConversionErrorLabel_; + NonAssertingLabel onOutOfBoundsLabel_; + int64_t usecBefore_; public: - ModuleGlobals(ExclusiveContext* cx) - : functions_(cx), - funcPtrTables_(cx) + explicit ModuleCompileResults(ExclusiveContext* cx) + : masm_(MacroAssembler::AsmJSToken()), + slowFunctions_(cx), + usecBefore_(PRMJ_Now()) {} - unsigned numFunctions() const { - return functions_.length(); - } - Func* function(unsigned i) { - return functions_[i]; - } - bool addFunction(Func* func) { - return functions_.append(func); - } - - unsigned numFuncPtrTables() const { - return funcPtrTables_.length(); - } - FuncPtrTable& funcPtrTable(unsigned i) { - return *funcPtrTables_[i]; - } - bool addFuncPtrTable(FuncPtrTable* table) { - return funcPtrTables_.append(table); - } + MacroAssembler& masm() { return masm_; } + Label& stackOverflowLabel() { return stackOverflowLabel_; } + Label& asyncInterruptLabel() { return asyncInterruptLabel_; } + Label& syncInterruptLabel() { return syncInterruptLabel_; } + Label& onOutOfBoundsLabel() { return onOutOfBoundsLabel_; } + Label& onDetachedLabel() { return onDetachedLabel_; } + Label& onConversionErrorLabel() { return onConversionErrorLabel_; } + int64_t usecBefore() { return usecBefore_; } + SlowFunctionVector& slowFunctions() { return slowFunctions_; } }; +namespace { + // The ModuleValidator encapsulates the entire validation of an asm.js module. // Its lifetime goes from the validation of the top components of an asm.js // module (all the globals), the emission of bytecode for all the functions in -// the module and the validation of function's pointer tables. +// the module and the validation of function's pointer tables. It also finishes +// the compilation of all the module's stubs. // // Rooting note: ModuleValidator is a stack class that contains unrooted // PropertyName (JSAtom) pointers. This is safe because it cannot be // constructed without a TokenStream reference. TokenStream is itself a stack // class that cannot be constructed without an AutoKeepAtoms being live on the // stack, which prevents collection of atoms. // // ModuleValidator is marked as rooted in the rooting analysis. Don't add @@ -1856,24 +1867,29 @@ class MOZ_STACK_CLASS ModuleValidator }; private: typedef HashMap<PropertyName*, Global*> GlobalMap; typedef HashMap<PropertyName*, MathBuiltin> MathNameMap; typedef HashMap<PropertyName*, AsmJSAtomicsBuiltinFunction> AtomicsNameMap; typedef HashMap<PropertyName*, AsmJSSimdOperation> SimdOperationNameMap; typedef Vector<ArrayView> ArrayViewVector; + + public: typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor> ExitMap; + private: ExclusiveContext* cx_; AsmJSParser& parser_; ScopedJSDeletePtr<AsmJSModule> module_; LifoAlloc moduleLifo_; - ModuleGlobals& moduleGlobals_; + + FuncVector functions_; + FuncPtrTableVector funcPtrTables_; GlobalMap globals_; ArrayViewVector arrayViews_; ExitMap exits_; MathNameMap standardLibraryMathNames_; AtomicsNameMap standardLibraryAtomicsNames_; SimdOperationNameMap standardLibrarySimdOpNames_; @@ -1885,36 +1901,42 @@ class MOZ_STACK_CLASS ModuleValidator uint32_t errorOffset_; bool errorOverRecursed_; bool canValidateChangeHeap_; bool hasChangeHeap_; bool supportsSimd_; + ModuleCompileResults* compileData_; + DebugOnly<bool> finishedFunctionBodies_; + public: - ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser, ModuleGlobals& moduleGlobals) + ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser) : cx_(cx), parser_(parser), moduleLifo_(LIFO_ALLOC_PRIMARY_CHUNK_SIZE), - moduleGlobals_(moduleGlobals), + functions_(cx), + funcPtrTables_(cx), globals_(cx), arrayViews_(cx), exits_(cx), standardLibraryMathNames_(cx), standardLibraryAtomicsNames_(cx), standardLibrarySimdOpNames_(cx), moduleFunctionNode_(parser.pc->maybeFunction), moduleFunctionName_(nullptr), errorString_(nullptr), errorOffset_(UINT32_MAX), errorOverRecursed_(false), canValidateChangeHeap_(false), hasChangeHeap_(false), - supportsSimd_(cx->jitSupportsSimd()) + supportsSimd_(cx->jitSupportsSimd()), + compileData_(nullptr), + finishedFunctionBodies_(false) { MOZ_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox()); } ~ModuleValidator() { if (errorString_) { MOZ_ASSERT(errorOffset_ != UINT32_MAX); tokenStream().reportAsmJSError(errorOffset_, @@ -2176,61 +2198,61 @@ class MOZ_STACK_CLASS ModuleValidator if (!global) return false; uint32_t index; if (!module_->addFFI(field, &index)) return false; global->u.ffiIndex_ = index; return globals_.putNew(varName, global); } - bool addExportedFunction(const ModuleGlobals::Func& func, PropertyName* maybeFieldName) { + bool addExportedFunction(const Func& func, PropertyName* maybeFieldName) { AsmJSModule::ArgCoercionVector argCoercions; const VarTypeVector& args = func.sig().args(); if (!argCoercions.resize(args.length())) return false; for (unsigned i = 0; i < args.length(); i++) argCoercions[i] = args[i].toCoercion(); AsmJSModule::ReturnType retType = func.sig().retType().toModuleReturnType(); return module_->addExportedFunction(func.name(), func.srcBegin(), func.srcEnd(), maybeFieldName, Move(argCoercions), retType, func.funcIndex()); } bool addExportedChangeHeap(PropertyName* name, const Global& g, PropertyName* maybeFieldName) { return module_->addExportedChangeHeap(name, g.changeHeapSrcBegin(), g.changeHeapSrcEnd(), maybeFieldName); } - bool addFunction(PropertyName* name, Signature&& sig, ModuleGlobals::Func** func) { - uint32_t funcIndex = moduleGlobals_.numFunctions(); + bool addFunction(PropertyName* name, Signature&& sig, Func** func) { + uint32_t funcIndex = numFunctions(); Global* global = moduleLifo_.new_<Global>(Global::Function); if (!global) return false; global->u.funcIndex_ = funcIndex; if (!globals_.putNew(name, global)) return false; Label* entry = moduleLifo_.new_<Label>(); if (!entry) return false; - *func = moduleLifo_.new_<ModuleGlobals::Func>(name, Move(sig), entry, funcIndex); + *func = moduleLifo_.new_<Func>(name, Move(sig), entry, funcIndex); if (!*func) return false; - return moduleGlobals_.addFunction(*func); - } - bool addFuncPtrTable(PropertyName* name, Signature&& sig, uint32_t mask, ModuleGlobals::FuncPtrTable** table) - { - uint32_t tableIndex = moduleGlobals_.numFuncPtrTables(); + return functions_.append(*func); + } + bool addFuncPtrTable(PropertyName* name, Signature&& sig, uint32_t mask, FuncPtrTable** table) + { + uint32_t tableIndex = numFuncPtrTables(); Global* global = moduleLifo_.new_<Global>(Global::FuncPtrTable); if (!global) return false; global->u.funcPtrTableIndex_ = tableIndex; if (!globals_.putNew(name, global)) return false; uint32_t globalDataOffset; if (!module_->addFuncPtrTable(/* numElems = */ mask + 1, &globalDataOffset)) return false; - *table = moduleLifo_.new_<ModuleGlobals::FuncPtrTable>(cx_, Move(sig), mask, globalDataOffset, tableIndex); - return *table && moduleGlobals_.addFuncPtrTable(*table); + *table = moduleLifo_.new_<FuncPtrTable>(cx_, Move(sig), mask, globalDataOffset, tableIndex); + return *table && funcPtrTables_.append(*table); } bool addExit(unsigned ffiIndex, PropertyName* name, Signature&& sig, unsigned* exitIndex, Signature** lifoSig) { Signature* signature = moduleLifo_.new_<Signature>(Move(sig)); if (!signature) return false; *lifoSig = signature; @@ -2330,39 +2352,39 @@ class MOZ_STACK_CLASS ModuleValidator unsigned numArrayViews() const { return arrayViews_.length(); } const ArrayView& arrayView(unsigned i) const { return arrayViews_[i]; } unsigned numFunctions() const { - return moduleGlobals_.numFunctions(); - } - ModuleGlobals::Func& function(unsigned i) const { - return *moduleGlobals_.function(i); + return functions_.length(); + } + Func& function(unsigned i) const { + return *functions_[i]; } unsigned numFuncPtrTables() const { - return moduleGlobals_.numFuncPtrTables(); - } - ModuleGlobals::FuncPtrTable& funcPtrTable(unsigned i) const { - return moduleGlobals_.funcPtrTable(i); + return funcPtrTables_.length(); + } + FuncPtrTable& funcPtrTable(unsigned i) const { + return *funcPtrTables_[i]; } const Global* lookupGlobal(PropertyName* name) const { if (GlobalMap::Ptr p = globals_.lookup(name)) return p->value(); return nullptr; } - ModuleGlobals::Func* lookupFunction(PropertyName* name) { + Func* lookupFunction(PropertyName* name) { if (GlobalMap::Ptr p = globals_.lookup(name)) { Global* value = p->value(); if (value->which() == Global::Function) - return moduleGlobals_.function(value->funcIndex()); + return functions_[value->funcIndex()]; } return nullptr; } bool lookupStandardLibraryMathName(PropertyName* name, MathBuiltin* mathBuiltin) const { if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) { *mathBuiltin = p->value(); return true; @@ -2378,18 +2400,173 @@ class MOZ_STACK_CLASS ModuleValidator } bool lookupStandardSimdOpName(PropertyName* name, AsmJSSimdOperation* op) const { if (SimdOperationNameMap::Ptr p = standardLibrarySimdOpNames_.lookup(name)) { *op = p->value(); return true; } return false; } + + // End-of-compilation utils + void takeCompileData(ModuleCompileResults* compileData) { compileData_ = compileData; } + + MacroAssembler& masm() { return compileData_->masm(); } + Label& stackOverflowLabel() { return compileData_->stackOverflowLabel(); } + Label& asyncInterruptLabel() { return compileData_->asyncInterruptLabel(); } + Label& syncInterruptLabel() { return compileData_->syncInterruptLabel(); } + Label& onDetachedLabel() { return compileData_->onDetachedLabel(); } + Label& onOutOfBoundsLabel() { return compileData_->onOutOfBoundsLabel(); } + Label& onConversionErrorLabel() { return compileData_->onConversionErrorLabel(); } + ExitMap::Range allExits() const { return exits_.all(); } + + bool finishGeneratingEntry(unsigned exportIndex, Label* begin) { + MOZ_ASSERT(finishedFunctionBodies_); + module_->exportedFunction(exportIndex).initCodeOffset(begin->offset()); + uint32_t end = masm().currentOffset(); + return module_->addCodeRange(AsmJSModule::CodeRange::Entry, begin->offset(), end); + } + bool finishGeneratingInterpExit(unsigned exitIndex, Label* begin, Label* profilingReturn) { + MOZ_ASSERT(finishedFunctionBodies_); + uint32_t beg = begin->offset(); + module_->exit(exitIndex).initInterpOffset(beg); + uint32_t pret = profilingReturn->offset(); + uint32_t end = masm().currentOffset(); + return module_->addCodeRange(AsmJSModule::CodeRange::SlowFFI, beg, pret, end); + } + bool finishGeneratingJitExit(unsigned exitIndex, Label* begin, Label* profilingReturn) { + MOZ_ASSERT(finishedFunctionBodies_); + uint32_t beg = begin->offset(); + module_->exit(exitIndex).initJitOffset(beg); + uint32_t pret = profilingReturn->offset(); + uint32_t end = masm().currentOffset(); + return module_->addCodeRange(AsmJSModule::CodeRange::JitFFI, beg, pret, end); + } + bool finishGeneratingInlineStub(Label* begin) { + MOZ_ASSERT(finishedFunctionBodies_); + uint32_t end = masm().currentOffset(); + return module_->addCodeRange(AsmJSModule::CodeRange::Inline, begin->offset(), end); + } + bool finishGeneratingInterrupt(Label* begin, Label* profilingReturn) { + MOZ_ASSERT(finishedFunctionBodies_); + uint32_t beg = begin->offset(); + uint32_t pret = profilingReturn->offset(); + uint32_t end = masm().currentOffset(); + return module_->addCodeRange(AsmJSModule::CodeRange::Interrupt, beg, pret, end); + } + bool finishGeneratingBuiltinThunk(AsmJSExit::BuiltinKind builtin, Label* begin, Label* pret) { + MOZ_ASSERT(finishedFunctionBodies_); + uint32_t end = masm().currentOffset(); + return module_->addBuiltinThunkCodeRange(builtin, begin->offset(), pret->offset(), end); + } + + bool finish(ScopedJSDeletePtr<AsmJSModule>* module) { + masm().finish(); + if (masm().oom()) + return false; + + if (!module_->finish(cx_, tokenStream(), masm(), asyncInterruptLabel(), onOutOfBoundsLabel())) + return false; + + // Finally, convert all the function-pointer table elements into + // RelativeLinks that will be patched by AsmJSModule::staticallyLink. + for (unsigned tableIndex = 0; tableIndex < numFuncPtrTables(); tableIndex++) { + FuncPtrTable& table = funcPtrTable(tableIndex); + unsigned tableBaseOffset = module_->offsetOfGlobalData() + table.globalDataOffset(); + for (unsigned elemIndex = 0; elemIndex < table.numElems(); elemIndex++) { + AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::RawPointer); + link.patchAtOffset = tableBaseOffset + elemIndex * sizeof(uint8_t*); + link.targetOffset = masm().actualOffset(table.elem(elemIndex).entry().offset()); + if (!module_->addRelativeLink(link)) + return false; + } + } + + *module = module_.forget(); + return true; + } + + void startFunctionBodies() { + module_->startFunctionBodies(); + } + void finishFunctionBodies() { + // When an interrupt is triggered, all function code is mprotected and, + // for sanity, stub code (particularly the interrupt stub) is not. + // Protection works at page granularity, so we need to ensure that no + // stub code gets into the function code pages. + MOZ_ASSERT(!finishedFunctionBodies_); + masm().haltingAlign(AsmJSPageSize); + module_->finishFunctionBodies(masm().currentOffset()); + finishedFunctionBodies_ = true; + } + + void buildCompilationTimeReport(JS::AsmJSCacheResult cacheResult, ScopedJSFreePtr<char>* out) { + ScopedJSFreePtr<char> slowFuns; +#ifndef JS_MORE_DETERMINISTIC + int64_t usecAfter = PRMJ_Now(); + int msTotal = (usecAfter - compileData_->usecBefore()) / PRMJ_USEC_PER_MSEC; + ModuleCompileResults::SlowFunctionVector& slowFunctions = compileData_->slowFunctions(); + if (!slowFunctions.empty()) { + slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions.length())); + if (!slowFuns) + return; + for (unsigned i = 0; i < slowFunctions.length(); i++) { + ModuleCompileResults::SlowFunction& func = slowFunctions[i]; + JSAutoByteString name; + if (!AtomToPrintableString(cx_, func.name, &name)) + return; + slowFuns.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowFuns.get(), + name.ptr(), func.line, func.column, func.ms, + i+1 < slowFunctions.length() ? ", " : "")); + if (!slowFuns) + return; + } + } + const char* cacheString = ""; + switch (cacheResult) { + case JS::AsmJSCache_Success: + cacheString = "stored in cache"; + break; + case JS::AsmJSCache_ModuleTooSmall: + cacheString = "not stored in cache (too small to benefit)"; + break; + case JS::AsmJSCache_SynchronousScript: + cacheString = "unable to cache asm.js in synchronous scripts; try loading " + "asm.js via <script async> or createElement('script')"; + break; + case JS::AsmJSCache_QuotaExceeded: + cacheString = "not enough temporary storage quota to store in cache"; + break; + case JS::AsmJSCache_StorageInitFailure: + cacheString = "storage initialization failed (consider filing a bug)"; + break; + case JS::AsmJSCache_Disabled_Internal: + cacheString = "caching disabled by internal configuration (consider filing a bug)"; + break; + case JS::AsmJSCache_Disabled_ShellFlags: + cacheString = "caching disabled by missing command-line arguments"; + break; + case JS::AsmJSCache_Disabled_JitInspector: + cacheString = "caching disabled by active JIT inspector"; + break; + case JS::AsmJSCache_InternalError: + cacheString = "unable to store in cache due to internal error (consider filing a bug)"; + break; + case JS::AsmJSCache_LIMIT: + MOZ_CRASH("bad AsmJSCacheResult"); + break; + } + out->reset(JS_smprintf("total compilation time %dms; %s%s", + msTotal, cacheString, slowFuns ? slowFuns.get() : "")); +#endif + } }; +} // namespace + // ModuleCompiler encapsulates the compilation of an entire asm.js module. Over // the course of an ModuleCompiler object's lifetime, many FunctionCompiler // objects will be created and destroyed in sequence, one for each function in // the module. // // *** asm.js FFI calls *** // // asm.js allows calling out to non-asm.js via "FFI calls". The asm.js type @@ -2440,123 +2617,70 @@ class MOZ_STACK_CLASS ModuleValidator // well. class MOZ_STACK_CLASS ModuleCompiler { public: // Map exitIndex to the corresponding exit's Signature. typedef HashMap<unsigned, const Signature*> ExitMap; private: - struct SlowFunction - { - SlowFunction(PropertyName* name, unsigned ms, unsigned line, unsigned column) - : name(name), ms(ms), line(line), column(column) - {} - - PropertyName* name; - unsigned ms; - unsigned line; - unsigned column; - }; - - typedef Vector<SlowFunction> SlowFunctionVector; - ExclusiveContext * cx_; AsmJSParser & parser_; - MacroAssembler masm_; + ModuleCompileResults * compileData_; ScopedJSDeletePtr<AsmJSModule>& module_; - ModuleGlobals & moduleGlobals_; - - ExitMap exits_; - - NonAssertingLabel stackOverflowLabel_; - NonAssertingLabel asyncInterruptLabel_; - NonAssertingLabel syncInterruptLabel_; - NonAssertingLabel onDetachedLabel_; - NonAssertingLabel onOutOfBoundsLabel_; - NonAssertingLabel onConversionErrorLabel_; - - int64_t usecBefore_; - SlowFunctionVector slowFunctions_; - - DebugOnly<bool> finishedFunctionBodies_; public: - ModuleCompiler(ExclusiveContext* cx, AsmJSParser& parser, ScopedJSDeletePtr<AsmJSModule>& module, - ModuleGlobals& moduleGlobals) + ModuleCompiler(ExclusiveContext* cx, ModuleCompileResults* compileData, AsmJSParser& parser, + ScopedJSDeletePtr<AsmJSModule>& module) : cx_(cx), parser_(parser), - masm_(MacroAssembler::AsmJSToken()), - module_(module), - moduleGlobals_(moduleGlobals), - exits_(cx), - usecBefore_(PRMJ_Now()), - slowFunctions_(cx), - finishedFunctionBodies_(false) + compileData_(compileData), + module_(module) {} - bool init() { - return exits_.init(); - } - /*************************************************** Read-only interface */ ExclusiveContext* cx() const { return cx_; } AsmJSParser& parser() const { return parser_; } TokenStream& tokenStream() const { return parser_.tokenStream; } - MacroAssembler& masm() { return masm_; } - Label& stackOverflowLabel() { return stackOverflowLabel_; } - Label& asyncInterruptLabel() { return asyncInterruptLabel_; } - Label& syncInterruptLabel() { return syncInterruptLabel_; } - Label& onDetachedLabel() { return onDetachedLabel_; } - Label& onOutOfBoundsLabel() { return onOutOfBoundsLabel_; } - Label& onConversionErrorLabel() { return onConversionErrorLabel_; } + MacroAssembler& masm() { return compileData_->masm(); } + Label& stackOverflowLabel() { return compileData_->stackOverflowLabel(); } + Label& asyncInterruptLabel() { return compileData_->asyncInterruptLabel(); } + Label& syncInterruptLabel() { return compileData_->syncInterruptLabel(); } + Label& onOutOfBoundsLabel() { return compileData_->onOutOfBoundsLabel(); } + Label& onConversionErrorLabel() { return compileData_->onConversionErrorLabel(); } + int64_t usecBefore() { return compileData_->usecBefore(); } const AsmJSModule& module() const { return *module_.get(); } bool usesSignalHandlersForInterrupt() const { return module_->usesSignalHandlersForInterrupt(); } bool usesSignalHandlersForOOB() const { return module_->usesSignalHandlersForOOB(); } uint32_t minHeapLength() const { return module_->minHeapLength(); } - ExitMap::Range allExits() const { - return exits_.all(); - } - ModuleGlobals::Func& function(unsigned i) const { - return *moduleGlobals_.function(i); - } - /***************************************************** Mutable interface */ - bool addExit(unsigned exitIndex, const Signature* signature) - { - ExitMap::AddPtr p = exits_.lookupForAdd(exitIndex); - if (p) - return true; - return exits_.add(p, exitIndex, signature); - } - - bool finishGeneratingFunction(ModuleGlobals::Func& func, CodeGenerator& codegen, + bool finishGeneratingFunction(Func& func, CodeGenerator& codegen, const AsmJSFunctionLabels& labels) { uint32_t line, column; tokenStream().srcCoords.lineNumAndColumnIndex(func.srcBegin(), &line, &column); if (!module_->addFunctionCodeRange(func.name(), line, labels)) return false; jit::IonScriptCounts* counts = codegen.extractScriptCounts(); if (counts && !module_->addFunctionCounts(counts)) { js_delete(counts); return false; } if (func.compileTime() >= 250) { - SlowFunction sf(func.name(), func.compileTime(), line, column); - if (!slowFunctions_.append(sf)) + ModuleCompileResults::SlowFunction sf(func.name(), func.compileTime(), line, column); + if (!compileData_->slowFunctions().append(Move(sf))) return false; } #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) unsigned begin = labels.begin.offset(); unsigned end = labels.end.offset(); if (!module_->addProfiledFunction(func.name(), begin, end, line, column)) return false; @@ -2570,160 +2694,18 @@ class MOZ_STACK_CLASS ModuleCompiler if (!module_->addProfiledBlocks(func.name(), begin, inlineEnd, end, ps.basicBlocks())) return false; } # endif #endif return true; } - void startFunctionBodies() { - module_->startFunctionBodies(); - } - void finishFunctionBodies() { - // When an interrupt is triggered, all function code is mprotected and, - // for sanity, stub code (particularly the interrupt stub) is not. - // Protection works at page granularity, so we need to ensure that no - // stub code gets into the function code pages. - MOZ_ASSERT(!finishedFunctionBodies_); - masm_.haltingAlign(AsmJSPageSize); - module_->finishFunctionBodies(masm_.currentOffset()); - finishedFunctionBodies_ = true; - } - - bool finishGeneratingEntry(unsigned exportIndex, Label* begin) { - MOZ_ASSERT(finishedFunctionBodies_); - module_->exportedFunction(exportIndex).initCodeOffset(begin->offset()); - uint32_t end = masm_.currentOffset(); - return module_->addCodeRange(AsmJSModule::CodeRange::Entry, begin->offset(), end); - } - bool finishGeneratingInterpExit(unsigned exitIndex, Label* begin, Label* profilingReturn) { - MOZ_ASSERT(finishedFunctionBodies_); - uint32_t beg = begin->offset(); - module_->exit(exitIndex).initInterpOffset(beg); - uint32_t pret = profilingReturn->offset(); - uint32_t end = masm_.currentOffset(); - return module_->addCodeRange(AsmJSModule::CodeRange::SlowFFI, beg, pret, end); - } - bool finishGeneratingJitExit(unsigned exitIndex, Label* begin, Label* profilingReturn) { - MOZ_ASSERT(finishedFunctionBodies_); - uint32_t beg = begin->offset(); - module_->exit(exitIndex).initJitOffset(beg); - uint32_t pret = profilingReturn->offset(); - uint32_t end = masm_.currentOffset(); - return module_->addCodeRange(AsmJSModule::CodeRange::JitFFI, beg, pret, end); - } - bool finishGeneratingInterrupt(Label* begin, Label* profilingReturn) { - MOZ_ASSERT(finishedFunctionBodies_); - uint32_t beg = begin->offset(); - uint32_t pret = profilingReturn->offset(); - uint32_t end = masm_.currentOffset(); - return module_->addCodeRange(AsmJSModule::CodeRange::Interrupt, beg, pret, end); - } - bool finishGeneratingInlineStub(Label* begin) { - MOZ_ASSERT(finishedFunctionBodies_); - uint32_t end = masm_.currentOffset(); - return module_->addCodeRange(AsmJSModule::CodeRange::Inline, begin->offset(), end); - } - bool finishGeneratingBuiltinThunk(AsmJSExit::BuiltinKind builtin, Label* begin, Label* pret) { - MOZ_ASSERT(finishedFunctionBodies_); - uint32_t end = masm_.currentOffset(); - return module_->addBuiltinThunkCodeRange(builtin, begin->offset(), pret->offset(), end); - } - - void buildCompilationTimeReport(JS::AsmJSCacheResult cacheResult, ScopedJSFreePtr<char>* out) { - ScopedJSFreePtr<char> slowFuns; -#ifndef JS_MORE_DETERMINISTIC - int64_t usecAfter = PRMJ_Now(); - int msTotal = (usecAfter - usecBefore_) / PRMJ_USEC_PER_MSEC; - if (!slowFunctions_.empty()) { - slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions_.length())); - if (!slowFuns) - return; - for (unsigned i = 0; i < slowFunctions_.length(); i++) { - SlowFunction& func = slowFunctions_[i]; - JSAutoByteString name; - if (!AtomToPrintableString(cx_, func.name, &name)) - return; - slowFuns.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowFuns.get(), - name.ptr(), func.line, func.column, func.ms, - i+1 < slowFunctions_.length() ? ", " : "")); - if (!slowFuns) - return; - } - } - const char* cacheString = ""; - switch (cacheResult) { - case JS::AsmJSCache_Success: - cacheString = "stored in cache"; - break; - case JS::AsmJSCache_ModuleTooSmall: - cacheString = "not stored in cache (too small to benefit)"; - break; - case JS::AsmJSCache_SynchronousScript: - cacheString = "unable to cache asm.js in synchronous scripts; try loading " - "asm.js via <script async> or createElement('script')"; - break; - case JS::AsmJSCache_QuotaExceeded: - cacheString = "not enough temporary storage quota to store in cache"; - break; - case JS::AsmJSCache_StorageInitFailure: - cacheString = "storage initialization failed (consider filing a bug)"; - break; - case JS::AsmJSCache_Disabled_Internal: - cacheString = "caching disabled by internal configuration (consider filing a bug)"; - break; - case JS::AsmJSCache_Disabled_ShellFlags: - cacheString = "caching disabled by missing command-line arguments"; - break; - case JS::AsmJSCache_Disabled_JitInspector: - cacheString = "caching disabled by active JIT inspector"; - break; - case JS::AsmJSCache_InternalError: - cacheString = "unable to store in cache due to internal error (consider filing a bug)"; - break; - case JS::AsmJSCache_LIMIT: - MOZ_CRASH("bad AsmJSCacheResult"); - break; - } - out->reset(JS_smprintf("total compilation time %dms; %s%s", - msTotal, cacheString, slowFuns ? slowFuns.get() : "")); -#endif - } - - bool finish(ScopedJSDeletePtr<AsmJSModule>* module) - { - masm_.finish(); - if (masm_.oom()) - return false; - - if (!module_->finish(cx_, tokenStream(), masm_, asyncInterruptLabel_, onOutOfBoundsLabel_)) - return false; - - // Finally, convert all the function-pointer table elements into - // RelativeLinks that will be patched by AsmJSModule::staticallyLink. - for (unsigned tableIndex = 0; tableIndex < moduleGlobals_.numFuncPtrTables(); tableIndex++) { - ModuleGlobals::FuncPtrTable& table = moduleGlobals_.funcPtrTable(tableIndex); - unsigned tableBaseOffset = module_->offsetOfGlobalData() + table.globalDataOffset(); - for (unsigned elemIndex = 0; elemIndex < table.numElems(); elemIndex++) { - AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::RawPointer); - link.patchAtOffset = tableBaseOffset + elemIndex * sizeof(uint8_t*); - link.targetOffset = masm_.actualOffset(table.elem(elemIndex).entry().offset()); - if (!module_->addRelativeLink(link)) - return false; - } - } - - *module = module_.forget(); - return true; - } }; -} /* anonymous namespace */ - /*****************************************************************************/ // Numeric literal utilities static bool IsNumericNonFloatLiteral(ParseNode* pn) { // Note: '-' is never rolled into the number; numbers are always positive // and negations must be applied manually. @@ -6396,19 +6378,19 @@ CheckSignatureAgainstExisting(ModuleVali } MOZ_ASSERT(sig == existing); return true; } static bool CheckFunctionSignature(ModuleValidator& m, ParseNode* usepn, Signature&& sig, PropertyName* name, - ModuleGlobals::Func** func) -{ - ModuleGlobals::Func* existing = m.lookupFunction(name); + Func** func) +{ + Func* existing = m.lookupFunction(name); if (!existing) { if (!CheckModuleLevelName(m, usepn, name)) return false; return m.addFunction(name, Move(sig), func); } if (!CheckSignatureAgainstExisting(m, usepn, sig, existing->sig())) return false; @@ -6449,17 +6431,17 @@ CheckInternalCall(FunctionBuilder& f, Pa size_t signatureAt = f.tempPtr(); // Call node position (asm.js specific) f.writeU32(callNode->pn_pos.begin); Signature signature(f.m().lifo(), retType); if (!CheckCallArgs(f, callNode, CheckIsVarType, signature)) return false; - ModuleGlobals::Func* callee; + Func* callee; if (!CheckFunctionSignature(f.m(), callNode, Move(signature), calleeName, &callee)) return false; f.patchPtr(entryAt, (uint8_t*) &callee->entry()); f.patchPtr(signatureAt, (uint8_t*) &callee->sig()); *type = retType.toType(); return true; } @@ -6479,23 +6461,23 @@ EmitInternalCall(FunctionCompiler& f, Re return false; return f.internalCall(sig, entry, call, def); } static bool CheckFuncPtrTableAgainstExisting(ModuleValidator& m, ParseNode* usepn, PropertyName* name, Signature&& sig, unsigned mask, - ModuleGlobals::FuncPtrTable** tableOut) + FuncPtrTable** tableOut) { if (const ModuleValidator::Global* existing = m.lookupGlobal(name)) { if (existing->which() != ModuleValidator::Global::FuncPtrTable) return m.failName(usepn, "'%s' is not a function-pointer table", name); - ModuleGlobals::FuncPtrTable& table = m.funcPtrTable(existing->funcPtrTableIndex()); + FuncPtrTable& table = m.funcPtrTable(existing->funcPtrTableIndex()); if (mask != table.mask()) return m.failf(usepn, "mask does not match previous value (%u)", table.mask()); if (!CheckSignatureAgainstExisting(m, usepn, sig, table.sig())) return false; *tableOut = &table; return true; @@ -6563,17 +6545,17 @@ CheckFuncPtrCall(FunctionBuilder& f, Par if (!indexType.isIntish()) return f.failf(indexNode, "%s is not a subtype of intish", indexType.toChars()); Signature sig(f.m().lifo(), retType); if (!CheckCallArgs(f, callNode, CheckIsVarType, sig)) return false; - ModuleGlobals::FuncPtrTable* table; + FuncPtrTable* table; if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, Move(sig), mask, &table)) return false; f.patch32(globalDataOffsetAt, table->globalDataOffset()); f.patchPtr(signatureAt, (uint8_t*) &table->sig()); *type = retType.toType(); return true; @@ -6666,19 +6648,16 @@ EmitFFICall(FunctionCompiler& f, RetType const Signature& sig = *reinterpret_cast<Signature*>(signaturePtr); MOZ_ASSERT_IF(sig.retType() != RetType::Void, sig.retType() == retType); uint32_t callNodePosition = f.readU32(); FunctionCompiler::Call call(f, callNodePosition); if (!EmitCallArgs(f, sig, &call)) return false; - if (!f.m().addExit(exitIndex, &sig)) - return false; - return f.ffiCall(exitIndex, call, retType.toMIRType(), def); } static bool CheckFloatCoercionArg(FunctionBuilder& f, ParseNode* inputNode, Type inputType, size_t opcodeAt) { if (inputType.isMaybeDouble()) { @@ -10366,17 +10345,17 @@ EmitMIR(ModuleCompiler& m, const AsmFunc jit::SpewBeginFunction(mir, nullptr); f.checkPostconditions(); return mir; } static bool -CheckFunction(ModuleValidator& m, LifoAlloc& lifo, ModuleGlobals::Func** funcOut) +CheckFunction(ModuleValidator& m, LifoAlloc& lifo, Func** funcOut) { int64_t before = PRMJ_Now(); // asm.js modules can be quite large when represented as parse trees so pop // the backing LifoAlloc after parsing/compiling each function. AsmJSParser::Mark mark = m.parser().mark(); ParseNode* fn = nullptr; // initialize to silence GCC warning @@ -10420,17 +10399,17 @@ CheckFunction(ModuleValidator& m, LifoAl if (!IsEmptyStatement(stmtIter)) lastNonEmptyStmt = stmtIter; } if (!CheckFinalReturn(f, lastNonEmptyStmt)) return false; Signature sig(Move(argTypes), f.returnedType()); - ModuleGlobals::Func* func = nullptr; + Func* func = nullptr; if (!CheckFunctionSignature(m, fn, Move(sig), FunctionName(fn), &func)) return false; if (func->defined()) return m.failName(fn, "function '%s' already defined", FunctionName(fn)); asmFunc->setNumLocals(f.numLocals()); func->define(fn, asmFunc); @@ -10439,31 +10418,31 @@ CheckFunction(ModuleValidator& m, LifoAl m.parser().release(mark); *funcOut = func; return true; } static bool -GenerateMIR(ModuleCompiler& mc, LifoAlloc& lifo, ModuleGlobals::Func* func, +GenerateMIR(ModuleCompiler& mc, LifoAlloc& lifo, Func* func, MIRGenerator** mir) { int64_t before = PRMJ_Now(); *mir = EmitMIR(mc, *func->bytecode(), lifo, func->sig().args()); if (!*mir) return false; func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC); return true; } static bool -GenerateCode(ModuleCompiler& m, ModuleGlobals::Func& func, MIRGenerator& mir, LIRGraph& lir) +GenerateCode(ModuleCompiler& m, Func& func, MIRGenerator& mir, LIRGraph& lir) { int64_t before = PRMJ_Now(); // A single MacroAssembler is reused for all function compilations so // that there is a single linear code segment for each module. To avoid // spiking memory, a LifoAllocScope in the caller frees all MIR/LIR // after each function is compiled. This method is responsible for cleaning // out any dangling pointers that the MacroAssembler may have kept. @@ -10488,17 +10467,17 @@ GenerateCode(ModuleCompiler& m, ModuleGl // CodeGenerator so we can destroy it now (via ScopedJSDeletePtr). return true; } static bool CheckAllFunctionsDefined(ModuleValidator& m) { for (unsigned i = 0; i < m.numFunctions(); i++) { - if (!m.function(i).entry().bound()) + if (!m.function(i).defined()) return m.failName(nullptr, "missing definition of function %s", m.function(i).name()); } return true; } static bool CheckFunctionsSequential(ModuleValidator& m, ModuleCompiler& mc) @@ -10512,17 +10491,17 @@ CheckFunctionsSequential(ModuleValidator TokenKind tk; if (!PeekToken(m.parser(), &tk)) return false; if (tk != TOK_FUNCTION) break; LifoAllocScope scope(&lifo); - ModuleGlobals::Func* func; + Func* func; if (!CheckFunction(m, lifo, &func)) return false; // In the case of the change-heap function, no function is produced. if (!func) continue; MIRGenerator* mir; @@ -10625,17 +10604,17 @@ GetFinishedCompilation(ModuleCompiler& m static bool GetUsedTask(ModuleCompiler& m, ParallelGroupState& group, AsmJSParallelTask** outTask) { // Block until a used LifoAlloc becomes available. AsmJSParallelTask* task = GetFinishedCompilation(m, group); if (!task) return false; - ModuleGlobals::Func& func = *reinterpret_cast<ModuleGlobals::Func*>(task->func); + Func& func = *reinterpret_cast<Func*>(task->func); func.accumulateCompileTime(task->compileTime); { // Perform code generation on the main thread. JitContext jitContext(m.cx(), &task->mir->alloc()); if (!GenerateCode(m, func, *task->mir, *task->lir)) return false; } @@ -10681,17 +10660,17 @@ CheckFunctionsParallel(ModuleValidator& if (!PeekToken(m.parser(), &tk)) return false; if (tk != TOK_FUNCTION) break; if (!task && !GetUnusedTask(group, i, &task) && !GetUsedTask(mc, group, &task)) return false; - ModuleGlobals::Func* func; + Func* func; if (!CheckFunction(m, task->lifo, &func)) return false; // In the case of the change-heap function, no function is produced. if (!func) continue; // Generate MIR into the LifoAlloc on the main thread. @@ -10802,17 +10781,17 @@ CheckFunctions(ModuleValidator& m, Modul // With compilation memory in-scope, dispatch helper threads. ParallelGroupState group(tasks); if (!CheckFunctionsParallel(m, mc, group)) { CancelOutstandingJobs(group); // If failure was triggered by a helper thread, report error. if (void* maybeFunc = HelperThreadState().maybeAsmJSFailedFunction()) { - ModuleGlobals::Func* func = reinterpret_cast<ModuleGlobals::Func*>(maybeFunc); + Func* func = reinterpret_cast<Func*>(maybeFunc); return m.failOffset(func->srcBegin(), "allocation failure during compilation"); } // Otherwise, the error occurred on the main thread and was already reported. return false; } return true; } @@ -10829,25 +10808,25 @@ CheckFuncPtrTable(ModuleValidator& m, Pa unsigned length = ListLength(arrayLiteral); if (!IsPowerOfTwo(length)) return m.failf(arrayLiteral, "function-pointer table length must be a power of 2 (is %u)", length); unsigned mask = length - 1; - ModuleGlobals::FuncPtrVector elems(m.cx()); + FuncPtrVector elems(m.cx()); const Signature* firstSig = nullptr; for (ParseNode* elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) { if (!elem->isKind(PNK_NAME)) return m.fail(elem, "function-pointer table's elements must be names of functions"); PropertyName* funcName = elem->name(); - const ModuleGlobals::Func* func = m.lookupFunction(funcName); + const Func* func = m.lookupFunction(funcName); if (!func) return m.fail(elem, "function-pointer table's elements must be names of functions"); if (firstSig) { if (*firstSig != func->sig()) return m.fail(elem, "all functions in table must have same signature"); } else { firstSig = &func->sig(); @@ -10856,17 +10835,17 @@ CheckFuncPtrTable(ModuleValidator& m, Pa if (!elems.append(func)) return false; } Signature sig(m.lifo()); if (!sig.copy(*firstSig)) return false; - ModuleGlobals::FuncPtrTable* table; + FuncPtrTable* table; if (!CheckFuncPtrTableAgainstExisting(m, var, var->name(), Move(sig), mask, &table)) return false; table->initElems(Move(elems)); return true; } static bool @@ -11026,17 +11005,17 @@ static const unsigned FramePushedAfterSa static const unsigned FramePushedAfterSave = 0; #else static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t) + NonVolatileRegs.fpus().getPushSizeInBytes(); #endif static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void*); static bool -GenerateEntry(ModuleCompiler& m, unsigned exportIndex) +GenerateEntry(ModuleValidator& m, unsigned exportIndex) { MacroAssembler& masm = m.masm(); Label begin; masm.haltingAlign(CodeAlignment); masm.bind(&begin); // Save the return address if it wasn't already saved by the call insn. @@ -11090,17 +11069,17 @@ GenerateEntry(ModuleCompiler& m, unsigne // Dynamically align the stack since ABIStackAlignment is not necessarily // AsmJSStackAlignment. We'll use entrySP to recover the original stack // pointer on return. masm.andToStackPtr(Imm32(~(AsmJSStackAlignment - 1))); // Bump the stack for the call. uint32_t funcIndex = m.module().exportedFunction(exportIndex).funcIndex(); - const ModuleGlobals::Func& func = m.function(funcIndex); + const Func& func = m.function(funcIndex); masm.reserveStack(AlignBytes(StackArgBytes(func.sig().args()), AsmJSStackAlignment)); // Copy parameters out of argv and into the registers/stack-slots specified by // the system ABI. for (ABIArgTypeIter iter(func.sig().args()); !iter.done(); iter++) { unsigned argOffset = iter.index() * sizeof(AsmJSModule::EntryArg); Address src(argv, argOffset); MIRType type = iter.mirType(); @@ -11203,17 +11182,17 @@ GenerateEntry(ModuleCompiler& m, unsigne masm.move32(Imm32(true), ReturnReg); masm.ret(); return !masm.oom() && m.finishGeneratingEntry(exportIndex, &begin); } static void -FillArgumentArray(ModuleCompiler& m, const VarTypeVector& argTypes, +FillArgumentArray(ModuleValidator& m, const VarTypeVector& argTypes, unsigned offsetToArgs, unsigned offsetToCallerStackArgs, Register scratch) { MacroAssembler& masm = m.masm(); for (ABIArgTypeIter i(argTypes); !i.done(); i++) { Address dstAddr(masm.getStackPointer(), offsetToArgs + i.index() * sizeof(Value)); switch (i->kind()) { @@ -11245,17 +11224,17 @@ FillArgumentArray(ModuleCompiler& m, con } } // If an FFI detaches its heap (viz., via ArrayBuffer.transfer), it must // call change-heap to another heap (viz., the new heap returned by transfer) // before returning to asm.js code. If the application fails to do this (if the // heap pointer is null), jump to a stub. static void -GenerateCheckForHeapDetachment(ModuleCompiler& m, Register scratch) +GenerateCheckForHeapDetachment(ModuleValidator& m, Register scratch) { if (!m.module().hasArrayView()) return; MacroAssembler& masm = m.masm(); MOZ_ASSERT(int(masm.framePushed()) >= int(ShadowStackSpace)); AssertStackAlignment(masm, ABIStackAlignment); #if defined(JS_CODEGEN_X86) @@ -11263,17 +11242,17 @@ GenerateCheckForHeapDetachment(ModuleCom masm.append(AsmJSGlobalAccess(label, AsmJSHeapGlobalDataOffset)); masm.branchTestPtr(Assembler::Zero, scratch, scratch, &m.onDetachedLabel()); #else masm.branchTestPtr(Assembler::Zero, HeapReg, HeapReg, &m.onDetachedLabel()); #endif } static bool -GenerateFFIInterpExit(ModuleCompiler& m, const Signature* sig, unsigned exitIndex, +GenerateFFIInterpExit(ModuleValidator& m, const Signature& sig, unsigned exitIndex, Label* throwLabel) { MacroAssembler& masm = m.masm(); MOZ_ASSERT(masm.framePushed() == 0); // Argument types for InvokeFromAsmJS_*: static const MIRType typeArray[] = { MIRType_Pointer, // exitDatum MIRType_Int32, // argc @@ -11282,39 +11261,39 @@ GenerateFFIInterpExit(ModuleCompiler& m, if (!invokeArgTypes.append(typeArray, ArrayLength(typeArray))) return false; // At the point of the call, the stack layout shall be (sp grows to the left): // | stack args | padding | Value argv[] | padding | retaddr | caller stack args | // The padding between stack args and argv ensures that argv is aligned. The // padding between argv and retaddr ensures that sp is aligned. unsigned offsetToArgv = AlignBytes(StackArgBytes(invokeArgTypes), sizeof(double)); - unsigned argvBytes = Max<size_t>(1, sig->args().length()) * sizeof(Value); + unsigned argvBytes = Max<size_t>(1, sig.args().length()) * sizeof(Value); unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, offsetToArgv + argvBytes); Label begin; GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::SlowFFI, &begin); // Fill the argument array. unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed(); Register scratch = ABIArgGenerator::NonArgReturnReg0; - FillArgumentArray(m, sig->args(), offsetToArgv, offsetToCallerStackArgs, scratch); + FillArgumentArray(m, sig.args(), offsetToArgv, offsetToCallerStackArgs, scratch); // Prepare the arguments for the call to InvokeFromAsmJS_*. ABIArgMIRTypeIter i(invokeArgTypes); // argument 0: exitIndex if (i->kind() == ABIArg::GPR) masm.mov(ImmWord(exitIndex), i->gpr()); else masm.store32(Imm32(exitIndex), Address(masm.getStackPointer(), i->offsetFromArgBase())); i++; // argument 1: argc - unsigned argc = sig->args().length(); + unsigned argc = sig.args().length(); if (i->kind() == ABIArg::GPR) masm.mov(ImmWord(argc), i->gpr()); else masm.store32(Imm32(argc), Address(masm.getStackPointer(), i->offsetFromArgBase())); i++; // argument 2: argv Address argv(masm.getStackPointer(), offsetToArgv); @@ -11324,17 +11303,17 @@ GenerateFFIInterpExit(ModuleCompiler& m, masm.computeEffectiveAddress(argv, scratch); masm.storePtr(scratch, Address(masm.getStackPointer(), i->offsetFromArgBase())); } i++; MOZ_ASSERT(i.done()); // Make the call, test whether it succeeded, and extract the return value. AssertStackAlignment(masm, ABIStackAlignment); - switch (sig->retType().which()) { + switch (sig.retType().which()) { case RetType::Void: masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_Ignore)); masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); break; case RetType::Signed: masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToInt32)); masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); masm.unboxInt32(argv, ReturnReg); @@ -11363,30 +11342,30 @@ GenerateFFIInterpExit(ModuleCompiler& m, #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) static const unsigned MaybeSavedGlobalReg = sizeof(void*); #else static const unsigned MaybeSavedGlobalReg = 0; #endif static bool -GenerateFFIIonExit(ModuleCompiler& m, const Signature* sig, unsigned exitIndex, +GenerateFFIIonExit(ModuleValidator& m, const Signature& sig, unsigned exitIndex, Label* throwLabel) { MacroAssembler& masm = m.masm(); MOZ_ASSERT(masm.framePushed() == 0); // Ion calls use the following stack layout (sp grows to the left): // | retaddr | descriptor | callee | argc | this | arg1..N | // After the Ion frame, the global register (if present) is saved since Ion // does not preserve non-volatile regs. Also, unlike most ABIs, Ion requires // that sp be JitStackAlignment-aligned *after* pushing the return address. static_assert(AsmJSStackAlignment >= JitStackAlignment, "subsumes"); unsigned sizeOfRetAddr = sizeof(void*); - unsigned ionFrameBytes = 3 * sizeof(void*) + (1 + sig->args().length()) * sizeof(Value); + unsigned ionFrameBytes = 3 * sizeof(void*) + (1 + sig.args().length()) * sizeof(Value); unsigned totalIonBytes = sizeOfRetAddr + ionFrameBytes + MaybeSavedGlobalReg; unsigned ionFramePushed = StackDecrementForCall(masm, JitStackAlignment, totalIonBytes) - sizeOfRetAddr; Label begin; GenerateAsmJSExitPrologue(masm, ionFramePushed, AsmJSExit::JitFFI, &begin); // 1. Descriptor @@ -11416,28 +11395,28 @@ GenerateFFIIonExit(ModuleCompiler& m, co masm.storePtr(callee, Address(masm.getStackPointer(), argOffset)); argOffset += sizeof(size_t); // 2.4. Load callee executable entry point masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee); masm.loadBaselineOrIonNoArgCheck(callee, callee, nullptr); // 3. Argc - unsigned argc = sig->args().length(); + unsigned argc = sig.args().length(); masm.storePtr(ImmWord(uintptr_t(argc)), Address(masm.getStackPointer(), argOffset)); argOffset += sizeof(size_t); // 4. |this| value masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), argOffset)); argOffset += sizeof(Value); // 5. Fill the arguments unsigned offsetToCallerStackArgs = ionFramePushed + sizeof(AsmJSFrame); - FillArgumentArray(m, sig->args(), argOffset, offsetToCallerStackArgs, scratch); - argOffset += sig->args().length() * sizeof(Value); + FillArgumentArray(m, sig.args(), argOffset, offsetToCallerStackArgs, scratch); + argOffset += sig.args().length() * sizeof(Value); MOZ_ASSERT(argOffset == ionFrameBytes); // 6. Jit code will clobber all registers, even non-volatiles. GlobalReg and // HeapReg are removed from the general register set for asm.js code, so // these will not have been saved by the caller like all other registers, // so they must be explicitly preserved. Only save GlobalReg since // HeapReg must be reloaded (from global data) after the call since the // heap may change during the FFI call. @@ -11569,17 +11548,17 @@ GenerateFFIIonExit(ModuleCompiler& m, co static_assert(ABIStackAlignment <= JitStackAlignment, "subsumes"); masm.reserveStack(sizeOfRetAddr); unsigned nativeFramePushed = masm.framePushed(); AssertStackAlignment(masm, ABIStackAlignment); masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel); Label oolConvert; - switch (sig->retType().which()) { + switch (sig.retType().which()) { case RetType::Void: break; case RetType::Signed: masm.convertValueToInt32(JSReturnOperand, ReturnDoubleReg, ReturnReg, &oolConvert, /* -0 check */ false); break; case RetType::Double: masm.convertValueToDouble(JSReturnOperand, ReturnDoubleReg, &oolConvert); @@ -11626,17 +11605,17 @@ GenerateFFIIonExit(ModuleCompiler& m, co masm.computeEffectiveAddress(argv, scratch); masm.storePtr(scratch, Address(masm.getStackPointer(), i->offsetFromArgBase())); } i++; MOZ_ASSERT(i.done()); // Call coercion function AssertStackAlignment(masm, ABIStackAlignment); - switch (sig->retType().which()) { + switch (sig.retType().which()) { case RetType::Signed: masm.call(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToInt32)); masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); masm.unboxInt32(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnReg); break; case RetType::Double: masm.call(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToNumber)); masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); @@ -11652,17 +11631,17 @@ GenerateFFIIonExit(ModuleCompiler& m, co MOZ_ASSERT(masm.framePushed() == 0); return !masm.oom() && m.finishGeneratingJitExit(exitIndex, &begin, &profilingReturn); } // See "asm.js FFI calls" comment above. static bool -GenerateFFIExits(ModuleCompiler& m, unsigned exitIndex, const Signature* signature, +GenerateFFIExits(ModuleValidator& m, unsigned exitIndex, const Signature& signature, Label* throwLabel) { // Generate the slow path through the interpreter if (!GenerateFFIInterpExit(m, signature, exitIndex, throwLabel)) return false; // Generate the fast path if (!GenerateFFIIonExit(m, signature, exitIndex, throwLabel)) @@ -11677,17 +11656,17 @@ GenerateFFIExits(ModuleCompiler& m, unsi // pushes an AsmJSFrame on the stack, that means we must rebuild the stack // frame. Fortunately, these are low arity functions and everything is passed in // regs on everything but x86 anyhow. // // NB: Since this thunk is being injected at system ABI callsites, it must // preserve the argument registers (going in) and the return register // (coming out) and preserve non-volatile registers. static bool -GenerateBuiltinThunk(ModuleCompiler& m, AsmJSExit::BuiltinKind builtin) +GenerateBuiltinThunk(ModuleValidator& m, AsmJSExit::BuiltinKind builtin) { MacroAssembler& masm = m.masm(); MOZ_ASSERT(masm.framePushed() == 0); MIRTypeVector argTypes(m.cx()); if (!argTypes.reserve(5)) return false; @@ -11773,39 +11752,39 @@ GenerateBuiltinThunk(ModuleCompiler& m, masm.call(BuiltinToImmKind(builtin)); Label profilingReturn; GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::Builtin(builtin), &profilingReturn); return !masm.oom() && m.finishGeneratingBuiltinThunk(builtin, &begin, &profilingReturn); } static bool -GenerateStackOverflowExit(ModuleCompiler& m, Label* throwLabel) +GenerateStackOverflowExit(ModuleValidator& m, Label* throwLabel) { MacroAssembler& masm = m.masm(); GenerateAsmJSStackOverflowExit(masm, &m.stackOverflowLabel(), throwLabel); return !masm.oom() && m.finishGeneratingInlineStub(&m.stackOverflowLabel()); } static bool -GenerateOnDetachedLabelExit(ModuleCompiler& m, Label* throwLabel) +GenerateOnDetachedLabelExit(ModuleValidator& m, Label* throwLabel) { MacroAssembler& masm = m.masm(); masm.bind(&m.onDetachedLabel()); masm.assertStackAlignment(ABIStackAlignment); // For now, OnDetached always throws (see OnDetached comment). masm.call(AsmJSImmPtr(AsmJSImm_OnDetached)); masm.jump(throwLabel); return !masm.oom() && m.finishGeneratingInlineStub(&m.onDetachedLabel()); } static bool -GenerateExceptionLabelExit(ModuleCompiler& m, Label* throwLabel, Label* exit, AsmJSImmKind func) +GenerateExceptionLabelExit(ModuleValidator& m, Label* throwLabel, Label* exit, AsmJSImmKind func) { MacroAssembler& masm = m.masm(); masm.bind(exit); // sp can be anything at this point, so ensure it is aligned when calling // into C++. We unconditionally jump to throw so don't worry about restoring sp. masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1))); @@ -11826,17 +11805,17 @@ static const LiveRegisterSet AllRegsExce // code. That means we must first save *all* registers and restore *all* // registers (except the stack pointer) when we resume. The address to resume to // (assuming that js::HandleExecutionInterrupt doesn't indicate that the // execution should be aborted) is stored in AsmJSActivation::resumePC_. // Unfortunately, loading this requires a scratch register which we don't have // after restoring all registers. To hack around this, push the resumePC on the // stack so that it can be popped directly into PC. static bool -GenerateAsyncInterruptExit(ModuleCompiler& m, Label* throwLabel) +GenerateAsyncInterruptExit(ModuleValidator& m, Label* throwLabel) { MacroAssembler& masm = m.masm(); masm.haltingAlign(CodeAlignment); masm.bind(&m.asyncInterruptLabel()); #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) // Be very careful here not to perturb the machine state before saving it // to the stack. In particular, add/sub instructions may set conditions in @@ -11979,17 +11958,17 @@ GenerateAsyncInterruptExit(ModuleCompile #else # error "Unknown architecture!" #endif return !masm.oom() && m.finishGeneratingInlineStub(&m.asyncInterruptLabel()); } static bool -GenerateSyncInterruptExit(ModuleCompiler& m, Label* throwLabel) +GenerateSyncInterruptExit(ModuleValidator& m, Label* throwLabel) { MacroAssembler& masm = m.masm(); masm.setFramePushed(0); unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, ShadowStackSpace); GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::Interrupt, &m.syncInterruptLabel()); @@ -12003,17 +11982,17 @@ GenerateSyncInterruptExit(ModuleCompiler } // If an exception is thrown, simply pop all frames (since asm.js does not // contain try/catch). To do this: // 1. Restore 'sp' to it's value right after the PushRegsInMask in GenerateEntry. // 2. PopRegsInMask to restore the caller's non-volatile registers. // 3. Return (to CallAsmJS). static bool -GenerateThrowStub(ModuleCompiler& m, Label* throwLabel) +GenerateThrowStub(ModuleValidator& m, Label* throwLabel) { MacroAssembler& masm = m.masm(); masm.haltingAlign(CodeAlignment); masm.bind(throwLabel); // We are about to pop all frames in this AsmJSActivation. Set fp to null to // maintain the invariant that fp is either null or pointing to a valid // frame. @@ -12029,29 +12008,29 @@ GenerateThrowStub(ModuleCompiler& m, Lab masm.mov(ImmWord(0), ReturnReg); masm.ret(); return !masm.oom() && m.finishGeneratingInlineStub(throwLabel); } static bool -GenerateStubs(ModuleCompiler& m) +GenerateStubs(ModuleValidator& m) { for (unsigned i = 0; i < m.module().numExportedFunctions(); i++) { if (m.module().exportedFunction(i).isChangeHeap()) continue; if (!GenerateEntry(m, i)) return false; } Label throwLabel; - for (ModuleCompiler::ExitMap::Range r = m.allExits(); !r.empty(); r.popFront()) { - if (!GenerateFFIExits(m, r.front().key(), r.front().value(), &throwLabel)) + for (ModuleValidator::ExitMap::Range r = m.allExits(); !r.empty(); r.popFront()) { + if (!GenerateFFIExits(m, r.front().value(), r.front().key().sig(), &throwLabel)) return false; } if (m.stackOverflowLabel().used() && !GenerateStackOverflowExit(m, &throwLabel)) return false; if (m.onDetachedLabel().used() && !GenerateOnDetachedLabelExit(m, &throwLabel)) return false; @@ -12073,17 +12052,17 @@ GenerateStubs(ModuleCompiler& m) if (!GenerateBuiltinThunk(m, AsmJSExit::BuiltinKind(i))) return false; } return true; } static bool -FinishModule(ModuleCompiler& m, +FinishModule(ModuleValidator& m, ScopedJSDeletePtr<AsmJSModule>* module) { LifoAlloc lifo(TempAllocator::PreferredLifoChunkSize); TempAllocator alloc(&lifo); JitContext jitContext(m.cx(), &alloc); m.masm().resetForNewCodeGenerator(alloc); @@ -12098,18 +12077,17 @@ CheckModule(ExclusiveContext* cx, AsmJSP ScopedJSDeletePtr<AsmJSModule>* moduleOut, ScopedJSFreePtr<char>* compilationTimeReport) { if (!LookupAsmJSModuleInCache(cx, parser, moduleOut, compilationTimeReport)) return false; if (*moduleOut) return true; - ModuleGlobals mg(cx); - ModuleValidator mv(cx, parser, mg); + ModuleValidator mv(cx, parser); if (!mv.init()) return false; if (PropertyName* moduleFunctionName = FunctionName(mv.moduleFunctionNode())) { if (!CheckModuleLevelName(mv, mv.moduleFunctionNode(), moduleFunctionName)) return false; mv.initModuleFunctionName(moduleFunctionName); } @@ -12129,26 +12107,28 @@ CheckModule(ExclusiveContext* cx, AsmJSP if (!CheckModuleGlobals(mv)) return false; #if !defined(ENABLE_SHARED_ARRAY_BUFFER) if (mv.module().hasArrayView() && mv.module().isSharedView()) return mv.fail(nullptr, "shared views not supported by this build"); #endif - ModuleCompiler mc(cx, parser, mv.modulePtr(), mg); - if (!mc.init()) - return false; - - mc.startFunctionBodies(); - - if (!CheckFunctions(mv, mc)) - return false; - - mc.finishFunctionBodies(); + mv.startFunctionBodies(); + + ModuleCompileResults mcd(cx); + { + ModuleCompiler mc(cx, &mcd, parser, mv.modulePtr()); + if (!CheckFunctions(mv, mc)) + return false; + } + + mv.takeCompileData(&mcd); + + mv.finishFunctionBodies(); if (!CheckFuncPtrTables(mv)) return false; if (!CheckModuleReturn(mv)) return false; TokenKind tk; @@ -12157,23 +12137,23 @@ CheckModule(ExclusiveContext* cx, AsmJSP if (tk != TOK_EOF && tk != TOK_RC) return mv.fail(nullptr, "top-level export (return) must be the last statement"); // Delay flushing until dynamic linking. The inhibited range is set by the // masm.executableCopy() called transitively by FinishModule. AutoFlushICache afc("CheckModule", /* inhibit = */ true); ScopedJSDeletePtr<AsmJSModule> module; - if (!FinishModule(mc, &module)) + if (!FinishModule(mv, &module)) return false; JS::AsmJSCacheResult cacheResult = StoreAsmJSModuleInCache(parser, *module, cx); module->staticallyLink(cx); - mc.buildCompilationTimeReport(cacheResult, compilationTimeReport); + mv.buildCompilationTimeReport(cacheResult, compilationTimeReport); *moduleOut = module.forget(); return true; } static bool Warn(AsmJSParser& parser, int errorNumber, const char* str) { parser.reportNoOffset(ParseWarning, /* strict = */ false, errorNumber, str ? str : "");
--- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -283,17 +283,17 @@ function isRootedGCPointerTypeName(name) if (name.startsWith('MaybeRooted<')) return /\(js::AllowGC\)1u>::RootType/.test(name); if (name == "ErrorResult" || name == "JSErrorResult" || name == "WrappableJSErrorResult" || name == "frontend::TokenStream" || name == "frontend::TokenStream::Position" || - name == "ModuleCompiler") + name == "ModuleCompiler" || name == "ModuleValidator") { return true; } return name.startsWith('Rooted') || name.startsWith('PersistentRooted'); } function isRootedTypeName(name)