Bug 1018290 - Add back IonScriptCounts to Asm.js modules, allow IonScriptCounts to be generated with off thread compilation, r=luke.
☠☠ backed out by 2215d5520922 ☠ ☠
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 02 Jun 2014 06:50:28 -0600
changeset 205382 743209d15d598a7f56f16fdd9be0edcc1971dcce
parent 205381 e09168ca307247b69bf9e494a512327a5fdb7f8c
child 205383 f0c34fad69cb9a3e2117d31b3514a02a9470230d
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1018290
milestone32.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
Bug 1018290 - Add back IonScriptCounts to Asm.js modules, allow IonScriptCounts to be generated with off thread compilation, r=luke.
js/src/jit/AsmJS.cpp
js/src/jit/AsmJSModule.cpp
js/src/jit/AsmJSModule.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/CompileWrappers.cpp
js/src/jit/CompileWrappers.h
js/src/jit/Ion.cpp
js/src/vm/OldDebugAPI.cpp
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -1441,16 +1441,20 @@ class MOZ_STACK_CLASS ModuleCompiler
         unsigned startCodeOffset = func.code()->offset();
         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_);
         masm_.align(AsmJSPageSize);
         finishedFunctionBodies_ = true;
         module_->initFunctionBytes(masm_.size());
     }
 
     void setInterpExitOffset(unsigned exitIndex) {
@@ -5476,16 +5480,22 @@ GenerateCode(ModuleCompiler &m, ModuleCo
     m.masm().resetForNewCodeGenerator(mir.alloc());
 
     m.masm().bind(func.code());
 
     ScopedJSDeletePtr<CodeGenerator> codegen(js_new<CodeGenerator>(&mir, &lir, &m.masm()));
     if (!codegen || !codegen->generateAsmJS(&m.stackOverflowLabel()))
         return m.fail(nullptr, "internal codegen failure (probably out of memory)");
 
+    jit::IonScriptCounts *counts = codegen->extractScriptCounts();
+    if (counts && !m.addFunctionCounts(counts)) {
+        js_delete(counts);
+        return false;
+    }
+
 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
     // Profiling might not be active now, but it may be activated later (perhaps
     // after the module has been cached and reloaded from the cache). Function
     // profiling info isn't huge, so store it always (in --enable-profiling
     // builds, which is only Nightly builds, but default).
     if (!m.trackProfiledFunction(func, m.masm().size()))
         return false;
 #endif
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -369,16 +369,19 @@ AsmJSModule::~AsmJSModule()
                 continue;
 
             jit::DependentAsmJSModuleExit exit(this, i);
             script->ionScript()->removeDependentAsmJSModule(exit);
         }
 
         DeallocateExecutableMemory(code_, pod.totalBytes_);
     }
+
+    for (size_t i = 0; i < numFunctionCounts(); i++)
+        js_delete(functionCounts(i));
 }
 
 void
 AsmJSModule::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t *asmJSModuleCode,
                            size_t *asmJSModuleData)
 {
     *asmJSModuleCode += pod.totalBytes_;
     *asmJSModuleData += mallocSizeOf(this) +
@@ -389,16 +392,17 @@ AsmJSModule::addSizeOfMisc(mozilla::Mall
                         functionNames_.sizeOfExcludingThis(mallocSizeOf) +
                         heapAccesses_.sizeOfExcludingThis(mallocSizeOf) +
 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
                         profiledFunctions_.sizeOfExcludingThis(mallocSizeOf) +
 #endif
 #if defined(JS_ION_PERF)
                         perfProfiledBlocksFunctions_.sizeOfExcludingThis(mallocSizeOf) +
 #endif
+                        functionCounts_.sizeOfExcludingThis(mallocSizeOf) +
                         staticLinkData_.sizeOfExcludingThis(mallocSizeOf);
 }
 
 static void
 AsmJSModuleObject_finalize(FreeOp *fop, JSObject *obj)
 {
     fop->delete_(&obj->as<AsmJSModuleObject>().module());
 }
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -460,16 +460,18 @@ class AsmJSModule
     // The next two fields need to be kept out of the Pod as they depend on the
     // position of the module within the ScriptSource and thus aren't invariant
     // with caching.
     uint32_t                              funcStart_;
     uint32_t                              offsetToEndOfUseAsm_;
 
     ScriptSource *                        scriptSource_;
 
+    FunctionCountsVector                  functionCounts_;
+
     // This field is accessed concurrently when requesting an interrupt.
     // Access must be synchronized via the runtime's interrupt lock.
     mutable bool                          codeIsProtected_;
 
   public:
     explicit AsmJSModule(ScriptSource *scriptSource, uint32_t functStart,
                          uint32_t offsetToEndOfUseAsm, bool strict);
     ~AsmJSModule();
@@ -598,16 +600,19 @@ class AsmJSModule
         if (SIZE_MAX - pod.funcPtrTableAndExitBytes_ < sizeof(ExitDatum))
             return false;
         uint32_t globalDataOffset = globalDataBytes();
         JS_STATIC_ASSERT(sizeof(ExitDatum) % sizeof(void*) == 0);
         pod.funcPtrTableAndExitBytes_ += sizeof(ExitDatum);
         *exitIndex = unsigned(exits_.length());
         return exits_.append(Exit(ffiIndex, globalDataOffset));
     }
