The table is now per-thread in a multi-threaded environment, and per-runtime otherwise. During code generation we merely allocate a loop table slot to each loop. Each thread will enlarge the table as needed in JSOP_HEADER.
authorAndreas Gal <gal@uci.edu>
Fri, 30 May 2008 18:58:43 -0700
changeset 17186 5e055a8c1fef2ef1038820bc2285eca4b53e14dd
parent 17185 0c74d1995a37ea4f9844bb86023994fffff6fb00
child 17187 1805b63c9f77b324f91468614f00ee6b9e4a4d71
push id1452
push usershaver@mozilla.com
push dateFri, 22 Aug 2008 00:08:22 +0000
treeherdermozilla-central@d13bb0868596 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.1a1pre
The table is now per-thread in a multi-threaded environment, and per-runtime otherwise. During code generation we merely allocate a loop table slot to each loop. Each thread will enlarge the table as needed in JSOP_HEADER.
js/src/jscntxt.h
js/src/jsemit.cpp
js/src/jsgc.cpp
js/src/jsinterp.cpp
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -120,20 +120,25 @@ struct JSThread {
      * or another Gecko application) that uses many contexts per thread is
      * unlikely to interleave js_GetSrcNote-intensive loops in the decompiler
      * among two or more contexts running script in one thread.
      */
     JSGSNCache          gsnCache;
 
     /* Property cache for faster call/get/set invocation. */
     JSPropertyCache     propertyCache;
+
+#ifdef JS_TRACER
+    JSTraceMonitor      traceMonitor;
+#endif    
 };
 
 #define JS_GSN_CACHE(cx)        ((cx)->thread->gsnCache)
 #define JS_PROPERTY_CACHE(cx)   ((cx)->thread->propertyCache)
+#define JS_TRACE_MONITOR(cx)    ((cx)->thread->traceMonitor)
 
 extern void JS_DLL_CALLBACK
 js_ThreadDestructorCB(void *ptr);
 
 extern JSBool
 js_SetContextThread(JSContext *cx);
 
 extern void
@@ -390,21 +395,33 @@ struct JSRuntime {
      * a longer script, then hit repeatedly as js_GetSrcNote is called during
      * the decompiler activation that filled it.
      */
     JSGSNCache          gsnCache;
 
     /* Property cache for faster call/get/set invocation. */
     JSPropertyCache     propertyCache;
 
+#ifdef JS_TRACER
+    JSTraceMonitor      traceMonitor;
+#endif    
+    
 #define JS_GSN_CACHE(cx)        ((cx)->runtime->gsnCache)
 #define JS_PROPERTY_CACHE(cx)   ((cx)->runtime->propertyCache)
+#define JS_TRACE_MONITOR(cx)    ((cx)->runtime->traceMonitor)
 #endif
 
     /*
+     * Loops are globally numbered (per runtime) using this counter. The actual
+     * loop table that tracks loop statistics is per-thread in a multi-threaded
+     * environment.
+     */
+    uint32              loopTableIndexGen;
+    
+    /*
      * Object shape (property cache structural type) identifier generator.
      *
      * Type 0 stands for the empty scope, and must not be regenerated due to
      * uint32 wrap-around. Since we use atomic pre-increment, the initial
      * value for the first typed non-empty scope will be 1.
      *
      * The GC compresses live types, minimizing rt->shapeGen in the process.
      * If this counter overflows into SHAPE_OVERFLOW_BIT (in jsinterp.h), the
@@ -477,20 +494,16 @@ struct JSRuntime {
      */
     JSBasicStats        hostenvScopeDepthStats;
     JSBasicStats        lexicalScopeDepthStats;
 #endif
 
 #ifdef JS_GCMETER
     JSGCStats           gcStats;
 #endif
-    
-#ifdef JS_TRACER
-    JSTraceMonitor      traceMonitor;
-#endif    
 };
 
 #ifdef DEBUG
 # define JS_RUNTIME_METER(rt, which)    JS_ATOMIC_INCREMENT(&(rt)->which)
 # define JS_RUNTIME_UNMETER(rt, which)  JS_ATOMIC_DECREMENT(&(rt)->which)
 #else
 # define JS_RUNTIME_METER(rt, which)    /* nothing */
 # define JS_RUNTIME_UNMETER(rt, which)  /* nothing */
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -3895,34 +3895,25 @@ EmitFunctionDefNop(JSContext *cx, JSCode
     return js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)index) >= 0 &&
            js_Emit1(cx, cg, JSOP_NOP) >= 0;
 }
 
 static JSBool
 EmitLoopHeader(JSContext *cx, JSCodeGenerator *cg)
 {
 #ifdef JS_TRACER    
-    JSTraceMonitor *tm = &cx->runtime->traceMonitor;
     ptrdiff_t off;
     jsbytecode *pc;
 
-    /* 
-     * Atomically increment the index generator and get us a unique index 
-     * number. If we get an index that exceeds the size of the loop table,
-     * we request for its size to be increased.
-     */
-    uint32 index = JS_ATOMIC_INCREMENT(&tm->loopIndexGen);
-    JS_ASSERT(index < JS_BITMASK(24));
-    if (index >= tm->loopTableSize) 
-        js_GrowLoopTableIfNeeded(cx->runtime, index);
+    uint32 slot = js_AllocateLoopTableSlot(cx->runtime);
     off = js_EmitN(cx, cg, JSOP_HEADER, 3);
     if (off < 0)
         return JS_FALSE;
     pc = CG_CODE(cg, off);
-    SET_UINT24(pc, index);
+    SET_UINT24(pc, slot);
 #endif    
     return JS_TRUE;
 }
 
 JSBool
 js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
 {
     JSBool ok, useful, wantval;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2875,20 +2875,21 @@ js_TraceRuntime(JSTracer *trc, JSBool al
 
     iter = NULL;
     while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL)
         js_TraceContext(trc, acx);
 
     if (rt->gcExtraRootsTraceOp)
         rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData);
 
