Bug 494864 - Make nanojit debug output easier to follow, r=graydon.
authorJulian Seward <jseward@acm.org>
Wed, 24 Jun 2009 20:32:00 -0700
changeset 29883 f694ffd22e6c0a527d14f642202317e18548fc83
parent 29882 147d92057b26826f8c048bdc41971279c7bc185b
child 29884 dc1593de081a48c1d79ef56f8811c554d37b4050
child 29907 8d6da1b2fb10552e4f82dbaa24d0634d953e27ac
push id7810
push userrsayre@mozilla.com
push dateTue, 30 Jun 2009 19:21:13 +0000
treeherdermozilla-central@83e105e5f0db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgraydon
bugs494864
milestone1.9.2a1pre
Bug 494864 - Make nanojit debug output easier to follow, r=graydon.
js/src/jsregexp.cpp
js/src/jstracer.cpp
js/src/jstracer.h
js/src/nanojit/Assembler.cpp
js/src/nanojit/Assembler.h
js/src/nanojit/Fragmento.cpp
js/src/nanojit/Fragmento.h
js/src/nanojit/LIR.cpp
js/src/nanojit/LIR.h
js/src/nanojit/Native.h
js/src/nanojit/NativeARM.cpp
js/src/nanojit/Nativei386.cpp
js/src/nanojit/Nativei386.h
js/src/nanojit/avmplus.h
js/src/nanojit/nanojit.h
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -2338,17 +2338,17 @@ class RegExpNativeCompiler {
         
         return JS_TRUE;
     }
 
     inline LIns*
     addName(LirBuffer* lirbuf, LIns* ins, const char* name)
     {
 #ifdef NJ_VERBOSE
-        debug_only_v(lirbuf->names->addName(ins, name);)
+        debug_only_stmt(lirbuf->names->addName(ins, name);)
 #endif
         return ins;
     }
 
     /*
      * Insert the side exit and guard record for a compiled regexp. Most
      * of the fields are not used. The important part is the regexp source
      * and flags, which we use as the fragment lookup key.
@@ -2396,21 +2396,28 @@ class RegExpNativeCompiler {
         LirBuffer* lirbuf = fragment->lirbuf;
         if (lirbuf->outOMem()) 
             goto fail;
         /* FIXME Use bug 463260 smart pointer when available. */
         lir = lirBufWriter = new (&gc) LirBufWriter(lirbuf);
 
         /* FIXME Use bug 463260 smart pointer when available. */
 #ifdef NJ_VERBOSE
-        debug_only_v(fragment->lirbuf->names = new (&gc) LirNameMap(&gc, fragmento->labels);)
+        debug_only_stmt(
+        fragment->lirbuf->names = new (&gc) LirNameMap(&gc, fragmento->labels);
+        )
 #endif
         /* FIXME Use bug 463260 smart pointer when available. */
 #ifdef NJ_VERBOSE
-        debug_only_v(lir = new (&gc) VerboseWriter(&gc, lir, lirbuf->names);)
+        debug_only_stmt(
+            if (js_LogController.lcbits & LC_TMRegexp) {
+                lir = new (&gc) VerboseWriter(&gc, lir, lirbuf->names,
+                                              &js_LogController);
+            }
+        )
 #endif
 
         lir->ins0(LIR_start);
         lirbuf->state = state = addName(lirbuf, lir->insParam(0, 0), "state");
         lirbuf->param1 = gdata = addName(lirbuf, lir->insParam(1, 0), "gdata");
         start = addName(lirbuf, lir->insLoad(LIR_ldp, lirbuf->param1, (int) offsetof(REGlobalData, skipped)), "start");
         cpend = addName(lirbuf, lir->insLoad(LIR_ldp, lirbuf->param1, offsetof(REGlobalData, cpend)), "cpend");
 
@@ -2429,31 +2436,33 @@ class RegExpNativeCompiler {
         ::compile(fragmento->assm(), fragment);
         if (fragmento->assm()->error() != nanojit::None) {
             oom = fragmento->assm()->error() == nanojit::OutOMem;
             goto fail;
         }
 
         delete lirBufWriter;
 #ifdef NJ_VERBOSE
-        debug_only_v(delete lir;)
+        debug_only_stmt( if (js_LogController.lcbits & LC_TMRegexp)
+                             delete lir; )
 #endif
         return JS_TRUE;
     fail:
         if (lirbuf->outOMem() || oom || 
             js_OverfullFragmento(&JS_TRACE_MONITOR(cx), fragmento)) {
             fragmento->clearFrags();
             lirbuf->rewind();
         } else {
             if (!guard) insertGuard(re_chars, re_length);
             fragment->blacklist();
         }
         delete lirBufWriter;
 #ifdef NJ_VERBOSE
-        debug_only_v(delete lir;)
+        debug_only_stmt( if (js_LogController.lcbits & LC_TMRegexp)
+                             delete lir; )
 #endif
         return JS_FALSE;
     }
 };
 
 /*
  * Compile a regexp to native code in the given fragment.
  */
@@ -3918,36 +3927,37 @@ MatchRegExp(REGlobalData *gData, REMatch
     NativeRegExp native;
 
     /* Run with native regexp if possible. */
     if (TRACING_ENABLED(gData->cx) && 
         (native = GetNativeRegExp(gData->cx, gData->regexp))) {
         gData->skipped = (ptrdiff_t) x->cp;
 
 #ifdef JS_JIT_SPEW
-        debug_only_v({
+        debug_only_stmt({
             VOUCH_DOES_NOT_REQUIRE_STACK();
             JSStackFrame *caller = (JS_ON_TRACE(gData->cx))
                                    ? NULL
                                    : js_GetScriptedCaller(gData->cx, NULL);
-            printf("entering REGEXP trace at %s:%u@%u, code: %p\n",
-                   caller ? caller->script->filename : "<unknown>",
-                   caller ? js_FramePCToLineNumber(gData->cx, caller) : 0,
-                   caller ? FramePCOffset(caller) : 0,
-                   JS_FUNC_TO_DATA_PTR(void *, native));
+            debug_only_printf(LC_TMRegexp,
+                              "entering REGEXP trace at %s:%u@%u, code: %p\n",
+                              caller ? caller->script->filename : "<unknown>",
+                              caller ? js_FramePCToLineNumber(gData->cx, caller) : 0,
+                              caller ? FramePCOffset(caller) : 0,
+                              JS_FUNC_TO_DATA_PTR(void *, native));
         })
 #endif
 
 #if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32)
         SIMULATE_FASTCALL(result, x, gData, native);
 #else
         result = native(x, gData);
 #endif
 
-        debug_only_v(printf("leaving REGEXP trace\n"));
+        debug_only_print0(LC_TMRegexp, "leaving  REGEXP trace\n");
 
         gData->skipped = ((const jschar *) gData->skipped) - cp;
         return result;
     }
 #endif
     /*
      * Have to include the position beyond the last character
      * in order to detect end-of-input/line condition.
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -153,24 +153,22 @@ static const char tagChar[]  = "OIDISIBI
 #define CHECK_STATUS(expr)                                                    \
     JS_BEGIN_MACRO                                                            \
         JSRecordingStatus _status = (expr);                                   \
         if (_status != JSRS_CONTINUE)                                        \
           return _status;                                                     \
     JS_END_MACRO
 
 #ifdef JS_JIT_SPEW
-#define debug_only_a(x) if (js_verboseAbort || js_verboseDebug ) { x; }
 #define ABORT_TRACE_RV(msg, value)                                    \
     JS_BEGIN_MACRO                                                            \
-        debug_only_a(fprintf(stdout, "abort: %d: %s\n", __LINE__, (msg));)    \
+        debug_only_printf(LC_TMAbort, "abort: %d: %s\n", __LINE__, (msg));    \
         return (value);                                                       \
     JS_END_MACRO
 #else
-#define debug_only_a(x)
 #define ABORT_TRACE_RV(msg, value)   return (value)
 #endif
 
 #define ABORT_TRACE(msg)         ABORT_TRACE_RV(msg, JSRS_STOP)
 #define ABORT_TRACE_ERROR(msg)   ABORT_TRACE_RV(msg, JSRS_ERROR)
 
 #ifdef JS_JIT_SPEW
 struct __jitstats {
@@ -264,24 +262,106 @@ static GC gc = GC();
 static avmplus::AvmCore s_core = avmplus::AvmCore();
 static avmplus::AvmCore* core = &s_core;
 
 #ifdef JS_JIT_SPEW
 void
 js_DumpPeerStability(JSTraceMonitor* tm, const void* ip, JSObject* globalObj, uint32 globalShape, uint32 argc);
 #endif
 
-/* We really need a better way to configure the JIT. Shaver, where is my fancy JIT object? */
+/* We really need a better way to configure the JIT. Shaver, where is
+   my fancy JIT object? */
+/* NB: this is raced on, if jstracer.cpp should ever be running MT.
+   I think it's harmless tho. */
 static bool did_we_check_processor_features = false;
 
+/* ------ Debug logging control ------ */
+
+/* All the logging control stuff lives in here.  It is shared between
+   all threads, but I think that's OK. */
+LogControl js_LogController;
+
 #ifdef JS_JIT_SPEW
-bool js_verboseDebug = getenv("TRACEMONKEY") && strstr(getenv("TRACEMONKEY"), "verbose");
-bool js_verboseStats = js_verboseDebug ||
-    (getenv("TRACEMONKEY") && strstr(getenv("TRACEMONKEY"), "stats"));
-bool js_verboseAbort = getenv("TRACEMONKEY") && strstr(getenv("TRACEMONKEY"), "abort");
+
+/* NB: this is raced on too, if jstracer.cpp should ever be running MT.
+   Also harmless. */
+static bool did_we_set_up_debug_logging = false;
+
+static void
+js_InitJITLogController ( void )
+{
+    char *tm, *tmf;
+    uint32_t bits;
+
+    js_LogController.lcbits = 0;
+
+    tm = getenv("TRACEMONKEY");
+    if (tm) goto help;
+
+    tmf = getenv("TMFLAGS");
+    if (!tmf) return;
+
+    /* This is really a cheap hack as far as flag decoding goes. */
+    if (strstr(tmf, "help")) goto help;
+
+    bits = 0;
+    /* flags for jstracer.cpp */
+    if (strstr(tmf, "minimal"))     bits |= LC_TMMinimal;
+    if (strstr(tmf, "tracer"))      bits |= LC_TMTracer;
+    if (strstr(tmf, "recorder"))    bits |= LC_TMRecorder;
+    if (strstr(tmf, "patcher"))     bits |= LC_TMPatcher;
+    if (strstr(tmf, "abort"))       bits |= LC_TMAbort;
+    if (strstr(tmf, "stats"))       bits |= LC_TMStats;
+    if (strstr(tmf, "regexp"))      bits |= LC_TMRegexp;
+    /* flags for nanojit */
+    if (strstr(tmf, "liveness"))    bits |= LC_Liveness;
+    if (strstr(tmf, "readlir"))     bits |= LC_ReadLIR;
+    if (strstr(tmf, "aftersf_sp"))  bits |= LC_AfterSF_SP;
+    if (strstr(tmf, "aftersf_rp"))  bits |= LC_AfterSF_RP;
+    if (strstr(tmf, "afterdeadf"))  bits |= LC_AfterDeadF;
+    if (strstr(tmf, "regalloc"))    bits |= LC_RegAlloc;
+    if (strstr(tmf, "assembly"))    bits |= LC_Assembly;
+    if (strstr(tmf, "nocodeaddrs")) bits |= LC_NoCodeAddrs;
+
+    js_LogController.lcbits = bits;
+    return;
+
+   help:
+    fflush(NULL);
+    printf("\n");
+    printf("Debug output control help summary for TraceMonkey:\n");
+    printf("\n");
+    printf("TRACEMONKEY= is no longer used; use TMFLAGS= "
+           "instead.\n");
+    printf("\n");
+    printf("usage: TMFLAGS=option,option,option,... where options can be:\n");
+    printf("   help         show this message\n");
+    printf("   ------ options for jstracer & jsregexp ------\n");
+    printf("   minimal      ultra-minimalist output; try this first\n");
+    printf("   tracer       tracer lifetime (FIXME:better description)\n");
+    printf("   recorder     trace recording stuff (FIXME:better description)\n");
+    printf("   patcher      patching stuff (FIXME:better description)\n");
+    printf("   abort        show trace recording aborts\n");
+    printf("   stats        show trace recording stats\n");
+    printf("   regexp       show compilation & entry for regexps\n");
+    printf("   ------ options for Nanojit ------\n");
+    printf("   liveness     show LIR liveness at start of rdr pipeline\n");
+    printf("   readlir      show LIR as it enters the reader pipeline\n");
+    printf("   aftersf_sp   show LIR after StackFilter(sp)\n");
+    printf("   aftersf_rp   show LIR after StackFilter(rp)\n");
+    printf("   afterdeadf   show LIR after DeadCodeFilter\n");
+    printf("   regalloc     show regalloc details\n");
+    printf("   assembly     show final aggregated assembly code\n");
+    printf("   nocodeaddrs  don't show code addresses in assembly listings\n");
+    printf("\n");
+    printf("Exiting now.  Bye.\n");
+    printf("\n");
+    exit(0);
+    /*NOTREACHED*/
+}
 #endif
 
 /* The entire VM shares one oracle. Collisions and concurrent updates are tolerated and worst
    case cause performance regressions. */
 static Oracle oracle;
 
 Tracker::Tracker()
 {
@@ -1345,31 +1425,33 @@ public:
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) {
             uint8 type = getCoercedType(*vp);
             if ((type == JSVAL_INT) &&
                 oracle.isGlobalSlotUndemotable(mCx, slot))
                 type = JSVAL_DOUBLE;
             JS_ASSERT(type != JSVAL_BOXED);
-            debug_only_v(nj_dprintf("capture type global%d: %d=%c\n",
-                                    n, type, typeChar[type]);)
+            debug_only_printf(LC_TMTracer,
+                              "capture type global%d: %d=%c\n",
+                              n, type, typeChar[type]);
             *mPtr++ = type;
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(jsval *vp, int count, JSStackFrame* fp) {
         for (int i = 0; i < count; ++i) {
             uint8 type = getCoercedType(vp[i]);
             if ((type == JSVAL_INT) &&
                 oracle.isStackSlotUndemotable(mCx, length()))
                 type = JSVAL_DOUBLE;
             JS_ASSERT(type != JSVAL_BOXED);
-            debug_only_v(nj_dprintf("capture type %s%d: %d=%c\n",
-                                    stackSlotKind(), i, type, typeChar[type]);)
+            debug_only_printf(LC_TMTracer,
+                              "capture type %s%d: %d=%c\n",
+                              stackSlotKind(), i, type, typeChar[type]);
             *mPtr++ = type;
         }
         return true;
     }
 
     JS_ALWAYS_INLINE uintptr_t length() {
         return mPtr - mTypeMap;
     }
@@ -1473,22 +1555,32 @@ TraceRecorder::TraceRecorder(JSContext* 
     this->wasRootFragment = _fragment == _fragment->root;
     this->outer = outer;
     this->outerArgc = outerArgc;
     this->pendingTraceableNative = NULL;
     this->newobj_ins = NULL;
     this->generatedTraceableNative = new JSTraceableNative();
     JS_ASSERT(generatedTraceableNative);
 
-    debug_only_v(nj_dprintf("recording starting from %s:%u@%u\n",
-                            ti->treeFileName, ti->treeLineNumber, ti->treePCOffset);)
-    debug_only_v(nj_dprintf("globalObj=%p, shape=%d\n", (void*)this->globalObj, OBJ_SHAPE(this->globalObj));)
+#ifdef JS_JIT_SPEW
+    debug_only_print0(LC_TMMinimal, "\n");
+    debug_only_printf(LC_TMMinimal, "Recording starting from %s:%u@%u\n",
+                      ti->treeFileName, ti->treeLineNumber, ti->treePCOffset);
+
+    debug_only_printf(LC_TMTracer, "globalObj=%p, shape=%d\n",
+                      (void*)this->globalObj, OBJ_SHAPE(this->globalObj));
+#endif
 
     lir = lir_buf_writer = new (&gc) LirBufWriter(lirbuf);
-    debug_only_v(lir = verbose_filter = new (&gc) VerboseWriter(&gc, lir, lirbuf->names);)
+    debug_only_stmt(
+        if (js_LogController.lcbits & LC_TMRecorder) {
+           lir = verbose_filter
+               = new (&gc) VerboseWriter(&gc, lir, lirbuf->names, &js_LogController);
+        }
+    )
     if (nanojit::AvmCore::config.soft_float)
         lir = float_filter = new (&gc) SoftFloatFilter(lir);
     else
         float_filter = 0;
     lir = cse_filter = new (&gc) CseFilter(lir, &gc);
     lir = expr_filter = new (&gc) ExprFilter(lir);
     lir = func_filter = new (&gc) FuncFilter(lir);
     lir->ins0(LIR_start);
@@ -1561,43 +1653,45 @@ TraceRecorder::~TraceRecorder()
             js_TrashTree(cx, fragment->root);
 
         for (unsigned int i = 0; i < whichTreesToTrash.length(); i++)
             js_TrashTree(cx, whichTreesToTrash.get(i));
     } else if (wasRootFragment) {
         delete treeInfo;
     }
 #ifdef DEBUG
-    delete verbose_filter;
+    debug_only_stmt( delete verbose_filter; )
 #endif
     delete cse_filter;
     delete expr_filter;
     delete func_filter;
     delete float_filter;
     delete lir_buf_writer;
     delete generatedTraceableNative;
 }
 
 void TraceRecorder::removeFragmentoReferences()
 {
     fragment = NULL;
 }
 
 void TraceRecorder::deepAbort()
 {
-    debug_only_v(nj_dprintf("deep abort");)
+    debug_only_print0(LC_TMTracer|LC_TMAbort, "deep abort");
     deepAborted = true;
 }
 
 /* Add debug information to a LIR instruction as we emit it. */
 inline LIns*
 TraceRecorder::addName(LIns* ins, const char* name)
 {
 #ifdef JS_JIT_SPEW
-    if (js_verboseDebug)
+    /* We'll only ask for verbose Nanojit when .lcbits > 0, so
+       there's no point in adding names otherwise. */
+    if (js_LogController.lcbits > 0)
         lirbuf->names->addName(ins, name);
 #endif
     return ins;
 }
 
 /* Determine the current call depth (starting with the entry frame.) */
 unsigned
 TraceRecorder::getCallDepth() const
@@ -1657,70 +1751,72 @@ static void
 ValueToNative(JSContext* cx, jsval v, uint8 type, double* slot)
 {
     unsigned tag = JSVAL_TAG(v);
     switch (type) {
       case JSVAL_OBJECT:
         JS_ASSERT(tag == JSVAL_OBJECT);
         JS_ASSERT(!JSVAL_IS_NULL(v) && !HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v)));
         *(JSObject**)slot = JSVAL_TO_OBJECT(v);
-        debug_only_v(nj_dprintf("object<%p:%s> ", (void*)JSVAL_TO_OBJECT(v),
-                                JSVAL_IS_NULL(v)
-                                ? "null"
-                                : STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name);)
+        debug_only_printf(LC_TMTracer,
+                          "object<%p:%s> ", (void*)JSVAL_TO_OBJECT(v),
+                          JSVAL_IS_NULL(v)
+                          ? "null"
+                          : STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name);
         return;
       case JSVAL_INT:
         jsint i;
         if (JSVAL_IS_INT(v))
             *(jsint*)slot = JSVAL_TO_INT(v);
         else if ((tag == JSVAL_DOUBLE) && JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i))
             *(jsint*)slot = i;
         else
             JS_ASSERT(JSVAL_IS_INT(v));
-        debug_only_v(nj_dprintf("int<%d> ", *(jsint*)slot);)
+        debug_only_printf(LC_TMTracer, "int<%d> ", *(jsint*)slot);
         return;
       case JSVAL_DOUBLE:
         jsdouble d;
         if (JSVAL_IS_INT(v))
             d = JSVAL_TO_INT(v);
         else
             d = *JSVAL_TO_DOUBLE(v);
         JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v));
         *(jsdouble*)slot = d;
-        debug_only_v(nj_dprintf("double<%g> ", d);)
+        debug_only_printf(LC_TMTracer, "double<%g> ", d);
         return;
       case JSVAL_BOXED:
         JS_NOT_REACHED("found boxed type in an entry type map");
         return;
       case JSVAL_STRING:
         JS_ASSERT(tag == JSVAL_STRING);
         *(JSString**)slot = JSVAL_TO_STRING(v);
-        debug_only_v(nj_dprintf("string<%p> ", (void*)(*(JSString**)slot));)
+        debug_only_printf(LC_TMTracer, "string<%p> ", (void*)(*(JSString**)slot));
         return;
       case JSVAL_TNULL:
         JS_ASSERT(tag == JSVAL_OBJECT);
         *(JSObject**)slot = NULL;
-        debug_only_v(nj_dprintf("null ");)
+        debug_only_print0(LC_TMTracer, "null ");
         return;
       case JSVAL_BOOLEAN:
         /* Watch out for pseudo-booleans. */
         JS_ASSERT(tag == JSVAL_BOOLEAN);
         *(JSBool*)slot = JSVAL_TO_PSEUDO_BOOLEAN(v);
-        debug_only_v(nj_dprintf("boolean<%d> ", *(JSBool*)slot);)
+        debug_only_printf(LC_TMTracer, "boolean<%d> ", *(JSBool*)slot);
         return;
       case JSVAL_TFUN: {
         JS_ASSERT(tag == JSVAL_OBJECT);
         JSObject* obj = JSVAL_TO_OBJECT(v);
         *(JSObject**)slot = obj;
 #ifdef DEBUG
         JSFunction* fun = GET_FUNCTION_PRIVATE(cx, obj);
-        debug_only_v(nj_dprintf("function<%p:%s> ", (void*) obj,
-                                fun->atom
-                                ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom))
-                                : "unnamed");)
+        debug_only_printf(LC_TMTracer,
+                          "function<%p:%s> ", (void*) obj,
+                          fun->atom
+                          ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom))
+                          : "unnamed");
 #endif
         return;
       }
     }
 
     JS_NOT_REACHED("unexpected type");
 }
 
