Bug 864216 - Allow generating IonScriptCounts for asm.js compiled functions, r=luke.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 23 Apr 2013 13:31:03 -0600
changeset 129673 bc935b861f48ffa4fb91da4af7b8509e552bdf72
parent 129671 9a8232d88dc4272613a69c5fe5d60e5922925b78
child 129674 faafe91695d97fa54cff7d34fab0ea0dcd827b5c
push id24586
push userryanvm@gmail.com
push dateWed, 24 Apr 2013 12:15:57 +0000
treeherdermozilla-central@1c5977e8d52f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs864216
milestone23.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 864216 - Allow generating IonScriptCounts for asm.js compiled functions, r=luke.
js/src/ion/AsmJS.cpp
js/src/ion/AsmJSModule.h
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/jsdbgapi.cpp
js/src/jsopcode.cpp
js/src/jsopcode.h
--- a/js/src/ion/AsmJS.cpp
+++ b/js/src/ion/AsmJS.cpp
@@ -1316,17 +1316,19 @@ class ModuleCompiler
         if (p) {
             *exitIndex = p->value;
             return true;
         }
         if (!module_->addExit(ffiIndex, exitIndex))
             return false;
         return exits_.add(p, Move(exitDescriptor), *exitIndex);
     }
-
+    bool addFunctionCounts(IonScriptCounts *counts) {
+        return module_->addFunctionCounts(counts);
+    }
 
     void setSecondPassComplete() {
         JS_ASSERT(currentPass_ == 2);
         masm_.align(gc::PageSize);
         module_->setFunctionBytes(masm_.size());
         currentPass_ = 3;
     }
 