+    bool addFunctionCounts(jit::IonScriptCounts *counts) {
+        return functionCounts_.append(counts);
+    }
 
     bool addExportedFunction(PropertyName *name, uint32_t srcStart, uint32_t srcEnd,
                              PropertyName *maybeFieldName,
                              ArgCoercionVector &&argCoercions,
                              ReturnType returnType)
     {
         ExportedFunction func(name, srcStart, srcEnd, maybeFieldName,
                               mozilla::Move(argCoercions), returnType);
@@ -696,16 +701,22 @@ class AsmJSModule
     uint8_t *interpExitTrampoline(const Exit &exit) const {
         JS_ASSERT(exit.interpCodeOffset_);
         return code_ + exit.interpCodeOffset_;
     }
     uint8_t *ionExitTrampoline(const Exit &exit) const {
         JS_ASSERT(exit.ionCodeOffset_);
         return code_ + exit.ionCodeOffset_;
     }
+    unsigned numFunctionCounts() const {
+        return functionCounts_.length();
+    }
+    jit::IonScriptCounts *functionCounts(unsigned i) {
+        return functionCounts_[i];
+    }
 
     // An Exit holds bookkeeping information about an exit; the ExitDatum
     // struct overlays the actual runtime data stored in the global data
     // section.
     struct ExitDatum
     {
         uint8_t *exit;
         HeapPtrFunction fun;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -147,22 +147,24 @@ CodeGenerator::visitOutOfLineCache(OutOf
 StringObject *
 MNewStringObject::templateObj() const {
     return &templateObj_->as<StringObject>();
 }
 
 CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
   : CodeGeneratorSpecific(gen, graph, masm)
   , ionScriptLabels_(gen->alloc())
+  , scriptCounts_(nullptr)
 {
 }
 
 CodeGenerator::~CodeGenerator()
 {
     JS_ASSERT_IF(!gen->compilingAsmJS(), masm.numAsmJSAbsoluteLinks() == 0);
+    js_delete(scriptCounts_);
 }
 
 typedef bool (*StringToNumberFn)(ThreadSafeContext *, JSString *, double *);
 typedef bool (*StringToNumberParFn)(ForkJoinContext *, JSString *, double *);
 static const VMFunctionsModal StringToNumberInfo = VMFunctionsModal(
     FunctionInfo<StringToNumberFn>(StringToNumber),
     FunctionInfo<StringToNumberParFn>(StringToNumberPar));
 
@@ -3152,57 +3154,56 @@ CodeGenerator::visitOutOfLineInterruptCh
     masm.jump(ool->rejoin());
 
     return true;
 }
 
 IonScriptCounts *
 CodeGenerator::maybeCreateScriptCounts()
 {
-    // If scripts are being profiled, create a new IonScriptCounts and attach
-    // it to the script. This must be done on the main thread.
-    JSContext *cx = GetIonContext()->cx;
-    if (!cx || !cx->runtime()->profilingScripts)
+    // If scripts are being profiled, create a new IonScriptCounts for the
+    // profiling data, which will be attached to the associated JSScript or
+    // AsmJS module after code generation finishes.
+    if (!GetIonContext()->runtime->profilingScripts())
         return nullptr;
 
+    IonScriptCounts *counts = nullptr;
+
     CompileInfo *outerInfo = &gen->info();
     JSScript *script = outerInfo->script();
-    if (!script)
-        return nullptr;
-
-    if (!script->hasScriptCounts() && !script->initScriptCounts(cx))
-        return nullptr;
-
-    IonScriptCounts *counts = js_new<IonScriptCounts>();
+
+    counts = js_new<IonScriptCounts>();
     if (!counts || !counts->init(graph.numBlocks())) {
         js_delete(counts);
         return nullptr;
     }
 
     for (size_t i = 0; i < graph.numBlocks(); i++) {
         MBasicBlock *block = graph.getBlock(i)->mir();
 
-        // Find a PC offset in the outermost script to use. If this block
-        // is from an inlined script, find a location in the outer script
-        // to associate information about the inlining with.
-        MResumePoint *resume = block->entryResumePoint();
-        while (resume->caller())
-            resume = resume->caller();
-
-        uint32_t offset = script->pcToOffset(resume->pc());
+        uint32_t offset = 0;
+        if (script) {
+            // Find a PC offset in the outermost script to use. If this block
+            // is from an inlined script, find a location in the outer script
+            // to associate information about the inlining with.
+            MResumePoint *resume = block->entryResumePoint();
+            while (resume->caller())
+                resume = resume->caller();
+            offset = script->pcToOffset(resume->pc());
+        }
+
         if (!counts->block(i).init(block->id(), offset, block->numSuccessors())) {
             js_delete(counts);
             return nullptr;
         }
-
         for (size_t j = 0; j < block->numSuccessors(); j++)
             counts->block(i).setSuccessor(j, block->getSuccessor(j)->id());
     }
 
-    script->addIonCounts(counts);
+    scriptCounts_ = counts;
     return counts;
 }
 
 // Structure for managing the state tracked for a block by script counters.
 struct ScriptCountBlockState
 {
     IonBlockCounts &block;
     MacroAssembler &masm;
@@ -6697,16 +6698,19 @@ CodeGenerator::link(JSContext *cx, types
         // since that will cancel this compilation too.
         if (!Invalidate(cx, script, SequentialExecution,
                         /* resetUses */ false, /* cancelOffThread*/ false))
         {
             return false;
         }
     }
 
+    if (scriptCounts_ && !script->initScriptCounts(cx))
+        return false;
+
     // Check to make sure we didn't have a mid-build invalidation. If so, we
     // will trickle to jit::Compile() and return Method_Skipped.
     types::RecompileInfo recompileInfo;
     if (!types::FinishCompilation(cx, script, executionMode, constraints, &recompileInfo))
         return true;
 
     uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None()
                            ? frameDepth_
@@ -6874,16 +6878,20 @@ CodeGenerator::link(JSContext *cx, types
       case ParallelExecution:
         // We don't run incremental GC during parallel execution; no need to
         // turn on barriers.
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 
+    // Attach any generated script counts to the script.
+    if (IonScriptCounts *counts = extractScriptCounts())
+        script->addIonCounts(counts);
+
     return true;
 }
 
 // An out-of-line path to convert a boxed int32 to either a float or double.
 class OutOfLineUnboxFloatingPoint : public OutOfLineCodeBase<CodeGenerator>
 {
     LUnboxFloatingPoint *unboxFloatingPoint_;
 
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -349,16 +349,22 @@ class CodeGenerator : public CodeGenerat
     bool visitAssertRangeI(LAssertRangeI *ins);
     bool visitAssertRangeD(LAssertRangeD *ins);
     bool visitAssertRangeF(LAssertRangeF *ins);
     bool visitAssertRangeV(LAssertRangeV *ins);
 
     bool visitInterruptCheck(LInterruptCheck *lir);
     bool visitRecompileCheck(LRecompileCheck *ins);
 
+    IonScriptCounts *extractScriptCounts() {
+        IonScriptCounts *counts = scriptCounts_;
+        scriptCounts_ = nullptr;  // prevent delete in dtor
+        return counts;
+    }
+
   private:
     bool addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
                              PropertyName *name, TypedOrValueRegister output,
                              bool monitoredResult, jsbytecode *profilerLeavePc);
     bool addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index,
                             TypedOrValueRegister output, bool monitoredResult,
                             bool allowDoubleResult, jsbytecode *profilerLeavePc);
     bool addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
@@ -454,16 +460,19 @@ class CodeGenerator : public CodeGenerat
 #ifdef DEBUG
     bool branchIfInvalidated(Register temp, Label *invalidated);
 
     bool emitDebugResultChecks(LInstruction *ins);
     bool emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mir);
     bool emitValueResultChecks(LInstruction *lir, MDefinition *mir);
 #endif
 
+    // Script counts created during code generation.
+    IonScriptCounts *scriptCounts_;
+
 #if defined(JS_ION_PERF)
     PerfSpewer perfSpewer_;
 #endif
 };
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -117,16 +117,22 @@ CompileRuntime::jitSupportsFloatingPoint
 }
 
 bool
 CompileRuntime::hadOutOfMemory()
 {
     return runtime()->hadOutOfMemory;
 }
 