@@ -1789,34 +1885,35 @@ NativeToValue(JSContext* cx, jsval& v, u
 {
     jsint i;
     jsdouble d;
     switch (type) {
       case JSVAL_OBJECT:
         v = OBJECT_TO_JSVAL(*(JSObject**)slot);
         JS_ASSERT(JSVAL_TAG(v) == JSVAL_OBJECT); /* if this fails the pointer was not aligned */
         JS_ASSERT(v != JSVAL_ERROR_COOKIE); /* don't leak JSVAL_ERROR_COOKIE */
-        debug_only_v(nj_dprintf("object<%p:%s> ", (void*)JSVAL_TO_OBJECT(v),
-                                JSVAL_IS_NULL(v)
-                                ? "null"
-                                : STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name);)
+        debug_only_printf(LC_TMTracer, 
+                          "object<%p:%s> ", (void*)JSVAL_TO_OBJECT(v),
+                          JSVAL_IS_NULL(v)
+                          ? "null"
+                          : STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name);
         break;
       case JSVAL_INT:
         i = *(jsint*)slot;
-        debug_only_v(nj_dprintf("int<%d> ", i);)
+        debug_only_printf(LC_TMTracer, "int<%d> ", i);
       store_int:
         if (INT_FITS_IN_JSVAL(i)) {
             v = INT_TO_JSVAL(i);
             break;
         }
         d = (jsdouble)i;
         goto store_double;
       case JSVAL_DOUBLE:
         d = *slot;
-        debug_only_v(nj_dprintf("double<%g> ", d);)
+        debug_only_printf(LC_TMTracer, "double<%g> ", d);
         if (JSDOUBLE_IS_INT(d, i))
             goto store_int;
       store_double: {
         /* Its not safe to trigger the GC here, so use an emergency heap if we are out of
            double boxes. */
         if (cx->doubleFreeList) {
 #ifdef DEBUG
             JSBool ok =
@@ -1828,42 +1925,43 @@ NativeToValue(JSContext* cx, jsval& v, u
         v = AllocateDoubleFromReservedPool(cx);
         JS_ASSERT(JSVAL_IS_DOUBLE(v) && *JSVAL_TO_DOUBLE(v) == 0.0);
         *JSVAL_TO_DOUBLE(v) = d;
         return;
       }
       case JSVAL_BOXED:
         v = *(jsval*)slot;
         JS_ASSERT(v != JSVAL_ERROR_COOKIE); /* don't leak JSVAL_ERROR_COOKIE */
-        debug_only_v(nj_dprintf("box<%p> ", (void*)v));
+        debug_only_printf(LC_TMTracer, "box<%p> ", (void*)v);
         break;
       case JSVAL_STRING:
         v = STRING_TO_JSVAL(*(JSString**)slot);
         JS_ASSERT(JSVAL_TAG(v) == JSVAL_STRING); /* if this fails the pointer was not aligned */
-        debug_only_v(nj_dprintf("string<%p> ", (void*)(*(JSString**)slot));)
+        debug_only_printf(LC_TMTracer, "string<%p> ", (void*)(*(JSString**)slot));
         break;
       case JSVAL_TNULL:
         JS_ASSERT(*(JSObject**)slot == NULL);
         v = JSVAL_NULL;
-        debug_only_v(nj_dprintf("null<%p> ", (void*)(*(JSObject**)slot)));
+        debug_only_printf(LC_TMTracer, "null<%p> ", (void*)(*(JSObject**)slot));
         break;
       case JSVAL_BOOLEAN:
         /* Watch out for pseudo-booleans. */
         v = PSEUDO_BOOLEAN_TO_JSVAL(*(JSBool*)slot);
-        debug_only_v(nj_dprintf("boolean<%d> ", *(JSBool*)slot);)
+        debug_only_printf(LC_TMTracer, "boolean<%d> ", *(JSBool*)slot);
         break;
       case JSVAL_TFUN: {
         JS_ASSERT(HAS_FUNCTION_CLASS(*(JSObject**)slot));
         v = OBJECT_TO_JSVAL(*(JSObject**)slot);
 #ifdef DEBUG
         JSFunction* fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v));
-        debug_only_v(nj_dprintf("function<%p:%s> ", (void*)JSVAL_TO_OBJECT(v),
-                                fun->atom
-                                ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom))
-                                : "unnamed");)
+        debug_only_printf(LC_TMTracer,
+                          "function<%p:%s> ", (void*)JSVAL_TO_OBJECT(v),
+                          fun->atom
+                          ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom))
+                          : "unnamed");
 #endif
         break;
       }
     }
 }
 
 class BuildNativeFrameVisitor : public SlotVisitorBase
 {
@@ -1879,38 +1977,38 @@ public:
         mCx(cx),
         mTypeMap(typemap),
         mGlobal(global),
         mStack(stack)
     {}
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) {
-        debug_only_v(nj_dprintf("global%d: ", n);)
+        debug_only_printf(LC_TMTracer, "global%d: ", n);
         ValueToNative(mCx, *vp, *mTypeMap++, &mGlobal[slot]);
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(jsval *vp, int count, JSStackFrame* fp) {
         for (int i = 0; i < count; ++i) {
-            debug_only_v(nj_dprintf("%s%d: ", stackSlotKind(), i);)
+            debug_only_printf(LC_TMTracer, "%s%d: ", stackSlotKind(), i);
             ValueToNative(mCx, *vp++, *mTypeMap++, mStack++);
         }
         return true;
     }
 };
 
 static JS_REQUIRES_STACK void
 BuildNativeFrame(JSContext *cx, JSObject *globalObj, unsigned callDepth,
                  unsigned ngslots, uint16 *gslots,
                  uint8 *typeMap, double *global, double *stack)
 {
     BuildNativeFrameVisitor visitor(cx, typeMap, global, stack);
     VisitSlots(visitor, cx, globalObj, callDepth, ngslots, gslots);
-    debug_only_v(nj_dprintf("\n");)
+    debug_only_print0(LC_TMTracer, "\n");
 }
 
 class FlushNativeGlobalFrameVisitor : public SlotVisitorBase
 {
     JSContext *mCx;
     uint8 *mTypeMap;
     double *mGlobal;
 public:
@@ -1919,17 +2017,17 @@ public:
                                   double *global) :
         mCx(cx),
         mTypeMap(typeMap),
         mGlobal(global)
     {}
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) {
-        debug_only_v(nj_dprintf("global%d=", n);)
+        debug_only_printf(LC_TMTracer, "global%d=", n);
         NativeToValue(mCx, *vp, *mTypeMap++, &mGlobal[slot]);
     }
 };
 
 class FlushNativeStackFrameVisitor : public SlotVisitorBase
 {
     JSContext *mCx;
     uint8 *mTypeMap;
@@ -1951,32 +2049,32 @@ public:
         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)
                 return false;
-            debug_only_v(nj_dprintf("%s%u=", stackSlotKind(), unsigned(i));)
+            debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), unsigned(i));
             NativeToValue(mCx, *vp++, *mTypeMap++, mStack++);
         }
         return true;
     }
 };
 
 /* Box the given native frame into a JS frame. This is infallible. */
 static JS_REQUIRES_STACK void
 FlushNativeGlobalFrame(JSContext *cx, double *global, unsigned ngslots,
                        uint16 *gslots, uint8 *typemap)
 {
     FlushNativeGlobalFrameVisitor visitor(cx, typemap, global);
     JSObject *globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain);
     VisitGlobalSlots(visitor, cx, globalObj, ngslots, gslots);
