Bug 710032 - CollectNativeRegions() utility to simplify handling of inlined frames. r=bhackett
authorSteve Fink <sfink@mozilla.com>
Fri, 21 Oct 2011 15:43:55 -0700
changeset 84754 23936f5667810507e18f0429abba3fa8075bcc3b
parent 84753 c245807aad3aa0cca9a1c01575397fff9e9f174a
child 84755 136c73b6457cc8a917d0fdaff58b22582850248b
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs710032
milestone12.0a1
Bug 710032 - CollectNativeRegions() utility to simplify handling of inlined frames. r=bhackett
js/src/jsprobes.cpp
js/src/jsprobes.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
--- a/js/src/jsprobes.cpp
+++ b/js/src/jsprobes.cpp
@@ -50,16 +50,20 @@
 #include "jsdbgapi.h"
 #include "jsfun.h"
 #include "jsinterp.h"
 #include "jsobj.h"
 #include "jsprobes.h"
 #include "jsscript.h"
 #include "jsstr.h"
 
+#ifdef JS_METHODJIT
+# include "methodjit/Compiler.h"
+#endif
+
 #include "jsobjinlines.h"
 
 #define TYPEOF(cx,v)    (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
 
 using namespace js;
 
 const char Probes::nullName[] = "(null)";
 const char Probes::anonymousName[] = "(anonymous)";
@@ -109,26 +113,125 @@ Probes::JITGranularityRequested()
         JITReportGranularity request = (*p)->granularityRequested();
         if (request > want)
             want = request;
     }
 
     return want;
 }
 
+/*
+ * Flatten the tree of inlined frames into a series of native code regions, one
+ * for each contiguous section of native code that belongs to a single
+ * ActiveFrame. (Note that some of these regions may be zero-length, for
+ * example if two ActiveFrames end at the same place.)
+ */
+typedef mjit::Compiler::ActiveFrame ActiveFrame;
+
+bool
+Probes::JITWatcher::CollectNativeRegions(RegionVector &regions,
+                                         JSRuntime *rt,
+                                         mjit::JITScript *jit,
+                                         mjit::JSActiveFrame *outerFrame,
+                                         mjit::JSActiveFrame **inlineFrames)
+{
+    regions.resize(jit->nInlineFrames * 2 + 2);
+
+    mjit::JSActiveFrame **stack =
+        rt->array_new<mjit::JSActiveFrame*>(jit->nInlineFrames+2);
+    if (!stack)
+        return false;
+    uint32_t depth = 0;
+    uint32_t ip = 0;
+
+    stack[depth++] = NULL;
+    stack[depth++] = outerFrame;
+    regions[0].frame = outerFrame;
+    regions[0].script = outerFrame->script;
+    regions[0].pc = outerFrame->script->code;
+    regions[0].enter = true;
+    ip++;
+
+    for (uint32_t i = 0; i <= jit->nInlineFrames; i++) {
+        mjit::JSActiveFrame *frame = (i < jit->nInlineFrames) ? inlineFrames[i] : outerFrame;
+
+        // Not a down frame; pop the current frame, then pop until we reach
+        // this frame's parent, recording subframe ends as we go
+        while (stack[depth-1] != frame->parent) {
+            depth--;
+            JS_ASSERT(depth > 0);
+            // Pop up from regions[ip-1].frame to top of the stack: start a
+            // region in the destination frame and close off the source
+            // (origin) frame at the end of its script
+            mjit::JSActiveFrame *src = regions[ip-1].frame;
+            mjit::JSActiveFrame *dst = stack[depth-1];
+            JS_ASSERT_IF(!dst, i == jit->nInlineFrames);
+            regions[ip].frame = dst;
+            regions[ip].script = dst ? dst->script : NULL;
+            regions[ip].pc = src->parentPC + 1;
+            regions[ip-1].endpc = src->script->code + src->script->length;
+            regions[ip].enter = false;
+            ip++;
+        }
+
+        if (i < jit->nInlineFrames) {
+            // Push a frame (enter an inlined function). Start a region at the
+            // beginning of the new frame's script, and end the previous region
+            // at parentPC.
+            stack[depth++] = frame;
+
+            regions[ip].frame = frame;
+            regions[ip].script = frame->script;
+            regions[ip].pc = frame->script->code;
+            regions[ip-1].endpc = frame->parentPC;
+            regions[ip].enter = true;
+            ip++;
+        }
+    }
+
+    // Final region is always zero-length and not particularly useful
+    ip--;
+    regions.popBack();
+
+    mjit::JSActiveFrame *prev = NULL;
+    for (NativeRegion *iter = regions.begin(); iter != regions.end(); ++iter) {
+        mjit::JSActiveFrame *frame = iter->frame;
+        if (iter->enter) {
+            // Pushing down a frame, so region starts at the beginning of the
+            // (destination) frame
+            iter->mainOffset = frame->mainCodeStart;
+            iter->stubOffset = frame->stubCodeStart;
+        } else {
+            // Popping up a level, so region starts at the end of the (source) frame
+            iter->mainOffset = prev->mainCodeEnd;
+            iter->stubOffset = prev->stubCodeEnd;
+        }
+        prev = frame;
+    }
+
+    JS_ASSERT(ip == 2 * jit->nInlineFrames + 1);
+    rt->array_delete(stack);
+
+    // All of the stub code comes immediately after the main code
+    for (NativeRegion *iter = regions.begin(); iter != regions.end(); ++iter)
+        iter->stubOffset += outerFrame->mainCodeEnd;
+
+    return true;
+}
+
 #ifdef JS_METHODJIT
 void
 Probes::registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr,