-
+#ifndef JS_THREADSAFE
     /* Trace the loop table which can contain pointers to code objects. */
     JSTraceMonitor* tm = &rt->traceMonitor;
-    TRACE_JSVALS(trc, tm->loopIndexGen, tm->loopTable, "rt->traceMonitor.loopTable");
+    TRACE_JSVALS(trc, tm->loopTableSize, tm->loopTable, "rt->traceMonitor.loopTable");
+#endif    
 }
 
 static void
 ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr)
 {
     JSObject *obj, *pobj;
     uint32 slot;
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2796,29 +2796,44 @@ JS_INTERPRET(JSContext *cx)
 #if JS_HAS_XML_SUPPORT
           ADD_EMPTY_CASE(JSOP_STARTXML)
           ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
 #endif
           END_EMPTY_CASES
 
           BEGIN_CASE(JSOP_HEADER)
             i = GET_UINT24(regs.pc);
-            JS_ASSERT(((uint32)i) <= rt->traceMonitor.loopIndexGen);
+            JS_ASSERT((i > 0) && (i <= (jsint)rt->loopTableIndexGen));
+            JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx);
+            if (i >= (jsint)tm->loopTableSize) 
+              js_GrowLoopTableIfNeeded(cx, i);
             vp = &rt->traceMonitor.loopTable[i];
             rval = *vp;