-    debug_only_v(nj_dprintf("\n");)
+    debug_only_print0(LC_TMTracer, "\n");
 }
 
 /*
  * Generic function to read upvars on trace.
  *     T   Traits type parameter. Must provide static functions:
  *             interp_get(fp, slot)     Read the value out of an interpreter frame.
  *             native_slot(argc, slot)  Return the position of the desired value in the on-trace
  *                                      stack frame (with position 0 being callee).
@@ -2181,17 +2279,17 @@ FlushNativeStackFrame(JSContext* cx, uns
                     }
                 }
                 fp->thisp = JSVAL_TO_OBJECT(fp->argv[-1]);
                 if (fp->flags & JSFRAME_CONSTRUCTING) // constructors always compute 'this'
                     fp->flags |= JSFRAME_COMPUTED_THIS;
             }
         }
     }
-    debug_only_v(nj_dprintf("\n");)
+    debug_only_print0(LC_TMTracer, "\n");
     return visitor.getTypeMap() - mp;
 }
 
 /* Emit load instructions onto the trace that read the initial stack state. */
 JS_REQUIRES_STACK void
 TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8 t,
                       const char *prefix, uintN index, JSStackFrame *fp)
 {
@@ -2245,18 +2343,18 @@ TraceRecorder::import(LIns* base, ptrdif
 
     if (mark)
         JS_ARENA_RELEASE(&cx->tempPool, mark);
     addName(ins, name);
 
     static const char* typestr[] = {
         "object", "int", "double", "boxed", "string", "null", "boolean", "function"
     };
-    debug_only_v(nj_dprintf("import vp=%p name=%s type=%s flags=%d\n",
-                            (void*)p, name, typestr[t & 7], t >> 3);)
+    debug_only_printf(LC_TMTracer, "import vp=%p name=%s type=%s flags=%d\n",
+                      (void*)p, name, typestr[t & 7], t >> 3);
 #endif
 }
 
 class ImportGlobalSlotVisitor : public SlotVisitorBase
 {
     TraceRecorder &mRecorder;
     LIns *mBase;
     uint8 *mTypemap;
@@ -2505,17 +2603,18 @@ TraceRecorder::known(jsval* p)
  * The dslots of the global object are sometimes reallocated by the interpreter.
  * This function check for that condition and re-maps the entries of the tracker
  * accordingly.
  */
 JS_REQUIRES_STACK void
 TraceRecorder::checkForGlobalObjectReallocation()
 {
     if (global_dslots != globalObj->dslots) {
-        debug_only_v(nj_dprintf("globalObj->dslots relocated, updating tracker\n");)
+        debug_only_print0(LC_TMTracer,
+                          "globalObj->dslots relocated, updating tracker\n");
         jsval* src = global_dslots;
         jsval* dst = globalObj->dslots;
         jsuint length = globalObj->dslots[-1] - JS_INITIAL_NSLOTS;
         LIns** map = (LIns**)alloca(sizeof(LIns*) * length);
         for (jsuint n = 0; n < length; ++n) {
             map[n] = tracker.get(src);
             tracker.set(src++, NULL);
         }
@@ -2836,19 +2935,20 @@ TraceRecorder::createGuardRecord(VMSideE
 
 /*
  * Emit a guard for condition (cond), expecting to evaluate to boolean result
  * (expected) and using the supplied side exit if the conditon doesn't hold.
  */
 JS_REQUIRES_STACK void
 TraceRecorder::guard(bool expected, LIns* cond, VMSideExit* exit)
 {
-    debug_only_v(nj_dprintf("    About to try emitting guard code for "
-                            "SideExit=%p exitType=%d\n",
-                            (void*)exit, exit->exitType);)
+    debug_only_printf(LC_TMRecorder,
+                      "    About to try emitting guard code for "
+                      "SideExit=%p exitType=%d\n",
+                      (void*)exit, exit->exitType);
 
     LIns* guardRec = createGuardRecord(exit);
 
     /*
      * BIG FAT WARNING: If compilation fails we don't reset the lirbuf, so it's
      * safe to keep references to the side exits here. If we ever start
      * rewinding those lirbufs, we have to make sure we purge the side exits
      * that then no longer will be in valid memory.
@@ -2859,17 +2959,18 @@ TraceRecorder::guard(bool expected, LIns
     if (!cond->isCond()) {
         expected = !expected;
         cond = lir->ins_eq0(cond);
     }
 
     LIns* guardIns =
         lir->insGuard(expected ? LIR_xf : LIR_xt, cond, guardRec);
     if (!guardIns) {
-        debug_only_v(nj_dprintf("    redundant guard, eliminated, no codegen\n");)
+        debug_only_print0(LC_TMRecorder,
+                          "    redundant guard, eliminated, no codegen\n");
     }
 }
 
 JS_REQUIRES_STACK VMSideExit*
 TraceRecorder::copy(VMSideExit* copy)
 {
     size_t typemap_size = copy->numGlobalSlots + copy->numStackSlots;
     LIns* data = lir->insSkip(sizeof(VMSideExit) + typemap_size * sizeof(uint8));
@@ -2910,41 +3011,43 @@ TraceRecorder::guard(bool expected, LIns
  * @param stage_count   Outparam for set() buffer count.
  * @return              True if types are compatible, false otherwise.
  */
 JS_REQUIRES_STACK bool
 TraceRecorder::checkType(jsval& v, uint8 t, jsval*& stage_val, LIns*& stage_ins,
                          unsigned& stage_count)
 {
     if (t == JSVAL_INT) { /* initially all whole numbers cause the slot to be demoted */
-        debug_only_v(nj_dprintf("checkType(tag=1, t=%d, isnum=%d, i2f=%d) stage_count=%d\n",
-                                t,
-                                isNumber(v),
-                                isPromoteInt(get(&v)),
-                                stage_count);)
+        debug_only_printf(LC_TMTracer,
+                          "checkType(tag=1, t=%d, isnum=%d, i2f=%d) stage_count=%d\n",
+                          t,
+                          isNumber(v),
+                          isPromoteInt(get(&v)),
+                          stage_count);
         if (!isNumber(v))
             return false; /* not a number? type mismatch */
         LIns* i = get(&v);
         /* This is always a type mismatch, we can't close a double to an int. */
         if (!isPromoteInt(i))
             return false;
         /* Looks good, slot is an int32, the last instruction should be promotable. */
         JS_ASSERT(isInt32(v) && isPromoteInt(i));
         /* Overwrite the value in this slot with the argument promoted back to an integer. */
         stage_val = &v;
         stage_ins = f2i(i);
         stage_count++;
         return true;
     }
     if (t == JSVAL_DOUBLE) {
-        debug_only_v(nj_dprintf("checkType(tag=2, t=%d, isnum=%d, promote=%d) stage_count=%d\n",
-                                t,
-                                isNumber(v),
-                                isPromoteInt(get(&v)),
-                                stage_count);)
+        debug_only_printf(LC_TMTracer,
+                          "checkType(tag=2, t=%d, isnum=%d, promote=%d) stage_count=%d\n",
+                          t,
+                          isNumber(v),
+                          isPromoteInt(get(&v)),
+                          stage_count);
         if (!isNumber(v))
             return false; /* not a number? type mismatch */
         LIns* i = get(&v);
         /* We sink i2f conversions into the side exit, but at the loop edge we have to make
            sure we promote back to double if at loop entry we want a double. */
         if (isPromoteInt(i)) {
             stage_val = &v;
             stage_ins = lir->ins1(LIR_i2f, i);
@@ -2958,22 +3061,22 @@ TraceRecorder::checkType(jsval& v, uint8
         return !JSVAL_IS_PRIMITIVE(v) && HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v));
     if (t == JSVAL_OBJECT)
         return !JSVAL_IS_PRIMITIVE(v) && !HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v));
 
     /* for non-number types we expect a precise match of the type */
     uint8 vt = getCoercedType(v);
 #ifdef DEBUG
     if (vt != t) {
-        debug_only_v(nj_dprintf("Type mismatch: val %c, map %c ", typeChar[vt],
-                                typeChar[t]);)
-    }
-#endif
-    debug_only_v(nj_dprintf("checkType(vt=%d, t=%d) stage_count=%d\n",
-                            (int) vt, t, stage_count);)
+        debug_only_printf(LC_TMTracer, "Type mismatch: val %c, map %c ", typeChar[vt],
+                          typeChar[t]);
+    }
+#endif
+    debug_only_printf(LC_TMTracer, "checkType(vt=%d, t=%d) stage_count=%d\n",
+                      (int) vt, t, stage_count);
     return vt == t;
 }
 
 class SelfTypeStabilityVisitor : public SlotVisitorBase
 {
     TraceRecorder &mRecorder;
     uint8 *mTypeMap;
     JSContext *mCx;
@@ -3001,17 +3104,17 @@ public:
         mStageCount(stageCount),
         mStackSlotNum(0),
         mOk(true)
     {}
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) {
         if (mOk) {
-            debug_only_v(nj_dprintf("global%d ", n);)
+            debug_only_printf(LC_TMTracer, "global%d ", n);
             if (!mRecorder.checkType(*vp, *mTypeMap,
                                      mStageVals[mStageCount],
                                      mStageIns[mStageCount],
                                      mStageCount)) {
                 /* If the failure was an int->double, tell the oracle. */
                 if (*mTypeMap == JSVAL_INT && isNumber(*vp) &&
                     !isPromoteInt(mRecorder.get(vp))) {
                     oracle.markGlobalSlotUndemotable(mCx, slot);
@@ -3022,17 +3125,17 @@ public:
             }
             mTypeMap++;
         }
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) {
         for (size_t i = 0; i < count; ++i) {
-            debug_only_v(nj_dprintf("%s%u ", stackSlotKind(), unsigned(i));)
+            debug_only_printf(LC_TMTracer, "%s%u ", stackSlotKind(), unsigned(i));
             if (!mRecorder.checkType(*vp, *mTypeMap,
                                      mStageVals[mStageCount],
                                      mStageIns[mStageCount],
                                      mStageCount)) {
                 if (*mTypeMap == JSVAL_INT && isNumber(*vp) &&
                     !isPromoteInt(mRecorder.get(vp))) {
                     oracle.markStackSlotUndemotable(mCx, mStackSlotNum);
                     mDemote = true;
@@ -3188,17 +3291,17 @@ TraceRecorder::deduceTypeStability(Fragm
     unsigned stage_count;
     jsval** stage_vals = (jsval**)alloca(sizeof(jsval*) * (treeInfo->typeMap.length()));
     LIns** stage_ins = (LIns**)alloca(sizeof(LIns*) * (treeInfo->typeMap.length()));
 
     /* First run through and see if we can close ourselves - best case! */
     stage_count = 0;
     success = false;
 
-    debug_only_v(nj_dprintf("Checking type stability against self=%p\n", (void*)fragment);)
+    debug_only_printf(LC_TMTracer, "Checking type stability against self=%p\n", (void*)fragment);
     SelfTypeStabilityVisitor selfVisitor(*this, treeInfo->stackTypeMap(), demote,
                                          stage_vals, stage_ins, stage_count);
     VisitSlots(selfVisitor, cx, 0, *treeInfo->globalSlots);
     success = selfVisitor.isOk();
 
     /* If we got a success and we don't need to recompile, we should just close here. */
     if (success && !demote) {
         for (unsigned i = 0; i < stage_count; i++)
@@ -3212,17 +3315,19 @@ TraceRecorder::deduceTypeStability(Fragm
     demote = false;
 
     /* At this point the tree is about to be incomplete, so let's see if we can connect to any
      * peer fragment that is type stable.
      */
     Fragment* f;
     TreeInfo* ti;
     for (f = root_peer; f != NULL; f = f->peer) {
-        debug_only_v(nj_dprintf("Checking type stability against peer=%p (code=%p)\n", (void*)f, f->code());)
+        debug_only_printf(LC_TMTracer,
+                          "Checking type stability against peer=%p (code=%p)\n",
+                          (void*)f, f->code());
         if (!f->code())
             continue;
         ti = (TreeInfo*)f->vmprivate;
         /* Don't allow varying stack depths */
         if ((ti->nStackTypes != treeInfo->nStackTypes) ||
             (ti->typeMap.length() != treeInfo->typeMap.length()) ||
             (ti->globalSlots->length() != treeInfo->globalSlots->length()))
             continue;
@@ -3265,29 +3370,30 @@ TraceRecorder::deduceTypeStability(Fragm
 }
 
 static JS_REQUIRES_STACK void
 FlushJITCache(JSContext* cx)
 {
     if (!TRACING_ENABLED(cx))
         return;
     JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
-    debug_only_v(nj_dprintf("Flushing cache.\n");)
+    debug_only_print0(LC_TMTracer, "Flushing cache.\n");
     if (tm->recorder)
         js_AbortRecording(cx, "flush cache");
     TraceRecorder* tr;
     while ((tr = tm->abortStack) != NULL) {
         tr->removeFragmentoReferences();
         tr->deepAbort();
         tr->popAbortStack();
     }
     Fragmento* fragmento = tm->fragmento;
     if (fragmento) {
         if (tm->prohibitFlush) {
-            debug_only_v(nj_dprintf("Deferring fragmento flush due to deep bail.\n");)
+            debug_only_print0(LC_TMTracer,
+                              "Deferring fragmento flush due to deep bail.\n");
             tm->needFlush = JS_TRUE;
             return;
         }
 
         fragmento->clearFrags();
 #ifdef DEBUG
         JS_ASSERT(fragmento->labels);
         fragmento->labels->clear();
@@ -3319,31 +3425,31 @@ TraceRecorder::compile(JSTraceMonitor* t
 #endif
 
     if (tm->needFlush) {
         FlushJITCache(cx);
         return;
     }
     Fragmento* fragmento = tm->fragmento;
     if (treeInfo->maxNativeStackSlots >= MAX_NATIVE_STACK_SLOTS) {
-        debug_only_v(nj_dprintf("Blacklist: excessive stack use.\n"));
+        debug_only_print0(LC_TMTracer, "Blacklist: excessive stack use.\n");
         js_Blacklist((jsbytecode*) fragment->root->ip);
         return;
     }
     if (anchor && anchor->exitType != CASE_EXIT)
         ++treeInfo->branchCount;
     if (lirbuf->outOMem()) {
         fragmento->assm()->setError(nanojit::OutOMem);
         return;
     }
     ::compile(fragmento->assm(), fragment);
     if (fragmento->assm()->error() == nanojit::OutOMem)
         return;
     if (fragmento->assm()->error() != nanojit::None) {
-        debug_only_v(nj_dprintf("Blacklisted: error during compilation\n");)
+        debug_only_print0(LC_TMTracer, "Blacklisted: error during compilation\n");
         js_Blacklist((jsbytecode*) fragment->root->ip);
         return;
     }
     js_resetRecordingAttempts(cx, (jsbytecode*) fragment->ip);
     js_resetRecordingAttempts(cx, (jsbytecode*) fragment->root->ip);
     if (anchor) {
 #ifdef NANOJIT_IA32
         if (anchor->exitType == CASE_EXIT)
@@ -3401,17 +3507,18 @@ TraceRecorder::closeLoop(JSTraceMonitor*
     JS_ASSERT((*cx->fp->regs->pc == JSOP_LOOP || *cx->fp->regs->pc == JSOP_NOP) && !cx->fp->imacpc);
 
     bool stable;
     Fragment* peer;
     VMFragment* peer_root;
     Fragmento* fragmento = tm->fragmento;
 
     if (callDepth != 0) {
-        debug_only_v(nj_dprintf("Blacklisted: stack depth mismatch, possible recursion.\n");)
+        debug_only_print0(LC_TMTracer,
+                          "Blacklisted: stack depth mismatch, possible recursion.\n");
         js_Blacklist((jsbytecode*) fragment->root->ip);
         trashSelf = true;
         return;
     }
 
     VMSideExit* exit = snapshot(UNSTABLE_LOOP_EXIT);
     JS_ASSERT(exit->numStackSlots == treeInfo->nStackTypes);
 
@@ -3422,17 +3529,17 @@ TraceRecorder::closeLoop(JSTraceMonitor*
     stable = deduceTypeStability(peer_root, &peer, demote);
 
 #if DEBUG
     if (!stable)
         AUDIT(unstableLoopVariable);
 #endif
 
     if (trashSelf) {
-        debug_only_v(nj_dprintf("Trashing tree from type instability.\n");)
+        debug_only_print0(LC_TMTracer, "Trashing tree from type instability.\n");
         return;
     }
 
     if (stable && demote) {
         JS_ASSERT(fragment->kind == LoopTrace);
         return;
     }
 
@@ -3444,57 +3551,64 @@ TraceRecorder::closeLoop(JSTraceMonitor*
          * hope it becomes stable later.
          */
         if (!peer) {
             /*
              * If such a fragment does not exist, let's compile the loop ahead
              * of time anyway.  Later, if the loop becomes type stable, we will
              * connect these two fragments together.
              */
-            debug_only_v(nj_dprintf("Trace has unstable loop variable with no stable peer, "
-                                "compiling anyway.\n");)
+            debug_only_print0(LC_TMTracer,
+                              "Trace has unstable loop variable with no stable peer, "
+                              "compiling anyway.\n");
             UnstableExit* uexit = new UnstableExit;
             uexit->fragment = fragment;
             uexit->exit = exit;
             uexit->next = treeInfo->unstableExits;
             treeInfo->unstableExits = uexit;
         } else {
             JS_ASSERT(peer->code());
             exit->target = peer;
-            debug_only_v(nj_dprintf("Joining type-unstable trace to target fragment %p.\n", (void*)peer);)
+            debug_only_printf(LC_TMTracer,
+                              "Joining type-unstable trace to target fragment %p.\n",
+                              (void*)peer);
             stable = true;
             ((TreeInfo*)peer->vmprivate)->dependentTrees.addUnique(fragment->root);
             treeInfo->linkedTrees.addUnique(peer);
         }
     } else {
         exit->target = fragment->root;
         fragment->lastIns = lir->insGuard(LIR_loop, lir->insImm(1), createGuardRecord(exit));
     }
     compile(tm);
 
     if (fragmento->assm()->error() != nanojit::None)
         return;
 
     joinEdgesToEntry(fragmento, peer_root);
 
-    debug_only_v(nj_dprintf("updating specializations on dependent and linked trees\n"))
+    debug_only_print0(LC_TMTracer,
+                      "updating specializations on dependent and linked trees\n");
     if (fragment->root->vmprivate)
         specializeTreesToMissingGlobals(cx, globalObj, (TreeInfo*)fragment->root->vmprivate);
 
     /*
      * If this is a newly formed tree, and the outer tree has not been compiled yet, we
      * should try to compile the outer tree again.
      */
     if (outer)
         js_AttemptCompilation(cx, tm, globalObj, outer, outerArgc);
-
-    debug_only_v(nj_dprintf("recording completed at %s:%u@%u via closeLoop\n",
-                            cx->fp->script->filename,
-                            js_FramePCToLineNumber(cx, cx->fp),
-                            FramePCOffset(cx->fp));)
+#ifdef JS_JIT_SPEW
+    debug_only_printf(LC_TMMinimal,
+                      "recording completed at  %s:%u@%u via closeLoop\n",
+                      cx->fp->script->filename,
+                      js_FramePCToLineNumber(cx, cx->fp),
+                      FramePCOffset(cx->fp));
+    debug_only_print0(LC_TMMinimal, "\n");
+#endif
 }
 
 JS_REQUIRES_STACK void
 TraceRecorder::joinEdgesToEntry(Fragmento* fragmento, VMFragment* peer_root)
 {
     if (fragment->kind == LoopTrace) {
         TreeInfo* ti;
         Fragment* peer;
@@ -3507,19 +3621,23 @@ TraceRecorder::joinEdgesToEntry(Fragment
             if (!peer->code())
                 continue;
             ti = (TreeInfo*)peer->vmprivate;
             uexit = ti->unstableExits;
             unext = &ti->unstableExits;
             while (uexit != NULL) {
                 bool remove = js_JoinPeersIfCompatible(fragmento, fragment, treeInfo, uexit->exit);
                 JS_ASSERT(!remove || fragment != peer);
-                debug_only_v(if (remove) {
-                             nj_dprintf("Joining type-stable trace to target exit %p->%p.\n",
-                                    (void*)uexit->fragment, (void*)uexit->exit); });
+                debug_only_stmt(
+                    if (remove) {
+                        debug_only_printf(LC_TMTracer, 
+                                          "Joining type-stable trace to target exit %p->%p.\n",
+                                          (void*)uexit->fragment, (void*)uexit->exit);
+                    }
+                )
                 if (!remove) {
                     /* See if this exit contains mismatch demotions, which imply trashing a tree.
                        This is actually faster than trashing the original tree as soon as the
                        instability is detected, since we could have compiled a fairly stable
                        tree that ran faster with integers. */
                     unsigned stackCount = 0;
                     unsigned globalCount = 0;
                     t1 = treeInfo->stackTypeMap();
@@ -3563,26 +3681,26 @@ TraceRecorder::joinEdgesToEntry(Fragment
                 } else {
                     unext = &uexit->next;
                     uexit = uexit->next;
                 }
             }
         }
     }
 
-    debug_only_v(js_DumpPeerStability(traceMonitor, peer_root->ip, peer_root->globalObj,
-                                      peer_root->globalShape, peer_root->argc);)
+    debug_only_stmt(js_DumpPeerStability(traceMonitor, peer_root->ip, peer_root->globalObj,
+                             peer_root->globalShape, peer_root->argc);)
 }
 
 /* Emit an always-exit guard and compile the tree (used for break statements. */
 JS_REQUIRES_STACK void
 TraceRecorder::endLoop(JSTraceMonitor* tm)
 {
     if (callDepth != 0) {
-        debug_only_v(nj_dprintf("Blacklisted: stack depth mismatch, possible recursion.\n");)
+        debug_only_print0(LC_TMTracer, "Blacklisted: stack depth mismatch, possible recursion.\n");
         js_Blacklist((jsbytecode*) fragment->root->ip);
         trashSelf = true;
         return;
     }
 
     fragment->lastIns =
         lir->insGuard(LIR_x, lir->insImm(1), createGuardRecord(snapshot(LOOP_EXIT)));
     compile(tm);
@@ -3590,31 +3708,35 @@ TraceRecorder::endLoop(JSTraceMonitor* t
     if (tm->fragmento->assm()->error() != nanojit::None)
         return;
 
     VMFragment* root = (VMFragment*)fragment->root;
     joinEdgesToEntry(tm->fragmento, getLoop(tm, root->ip, root->globalObj, root->globalShape, root->argc));
 
     /* Note: this must always be done, in case we added new globals on trace and haven't yet
        propagated those to linked and dependent trees. */
-    debug_only_v(nj_dprintf("updating specializations on dependent and linked trees\n"))
+    debug_only_print0(LC_TMTracer,
+                      "updating specializations on dependent and linked trees\n");
     if (fragment->root->vmprivate)
         specializeTreesToMissingGlobals(cx, globalObj, (TreeInfo*)fragment->root->vmprivate);
 
     /*
      * If this is a newly formed tree, and the outer tree has not been compiled yet, we
      * should try to compile the outer tree again.
      */
     if (outer)
         js_AttemptCompilation(cx, tm, globalObj, outer, outerArgc);
-
-    debug_only_v(nj_dprintf("recording completed at %s:%u@%u via endLoop\n",
-                            cx->fp->script->filename,
-                            js_FramePCToLineNumber(cx, cx->fp),
-                            FramePCOffset(cx->fp));)
+#ifdef JS_JIT_SPEW
+    debug_only_printf(LC_TMMinimal,
+                      "Recording completed at  %s:%u@%u via endLoop\n",
+                      cx->fp->script->filename,
+                      js_FramePCToLineNumber(cx, cx->fp),
+                      FramePCOffset(cx->fp));
+    debug_only_print0(LC_TMTracer, "\n");
+#endif
 }
 
 /* Emit code to adjust the stack to match the inner tree's stack expectations. */
 JS_REQUIRES_STACK void
 TraceRecorder::prepareTreeCall(Fragment* inner)
 {
     TreeInfo* ti = (TreeInfo*)inner->vmprivate;
     inner_sp_ins = lirbuf->sp;
@@ -3625,18 +3747,19 @@ TraceRecorder::prepareTreeCall(Fragment*
     if (callDepth > 0) {
         /* Calculate the amount we have to lift the native stack pointer by to compensate for
            any outer frames that the inner tree doesn't expect but the outer tree has. */
         ptrdiff_t sp_adj = nativeStackOffset(&cx->fp->argv[-2]);
         /* Calculate the amount we have to lift the call stack by */
         ptrdiff_t rp_adj = callDepth * sizeof(FrameInfo*);
         /* Guard that we have enough stack space for the tree we are trying to call on top
            of the new value for sp. */
-        debug_only_v(nj_dprintf("sp_adj=%d outer=%d inner=%d\n",
-                                sp_adj, treeInfo->nativeStackBase, ti->nativeStackBase));
+        debug_only_printf(LC_TMTracer,
+                          "sp_adj=%d outer=%d inner=%d\n",
+                          sp_adj, treeInfo->nativeStackBase, ti->nativeStackBase);
         LIns* sp_top = lir->ins2i(LIR_piadd, lirbuf->sp,
                 - treeInfo->nativeStackBase /* rebase sp to beginning of outer tree's stack */
                 + sp_adj /* adjust for stack in outer frame inner tree can't see */
                 + ti->maxNativeStackSlots * sizeof(double)); /* plus the inner tree's stack */
         guard(true, lir->ins2(LIR_lt, sp_top, eos_ins), OOM_EXIT);
         /* Guard that we have enough call stack space. */
         LIns* rp_top = lir->ins2i(LIR_piadd, lirbuf->rp, rp_adj +
                 ti->maxCallDepth * sizeof(FrameInfo*));
@@ -3711,17 +3834,18 @@ TraceRecorder::emitIf(jsbytecode* pc, bo
         exitType = LOOP_EXIT;
 
         /*
          * If we are about to walk out of the loop, generate code for the inverse loop
          * condition, pretending we recorded the case that stays on trace.
          */
         if ((*pc == JSOP_IFEQ || *pc == JSOP_IFEQX) == cond) {
             JS_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX || *pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
-            debug_only_v(nj_dprintf("Walking out of the loop, terminating it anyway.\n");)
+            debug_only_print0(LC_TMTracer,
+                              "Walking out of the loop, terminating it anyway.\n");
             cond = !cond;
         }
 
         /*
          * Conditional guards do not have to be emitted if the condition is constant. We
          * make a note whether the loop condition is true or false here, so we later know
          * whether to emit a loop edge or a loop end.
          */
@@ -3904,19 +4028,20 @@ CheckGlobalObjectShape(JSContext* cx, JS
     uint32 globalShape = OBJ_SHAPE(globalObj);
 
     if (tm->recorder) {
         VMFragment* root = (VMFragment*)tm->recorder->getFragment()->root;
         TreeInfo* ti = tm->recorder->getTreeInfo();
         /* Check the global shape matches the recorder's treeinfo's shape. */
         if (globalObj != root->globalObj || globalShape != root->globalShape) {
             AUDIT(globalShapeMismatchAtEntry);
-            debug_only_v(nj_dprintf("Global object/shape mismatch (%p/%u vs. %p/%u), flushing cache.\n",
-                                    (void*)globalObj, globalShape, (void*)root->globalObj,
-                                    root->globalShape);)
+            debug_only_printf(LC_TMTracer, 
+                              "Global object/shape mismatch (%p/%u vs. %p/%u), flushing cache.\n",
+                              (void*)globalObj, globalShape, (void*)root->globalObj,
+                              root->globalShape);
             js_Backoff(cx, (jsbytecode*) root->ip);
             FlushJITCache(cx);
             return false;
         }
         if (shape)
             *shape = globalShape;
         if (slots)
             *slots = ti->globalSlots;
@@ -3940,18 +4065,19 @@ CheckGlobalObjectShape(JSContext* cx, JS
             if (slots)
                 *slots = state.globalSlots;
             return true;
         }
     }
 
     /* No currently-tracked-global found and no room to allocate, abort. */
     AUDIT(globalShapeMismatchAtEntry);
-    debug_only_v(nj_dprintf("No global slotlist for global shape %u, flushing cache.\n",
-                            globalShape));
+    debug_only_printf(LC_TMTracer, 
+                      "No global slotlist for global shape %u, flushing cache.\n",
+                      globalShape);
     FlushJITCache(cx);
     return false;
 }
 
 static JS_REQUIRES_STACK bool
 js_StartRecorder(JSContext* cx, VMSideExit* anchor, Fragment* f, TreeInfo* ti,
                  unsigned stackSlots, unsigned ngslots, uint8* typeMap,
                  VMSideExit* expectedInnerExit, jsbytecode* outer, uint32 outerArgc)
@@ -3981,17 +4107,17 @@ js_StartRecorder(JSContext* cx, VMSideEx
 static void
 js_TrashTree(JSContext* cx, Fragment* f)
 {
     JS_ASSERT((!f->code()) == (!f->vmprivate));
     JS_ASSERT(f == f->root);
     if (!f->code())
         return;
     AUDIT(treesTrashed);
-    debug_only_v(nj_dprintf("Trashing tree info.\n");)
+    debug_only_print0(LC_TMTracer, "Trashing tree info.\n");
     Fragmento* fragmento = JS_TRACE_MONITOR(cx).fragmento;
     TreeInfo* ti = (TreeInfo*)f->vmprivate;
     f->vmprivate = NULL;
     f->releaseCode(fragmento);
     Fragment** data = ti->dependentTrees.data();
     unsigned length = ti->dependentTrees.length();
     for (unsigned n = 0; n < length; ++n)
         js_TrashTree(cx, data[n]);
@@ -4228,17 +4354,18 @@ js_RecordTree(JSContext* cx, JSTraceMoni
     }
 
     f->root = f;
     f->lirbuf = tm->lirbuf;
 
     if (f->lirbuf->outOMem() || js_OverfullFragmento(tm, tm->fragmento)) {
         js_Backoff(cx, (jsbytecode*) f->root->ip);
         FlushJITCache(cx);
-        debug_only_v(nj_dprintf("Out of memory recording new tree, flushing cache.\n");)
+        debug_only_print0(LC_TMTracer,
+                          "Out of memory recording new tree, flushing cache.\n");
         return false;
     }
 
     JS_ASSERT(!f->code() && !f->vmprivate);
 
     /* setup the VM-private treeInfo structure for this fragment */
     TreeInfo* ti = new (&gc) TreeInfo(f, globalSlots);
 
@@ -4388,17 +4515,17 @@ js_AttemptToStabilizeTree(JSContext* cx,
                     *tail = uexit->next;
                     delete uexit;
                     bound = true;
                     break;
                 }
                 tail = &uexit->next;
             }
             JS_ASSERT(bound);
-            debug_only_v(js_DumpPeerStability(tm, f->ip, from->globalObj, from->globalShape, from->argc);)
+            debug_only_stmt( js_DumpPeerStability(tm, f->ip, from->globalObj, from->globalShape, from->argc); )
             break;
         } else if (undemote) {
             /* The original tree is unconnectable, so trash it. */
             js_TrashTree(cx, f);
             /* We shouldn't attempt to record now, since we'll hit a duplicate. */
             return false;
         }
     }
@@ -4449,17 +4576,18 @@ js_AttemptToExtendTree(JSContext* cx, VM
 
     /*
      * If we are recycling a fragment, it might have a different ip so reset it here. This
      * can happen when attaching a branch to a NESTED_EXIT, which might extend along separate paths
      * (i.e. after the loop edge, and after a return statement).
      */
     c->ip = cx->fp->regs->pc;
 
-    debug_only_v(nj_dprintf("trying to attach another branch to the tree (hits = %d)\n", c->hits());)
+    debug_only_printf(LC_TMTracer,
+                      "trying to attach another branch to the tree (hits = %d)\n", c->hits());
 
     int32_t& hits = c->hits();
     if (outer || (hits++ >= HOTEXIT && hits <= HOTEXIT+MAXEXIT)) {
         /* start tracing secondary trace from this point */
         c->lirbuf = f->lirbuf;
         unsigned stackSlots;
         unsigned ngslots;
         uint8* typeMap;
@@ -4534,19 +4662,20 @@ js_RecordLoopEdge(JSContext* cx, TraceRe
 
     /* Does this branch go to an inner loop? */
     Fragment* first = getLoop(&JS_TRACE_MONITOR(cx), cx->fp->regs->pc,
                               root->globalObj, root->globalShape, cx->fp->argc);
     if (!first) {
         /* Not an inner loop we can call, abort trace. */
         AUDIT(returnToDifferentLoopHeader);
         JS_ASSERT(!cx->fp->imacpc);
-        debug_only_v(nj_dprintf("loop edge to %d, header %d\n",
-                                cx->fp->regs->pc - cx->fp->script->code,
-                                (jsbytecode*)r->getFragment()->root->ip - cx->fp->script->code));
+        debug_only_printf(LC_TMTracer,
+                          "loop edge to %d, header %d\n",
+                          cx->fp->regs->pc - cx->fp->script->code,
+                          (jsbytecode*)r->getFragment()->root->ip - cx->fp->script->code);
         js_AbortRecording(cx, "Loop edge does not return to header");
         return false;
     }
 
     /* Make sure inner tree call will not run into an out-of-memory condition. */
     if (tm->reservedDoublePoolPtr < (tm->reservedDoublePool + MAX_NATIVE_STACK_SLOTS) &&
         !js_ReplenishReservedPool(cx, tm)) {
         js_AbortRecording(cx, "Couldn't call inner tree (out of memory)");
@@ -4556,20 +4685,21 @@ js_RecordLoopEdge(JSContext* cx, TraceRe
     /* Make sure the shape of the global object still matches (this might flush
        the JIT cache). */
     JSObject* globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain);
     uint32 globalShape = -1;
     SlotList* globalSlots = NULL;
     if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots))
         return false;
 
-    debug_only_v(nj_dprintf("Looking for type-compatible peer (%s:%d@%d)\n",
-                            cx->fp->script->filename,
-                            js_FramePCToLineNumber(cx, cx->fp),
-                            FramePCOffset(cx->fp));)
+    debug_only_printf(LC_TMTracer,
+                      "Looking for type-compatible peer (%s:%d@%d)\n",
+                      cx->fp->script->filename,
+                      js_FramePCToLineNumber(cx, cx->fp),
+                      FramePCOffset(cx->fp));
 
     // Find a matching inner tree. If none can be found, compile one.
     Fragment* f = r->findNestedCompatiblePeer(first);
     if (!f || !f->code()) {
         AUDIT(noCompatInnerTrees);
 
         VMFragment* outerFragment = (VMFragment*) tm->recorder->getFragment()->root;
         jsbytecode* outer = (jsbytecode*) outerFragment->ip;
@@ -4623,75 +4753,75 @@ js_RecordLoopEdge(JSContext* cx, TraceRe
         oracle.markInstructionUndemotable(cx->fp->regs->pc);
         /* fall through */
       case BRANCH_EXIT:
       case CASE_EXIT:
         /* abort recording the outer tree, extend the inner tree */
         js_AbortRecording(cx, "Inner tree is trying to grow, abort outer recording");
         return js_AttemptToExtendTree(cx, lr, NULL, outer);
       default:
-        debug_only_v(nj_dprintf("exit_type=%d\n", lr->exitType);)
-            js_AbortRecording(cx, "Inner tree not suitable for calling");
+        debug_only_printf(LC_TMTracer, "exit_type=%d\n", lr->exitType);
+        js_AbortRecording(cx, "Inner tree not suitable for calling");
         return false;
     }
 }
 
 static bool
 js_IsEntryTypeCompatible(jsval* vp, uint8* m)
 {
     unsigned tag = JSVAL_TAG(*vp);
 
-    debug_only_v(nj_dprintf("%c/%c ", tagChar[tag], typeChar[*m]);)
+    debug_only_printf(LC_TMTracer, "%c/%c ", tagChar[tag], typeChar[*m]);
 
     switch (*m) {
       case JSVAL_OBJECT:
         if (tag == JSVAL_OBJECT && !JSVAL_IS_NULL(*vp) &&
             !HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(*vp))) {
             return true;
         }
-        debug_only_v(nj_dprintf("object != tag%u ", tag);)
+        debug_only_printf(LC_TMTracer, "object != tag%u ", tag);
         return false;
       case JSVAL_INT:
         jsint i;
         if (JSVAL_IS_INT(*vp))
             return true;
         if ((tag == JSVAL_DOUBLE) && JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(*vp), i))
             return true;
-        debug_only_v(nj_dprintf("int != tag%u(value=%lu) ", tag, (unsigned long)*vp);)
+        debug_only_printf(LC_TMTracer, "int != tag%u(value=%lu) ", tag, (unsigned long)*vp);
         return false;
       case JSVAL_DOUBLE:
         if (JSVAL_IS_INT(*vp) || tag == JSVAL_DOUBLE)
             return true;
-        debug_only_v(nj_dprintf("double != tag%u ", tag);)
+        debug_only_printf(LC_TMTracer, "double != tag%u ", tag);
         return false;
       case JSVAL_BOXED:
         JS_NOT_REACHED("shouldn't see boxed type in entry");
         return false;
       case JSVAL_STRING:
         if (tag == JSVAL_STRING)
             return true;
-        debug_only_v(nj_dprintf("string != tag%u ", tag);)
+        debug_only_printf(LC_TMTracer, "string != tag%u ", tag);
         return false;
       case JSVAL_TNULL:
         if (JSVAL_IS_NULL(*vp))
             return true;
-        debug_only_v(nj_dprintf("null != tag%u ", tag);)
+        debug_only_printf(LC_TMTracer, "null != tag%u ", tag);
         return false;
       case JSVAL_BOOLEAN:
         if (tag == JSVAL_BOOLEAN)
             return true;
-        debug_only_v(nj_dprintf("bool != tag%u ", tag);)
+        debug_only_printf(LC_TMTracer, "bool != tag%u ", tag);
         return false;
       default:
         JS_ASSERT(*m == JSVAL_TFUN);
         if (tag == JSVAL_OBJECT && !JSVAL_IS_NULL(*vp) &&
             HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(*vp))) {
             return true;
         }
-        debug_only_v(nj_dprintf("fun != tag%u ", tag);)
+        debug_only_printf(LC_TMTracer, "fun != tag%u ", tag);
         return false;
     }
 }
 
 class TypeCompatibilityVisitor : public SlotVisitorBase
 {
     TraceRecorder &mRecorder;
     JSContext *mCx;
@@ -4705,33 +4835,33 @@ public:
         mCx(mRecorder.cx),
         mTypeMap(typeMap),
         mStackSlotNum(0),
         mOk(true)
     {}
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) {
-        debug_only_v(nj_dprintf("global%d=", n);)
+        debug_only_printf(LC_TMTracer, "global%d=", n);
         if (!js_IsEntryTypeCompatible(vp, mTypeMap)) {
             mOk = false;
         } else if (!isPromoteInt(mRecorder.get(vp)) &&
                    *mTypeMap == JSVAL_INT) {
             oracle.markGlobalSlotUndemotable(mCx, slot);
             mOk = false;
         } else if (JSVAL_IS_INT(*vp) && *mTypeMap == JSVAL_DOUBLE) {
             oracle.markGlobalSlotUndemotable(mCx, slot);
         }
         mTypeMap++;
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) {
         for (size_t i = 0; i < count; ++i) {
-            debug_only_v(nj_dprintf("%s%u=", stackSlotKind(), unsigned(i));)
+            debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), unsigned(i));
             if (!js_IsEntryTypeCompatible(vp, mTypeMap)) {
                 mOk = false;
             } else if (!isPromoteInt(mRecorder.get(vp)) &&
                        *mTypeMap == JSVAL_INT) {
                 oracle.markStackSlotUndemotable(mCx, mStackSlotNum);
                 mOk = false;
             } else if (JSVAL_IS_INT(*vp) && *mTypeMap == JSVAL_DOUBLE) {
                 oracle.markStackSlotUndemotable(mCx, mStackSlotNum);
@@ -4758,34 +4888,34 @@ TraceRecorder::findNestedCompatiblePeer(
 
     TreeInfo* ti;
     for (; f != NULL; f = f->peer) {
         if (!f->code())
             continue;
 
         ti = (TreeInfo*)f->vmprivate;
 
-        debug_only_v(nj_dprintf("checking nested types %p: ", (void*)f);)
+        debug_only_printf(LC_TMTracer, "checking nested types %p: ", (void*)f);
 
         if (ngslots > ti->nGlobalTypes())
             specializeTreesToMissingGlobals(cx, globalObj, ti);
 
         /*
          * Determine whether the typemap of the inner tree matches the outer tree's
          * current state. If the inner tree expects an integer, but the outer tree
          * doesn't guarantee an integer for that slot, we mark the slot undemotable
          * and mismatch here. This will force a new tree to be compiled that accepts
          * a double for the slot. If the inner tree expects a double, but the outer
          * tree has an integer, we can proceed, but we mark the location undemotable.
          */
 
         TypeCompatibilityVisitor visitor(*this, ti->typeMap.data());
         VisitSlots(visitor, cx, 0, *treeInfo->globalSlots);
 
-        debug_only_v(nj_dprintf(" %s\n", visitor.isOk() ? "match" : "");)
+        debug_only_printf(LC_TMTracer, " %s\n", visitor.isOk() ? "match" : "");
         if (visitor.isOk())
             return f;
     }
 
     return NULL;
 }
 
 class CheckEntryTypeVisitor : public SlotVisitorBase
@@ -4794,17 +4924,17 @@ class CheckEntryTypeVisitor : public Slo
     uint8 *mTypeMap;
 public:
     CheckEntryTypeVisitor(uint8 *typeMap) :
         mOk(true),
         mTypeMap(typeMap)
     {}
 
     JS_ALWAYS_INLINE void checkSlot(jsval *vp, char const *name, int i) {
-        debug_only_v(nj_dprintf("%s%d=", name, i);)
+        debug_only_printf(LC_TMTracer, "%s%d=", name, i);
         JS_ASSERT(*mTypeMap != 0xCD);
         mOk = js_IsEntryTypeCompatible(vp, mTypeMap++);
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) {
         if (mOk)
             checkSlot(vp, "global", n);
@@ -4844,17 +4974,17 @@ js_CheckEntryTypes(JSContext* cx, JSObje
 
     JS_ASSERT(ti->typeMap.length() == js_NativeStackSlots(cx, 0) + ngslots);
     JS_ASSERT(ti->typeMap.length() == ti->nStackTypes + ngslots);
     JS_ASSERT(ti->nGlobalTypes() == ngslots);
 
     CheckEntryTypeVisitor visitor(ti->typeMap.data());
     VisitSlots(visitor, cx, 0, *ti->globalSlots);
 
-    debug_only_v(nj_dprintf("\n");)
+    debug_only_print0(LC_TMTracer, "\n");
     return visitor.isOk();
 }
 
 /**
  * Find an acceptable entry tree given a PC.
  *
  * @param cx            Context.
  * @param globalObj     Global object.
@@ -4864,17 +4994,18 @@ js_CheckEntryTypes(JSContext* cx, JSObje
  */
 static JS_REQUIRES_STACK Fragment*
 js_FindVMCompatiblePeer(JSContext* cx, JSObject* globalObj, Fragment* f, uintN& count)
 {
     count = 0;
     for (; f != NULL; f = f->peer) {
         if (f->vmprivate == NULL)
             continue;
-        debug_only_v(nj_dprintf("checking vm types %p (ip: %p): ", (void*)f, f->ip);)
+        debug_only_printf(LC_TMTracer,
+                          "checking vm types %p (ip: %p): ", (void*)f, f->ip);
         if (js_CheckEntryTypes(cx, globalObj, (TreeInfo*)f->vmprivate))
             return f;
         ++count;
     }
     return NULL;
 }
 
 static void
@@ -4947,23 +5078,24 @@ js_ExecuteTree(JSContext* cx, Fragment* 
         return NULL;
 
 #ifdef DEBUG
     memset(stack_buffer, 0xCD, sizeof(stack_buffer));
     memset(global, 0xCD, (globalFrameSize+1)*sizeof(double));
     JS_ASSERT(globalFrameSize <= MAX_GLOBAL_SLOTS);
 #endif
 
-    debug_only(*(uint64*)&global[globalFrameSize] = 0xdeadbeefdeadbeefLL;)
-    debug_only_v(nj_dprintf("entering trace at %s:%u@%u, native stack slots: %u code: %p\n",
-                            cx->fp->script->filename,
-                            js_FramePCToLineNumber(cx, cx->fp),
-                            FramePCOffset(cx->fp),
-                            ti->maxNativeStackSlots,
-                            f->code());)
+    debug_only_stmt(*(uint64*)&global[globalFrameSize] = 0xdeadbeefdeadbeefLL;)
+    debug_only_printf(LC_TMTracer, 
+                      "entering trace at %s:%u@%u, native stack slots: %u code: %p\n",
+                      cx->fp->script->filename,
+                      js_FramePCToLineNumber(cx, cx->fp),
+                      FramePCOffset(cx->fp),
+                      ti->maxNativeStackSlots,
+                      f->code());
 
     JS_ASSERT(ti->nGlobalTypes() == ngslots);
     BuildNativeFrame(cx, globalObj, 0/*callDepth*/, ngslots, gslots,
                      ti->typeMap.data(), global, stack_buffer);
 
     union { NIns *code; GuardRecord* (FASTCALL *func)(InterpState*, Fragment*); } u;
     u.code = f->code();
 
@@ -4971,17 +5103,17 @@ js_ExecuteTree(JSContext* cx, Fragment* 
     state->startTime = rdtsc();
 #endif
 
     JS_ASSERT(!tm->tracecx);
     tm->tracecx = cx;
     state->prev = cx->interpState;
     cx->interpState = state;
 
-    debug_only(fflush(NULL);)
+    debug_only_stmt(fflush(NULL));
     GuardRecord* rec;
     // Note that the block scoping is crucial here for TraceVis;  the
     // TraceVisStateObj constructors and destructors must run at the right times.
     {
 #ifdef MOZ_TRACEVIS
         TraceVisStateObj tvso_n(S_NATIVE);
 #endif
 #if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32)
@@ -5113,21 +5245,22 @@ LeaveTree(InterpState& state, VMSideExit
     JS_ARENA_RELEASE(&cx->stackPool, state.stackMark);
     while (callstack < rp) {
         /* Synthesize a stack frame and write out the values in it using the type map pointer
            on the native call stack. */
         js_SynthesizeFrame(cx, **callstack);
         int slots = FlushNativeStackFrame(cx, 1/*callDepth*/, (uint8*)(*callstack+1), stack, cx->fp);
 #ifdef DEBUG
         JSStackFrame* fp = cx->fp;
-        debug_only_v(nj_dprintf("synthesized deep frame for %s:%u@%u, slots=%d\n",
-                                fp->script->filename,
-                                js_FramePCToLineNumber(cx, fp),
-                                FramePCOffset(fp),
-                                slots);)
+        debug_only_printf(LC_TMTracer,
+                          "synthesized deep frame for %s:%u@%u, slots=%d\n",
+                          fp->script->filename,
+                          js_FramePCToLineNumber(cx, fp),
+                          FramePCOffset(fp),
+                          slots);
 #endif
         /* Keep track of the additional frames we put on the interpreter stack and the native
            stack slots we consumed. */
         ++*state.inlineCallCountp;
         ++callstack;
         stack += slots;
     }
 
@@ -5136,19 +5269,20 @@ LeaveTree(InterpState& state, VMSideExit
     JS_ASSERT(rp == callstack);
     unsigned calldepth = innermost->calldepth;
     unsigned calldepth_slots = 0;
     for (unsigned n = 0; n < calldepth; ++n) {
         calldepth_slots += js_SynthesizeFrame(cx, *callstack[n]);
         ++*state.inlineCallCountp;
 #ifdef DEBUG
         JSStackFrame* fp = cx->fp;
-        debug_only_v(nj_dprintf("synthesized shallow frame for %s:%u@%u\n",
-                                fp->script->filename, js_FramePCToLineNumber(cx, fp),
-                                FramePCOffset(fp));)
+        debug_only_printf(LC_TMTracer,
+                          "synthesized shallow frame for %s:%u@%u\n",
+                          fp->script->filename, js_FramePCToLineNumber(cx, fp),
+                          FramePCOffset(fp));
 #endif
     }
 
     /* Adjust sp and pc relative to the tree we exited from (not the tree we entered into).
        These are our final values for sp and pc since js_SynthesizeFrame has already taken
        care of all frames in between. But first we recover fp->blockChain, which comes from
        the side exit struct. */
     JSStackFrame* fp = cx->fp;
@@ -5165,27 +5299,28 @@ LeaveTree(InterpState& state, VMSideExit
                  js_ReconstructStackDepth(cx, fp->script, fp->regs->pc) == fp->regs->sp);
 
 #ifdef EXECUTE_TREE_TIMER
     uint64 cycles = rdtsc() - state.startTime;
 #elif defined(JS_JIT_SPEW)
     uint64 cycles = 0;
 #endif
 
-    debug_only_v(nj_dprintf("leaving trace at %s:%u@%u, op=%s, lr=%p, exitType=%d, sp=%d, "
-                            "calldepth=%d, cycles=%llu\n",
-                            fp->script->filename,
-                            js_FramePCToLineNumber(cx, fp),
-                            FramePCOffset(fp),
-                            js_CodeName[fp->imacpc ? *fp->imacpc : *fp->regs->pc],
-                            (void*)lr,
-                            lr->exitType,
-                            fp->regs->sp - StackBase(fp),
-                            calldepth,
-                            cycles));
+    debug_only_printf(LC_TMTracer,
+                      "leaving trace at %s:%u@%u, op=%s, lr=%p, exitType=%d, sp=%d, "
+                      "calldepth=%d, cycles=%llu\n",
+                      fp->script->filename,
+                      js_FramePCToLineNumber(cx, fp),
+                      FramePCOffset(fp),
+                      js_CodeName[fp->imacpc ? *fp->imacpc : *fp->regs->pc],
+                      (void*)lr,
+                      lr->exitType,
+                      fp->regs->sp - StackBase(fp),
+                      calldepth,
+                      cycles);
 
     /* If this trace is part of a tree, later branches might have added additional globals for
        which we don't have any type information available in the side exit. We merge in this
        information from the entry type-map. See also comment in the constructor of TraceRecorder
        why this is always safe to do. */
     TreeInfo* outermostTree = state.outermostTree;
     uint16* gslots = outermostTree->globalSlots->data();
     unsigned ngslots = outermostTree->globalSlots->length();
@@ -5340,28 +5475,29 @@ js_MonitorLoopEdge(JSContext* cx, uintN&
                                 globalSlots, argc);
 #ifdef MOZ_TRACEVIS
         if (!rv)
             tvso.r = R_FAIL_RECORD_TREE;
 #endif
         return rv;
     }
 
-    debug_only_v(nj_dprintf("Looking for compat peer %d@%d, from %p (ip: %p)\n",
-                            js_FramePCToLineNumber(cx, cx->fp),
-                            FramePCOffset(cx->fp), (void*)f, f->ip);)
+    debug_only_printf(LC_TMTracer,
+                      "Looking for compat peer %d@%d, from %p (ip: %p)\n",
+                      js_FramePCToLineNumber(cx, cx->fp),
+                      FramePCOffset(cx->fp), (void*)f, f->ip);
 
     uintN count;
     Fragment* match = js_FindVMCompatiblePeer(cx, globalObj, f, count);
     if (!match) {
         if (count < MAXPEERS)
             goto record;
         /* If we hit the max peers ceiling, don't try to lookup fragments all the time. Thats
            expensive. This must be a rather type-unstable loop. */
-        debug_only_v(nj_dprintf("Blacklisted: too many peer trees.\n");)
+        debug_only_print0(LC_TMTracer, "Blacklisted: too many peer trees.\n");
         js_Blacklist((jsbytecode*) f->root->ip);
 #ifdef MOZ_TRACEVIS
         tvso.r = R_MAX_PEERS;
 #endif
         return false;
     }
 
     VMSideExit* lr = NULL;
@@ -5440,19 +5576,24 @@ TraceRecorder::monitorRecording(JSContex
 
     /*
      * Clear one-shot state used to communicate between record_JSOP_CALL and post-
      * opcode-case-guts record hook (record_NativeCallComplete).
      */
     tr->pendingTraceableNative = NULL;
     tr->newobj_ins = NULL;
 
-    debug_only_v(js_Disassemble1(cx, cx->fp->script, cx->fp->regs->pc,
-                                 cx->fp->imacpc ? 0 : cx->fp->regs->pc - cx->fp->script->code,
-                                 !cx->fp->imacpc, stdout);)
+    debug_only_stmt(
+        if (js_LogController.lcbits & LC_TMRecorder) {
+            js_Disassemble1(cx, cx->fp->script, cx->fp->regs->pc,
+                            cx->fp->imacpc
+                                ? 0 : cx->fp->regs->pc - cx->fp->script->code,
+                            !cx->fp->imacpc, stdout);
+        }
+    )
 
     /* If op is not a break or a return from a loop, continue recording and follow the
        trace. We check for imacro-calling bytecodes inside each switch case to resolve
        the if (JSOP_IS_IMACOP(x)) conditions at compile time. */
 
     JSRecordingStatus status;
 #ifdef DEBUG
     bool wasInImacro = (cx->fp->imacpc != NULL);
@@ -5527,24 +5668,25 @@ js_AbortRecording(JSContext* cx, const c
     if (!f || f->lastIns) {
         js_DeleteRecorder(cx);
         return;
     }
 
     JS_ASSERT(!f->vmprivate);
 #ifdef DEBUG
     TreeInfo* ti = tm->recorder->getTreeInfo();
-    debug_only_a(nj_dprintf("Abort recording of tree %s:%d@%d at %s:%d@%d: %s.\n",
-                            ti->treeFileName,
-                            ti->treeLineNumber,
-                            ti->treePCOffset,
-                            cx->fp->script->filename,
-                            js_FramePCToLineNumber(cx, cx->fp),
-                            FramePCOffset(cx->fp),
-                            reason);)
+    debug_only_printf(LC_TMAbort,
+                      "Abort recording of tree %s:%d@%d at %s:%d@%d: %s.\n",
+                      ti->treeFileName,
+                      ti->treeLineNumber,
+                      ti->treePCOffset,
+                      cx->fp->script->filename,
+                      js_FramePCToLineNumber(cx, cx->fp),
+                      FramePCOffset(cx->fp),
+                      reason);
 #endif
 
     js_Backoff(cx, (jsbytecode*) f->root->ip, f->root);
 
     /*
      * If js_DeleteRecorder flushed the code cache, we can't rely on f any more.
      */
     if (!js_DeleteRecorder(cx))
@@ -5794,16 +5936,26 @@ js_SetMaxCodeCacheBytes(JSContext* cx, u
     if (bytes < 128 K)
         bytes = 128 K;
     tm->maxCodeCacheBytes = bytes;
 }
 
 void
 js_InitJIT(JSTraceMonitor *tm)
 {
+#if defined JS_JIT_SPEW
+    /* Set up debug logging */
+    if (!did_we_set_up_debug_logging) {
+        js_InitJITLogController();
+        did_we_set_up_debug_logging = true;
+    }
+#else
+    memset(&js_LogController, 0, sizeof(js_LogController));
+#endif
+
     if (!did_we_check_processor_features) {
 #if defined NANOJIT_IA32
         avmplus::AvmCore::config.use_cmov =
         avmplus::AvmCore::config.sse2 = js_CheckForSSE2();
 #endif
 #if defined NANOJIT_ARM
         bool            arm_vfp     = js_arm_check_vfp();
         bool            arm_thumb   = js_arm_check_thumb();
@@ -5837,57 +5989,59 @@ js_InitJIT(JSTraceMonitor *tm)
     if (!tm->recordAttempts.ops) {
         JS_DHashTableInit(&tm->recordAttempts, JS_DHashGetStubOps(),
                           NULL, sizeof(PCHashEntry),
                           JS_DHASH_DEFAULT_CAPACITY(PC_HASH_COUNT));
     }
 
     if (!tm->fragmento) {
         JS_ASSERT(!tm->reservedDoublePool);
-        Fragmento* fragmento = new (&gc) Fragmento(core, 32);
+        Fragmento* fragmento = new (&gc) Fragmento(core, &js_LogController, 32);
         verbose_only(fragmento->labels = new (&gc) LabelMap(core);)
         tm->fragmento = fragmento;
         tm->lirbuf = new (&gc) LirBuffer(fragmento);
 #ifdef DEBUG
         tm->lirbuf->names = new (&gc) LirNameMap(&gc, tm->fragmento->labels);
 #endif
         for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) {
             tm->globalStates[i].globalShape = -1;
             JS_ASSERT(!tm->globalStates[i].globalSlots);
             tm->globalStates[i].globalSlots = new (&gc) SlotList();
         }
         tm->reservedDoublePoolPtr = tm->reservedDoublePool = new jsval[MAX_NATIVE_STACK_SLOTS];
         memset(tm->vmfragments, 0, sizeof(tm->vmfragments));
     }
     if (!tm->reFragmento) {
-        Fragmento* fragmento = new (&gc) Fragmento(core, 32);
+        Fragmento* fragmento = new (&gc) Fragmento(core, &js_LogController, 32);
         verbose_only(fragmento->labels = new (&gc) LabelMap(core);)
         tm->reFragmento = fragmento;
         tm->reLirBuf = new (&gc) LirBuffer(fragmento);
     }
 #if !defined XP_WIN
     debug_only(memset(&jitstats, 0, sizeof(jitstats)));
 #endif
 }
 
 void
 js_FinishJIT(JSTraceMonitor *tm)
 {
 #ifdef JS_JIT_SPEW
-    if (js_verboseStats && jitstats.recorderStarted) {
-        nj_dprintf("recorder: started(%llu), aborted(%llu), completed(%llu), different header(%llu), "
-                   "trees trashed(%llu), slot promoted(%llu), unstable loop variable(%llu), "
-                   "breaks(%llu), returns(%llu), unstableInnerCalls(%llu), blacklisted(%llu)\n",
-                   jitstats.recorderStarted, jitstats.recorderAborted, jitstats.traceCompleted,
-                   jitstats.returnToDifferentLoopHeader, jitstats.treesTrashed, jitstats.slotPromoted,
-                   jitstats.unstableLoopVariable, jitstats.breakLoopExits, jitstats.returnLoopExits,
-                   jitstats.noCompatInnerTrees, jitstats.blacklisted);
-        nj_dprintf("monitor: triggered(%llu), exits(%llu), type mismatch(%llu), "
-                   "global mismatch(%llu)\n", jitstats.traceTriggered, jitstats.sideExitIntoInterpreter,
-                   jitstats.typeMapMismatchAtEntry, jitstats.globalShapeMismatchAtEntry);
+    if (jitstats.recorderStarted) {
+        debug_only_printf(LC_TMStats,
+                          "recorder: started(%llu), aborted(%llu), completed(%llu), different header(%llu), "
+                          "trees trashed(%llu), slot promoted(%llu), unstable loop variable(%llu), "
+                          "breaks(%llu), returns(%llu), unstableInnerCalls(%llu), blacklisted(%llu)\n",
+                          jitstats.recorderStarted, jitstats.recorderAborted, jitstats.traceCompleted,
+                          jitstats.returnToDifferentLoopHeader, jitstats.treesTrashed, jitstats.slotPromoted,
+                          jitstats.unstableLoopVariable, jitstats.breakLoopExits, jitstats.returnLoopExits,
+                          jitstats.noCompatInnerTrees, jitstats.blacklisted);
+        debug_only_printf(LC_TMStats,
+                          "monitor: triggered(%llu), exits(%llu), type mismatch(%llu), "
+                          "global mismatch(%llu)\n", jitstats.traceTriggered, jitstats.sideExitIntoInterpreter,
+                          jitstats.typeMapMismatchAtEntry, jitstats.globalShapeMismatchAtEntry);
     }
 #endif
     if (tm->fragmento != NULL) {
         JS_ASSERT(tm->reservedDoublePool);
         verbose_only(delete tm->fragmento->labels;)
 #ifdef DEBUG
         delete tm->lirbuf->names;
         tm->lirbuf->names = NULL;
@@ -5976,20 +6130,21 @@ js_IterateScriptFragments(JSContext* cx,
     for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
         for (VMFragment **f = &(tm->vmfragments[i]); *f; ) {
             VMFragment* frag = *f;
             /* Disable future use of any script-associated VMFragment.*/
             if (JS_UPTRDIFF(frag->ip, script->code) < script->length) {
                 JS_ASSERT(frag->root == frag);
                 VMFragment* next = frag->next;
                 if (action(cx, tm, frag)) {
-                    debug_only_v(nj_dprintf("Disconnecting VMFragment %p "
-                                            "with ip %p, in range [%p,%p).\n",
-                                            (void*)frag, frag->ip, script->code,
-                                            script->code + script->length));
+                    debug_only_printf(LC_TMTracer,
+                                      "Disconnecting VMFragment %p "
+                                      "with ip %p, in range [%p,%p).\n",
+                                      (void*)frag, frag->ip, script->code,
+                                      script->code + script->length);
                     *f = next;
                 } else {
                     f = &((*f)->next);
                 }
             } else {
                 f = &((*f)->next);
             }
         }
@@ -6011,17 +6166,18 @@ clearFragmentAction(JSContext* cx, JSTra
     return true;
 }
 
 JS_REQUIRES_STACK void
 js_PurgeScriptFragments(JSContext* cx, JSScript* script)
 {
     if (!TRACING_ENABLED(cx))
         return;
-    debug_only_v(nj_dprintf("Purging fragments for JSScript %p.\n", (void*)script);)
+    debug_only_printf(LC_TMTracer,
+                      "Purging fragments for JSScript %p.\n", (void*)script);
     /*
      * js_TrashTree trashes dependent trees recursively, so we must do all the trashing
      * before clearing in order to avoid calling js_TrashTree with a deleted fragment.
      */
     js_IterateScriptFragments(cx, script, trashTreeAction);
     js_IterateScriptFragments(cx, script, clearFragmentAction);
     JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
     JS_DHashTableEnumerate(&(tm->recordAttempts),
@@ -6094,17 +6250,17 @@ js_DeepBail(JSContext *cx)
     JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx);
     JSContext *tracecx = tm->tracecx;
 
     /* It's a bug if a non-FAIL_STATUS builtin gets here. */
     JS_ASSERT(tracecx->bailExit);
 
     tm->tracecx = NULL;
     tm->prohibitFlush++;
-    debug_only_v(nj_dprintf("Deep bail.\n");)
+    debug_only_print0(LC_TMTracer, "Deep bail.\n");
     LeaveTree(*tracecx->interpState, tracecx->bailExit);
     tracecx->bailExit = NULL;
     tracecx->interpState->builtinStatus |= JSBUILTIN_BAILED;
 }
 
 JS_REQUIRES_STACK jsval&
 TraceRecorder::argval(unsigned n) const
 {
@@ -7665,22 +7821,25 @@ TraceRecorder::record_EnterFrame()
 
     if (++callDepth >= MAX_CALLDEPTH)
         ABORT_TRACE("exceeded maximum call depth");
     // FIXME: Allow and attempt to inline a single level of recursion until we compile
     //        recursive calls as independent trees (459301).
     if (fp->script == fp->down->script && fp->down->down && fp->down->down->script == fp->script)
         ABORT_TRACE("recursive call");
 
-    debug_only_v(nj_dprintf("EnterFrame %s, callDepth=%d\n",
-                            js_AtomToPrintableString(cx, cx->fp->fun->atom),
-                            callDepth);)
-    debug_only_v(
-        js_Disassemble(cx, cx->fp->script, JS_TRUE, stdout);
-        nj_dprintf("----\n");)
+    debug_only_printf(LC_TMTracer, "EnterFrame %s, callDepth=%d\n",
+                      js_AtomToPrintableString(cx, cx->fp->fun->atom),
+                      callDepth);
+    debug_only_stmt(
+        if (js_LogController.lcbits & LC_TMRecorder) {
+            js_Disassemble(cx, cx->fp->script, JS_TRUE, stdout);
+            debug_only_print0(LC_TMTracer, "----\n");
+        }
+    )
     LIns* void_ins = INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
 
     jsval* vp = &fp->argv[fp->argc];
     jsval* vpstop = vp + ptrdiff_t(fp->fun->nargs) - ptrdiff_t(fp->argc);
     while (vp < vpstop) {
         if (vp >= fp->down->regs->sp)
             nativeFrameTracker.set(vp, (LIns*)0);
         set(vp++, void_ins, true);
@@ -7691,21 +7850,22 @@ TraceRecorder::record_EnterFrame()
     while (vp < vpstop)
         set(vp++, void_ins, true);
     return JSRS_CONTINUE;
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
 TraceRecorder::record_LeaveFrame()
 {
-    debug_only_v(
+    debug_only_stmt(
         if (cx->fp->fun)
-            nj_dprintf("LeaveFrame (back to %s), callDepth=%d\n",
-                       js_AtomToPrintableString(cx, cx->fp->fun->atom),
-                       callDepth);
+            debug_only_printf(LC_TMTracer,
+                              "LeaveFrame (back to %s), callDepth=%d\n",
+                              js_AtomToPrintableString(cx, cx->fp->fun->atom),
+                              callDepth);
         );
     if (callDepth-- <= 0)
         ABORT_TRACE("returned out of a loop we started tracing");
 
     // LeaveFrame gets called after the interpreter popped the frame and
     // stored rval, so cx->fp not cx->fp->down, and -1 not 0.
     atoms = FrameAtomBase(cx, cx->fp);
     set(&stackval(-1), rval_ins, true);
@@ -7760,17 +7920,19 @@ TraceRecorder::record_JSOP_RETURN()
     jsval& rval = stackval(-1);
     JSStackFrame *fp = cx->fp;
     if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && JSVAL_IS_PRIMITIVE(rval)) {
         JS_ASSERT(OBJECT_TO_JSVAL(fp->thisp) == fp->argv[-1]);
         rval_ins = get(&fp->argv[-1]);
     } else {
         rval_ins = get(&rval);
     }
-    debug_only_v(nj_dprintf("returning from %s\n", js_AtomToPrintableString(cx, cx->fp->fun->atom));)
+    debug_only_printf(LC_TMTracer,
+                      "returning from %s\n",
+                      js_AtomToPrintableString(cx, cx->fp->fun->atom));
     clearFrameSlotsFromCache();
 
     return JSRS_CONTINUE;
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
 TraceRecorder::record_JSOP_GOTO()
 {
@@ -11085,39 +11247,39 @@ TraceRecorder::record_JSOP_CALLPROP()
     LIns* obj_ins;
     LIns* this_ins;
     if (!JSVAL_IS_PRIMITIVE(l)) {
         obj = JSVAL_TO_OBJECT(l);
         obj_ins = get(&l);
         this_ins = obj_ins; // |this| for subsequent call
     } else {
         jsint i;
-        debug_only(const char* protoname = NULL;)
+        debug_only_stmt(const char* protoname = NULL;)
         if (JSVAL_IS_STRING(l)) {
             i = JSProto_String;
-            debug_only(protoname = "String.prototype";)
+            debug_only_stmt(protoname = "String.prototype";)
         } else if (JSVAL_IS_NUMBER(l)) {
             i = JSProto_Number;
-            debug_only(protoname = "Number.prototype";)
+            debug_only_stmt(protoname = "Number.prototype";)
         } else if (JSVAL_TAG(l) == JSVAL_BOOLEAN) {
             if (l == JSVAL_VOID)
                 ABORT_TRACE("callprop on void");
             guard(false, lir->ins2i(LIR_eq, get(&l), JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID)), MISMATCH_EXIT);
             i = JSProto_Boolean;
-            debug_only(protoname = "Boolean.prototype";)
+            debug_only_stmt(protoname = "Boolean.prototype";)
         } else {
             JS_ASSERT(JSVAL_IS_NULL(l) || JSVAL_IS_VOID(l));
             ABORT_TRACE("callprop on null or void");
         }
 
         if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj))
             ABORT_TRACE_ERROR("GetClassPrototype failed!");
 
         obj_ins = INS_CONSTPTR(obj);
-        debug_only(obj_ins = addName(obj_ins, protoname);)
+        debug_only_stmt(obj_ins = addName(obj_ins, protoname);)
         this_ins = get(&l); // use primitive as |this|
     }
 
     JSObject* obj2;
     jsuword pcval;
     CHECK_STATUS(test_property_cache(obj, obj_ins, obj2, pcval));
 
     if (PCVAL_IS_NULL(pcval) || !PCVAL_IS_OBJECT(pcval))
@@ -11608,34 +11770,34 @@ js_DumpPeerStability(JSTraceMonitor* tm,
     Fragment* f;
     TreeInfo* ti;
     bool looped = false;
     unsigned length = 0;
 
     for (f = getLoop(tm, ip, globalObj, globalShape, argc); f != NULL; f = f->peer) {
         if (!f->vmprivate)
             continue;
-        nj_dprintf("fragment %p:\nENTRY: ", (void*)f);
+        debug_only_printf(LC_TMStats, "fragment %p:\nENTRY: ", (void*)f);
         ti = (TreeInfo*)f->vmprivate;
         if (looped)
             JS_ASSERT(ti->nStackTypes == length);
         for (unsigned i = 0; i < ti->nStackTypes; i++)
-            nj_dprintf("S%d ", ti->stackTypeMap()[i]);
+            debug_only_printf(LC_TMStats, "S%d ", ti->stackTypeMap()[i]);
         for (unsigned i = 0; i < ti->nGlobalTypes(); i++)
-            nj_dprintf("G%d ", ti->globalTypeMap()[i]);
-        nj_dprintf("\n");
+            debug_only_printf(LC_TMStats, "G%d ", ti->globalTypeMap()[i]);
+        debug_only_print0(LC_TMStats, "\n");
         UnstableExit* uexit = ti->unstableExits;
         while (uexit != NULL) {
-            nj_dprintf("EXIT:  ");
+            debug_only_print0(LC_TMStats, "EXIT:  ");
             uint8* m = getFullTypeMap(uexit->exit);
             for (unsigned i = 0; i < uexit->exit->numStackSlots; i++)
-                nj_dprintf("S%d ", m[i]);
+                debug_only_printf(LC_TMStats, "S%d ", m[i]);
             for (unsigned i = 0; i < uexit->exit->numGlobalSlots; i++)
-                nj_dprintf("G%d ", m[uexit->exit->numStackSlots + i]);
-            nj_dprintf("\n");
+                debug_only_printf(LC_TMStats, "G%d ", m[uexit->exit->numStackSlots + i]);
+            debug_only_print0(LC_TMStats, "\n");
             uexit = uexit->next;
         }
         length = ti->nStackTypes;
         looped = true;
     }
 }
 #endif
 
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -149,20 +149,49 @@ public:
 
     bool            has(const void* v) const;
     nanojit::LIns*  get(const void* v) const;
     void            set(const void* v, nanojit::LIns* ins);
     void            clear();
 };
 
 #ifdef JS_JIT_SPEW
-extern bool js_verboseDebug;
-#define debug_only_v(x) if (js_verboseDebug) { x; fflush(stdout); }
+
+enum LC_TMBits {
+    /* Output control bits for all non-Nanojit code.  Only use bits 16
+       and above, since Nanojit uses 0 .. 15 itself. */
+    LC_TMMinimal  = 1<<16,
+    LC_TMTracer   = 1<<17,
+    LC_TMRecorder = 1<<18,
+    LC_TMPatcher  = 1<<19,
+    LC_TMAbort    = 1<<20,
+    LC_TMStats    = 1<<21,
+    LC_TMRegexp   = 1<<22
+};
+
+// Top level logging controller object.
+extern nanojit::LogControl js_LogController;
+
+#define debug_only_stmt(stmt) \
+    stmt
+#define debug_only_printf(mask, fmt, ...) \
+    do { if ((js_LogController.lcbits & (mask)) > 0) {             \
+        js_LogController.printf(fmt, __VA_ARGS__); fflush(stdout); \
+    }} while (0)
+#define debug_only_print0(mask, str) \
+    do { if ((js_LogController.lcbits & (mask)) > 0) { \
+        js_LogController.printf(str); fflush(stdout);  \
+    }} while (0)
+
 #else
-#define debug_only_v(x)
+
+#define debug_only_stmt(action)            /* */
+#define debug_only_printf(mask, fmt, ...)  /* */
+#define debug_only_print0(mask, str)       /* */
+
 #endif
 
 /*
  * The oracle keeps track of hit counts for program counter locations, as
  * well as slots that should not be demoted to int because we know them to
  * overflow or they result in type-unstable traces. We are using simple
  * hash tables.  Collisions lead to loss of optimization (demotable slots
  * are not demoted, etc.) but have no correctness implications.
--- a/js/src/nanojit/Assembler.cpp
+++ b/js/src/nanojit/Assembler.cpp
@@ -132,37 +132,90 @@ namespace nanojit
                     flush();
 				block.add(i);//flush_add(i);
                 if (i->isop(LIR_label))
                     flushnext = true;
 			}
 			return i;
 		}
 	};
+
+	/* A listing filter for LIR, going through backwards.  It merely
+       passes its input to its output, but notes it down too.  When
+       destructed, prints out what went through.  Is intended to be
+       used to print arbitrary intermediate transformation stages of
+       LIR. */
+	class ReverseLister : public LirFilter
+	{
+		avmplus::GC* _gc;
+        LirNameMap*  _names;
+		const char*  _title;
+		StringList*  _strs;
+		LogControl*  _logc;
+	public:
+		ReverseLister(LirFilter* in, avmplus::GC* gc,
+					  LirNameMap* names, LogControl* logc, const char* title)
+			: LirFilter(in)
+		{
+			_gc    = gc;
+			_names = names;
+			_title = title;
+			_strs  = new StringList(gc);
+			_logc  = logc;
+		}
+
+		~ReverseLister()
+        {
+			_logc->printf("\n");
+			_logc->printf("=== BEGIN %s ===\n", _title);
+    		int i, j;
+			const char* prefix = "  ";
+			for (j = 0, i = _strs->size()-1; i >= 0; i--, j++) {
+				char* str = _strs->get(i);
+				_logc->printf("%s%02d: %s\n", prefix, j, str);
+				_gc->Free(str);
+			}
+			delete _strs;
+			_logc->printf("=== END %s ===\n", _title);
+			_logc->printf("\n");
+		}
+
+		LInsp read() 
+		{
+			LInsp i = in->read();
+			if (!i)
+				return i;
+			const char* str = _names->formatIns(i);
+			char* cpy = (char*)_gc->Alloc(strlen(str) + 1,  0/*AllocFlags*/);
+			strcpy(cpy, str);
+			_strs->add(cpy);
+			return i;
+		}
+	};
 #endif
 	
 	/**
 	 * Need the following:
 	 *
 	 *	- merging paths ( build a graph? ), possibly use external rep to drive codegen
 	 */
-    Assembler::Assembler(Fragmento* frago)
+    Assembler::Assembler(Fragmento* frago, LogControl* logc)
         : hasLoop(0)
         , _frago(frago)
         , _gc(frago->core()->gc)
         , _labels(_gc)
         , _patches(_gc)
         , pending_lives(_gc)
 		, config(frago->core()->config)
 	{
         AvmCore *core = frago->core();
 		nInit(core);
-		verbose_only( _verbose = !core->quiet_opt() && core->verbose() );
-		verbose_only( _outputCache = 0);
-		verbose_only( outlineEOL[0] = '\0');
+		verbose_only( _logc = logc; )
+		verbose_only( _outputCache = 0; )
+		verbose_only( outlineEOL[0] = '\0'; )
 		
 		internalReset();
 		pageReset();		
 	}
 
     void Assembler::arReset()
 	{
 		_activation.highwatermark = 0;
@@ -573,17 +626,19 @@ namespace nanojit
 		return rr;
 	}
 
 	void Assembler::asm_spilli(LInsp i, Reservation *resv, bool pop)
 	{
 		int d = disp(resv);
 		Register rr = resv->reg;
 		bool quad = i->opcode() == LIR_param || i->isQuad();
-		verbose_only( if (d && _verbose) { outputForEOL("  <= spill %s", _thisfrag->lirbuf->names->formatRef(i)); } )
+		verbose_only( if (d && (_logc->lcbits & LC_RegAlloc)) { 
+			             outputForEOL("  <= spill %s",
+									  _thisfrag->lirbuf->names->formatRef(i)); } )
 		asm_spill(rr, d, pop, quad);
 	}
 
     // NOTE: Because this function frees slots on the stack, it is not safe to
     // follow a call to this with a call to anything which might spill a
     // register, as the stack can be corrupted. Refer to bug 495239 for a more
     // detailed description.
 	void Assembler::freeRsrcOf(LIns *i, bool pop)
@@ -648,33 +703,31 @@ namespace nanojit
 		{
 			at = asm_leave_trace(guard);
 		}
 		else
 		{
 			RegAlloc* captured = _branchStateMap->get(exit);
 			intersectRegisterState(*captured);
 			verbose_only(
-				verbose_outputf("        merging trunk with %s",
+				verbose_outputf("## merging trunk with %s",
 					_frago->labels->format(exit->target));
-				verbose_outputf("        %p:",_nIns);
+				verbose_outputf("%010lx:", (unsigned long)_nIns);
 			)			
 			at = exit->target->fragEntry;
 			NanoAssert(at != 0);
 			_branchStateMap->remove(exit);
 		}
 		return at;
 	}
 	
 	NIns* Assembler::asm_leave_trace(LInsp guard)
 	{
-        verbose_only(bool priorVerbose = _verbose; )
-		verbose_only( _verbose = verbose_enabled() && _frago->core()->config.verbose_exits; )
         verbose_only( int32_t nativeSave = _stats.native );
-		verbose_only(verbose_outputf("--------------------------------------- end exit block %p", guard);)
+		verbose_only( verbose_outputf("----------------------------------- ## END exit block %p", guard);)
 
 		RegAlloc capture = _allocator;
 
         // this point is unreachable.  so free all the registers.
 		// if an instruction has a stack entry we will leave it alone,
 		// otherwise we free it entirely.  intersectRegisterState will restore.
 		releaseRegisters();
 		
@@ -699,25 +752,24 @@ namespace nanojit
 		// we are done producing the exit logic for the guard so demark where our exit block code begins
 		NIns* jmpTarget = _nIns;	 // target in exit path for our mainline conditional jump 
 
 		// swap back pointers, effectively storing the last location used in the exit path
 		swapptrs();
 		_inExit = false;
 		
 		//verbose_only( verbose_outputf("         LIR_xt/xf swapptrs, _nIns is now %08X(%08X), _nExitIns is now %08X(%08X)",_nIns, *_nIns,_nExitIns,*_nExitIns) );
-		verbose_only( verbose_outputf("        %p:",jmpTarget);)
-		verbose_only( verbose_outputf("--------------------------------------- exit block (LIR_xt|LIR_xf)") );
+		verbose_only( verbose_outputf("%010lx:", (unsigned long)jmpTarget);)
+		verbose_only( verbose_outputf("----------------------------------- ## BEGIN exit block (LIR_xt|LIR_xf)") );
 
 #ifdef NANOJIT_IA32
 		NanoAssertMsgf(_fpuStkDepth == _sv_fpuStkDepth, "LIR_xtf, _fpuStkDepth=%d, expect %d",_fpuStkDepth, _sv_fpuStkDepth);
 		debug_only( _fpuStkDepth = _sv_fpuStkDepth; _sv_fpuStkDepth = 9999; )
 #endif
 
-        verbose_only( _verbose = priorVerbose; )
         verbose_only(_stats.exitnative += (_stats.native-nativeSave));
 
         return jmpTarget;
     }
 	
 	void Assembler::beginAssembly(Fragment *frag, RegAllocMap* branchStateMap)
 	{
 		internalReset();
@@ -766,34 +818,83 @@ namespace nanojit
 	}
 	
 	void Assembler::assemble(Fragment* frag,  NInsList& loopJumps)
 	{
 		if (error()) return;	
 		AvmCore *core = _frago->core();
         _thisfrag = frag;
 
+		// Used for debug printing, if needed
+		verbose_only(
+		ReverseLister *pp_init = NULL,
+            		  *pp_after_sf1 = NULL,
+			          *pp_after_sf2 = NULL,
+			          *pp_after_dead = NULL;
+    	)
+
 		// set up backwards pipeline: assembler -> StackFilter -> LirReader
-		LirReader bufreader(frag->lastIns);
 		avmplus::GC *gc = core->gc;
-		StackFilter storefilter1(&bufreader, gc, frag->lirbuf, frag->lirbuf->sp);
-		StackFilter storefilter2(&storefilter1, gc, frag->lirbuf, frag->lirbuf->rp);
-		DeadCodeFilter deadfilter(&storefilter2);
-		LirFilter* rdr = &deadfilter;
+		LirReader bufreader(frag->lastIns);
+
+		// Used to construct the pipeline
+		LirFilter* prev = &bufreader;
+
+		// The LIR passes through these filters as listed in this
+		// function, viz, top to bottom.
+
+		// INITIAL PRINTING
+		verbose_only( if (_logc->lcbits & LC_ReadLIR) {
+		pp_init = new ReverseLister(prev, gc, frag->lirbuf->names, _logc,
+									"Initial LIR");
+		prev = pp_init;
+		})
+
+		// STOREFILTER for sp
+		StackFilter storefilter1(prev, gc, frag->lirbuf, frag->lirbuf->sp);
+		prev = &storefilter1;
+
+		verbose_only( if (_logc->lcbits & LC_AfterSF_SP) {
+		pp_after_sf1 = new ReverseLister(prev, gc, frag->lirbuf->names, _logc,
+										 "After Storefilter(sp)");
+		prev = pp_after_sf1;
+		})
+
+		// STOREFILTER for rp
+		StackFilter storefilter2(prev, gc, frag->lirbuf, frag->lirbuf->rp);
+		prev = &storefilter2;
+
+		verbose_only( if (_logc->lcbits & LC_AfterSF_RP) {
+		pp_after_sf2 = new ReverseLister(prev, gc, frag->lirbuf->names, _logc,
+										 "After StoreFilter(rp)");
+		prev = pp_after_sf2;
+		})
+
+		// DEAD CODE FILTER
+		DeadCodeFilter deadfilter(prev);
+		prev = &deadfilter;
+
+		verbose_only( if (_logc->lcbits & LC_AfterDeadF) {
+		pp_after_dead = new ReverseLister(prev, gc, frag->lirbuf->names, _logc,
+										  "After DeadFilter == Final LIR");
+		prev = pp_after_dead;
+		})
+
+		// end of pipeline
 		verbose_only(
-			VerboseBlockReader vbr(rdr, this, frag->lirbuf->names);
-			if (verbose_enabled())
-				rdr = &vbr;
+		VerboseBlockReader vbr(prev, this, frag->lirbuf->names);
+		if (_logc->lcbits & LC_Assembly)
+			prev = &vbr;
 		)
 
 		verbose_only(_thisfrag->compileNbr++; )
 		verbose_only(_frago->_stats.compiles++; )
 		verbose_only(_frago->_stats.totalCompiles++; )
 		_inExit = false;	
-        gen(rdr, loopJumps);
+        gen(prev, loopJumps);
 		frag->loopEntry = _nIns;
 		//frag->outbound = core->config.tree_opt? _latestGuard : 0;
 		//nj_dprintf("assemble frag %X entry %X\n", (int)frag, (int)frag->fragEntry);
 
         if (!error()) {
 		    // patch all branches
 		    while(!_patches.isEmpty())
 		    {
@@ -809,37 +910,48 @@ namespace nanojit
 				    break;
 			    }
 		    }
         }
 		else {
             // In case of failure, reset _nIns ready for the next assembly run.
             resetInstructionPointer();
 		}
+
+		// If we were accumulating debug info in the various ReverseListers,
+		// destruct them now.  Their destructors cause them to emit whatever
+		// contents they have accumulated.
+		verbose_only(
+		if (pp_init)       delete pp_init;
+		if (pp_after_sf1)  delete pp_after_sf1;
+		if (pp_after_sf2)  delete pp_after_sf2;
+		if (pp_after_dead) delete pp_after_dead;
+   	    )
 	}
 
 	void Assembler::endAssembly(Fragment* frag, NInsList& loopJumps)
 	{
 		// don't try to patch code if we are in an error state since we might have partially 
 		// overwritten the code cache already
 		if (error())
 			return;
 
 	    NIns* SOT = 0;
 	    if (frag->isRoot()) {
 	        SOT = frag->loopEntry;
-            verbose_only( verbose_outputf("        %p:",_nIns); )
+            verbose_only( verbose_outputf("%010lx:", (unsigned long)_nIns); )
 	    } else {
 	        SOT = frag->root->fragEntry;
 	    }
         AvmAssert(SOT);
 		while(!loopJumps.isEmpty())
 		{
 			NIns* loopJump = (NIns*)loopJumps.removeLast();
-            verbose_only( verbose_outputf("patching %p to %p", loopJump, SOT); )
+            verbose_only( verbose_outputf("## patching branch at %010lx to %010lx",
+										  loopJump, SOT); )
 			nPatchBranch(loopJump, SOT);
 		}
 
 		NIns* fragEntry = 0;
 		
 		if (!error())
 		{
 			fragEntry = genPrologue();
@@ -1307,17 +1419,17 @@ namespace nanojit
                     else {
                         // we're at the top of a loop
                         hasLoop = true;
                         NanoAssert(label->addr == 0 && label->regs.isValid());
                         //evictRegs(~_allocator.free);
                         intersectRegisterState(label->regs);
                         label->addr = _nIns;
                     }
-					verbose_only( if (_verbose) { outputAddr=true; asm_output("[%s]", _thisfrag->lirbuf->names->formatRef(ins)); } )
+					verbose_only( if (_logc->lcbits & LC_Assembly) { outputAddr=true; asm_output("[%s]", _thisfrag->lirbuf->names->formatRef(ins)); } )
 					break;
 				}
 				case LIR_xbarrier: {
 					break;
 				}
 #ifdef NANOJIT_IA32
 				case LIR_xtbl: {
                     NIns* exit = asm_exit(ins); // does intersectRegisterState()
@@ -1337,17 +1449,18 @@ namespace nanojit
                     NIns* exit = asm_exit(ins); // does intersectRegisterState()
 					LIns* cond = ins->oprnd1();
 					asm_branch(op == LIR_xf, cond, exit, false);
 					break;
 				}
 				case LIR_x:
 				{
                     countlir_x();
-		            verbose_only(verbose_output(""));
+		            verbose_only( if (_logc->lcbits & LC_Assembly)
+									  asm_output("FIXME-whats-this?\n"); )
 					// generate the side exit branch on the main trace.
                     NIns *exit = asm_exit(ins);
 					JMP( exit ); 
 					break;
 				}
 				case LIR_loop:
 				{
                     countlir_loop();
@@ -1502,17 +1615,17 @@ namespace nanojit
 	{
 		bool verbose_activation = false;
 		if (!verbose_activation)
 			return;
 			
 #ifdef NANOJIT_ARM
 		// @todo Why is there here?!?  This routine should be indep. of platform
 		verbose_only(
-			if (_verbose) {
+			if (_logc->lcbits & LC_Assembly) {
 				char* s = &outline[0];
 				memset(s, ' ', 51);  s[51] = '\0';
 				s += strlen(s);
 				sprintf(s, " SP ");
 				s += strlen(s);
 				for(uint32_t i=_activation.lowwatermark; i<_activation.tos;i++) {
 					LInsp ins = _activation.entry[i];
 					if (ins && ins !=_activation.entry[i+1]) {
@@ -1521,17 +1634,17 @@ namespace nanojit
 					}
 				}
 				output(&outline[0]);
 			}
 		)
 #else
 		verbose_only(
 			char* s = &outline[0];
-			if (_verbose) {
+			if (_logc->lcbits & LC_Assembly) {
 				memset(s, ' ', 51);  s[51] = '\0';
 				s += strlen(s);
 				sprintf(s, " ebp ");
 				s += strlen(s);
 
 				for(uint32_t i=_activation.lowwatermark; i<_activation.tos;i++) {
 					LInsp ins = _activation.entry[i];
 					if (ins) {
@@ -1728,17 +1841,21 @@ namespace nanojit
 				if (savedins && (rmask(r) & x87Regs)) {
 					verbose_only( shouldMention=true; )
 					FSTP(r);
 				}
 				#endif
 			}
 		}
         assignSaved(saved, skip);
-		verbose_only( if (shouldMention) verbose_outputf("                                              merging registers (intersect) with existing edge");  )
+		verbose_only(
+		    if (shouldMention)
+                verbose_outputf("## merging registers (intersect) "
+                                "with existing edge");
+        )
 	}
 
 	/**
 	 * Merge the current state of the registers with a previously stored version.
      * 
      * current == saved    skip
      * current & saved     evict current, keep saved
      * current & !saved    keep current (intersectRegisterState would evict)
@@ -1858,23 +1975,23 @@ namespace nanojit
             if (_outputCache)
             {
                 char* str = (char*)_gc->Alloc(strlen(s)+1);
                 strcpy(str, s);
                 _outputCache->add(str);
             }
             else
             {
-                nj_dprintf("%s\n", s);
+                _logc->printf("%s\n", s);
             }
         }
 
         void Assembler::output_asm(const char* s)
         {
-            if (!verbose_enabled())
+            if (!(_logc->lcbits & LC_Assembly))
                 return;
 
             // Add the EOL string to the output, ensuring that we leave enough
             // space for the terminating NULL character, then reset it so it
             // doesn't repeat on the next outputf.
             strncat(outline, outlineEOL, sizeof(outline)-(strlen(outline)+1));
             outlineEOL[0] = '\0';
 
--- a/js/src/nanojit/Assembler.h
+++ b/js/src/nanojit/Assembler.h
@@ -154,23 +154,29 @@ namespace nanojit
 			static char  outlineEOL[512];  // string to be added to the end of the line
 			static char* outputAlign(char* s, int col); 
 
 			void FASTCALL outputForEOL(const char* format, ...);
 			void FASTCALL output(const char* s); 
 			void FASTCALL outputf(const char* format, ...); 
 			void FASTCALL output_asm(const char* s); 
 			
-			bool _verbose, outputAddr, vpad[2];  // if outputAddr=true then next asm instr. will include address in output
+			// if outputAddr=true then next asm instr. will include
+			// address in output
+			bool outputAddr, vpad[2];
 			void printActivationState();
 
 			StringList* _outputCache;
+
+			// Log controller object.  Contains what-stuff-should-we-print
+			// bits, and a sink function for debug printing
+			LogControl* _logc;
 			#endif
 
-			Assembler(Fragmento* frago);
+			Assembler(Fragmento* frago, LogControl* logc);
             ~Assembler() {}
 
 			void		assemble(Fragment* frag, NInsList& loopJumps);
 			void		endAssembly(Fragment* frag, NInsList& loopJumps);
 			void		beginAssembly(Fragment *frag, RegAllocMap* map);
 			void		copyRegisters(RegAlloc* copyTo);
 			void		releaseRegisters();
             void        patch(GuardRecord *lr);
--- a/js/src/nanojit/Fragmento.cpp
+++ b/js/src/nanojit/Fragmento.cpp
@@ -52,17 +52,17 @@ namespace nanojit
 		if (in < uint32_t(NJ_LOG2_PAGE_SIZE)) return NJ_LOG2_PAGE_SIZE;	// at least 1 page
 		if (in > 32) return 32;	// 4GB should be enough for anyone
 		return in;
 	}
 
 	/**
 	 * This is the main control center for creating and managing fragments.
 	 */
-	Fragmento::Fragmento(AvmCore* core, uint32_t cacheSizeLog2) 
+	Fragmento::Fragmento(AvmCore* core, LogControl* logc, uint32_t cacheSizeLog2) 
 		:
 #ifdef NJ_VERBOSE
 		  enterCounts(NULL),
 		  mergeCounts(NULL),
 		  labels(NULL),
 #endif
 		  _frags(core->GetGC()),
 		  _freePages(core->GetGC(), 1024),
@@ -94,17 +94,17 @@ namespace nanojit
 #endif
 
 #ifdef MEMORY_INFO
 		_allocList.set_meminfo_name("Fragmento._allocList");
 #endif
 		NanoAssert(_max_pages > _pagesGrowth); // shrink growth if needed 
 		_core = core;
 		GC *gc = core->GetGC();
-		_assm = NJ_NEW(gc, nanojit::Assembler)(this);
+		_assm = NJ_NEW(gc, nanojit::Assembler)(this, logc);
 		verbose_only( enterCounts = NJ_NEW(gc, BlockHist)(gc); )
 		verbose_only( mergeCounts = NJ_NEW(gc, BlockHist)(gc); )
 
 		memset(&_stats, 0, sizeof(_stats));
 	}
 
 	Fragmento::~Fragmento()
 	{
@@ -379,36 +379,32 @@ namespace nanojit
 			}
 			total += c;
 		}
 		_assm->outputf("%s total %d unique %d ratio %.1f%", label, total, unique, double(total)/unique);
 	}
 
 	void Fragmento::dumpStats()
 	{
-		bool vsave = _assm->_verbose;
-		_assm->_verbose = true;
-
 		_assm->output("");
 		dumpRatio("inline", enterCounts);
 		dumpRatio("merges", mergeCounts);
 		_assm->outputf("abc %d il %d (%.1fx) abc+il %d (%.1fx)",
 			_stats.abcsize, _stats.ilsize, (double)_stats.ilsize/_stats.abcsize,
 			_stats.abcsize + _stats.ilsize,
 			double(_stats.abcsize+_stats.ilsize)/_stats.abcsize);
 
 		int32_t count = _frags.size();
 		int32_t pages =  _stats.pages;
 		int32_t maxPageUse =  _stats.maxPageUse;
 		int32_t free = _freePages.size();
 		int32_t flushes = _stats.flushes;
 		if (!count)
 		{
 			_assm->outputf("No fragments in cache, %d flushes", flushes);
-    		_assm->_verbose = vsave;
             return;
 		}
 
         _assm->outputf("\nFragment statistics");
 		_assm->outputf("  loop trees:     %d", count);
 		_assm->outputf("  flushes:        %d", flushes);
 		_assm->outputf("  compiles:       %d / %d", _stats.compiles, _stats.totalCompiles);
 		_assm->outputf("  used:           %dk / %dk", (pages-free)<<(NJ_LOG2_PAGE_SIZE-10), pages<<(NJ_LOG2_PAGE_SIZE-10));
@@ -463,24 +459,22 @@ namespace nanojit
 				labels->format(d.frag),
 				bothDur/1000, int(100.0*bothDur/totaldur),
 				d.traceDur/1000, int(100.0*d.traceDur/totaldur),
 				d.interpDur/1000, int(100.0*d.interpDur/totaldur),
 				size, int(100.0*size/totalsize),
 				labels->format(d.frag->ip));
 		}
 
-		_assm->_verbose = vsave;
-
 	}
 
 	void Fragmento::countBlock(BlockHist *hist, const void* ip)
 	{
 		int c = hist->count(ip);
-		if (_assm->_verbose)
+		if (_assm->_logc->lcbits & LC_Assembly)
 			_assm->outputf("++ %s %d", labels->format(ip), c);
 	}
 
 	void Fragmento::countIL(uint32_t il, uint32_t abc)
 	{
 		_stats.ilsize += il;
 		_stats.abcsize += abc;
 	}
--- a/js/src/nanojit/Fragmento.h
+++ b/js/src/nanojit/Fragmento.h
@@ -89,17 +89,17 @@ namespace nanojit
 	struct fragstats;
 	/*
 	 *
 	 * This is the main control center for creating and managing fragments.
 	 */
 	class Fragmento : public avmplus::GCFinalizedObject
 	{
 		public:
-			Fragmento(AvmCore* core, uint32_t cacheSizeLog2);
+			Fragmento(AvmCore* core, LogControl* logc, uint32_t cacheSizeLog2);
 			~Fragmento();
 
 			void		addMemory(void* firstPage, uint32_t pageCount);  // gives memory to the Assembler
 			Assembler*	assm();
 			AvmCore*	core();
 			Page*		pageAlloc();
 			void		pageFree(Page* page);
 			void		pagesRelease(PageList& list);
--- a/js/src/nanojit/LIR.cpp
+++ b/js/src/nanojit/LIR.cpp
@@ -40,28 +40,16 @@
 #include <stdio.h>
 #include <ctype.h>
 
 #ifdef PERFM
 #include "../vprof/vprof.h"
 #endif /* PERFM */
 
 
-#if defined(NJ_VERBOSE)
-void nj_dprintf( const char* format, ... )
-{
-	va_list vargs;
-	va_start(vargs, format);
-	vfprintf(stdout, format, vargs);
-	va_end(vargs);
-}
-#endif /* NJ_VERBOSE */
-
-
-
 namespace nanojit
 {
     using namespace avmplus;
 	#ifdef FEATURE_NANOJIT
 
 	const uint8_t operandCount[] = {
 #define OPDEF(op, number, operands) \
         operands,
@@ -1481,17 +1469,17 @@ namespace nanojit
             live.remove(i);
             retired.add(e);
 		}
 		bool contains(LInsp i) {
 			return live.containsKey(i);
 		}
 	};
 
-    void live(GC *gc, Fragment *frag)
+    void live(GC *gc, Fragment *frag, LogControl *logc)
 	{
 		// traverse backwards to find live exprs and a few other stats.
 
 		LiveTable live(gc);
 		uint32_t exits = 0;
         LirReader br(frag->lastIns);
 		StackFilter sf(&br, gc, frag->lirbuf, frag->lirbuf->sp);
 		StackFilter r(&sf, gc, frag->lirbuf, frag->lirbuf->rp);
@@ -1533,41 +1521,52 @@ namespace nanojit
 				}
 				else if (i->isCall()) {
 					for (int j=0, c=i->argc(); j < c; j++)
 						live.add(i->arg(j),i);
 				}
 			}
 		}
  
-		nj_dprintf("live instruction count %d, total %u, max pressure %d\n",
-			live.retired.size(), total, live.maxlive);
-        nj_dprintf("side exits %u\n", exits);
+		logc->printf("  Live instruction count %d, total %u, max pressure %d\n",
+					 live.retired.size(), total, live.maxlive);
+        logc->printf("  Side exits %u\n", exits);
+		logc->printf("  Showing LIR instructions with live-after variables\n");
+		logc->printf("\n");
 
 		// print live exprs, going forwards
 		LirNameMap *names = frag->lirbuf->names;
         bool newblock = true;
 		for (int j=live.retired.size()-1; j >= 0; j--) 
         {
             RetiredEntry *e = live.retired[j];
             char livebuf[4000], *s=livebuf;
             *s = 0;
             if (!newblock && e->i->isop(LIR_label)) {
-                nj_dprintf("\n");
+                logc->printf("\n");
             }
             newblock = false;
             for (int k=0,n=e->live.size(); k < n; k++) {
 				strcpy(s, names->formatRef(e->live[k]));
 				s += strlen(s);
 				*s++ = ' '; *s = 0;
 				NanoAssert(s < livebuf+sizeof(livebuf));
             }
-			nj_dprintf("%-60s %s\n", livebuf, names->formatIns(e->i));
+			/* If the LIR insn is pretty short, print it and its
+			   live-after set on the same line.  If not, put
+			   live-after set on a new line, suitably indented. */
+			const char* insn_text = names->formatIns(e->i);
+			if (strlen(insn_text) >= 30-2) {
+				logc->printf("  %-30s\n  %-30s %s\n", names->formatIns(e->i), "", livebuf);
+			} else {
+				logc->printf("  %-30s %s\n", names->formatIns(e->i), livebuf);
+			}
+
             if (e->i->isGuard() || e->i->isBranch() || e->i->isRet()) {
-				nj_dprintf("\n");
+				logc->printf("\n");
                 newblock = true;
             }
 		}
 	}
 
     LabelMap::Entry::~Entry()
     {
     }
@@ -1960,87 +1959,164 @@ namespace nanojit
 	}
 
     void compile(Assembler* assm, Fragment* triggerFrag)
     {
         Fragmento *frago = triggerFrag->lirbuf->_frago;
         AvmCore *core = frago->core();
         GC *gc = core->gc;
 
+		verbose_only(
+		LogControl *logc = assm->_logc;
+		bool anyVerb = (logc->lcbits & 0xFFFF) > 0;
+		bool asmVerb = (logc->lcbits & 0xFFFF & LC_Assembly) > 0;
+		bool liveVerb = (logc->lcbits & 0xFFFF & LC_Liveness) > 0;
+		)
+
+		/* BEGIN decorative preamble */
+		verbose_only( 
+        if (anyVerb) {
+			logc->printf("========================================"
+						 "========================================\n");
+			logc->printf("=== BEGIN LIR::compile(%p, %p)\n",
+						 (void*)assm, (void*)triggerFrag);
+		    logc->printf("===\n");
+		})
+		/* END decorative preamble */
+
+		verbose_only( if (liveVerb) {
+		    logc->printf("\n");
+			logc->printf("=== Results of liveness analysis:\n");
+		    logc->printf("===\n");
+			live(gc, triggerFrag, logc);
+		})
+
+		/* Set up the generic text output cache for the assembler */
 		verbose_only( StringList asmOutput(gc); )
 		verbose_only( assm->_outputCache = &asmOutput; )
 
-		verbose_only(if (assm->_verbose && core->config.verbose_live)
-			live(gc, triggerFrag);)
-
 		bool treeCompile = core->config.tree_opt && (triggerFrag->kind == BranchTrace);
 		RegAllocMap regMap(gc);
 		NInsList loopJumps(gc);
 #ifdef MEMORY_INFO
 //		loopJumps.set_meminfo_name("LIR loopjumps");
 #endif
 		assm->beginAssembly(triggerFrag, &regMap);
 		if (assm->error())
 			return;
 
-		//nj_dprintf("recompile trigger %X kind %d\n", (int)triggerFrag, triggerFrag->kind);
+		//logc->printf("recompile trigger %X kind %d\n", (int)triggerFrag, triggerFrag->kind);
+
+		verbose_only( if (anyVerb) {
+		    logc->printf("=== Translating LIR fragments into assembly:\n");
+		})
+
 		Fragment* root = triggerFrag;
 		if (treeCompile)
 		{
 			// recompile the entire tree
+			verbose_only( if (anyVerb) {
+			   logc->printf("=== -- Compile the entire tree: begin\n");
+			})
 			root = triggerFrag->root;
 			root->fragEntry = 0;
 			root->loopEntry = 0;
 			root->releaseCode(frago);
 			
 			// do the tree branches
+			verbose_only( if (anyVerb) {
+			   logc->printf("=== -- Do the tree branches\n");
+			})
 			Fragment* frag = root->treeBranches;
-			while(frag)
+			while (frag)
 			{
 				// compile til no more frags
 				if (frag->lastIns)
 				{
+        			verbose_only( if (anyVerb) {
+					    logc->printf("=== -- Compiling branch %s ip %s\n",
+									 frago->labels->format(frag),
+									 frago->labels->format(frag->ip));
+					})
 					assm->assemble(frag, loopJumps);
-					verbose_only(if (assm->_verbose) 
-						assm->outputf("compiling branch %s ip %s",
-							frago->labels->format(frag),
-							frago->labels->format(frag->ip)); )
+					verbose_only(if (asmVerb) 
+						assm->outputf("## compiling branch %s ip %s",
+									  frago->labels->format(frag),
+									  frago->labels->format(frag->ip)); )
 					
 					NanoAssert(frag->kind == BranchTrace);
 					RegAlloc* regs = NJ_NEW(gc, RegAlloc)();
 					assm->copyRegisters(regs);
 					assm->releaseRegisters();
 					SideExit* exit = frag->spawnedFrom;
 					regMap.put(exit, regs);
 				}
 				frag = frag->treeBranches;
 			}
+			verbose_only( if (anyVerb) {
+			   logc->printf("=== -- Compile the entire tree: end\n");
+			})
 		}
 		
 		// now the the main trunk
+		verbose_only( if (anyVerb) {
+		    logc->printf("=== -- Compile trunk %s: begin\n",
+						 frago->labels->format(root));
+		})
 		assm->assemble(root, loopJumps);
-		verbose_only(if (assm->_verbose) 
-			assm->outputf("compiling trunk %s",
-				frago->labels->format(root));)
-		NanoAssert(!frago->core()->config.tree_opt || root == root->anchor || root->kind == MergeTrace);			
+		verbose_only( if (anyVerb) {
+		    logc->printf("=== -- Compile trunk %s: end\n",
+						 frago->labels->format(root));
+		})
+
+		verbose_only(
+		    if (asmVerb) 
+				assm->outputf("## compiling trunk %s",
+							  frago->labels->format(root));
+		)
+		NanoAssert(!frago->core()->config.tree_opt
+				   || root == root->anchor || root->kind == MergeTrace);
 		assm->endAssembly(root, loopJumps);
 			
 		// reverse output so that assembly is displayed low-to-high
 		// Up to this point, assm->_outputCache has been non-NULL, and so
 		// has been accumulating output.  Now we set it to NULL, traverse
 		// the entire list of stored strings, and hand them a second time
 		// to assm->output.  Since _outputCache is now NULL, outputf just
-		// hands these strings directly onwards to nj_dprintf.
-		verbose_only( assm->_outputCache = 0; )
-		verbose_only(for(int i=asmOutput.size()-1; i>=0; --i) { assm->outputf("%s",asmOutput.get(i)); } );
+		// hands these strings directly onwards to logc->printf.
+		verbose_only( if (anyVerb) {
+   		    logc->printf("\n");
+			logc->printf("=== Aggregated assembly output: BEGIN\n");
+   		    logc->printf("===\n");
+		    assm->_outputCache = 0;
+			for (int i = asmOutput.size() - 1; i >= 0; --i) { 
+				char* str = asmOutput.get(i);
+				assm->outputf("  %s", str);
+				gc->Free(str);
+			}
+   		    logc->printf("===\n");
+			logc->printf("=== Aggregated assembly output: END\n");
+		});
 
 		if (assm->error()) {
 			root->fragEntry = 0;
 			root->loopEntry = 0;
 		}
+
+		/* BEGIN decorative postamble */
+		verbose_only( if (anyVerb) {
+		    logc->printf("\n");
+		    logc->printf("===\n");
+		    logc->printf("=== END LIR::compile(%p, %p)\n",
+						 (void*)assm, (void*)triggerFrag);
+			logc->printf("========================================"
+						 "========================================\n");
+			logc->printf("\n");
+		});
+		/* END decorative postamble */
     }
 
     LInsp LoadFilter::insLoad(LOpcode v, LInsp base, LInsp disp)
     {
         if (base != sp && base != rp && (v == LIR_ld || v == LIR_ldq)) {
             uint32_t k;
             LInsp found = exprs.find2(v, base, disp, k);
             if (found)
@@ -2153,11 +2229,24 @@ namespace nanojit
 		end += need;
 		if (end > buf+sizeof(buf)) {
 			s = buf;
 			end = s+need;
 		}
 		strcpy(s, b);
 		return s;
 	}
+
+	// ---------------------------------------------------------------
+	// START debug-logging definitions
+	// ---------------------------------------------------------------
+
+	void LogControl::printf( const char* format, ... )
+	{
+        va_list vargs;
+        va_start(vargs, format);
+        vfprintf(stdout, format, vargs);
+        va_end(vargs);
+	}
+
 #endif // NJ_VERBOSE
 }
 	
--- a/js/src/nanojit/LIR.h
+++ b/js/src/nanojit/LIR.h
@@ -577,19 +577,21 @@ namespace nanojit
 		void formatGuard(LInsp i, char *buf);
 	};
 
 
 	class VerboseWriter : public LirWriter
 	{
 		InsList code;
 		DWB(LirNameMap*) names;
+		LogControl* logc;
     public:
-		VerboseWriter(avmplus::GC *gc, LirWriter *out, LirNameMap* names) 
-			: LirWriter(out), code(gc), names(names)
+		VerboseWriter(avmplus::GC *gc, LirWriter *out,
+					  LirNameMap* names, LogControl* logc)
+			: LirWriter(out), code(gc), names(names), logc(logc)
 		{}
 
 		LInsp add(LInsp i) {
             if (i)
                 code.add(i);
 			return i;
 		}
 
@@ -599,20 +601,20 @@ namespace nanojit
             return i;
         }
 
 		void flush()
 		{
             int n = code.size();
             if (n) {
 			    for (int i=0; i < n; i++)
-				    nj_dprintf("    %s\n",names->formatIns(code[i]));
+				    logc->printf("    %s\n",names->formatIns(code[i]));
 			    code.clear();
                 if (n > 1)
-        			nj_dprintf("\n");
+        			logc->printf("\n");
             }
 		}
 
 		LIns* insGuard(LOpcode op, LInsp cond, LIns *x) {
 			return add_flush(out->insGuard(op,cond,x));
 		}
 
 		LIns* insBranch(LOpcode v, LInsp condition, LInsp to) {
--- a/js/src/nanojit/Native.h
+++ b/js/src/nanojit/Native.h
@@ -94,28 +94,30 @@ namespace nanojit {
 	
 	#define isSPorFP(r)		( (r)==SP || (r)==FP )
 
 	#if defined(_MSC_VER) && _MSC_VER < 1400
 		static void asm_output(const char *f, ...) {}
 		#define gpn(r)					regNames[(r)]
 		#define fpn(r)					regNames[(r)]
 	#elif defined(NJ_VERBOSE)
-		#define asm_output(...) do {\
-			counter_increment(native);\
-			if (verbose_enabled()) {\
-				outline[0]='\0';\
-				if (outputAddr) sprintf(outline, "  %10p  ",_nIns);\
-				else sprintf(outline, "              ");\
-				sprintf(&outline[14], ##__VA_ARGS__);\
-				Assembler::outputAlign(outline, 45);\
-				RegAlloc::formatRegisters(_allocator, outline, _thisfrag);\
-				Assembler::output_asm(outline);\
-				outputAddr=false; /* set =true if you like to see addresses for each native instruction */ \
-			}\
+		#define asm_output(...) do { \
+			counter_increment(native); \
+			if (_logc->lcbits & LC_Assembly) { \
+				outline[0]='\0'; \
+				if (outputAddr) \
+                   sprintf(outline, "%010lx   ", (unsigned long)_nIns); \
+				else \
+                   memset(outline, (int)' ', 10+3); \
+				sprintf(&outline[13], ##__VA_ARGS__); \
+				Assembler::outputAlign(outline, 35); \
+				RegAlloc::formatRegisters(_allocator, outline, _thisfrag); \
+				Assembler::output_asm(outline); \
+				outputAddr=(_logc->lcbits & LC_NoCodeAddrs) ? false : true;	\
+			} \
 		} while (0) /* no semi */ 
 		#define gpn(r)					regNames[(r)] 
 		#define fpn(r)					regNames[(r)] 
 	#else
 		#define asm_output(...)
 		#define gpn(r)		
 		#define fpn(r)		
 	#endif /* NJ_VERBOSE */
--- a/js/src/nanojit/NativeARM.cpp
+++ b/js/src/nanojit/NativeARM.cpp
@@ -152,18 +152,18 @@ Assembler::genPrologue()
     uint32_t stackPushed = STACK_GRANULARITY * savingCount;
     uint32_t aligned = alignUp(stackNeeded + stackPushed, NJ_ALIGN_STACK);
     int32_t amt = aligned - stackPushed;
 
     // Make room on stack for what we are doing
     if (amt)
         SUBi(SP, SP, amt);
 
-    verbose_only( verbose_outputf("         %p:",_nIns); )
-    verbose_only( verbose_output("         patch entry"); )
+    verbose_only( asm_output("## %p:",(void*)_nIns); )
+    verbose_only( asm_output("## patch entry"); )
     NIns *patchEntry = _nIns;
 
     MOV(FP, SP);
     PUSH_mask(savingMask);
     return patchEntry;
 }
 
 void
@@ -689,18 +689,17 @@ Assembler::asm_restore(LInsp i, Reservat
         asm_ld_imm(r, i->imm32());
 #endif
     } else {
         int d = findMemFor(i);
         LDR(r, FP, d);
     }
 
     verbose_only(
-        if (_verbose)
-            outputf("        restore %s",_thisfrag->lirbuf->names->formatRef(i));
+        asm_output("        restore %s",_thisfrag->lirbuf->names->formatRef(i));
     )
 }
 
 void
 Assembler::asm_spill(Register rr, int d, bool pop, bool quad)
 {
     (void) pop;
     (void) quad;
@@ -1071,25 +1070,25 @@ Assembler::JMP_far(NIns* addr)
     if (isS24(offs>>2)) {
         // Emit a BKPT to ensure that we reserve enough space for a full 32-bit
         // branch patch later on. The BKPT should never be executed.
         BKPT_nochk();
 
         // B [PC+offs]
         *(--_nIns) = (NIns)( COND_AL | (0xA<<24) | ((offs>>2) & 0xFFFFFF) );
 
-        asm_output("b %p", addr);
+        asm_output("b %p", (void*)addr);
     } else {
         // Insert the target address as a constant in the instruction stream.
         *(--_nIns) = (NIns)((addr));
         // ldr pc, [pc, #-4] // load the address into pc, reading it from [pc-4] (e.g.,
         // the next instruction)
         *(--_nIns) = (NIns)( COND_AL | (0x51<<20) | (PC<<16) | (PC<<12) | (4));
 
-        asm_output("ldr pc, =%p", addr);
+        asm_output("ldr pc, =%p", (void*)addr);
     }
 }
 
 // Perform a branch with link, and ARM/Thumb exchange if necessary. The actual
 // BLX instruction is only available from ARMv5 onwards, but as we don't
 // support anything older than that this function will not attempt to output
 // pre-ARMv5 sequences.
 //
@@ -1118,32 +1117,32 @@ Assembler::BranchWithLink(NIns* addr)
     if (isS24(offs>>2)) {
 
         if (((intptr_t)addr & 1) == 0) {
             // The target is ARM, so just emit a BL.
 
             // BL addr
             NanoAssert( ((offs>>2) & ~0xffffff) == 0);
             *(--_nIns) = (NIns)( (COND_AL) | (0xB<<24) | (offs>>2) );
-            asm_output("bl %p", addr);
+            asm_output("bl %p", (void*)addr);
         } else {
             // The target is Thumb, so emit a BLX.
 
             // The (pre-shifted) value of the "H" bit in the BLX encoding.
             uint32_t    H = (offs & 0x2) << 23;
 
             // BLX addr
             NanoAssert( ((offs>>2) & ~0xffffff) == 0);
             *(--_nIns) = (NIns)( (0xF << 28) | (0x5<<25) | (H) | (offs>>2) );
-            asm_output("blx %p", addr);
+            asm_output("blx %p", (void*)addr);
         }
     } else {
         // BLX IP
         *(--_nIns) = (NIns)( (COND_AL) | (0x12<<20) | (0xFFF<<8) | (0x3<<4) | (IP) );
-        asm_output("blx ip (=%p)", addr);
+        asm_output("blx ip (=%p)", (void*)addr);
 
         // LDR IP, =addr
         LD32_nochk(IP, (int32_t)addr);
     }
 }
 
 void
 Assembler::LD32_nochk(Register r, int32_t imm)
@@ -1188,17 +1187,17 @@ Assembler::LD32_nochk(Register r, int32_
     while (offset <= -4096) {
         ++_nSlot;
         offset += sizeof(_nSlot);
     }
     NanoAssert(isS12(offset) && (offset < 0));
 
     // Write the literal.
     *(++_nSlot) = imm;
-    asm_output("  (%d(PC) = 0x%x)", offset, imm);
+    asm_output("## imm= 0x%x", imm);
 
     // Load the literal.
     LDR_nochk(r,PC,offset);
 }
 
 void
 Assembler::asm_ldr_chk(Register d, Register b, int32_t off, bool chk)
 {
--- a/js/src/nanojit/Nativei386.cpp
+++ b/js/src/nanojit/Nativei386.cpp
@@ -409,28 +409,32 @@ namespace nanojit
 		{
 			LD(r, d, FP);
 		}
 	}
 	
 	void Assembler::asm_restore(LInsp i, Reservation *resv, Register r)
 	{
         if (i->isop(LIR_alloc)) {
-			verbose_only( if (_verbose) { outputForEOL("  <= remat %s size %d", _thisfrag->lirbuf->names->formatRef(i), i->size()); } )
+			verbose_only( if (_logc->lcbits & LC_RegAlloc) {
+					        outputForEOL("  <= remat %s size %d",
+							_thisfrag->lirbuf->names->formatRef(i), i->size()); } )
             LEA(r, disp(resv), FP);
         }
         else if (i->isconst()) {
             if (!resv->arIndex) {
                 i->resv()->clear();
             }
             LDi(r, i->imm32());
         }
         else {
             int d = findMemFor(i);
-			verbose_only( if (_verbose) { outputForEOL("  <= restore %s", _thisfrag->lirbuf->names->formatRef(i)); } )
+			verbose_only( if (_logc->lcbits & LC_RegAlloc) {
+					        outputForEOL("  <= restore %s", 
+							_thisfrag->lirbuf->names->formatRef(i)); } )
 			asm_load(d,r);
         }
 	}
 
     void Assembler::asm_store32(LIns *value, int dr, LIns *base)
     {
         if (value->isconst())
         {
--- a/js/src/nanojit/Nativei386.h
+++ b/js/src/nanojit/Nativei386.h
@@ -333,73 +333,73 @@ namespace nanojit
 		underrunProtect(3); \
 		MODRM((d),(s));	\
 		_nIns -= 2; \
 		_nIns[0] = (uint8_t) ( ((c)>>8) ); \
 		_nIns[1] = (uint8_t) ( (c) )
 
 #define LAHF()		do { count_alu(); ALU0(0x9F);					asm_output("lahf"); } while(0)
 #define SAHF()		do { count_alu(); ALU0(0x9E);					asm_output("sahf"); } while(0)
-#define OR(l,r)		do { count_alu(); ALU(0x0b, (l),(r));			asm_output("or %s,%s",gpn(l),gpn(r)); } while(0)
-#define AND(l,r)	do { count_alu(); ALU(0x23, (l),(r));			asm_output("and %s,%s",gpn(l),gpn(r)); } while(0)
-#define XOR(l,r)	do { count_alu(); ALU(0x33, (l),(r));			asm_output("xor %s,%s",gpn(l),gpn(r)); } while(0)
-#define ADD(l,r)	do { count_alu(); ALU(0x03, (l),(r));			asm_output("add %s,%s",gpn(l),gpn(r)); } while(0)
-#define SUB(l,r)	do { count_alu(); ALU(0x2b, (l),(r));			asm_output("sub %s,%s",gpn(l),gpn(r)); } while(0)
-#define MUL(l,r)	do { count_alu(); ALU2(0x0faf,(l),(r));         asm_output("mul %s,%s",gpn(l),gpn(r)); } while(0)
-#define DIV(r)      do { count_alu(); ALU(0xf7, (Register)7,(r));   asm_output("idiv edx:eax, %s",gpn(r)); } while(0)
-#define NOT(r)		do { count_alu(); ALU(0xf7, (Register)2,(r));	asm_output("not %s",gpn(r)); } while(0)
-#define NEG(r)		do { count_alu(); ALU(0xf7, (Register)3,(r));	asm_output("neg %s",gpn(r)); } while(0)
-#define SHR(r,s)	do { count_alu(); ALU(0xd3, (Register)5,(r));	asm_output("shr %s,%s",gpn(r),gpn(s)); } while(0)
-#define SAR(r,s)	do { count_alu(); ALU(0xd3, (Register)7,(r));	asm_output("sar %s,%s",gpn(r),gpn(s)); } while(0)
-#define SHL(r,s)	do { count_alu(); ALU(0xd3, (Register)4,(r));	asm_output("shl %s,%s",gpn(r),gpn(s)); } while(0)
+#define OR(l,r)		do { count_alu(); ALU(0x0b, (l),(r));			asm_output("or    %s,%s",gpn(l),gpn(r)); } while(0)
+#define AND(l,r)	do { count_alu(); ALU(0x23, (l),(r));			asm_output("and   %s,%s",gpn(l),gpn(r)); } while(0)
+#define XOR(l,r)	do { count_alu(); ALU(0x33, (l),(r));			asm_output("xor   %s,%s",gpn(l),gpn(r)); } while(0)
+#define ADD(l,r)	do { count_alu(); ALU(0x03, (l),(r));			asm_output("add   %s,%s",gpn(l),gpn(r)); } while(0)
+#define SUB(l,r)	do { count_alu(); ALU(0x2b, (l),(r));			asm_output("sub   %s,%s",gpn(l),gpn(r)); } while(0)
+#define MUL(l,r)	do { count_alu(); ALU2(0x0faf,(l),(r));         asm_output("mul   %s,%s",gpn(l),gpn(r)); } while(0)
+#define DIV(r)      do { count_alu(); ALU(0xf7, (Register)7,(r));   asm_output("idiv  edx:eax, %s",gpn(r)); } while(0)
+#define NOT(r)		do { count_alu(); ALU(0xf7, (Register)2,(r));	asm_output("not   %s",gpn(r)); } while(0)
+#define NEG(r)		do { count_alu(); ALU(0xf7, (Register)3,(r));	asm_output("neg   %s",gpn(r)); } while(0)
+#define SHR(r,s)	do { count_alu(); ALU(0xd3, (Register)5,(r));	asm_output("shr   %s,%s",gpn(r),gpn(s)); } while(0)
+#define SAR(r,s)	do { count_alu(); ALU(0xd3, (Register)7,(r));	asm_output("sar   %s,%s",gpn(r),gpn(s)); } while(0)
+#define SHL(r,s)	do { count_alu(); ALU(0xd3, (Register)4,(r));	asm_output("shl   %s,%s",gpn(r),gpn(s)); } while(0)
 
 #define SHIFT(c,r,i) \
 		underrunProtect(3);\
 		*--_nIns = (uint8_t)(i);\
 		MODRM((Register)c,r);\
 		*--_nIns = 0xc1;
 
-#define SHLi(r,i)	do { count_alu(); SHIFT(4,r,i);	asm_output("shl %s,%d", gpn(r),i); } while(0)
-#define SHRi(r,i)	do { count_alu(); SHIFT(5,r,i);	asm_output("shr %s,%d", gpn(r),i); } while(0)
-#define SARi(r,i)	do { count_alu(); SHIFT(7,r,i);	asm_output("sar %s,%d", gpn(r),i); } while(0)
+#define SHLi(r,i)	do { count_alu(); SHIFT(4,r,i);	asm_output("shl   %s,%d", gpn(r),i); } while(0)
+#define SHRi(r,i)	do { count_alu(); SHIFT(5,r,i);	asm_output("shr   %s,%d", gpn(r),i); } while(0)
+#define SARi(r,i)	do { count_alu(); SHIFT(7,r,i);	asm_output("sar   %s,%d", gpn(r),i); } while(0)
 
 #define MOVZX8(d,s) do { count_alu(); ALU2(0x0fb6,d,s); asm_output("movzx %s,%s", gpn(d),gpn(s)); } while(0)
 
-#define SUBi(r,i)	do { count_alu(); ALUi(0x2d,r,i);				asm_output("sub %s,%d",gpn(r),i); } while(0)
-#define ADDi(r,i)	do { count_alu(); ALUi(0x05,r,i);				asm_output("add %s,%d",gpn(r),i); } while(0)
-#define ANDi(r,i)	do { count_alu(); ALUi(0x25,r,i);				asm_output("and %s,%d",gpn(r),i); } while(0)
-#define ORi(r,i)	do { count_alu(); ALUi(0x0d,r,i);				asm_output("or %s,%d",gpn(r),i); } while(0)
-#define XORi(r,i)	do { count_alu(); ALUi(0x35,r,i);				asm_output("xor %s,%d",gpn(r),i); } while(0)
+#define SUBi(r,i)	do { count_alu(); ALUi(0x2d,r,i);				asm_output("sub   %s,%d",gpn(r),i); } while(0)
+#define ADDi(r,i)	do { count_alu(); ALUi(0x05,r,i);				asm_output("add   %s,%d",gpn(r),i); } while(0)
+#define ANDi(r,i)	do { count_alu(); ALUi(0x25,r,i);				asm_output("and   %s,%d",gpn(r),i); } while(0)
+#define ORi(r,i)	do { count_alu(); ALUi(0x0d,r,i);				asm_output("or    %s,%d",gpn(r),i); } while(0)
+#define XORi(r,i)	do { count_alu(); ALUi(0x35,r,i);				asm_output("xor   %s,%d",gpn(r),i); } while(0)
 
-#define ADDmi(d,b,i) do { count_alust(); ALUmi(0x05, d, b, i); asm_output("add %d(%s), %d", d, gpn(b), i); } while(0)
+#define ADDmi(d,b,i) do { count_alust(); ALUmi(0x05, d, b, i); asm_output("add   %d(%s), %d", d, gpn(b), i); } while(0)
 
-#define TEST(d,s)	do { count_alu(); ALU(0x85,d,s);				asm_output("test %s,%s",gpn(d),gpn(s)); } while(0)
-#define CMP(l,r)	do { count_alu(); ALU(0x3b, (l),(r));			asm_output("cmp %s,%s",gpn(l),gpn(r)); } while(0)
-#define CMPi(r,i)	do { count_alu(); ALUi(0x3d,r,i);				asm_output("cmp %s,%d",gpn(r),i); } while(0)
+#define TEST(d,s)	do { count_alu(); ALU(0x85,d,s);				asm_output("test  %s,%s",gpn(d),gpn(s)); } while(0)
+#define CMP(l,r)	do { count_alu(); ALU(0x3b, (l),(r));			asm_output("cmp   %s,%s",gpn(l),gpn(r)); } while(0)
+#define CMPi(r,i)	do { count_alu(); ALUi(0x3d,r,i);				asm_output("cmp   %s,%d",gpn(r),i); } while(0)
 
-#define MR(d,s)		do { count_mov(); ALU(0x8b,d,s);				asm_output("mov %s,%s",gpn(d),gpn(s)); } while(0)
-#define LEA(r,d,b)	do { count_alu(); ALUm(0x8d, r,d,b);			asm_output("lea %s,%d(%s)",gpn(r),d,gpn(b)); } while(0)
+#define MR(d,s)		do { count_mov(); ALU(0x8b,d,s);				asm_output("mov   %s,%s",gpn(d),gpn(s)); } while(0)
+#define LEA(r,d,b)	do { count_alu(); ALUm(0x8d, r,d,b);			asm_output("lea   %s,%d(%s)",gpn(r),d,gpn(b)); } while(0)
 // lea %r, d(%i*4)
 // This addressing mode is not supported by the MODRMSIB macro.
 #define LEAmi4(r,d,i) do { count_alu(); IMM32(d); *(--_nIns) = (2<<6)|(i<<3)|5; *(--_nIns) = (0<<6)|(r<<3)|4; *(--_nIns) = 0x8d;                    asm_output("lea %s, %p(%s*4)", gpn(r), (void*)d, gpn(i)); } while(0)
 
 #define CDQ()       do { SARi(EDX, 31); MR(EDX, EAX); } while(0)
 
-#define SETE(r)		do { count_alu(); ALU2(0x0f94,(r),(r));			asm_output("sete %s",gpn(r)); } while(0)
+#define SETE(r)		do { count_alu(); ALU2(0x0f94,(r),(r));			asm_output("sete  %s",gpn(r)); } while(0)
 #define SETNP(r)	do { count_alu(); ALU2(0x0f9B,(r),(r));			asm_output("setnp %s",gpn(r)); } while(0)
-#define SETL(r)		do { count_alu(); ALU2(0x0f9C,(r),(r));			asm_output("setl %s",gpn(r)); } while(0)
+#define SETL(r)		do { count_alu(); ALU2(0x0f9C,(r),(r));			asm_output("setl  %s",gpn(r)); } while(0)
 #define SETLE(r)	do { count_alu(); ALU2(0x0f9E,(r),(r));			asm_output("setle %s",gpn(r)); } while(0)
-#define SETG(r)		do { count_alu(); ALU2(0x0f9F,(r),(r));			asm_output("setg %s",gpn(r)); } while(0)
+#define SETG(r)		do { count_alu(); ALU2(0x0f9F,(r),(r));			asm_output("setg  %s",gpn(r)); } while(0)
 #define SETGE(r)	do { count_alu(); ALU2(0x0f9D,(r),(r));			asm_output("setge %s",gpn(r)); } while(0)
-#define SETB(r)     do { count_alu(); ALU2(0x0f92,(r),(r));          asm_output("setb %s",gpn(r)); } while(0)
+#define SETB(r)     do { count_alu(); ALU2(0x0f92,(r),(r));          asm_output("setb  %s",gpn(r)); } while(0)
 #define SETBE(r)    do { count_alu(); ALU2(0x0f96,(r),(r));          asm_output("setbe %s",gpn(r)); } while(0)
-#define SETA(r)     do { count_alu(); ALU2(0x0f97,(r),(r));          asm_output("seta %s",gpn(r)); } while(0)
+#define SETA(r)     do { count_alu(); ALU2(0x0f97,(r),(r));          asm_output("seta  %s",gpn(r)); } while(0)
 #define SETAE(r)    do { count_alu(); ALU2(0x0f93,(r),(r));          asm_output("setae %s",gpn(r)); } while(0)
-#define SETC(r)     do { count_alu(); ALU2(0x0f90,(r),(r));          asm_output("setc %s",gpn(r)); } while(0)
-#define SETO(r)     do { count_alu(); ALU2(0x0f92,(r),(r));          asm_output("seto %s",gpn(r)); } while(0)
+#define SETC(r)     do { count_alu(); ALU2(0x0f90,(r),(r));          asm_output("setc  %s",gpn(r)); } while(0)
+#define SETO(r)     do { count_alu(); ALU2(0x0f92,(r),(r));          asm_output("seto  %s",gpn(r)); } while(0)
 
 #define MREQ(dr,sr)	do { count_alu(); ALU2(0x0f44,dr,sr); asm_output("cmove %s,%s", gpn(dr),gpn(sr)); } while(0)
 #define MRNE(dr,sr)	do { count_alu(); ALU2(0x0f45,dr,sr); asm_output("cmovne %s,%s", gpn(dr),gpn(sr)); } while(0)
 #define MRL(dr,sr)	do { count_alu(); ALU2(0x0f4C,dr,sr); asm_output("cmovl %s,%s", gpn(dr),gpn(sr)); } while(0)
 #define MRLE(dr,sr)	do { count_alu(); ALU2(0x0f4E,dr,sr); asm_output("cmovle %s,%s", gpn(dr),gpn(sr)); } while(0)
 #define MRG(dr,sr)	do { count_alu(); ALU2(0x0f4F,dr,sr); asm_output("cmovg %s,%s", gpn(dr),gpn(sr)); } while(0)
 #define MRGE(dr,sr)	do { count_alu(); ALU2(0x0f4D,dr,sr); asm_output("cmovge %s,%s", gpn(dr),gpn(sr)); } while(0)
 #define MRB(dr,sr)	do { count_alu(); ALU2(0x0f42,dr,sr); asm_output("cmovb %s,%s", gpn(dr),gpn(sr)); } while(0)
@@ -411,31 +411,31 @@ namespace nanojit
 
 // these aren't currently used but left in for reference
 //#define LDEQ(r,d,b) do { ALU2m(0x0f44,r,d,b); asm_output("cmove %s,%d(%s)", gpn(r),d,gpn(b)); } while(0)
 //#define LDNEQ(r,d,b) do { ALU2m(0x0f45,r,d,b); asm_output("cmovne %s,%d(%s)", gpn(r),d,gpn(b)); } while(0)
 
 #define LD(reg,disp,base)	do { 	\
 	count_ld();\
 	ALUm(0x8b,reg,disp,base);	\
-	asm_output("mov %s,%d(%s)",gpn(reg),disp,gpn(base)); } while(0)
+	asm_output("mov   %s,%d(%s)",gpn(reg),disp,gpn(base)); } while(0)
 
 #define LDdm(reg,addr) do {		\
 	count_ld();                 \
 	ALUdm(0x8b,reg,addr);		\
-	asm_output("mov %s,0(%lx)",gpn(reg),(unsigned long)addr); \
+	asm_output("mov   %s,0(%lx)",gpn(reg),(unsigned long)addr); \
 	} while (0)
 
 
 #define SIBIDX(n)	"1248"[n]
 
 #define LDsib(reg,disp,base,index,scale) do {	\
 	count_ld();                                 \
 	ALUsib(0x8b,reg,base,index,scale,disp);		\
-	asm_output("mov %s,%d(%s+%s*%c)",gpn(reg),disp,gpn(base),gpn(index),SIBIDX(scale)); \
+	asm_output("mov   %s,%d(%s+%s*%c)",gpn(reg),disp,gpn(base),gpn(index),SIBIDX(scale)); \
 	} while (0)
 
 // load 16-bit, sign extend
 #define LD16S(r,d,b) do { count_ld(); ALU2m(0x0fbf,r,d,b); asm_output("movsx %s,%d(%s)", gpn(r),d,gpn(b)); } while(0)
 	
 // load 16-bit, zero extend
 #define LD16Z(r,d,b) do { count_ld(); ALU2m(0x0fb7,r,d,b); asm_output("movsz %s,%d(%s)", gpn(r),d,gpn(b)); } while(0)
 
@@ -468,127 +468,127 @@ namespace nanojit
 	
 
 #define LDi(r,i) do { \
 	count_ld();\
 	underrunProtect(5);			\
 	IMM32(i);					\
 	NanoAssert(((unsigned)r)<8); \
 	*(--_nIns) = (uint8_t) (0xb8 | (r) );		\
-	asm_output("mov %s,%d",gpn(r),i); } while(0)
+	asm_output("mov   %s,%d",gpn(r),i); } while(0)
 
 #define ST(base,disp,reg) do {  \
 	count_st();\
 	ALUm(0x89,reg,disp,base);	\
-    asm_output("mov %d(%s),%s",disp,base==UnknownReg?"0":gpn(base),gpn(reg)); } while(0)
+    asm_output("mov   %d(%s),%s",disp,base==UnknownReg?"0":gpn(base),gpn(reg)); } while(0)
 
 #define STi(base,disp,imm)	do { \
 	count_st();\
 	underrunProtect(12);	\
 	IMM32(imm);				\
 	MODRMm(0, disp, base);	\
 	*(--_nIns) = 0xc7;		\
-	asm_output("mov %d(%s),%d",disp,gpn(base),imm); } while(0)
+	asm_output("mov   %d(%s),%d",disp,gpn(base),imm); } while(0)
 
 #define RET()   do { count_ret(); ALU0(0xc3); asm_output("ret"); } while(0)
 #define NOP() 	do { count_alu(); ALU0(0x90); asm_output("nop"); } while(0)
 #define INT3()  do { ALU0(0xcc); asm_output("int3"); } while(0)
 
 #define PUSHi(i) do { \
 	count_push();\
 	if (isS8(i)) { \
 		underrunProtect(2);			\
 		_nIns-=2; _nIns[0] = 0x6a; _nIns[1] = (uint8_t)(i); \
-		asm_output("push %d",i); \
+		asm_output("push  %d",i); \
 	} else \
 		{ PUSHi32(i); } } while(0)
 
 #define PUSHi32(i)	do {	\
 	count_push();\
 	underrunProtect(5);	\
 	IMM32(i);			\
 	*(--_nIns) = 0x68;	\
-	asm_output("push %d",i); } while(0)
+	asm_output("push  %d",i); } while(0)
 
 #define PUSHr(r) do {  \
 	count_push();\
 	underrunProtect(1);			\
 	NanoAssert(((unsigned)r)<8); \
 	*(--_nIns) = (uint8_t) ( 0x50 | (r) );	\
-	asm_output("push %s",gpn(r)); } while(0)
+	asm_output("push  %s",gpn(r)); } while(0)
 
 #define PUSHm(d,b) do { \
 	count_pushld();\
 	ALUm(0xff, 6, d, b);		\
-	asm_output("push %d(%s)",d,gpn(b)); } while(0)
+	asm_output("push  %d(%s)",d,gpn(b)); } while(0)
 
 #define POPr(r) do { \
 	count_pop();\
 	underrunProtect(1);			\
 	NanoAssert(((unsigned)r)<8); \
 	*(--_nIns) = (uint8_t) ( 0x58 | (r) ); \
-	asm_output("pop %s",gpn(r)); } while(0)
+	asm_output("pop   %s",gpn(r)); } while(0)
 
 #define JCC32 0x0f
 #define JMP8  0xeb
 #define JMP32 0xe9  
     
 #define JCC(o,t,isfar,n) do { \
 	count_jcc();\
 	underrunProtect(6);	\
 	intptr_t tt = (intptr_t)t - (intptr_t)_nIns;	\
 	if (isS8(tt) && !isfar) { \
 		verbose_only( NIns* next = _nIns; (void)next; ) \
 		_nIns -= 2; \
 		_nIns[0] = (uint8_t) ( 0x70 | (o) ); \
 		_nIns[1] = (uint8_t) (tt); \
-		asm_output("%s %p",(n),(next+tt)); \
+		asm_output("%-5s %p",(n),(next+tt)); \
 	} else { \
 		verbose_only( NIns* next = _nIns; ) \
 		IMM32(tt); \
 		_nIns -= 2; \
 		_nIns[0] = JCC32; \
 		_nIns[1] = (uint8_t) ( 0x80 | (o) ); \
-		asm_output("%s %p",(n),(next+tt)); \
+		asm_output("%-5s %p",(n),(next+tt)); \
 	} } while(0)
 
 #define JMP_long(t) do { \
 	count_jmp();\
 	underrunProtect(5);	\
 	intptr_t tt = (intptr_t)t - (intptr_t)_nIns;	\
 	JMP_long_nochk_offset(tt);	\
-    verbose_only( verbose_outputf("        %p:",_nIns); ) \
+    verbose_only( verbose_outputf("%010lx:", (unsigned long)_nIns); )	\
 	} while(0)
 
 #define JMP(t)		do { 	\
 	count_jmp();\
    	underrunProtect(5);	\
 	intptr_t tt = (intptr_t)t - (intptr_t)_nIns;	\
 	if (isS8(tt)) { \
 		verbose_only( NIns* next = _nIns; (void)next; ) \
 		_nIns -= 2; \
 		_nIns[0] = JMP8; \
 		_nIns[1] = (uint8_t) ( (tt)&0xff ); \
-		asm_output("jmp %p",(next+tt)); \
+		asm_output("jmp   %p",(next+tt)); \
 	} else { \
 		JMP_long_nochk_offset(tt);	\
 	} } while(0)
 
 // this should only be used when you can guarantee there is enough room on the page
 #define JMP_long_nochk_offset(o) do {\
 		verbose_only( NIns* next = _nIns; (void)next; ) \
  		IMM32((o)); \
  		*(--_nIns) = JMP32; \
-		asm_output("jmp %p",(next+(o))); } while(0)
+		asm_output("jmp   %p",(next+(o))); } while(0)
 
 #define JMP_indirect(r) do { \
         underrunProtect(2);  \
         MODRMm(4, 0, r);     \
         *(--_nIns) = 0xff;   \
-        asm_output("jmp *(%s)", gpn(r)); } while (0)
+        asm_output("jmp   *(%s)", gpn(r)); } while (0)
 
 #define JE(t, isfar)	   JCC(0x04, t, isfar, "je")
 #define JNE(t, isfar)	   JCC(0x05, t, isfar, "jne")
 #define JP(t, isfar)	   JCC(0x0A, t, isfar, "jp")
 #define JNP(t, isfar)	   JCC(0x0B, t, isfar, "jnp")
 
 #define JB(t, isfar)	   JCC(0x02, t, isfar, "jb")
 #define JNB(t, isfar)	   JCC(0x03, t, isfar, "jnb")
@@ -654,23 +654,23 @@ namespace nanojit
 	count_stq();\
     SSEm(0xf20f11, (r)&7, (d), (b)); \
     asm_output("movsd %d(%s),%s",(d),gpn(b),gpn(r)); \
     } while(0)
 
 #define SSE_LDQ(r,d,b)do {  \
 	count_ldq();\
     SSEm(0xf30f7e, (r)&7, (d), (b)); \
-    asm_output("movq %s,%d(%s)",gpn(r),d,gpn(b)); \
+    asm_output("movq  %s,%d(%s)",gpn(r),d,gpn(b)); \
     } while(0)
 
 #define SSE_STQ(d,b,r)do {  \
 	count_stq();\
     SSEm(0x660fd6, (r)&7, (d), (b)); \
-    asm_output("movq %d(%s),%s",(d),gpn(b),gpn(r)); \
+    asm_output("movq  %d(%s),%s",(d),gpn(b),gpn(r)); \
     } while(0)
 
 #define SSE_CVTSI2SD(xr,gr) do{ \
 	count_fpu();\
     SSE(0xf20f2a, (xr)&7, (gr)&7); \
     asm_output("cvtsi2sd %s,%s",gpn(xr),gpn(gr)); \
     } while(0)
 
@@ -686,31 +686,31 @@ namespace nanojit
 	if (_is_xmm_reg_(s)) { \
 		NanoAssert(_is_gp_reg_(d)); \
 		SSE(0x660f7e, (s)&7, (d)&7); \
 	} else { \
 		NanoAssert(_is_gp_reg_(s)); \
 		NanoAssert(_is_xmm_reg_(d)); \
 		SSE(0x660f6e, (d)&7, (s)&7); \
 	} \
-    asm_output("movd %s,%s",gpn(d),gpn(s)); \
+    asm_output("movd  %s,%s",gpn(d),gpn(s)); \
     } while(0)
 
 #define SSE_MOVSD(rd,rs) do{ \
 	count_mov();\
     NanoAssert(_is_xmm_reg_(rd) && _is_xmm_reg_(rs));\
     SSE(0xf20f10, (rd)&7, (rs)&7); \
     asm_output("movsd %s,%s",gpn(rd),gpn(rs)); \
     } while(0)
 
 #define SSE_MOVDm(d,b,xrs) do {\
 	count_st();\
     NanoAssert(_is_xmm_reg_(xrs) && _is_gp_reg_(b));\
     SSEm(0x660f7e, (xrs)&7, d, b);\