@@ -2306,16 +2308,22 @@ static Class AsmJSModuleClass = {
 
 AsmJSModule &
 js::AsmJSModuleObjectToModule(JSObject *obj)
 {
     JS_ASSERT(obj->getClass() == &AsmJSModuleClass);
     return *(AsmJSModule *)obj->getReservedSlot(ASM_CODE_RESERVED_SLOT).toPrivate();
 }
 
+bool
+js::IsAsmJSModuleObject(JSObject *obj)
+{
+    return obj->getClass() == &AsmJSModuleClass;
+}
+
 static const unsigned ASM_MODULE_FUNCTION_MODULE_OBJECT_SLOT = 0;
 
 JSObject &
 js::AsmJSModuleObject(JSFunction *moduleFun)
 {
     return moduleFun->getExtendedSlot(ASM_MODULE_FUNCTION_MODULE_OBJECT_SLOT).toObject();
 }
 
@@ -4426,16 +4434,22 @@ GenerateAsmJSCode(ModuleCompiler &m, Mod
 
     ScopedJSDeletePtr<CodeGenerator> codegen(GenerateCode(&mirGen, &lir, &m.masm()));
     if (!codegen)
         return m.fail("Internal codegen failure (probably out of memory)", func.fn());
 
     if (!m.collectAccesses(mirGen))
         return false;
 
+    ion::IonScriptCounts *counts = codegen->extractUnassociatedScriptCounts();
+    if (counts && !m.addFunctionCounts(counts)) {
+        js_delete(counts);
+        return false;
+    }
+
     // 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.
     m.masm().resetForNewCodeGenerator();
 
     // Align internal function headers.
@@ -5559,8 +5573,13 @@ js::IsAsmJSCompilationAvailable(JSContex
 #else
     bool available = false;
 #endif
 
     args.rval().set(BooleanValue(available));
     return true;
 }
 
+AsmJSModule::~AsmJSModule()
+{
+    for (size_t i = 0; i < numFunctionCounts(); i++)
+        js_delete(functionCounts(i));
+}
--- a/js/src/ion/AsmJSModule.h
+++ b/js/src/ion/AsmJSModule.h
@@ -300,16 +300,17 @@ class AsmJSModule
   private:
     typedef Vector<ExportedFunction, 0, SystemAllocPolicy> ExportedFunctionVector;
     typedef Vector<Global, 0, SystemAllocPolicy> GlobalVector;
     typedef Vector<Exit, 0, SystemAllocPolicy> ExitVector;
     typedef Vector<ion::AsmJSHeapAccess, 0, SystemAllocPolicy> HeapAccessVector;
 #if defined(JS_CPU_ARM)
     typedef Vector<ion::AsmJSBoundsCheck, 0, SystemAllocPolicy> BoundsCheckVector;
 #endif
+    typedef Vector<ion::IonScriptCounts *, 0, SystemAllocPolicy> FunctionCountsVector;
 
     GlobalVector                          globals_;
     ExitVector                            exits_;
     ExportedFunctionVector                exports_;
     HeapAccessVector                      heapAccesses_;
 #if defined(JS_CPU_ARM)
     BoundsCheckVector                     boundsChecks_;
 #endif
@@ -329,32 +330,36 @@ class AsmJSModule
     HeapPtr<ArrayBufferObject>            maybeHeap_;
 
     HeapPtrPropertyName                   globalArgumentName_;
     HeapPtrPropertyName                   importArgumentName_;
     HeapPtrPropertyName                   bufferArgumentName_;
 
     PostLinkFailureInfo                   postLinkFailureInfo_;
 
+    FunctionCountsVector                  functionCounts_;
+
   public:
     explicit AsmJSModule(JSContext *cx)
       : numGlobalVars_(0),
         numFFIs_(0),
         numFuncPtrTableElems_(0),
         hasArrayView_(false),
         code_(NULL),
         operationCallbackExit_(NULL),
         functionBytes_(0),
         codeBytes_(0),
         totalBytes_(0),
         linked_(false),
         maybeHeap_(),
         postLinkFailureInfo_(cx)
     {}
 
+    ~AsmJSModule();
+
     void trace(JSTracer *trc) {
         for (unsigned i = 0; i < globals_.length(); i++)
             globals_[i].trace(trc);
         for (unsigned i = 0; i < exports_.length(); i++)
             exports_[i].trace(trc);
         for (unsigned i = 0; i < exits_.length(); i++) {
             if (exitIndexToGlobalDatum(i).fun)
                 MarkObject(trc, &exitIndexToGlobalDatum(i).fun, "asm.js imported function");
@@ -420,16 +425,19 @@ class AsmJSModule
         g.u.constantValue_ = value;
         g.name_ = fieldName;
         return globals_.append(g);
     }
     bool addExit(unsigned ffiIndex, unsigned *exitIndex) {
         *exitIndex = unsigned(exits_.length());
         return exits_.append(Exit(ffiIndex));
     }
+    bool addFunctionCounts(ion::IonScriptCounts *counts) {
+        return functionCounts_.append(counts);
+    }
 
     bool addExportedFunction(RawFunction fun, PropertyName *maybeFieldName,
                              MoveRef<ArgCoercionVector> argCoercions, ReturnType returnType)
     {
         ExportedFunction func(fun, maybeFieldName, argCoercions, returnType);
         return exports_.append(Move(func));
     }
     unsigned numExportedFunctions() const {
@@ -463,16 +471,22 @@ class AsmJSModule
         return exits_.length();
     }
     Exit &exit(unsigned i) {
         return exits_[i];
     }
     const Exit &exit(unsigned i) const {
         return exits_[i];
     }
+    unsigned numFunctionCounts() const {
+        return functionCounts_.length();
+    }
+    ion::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;
@@ -656,16 +670,19 @@ class AsmJSModule
     }
 };
 
 // The AsmJSModule C++ object is held by a JSObject that takes care of calling
 // 'trace' and the destructor on finalization.
 extern AsmJSModule &
 AsmJSModuleObjectToModule(JSObject *obj);
 
+extern bool
+IsAsmJSModuleObject(JSObject *obj);
+
 extern JSObject &
 AsmJSModuleObject(JSFunction *moduleFun);
 
 extern void
 SetAsmJSModuleObject(JSFunction *moduleFun, JSObject *moduleObj);
 
 }  // namespace js
 
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -122,18 +122,24 @@ CodeGenerator::visitOutOfLineCache(OutOf
 }
 
 StringObject *
 MNewStringObject::templateObj() const {
     return &templateObj_->asString();
 }
 
 CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