-                         JSScript *script, JSFunction *fun,
-                         js::mjit::Compiler_ActiveFrame **inlineFrames,
+                         js::mjit::JSActiveFrame *outerFrame,
+                         js::mjit::JSActiveFrame **inlineFrames,
                          void *mainCodeAddress, size_t mainCodeSize,
                          void *stubCodeAddress, size_t stubCodeSize)
 {
     for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
-        (*p)->registerMJITCode(cx, jscr, script, fun,
+        (*p)->registerMJITCode(cx, jscr, outerFrame,
                                inlineFrames,
                                mainCodeAddress, mainCodeSize,
                                stubCodeAddress, stubCodeSize);
 }
 
 void
 Probes::discardMJITCode(JSContext *cx, mjit::JITScript *jscr, JSScript *script, void* address)
 {
--- a/js/src/jsprobes.h
+++ b/js/src/jsprobes.h
@@ -47,17 +47,17 @@
 #include "jsprvtd.h"
 #include "jsscript.h"
 #include "jsobj.h"
 
 namespace js {
 
 namespace mjit {
 struct NativeAddressInfo;
-struct Compiler_ActiveFrame;
+struct JSActiveFrame;
 }
 
 namespace Probes {
 
 /*
  * Static probes
  *
  * The probe points defined in this file are scattered around the SpiderMonkey
@@ -225,22 +225,41 @@ enum JITReportGranularity {
 
 /*
  * Observer class for JIT code allocation/deallocation. Currently, this only
  * handles the method JIT, and does not get notifications when JIT code is
  * changed (patched) with no new allocation.
  */
 class JITWatcher {
 public:
+    struct NativeRegion {
+        mjit::JSActiveFrame *frame;
+        JSScript *script;
+        size_t inlinedOffset;
+        jsbytecode *pc;
+        jsbytecode *endpc;
+        uintptr_t mainOffset;
+        uintptr_t stubOffset;
+        bool enter;
+    };
+
+    typedef Vector<NativeRegion, 0, RuntimeAllocPolicy> RegionVector;
+
+    static bool CollectNativeRegions(RegionVector &regions,
+                                     JSRuntime *rt,
+                                     mjit::JITScript *jit,
+                                     mjit::JSActiveFrame *outerFrame,
+                                     mjit::JSActiveFrame **inlineFrames);
+
     virtual JITReportGranularity granularityRequested() = 0;
 
 #ifdef JS_METHODJIT
     virtual void registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr,
-                                  JSScript *script, JSFunction *fun,
-                                  mjit::Compiler_ActiveFrame** inlineFrames,
+                                  mjit::JSActiveFrame *outerFrame,
+                                  mjit::JSActiveFrame **inlineFrames,
                                   void *mainCodeAddress, size_t mainCodeSize,
                                   void *stubCodeAddress, size_t stubCodeSize) = 0;
 
     virtual void discardMJITCode(JSContext *cx, mjit::JITScript *jscr, JSScript *script,
                                  void* address) = 0;
 
     virtual void registerICCode(JSContext *cx,
                                 js::mjit::JITScript *jscr, JSScript *script, jsbytecode* pc,
@@ -277,18 +296,18 @@ JITReportGranularity
 JITGranularityRequested();
 
 #ifdef JS_METHODJIT
 /*
  * New method JIT code has been created
  */
 void
 registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr,
-                 JSScript *script, JSFunction *fun,
-                 mjit::Compiler_ActiveFrame** inlineFrames,
+                 mjit::JSActiveFrame *outerFrame,
+                 mjit::JSActiveFrame **inlineFrames,
                  void *mainCodeAddress, size_t mainCodeSize,
                  void *stubCodeAddress, size_t stubCodeSize);
 
 /*
  * Method JIT code is about to be discarded
  */
 void
 discardMJITCode(JSContext *cx, mjit::JITScript *jscr, JSScript *script, void* address);
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -481,18 +481,20 @@ mjit::Compiler::pushActiveFrame(JSScript
 
     return Compile_Okay;
 }
 
 void
 mjit::Compiler::popActiveFrame()
 {
     JS_ASSERT(a->parent);
+    a->mainCodeEnd = masm.size();
+    a->stubCodeEnd = stubcc.size();
     this->PC = a->parentPC;
-    this->a = a->parent;
+    this->a = (ActiveFrame *) a->parent;
     this->script = a->script;
     this->analysis = this->script->analysis();
 
     frame.popActiveFrame();
 }
 
 #define CHECK_STATUS(expr)                                           \
     JS_BEGIN_MACRO                                                   \
@@ -546,19 +548,24 @@ mjit::Compiler::performCompilation(JITSc
 
     JaegerSpew(JSpew_Scripts, "successfully compiled (code \"%p\") (size \"%u\")\n",
                (*jitp)->code.m_code.executableAddress(), unsigned((*jitp)->code.m_size));
     return Compile_Okay;
 }
 
 #undef CHECK_STATUS
 
+mjit::JSActiveFrame::JSActiveFrame()
+    : parent(NULL), parentPC(NULL), script(NULL), inlineIndex(UINT32_MAX)
+{
+}
+
 mjit::Compiler::ActiveFrame::ActiveFrame(JSContext *cx)
-    : parent(NULL), parentPC(NULL), script(NULL), jumpMap(NULL),
-      inlineIndex(UINT32_MAX), varTypes(NULL), needReturnValue(false),
+    : jumpMap(NULL),
+      varTypes(NULL), needReturnValue(false),
       syncReturnValue(false), returnValueDouble(false), returnSet(false),
       returnEntry(NULL), returnJumps(NULL), exitState(NULL)
 {}
 
 mjit::Compiler::ActiveFrame::~ActiveFrame()
 {
     js::Foreground::free_(jumpMap);
     if (varTypes)
@@ -917,16 +924,19 @@ mjit::Compiler::finishThisUp(JITScript *
     if (cx->runtime->gcNumber != gcNumber)
         return Compile_Retry;
 
     if (overflowICSpace) {
         JaegerSpew(JSpew_Scripts, "dumped a constant pool while generating an IC\n");
         return Compile_Abort;
     }
 
+    a->mainCodeEnd = masm.size();
+    a->stubCodeEnd = stubcc.size();
+
     for (size_t i = 0; i < branchPatches.length(); i++) {
         Label label = labelOf(branchPatches[i].pc, branchPatches[i].inlineIndex);
         branchPatches[i].jump.linkTo(label, &masm);
     }
 
 #ifdef JS_CPU_ARM
     masm.forceFlushConstantPool();
     stubcc.masm.forceFlushConstantPool();
@@ -1380,18 +1390,19 @@ mjit::Compiler::finishThisUp(JITScript *
 
     /* Patch all outgoing calls. */
     masm.finalize(fullCode, inlineDoubles);
     stubcc.masm.finalize(stubCode, oolDoubles);
 
     JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size());
     JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size());
 
-    Probes::registerMJITCode(cx, jit, script, script->function() ? script->function() : NULL,
-                             (mjit::Compiler_ActiveFrame**) inlineFrames.begin(),
+    Probes::registerMJITCode(cx, jit,
+                             a,
+                             (JSActiveFrame**) inlineFrames.begin(),
                              result, masm.size(),
                              result + masm.size(), stubcc.size());
 
     *jitp = jit;
 
     return Compile_Okay;
 }
 
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -57,16 +57,37 @@ namespace mjit {
  * redirecting the return address in InvariantFailure.
  */
 struct InvariantCodePatch {
     bool hasPatch;
     JSC::MacroAssembler::DataLabelPtr codePatch;
     InvariantCodePatch() : hasPatch(false) {}
 };
 
+struct JSActiveFrame {
+    JSActiveFrame *parent;
+    jsbytecode *parentPC;
+    JSScript *script;
+
+    /*
+     * Index into inlineFrames or OUTER_FRAME, matches this frame's index in
+     * the cross script SSA.
+     */
+    uint32_t inlineIndex;
+
+    /* JIT code generation tracking state */
+    size_t mainCodeStart;
+    size_t stubCodeStart;
+    size_t mainCodeEnd;
+    size_t stubCodeEnd;
+    size_t inlinePCOffset;
+
+    JSActiveFrame();
+};
+
 class Compiler : public BaseCompiler
 {
     friend class StubCompiler;
 
     struct BranchPatch {
         BranchPatch(const Jump &j, jsbytecode *pc, uint32_t inlineIndex)
           : jump(j), pc(pc), inlineIndex(inlineIndex)
         { }
@@ -350,36 +371,22 @@ class Compiler : public BaseCompiler
     FrameState frame;
 
     /*
      * State for the current stack frame, and links to its parents going up to
      * the outermost script.
      */
 
 public:
-    struct ActiveFrame {
-        ActiveFrame *parent;
-        jsbytecode *parentPC;
-        JSScript *script;
+    struct ActiveFrame : public JSActiveFrame {
         Label *jumpMap;
 
-        /*
-         * Index into inlineFrames or OUTER_FRAME, matches this frame's index
-         * in the cross script SSA.
-         */
-        uint32_t inlineIndex;
-
         /* Current types for non-escaping vars in the script. */
         VarType *varTypes;
 
-        /* JIT code generation tracking state */
-        size_t mainCodeStart;
-        size_t stubCodeStart;
-        size_t inlinePCOffset;
-
         /* State for managing return from inlined frames. */
         bool needReturnValue;          /* Return value will be used. */
         bool syncReturnValue;          /* Return value should be fully synced. */
         bool returnValueDouble;        /* Return value should be a double. */
         bool returnSet;                /* Whether returnRegister is valid. */
         AnyRegisterID returnRegister;  /* Register holding return value. */
         const FrameEntry *returnEntry; /* Entry copied by return value. */
         Vector<Jump, 4, CompilerAllocPolicy> *returnJumps;
@@ -465,17 +472,17 @@ private:
     bool inlining() { return inlining_; }
     bool constructing() { return isConstructing; }
 
     jsbytecode *outerPC() {
         if (a == outer)
             return PC;
         ActiveFrame *scan = a;
         while (scan && scan->parent != outer)
-            scan = scan->parent;
+            scan = static_cast<ActiveFrame *>(scan->parent);
         return scan->parentPC;
     }
 
     jsbytecode *inlinePC() { return PC; }
     uint32_t inlineIndex() { return a->inlineIndex; }
 
     Assembler &getAssembler(bool ool) { return ool ? stubcc.masm : masm; }
 
@@ -486,17 +493,17 @@ private:
         return callSites[index].inlinepc;
     }
 
     bool activeFrameHasMultipleExits() {
         ActiveFrame *na = a;
         while (na->parent) {
             if (na->exitState)
                 return true;
-            na = na->parent;
+            na = static_cast<ActiveFrame *>(na->parent);
         }
         return false;
     }
 
   private:
     CompileStatus performCompilation(JITScript **jitp);
     CompileStatus generatePrologue();
     CompileStatus generateMethod();