-    asm_output("movd %d(%s),%s", d, gpn(b), gpn(xrs));\
+    asm_output("movd  %d(%s),%s", d, gpn(b), gpn(xrs));\
     } while(0)
 
 #define SSE_ADDSD(rd,rs) do{ \
 	count_fpu();\
     NanoAssert(_is_xmm_reg_(rd) && _is_xmm_reg_(rs));\
     SSE(0xf20f58, (rd)&7, (rs)&7); \
     asm_output("addsd %s,%s",gpn(rd),gpn(rs)); \
     } while(0)
@@ -794,67 +794,67 @@ namespace nanojit
 		*(--_nIns) = (uint8_t)((o)>>8)
 
 #define TEST_AH(i) do { 							\
 		count_alu();\
 		underrunProtect(3);					\
 		*(--_nIns) = ((uint8_t)(i));			\
 		*(--_nIns) = 0xc4;					\
 		*(--_nIns) = 0xf6;					\
-		asm_output("test ah, %d",i); } while(0)
+		asm_output("test  ah, %d",i); } while(0)
 
 #define TEST_AX(i) do { 							\
 		count_fpu();\
 		underrunProtect(5);					\
 		*(--_nIns) = (0);		\
 		*(--_nIns) = ((uint8_t)(i));			\
 		*(--_nIns) = ((uint8_t)((i)>>8));		\
 		*(--_nIns) = (0);		\
 		*(--_nIns) = 0xa9;					\