+bool
+CompileRuntime::profilingScripts()
+{
+    return runtime()->profilingScripts;
+}
+
 const JSAtomState &
 CompileRuntime::names()
 {
     return *runtime()->commonNames;
 }
 
 const StaticStrings &
 CompileRuntime::staticStrings()
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -63,16 +63,17 @@ class CompileRuntime
     const JitRuntime *jitRuntime();
 
     // Compilation does not occur off thread when the SPS profiler is enabled.
     SPSProfiler &spsProfiler();
 
     bool signalHandlersInstalled();
     bool jitSupportsFloatingPoint();
     bool hadOutOfMemory();
+    bool profilingScripts();
 
     const JSAtomState &names();
     const StaticStrings &staticStrings();
     const Value &NaNValue();
     const Value &positiveInfinityValue();
 
 #ifdef DEBUG
     bool isInsideNursery(gc::Cell *cell);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1741,24 +1741,19 @@ OffThreadCompilationAvailable(JSContext 
 {
 #ifdef JS_THREADSAFE
     // Even if off thread compilation is enabled, compilation must still occur
     // on the main thread in some cases. Do not compile off thread during an
     // incremental GC, as this may trip incremental read barriers.
     //
     // Require cpuCount > 1 so that Ion compilation jobs and main-thread
     // execution are not competing for the same resources.
-    //
-    // Skip off thread compilation if PC count profiling is enabled, as
-    // CodeGenerator::maybeCreateScriptCounts will not attach script profiles
-    // when running off thread.
     return cx->runtime()->canUseParallelIonCompilation()
         && HelperThreadState().cpuCount > 1
-        && cx->runtime()->gc.incrementalState == gc::NO_INCREMENTAL
-        && !cx->runtime()->profilingScripts;
+        && cx->runtime()->gc.incrementalState == gc::NO_INCREMENTAL;
 #else
     return false;
 #endif
 }
 
 static void
 TrackAllProperties(JSContext *cx, JSObject *obj)
 {
@@ -1983,20 +1978,19 @@ CheckScriptSize(JSContext *cx, JSScript*
 #ifdef JS_THREADSAFE
         size_t cpuCount = HelperThreadState().cpuCount;
 #else
         size_t cpuCount = 1;
 #endif
         if (cx->runtime()->canUseParallelIonCompilation() && cpuCount > 1) {
             // Even if off thread compilation is enabled, there are cases where
             // compilation must still occur on the main thread. Don't compile
-            // in these cases (except when profiling scripts, as compilations
-            // occurring with profiling should reflect those without), but do
-            // not forbid compilation so that the script may be compiled later.
-            if (!OffThreadCompilationAvailable(cx) && !cx->runtime()->profilingScripts) {
+            // in these cases, but do not forbid compilation so that the script
+            // may be compiled later.
+            if (!OffThreadCompilationAvailable(cx)) {
                 IonSpew(IonSpew_Abort,
                         "Script too large for main thread, skipping (%u bytes) (%u locals/args)",
                         script->length(), numLocalsAndArgs);
                 return Method_Skipped;
             }
         } else {
             IonSpew(IonSpew_Abort, "Script too large (%u bytes) (%u locals/args)",
                     script->length(), numLocalsAndArgs);
--- a/js/src/vm/OldDebugAPI.cpp
+++ b/js/src/vm/OldDebugAPI.cpp
@@ -838,16 +838,44 @@ JS_DumpCompartmentPCCounts(JSContext *cx
     for (ZoneCellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         RootedScript script(cx, i.get<JSScript>());
         if (script->compartment() != cx->compartment())
             continue;
 
         if (script->hasScriptCounts())
             JS_DumpPCCounts(cx, script);
     }
+
+#if defined(JS_ION)
+    for (unsigned thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) {
+        for (ZoneCellIter i(cx->zone(), (AllocKind) thingKind); !i.done(); i.next()) {
+            JSObject *obj = i.get<JSObject>();
+            if (obj->compartment() != cx->compartment())
+                continue;
+
+            if (obj->is<AsmJSModuleObject>()) {
+                AsmJSModule &module = obj->as<AsmJSModuleObject>().module();
+
+                Sprinter sprinter(cx);
+                if (!sprinter.init())
+                    return;
+
+                fprintf(stdout, "--- Asm.js Module ---\n");
+
+                for (size_t i = 0; i < module.numFunctionCounts(); i++) {
+                    jit::IonScriptCounts *counts = module.functionCounts(i);
+                    DumpIonScriptCounts(&sprinter, counts);
+                }
+
+                fputs(sprinter.string(), stdout);
+                fprintf(stdout, "--- END Asm.js Module ---\n");
+            }
+        }
+    }
+#endif
 }
 
 JS_FRIEND_API(bool)
 js::CanCallContextDebugHandler(JSContext *cx)
 {
     return !!cx->runtime()->debugHooks.debuggerHandler;
 }