-            if (JSVAL_IS_INT(rval)) { 
-                /* 
-                 * Try to atomically fast-increment (search for FAST_INC_DEC) 
-                 * the counter. If another thread beats us to this and
-                 * changes the slot to a tree pointer, undo our write (which
-                 * just replaced the pointer with a counter value).
+            if (JSVAL_IS_INT(rval)) {
+                /*
+                 * There are no concurrent writes to slots. This point in
+                 * the program is the only place a slot is updated from. 
                  */
-                lval = JS_ATOMIC_SET(vp, rval + 2);
-                if (!JSVAL_IS_INT(lval))
-                    JS_ATOMIC_SET(vp, lval);
+                if (rval >= TRACE_THRESHOLD) {
+                    /*
+                     * Once a thread hits the threshold, it should first consult
+                     * (read) the other threads' loop tables to see if anyone
+                     * already compiled a tree for us, and in that case reuse
+                     * that tree instead of recording a new one.
+                     */
+                    *vp = OBJECT_TO_JSVAL(js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0));
+                } else {
+                    /*
+                     * We use FAST_INC_DEC to increment the jsval counter, which
+                     * currently contains a jsint. *vp += 2 is equivalent to
+                     * INT_TO_JSVAL(JSVAL_TO_INT(*vp) + 1). JS_ATOMIC_ADD(v, 2)
+                     * is the atomic version of *vp += 2.
+                     */
+                    JS_ATOMIC_ADD(vp, 2);
+                }
             } else {
                 JS_ASSERT(JSVAL_IS_GCTHING(rval));
                 /* Execute the tree. */
             }
           END_CASE(JSOP_HEADER)
 
           /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
           TRACE_CASE(JSOP_LINENO)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -51,29 +51,41 @@ js_InitTracer(JSRuntime *rt) {
     return JS_TRUE;
 bad:
     return JS_FALSE;
 #else
     return JS_TRUE;
 #endif    
 }
 
+
+/*
+ * Allocate a new slot in the loop table. Slots are numbered globally
+ * (per-runtime), even if we run in a multi-threaded environment where
+ * the actual tables are maintained per thread.
+ */
+uint32 js_AllocateLoopTableSlot(JSRuntime *rt) {
+    uint32 slot = JS_ATOMIC_INCREMENT(&rt->loopTableIndexGen);
+    JS_ASSERT(slot < JS_BITMASK(24));
+    return slot;
+}
+
 /*
  * To grow the loop table that we take the traceMonitor lock, double check
  * that no other thread grew the table while we were deciding to grow the 
  * table, and only then double the size of the loop table. 
  * 
  * The initial size of the table is 2^8 and grows to at most 2^24 entries. 
  * It is extended at most a constant number of times (C=16) by doubling its 
  * size every time. When extending the table, each slot is initially filled 
  * with JS_ZERO.
  */
 void
-js_GrowLoopTableIfNeeded(JSRuntime* rt, uint32 index) {
-    JSTraceMonitor *tm = &rt->traceMonitor;
+js_GrowLoopTableIfNeeded(JSContext *cx, uint32 index) {
+    JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx);
     JS_ACQUIRE_LOCK(&tm->lock);
     uint32 oldSize;
     if (index >= (oldSize = tm->loopTableSize)) {
         uint32 newSize = oldSize << 1;
         jsval* t = tm->loopTable;
         if (t == NULL) {
             JS_ASSERT(oldSize == 0);
             newSize = 256;
new file mode 100644
--- /dev/null
+++ b/js/src/jstracer.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
+ * May 28, 2008.
+ *
+ * The Initial Developer of the Original Code is
+ *   Brendan Eich <brendan@mozilla.org
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef jstracer_h__
+#define jstracer_h__
+
+#include "jsstddef.h"
+#include "jslock.h"
+
+/* 
+ * Trace monitor. Every runtime is associated with a trace monitor that
+ * keeps track of loop frequencies for all JavaScript code loaded into
+ * that runtime. For this we use a loop table. Entries in the loop
+ * table are requested by jsemit.c during compilation. By using atomic
+ * pre-increment obtaining the next index is lock free, but to allocate
+ * more table space the trace monitor lock has to be aquired first.
+ * 
+ * The loop table also doubles as tree pointer table once a loop 
+ * achieves a certain number of iterations and we recorded a tree for
+ * that loop.
+ */
+struct JSTraceMonitor {
+#ifdef JS_THREADSAFE    
+    JSLock             *lock;
+#endif    
+    jsval              *loopTable;
+    uint32              loopTableSize;
+};
+
+#define TRACE_THRESHOLD 10
+
+JSBool js_InitTracer(JSRuntime *rt);
+uint32 js_AllocateLoopTableSlot(JSRuntime *rt);
+void   js_GrowLoopTableIfNeeded(JSContext *cx, uint32 index);
+
+#endif
\ No newline at end of file