-		asm_output("test ax, %d",i); } while(0)
+		asm_output("test  ax, %d",i); } while(0)
 
 #define FNSTSW_AX()	do { count_fpu(); FPUc(0xdfe0);				asm_output("fnstsw_ax"); } while(0)
 #define FCHS()		do { count_fpu(); FPUc(0xd9e0);				asm_output("fchs"); } while(0)
 #define FLD1()		do { count_fpu(); FPUc(0xd9e8);				asm_output("fld1"); fpu_push(); } while(0)
 #define FLDZ()		do { count_fpu(); FPUc(0xd9ee);				asm_output("fldz"); fpu_push(); } while(0)
 #define FFREE(r)	do { count_fpu(); FPU(0xddc0, r);			asm_output("ffree %s",fpn(r)); } while(0)
 #define FSTQ(p,d,b)	do { count_stq(); FPUm(0xdd02|(p), d, b);	asm_output("fst%sq %d(%s)",((p)?"p":""),d,gpn(b)); if (p) fpu_pop(); } while(0)
 #define FSTPQ(d,b)  FSTQ(1,d,b)
 #define FCOM(p,d,b)	do { count_fpuld(); FPUm(0xdc02|(p), d, b);	asm_output("fcom%s %d(%s)",((p)?"p":""),d,gpn(b)); if (p) fpu_pop(); } while(0)