-  : CodeGeneratorSpecific(gen, graph, masm)
-{
+  : CodeGeneratorSpecific(gen, graph, masm),
+    unassociatedScriptCounts_(NULL)
+{
+}
+
+CodeGenerator::~CodeGenerator()
+{
+    js_delete(unassociatedScriptCounts_);
 }
 
 bool
 CodeGenerator::visitValueToInt32(LValueToInt32 *lir)
 {
     ValueOperand operand = ToValue(lir, LValueToInt32::Input);
     Register output = ToRegister(lir->output());
 
@@ -2088,53 +2094,60 @@ CodeGenerator::maybeCreateScriptCounts()
     if (!cx)
         return NULL;
 
     IonScriptCounts *counts = NULL;
 
     CompileInfo *outerInfo = &gen->info();
     RawScript script = outerInfo->script();
 
-    if (!script)
-        return NULL;
-
-    if (cx->runtime->profilingScripts && !script->hasScriptCounts) {
-        if (!script->initScriptCounts(cx))
+    if (cx->runtime->profilingScripts) {
+        if (script && !script->hasScriptCounts && !script->initScriptCounts(cx))
             return NULL;
     }
 
-    if (!script->hasScriptCounts)
+    if (script && !script->hasScriptCounts)
         return NULL;
 
     counts = js_new<IonScriptCounts>();
     if (!counts || !counts->init(graph.numBlocks())) {
         js_delete(counts);
         return NULL;
     }
 
-    script->addIonCounts(counts);
+    if (script)
+        script->addIonCounts(counts);
 
     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 inling with.
-        MResumePoint *resume = block->entryResumePoint();
-        while (resume->caller())
-            resume = resume->caller();
-        uint32_t offset = resume->pc() - script->code;
-        JS_ASSERT(offset < script->length);
+        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();
+            uint32_t offset = resume->pc() - script->code;
+            JS_ASSERT(offset < script->length);
+        }
 
         if (!counts->block(i).init(block->id(), offset, block->numSuccessors()))
             return NULL;
         for (size_t j = 0; j < block->numSuccessors(); j++)
             counts->block(i).setSuccessor(j, block->getSuccessor(j)->id());
     }
 
+    if (!script) {
+        // Compiling code for Asm.js. Leave the counts on the CodeGenerator to
+        // be picked up by the AsmJSModule after generation finishes.
+        unassociatedScriptCounts_ = counts;
+    }
+
     return counts;
 }
 
 // Structure for managing the state tracked for a block by script counters.
 struct ScriptCountBlockState
 {
     IonBlockCounts &block;
     MacroAssembler &masm;
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -36,16 +36,17 @@ class OutOfLineUpdateCache;
 
 class CodeGenerator : public CodeGeneratorSpecific
 {
     bool generateArgumentsChecks();
     bool generateBody();
 
   public:
     CodeGenerator(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm = NULL);
+    ~CodeGenerator();
 
   public:
     bool generate();
     bool generateAsmJS();
     bool link();
 
     bool visitLabel(LLabel *lir);
     bool visitNop(LNop *lir);
@@ -255,16 +256,22 @@ class CodeGenerator : public CodeGenerat
     bool visitGetPropertyIC(OutOfLineUpdateCache *ool, GetPropertyIC *ic);
     bool visitParallelGetPropertyIC(OutOfLineUpdateCache *ool, ParallelGetPropertyIC *ic);
     bool visitSetPropertyIC(OutOfLineUpdateCache *ool, SetPropertyIC *ic);
     bool visitGetElementIC(OutOfLineUpdateCache *ool, GetElementIC *ic);
     bool visitBindNameIC(OutOfLineUpdateCache *ool, BindNameIC *ic);
     bool visitNameIC(OutOfLineUpdateCache *ool, NameIC *ic);
     bool visitCallsiteCloneIC(OutOfLineUpdateCache *ool, CallsiteCloneIC *ic);
 
+    IonScriptCounts *extractUnassociatedScriptCounts() {
+        IonScriptCounts *counts = unassociatedScriptCounts_;
+        unassociatedScriptCounts_ = NULL;  // prevent delete in dtor
+        return counts;
+    }
+
   private:
     bool addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
                              PropertyName *name, TypedOrValueRegister output,
                              bool allowGetters);
 
     bool checkForParallelBailout();
     bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
 
@@ -296,14 +303,17 @@ class CodeGenerator : public CodeGenerat
     // Like testValueTruthy but takes an object, and |ool| must be non-null.
     // (If it's known that an object can never emulate |undefined| it shouldn't
     // be tested in the first place.)
     void testObjectTruthy(Register objreg, Label *ifTruthy, Label *ifFalsy, Register scratch,
                           OutOfLineTestObject *ool);
 
     // Bailout if an element about to be written to is a hole.
     bool emitStoreHoleCheck(Register elements, const LAllocation *index, LSnapshot *snapshot);
+
+    // Script counts created when compiling code with no associated JSScript.
+    IonScriptCounts *unassociatedScriptCounts_;
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_codegen_h__
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -31,16 +31,20 @@
 #include "jswrapper.h"
 
 #include "gc/Marking.h"
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/Parser.h"
 #include "vm/Debugger.h"
 #include "vm/Shape.h"
 
+#ifdef JS_ASMJS
+#include "ion/AsmJSModule.h"
+#endif
+
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsinterpinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Shape-inl.h"
 #include "vm/Stack-inl.h"
@@ -912,16 +916,44 @@ JS_DumpCompartmentPCCounts(JSContext *cx
     for (CellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         if (script->compartment() != cx->compartment)
             continue;
 
         if (script->hasScriptCounts && script->enclosingScriptsCompiledSuccessfully())
             JS_DumpPCCounts(cx, script);
     }
+
+#if defined(JS_ASMJS) && defined(DEBUG)
+    for (unsigned thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) {
+        for (CellIter i(cx->zone(), (AllocKind) thingKind); !i.done(); i.next()) {
+            JSObject *obj = i.get<JSObject>();
+            if (obj->compartment() != cx->compartment)
+                continue;
+
+            if (IsAsmJSModuleObject(obj)) {
+                AsmJSModule &module = AsmJSModuleObjectToModule(obj);
+
+                Sprinter sprinter(cx);
+                if (!sprinter.init())
+                    return;
+
+                fprintf(stdout, "--- Asm.js Module ---\n");
+
+                for (size_t i = 0; i < module.numFunctionCounts(); i++) {
+                    ion::IonScriptCounts *counts = module.functionCounts(i);
+                    DumpIonScriptCounts(&sprinter, counts);
+                }
+
+                fputs(sprinter.string(), stdout);
+                fprintf(stdout, "--- END Asm.js Module ---\n");
+            }
+        }
+    }
+#endif
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_UnwrapObject(JSObject *obj)
 {
     return UncheckedUnwrap(obj);
 }
 
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -264,16 +264,33 @@ PCCounts::countName(JSOp op, size_t whic
 
     JS_NOT_REACHED("bad op");
     return NULL;
 }
 
 #ifdef DEBUG
 
 void
+js::DumpIonScriptCounts(Sprinter *sp, ion::IonScriptCounts *ionCounts)
+{
+    Sprint(sp, "IonScript [%lu blocks]:\n", ionCounts->numBlocks());
+    for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
+        const ion::IonBlockCounts &block = ionCounts->block(i);
+        if (block.hitCount() < 10)
+            continue;
+        Sprint(sp, "BB #%lu [%05u]", block.id(), block.offset());
+        for (size_t j = 0; j < block.numSuccessors(); j++)
+            Sprint(sp, " -> #%lu", block.successor(j));
+        Sprint(sp, " :: %llu hits %u instruction bytes %u spill bytes\n",
+               block.hitCount(), block.instructionBytes(), block.spillBytes());
+        Sprint(sp, "%s\n", block.code());
+    }
+}
+
+void
 js_DumpPCCounts(JSContext *cx, HandleScript script, js::Sprinter *sp)
 {
     JS_ASSERT(script->hasScriptCounts);
 
     jsbytecode *pc = script->code;
     while (pc < script->code + script->length) {
         JSOp op = JSOp(*pc);
 
@@ -300,28 +317,17 @@ js_DumpPCCounts(JSContext *cx, HandleScr
         Sprint(sp, "}\n");
 
         pc = next;
     }
 
     ion::IonScriptCounts *ionCounts = script->getIonCounts();
 
     while (ionCounts) {
-        Sprint(sp, "IonScript [%lu blocks]:\n", ionCounts->numBlocks());
-        for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
-            const ion::IonBlockCounts &block = ionCounts->block(i);
-            if (block.hitCount() < 10)
-                continue;
-            Sprint(sp, "BB #%lu [%05u]", block.id(), block.offset());
-            for (size_t j = 0; j < block.numSuccessors(); j++)
-                Sprint(sp, " -> #%lu", block.successor(j));
-            Sprint(sp, " :: %llu hits %u instruction bytes %u spill bytes\n",
-                   block.hitCount(), block.instructionBytes(), block.spillBytes());
-            Sprint(sp, "%s\n", block.code());
-        }
+        DumpIonScriptCounts(sp, ionCounts);
         ionCounts = ionCounts->previous();
     }
 }
 
 /*
  * If pc != NULL, include a prefix indicating whether the PC is at the current line.
  * If showAll is true, include the source note type and the entry stack depth.
  */
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -627,11 +627,20 @@ JSBool
 js_Disassemble(JSContext *cx, JS::Handle<JSScript*> script, JSBool lines, js::Sprinter *sp);
 
 unsigned
 js_Disassemble1(JSContext *cx, JS::Handle<JSScript*> script, jsbytecode *pc, unsigned loc,
                 JSBool lines, js::Sprinter *sp);
 
 void
 js_DumpPCCounts(JSContext *cx, JS::Handle<JSScript*> script, js::Sprinter *sp);
+
+#ifdef JS_ION
+namespace js {
+namespace ion { struct IonScriptCounts; }
+void
+DumpIonScriptCounts(js::Sprinter *sp, ion::IonScriptCounts *ionCounts);
+}
+#endif
+
 #endif
 
 #endif /* jsopcode_h___ */