author | Douglas Crosher <dtc-moz@scieneer.com> |
Wed, 04 Sep 2013 11:57:24 +1000 | |
changeset 145468 | 7850adf40329e17565cab0a26e7f93e1f7ef3100 |
parent 145467 | 48952adc1a955e189aa7586a3242f3c4785ef564 |
child 145469 | 907989350527f6f73e5b107cce519453afd34006 |
push id | 33289 |
push user | ryanvm@gmail.com |
push date | Wed, 04 Sep 2013 17:07:57 +0000 |
treeherder | mozilla-inbound@cb07c1c976f0 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bbouvier |
bugs | 893363 |
milestone | 26.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
|
--- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -1486,25 +1486,27 @@ class MOZ_STACK_CLASS ModuleCompiler unsigned startCodeOffset = func.code()->offset(); return module_->trackProfiledFunction(func.name(), startCodeOffset, endCodeOffset); } #endif #ifdef JS_ION_PERF bool trackPerfProfiledFunction(const Func &func, unsigned endCodeOffset) { unsigned lineno = 0U, columnIndex = 0U; parser().tokenStream.srcCoords.lineNumAndColumnIndex(func.srcOffset(), &lineno, &columnIndex); - unsigned startCodeOffset = func.code()->offset(); - return module_->trackPerfProfiledFunction(func.name(), startCodeOffset, endCodeOffset, lineno, columnIndex); + return module_->trackPerfProfiledFunction(func.name(), startCodeOffset, endCodeOffset, + lineno, columnIndex); } bool trackPerfProfiledBlocks(AsmJSPerfSpewer &perfSpewer, const Func &func, unsigned endCodeOffset) { unsigned startCodeOffset = func.code()->offset(); - perfSpewer.noteBlocksOffsets(masm_); - return module_->trackPerfProfiledBlocks(func.name(), startCodeOffset, endCodeOffset, perfSpewer.basicBlocks()); + perfSpewer.noteBlocksOffsets(); + unsigned endInlineCodeOffset = perfSpewer.endInlineCode.offset(); + return module_->trackPerfProfiledBlocks(func.name(), startCodeOffset, endInlineCodeOffset, + endCodeOffset, perfSpewer.basicBlocks()); } #endif bool addFunctionCounts(IonScriptCounts *counts) { return module_->addFunctionCounts(counts); } void finishFunctionBodies() { JS_ASSERT(!finishedFunctionBodies_); @@ -1580,16 +1582,37 @@ class MOZ_STACK_CLASS ModuleCompiler masm_.processCodeLabels(code); JS_ASSERT(masm_.jumpRelocationTableBytes() == 0); JS_ASSERT(masm_.dataRelocationTableBytes() == 0); JS_ASSERT(masm_.preBarrierTableBytes() == 0); JS_ASSERT(!masm_.hasEnteredExitFrame()); // Patch everything that needs an absolute address: +#ifdef JS_ION_PERF + // Fix up the code offsets. Note the endCodeOffset should not be filtered through + // 'actualOffset' as it is generated using 'size()' rather than a label. + for (unsigned i = 0; i < module_->numPerfFunctions(); i++) { + AsmJSModule::ProfiledFunction &func = module_->perfProfiledFunction(i); + func.startCodeOffset = masm_.actualOffset(func.startCodeOffset); + } + + for (unsigned i = 0; i < module_->numPerfBlocksFunctions(); i++) { + AsmJSModule::ProfiledBlocksFunction &func = module_->perfProfiledBlocksFunction(i); + func.startCodeOffset = masm_.actualOffset(func.startCodeOffset); + func.endInlineCodeOffset = masm_.actualOffset(func.endInlineCodeOffset); + BasicBlocksVector &basicBlocks = func.blocks; + for (uint32_t i = 0; i < basicBlocks.length(); i++) { + Record &r = basicBlocks[i]; + r.startOffset = masm_.actualOffset(r.startOffset); + r.endOffset = masm_.actualOffset(r.endOffset); + } + } +#endif + // Exit points for (unsigned i = 0; i < module_->numExits(); i++) { module_->exitIndexToGlobalDatum(i).exit = module_->interpExitTrampoline(module_->exit(i)); module_->exitIndexToGlobalDatum(i).fun = NULL; } module_->setOperationCallbackExit(code + masm_.actualOffset(operationCallbackLabel_.offset())); // Function-pointer table entries
--- a/js/src/jit/AsmJSLink.cpp +++ b/js/src/jit/AsmJSLink.cpp @@ -12,19 +12,17 @@ #include "jscntxt.h" #include "jsmath.h" #include "jswrapper.h" #include "frontend/BytecodeCompiler.h" #include "jit/AsmJSModule.h" #include "jit/Ion.h" -#ifdef JS_ION_PERF -# include "jit/PerfSpewer.h" -#endif +#include "jit/PerfSpewer.h" #include "jsfuninlines.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" using namespace js; using namespace js::jit; @@ -496,81 +494,77 @@ SendFunctionsToVTune(JSContext *cx, AsmJ #ifdef JS_ION_PERF static bool SendFunctionsToPerf(JSContext *cx, AsmJSModule &module) { if (!PerfFuncEnabled()) return true; - AsmJSPerfSpewer perfSpewer; - - unsigned long base = (unsigned long) module.functionCode(); - + uintptr_t base = (uintptr_t) module.functionCode(); const char *filename = module.sourceDesc().scriptSource()->filename(); for (unsigned i = 0; i < module.numPerfFunctions(); i++) { const AsmJSModule::ProfiledFunction &func = module.perfProfiledFunction(i); - - unsigned long start = base + (unsigned long) func.startCodeOffset; - unsigned long end = base + (unsigned long) func.endCodeOffset; + uintptr_t start = base + (unsigned long) func.startCodeOffset; + uintptr_t end = base + (unsigned long) func.endCodeOffset; JS_ASSERT(end >= start); - - unsigned long size = (end - start); + size_t size = end - start; JSAutoByteString bytes; - const char *method_name = AtomToPrintableString(cx, func.name, &bytes); - if (!method_name) + const char *name = AtomToPrintableString(cx, func.name, &bytes); + if (!name) return false; - unsigned lineno = func.lineno; - unsigned columnIndex = func.columnIndex; - - perfSpewer.writeFunctionMap(start, size, filename, lineno, columnIndex, method_name); + writePerfSpewerAsmJSFunctionMap(start, size, filename, func.lineno, func.columnIndex, name); } return true; } static bool SendBlocksToPerf(JSContext *cx, AsmJSModule &module) { if (!PerfBlockEnabled()) return true; - AsmJSPerfSpewer spewer; unsigned long funcBaseAddress = (unsigned long) module.functionCode(); - const char *filename = module.sourceDesc().scriptSource()->filename(); for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) { const AsmJSModule::ProfiledBlocksFunction &func = module.perfProfiledBlocksFunction(i); - unsigned long size = (unsigned long)func.endCodeOffset - (unsigned long)func.startCodeOffset; + size_t size = func.endCodeOffset - func.startCodeOffset; + JSAutoByteString bytes; - const char *method_name = AtomToPrintableString(cx, func.name, &bytes); - if (!method_name) + const char *name = AtomToPrintableString(cx, func.name, &bytes); + if (!name) return false; - spewer.writeBlocksMap(funcBaseAddress, func.startCodeOffset, size, filename, method_name, func.blocks); + writePerfSpewerAsmJSBlocksMap(funcBaseAddress, func.startCodeOffset, + func.endInlineCodeOffset, size, filename, name, func.blocks); } return true; } #endif static bool SendModuleToAttachedProfiler(JSContext *cx, AsmJSModule &module) { #if defined(MOZ_VTUNE) if (IsVTuneProfilingActive() && !SendFunctionsToVTune(cx, module)) return false; #endif #if defined(JS_ION_PERF) + if (module.numExportedFunctions() > 0) { + size_t firstEntryCode = (size_t) module.entryTrampoline(module.exportedFunction(0)); + writePerfSpewerAsmJSEntriesAndExits(firstEntryCode, (size_t) module.globalData() - firstEntryCode); + } if (!SendBlocksToPerf(cx, module)) return false; if (!SendFunctionsToPerf(cx, module)) return false; #endif return true; }
--- a/js/src/jit/AsmJSModule.h +++ b/js/src/jit/AsmJSModule.h @@ -11,19 +11,17 @@ #include "mozilla/PodOperations.h" #include "jsscript.h" #include "gc/Marking.h" #include "jit/AsmJS.h" #include "jit/IonMacroAssembler.h" -#if defined(JS_ION_PERF) -# include "jit/PerfSpewer.h" -#endif +#include "jit/PerfSpewer.h" #include "jit/RegisterSets.h" namespace js { // These EcmaScript-defined coercions form the basis of the asm.js type system. enum AsmJSCoercion { AsmJS_ToInt32, @@ -275,26 +273,30 @@ class AsmJSModule MarkStringUnbarriered(trc, &name, "asm.js profiled function name"); } }; #endif #if defined(JS_ION_PERF) struct ProfiledBlocksFunction : public ProfiledFunction { - jit::PerfSpewer::BasicBlocksVector blocks; + unsigned endInlineCodeOffset; + jit::BasicBlocksVector blocks; - ProfiledBlocksFunction(JSAtom *name, unsigned start, unsigned end, - jit::PerfSpewer::BasicBlocksVector &blocksVector) - : ProfiledFunction(name, start, end), blocks(mozilla::OldMove(blocksVector)) - { } + ProfiledBlocksFunction(JSAtom *name, unsigned start, unsigned endInline, unsigned end, + jit::BasicBlocksVector &blocksVector) + : ProfiledFunction(name, start, end), endInlineCodeOffset(endInline), + blocks(mozilla::OldMove(blocksVector)) + { + JS_ASSERT(name->isTenured()); + } ProfiledBlocksFunction(const ProfiledBlocksFunction ©) : ProfiledFunction(copy.name, copy.startCodeOffset, copy.endCodeOffset), - blocks(mozilla::OldMove(copy.blocks)) + endInlineCodeOffset(copy.endInlineCodeOffset), blocks(mozilla::OldMove(copy.blocks)) { } }; #endif private: typedef Vector<ExportedFunction, 0, SystemAllocPolicy> ExportedFunctionVector; typedef Vector<Global, 0, SystemAllocPolicy> GlobalVector; typedef Vector<Exit, 0, SystemAllocPolicy> ExitVector; @@ -471,42 +473,43 @@ class AsmJSModule #ifdef MOZ_VTUNE bool trackProfiledFunction(JSAtom *name, unsigned startCodeOffset, unsigned endCodeOffset) { ProfiledFunction func(name, startCodeOffset, endCodeOffset); return profiledFunctions_.append(func); } unsigned numProfiledFunctions() const { return profiledFunctions_.length(); } - const ProfiledFunction &profiledFunction(unsigned i) const { + ProfiledFunction &profiledFunction(unsigned i) { return profiledFunctions_[i]; } #endif #ifdef JS_ION_PERF bool trackPerfProfiledFunction(JSAtom *name, unsigned startCodeOffset, unsigned endCodeOffset, unsigned line, unsigned column) { ProfiledFunction func(name, startCodeOffset, endCodeOffset, line, column); return profiledFunctions_.append(func); } unsigned numPerfFunctions() const { return profiledFunctions_.length(); } - const ProfiledFunction &perfProfiledFunction(unsigned i) const { + ProfiledFunction &perfProfiledFunction(unsigned i) { return profiledFunctions_[i]; } - bool trackPerfProfiledBlocks(JSAtom *name, unsigned startCodeOffset, unsigned endCodeOffset, jit::PerfSpewer::BasicBlocksVector &basicBlocks) { - ProfiledBlocksFunction func(name, startCodeOffset, endCodeOffset, basicBlocks); + bool trackPerfProfiledBlocks(JSAtom *name, unsigned startCodeOffset, unsigned endInlineCodeOffset, + unsigned endCodeOffset, jit::BasicBlocksVector &basicBlocks) { + ProfiledBlocksFunction func(name, startCodeOffset, endInlineCodeOffset, endCodeOffset, basicBlocks); return perfProfiledBlocksFunctions_.append(func); } unsigned numPerfBlocksFunctions() const { return perfProfiledBlocksFunctions_.length(); } - const ProfiledBlocksFunction perfProfiledBlocksFunction(unsigned i) const { + ProfiledBlocksFunction &perfProfiledBlocksFunction(unsigned i) { return perfProfiledBlocksFunctions_[i]; } #endif bool hasArrayView() const { return pod.hasArrayView_; } unsigned numFFIs() const { return pod.numFFIs_;
--- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -7,16 +7,17 @@ #include "jit/BaselineCompiler.h" #include "jit/BaselineHelpers.h" #include "jit/BaselineIC.h" #include "jit/BaselineJIT.h" #include "jit/FixedList.h" #include "jit/IonLinker.h" #include "jit/IonSpewer.h" +#include "jit/PerfSpewer.h" #include "jit/VMFunctions.h" #include "jsscriptinlines.h" #include "vm/Interpreter-inl.h" using namespace js; using namespace js::jit; @@ -160,16 +161,20 @@ BaselineCompiler::compile() baselineScript->setMethod(code); script->setBaselineScript(baselineScript); IonSpew(IonSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%d", (void *) script->baselineScript(), (void *) code->raw(), script->filename(), script->lineno); +#ifdef JS_ION_PERF + writePerfSpewerBaselineProfile(script, code); +#endif + JS_ASSERT(pcMappingIndexEntries.length() > 0); baselineScript->copyPCMappingIndexEntries(&pcMappingIndexEntries[0]); JS_ASSERT(pcEntries.length() > 0); baselineScript->copyPCMappingEntries(pcEntries); // Copy IC entries if (icEntries_.length())
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -10,16 +10,17 @@ #include "jslibmath.h" #include "builtin/Eval.h" #include "jit/BaselineCompiler.h" #include "jit/BaselineHelpers.h" #include "jit/BaselineJIT.h" #include "jit/IonLinker.h" #include "jit/IonSpewer.h" +#include "jit/PerfSpewer.h" #include "jit/VMFunctions.h" #include "jsboolinlines.h" #include "jsscriptinlines.h" #include "vm/Interpreter-inl.h" #include "vm/ScopeObject-inl.h" @@ -567,16 +568,20 @@ ICStubCompiler::getStubCode() newStubCode->togglePreBarriers(true); // Cache newly compiled stubcode. if (!ion->putStubCode(stubKey, newStubCode)) return NULL; JS_ASSERT(entersStubFrame_ == ICStub::CanMakeCalls(kind)); +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(newStubCode, "BaselineIC"); +#endif + return newStubCode; } bool ICStubCompiler::tailCallVM(const VMFunction &fun, MacroAssembler &masm) { IonCode *code = cx->runtime()->ionRuntime()->getVMWrapper(fun); if (!code)
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -4347,17 +4347,23 @@ IonCompartment::generateStringConcatStub masm.pop(temp2); masm.pop(temp1); masm.bind(&failure); masm.movePtr(ImmWord((void *)NULL), output); masm.ret(); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "StringConcatStub"); +#endif + + return code; } typedef bool (*CharCodeAtFn)(JSContext *, HandleString, int32_t, uint32_t *); static const VMFunction CharCodeAtInfo = FunctionInfo<CharCodeAtFn>(jit::CharCodeAt); bool CodeGenerator::visitCharCodeAt(LCharCodeAt *lir) { @@ -5438,16 +5444,20 @@ CodeGenerator::generateAsmJS() // Thus, there is nothing special to do in the prologue except (possibly) // bump the stack. if (!generatePrologue()) return false; if (!generateBody()) return false; if (!generateEpilogue()) return false; +#if defined(JS_ION_PERF) + // Note the end of the inline code and start of the OOL code. + gen->perfSpewer().noteEndInlineCode(masm); +#endif if (!generateOutOfLineCode()) return false; // The only remaining work needed to compile this function is to patch the // switch-statement jump tables (the entries of the table need the absolute // address of the cases). These table entries are accmulated as CodeLabels // in the MacroAssembler's codeLabels_ list and processed all at once at in // the "static-link" phase of module compilation. It is critical that there @@ -5505,16 +5515,20 @@ CodeGenerator::generate() if (!generatePrologue()) return false; if (!generateBody()) return false; if (!generateEpilogue()) return false; if (!generateInvalidateEpilogue()) return false; +#if defined(JS_ION_PERF) + // Note the end of the inline code and start of the OOL code. + perfSpewer_.noteEndInlineCode(masm); +#endif if (!generateOutOfLineCode()) return false; return !masm.oom(); } bool CodeGenerator::link() @@ -5604,18 +5618,20 @@ CodeGenerator::link() ionScript->setInvalidationEpilogueDataOffset(invalidateEpilogueData_.offset()); ionScript->setOsrPc(gen->info().osrPc()); ionScript->setOsrEntryOffset(getOsrEntryOffset()); ptrdiff_t real_invalidate = masm.actualOffset(invalidate_.offset()); ionScript->setInvalidationEpilogueOffset(real_invalidate); ionScript->setDeoptTable(deoptTable_); +#if defined(JS_ION_PERF) if (PerfEnabled()) perfSpewer_.writeProfile(script, code, masm); +#endif // for generating inline caches during the execution. if (runtimeData_.length()) ionScript->copyRuntimeData(&runtimeData_[0]); if (cacheList_.length()) ionScript->copyCacheEntries(&cacheList_[0], masm); // for marking during GC.
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -356,15 +356,17 @@ class CodeGenerator : public CodeGenerat bool emitStoreHoleCheck(Register elements, const LAllocation *index, LSnapshot *snapshot); bool emitAssertRangeI(Range *r, Register input); bool emitAssertRangeD(Range *r, FloatRegister input, FloatRegister temp); // Script counts created when compiling code with no associated JSScript. IonScriptCounts *unassociatedScriptCounts_; +#if defined(JS_ION_PERF) PerfSpewer perfSpewer_; +#endif }; } // namespace jit } // namespace js #endif /* jit_CodeGenerator_h */
--- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -403,16 +403,21 @@ IonCache::linkAndAttachStub(JSContext *c if (pc_) { IonSpew(IonSpew_InlineCaches, "Cache %p(%s:%d/%d) generated %s %s stub at %p", this, script_->filename(), script_->lineno, pc_ - script_->code, attachKind, CacheName(kind()), code->raw()); } else { IonSpew(IonSpew_InlineCaches, "Cache %p generated %s %s stub at %p", this, attachKind, CacheName(kind()), code->raw()); } + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "IonCache"); +#endif + return true; } void IonCache::updateBaseAddress(IonCode *code, MacroAssembler &masm) { fallbackLabel_.repoint(code, &masm); }
--- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -13,19 +13,17 @@ #include <stdarg.h> #include "jscntxt.h" #include "jscompartment.h" #include "jit/CompileInfo.h" #include "jit/IonAllocPolicy.h" #include "jit/IonCompartment.h" -#if defined(JS_ION_PERF) -# include "jit/PerfSpewer.h" -#endif +#include "jit/PerfSpewer.h" #include "jit/RegisterSets.h" namespace js { namespace jit { class MBasicBlock; class MIRGraph; class MStart;
--- a/js/src/jit/PerfSpewer.cpp +++ b/js/src/jit/PerfSpewer.cpp @@ -12,28 +12,69 @@ #include "jit/IonSpewer.h" #include "jit/LinearScan.h" #include "jit/LIR.h" #include "jit/MIR.h" #include "jit/MIRGraph.h" #include "jit/RangeAnalysis.h" +// perf expects its data to be in a file /tmp/perf-PID.map, but for Android +// and B2G the map files are written to /data/local/tmp/perf-PID.map +// +// Except that Android 4.3 no longer allows the browser to write to /data/local/tmp/ +// so also try /sdcard/. + +#ifndef PERF_SPEW_DIR +# if defined(__ANDROID__) +# define PERF_SPEW_DIR "/data/local/tmp/" +# define PERF_SPEW_DIR_2 "/sdcard/" +# else +# define PERF_SPEW_DIR "/tmp/" +# endif +#endif + using namespace js; using namespace js::jit; #define PERF_MODE_NONE 1 #define PERF_MODE_FUNC 2 #define PERF_MODE_BLOCK 3 #ifdef JS_ION_PERF + static uint32_t PerfMode = 0; static bool PerfChecked = false; +static FILE *PerfFilePtr = NULL; + +#ifdef JS_THREADSAFE +# include "jslock.h" +static PRLock *PerfMutex; +#endif + +static bool +openPerfMap(const char *dir) +{ + const ssize_t bufferSize = 256; + char filenameBuffer[bufferSize]; + + if (snprintf(filenameBuffer, bufferSize, "%sperf-%d.map", dir, getpid()) >= bufferSize) + return false; + + JS_ASSERT(!PerfFilePtr); + PerfFilePtr = fopen(filenameBuffer, "a"); + + if (!PerfFilePtr) + return false; + + return true; +} + void js::jit::CheckPerf() { if (!PerfChecked) { const char *env = getenv("IONPERF"); if (env == NULL) { PerfMode = PERF_MODE_NONE; fprintf(stderr, "Warning: JIT perf reporting requires IONPERF set to \"block\" or \"func\". "); fprintf(stderr, "Perf mapping will be deactivated.\n"); @@ -46,70 +87,85 @@ js::jit::CheckPerf() { } else { fprintf(stderr, "Use IONPERF=func to record at function granularity\n"); fprintf(stderr, "Use IONPERF=block to record at basic block granularity\n"); fprintf(stderr, "\n"); fprintf(stderr, "Be advised that using IONPERF will cause all scripts\n"); fprintf(stderr, "to be leaked.\n"); exit(0); } + + if (PerfMode != PERF_MODE_NONE) { +#ifdef JS_THREADSAFE + PerfMutex = PR_NewLock(); + if (!PerfMutex) + MOZ_CRASH(); +#endif + + if (openPerfMap(PERF_SPEW_DIR)) { + PerfChecked = true; + return; + } + +#if defined(__ANDROID__) + if (openPerfMap(PERF_SPEW_DIR_2)) { + PerfChecked = true; + return; + } +#endif + fprintf(stderr, "Failed to open perf map file. Disabling IONPERF.\n"); + PerfMode = PERF_MODE_NONE; + } PerfChecked = true; } } bool js::jit::PerfBlockEnabled() { JS_ASSERT(PerfMode); return PerfMode == PERF_MODE_BLOCK; } bool js::jit::PerfFuncEnabled() { JS_ASSERT(PerfMode); return PerfMode == PERF_MODE_FUNC; } -#endif - -uint32_t PerfSpewer::nextFunctionIndex = 0; - -PerfSpewer::PerfSpewer() - : fp_(NULL) +static bool +lockPerfMap(void) { if (!PerfEnabled()) - return; + return false; -# if defined(__linux__) - // perf expects its data to be in a file /tmp/perf-PID.map - const ssize_t bufferSize = 256; - char filenameBuffer[bufferSize]; - if (snprintf(filenameBuffer, bufferSize, - "/tmp/perf-%d.map", - getpid()) >= bufferSize) - return; +#ifdef JS_THREADSAFE + PR_Lock(PerfMutex); +#endif - fp_ = fopen(filenameBuffer, "a"); - if (!fp_) - return; -# else - fprintf(stderr, "Warning: PerfEnabled, but not running on linux\n"); -# endif + JS_ASSERT(PerfFilePtr); + return true; } -PerfSpewer::~PerfSpewer() +static void +unlockPerfMap() { - if (fp_) - fclose(fp_); + JS_ASSERT(PerfFilePtr); + fflush(PerfFilePtr); +#ifdef JS_THREADSAFE + PR_Unlock(PerfMutex); +#endif } +uint32_t PerfSpewer::nextFunctionIndex = 0; + bool PerfSpewer::startBasicBlock(MBasicBlock *blk, MacroAssembler &masm) { - if (!PerfBlockEnabled() || !fp_) + if (!PerfBlockEnabled()) return true; const char *filename = blk->info().script()->filename(); unsigned lineNumber, columnNumber; if (blk->pc()) { lineNumber = PCToLineNumber(blk->info().script(), blk->pc(), &columnNumber); @@ -120,176 +176,268 @@ PerfSpewer::startBasicBlock(MBasicBlock Record r(filename, lineNumber, columnNumber, blk->id()); masm.bind(&r.start); return basicBlocks_.append(r); } bool PerfSpewer::endBasicBlock(MacroAssembler &masm) { - if (!PerfBlockEnabled() || !fp_) + if (!PerfBlockEnabled()) return true; masm.bind(&basicBlocks_[basicBlocks_.length() - 1].end); return true; } +bool +PerfSpewer::noteEndInlineCode(MacroAssembler &masm) +{ + if (!PerfBlockEnabled()) + return true; + + masm.bind(&endInlineCode); + return true; +} + void PerfSpewer::writeProfile(JSScript *script, IonCode *code, MacroAssembler &masm) { - if (!fp_) - return; - - uint32_t thisFunctionIndex = nextFunctionIndex++; + if (PerfFuncEnabled()) { + if (!lockPerfMap()) + return; - if (PerfFuncEnabled()) { - unsigned long size = (unsigned long) code->instructionsSize(); + uint32_t thisFunctionIndex = nextFunctionIndex++; + + size_t size = code->instructionsSize(); if (size > 0) { - fprintf(fp_, - "%lx %lx %s:%d: Func%02d\n", + fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d\n", reinterpret_cast<uintptr_t>(code->raw()), size, script->filename(), script->lineno, thisFunctionIndex); } - } else if (PerfBlockEnabled()) { + unlockPerfMap(); + return; + } + + if (PerfBlockEnabled() && basicBlocks_.length() > 0) { + if (!lockPerfMap()) + return; + + uint32_t thisFunctionIndex = nextFunctionIndex++; uintptr_t funcStart = uintptr_t(code->raw()); + uintptr_t funcEndInlineCode = funcStart + masm.actualOffset(endInlineCode.offset()); uintptr_t funcEnd = funcStart + code->instructionsSize(); - uintptr_t cur = funcStart; + // function begins with the prologue, which is located before the first basic block + size_t prologueSize = masm.actualOffset(basicBlocks_[0].start.offset()); + + if (prologueSize > 0) { + fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Prologue\n", + funcStart, prologueSize, script->filename(), script->lineno, thisFunctionIndex); + } + + uintptr_t cur = funcStart + prologueSize; for (uint32_t i = 0; i < basicBlocks_.length(); i++) { Record &r = basicBlocks_[i]; uintptr_t blockStart = funcStart + masm.actualOffset(r.start.offset()); uintptr_t blockEnd = funcStart + masm.actualOffset(r.end.offset()); JS_ASSERT(cur <= blockStart); if (cur < blockStart) { - fprintf(fp_, - "%lx %lx %s:%d: Func%02d-Block?\n", - static_cast<unsigned long>(cur), - static_cast<unsigned long>(blockStart - cur), + fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Block?\n", + static_cast<uintptr_t>(cur), + static_cast<uintptr_t>(blockStart - cur), script->filename(), script->lineno, thisFunctionIndex); } cur = blockEnd; - unsigned long size = blockEnd - blockStart; + size_t size = blockEnd - blockStart; if (size > 0) { - fprintf(fp_, - "%lx %lx %s:%d:%d: Func%02d-Block%d\n", - static_cast<unsigned long>(blockStart), size, + fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Func%02d-Block%d\n", + static_cast<uintptr_t>(blockStart), size, r.filename, r.lineNumber, r.columnNumber, thisFunctionIndex, r.id); } } - // Any stuff after the basic blocks is presumably OOL code, - // which I do not currently categorize. - JS_ASSERT(cur <= funcEnd); - if (cur < funcEnd) { - fprintf(fp_, - "%lx %lx %s:%d: Func%02d-OOL\n", - static_cast<unsigned long>(cur), - static_cast<unsigned long>(funcEnd - cur), + JS_ASSERT(cur <= funcEndInlineCode); + if (cur < funcEndInlineCode) { + fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Epilogue\n", + cur, funcEndInlineCode - cur, + script->filename(), script->lineno, + thisFunctionIndex); + } + + JS_ASSERT(funcEndInlineCode <= funcEnd); + if (funcEndInlineCode < funcEnd) { + fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-OOL\n", + funcEndInlineCode, funcEnd - funcEndInlineCode, script->filename(), script->lineno, thisFunctionIndex); } + + unlockPerfMap(); + return; } } -#if defined(JS_ION_PERF) void -AsmJSPerfSpewer::writeFunctionMap(unsigned long base, unsigned long size, const char *filename, unsigned lineno, unsigned colIndex, const char *funcName) +js::jit::writePerfSpewerBaselineProfile(JSScript *script, IonCode *code) { - if (!fp_ || !PerfFuncEnabled() || size == 0U) + if (!PerfEnabled()) + return; + + if (!lockPerfMap()) + return; + + size_t size = code->instructionsSize(); + if (size > 0) { + fprintf(PerfFilePtr, "%zx %zx %s:%d: Baseline\n", + reinterpret_cast<uintptr_t>(code->raw()), + size, script->filename(), script->lineno); + } + + unlockPerfMap(); +} + +void +js::jit::writePerfSpewerIonCodeProfile(IonCode *code, const char *msg) +{ + if (!code || !PerfEnabled()) return; - fprintf(fp_, - "%lx %lx %s:%d:%d: Function %s\n", - base, size, - filename, lineno, colIndex, funcName); + if (!lockPerfMap()) + return; + + size_t size = code->instructionsSize(); + if (size > 0) { + fprintf(PerfFilePtr, "%zx %zx %s (%p 0x%zx)\n", + reinterpret_cast<uintptr_t>(code->raw()), + size, msg, code->raw(), size); + } + + unlockPerfMap(); +} + +void +js::jit::writePerfSpewerAsmJSFunctionMap(uintptr_t base, uintptr_t size, + const char *filename, unsigned lineno, unsigned colIndex, + const char *funcName) +{ + if (!PerfFuncEnabled() || size == 0U) + return; + + if (!lockPerfMap()) + return; + + fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Function %s\n", base, size, filename, lineno, colIndex, funcName); + + unlockPerfMap(); } bool AsmJSPerfSpewer::startBasicBlock(MBasicBlock *blk, MacroAssembler &masm) { - if (!PerfBlockEnabled() || !fp_) + if (!PerfBlockEnabled()) return true; Record r("", blk->lineno(), blk->columnIndex(), blk->id()); // filename is retrieved later masm.bind(&r.start); return basicBlocks_.append(r); } void -AsmJSPerfSpewer::noteBlocksOffsets(MacroAssembler &masm) +AsmJSPerfSpewer::noteBlocksOffsets() { - if (!PerfBlockEnabled() || !fp_) + if (!PerfBlockEnabled()) return; for (uint32_t i = 0; i < basicBlocks_.length(); i++) { Record &r = basicBlocks_[i]; - r.startOffset = masm.actualOffset(r.start.offset()); - r.endOffset = masm.actualOffset(r.end.offset()); + r.startOffset = r.start.offset(); + r.endOffset = r.end.offset(); } } void -AsmJSPerfSpewer::writeBlocksMap(unsigned long baseAddress, unsigned long funcStartOffset, unsigned long funcSize, - const char *filename, const char *funcName, const BasicBlocksVector &basicBlocks) +js::jit::writePerfSpewerAsmJSBlocksMap(uintptr_t baseAddress, size_t funcStartOffset, + size_t funcEndInlineOffset, size_t funcSize, + const char *filename, const char *funcName, + const js::jit::BasicBlocksVector &basicBlocks) { - if (!fp_ || !PerfBlockEnabled() || basicBlocks.length() == 0) + if (!PerfBlockEnabled() || basicBlocks.length() == 0) + return; + + if (!lockPerfMap()) return; // function begins with the prologue, which is located before the first basic block - unsigned long prologueSize = basicBlocks[0].startOffset - funcStartOffset; + size_t prologueSize = basicBlocks[0].startOffset - funcStartOffset; + size_t cur = baseAddress + funcStartOffset + prologueSize; + size_t funcEndInlineCode = baseAddress + funcEndInlineOffset; + size_t funcEnd = baseAddress + funcStartOffset + funcSize; - unsigned long cur = baseAddress + funcStartOffset + prologueSize; - unsigned long funcEnd = baseAddress + funcStartOffset + funcSize - prologueSize; + if (prologueSize > 0) { + fprintf(PerfFilePtr, "%zx %zx %s: Function %s - Prologue\n", + baseAddress + funcStartOffset, prologueSize, filename, funcName); + } for (uint32_t i = 0; i < basicBlocks.length(); i++) { const Record &r = basicBlocks[i]; - unsigned long blockStart = baseAddress + (unsigned long) r.startOffset; - unsigned long blockEnd = baseAddress + (unsigned long) r.endOffset; - - if (i == basicBlocks.length() - 1) { - // for the last block, manually add the ret instruction - blockEnd += 1u; - } + size_t blockStart = baseAddress + r.startOffset; + size_t blockEnd = baseAddress + r.endOffset; JS_ASSERT(cur <= blockStart); if (cur < blockStart) { - fprintf(fp_, - "%lx %lx %s: Function %s - unknown block\n", + fprintf(PerfFilePtr, "%zx %zx %s: Function %s - unknown block\n", cur, blockStart - cur, filename, funcName); } cur = blockEnd; - unsigned long size = blockEnd - blockStart; + size_t size = blockEnd - blockStart; if (size > 0) { - fprintf(fp_, - "%lx %lx %s:%d:%d: Function %s - Block %d\n", + fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Function %s - Block %d\n", blockStart, size, filename, r.lineNumber, r.columnNumber, funcName, r.id); } } - // Any stuff after the basic blocks is presumably OOL code, - // which I do not currently categorize. - JS_ASSERT(cur <= funcEnd); - if (cur < funcEnd) { - fprintf(fp_, - "%lx %lx %s: Function %s - OOL\n", - cur, funcEnd - cur, - filename, - funcName); + JS_ASSERT(cur <= funcEndInlineCode); + if (cur < funcEndInlineCode) + fprintf(PerfFilePtr, "%zx %zx %s: Function %s - Epilogue\n", + cur, funcEndInlineCode - cur, filename, funcName); + + JS_ASSERT(funcEndInlineCode <= funcEnd); + if (funcEndInlineCode < funcEnd) { + fprintf(PerfFilePtr, "%zx %zx %s: Function %s - OOL\n", + funcEndInlineCode, funcEnd - funcEndInlineCode, filename, funcName); } + + unlockPerfMap(); } + +void +js::jit::writePerfSpewerAsmJSEntriesAndExits(uintptr_t base, size_t size) +{ + if (size == 0) + return; + + if (!lockPerfMap()) + return; + + fprintf(PerfFilePtr, "%zx %zx AsmJS Entries and Exits (0x%zx 0x%zx)\n", base, size, base, size); + + unlockPerfMap(); +} + #endif // defined (JS_ION_PERF)
--- a/js/src/jit/PerfSpewer.h +++ b/js/src/jit/PerfSpewer.h @@ -26,67 +26,77 @@ static inline bool PerfEnabled() { } #else static inline void CheckPerf() {} static inline bool PerfBlockEnabled() { return false; } static inline bool PerfFuncEnabled() { return false; } static inline bool PerfEnabled() { return false; } #endif +#ifdef JS_ION_PERF + +struct Record { + const char *filename; + unsigned lineNumber; + unsigned columnNumber; + uint32_t id; + Label start, end; + size_t startOffset, endOffset; + + Record(const char *filename, + unsigned lineNumber, + unsigned columnNumber, + uint32_t id) + : filename(filename), lineNumber(lineNumber), + columnNumber(columnNumber), id(id), + startOffset(0u), endOffset(0u) + {} +}; + +typedef Vector<Record, 1, SystemAllocPolicy> BasicBlocksVector; + class PerfSpewer { protected: static uint32_t nextFunctionIndex; - FILE *fp_; public: - struct Record { - const char *filename; - unsigned lineNumber; - unsigned columnNumber; - uint32_t id; - Label start, end; - unsigned startOffset, endOffset; + Label endInlineCode; - Record(const char *filename, - unsigned lineNumber, - unsigned columnNumber, - uint32_t id) - : filename(filename), lineNumber(lineNumber), - columnNumber(columnNumber), id(id), - startOffset(0u), endOffset(0u) - {} - }; - - typedef Vector<Record, 1, SystemAllocPolicy> BasicBlocksVector; protected: BasicBlocksVector basicBlocks_; public: - PerfSpewer(); - virtual ~PerfSpewer(); - virtual bool startBasicBlock(MBasicBlock *blk, MacroAssembler &masm); bool endBasicBlock(MacroAssembler &masm); - void writeProfile(JSScript *script, - IonCode *code, - MacroAssembler &masm); + bool noteEndInlineCode(MacroAssembler &masm); + + void writeProfile(JSScript *script, IonCode *code, MacroAssembler &masm); }; +void writePerfSpewerBaselineProfile(JSScript *script, IonCode *code); +void writePerfSpewerIonCodeProfile(IonCode *code, const char *msg); + class AsmJSPerfSpewer : public PerfSpewer { public: bool startBasicBlock(MBasicBlock *blk, MacroAssembler &masm); - void noteBlocksOffsets(MacroAssembler &masm); + void noteBlocksOffsets(); BasicBlocksVector &basicBlocks() { return basicBlocks_; } +}; + +void writePerfSpewerAsmJSFunctionMap(uintptr_t base, uintptr_t size, const char *filename, + unsigned lineno, unsigned colIndex, const char *funcName); - void writeBlocksMap(unsigned long baseAddress, unsigned long funcStartOffset, - unsigned long funcSize, const char *filename, const char *funcName, - const BasicBlocksVector &basicBlocks); - void writeFunctionMap(unsigned long base, unsigned long size, const char *filename, - unsigned lineno, unsigned colIndex, const char *funcName); -}; +void writePerfSpewerAsmJSBlocksMap(uintptr_t baseAddress, size_t funcStartOffset, + size_t funcStartOOLOffset, size_t funcSize, + const char *filename, const char *funcName, + const BasicBlocksVector &basicBlocks); + +void writePerfSpewerAsmJSEntriesAndExits(uintptr_t base, size_t size); + +#endif // JS_ION_PERF } // namespace jit } // namespace js #endif /* jit_PerfSpewer_h */
--- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -12,17 +12,16 @@ #include "jscompartment.h" #include "jsnum.h" #include "jit/CodeGenerator.h" #include "jit/IonCompartment.h" #include "jit/IonFrames.h" #include "jit/MIR.h" #include "jit/MIRGraph.h" -#include "jit/PerfSpewer.h" #include "vm/Shape.h" #include "jsscriptinlines.h" #include "jit/shared/CodeGenerator-shared-inl.h" #include "vm/Shape-inl.h" using namespace js;
--- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -9,16 +9,17 @@ #include "assembler/assembler/MacroAssembler.h" #include "jit/arm/BaselineHelpers-arm.h" #include "jit/Bailouts.h" #include "jit/ExecutionModeInlines.h" #include "jit/IonCompartment.h" #include "jit/IonFrames.h" #include "jit/IonLinker.h" #include "jit/IonSpewer.h" +#include "jit/PerfSpewer.h" #include "jit/VMFunctions.h" using namespace js; using namespace js::jit; static const FloatRegisterSet NonVolatileFloatRegs = FloatRegisterSet((1 << FloatRegisters::d8) | (1 << FloatRegisters::d9) | @@ -312,17 +313,23 @@ IonRuntime::generateEnterJIT(JSContext * // ASSERT(JSReturnReg_Type.code() == JSReturnReg_Data.code()+1); // aasm->as_extdtr(IsStore, 64, true, Offset, // JSReturnReg_Data, EDtrAddr(r5, EDtrOffImm(0))); // Restore non-volatile registers and return. GenerateReturn(masm, true); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "EnterJIT"); +#endif + + return code; } IonCode * IonRuntime::generateInvalidator(JSContext *cx) { // See large comment in x86's IonRuntime::generateInvalidator. MacroAssembler masm(cx); //masm.as_bkpt(); @@ -369,16 +376,21 @@ IonRuntime::generateInvalidator(JSContex // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2. IonCode *bailoutTail = cx->runtime()->ionRuntime()->getBailoutTail(); masm.branch(bailoutTail); Linker linker(masm); IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); IonSpew(IonSpew_Invalidate, " invalidation thunk created at %p", (void *) code->raw()); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "Invalidator"); +#endif + return code; } IonCode * IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut) { MacroAssembler masm(cx); // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame. @@ -471,16 +483,21 @@ IonRuntime::generateArgumentsRectifier(J masm.ret(); Linker linker(masm); IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); CodeOffsetLabel returnLabel(returnOffset); returnLabel.fixup(&masm); if (returnAddrOut) *returnAddrOut = (void *) (code->raw() + returnLabel.offset()); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "ArgumentsRectifier"); +#endif + return code; } static void GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass) { // the stack should look like: // [IonFrame] @@ -584,27 +601,39 @@ IonRuntime::generateBailoutTable(JSConte Label bailout; for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++) masm.ma_bl(&bailout); masm.bind(&bailout); GenerateBailoutThunk(cx, masm, frameClass); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "BailoutTable"); +#endif + + return code; } IonCode * IonRuntime::generateBailoutHandler(JSContext *cx) { MacroAssembler masm(cx); GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "BailoutHandler"); +#endif + + return code; } IonCode * IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) { typedef MoveResolver::MoveOperand MoveOperand; JS_ASSERT(functionWrappers_); @@ -772,16 +801,20 @@ IonRuntime::generateVMWrapper(JSContext if (!wrapper) return NULL; // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to // use relookupOrAdd instead of add. if (!functionWrappers_->relookupOrAdd(p, &f, wrapper)) return NULL; +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(wrapper, "VMWrapper"); +#endif + return wrapper; } IonCode * IonRuntime::generatePreBarrier(JSContext *cx, MIRType type) { MacroAssembler masm(cx); @@ -807,17 +840,23 @@ IonRuntime::generatePreBarrier(JSContext JS_ASSERT(type == MIRType_Shape); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon)); } masm.PopRegsInMask(save); masm.ret(); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "PreBarrier"); +#endif + + return code; } typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *); static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap); IonCode * IonRuntime::generateDebugTrapHandler(JSContext *cx) { @@ -856,32 +895,50 @@ IonRuntime::generateDebugTrapHandler(JSC masm.bind(&forcedReturn); masm.loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand); masm.mov(r11, sp); masm.pop(r11); masm.ret(); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *codeDbg = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(codeDbg, "DebugTrapHandler"); +#endif + + return codeDbg; } IonCode * IonRuntime::generateExceptionTailStub(JSContext *cx) { MacroAssembler masm; masm.handleFailureWithHandlerTail(); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "ExceptionTailStub"); +#endif + + return code; } IonCode * IonRuntime::generateBailoutTailStub(JSContext *cx) { MacroAssembler masm; masm.generateBailoutTail(r1, r2); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "BailoutTailStub"); +#endif + + return code; }
--- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -6,16 +6,17 @@ #include "assembler/assembler/MacroAssembler.h" #include "jit/Bailouts.h" #include "jit/ExecutionModeInlines.h" #include "jit/IonCompartment.h" #include "jit/IonFrames.h" #include "jit/IonLinker.h" #include "jit/IonSpewer.h" +#include "jit/PerfSpewer.h" #include "jit/VMFunctions.h" #include "jit/x64/BaselineHelpers-x64.h" using namespace js; using namespace js::jit; // All registers to save and restore. This includes the stack pointer, since we // use the ability to reference register values on the stack by index. @@ -266,17 +267,23 @@ IonRuntime::generateEnterJIT(JSContext * masm.pop(r12); masm.pop(rbx); // Restore frame pointer and return. masm.pop(rbp); masm.ret(); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "EnterJIT"); +#endif + + return code; } IonCode * IonRuntime::generateInvalidator(JSContext *cx) { AutoIonContextAlloc aica(cx); MacroAssembler masm(cx); @@ -309,17 +316,23 @@ IonRuntime::generateInvalidator(JSContex // Pop the machine state and the dead frame. masm.lea(Operand(rsp, rbx, TimesOne, sizeof(InvalidationBailoutStack)), rsp); // Jump to shared bailout tail. The BailoutInfo pointer has to be in r9. IonCode *bailoutTail = cx->runtime()->ionRuntime()->getBailoutTail(); masm.jmp(bailoutTail); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "Invalidator"); +#endif + + return code; } IonCode * IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut) { // Do not erase the frame pointer in this function. MacroAssembler masm(cx); @@ -390,16 +403,20 @@ IonRuntime::generateArgumentsRectifier(J masm.pop(r11); // Discard numActualArgs. masm.addq(r9, rsp); // Discard pushed arguments. masm.ret(); Linker linker(masm); IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "ArgumentsRectifier"); +#endif + CodeOffsetLabel returnLabel(returnOffset); returnLabel.fixup(&masm); if (returnAddrOut) *returnAddrOut = (void *) (code->raw() + returnLabel.offset()); return code; } static void @@ -450,17 +467,23 @@ IonRuntime::generateBailoutTable(JSConte IonCode * IonRuntime::generateBailoutHandler(JSContext *cx) { MacroAssembler masm; GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "BailoutHandler"); +#endif + + return code; } IonCode * IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) { typedef MoveResolver::MoveOperand MoveOperand; JS_ASSERT(!StackKeptAligned); @@ -631,16 +654,20 @@ IonRuntime::generateVMWrapper(JSContext f.explicitStackSlots() * sizeof(void *) + f.extraValuesToPop * sizeof(Value))); Linker linker(masm); IonCode *wrapper = linker.newCode(cx, JSC::OTHER_CODE); if (!wrapper) return NULL; +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(wrapper, "VMWrapper"); +#endif + // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to // use relookupOrAdd instead of add. if (!functionWrappers_->relookupOrAdd(p, &f, wrapper)) return NULL; return wrapper; } @@ -665,17 +692,23 @@ IonRuntime::generatePreBarrier(JSContext JS_ASSERT(type == MIRType_Shape); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon)); } masm.PopRegsInMask(regs); masm.ret(); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "PreBarrier"); +#endif + + return code; } typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *); static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap); IonCode * IonRuntime::generateDebugTrapHandler(JSContext *cx) { @@ -718,32 +751,50 @@ IonRuntime::generateDebugTrapHandler(JSC masm.bind(&forcedReturn); masm.loadValue(Address(ebp, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand); masm.mov(rbp, rsp); masm.pop(rbp); masm.ret(); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *codeDbg = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(codeDbg, "DebugTrapHandler"); +#endif + + return codeDbg; } IonCode * IonRuntime::generateExceptionTailStub(JSContext *cx) { MacroAssembler masm; masm.handleFailureWithHandlerTail(); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "ExceptionTailStub"); +#endif + + return code; } IonCode * IonRuntime::generateBailoutTailStub(JSContext *cx) { MacroAssembler masm; masm.generateBailoutTail(rdx, r9); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "BailoutTailStub"); +#endif + + return code; }
--- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -14,16 +14,20 @@ #include "jit/IonFrames.h" #include "jit/IonLinker.h" #include "jit/IonSpewer.h" #include "jit/VMFunctions.h" #include "jit/x86/BaselineHelpers-x86.h" #include "jsscriptinlines.h" +#ifdef JS_ION_PERF +# include "jit/PerfSpewer.h" +#endif + using namespace js; using namespace js::jit; // All registers to save and restore. This includes the stack pointer, since we // use the ability to reference register values on the stack by index. static const RegisterSet AllRegs = RegisterSet(GeneralRegisterSet(Registers::AllMask), FloatRegisterSet(FloatRegisters::AllMask)); @@ -246,17 +250,23 @@ IonRuntime::generateEnterJIT(JSContext * masm.pop(esi); masm.pop(ebx); // Restore old stack frame pointer masm.pop(ebp); masm.ret(); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "EnterJIT"); +#endif + + return code; } IonCode * IonRuntime::generateInvalidator(JSContext *cx) { AutoIonContextAlloc aica(cx); MacroAssembler masm(cx); @@ -299,16 +309,21 @@ IonRuntime::generateInvalidator(JSContex // Jump to shared bailout tail. The BailoutInfo pointer has to be in ecx. IonCode *bailoutTail = cx->runtime()->ionRuntime()->getBailoutTail(); masm.jmp(bailoutTail); Linker linker(masm); IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); IonSpew(IonSpew_Invalidate, " invalidation thunk created at %p", (void *) code->raw()); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "Invalidator"); +#endif + return code; } IonCode * IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut) { MacroAssembler masm(cx); @@ -392,16 +407,20 @@ IonRuntime::generateArgumentsRectifier(J masm.lea(Operand(unwind), esp); masm.pop(FramePointer); masm.ret(); Linker linker(masm); IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "ArgumentsRectifier"); +#endif + CodeOffsetLabel returnLabel(returnOffset); returnLabel.fixup(&masm); if (returnAddrOut) *returnAddrOut = (void *) (code->raw() + returnLabel.offset()); return code; } static void @@ -466,28 +485,40 @@ IonRuntime::generateBailoutTable(JSConte Label bailout; for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++) masm.call(&bailout); masm.bind(&bailout); GenerateBailoutThunk(cx, masm, frameClass); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "BailoutHandler"); +#endif + + return code; } IonCode * IonRuntime::generateBailoutHandler(JSContext *cx) { MacroAssembler masm; GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "BailoutHandler"); +#endif + + return code; } IonCode * IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) { typedef MoveResolver::MoveOperand MoveOperand; JS_ASSERT(!StackKeptAligned); @@ -653,16 +684,20 @@ IonRuntime::generateVMWrapper(JSContext f.explicitStackSlots() * sizeof(void *) + f.extraValuesToPop * sizeof(Value))); Linker linker(masm); IonCode *wrapper = linker.newCode(cx, JSC::OTHER_CODE); if (!wrapper) return NULL; +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(wrapper, "VMWrapper"); +#endif + // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to // use relookupOrAdd instead of add. if (!functionWrappers_->relookupOrAdd(p, &f, wrapper)) return NULL; return wrapper; } @@ -694,17 +729,23 @@ IonRuntime::generatePreBarrier(JSContext JS_ASSERT(type == MIRType_Shape); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon)); } masm.PopRegsInMask(save); masm.ret(); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "PreBarrier"); +#endif + + return code; } typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *); static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap); IonCode * IonRuntime::generateDebugTrapHandler(JSContext *cx) { @@ -747,32 +788,50 @@ IonRuntime::generateDebugTrapHandler(JSC masm.bind(&forcedReturn); masm.loadValue(Address(ebp, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand); masm.mov(ebp, esp); masm.pop(ebp); masm.ret(); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *codeDbg = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(codeDbg, "DebugTrapHandler"); +#endif + + return codeDbg; } IonCode * IonRuntime::generateExceptionTailStub(JSContext *cx) { MacroAssembler masm; masm.handleFailureWithHandlerTail(); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "ExceptionTailStub"); +#endif + + return code; } IonCode * IonRuntime::generateBailoutTailStub(JSContext *cx) { MacroAssembler masm; masm.generateBailoutTail(edx, ecx); Linker linker(masm); - return linker.newCode(cx, JSC::OTHER_CODE); + IonCode *code = linker.newCode(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerIonCodeProfile(code, "BailoutTailStub"); +#endif + + return code; }