-#define FLDQ(d,b)	do { count_ldq(); FPUm(0xdd00, d, b);		asm_output("fldq %d(%s)",d,gpn(b)); fpu_push();} while(0)
+#define FLDQ(d,b)	do { count_ldq(); FPUm(0xdd00, d, b);		asm_output("fldq  %d(%s)",d,gpn(b)); fpu_push();} while(0)
 #define FILDQ(d,b)	do { count_fpuld(); FPUm(0xdf05, d, b);		asm_output("fildq %d(%s)",d,gpn(b)); fpu_push(); } while(0)
-#define FILD(d,b)	do { count_fpuld(); FPUm(0xdb00, d, b);		asm_output("fild %d(%s)",d,gpn(b)); fpu_push(); } while(0)
-#define FADD(d,b)	do { count_fpu(); FPUm(0xdc00, d, b);		asm_output("fadd %d(%s)",d,gpn(b)); } while(0)
-#define FSUB(d,b)	do { count_fpu(); FPUm(0xdc04, d, b);		asm_output("fsub %d(%s)",d,gpn(b)); } while(0)
+#define FILD(d,b)	do { count_fpuld(); FPUm(0xdb00, d, b);		asm_output("fild  %d(%s)",d,gpn(b)); fpu_push(); } while(0)
+#define FADD(d,b)	do { count_fpu(); FPUm(0xdc00, d, b);		asm_output("fadd  %d(%s)",d,gpn(b)); } while(0)
+#define FSUB(d,b)	do { count_fpu(); FPUm(0xdc04, d, b);		asm_output("fsub  %d(%s)",d,gpn(b)); } while(0)
 #define FSUBR(d,b)	do { count_fpu(); FPUm(0xdc05, d, b);		asm_output("fsubr %d(%s)",d,gpn(b)); } while(0)
