Internalize and memoize FrameInfo pointers (bug 501398, r=brendan).
authorDavid Anderson <danderson@mozilla.com>
Wed, 30 Sep 2009 13:00:16 -0700
changeset 33563 89e665eb99446fed79e564034c539bfb08ec2414
parent 33562 8230525cfc3793d14aefb1653d65691d7ba339e0
child 33564 910f0c1ca2e5aff2814171749896d7b7fbf7e7f4
push id9581
push userrsayre@mozilla.com
push dateWed, 07 Oct 2009 06:47:58 +0000
treeherdermozilla-central@eb8a5ea7468c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs501398
milestone1.9.3a1pre
Internalize and memoize FrameInfo pointers (bug 501398, r=brendan).
js/src/jscntxt.h
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -121,16 +121,17 @@ typedef Queue<uint16> SlotList;
 #endif
 
 #define FRAGMENT_TABLE_SIZE 512
 struct VMFragment;
 
 #ifdef __cplusplus
 struct REHashKey;
 struct REHashFn;
+class FrameInfoCache;
 typedef nanojit::HashMap<REHashKey, nanojit::Fragment*, REHashFn> REHashMap;
 #endif
 
 #define MONITOR_N_GLOBAL_STATES 4
 struct GlobalState {
     JSObject*               globalObj;
     uint32                  globalShape;
     CLS(SlotList)           globalSlots;
@@ -172,16 +173,17 @@ struct JSTraceMonitor {
 
     CLS(VMAllocator)        dataAlloc;   /* A chunk allocator for fragments. */
     CLS(VMAllocator)        traceAlloc;  /* An allocator for trace metadata. */
     CLS(VMAllocator)        tempAlloc;   /* A temporary chunk allocator.  */
     CLS(nanojit::CodeAlloc) codeAlloc;   /* An allocator for native code. */
     CLS(nanojit::Assembler) assembler;
     CLS(nanojit::LirBuffer) lirbuf;
     CLS(nanojit::LirBuffer) reLirBuf;
+    CLS(FrameInfoCache)     frameCache;
 #ifdef DEBUG
     CLS(nanojit::LabelMap)  labels;
 #endif
 
     CLS(TraceRecorder)      recorder;
     jsval                   *reservedDoublePool;
     jsval                   *reservedDoublePoolPtr;
 
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -1137,16 +1137,104 @@ IsSlotUndemotable(JSContext* cx, TreeInf
 {
     if (slot < ti->nStackTypes)
         return oracle.isStackSlotUndemotable(cx, slot);
 
     uint16* gslots = ti->globalSlots->data();
     return oracle.isGlobalSlotUndemotable(cx, gslots[slot - ti->nStackTypes]);
 }
 
+class FrameInfoCache
+{
+    struct Entry : public JSDHashEntryHdr
+    {
+        FrameInfo *fi;
+    };
+
+    static JSBool
+    MatchFrameInfo(JSDHashTable *table, const JSDHashEntryHdr *entry, const void *key) {
+        const FrameInfo* fi1 = ((const Entry*)entry)->fi;
+        const FrameInfo* fi2 = (const FrameInfo*)key;
+        if (memcmp(fi1, fi2, sizeof(FrameInfo)) != 0)
+            return JS_FALSE;
+        return memcmp(fi1->get_typemap(), fi2->get_typemap(),
+                      fi1->callerHeight * sizeof(JSTraceType)) == 0;
+    }
+
+    static JSDHashNumber
+    HashFrameInfo(JSDHashTable *table, const void *key) {
+        FrameInfo* fi = (FrameInfo*)key;
+        size_t len = sizeof(FrameInfo) + fi->callerHeight * sizeof(JSTraceType);
+
+        JSDHashNumber h = 0;
+        const unsigned char *s = (const unsigned char*)fi;
+        for (size_t i = 0; i < len; i++, s++)
+            h = JS_ROTATE_LEFT32(h, 4) ^ *s;
+        return h;
+    }
+
+    static const JSDHashTableOps FrameCacheOps;
+
+    JSDHashTable *table;
+    VMAllocator *allocator;
+
+  public:
+    FrameInfoCache(VMAllocator *allocator) : allocator(allocator) {
+        init();
+    }
+
+    ~FrameInfoCache() {
+        clear();
+    }
+
+    void clear() {
+        if (table) {
+            JS_DHashTableDestroy(table);
+            table = NULL;
+        }
+    }
+
+    bool reset() {
+        clear();
+        return init();
+    }
+
+    bool init() {
+        table = JS_NewDHashTable(&FrameCacheOps, NULL, sizeof(Entry),
+                                 JS_DHASH_DEFAULT_CAPACITY(32));
+        return table != NULL;
+    }
+
+    FrameInfo *memoize(const FrameInfo *fi) {
+        Entry *entry = (Entry*)JS_DHashTableOperate(table, fi, JS_DHASH_ADD);
+        if (!entry)
+            return NULL;
+        if (!entry->fi) {
+            FrameInfo* n = (FrameInfo*)
+                allocator->alloc(sizeof(FrameInfo) + fi->callerHeight * sizeof(JSTraceType));
+            memcpy(n, fi, sizeof(FrameInfo) + fi->callerHeight * sizeof(JSTraceType));
+            entry->fi = n;
+        }
+        return entry->fi;
+    }
+};
+
+const JSDHashTableOps FrameInfoCache::FrameCacheOps =
+{
+    JS_DHashAllocTable,
+    JS_DHashFreeTable,
+    FrameInfoCache::HashFrameInfo,
+    FrameInfoCache::MatchFrameInfo,
+    JS_DHashMoveEntryStub,
+    JS_DHashClearEntryStub,
+    JS_DHashFinalizeStub,
+    NULL
+};
+
+
 struct PCHashEntry : public JSDHashEntryStub {
     size_t          count;
 };
 
 #define PC_HASH_COUNT 1024
 
 static void
 Blacklist(jsbytecode* pc)
@@ -2561,16 +2649,17 @@ JSTraceMonitor::flush()
         }
     )
 
     verbose_only(
         for (Seq<Fragment*>* f = branches; f; f = f->tail)
             js_FragProfiling_FragFinalizer(f->head, this);
     )
 
+    frameCache->reset();
     dataAlloc->reset();
     traceAlloc->reset();
     codeAlloc->reset();
 
     Allocator& alloc = *dataAlloc;
 
     for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) {
         globalStates[i].globalShape = -1;
@@ -2812,31 +2901,31 @@ public:
         debug_only_printf(LC_TMTracer, "global%d=", n);
         NativeToValue(mCx, *vp, *mTypeMap++, &mGlobal[slot]);
     }
 };
 
 class FlushNativeStackFrameVisitor : public SlotVisitorBase
 {
     JSContext *mCx;
-    JSTraceType *mTypeMap;
+    const JSTraceType *mTypeMap;
     double *mStack;
     jsval *mStop;
 public:
     FlushNativeStackFrameVisitor(JSContext *cx,
-                                 JSTraceType *typeMap,
+                                 const JSTraceType *typeMap,
                                  double *stack,
                                  jsval *stop) :
         mCx(cx),
         mTypeMap(typeMap),
         mStack(stack),
         mStop(stop)
     {}
 
-    JSTraceType* getTypeMap()
+    const JSTraceType* getTypeMap()
     {
         return mTypeMap;
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) {
         for (size_t i = 0; i < count; ++i) {
             if (vp == mStop)
@@ -3116,17 +3205,17 @@ GetClosureVar(JSContext* cx, JSObject* c
  *           on the stack are.
  * @param np pointer to the native stack.  We want to copy values from here to
  *           the JS stack as needed.
  * @param stopFrame if non-null, this frame and everything above it should not
  *                  be restored.
  * @return the number of things we popped off of np.
  */
 static JS_REQUIRES_STACK int
-FlushNativeStackFrame(JSContext* cx, unsigned callDepth, JSTraceType* mp, double* np,
+FlushNativeStackFrame(JSContext* cx, unsigned callDepth, const JSTraceType* mp, double* np,
                       JSStackFrame* stopFrame)
 {
     jsval* stopAt = stopFrame ? &stopFrame->argv[-2] : NULL;
 
     /* Root all string and object references first (we don't need to call the GC for this). */
     FlushNativeStackFrameVisitor visitor(cx, mp, np, stopAt);
     VisitStackSlots(visitor, cx, callDepth);
 
@@ -6296,17 +6385,17 @@ LeaveTree(InterpState& state, VMSideExit
         /* Peek at the callee native slot in the not-yet-synthesized down frame. */
         JSObject* callee = *(JSObject**)&stack[fi->callerHeight];
 
         /*
          * Synthesize a stack frame and write out the values in it using the
          * type map pointer on the native call stack.
          */
         SynthesizeFrame(cx, *fi, callee);
-        int slots = FlushNativeStackFrame(cx, 1 /* callDepth */, (JSTraceType*)(fi + 1),
+        int slots = FlushNativeStackFrame(cx, 1 /* callDepth */, (*callstack)->get_typemap(),
                                           stack, cx->fp);
 #ifdef DEBUG
         JSStackFrame* fp = cx->fp;
         debug_only_printf(LC_TMTracer,
                           "synthesized deep frame for %s:%u@%u, slots=%d\n",
                           fp->script->filename,
                           js_FramePCToLineNumber(cx, fp),
                           FramePCOffset(fp),
@@ -7159,16 +7248,17 @@ js_InitJIT(JSTraceMonitor *tm)
     }
 
     JS_ASSERT(!tm->dataAlloc && !tm->traceAlloc && !tm->codeAlloc);
     tm->dataAlloc = new VMAllocator();
     tm->traceAlloc = new VMAllocator();
     tm->tempAlloc = new VMAllocator();
     tm->reTempAlloc = new VMAllocator();
     tm->codeAlloc = new CodeAlloc();
+    tm->frameCache = new FrameInfoCache(tm->dataAlloc);
     tm->flush();
     verbose_only( tm->branches = NULL; )
 
     JS_ASSERT(!tm->reservedDoublePool);
     tm->reservedDoublePoolPtr = tm->reservedDoublePool = new jsval[MAX_NATIVE_STACK_SLOTS];
 
 #if !defined XP_WIN
     debug_only(memset(&jitstats, 0, sizeof(jitstats)));
@@ -7266,16 +7356,21 @@ js_FinishJIT(JSTraceMonitor *tm)
     }
 #endif
 
     memset(&tm->vmfragments[0], 0, FRAGMENT_TABLE_SIZE * sizeof(VMFragment*));
 
     delete[] tm->reservedDoublePool;
     tm->reservedDoublePool = tm->reservedDoublePoolPtr = NULL;
 
+    if (tm->frameCache) {
+        delete tm->frameCache;
+        tm->frameCache = NULL;
+    }
+
     if (tm->codeAlloc) {
         delete tm->codeAlloc;
         tm->codeAlloc = NULL;
     }
 
     if (tm->dataAlloc) {
         delete tm->dataAlloc;
         tm->dataAlloc = NULL;
@@ -11609,19 +11704,18 @@ TraceRecorder::interpretedFunctionCall(j
     if (argc < fun->nargs &&
         jsuword(fp->regs->sp + (fun->nargs - argc)) > cx->stackPool.current->limit) {
         RETURN_STOP("can't trace calls with too few args requiring argv move");
     }
 
     // Generate a type map for the outgoing frame and stash it in the LIR
     unsigned stackSlots = NativeStackSlots(cx, 0 /* callDepth */);
     FrameInfo* fi = (FrameInfo*)
-        traceMonitor->traceAlloc->alloc(sizeof(FrameInfo) +
-                                        stackSlots * sizeof(JSTraceType));
-    JSTraceType* typemap = reinterpret_cast<JSTraceType *>(fi + 1);
+        traceMonitor->tempAlloc->alloc(sizeof(FrameInfo) + stackSlots * sizeof(JSTraceType));
+    JSTraceType* typemap = (JSTraceType*)(fi + 1);
 
     DetermineTypesVisitor detVisitor(*this, typemap);
     VisitStackSlots(detVisitor, cx, 0);
 
     JS_ASSERT(argc < FrameInfo::CONSTRUCTING_FLAG);
 
     treeInfo->gcthings.addUnique(fval);
     fi->block = fp->blockChain;
@@ -11633,16 +11727,19 @@ TraceRecorder::interpretedFunctionCall(j
     fi->set_argc(argc, constructing);
     fi->callerHeight = NativeStackSlots(cx, 0) - (2 + argc);
     fi->callerArgc = fp->argc;
 
     unsigned callDepth = getCallDepth();
     if (callDepth >= treeInfo->maxCallDepth)
         treeInfo->maxCallDepth = callDepth + 1;
 
+    fi = traceMonitor->frameCache->memoize(fi);
+    if (!fi)
+        RETURN_STOP("out of memory");
     lir->insStorei(INS_CONSTPTR(fi), lirbuf->rp, callDepth * sizeof(FrameInfo*));
 
     atoms = fun->u.i.script->atomMap.vector;
     return RECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_CALL()
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -569,17 +569,17 @@ struct FrameInfo {
     enum { CONSTRUCTING_FLAG = 0x10000 };
     void   set_argc(uint16 argc, bool constructing) {
         this->argc = uint32(argc) | (constructing ? CONSTRUCTING_FLAG: 0);
     }
     uint16 get_argc() const { return uint16(argc & ~CONSTRUCTING_FLAG); }
     bool   is_constructing() const { return (argc & CONSTRUCTING_FLAG) != 0; }
 
     // The typemap just before the callee is called.
-    JSTraceType* get_typemap() { return (JSTraceType*) (this+1); }
+    const JSTraceType* get_typemap() const { return (JSTraceType*) (this+1); }
 };
 
 struct UnstableExit
 {
     nanojit::Fragment* fragment;
     VMSideExit* exit;
     UnstableExit* next;
 };