-#define FMUL(d,b)	do { count_fpu(); FPUm(0xdc01, d, b);		asm_output("fmul %d(%s)",d,gpn(b)); } while(0)
-#define FDIV(d,b)	do { count_fpu(); FPUm(0xdc06, d, b);		asm_output("fdiv %d(%s)",d,gpn(b)); } while(0)
+#define FMUL(d,b)	do { count_fpu(); FPUm(0xdc01, d, b);		asm_output("fmul  %d(%s)",d,gpn(b)); } while(0)
+#define FDIV(d,b)	do { count_fpu(); FPUm(0xdc06, d, b);		asm_output("fdiv  %d(%s)",d,gpn(b)); } while(0)
 #define FDIVR(d,b)	do { count_fpu(); FPUm(0xdc07, d, b);		asm_output("fdivr %d(%s)",d,gpn(b)); } while(0)
 #define FINCSTP()	do { count_fpu(); FPUc(0xd9f7);				asm_output("fincstp"); } while(0)
-#define FSTP(r)		do { count_fpu(); FPU(0xddd8, r&7);			asm_output("fstp %s",fpn(r)); fpu_pop();} while(0)
+#define FSTP(r)		do { count_fpu(); FPU(0xddd8, r&7);			asm_output("fstp  %s",fpn(r)); fpu_pop();} while(0)
 #define FCOMP()		do { count_fpu(); FPUc(0xD8D9);				asm_output("fcomp"); fpu_pop();} while(0)
 #define FCOMPP()	do { count_fpu(); FPUc(0xDED9);				asm_output("fcompp"); fpu_pop();fpu_pop();} while(0)
-#define FLDr(r)		do { count_ldq(); FPU(0xd9c0,r);				asm_output("fld %s",fpn(r)); fpu_push(); } while(0)
+#define FLDr(r)		do { count_ldq(); FPU(0xd9c0,r);				asm_output("fld   %s",fpn(r)); fpu_push(); } while(0)
 #define EMMS()		do { count_fpu(); FPUc(0x0f77);				asm_output("emms"); } while (0)
 
 // standard direct call
 #define CALL(c)	do { \
   count_call();\
   underrunProtect(5);					\
   int offset = (c->_address) - ((int)_nIns); \
   IMM32( (uint32_t)offset );	\
   *(--_nIns) = 0xE8;		\
-  verbose_only(asm_output("call %s",(c->_name));) \
+  verbose_only(asm_output("call  %s",(c->_name));) \
   debug_only(if ((c->_argtypes&3)==ARGSIZE_F) fpu_push();)\
 } while (0)
 
 // indirect call thru register
 #define CALLr(c,r)	do { \
   count_calli();\
   underrunProtect(2);\
   ALU(0xff, 2, (r));\
-  verbose_only(asm_output("call %s",gpn(r));) \
+  verbose_only(asm_output("call  %s",gpn(r));) \
   debug_only(if ((c->_argtypes&3)==ARGSIZE_F) fpu_push();)\
 } while (0)
 
 
 }
 #endif // __nanojit_Nativei386__
--- a/js/src/nanojit/avmplus.h
+++ b/js/src/nanojit/avmplus.h
@@ -318,17 +318,17 @@ namespace avmplus {
     typedef String* Stringp;
 
     class Config
     {
     public:
         Config() {
             memset(this, 0, sizeof(Config));
 #ifdef DEBUG
-            verbose = getenv("TRACEMONKEY") && strstr(getenv("TRACEMONKEY"), "verbose");
+            verbose = false;
             verbose_addrs = 1;
             verbose_exits = 1;
             verbose_live = 1;
             show_stats = 1;
 #endif
         }
         
         uint32_t tree_opt:1;
--- a/js/src/nanojit/nanojit.h
+++ b/js/src/nanojit/nanojit.h
@@ -176,30 +176,26 @@ namespace nanojit
 
 #ifdef AVMPLUS_VERBOSE
 #define NJ_VERBOSE 1
 #define NJ_PROFILE 1
 #endif
 
 #if defined(_MSC_VER) && _MSC_VER < 1400
 	#include <stdio.h>
-	#define verbose_output						if (verbose_enabled()) Assembler::output
-	#define verbose_outputf						if (verbose_enabled()) Assembler::outputf
-	#define verbose_enabled()					(_verbose)
-	#define verbose_only(x)						x
+	#define verbose_outputf			if (_logc->lcbits & LC_Assembly) \
+	                                    Assembler::outputf
+	#define verbose_only(x)		    x
 #elif defined(NJ_VERBOSE)
 	#include <stdio.h>
-	#define verbose_output						if (verbose_enabled()) Assembler::output
-	#define verbose_outputf						if (verbose_enabled()) Assembler::outputf
-	#define verbose_enabled()					(_verbose)
-	#define verbose_only(...)					__VA_ARGS__
+	#define verbose_outputf			if (_logc->lcbits & LC_Assembly) \
+	                                    Assembler::outputf
+	#define verbose_only(...)		__VA_ARGS__
 #else
-	#define verbose_output
 	#define verbose_outputf
-	#define verbose_enabled()
 	#define verbose_only(...)
 #endif /*NJ_VERBOSE*/
 
 #ifdef _DEBUG
 	#define debug_only(x)			x
 #else
 	#define debug_only(x)
 #endif /* DEBUG */
@@ -238,32 +234,70 @@ namespace nanojit
 #define alignUp(x,s)		((((uintptr_t)(x))+(((uintptr_t)s)-1))&~(((uintptr_t)s)-1))
 
 #define pageTop(x)          ( alignTo(x,NJ_PAGE_SIZE) )
 #define pageDataStart(x)    ( alignTo(x,NJ_PAGE_SIZE) + sizeof(PageHeader) )
 #define pageBottom(x)       ( alignTo(x,NJ_PAGE_SIZE) + NJ_PAGE_SIZE - 1 )
 #define samepage(x,y)       ( pageTop(x) == pageTop(y) )
 
 
-/* Debug printing stuff.  All Nanojit debug printing should be routed
-   through this function.  Don't use ad-hoc calls to printf,
-   fprintf(stderr, ...) etc. */
+// -------------------------------------------------------------------
+// START debug-logging definitions
+// -------------------------------------------------------------------
 
-#if defined(NJ_VERBOSE)
+/* Debug printing stuff.  All Nanojit and jstracer debug printing
+   should be routed through LogControl::printf.  Don't use
+   ad-hoc calls to printf, fprintf(stderr, ...) etc.
+
+   Similarly, don't use ad-hoc getenvs etc to decide whether or not to
+   print debug output.  Instead consult the relevant control bit in
+   LogControl::lcbits in the LogControl object you are supplied with.
+*/
 
 # if defined(__GNUC__)
 # define PRINTF_CHECK(x, y) __attribute__((format(__printf__, x, y)))
 # else
 # define PRINTF_CHECK(x, y)
 # endif
 
-/* is in LIR.cpp */
-void nj_dprintf( const char* format, ... ) PRINTF_CHECK(1,2);
+namespace nanojit {
+
+	// LogControl, a class for controlling and routing debug output
 
-#endif /* NJ_VERBOSE */
+	enum LC_Bits {
+		/* Output control bits for Nanojit code.  Only use bits 15
+		   and below, so that callers can use bits 16 and above for
+		   themselves. */
+		// TODO: add entries for the writer pipeline
+		LC_Liveness    = 1<<7, // (show LIR liveness analysis)
+		LC_ReadLIR     = 1<<6, // As read from LirBuffer
+		LC_AfterSF_SP  = 1<<5, // After StackFilter(sp)
+		LC_AfterSF_RP  = 1<<4, // After StackFilter(rp)
+		LC_AfterDeadF  = 1<<3, // After DeadFilter
+		LC_RegAlloc    = 1<<2, // stuff to do with reg alloc
+		LC_Assembly    = 1<<1, // final assembly
+		LC_NoCodeAddrs = 1<<0  // (don't show code addresses on asm output)
+	};
+
+	class LogControl
+	{
+	public:
+		// All Nanojit and jstracer printing should be routed through
+		// this function.
+		void printf( const char* format, ... ) PRINTF_CHECK(2,3);
+
+		// An OR of LC_Bits values, indicating what should be output
+		uint32_t lcbits;
+	};
+
+}
+
+// -------------------------------------------------------------------
+// END debug-logging definitions
+// -------------------------------------------------------------------
 
 
 
 #include "Native.h"
 #include "LIR.h"
 #include "RegAlloc.h"
 #include "Fragmento.h"
 #include "Assembler.h"