Start attaching our tracer to nanojit. This is incomplete and meant for review by David only.
authorAndreas Gal <gal@uci.edu>
Sat, 21 Jun 2008 14:55:32 -0700
changeset 17293 a7edb7cd3362cbe28a01a2a7b4ecb43c41309f48
parent 17292 a7479a421b1f1c8a9c80f4f0790ba21ee5ee2e46
child 17294 6fec9157a4067cd04cacc2ebd1301befc5953a5f
push id1452
push usershaver@mozilla.com
push dateFri, 22 Aug 2008 00:08:22 +0000
treeherdermozilla-central@d13bb0868596 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.1a1pre
Start attaching our tracer to nanojit. This is incomplete and meant for review by David only.
js/src/builtins.tbl
js/src/jsgc.cpp
js/src/jsinterp.cpp
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jstracerinlines.h
js/src/nanojit/Tests.cpp
js/src/nanojit/avmplus.h
js/src/nanojit/vm_fops.h
js/src/recorder.js
new file mode 100644
--- /dev/null
+++ b/js/src/builtins.tbl
@@ -0,0 +1,30 @@
+BUILTIN1(OBJ_IS_NULL,           LO,     LO,     bool,      JSObject*)
+BUILTIN1(DOUBLE_IS_INT,         F,      LO,     bool,      jsdouble)
+BUILTIN1(BOX_IS_BOOLEAN,        Q,      LO,     bool,      Box)
+BUILTIN1(BOX_IS_INT,            Q,      LO,     bool,      Box)
+BUILTIN1(BOX_IS_STRING,         Q,      LO,     bool,      Box)
+BUILTIN1(BOX_IS_DOUBLE,         Q,      LO,     bool,      Box)
+BUILTIN1(BOX_IS_NULL,           Q,      LO,     bool,      Box)
+BUILTIN1(BOX_IS_PRIMITIVE,      Q,      LO,     bool,      Box)
+BUILTIN2(BOTH_BOXES_ARE_INT,    Q,  Q,  LO,     bool,      Box, Box)
+BUILTIN2(BOTH_BOXES_ARE_STRING, Q,  Q,  LO,     bool,      Box, Box)
+BUILTIN1(BOX_BOOLEAN,           LO,     Q,      Box,       bool)
+BUILTIN1(BOX_STRING,            LO,     Q,      Box,       JSString*)
+BUILTIN1(BOX_OBJECT,            LO,     Q,      Box,       JSObject*)
+BUILTIN1(BOX_ID,                LO,     Q,      Box,       jsid)
+BUILTIN1(BOX_INT,               LO,     Q,      Box,       jsint)
+BUILTIN1(BOX_DOUBLE,            F,      Q,      Box,       jsdouble)
+BUILTIN1(BOX_UINT,              LO,     Q,      Box,       uint32)
+BUILTIN1(UNBOX_BOOLEAN,         Q,      LO,     bool,      Box)
+BUILTIN1(UNBOX_STRING,          Q,      LO,     JSString*, Box)
+BUILTIN1(UNBOX_OBJECT,          Q,      LO,     JSObject*, Box)
+BUILTIN1(UNBOX_INT,             Q,      LO,     jsint,     Box)
+BUILTIN1(UNBOX_DOUBLE,          Q,      F,      jsdouble,  Box)
+BUILTIN2(dmod,                  F,  F,  F,      jsdouble,  jsdouble, jsdouble)
+BUILTIN2(ValueToECMAInt32,      LO, Q,  LO,     jsint,     JSContext*, Box)
+BUILTIN2(ValueToECMAUint32,     LO, Q,  LO,     uint32,    JSContext*, Box)
+BUILTIN1(ValueToBoolean,        Q,      LO,     bool,      Box)
+BUILTIN2(ValueToNonNullObject,  LO, Q,  LO,     JSObject*, JSContext*, Box)
+BUILTIN2(ValueToNumber,         LO, Q,  F,      jsdouble,  JSContext*, Box)
+BUILTIN3(obj_default_value,     LO, LO, LO, Q,  Box,       JSContext*, JSObject*, JSType hint)        
+BUILTIN2(CompareStrings,        LO, LO, LO,     bool,      JSString*, JSString*)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2858,18 +2858,16 @@ js_TraceContext(JSTracer *trc, JSContext
 
     if (acx->sharpObjectMap.depth > 0)
         js_TraceSharpMap(trc, &acx->sharpObjectMap);
 }
 
 void
 js_TraceTraceMonitor(JSTracer *trc, JSTraceMonitor *tm)
 {
-    if (tm->recorder)
-        JS_CALL_OBJECT_TRACER(trc, tm->recorder, "recorder object");
 }
 
 void
 js_TraceRuntime(JSTracer *trc, JSBool allAtoms)
 {
     JSRuntime *rt = trc->context->runtime;
     JSContext *iter, *acx;
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -7000,17 +7000,17 @@ JS_INTERPRET(JSContext *cx, JSInterprete
 #endif /* !JS_THREADED_INTERP */
 
   error:
 #ifdef jstracer_cpp___
     SAVE_STATE(state, JS_NEXT_ERROR);
     return JS_FALSE;
 
   abort_trace:
-      js_CallRecorder(cx, "stop", native_pointer_to_jsval(regs.pc));
+      js_StopRecorder(cx, regs);
       SAVE_STATE(state, JS_NEXT_CONTINUE);
       return ok;
 #else
     JS_ASSERT((size_t)(regs.pc - script->code) < script->length);
     if (!cx->throwing) {
         /* This is an error, not a catchable exception, quit the frame ASAP. */
         ok = JS_FALSE;
     } else {
@@ -7222,17 +7222,17 @@ JS_INTERPRET(JSContext *cx, JSInterprete
         if (printable)
             js_ReportIsNotDefined(cx, printable);
         goto error;
     }
 
 #ifndef jstracer_cpp___
   attempt_tracing:
     {
-        js_CallRecorder(cx, "start", native_pointer_to_jsval(regs.pc));
+        js_StartRecorder(cx, regs);
         if (js_GetRecorderError(cx)) {
             op = (JSOp) *regs.pc;
             DO_OP();
         }
         ok = JS_TRUE;
         JSInterpreterState s;
         SAVE_STATE(&s, JS_NEXT_CONTINUE);
         js_TracingInterpret(cx, &s);
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -33,86 +33,176 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #define jstracer_cpp___
 
+#include "nanojit/avmplus.h"
+#include "nanojit/nanojit.h"
+
+using namespace nanojit;
+
 #include "jsinterp.cpp"
 
-JSObject*
-js_GetRecorder(JSContext* cx)
+Tracker::Tracker()
+{
+    pagelist = 0;
+}
+
+Tracker::~Tracker()
 {
-    JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
-    if (tm->recorder)
-        return tm->recorder;
-    JSScript* script = JS_CompileFile(cx, JS_GetGlobalObject(cx), "recorder.js");
-    JS_ASSERT(script != NULL);
-    jsval result;
+    clear();
+}
 
-#ifdef DEBUG
-    JSBool ok =
-#endif
-    JS_ExecuteScript(cx, JS_GetGlobalObject(cx), script, &result);
-    JS_ASSERT(ok && !JSVAL_IS_PRIMITIVE(result));
-    return tm->recorder = JSVAL_TO_OBJECT(result);
+long
+Tracker::getPageBase(const void* v) const
+{
+    return ((long)v) & (~(NJ_PAGE_SIZE-1));
 }
 
-void
-js_CallRecorder(JSContext* cx, const char* name, uintN argc, jsval* argv)
+struct Tracker::Page* 
+Tracker::findPage(const void* v) const 
 {
-    jsval rval;
-    JSBool ok =
-        JS_CallFunctionName(cx, js_GetRecorder(cx), name, argc, argv, &rval);
-    if (!ok) {
-#ifdef DEBUG
-        printf("recorder: unsupported instruction '%s'\n", name);
-#endif        
-        JS_ClearPendingException(cx);
-        js_TriggerRecorderError(cx);
-    }        
-    JS_ASSERT(ok);
+    long base = getPageBase(v);
+    struct Tracker::Page* p = pagelist;
+    while (p) {
+        if (p->base == base) 
+            return p;
+        p = p->next;
+    }
+    return 0;
+}
+
+struct Tracker::Page*
+Tracker::addPage(const void* v) {
+    long base = getPageBase(v);
+    struct Tracker::Page* p = (struct Tracker::Page*)
+        GC::Alloc(sizeof(struct Tracker::Page) + (NJ_PAGE_SIZE >> 2));
+    p->base = base;
+    p->next = pagelist;
+    pagelist = p;
+    return p;
 }
 
 void
-js_CallRecorder(JSContext* cx, const char* name, jsval a)
+Tracker::clear()
+{
+    while (pagelist) {
+        Page* p = pagelist;
+        pagelist = pagelist->next;
+        GC::Free(p);
+    }
+}
+
+LIns* 
+Tracker::get(const void* v) const 
+{
+    struct Tracker::Page* p = findPage(v);
+    JS_ASSERT(p != 0); /* we must have a page for the slot we are looking for */
+    return p->map[(((long)v) & 0xfff) >> 2];
+}
+
+void 
+Tracker::set(const void* v, LIns* ins) 
 {
-    jsval args[] = { a };
-    js_CallRecorder(cx, name, 1, args);
+    struct Tracker::Page* p = findPage(v);
+    if (!p) 
+        p = addPage(v);
+    p->map[(((long)v) & 0xfff) >> 2] = ins;
 }
 
+using namespace avmplus;
+using namespace nanojit;
+
+static avmplus::AvmCore* core = new avmplus::AvmCore();
+static GC gc = GC();
+
+#define LO ARGSIZE_LO
+#define F  ARGSIZE_F
+#define Q  ARGSIZE_Q
+
+#ifdef DEBUG
+#define NAME(op) ,#op
+#else
+#define NAME(op)
+#endif
+
+#define BUILTIN1(op, at0, atr, tr, t0) \
+    { 0, (at0 | (atr << 2)), 1/*cse*/, 1/*fold*/ NAME(op) },
+#define BUILTIN2(op, at0, at1, atr, tr, t0, t1) \
+    { 0, (at0 | (at1 << 2) | (atr << 4)), 1/*cse*/, 1/*fold*/ NAME(op) },
+#define BUILTIN3(op, at0, at1, at2, atr, tr, t0, t1, t2) \
+    { 0, (at0 | (at1 << 2) | (at2 << 4) | (atr << 6)), 1/*cse*/, 1/*fold*/ NAME(op) },
+
+static struct CallInfo builtins[] = {
+#include "builtins.tbl"
+};        
+
+#undef NAME    
+#undef BUILTIN1
+#undef BUILTIN2
+#undef BUILTIN3    
+
 void
-js_CallRecorder(JSContext* cx, const char* name, jsval a, jsval b)
+js_StartRecorder(JSContext* cx, JSFrameRegs& regs)
 {
-    jsval args[] = { a, b };
-    js_CallRecorder(cx, name, 2, args);
-}
+    struct JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
+
+    if (!tm->fragmento) {
+        tm->fragmento = new (&gc) Fragmento(core);
+        tm->fragmento->labels = new (&gc) LabelMap(core, NULL);
+    }   
+
+    InterpState state;
+    state.ip = NULL;
+    state.sp = NULL;
+    state.rp = NULL;
+    state.f = NULL;
 
-void
-js_CallRecorder(JSContext* cx, const char* name, jsval a, jsval b, jsval c)
-{
-    jsval args[] = { a, b, c };
-    js_CallRecorder(cx, name, 3, args);
+    Fragment* fragment = tm->fragmento->getLoop(state);
+    LirBuffer* lirbuf = new (&gc) LirBuffer(tm->fragmento, builtins);
+    lirbuf->names = new (&gc) LirNameMap(&gc, NULL, tm->fragmento->labels);
+    fragment->lirbuf = lirbuf;
+    LirWriter* lir = new (&gc) LirBufWriter(lirbuf);
+    lir->ins0(LIR_trace);
+    fragment->param0 = lir->insImm8(LIR_param, Assembler::argRegs[0], 0);
+    fragment->param1 = lir->insImm8(LIR_param, Assembler::argRegs[1], 0);
+    JSStackFrame* fp = cx->fp;
+
+    tm->tracker.set(cx, fragment->param0);
+
+#define STACK_OFFSET(p) (((char*)(p)) - ((char*)regs.sp))
+#define LOAD_CONTEXT(p) tm->tracker.set(p, lir->insLoadi(fragment->param1, STACK_OFFSET(p)))    
+
+    unsigned n;
+    for (n = 0; n < fp->argc; ++n)
+        LOAD_CONTEXT(&fp->argv[n]);
+    for (n = 0; n < fp->nvars; ++n)
+        LOAD_CONTEXT(&fp->vars[n]);
+    for (n = 0; n < (unsigned)(regs.sp - fp->spbase); ++n)
+        LOAD_CONTEXT(&fp->spbase[n]);
+    
+    tm->fragment = fragment;
+    tm->lir = lir;
 }
 
 void
-js_CallRecorder(JSContext* cx, const char* name, jsval a, jsval b, jsval c, jsval d)
+js_StopRecorder(JSContext* cx, JSFrameRegs& regs)
 {
-    jsval args[] = { a, b, c, d };
-    js_CallRecorder(cx, name, 4, args);
+    struct JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
+
+    SideExit exit;
+    memset(&exit, 0, sizeof(exit));
+    exit.from = tm->fragment;
+    tm->fragment->lastIns = tm->lir->insGuard(LIR_x, NULL, &exit);
+    compile(tm->fragmento->assm(), tm->fragment);
 }
 
-void
-js_TriggerRecorderError(JSContext* cx)
+bool
+js_GetRecorderError(JSContext* cx)
 {
-    jsval error = JSVAL_TRUE;
-    JS_SetProperty(cx, js_GetRecorder(cx), "error", &error); 
+    return false;
 }
 
-bool 
-js_GetRecorderError(JSContext* cx)
-{
-    jsval error;
-    bool ok = JS_GetProperty(cx, js_GetRecorder(cx), "error", &error);
-    return ok && (error != JSVAL_FALSE);
-}
+
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -37,61 +37,71 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jstracer_h___
 #define jstracer_h___
 
 #include "jsstddef.h"
 #include "jslock.h"
 
+namespace nanojit {
+    class LIns;
+    class Fragmento;
+    class Fragment;
+    class LirWriter;
+}
+
+/*
+ * Tracker is used to keep track of values being manipulated by the 
+ * interpreter during trace recording.
+ */
+class Tracker 
+{
+    struct Page {
+        struct Page* next;
+        long base;
+        nanojit::LIns* map[0];
+    };
+    struct Page* pagelist;
+    
+    long         getPageBase(const void* v) const;
+    struct Page* findPage(const void* v) const;
+    struct Page* addPage(const void* v);
+public:    
+    Tracker();
+    ~Tracker();
+    
+    nanojit::LIns*  get(const void* v) const;
+    void            set(const void* v, nanojit::LIns* ins);
+    void            clear();
+};
+
 /*
  * Trace monitor. Every runtime is associated with a trace monitor that keeps
  * track of loop frequencies for all JavaScript code loaded into that runtime.
  * For this we use a loop table. Adjacent slots in the loop table, one for each
  * loop header in a given script, are requested using lock-free synchronization
  * from the runtime-wide loop table slot space, when the script is compiled.
  *
  * The loop table also doubles as trace tree pointer table once a loop achieves
  * a certain number of iterations and we recorded a tree for that loop.
  */
 struct JSTraceMonitor {
-    int         freq;
-    JSObject*   recorder;
+    int                 freq;
+    Tracker             tracker;
+    nanojit::Fragment*  fragment;
+    nanojit::Fragmento* fragmento;
+    nanojit::LirWriter* lir;
 };
 
-#define ENABLE_TRACER      false
+#define ENABLE_TRACER      true
 #define TRACE_TRIGGER_MASK 0x3f
 
-void 
-js_CallRecorder(JSContext* cx, const char* name, uintN argc, jsval* argv);
-
-void 
-js_CallRecorder(JSContext* cx, const char* name, jsval a);
-
-void 
-js_CallRecorder(JSContext* cx, const char* name, jsval a, jsval b);
+extern void
+js_StartRecorder(JSContext* cx, JSFrameRegs& regs);
 
-void 
-js_CallRecorder(JSContext* cx, const char* name, jsval a, jsval b, jsval c);
+extern void
+js_StopRecorder(JSContext* cx, JSFrameRegs& regs);
 
-void 
-js_CallRecorder(JSContext* cx, const char* name, jsval a, jsval b, jsval c, jsval d);
-
-void
-js_TriggerRecorderError(JSContext* cx);
-
-bool 
+extern bool
 js_GetRecorderError(JSContext* cx);
 
-/*
- * The recorder needs to keep track of native machine addresses, including
- * bytecode addresses which are currently arbitrarily byte-aligned. Therefore
- * we cannot use PRIVATE_TO_JSVAL, which assumes at least (0 mod 2) alignment
- * and unconditionally sets the least significant (JSVAL_INT) bit. Instead, we
- * risk lopping off the most significant bit (or bits on 64-bit systems).
- */
-static inline jsval
-native_pointer_to_jsval(void* p)
-{
-    return INT_TO_JSVAL(JS_PTR_TO_UINT32(p));
-}
-
 #endif /* jstracer_h___ */
--- a/js/src/jstracerinlines.h
+++ b/js/src/jstracerinlines.h
@@ -40,595 +40,557 @@
 #define jstracerinlines_h___
 
 #define PRIMITIVE(x) interp_##x
 
 #include "jsinterpinlines.h"	
 
 #undef PRIMITIVE
 
-static inline void 
-record(JSContext* cx, const char* name, void* a) 
-{
-    js_CallRecorder(cx, name, native_pointer_to_jsval(a));
-}
-
-static inline void
-record(JSContext* cx, const char* name, void* a, void* b)
-{
-    js_CallRecorder(cx, name, native_pointer_to_jsval(a),
-                              native_pointer_to_jsval(b));
-}
-
-static inline void
-record(JSContext* cx, const char* name, void* a, void* b, void* c)
-{
-    js_CallRecorder(cx, name, native_pointer_to_jsval(a),
-                              native_pointer_to_jsval(b),
-                              native_pointer_to_jsval(c));
-}
-
-static inline void
-record(JSContext* cx, const char* name, void* a, void* b, void* c, void* d)
-{
-    js_CallRecorder(cx, name, native_pointer_to_jsval(a),
-                              native_pointer_to_jsval(b),
-                              native_pointer_to_jsval(c),
-                              native_pointer_to_jsval(d));
-}
-
-static inline void 
-record(JSContext* cx, const char* name, void* a, jsval v)
-{
-    js_CallRecorder(cx, name, native_pointer_to_jsval(a),
-                              v);
-}
-
-static inline void 
-record(JSContext* cx, const char* name, void* a, void* b, jsval v)
-{
-    js_CallRecorder(cx, name, native_pointer_to_jsval(a),
-                              native_pointer_to_jsval(b),
-                              v);
-}
-
-static inline void 
-record(JSContext* cx, const char* name, void* a, void* b, jsval v, jsval v2)
-{
-    js_CallRecorder(cx, name, native_pointer_to_jsval(a),
-                              native_pointer_to_jsval(b),
-                              v,
-                              v2);
-}
-
-static inline void 
-record(JSContext* cx, const char* name, void* a, jsdouble d)
-{
-    if (!JS_EnterLocalRootScope(cx)) {
-        js_TriggerRecorderError(cx);
-        return;
-    }
-    jsval v;
-    if (!JS_NewDoubleValue(cx, d, &v)) {
-        js_TriggerRecorderError(cx);
-    } else {
-        record(cx, name, a, &v);
-    }
-    JS_LeaveLocalRootScope(cx);
-}
-
-static inline void 
-record(JSContext* cx, const char* name, void* a, void* b, jsdouble d)
-{
-    if (!JS_EnterLocalRootScope(cx)) {
-        js_TriggerRecorderError(cx);
-        return;
-    }
-    jsval v;
-    if (!JS_NewDoubleValue(cx, d, &v)) {
-        js_TriggerRecorderError(cx);
-    } else {
-        record(cx, name, a, b, &v);
-    }
-    JS_LeaveLocalRootScope(cx);
-}
-
-static inline void 
-record(JSContext* cx, const char* name, void* a, void* b, void* c, jsdouble d)
-{
-    if (!JS_EnterLocalRootScope(cx)) {
-        js_TriggerRecorderError(cx);
-        return;
-    }
-    jsval v;
-    if (!JS_NewDoubleValue(cx, d, &v)) {
-        js_TriggerRecorderError(cx);
-    } else {
-        record(cx, name, a, b, c, &v);
-    }
-    JS_LeaveLocalRootScope(cx);
-}
+#define set(v, l)       JS_TRACE_MONITOR(cx).tracker.set(v, l)
+#define get(v)          JS_TRACE_MONITOR(cx).tracker.get(v)
+#define L               (*JS_TRACE_MONITOR(cx).lir)
+#define F(name)         F_##name
+#define G(ok)           (ok ? LIR_xf : LIR_xt)
+#define unary(op, a, v) set(v, L.ins1(op, get(a)))
+#define binary(op, a, b, v)                                                  \
+                        set(v, L.ins2(op, get(a), get(b)))
+#define call1(n, a, v)                                                       \
+    do {                                                                     \
+    LInsp args[] = { get(a) };                                               \
+    set(v, L.insCall(F(n), args));                                           \
+    } while(0)                                                            
+#define call2(n, a, b, v)                                                    \
+    do {                                                                     \
+    LInsp args[] = { get(a), get(b) };                                       \
+    set(v, L.insCall(F(n), args));                                           \
+    } while(0)                                                            
+#define call3(n, a, b, c, v)                                                 \
+    do {                                                                     \
+    LInsp args[] = { get(a), get(b), get(c) };                               \
+    set(v, L.insCall(F(n), args));                                           \
+    } while(0)                                                            
 
 static inline void
 prim_copy(JSContext* cx, jsval& from, jsval& to)
 {
     interp_prim_copy(cx, from, to);
-    record(cx, "track", &from, &to);
+    set(&to, get(&from));
 }
 
 static inline void
 prim_push_stack(JSContext* cx, JSFrameRegs& regs, jsval& v)
 {
-    record(cx, "track", &v, regs.sp);
+    set(regs.sp, get(&v));
     interp_prim_push_stack(cx, regs, v);
-    record(cx, "setSP", regs.sp);
 }
 
 static inline void
 prim_pop_stack(JSContext* cx, JSFrameRegs& regs, jsval& v)
 {
     interp_prim_pop_stack(cx, regs, v);
-    record(cx, "track", regs.sp, &v);
-    record(cx, "setSP", regs.sp);
+    set(&v, get(regs.sp));
 }
 
 static inline void
 prim_store_stack(JSContext* cx, JSFrameRegs& regs, int n, jsval& v)
 {
-    record(cx, "track", &v, &regs.sp[n]);
     interp_prim_store_stack(cx, regs, n, v);
+    set(&regs.sp[n], get(&v));
 }
 
 static inline void
 prim_fetch_stack(JSContext* cx, JSFrameRegs& regs, int n, jsval& v)
 {
-    record(cx, "track", &regs.sp[n], &v);
     interp_prim_fetch_stack(cx, regs, n, v);
+    s   et(&v, get(&regs.sp[n]));
 }
-
+    
 static inline void
 prim_adjust_stack(JSContext* cx, JSFrameRegs& regs, int n)
 {
     interp_prim_adjust_stack(cx, regs, n);
-    record(cx, "setSP", regs.sp);
 }
 
 static inline void
 prim_generate_constant(JSContext* cx, jsval c, jsval& v)
 {
     interp_prim_generate_constant(cx, c, v);
-    record(cx, "generate_constant", &v, c);
+    set(&v, L.insImm(c));
 }
 
 static inline void
 prim_boolean_to_jsval(JSContext* cx, JSBool& b, jsval& v)
 {
     interp_prim_boolean_to_jsval(cx, b, v);
-    record(cx, "boolean_to_jsval", &b, &v, v);
+    call1(BOX_BOOLEAN, &b, &v);
 }
 
 static inline void
 prim_string_to_jsval(JSContext* cx, JSString*& str, jsval& v)
 {
     interp_prim_string_to_jsval(cx, str, v);
-    record(cx, "string_to_jsval", &str, &v, v);
+    call1(BOX_STRING, &str, &v);
 }
 
 static inline void
 prim_object_to_jsval(JSContext* cx, JSObject*& obj, jsval& v)
 {
     interp_prim_object_to_jsval(cx, obj, v);
-    record(cx, "object_to_jsval", &obj, &v, v);
+    call1(BOX_OBJECT, &obj, &v);
 }
 
 static inline void
 prim_id_to_jsval(JSContext* cx, jsid& id, jsval& v)
 {
     interp_prim_id_to_jsval(cx, id, v);
-    record(cx, "id_to_jsval", &id, &v, v);
+    call1(BOX_ID, &id, &v);
 }
 
 static inline bool
 guard_jsdouble_is_int_and_int_fits_in_jsval(JSContext* cx, jsdouble& d, jsint& i)
 {
     bool ok = interp_guard_jsdouble_is_int_and_int_fits_in_jsval(cx, d, i);
-    record(cx, "guard_jsdouble_is_int_and_int_fits_in_jsval", &d, &i, 
-           INT_TO_JSVAL(i), BOOLEAN_TO_JSVAL(ok));
+    /* Trace-code boxes into 64-bit slots, and ints always fit, so we only check
+       that it is actually an int. */
+    call1(DOUBLE_IS_INT, &d, &i); 
+    L.insGuard(G(ok), 
+            L.ins1(LIR_qhi, get(&i)),
+            (SideExit*)0); // TODO
+    unary(LIR_qlo, &i, &i);
     return ok;
 }
 
 static inline void
 prim_int_to_jsval(JSContext* cx, jsint& i, jsval& v)
 {
     interp_prim_int_to_jsval(cx, i, v);
-    record(cx, "int_to_jsval", &i, &v, v);
+    call1(BOX_INT, &i, &v);
 }
 
 static inline bool
 call_NewDoubleInRootedValue(JSContext* cx, jsdouble& d, jsval& v)
 {
     bool ok = interp_call_NewDoubleInRootedValue(cx, d, v);
-    record(cx, "new_double_in_rooted_value", &d, &v, BOOLEAN_TO_JSVAL(ok));
+    call1(BOX_DOUBLE, &d, &v); 
     return ok;
 }
 
 static inline bool
 guard_int_fits_in_jsval(JSContext* cx, jsint& i)
 {
     bool ok = interp_guard_int_fits_in_jsval(cx, i);
-    record(cx, "guard_int_fits_in_jsval", &i, BOOLEAN_TO_JSVAL(ok));
+    /* Trace-code boxes into 64-bit slots and ints always fit. */
     return ok;
 }
 
 static inline void
 prim_int_to_double(JSContext* cx, jsint& i, jsdouble& d)
 {
     interp_prim_int_to_double(cx, i, d);
-    record(cx, "int_to_double", &i, &d, d);
+    unary(LIR_i2f, &i, &d);
 }
 
 static inline bool
 guard_uint_fits_in_jsval(JSContext* cx, uint32& u)
 {
     bool ok = interp_guard_uint_fits_in_jsval(cx, u);
-    record(cx, "guard_uint_fits_in_jsval", &u, BOOLEAN_TO_JSVAL(ok));
+    /* Trace-code boxes into 64-bit slots and uints always fit. */
     return ok;
 }
 
 static inline void
 prim_uint_to_jsval(JSContext* cx, uint32& u, jsval& v)
 {
     interp_prim_uint_to_jsval(cx, u, v);
-    record(cx, "uint_to_jsval", &u, &v, v);
+    call1(BOX_UINT, &u, &v);
 }
 
 static inline void
 prim_uint_to_double(JSContext* cx, uint32& u, jsdouble& d)
 {
     interp_prim_uint_to_double(cx, u, d);
-    record(cx, "uint_to_double", &u, &d, d);
+    unary(LIR_u2f, &u, &d);
 }
 
 static inline bool
 guard_jsval_is_int(JSContext* cx, jsval& v)
 {
     bool ok = interp_guard_jsval_is_int(cx, v);
-    record(cx, "guard_jsval_is_int", &v, BOOLEAN_TO_JSVAL(ok));
+    call1(BOX_IS_INT, &v, &ok);
+    L.insGuard(G(ok),
+            get(&ok),
+            (SideExit*)0); // TODO
     return ok;
 }
 
 static inline void
 prim_jsval_to_int(JSContext* cx, jsval& v, jsint& i)
 {
     interp_prim_jsval_to_int(cx, v, i);
-    record(cx, "jsval_to_int", &v, &i, v);
+    call1(UNBOX_INT, &v, &i);
 }
 
 static inline bool
 guard_jsval_is_double(JSContext* cx, jsval& v)
 {
     bool ok = interp_guard_jsval_is_double(cx, v);
-    record(cx, "guard_jsval_is_double", &v, BOOLEAN_TO_JSVAL(ok));
+    call1(BOX_IS_DOUBLE, &v, &ok);
+    L.insGuard(G(ok),
+            get(&ok),
+            (SideExit*)0); // TODO
     return ok;
 }
 
 static inline void
 prim_jsval_to_double(JSContext* cx, jsval& v, jsdouble& d)
 {
     interp_prim_jsval_to_double(cx, v, d);
-    record(cx, "jsval_to_double", &v, &d, v);
+    call1(UNBOX_DOUBLE, &v, &d);
 }
 
 static inline void
 call_ValueToNumber(JSContext* cx, jsval& v, jsdouble& d)
 {
     interp_call_ValueToNumber(cx, v, d);
-    record(cx, "ValueToNumber", &v, &d, v);
+    call2(ValueToNumber, cx, &v, &d);
 }
 
 static inline bool
 guard_jsval_is_null(JSContext* cx, jsval& v)
 {
     bool ok = interp_guard_jsval_is_null(cx, v);
-    record(cx, "guard_jsval_is_null", &v, BOOLEAN_TO_JSVAL(ok));
+    call1(BOX_IS_NULL, &v, &ok);
+    L.insGuard(G(ok),
+            get(&ok),
+            (SideExit*)0); // TODO
     return ok;
 }
 
 static inline void
 call_ValueToECMAInt32(JSContext* cx, jsval& v, jsint& i)
 {
     interp_call_ValueToECMAInt32(cx, v, i);
-    record(cx, "ValueToECMAInt32", &v, &i, (double)i);
+    call2(ValueToECMAInt32, cx, &v, &i);
 }
 
 static inline void
 prim_int_to_uint(JSContext* cx, jsint& i, uint32& u)
 {
     interp_prim_int_to_uint(cx, i, u);
-    record(cx, "int_to_uint", &i, &u, (double)u);
+    set(&u, get(&i));
 }
 
 static inline void
 call_ValueToECMAUint32(JSContext* cx, jsval& v, uint32& u)
 {
     interp_call_ValueToECMAUint32(cx, v, u);
-    record(cx, "ValueToECMAUint32", &v, &u, (double)u);
+    call2(ValueToECMAUint32, cx, &v, &u);
 }
 
 static inline void
 prim_generate_boolean_constant(JSContext* cx, JSBool c, JSBool& b)
 {
     interp_prim_generate_boolean_constant(cx, c, b);
-    record(cx, "generate_boolean_constant", &b, BOOLEAN_TO_JSVAL(c));
+    set(&b, L.insImm(c));
 }
 
 static inline bool
 guard_jsval_is_boolean(JSContext* cx, jsval& v)
 {
     bool ok = interp_guard_jsval_is_boolean(cx, v);
-    record(cx, "guard_jsval_is_boolean", &v, BOOLEAN_TO_JSVAL(ok));
+    call1(BOX_IS_BOOLEAN, &v, &ok);
+    L.insGuard(G(ok),
+            get(&ok),
+            (SideExit*)0); // TODO
     return ok;
 }
 
 static inline void
 prim_jsval_to_boolean(JSContext* cx, jsval& v, JSBool& b)
 {
     interp_prim_jsval_to_boolean(cx, v, b);
-    record(cx, "jsval_to_boolean", &v, &b, v);
+    call1(UNBOX_BOOLEAN, &v, &b);
 }
 
 static inline void
 call_ValueToBoolean(JSContext* cx, jsval& v, JSBool& b)
 {
     interp_call_ValueToBoolean(cx, v, b);
-    record(cx, "ValueToBoolean", &v, &b, BOOLEAN_TO_JSVAL(b));;
+    call2(ValueToBoolean, cx, &v, &b);
 }
 
 static inline bool
 guard_jsval_is_primitive(JSContext* cx, jsval& v)
 {
     bool ok = interp_guard_jsval_is_primitive(cx, v);
-    record(cx, "guard_jsval_is_primitive", &v, BOOLEAN_TO_JSVAL(ok));
+    call1(BOX_IS_PRIMITIVE, &v, &ok);
+    L.insGuard(G(ok),
+            get(&ok),
+            (SideExit*)0); // TODO
     return ok;
 }
 
 static inline void
 prim_jsval_to_object(JSContext* cx, jsval& v, JSObject*& obj)
 {
     interp_prim_jsval_to_object(cx, v, obj);
-    record(cx, "jsval_to_object", &v, &obj, v);
+    call1(UNBOX_OBJECT, &v, &obj);
 }
 
 static inline bool
 guard_obj_is_null(JSContext* cx, JSObject*& obj)
 {
     bool ok = interp_guard_obj_is_null(cx, obj);
-    record(cx, "guard_obj_is_null", &obj, BOOLEAN_TO_JSVAL(ok));
+    call1(OBJ_IS_NULL, &obj, &ok);
+    L.insGuard(G(ok),
+            get(&ok),
+            (SideExit*)0); // TODO
     return ok;
 }
 
 static inline void
 call_ValueToNonNullObject(JSContext* cx, jsval& v, JSObject*& obj)
 {
     interp_call_ValueToNonNullObject(cx, v, obj);
-    record(cx, "ValueToNonNullObject", &v, &obj, OBJECT_TO_JSVAL(obj));
+    call2(ValueToNonNullObject, cx, &v, &obj);
 }
 
 static inline bool
 call_obj_default_value(JSContext* cx, JSObject*& obj, JSType hint,
                        jsval& v)
 {
     bool ok = interp_call_obj_default_value(cx, obj, hint, v);
-    record(cx, "obj_default_value", &obj, &v, INT_TO_JSVAL(hint), 
-           BOOLEAN_TO_JSVAL(ok));
+    call3(obj_default_value, cx, &obj, L.insImm(hint), &v);
     return ok;
 }
 
 static inline void
 prim_dadd(JSContext* cx, jsdouble& a, jsdouble& b, jsdouble& r)
 {
     interp_prim_dadd(cx, a, b, r);
-    record(cx, "dadd", &a, &b, &r, r);
+    binary(LIR_fadd, &a, &b, &r);
 }
 
 static inline void
 prim_dsub(JSContext* cx, jsdouble& a, jsdouble& b, jsdouble& r)
 {
     interp_prim_dsub(cx, a, b, r);
-    record(cx, "dsub", &a, &b, &r, r);
+    binary(LIR_fsub, &a, &b, &r);
 }
 
 static inline void
 prim_dmul(JSContext* cx, jsdouble& a, jsdouble& b, jsdouble& r)
 {
     interp_prim_dmul(cx, a, b, r);
-    record(cx, "dmul", &a, &b, &r, r);
+    binary(LIR_fmul, &a, &b, &r);
 }
 
 static inline bool
 prim_ddiv(JSContext* cx, JSRuntime* rt, JSFrameRegs& regs, int n,
                      jsdouble& a, jsdouble& b)
 {
-    js_TriggerRecorderError(cx);
-    return interp_prim_ddiv(cx, rt, regs, n, a, b);
+    bool ok = interp_prim_ddiv(cx, rt, regs, n, a, b);
+    jsdouble r;
+    binary(LIR_fdiv, &a, &b, &r);
+    call1(BOX_DOUBLE, &r, &regs.sp[n]);
+    return ok;
 }
 
 static inline bool
 prim_dmod(JSContext* cx, JSRuntime* rt, JSFrameRegs& regs, int n,
                      jsdouble& a, jsdouble& b)
 {
-    js_TriggerRecorderError(cx);
-    return interp_prim_dmod(cx, rt, regs, n, a, b);
+    bool ok = interp_prim_dmod(cx, rt, regs, n, a, b);
+    jsdouble r;
+    call2(dmod, &a, &b, &r);
+    call1(BOX_DOUBLE, &r, &regs.sp[n]);
+    return ok;
 }
 
 static inline void
 prim_ior(JSContext* cx, jsint& a, jsint& b, jsint& r)
 {
     interp_prim_ior(cx, a, b, r);
-    record(cx, "ior", &a, &b, &r, (double)r);
+    binary(LIR_or, &a, &b, &r);
 }
 
 static inline void
 prim_ixor(JSContext* cx, jsint& a, jsint& b, jsint& r)
 {
     interp_prim_ixor(cx, a, b, r);
-    record(cx, "ixor", &a, &b, &r, (double)r);
+    binary(LIR_xor, &a, &b, &r);
 }
 
 static inline void
 prim_iand(JSContext* cx, jsint& a, jsint& b, jsint& r)
 {
     interp_prim_iand(cx, a, b, r);
-    record(cx, "iand", &a, &b, &r, (double)r);
+    binary(LIR_and, &a, &b, &r);
 }
 
 static inline void
 prim_ilsh(JSContext* cx, jsint& a, jsint& b, jsint& r)
 {
     interp_prim_ilsh(cx, a, b, r);
-    record(cx, "ilsh", &a, &b, &r, (double)r);
+    binary(LIR_lsh, &a, &b, &r);
 }
 
 static inline void
 prim_irsh(JSContext* cx, jsint& a, jsint& b, jsint& r)
 {
     interp_prim_irsh(cx, a, b, r);
-    record(cx, "irsh", &a, &b, &r, (double)r);
+    binary(LIR_rsh, &a, &b, &r);
 }
 
 static inline void
 prim_ursh(JSContext* cx, uint32& a, jsint& b, uint32& r)
 {
     interp_prim_ursh(cx, a, b, r);
-    record(cx, "ursh", &a, &b, &r, (double)r);
+    binary(LIR_ush, &a, &b, &r);
 }
 
 static inline bool
 guard_boolean_is_true(JSContext* cx, JSBool& cond)
 {
     bool ok = interp_guard_boolean_is_true(cx, cond);
-    record(cx, "guard_boolean_is_true", &cond, BOOLEAN_TO_JSVAL(ok));
+    L.insGuard(G(ok),
+            get(&cond),
+            (SideExit*)0); // TODO
     return ok;
 }
 
 static inline void
 prim_icmp_lt(JSContext* cx, jsint& a, jsint& b, JSBool& r)
 {
     interp_prim_icmp_lt(cx, a, b, r);
-    record(cx, "icmp_lt", &a, &b, &r, BOOLEAN_TO_JSVAL(r));
+    binary(LIR_lt, &a, &b, &r);
 }
 
 static inline void
 prim_icmp_le(JSContext* cx, jsint& a, jsint& b, JSBool& r)
 {
     interp_prim_icmp_le(cx, a, b, r);
-    record(cx, "icmp_le", &a, &b, &r, BOOLEAN_TO_JSVAL(r));
+    binary(LIR_le, &a, &b, &r);
 }
 
 static inline void
 prim_icmp_gt(JSContext* cx, jsint& a, jsint& b, JSBool& r)
 {
     interp_prim_icmp_gt(cx, a, b, r);
-    record(cx, "icmp_gt", &a, &b, &r, BOOLEAN_TO_JSVAL(r));
+    binary(LIR_gt, &a, &b, &r);
 }
 
 static inline void
 prim_icmp_ge(JSContext* cx, jsint& a, jsint& b, JSBool& r)
 {
     interp_prim_icmp_ge(cx, a, b, r);
-    record(cx, "icmp_ge", &a, &b, &r, BOOLEAN_TO_JSVAL(r));
+    binary(LIR_ge, &a, &b, &r);
 }
 
 static inline void
 prim_dcmp_lt(JSContext* cx, bool ifnan, jsdouble& a, jsdouble& b, JSBool& r)
 {
     interp_prim_dcmp_lt(cx, ifnan, a, b, r);
-    record(cx, ifnan ? "dcmp_lt_ifnan" : "dcmp_lt", &a, &b, &r, 
-           BOOLEAN_TO_JSVAL(r));
+    binary(LIR_lt, &a, &b, &r); // TODO: check ifnan handling
 }
 
 static inline void
 prim_dcmp_le(JSContext* cx, bool ifnan, jsdouble& a, jsdouble& b, JSBool& r)
 {
     interp_prim_dcmp_le(cx, ifnan, a, b, r);
-    record(cx, ifnan ? "dcmp_le_ifnan" : "dcmp_le", &a, &b, &r, 
-           BOOLEAN_TO_JSVAL(r));
+    binary(LIR_le, &a, &b, &r); // TODO: check ifnan handling
 }
 
 static inline void
 prim_dcmp_gt(JSContext* cx, bool ifnan, jsdouble& a, jsdouble& b, JSBool& r)
 {
     interp_prim_dcmp_gt(cx, ifnan, a, b, r);
-    record(cx, ifnan ? "dcmp_gt_ifnan" : "dcmp_gt", &a, &b, &r, 
-           BOOLEAN_TO_JSVAL(r));
+    binary(LIR_gt, &a, &b, &r); // TODO: check ifnan handling
 }
 
 static inline void
 prim_dcmp_ge(JSContext* cx, bool ifnan, jsdouble& a, jsdouble& b, JSBool& r)
 {
     interp_prim_dcmp_ge(cx, ifnan, a, b, r);
-    record(cx, ifnan ? "dcmp_ge_ifnan" : "dcmp_ge", &a, &b, &r, 
-           BOOLEAN_TO_JSVAL(r));
+    binary(LIR_ge, &a, &b, &r); // TODO: check ifnan handling
 }
 
 static inline void
 prim_generate_int_constant(JSContext* cx, jsint c, jsint& i)
 {
     interp_prim_generate_int_constant(cx, c, i);
-    record(cx, "generate_int_constant", &i, (double)c);
+    set(&i, L.insImm(c));
 }
 
 static inline void
 prim_jsval_to_string(JSContext* cx, jsval& v, JSString*& s)
 {
     interp_prim_jsval_to_string(cx, v, s);
-    record(cx, "jsval_to_string", &v, &s, v);
+    call1(UNBOX_STRING, &v, &s);
 }
 
 static inline void
 call_CompareStrings(JSContext* cx, JSString*& a, JSString*& b, jsint& r)
 {
     interp_call_CompareStrings(cx, a, b, r);
-    record(cx, "CompareStrings", &a, &b, &r, INT_TO_JSVAL(r));
+    call2(CompareStrings, &a, &b, &r);
 }
 
 static inline bool
 guard_both_jsvals_are_int(JSContext* cx, jsval& a, jsval& b)
 {
     bool ok = interp_guard_both_jsvals_are_int(cx, a, b);
-    record(cx, "guard_both_jsvals_are_int", &a, &b, 
-           BOOLEAN_TO_JSVAL(ok));
+    call2(BOTH_BOXES_ARE_INT, &a, &b, &ok);
+    L.insGuard(G(ok),
+            get(&ok),
+            (SideExit*)0); // TODO
     return ok;
 }
 
 static inline bool
 guard_both_jsvals_are_string(JSContext* cx, jsval& a, jsval& b)
 {
     bool ok = interp_guard_both_jsvals_are_string(cx, a, b);
-    record(cx, "guard_both_jsvals_are_string", &a, &b, 
-           BOOLEAN_TO_JSVAL(ok));
+    call2(BOTH_BOXES_ARE_STRING, &a, &b, &ok);
+    L.insGuard(G(ok),
+            get(&ok),
+            (SideExit*)0); // TODO
     return ok;
 }
 
 static inline bool
 guard_can_do_fast_inc_dec(JSContext* cx, jsval& v)
 {
     bool ok = interp_guard_can_do_fast_inc_dec(cx, v);
-    record(cx, "guard_can_do_fast_inc_dec", &v,
-           BOOLEAN_TO_JSVAL(ok));
+    /* Trace-code boxes into 64-bit values and can always do fast inc/dec as long
+       the box contains an integer. */
+    call1(BOX_IS_INT, &v, &ok);
+    L.insGuard(G(ok),
+            get(&ok),
+            (SideExit*)0); // TODO
     return ok;
 }
 
 static inline void
 prim_generate_double_constant(JSContext* cx, jsdouble c, jsdouble& d)
 {
     interp_prim_generate_double_constant(cx, c, d);
-    record(cx, "generate_double_constant", &d, c);
+    set(&d, L.insImmq(*(uint64_t*)&c));
 }
 
 static inline void
 prim_do_fast_inc_dec(JSContext* cx, jsval& a, jsval incr, jsval& r)
 {
     interp_prim_do_fast_inc_dec(cx, a, incr, r);
-    record(cx, incr == 2 ? "do_fast_inc" : "do_fast_dec", &a, &r, r); 
+    call1(UNBOX_INT, &a, &r);
+    set(&r, L.ins2(LIR_add, get(&r), L.insImm(incr/2)));
+    call1(BOX_INT, &r, &r);
 }
 
+#undef set
+#undef get
+#undef L
+#undef F
+#undef G
+#undef call1
+
 #endif /* jstracerinlines_h___ */
--- a/js/src/nanojit/Tests.cpp
+++ b/js/src/nanojit/Tests.cpp
@@ -376,11 +376,14 @@ do_test(Test* test)
     jit(NULL, NULL);
     test->Compare();
 }
 
 void 
 nanojit_test(void)
 {
     do_test(&s_Test1);
+    do_test(&s_Test2);
+    do_test(&s_Test3);
+    do_test(&s_Test4);
 }
 
 #endif
--- a/js/src/nanojit/avmplus.h
+++ b/js/src/nanojit/avmplus.h
@@ -129,17 +129,18 @@ operator new(size_t size, GC* gc)
 
 #define MMGC_MEM_TYPE(x)
 
 typedef int FunctionID;
 
 namespace avmplus
 {
     typedef const uint16_t* FOpcodep;
-
+    typedef long long Box;
+    
     class InterpState
     {
     public:
         void* f;
         FOpcodep ip;
         void* rp;
         void* sp;
     };
--- a/js/src/nanojit/vm_fops.h
+++ b/js/src/nanojit/vm_fops.h
@@ -30,10 +30,19 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#define BUILTIN1(op, at0,      atr, tr, t0)              F_##op,
+#define BUILTIN2(op, at0, at1, atr, tr, t0, t1)          F_##op,
+#define BUILTIN3(op, at0, at1, at2, atr, tr, t0, t1, t2) F_##op,
+
 INTERP_FOPCODE_LIST_BEGIN
+#include "builtins.tbl"
 INTERP_FOPCODE_LIST_END
+
+#undef BUILTIN1
+#undef BUILTIN2
+#undef BUILTIN3
deleted file mode 100644
--- a/js/src/recorder.js
+++ /dev/null
@@ -1,272 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sw=4 et tw=78:
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
- * May 28, 2008.
- *
- * The Initial Developer of the Original Code is
- *   Andreas Gal <gal@uci.edu>
- *
- * Contributor(s):
- *   Brendan Eich <brendan@mozilla.org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
- 
-({
-    start: function(pc, sp) {
-        this.error = false;
-        this.anchorPC = pc;
-        this.anchorSP = this.SP = sp;
-        this.code = [];
-        this.map = {};
-        print("Recording at @" + pc);
-    },
-    stop: function(pc) {
-        print("Recording failed at @" + pc);
-    },
-    /* track the data flow through locals */
-    track: function(from, to) {
-        this.map[to] = this.map[from];
-    },
-    /* register a change in the stack pointer */
-    setSP: function(sp) {
-        this.SP = sp;
-    },
-    /* emit an IR instruction */
-    emit: function(x, into) {
-        return this.code.push(x);
-    },
-    nullary: function(op, into, value) {
-        print("nullary=" + op + " into=" + into + " value=" + value);
-        var x = this.emit({ op: op, value: value});
-        if (into)
-            this.map[into] = x;
-        return x;
-    },
-    unary: function(op, operand, into, value) {
-        print("unary=" + op + " operand=" + operand + " into=" + into + " value=" + value);
-        var x = this.emit({ op: op, value: value, operand: operand });
-        if (into)
-            this.map[into] = x;
-        return x;
-    },
-    binary: function(op, left, right, into, value) {
-        print("binary=" + op + " left=" + left + " right=" + right + " into=" + into + " value=" + value);
-        var x = this.emit({ op: op, value: value, left: this.map[left], right: this.map[right] });
-        if (into)
-            this.map[into] = x;
-        return x;
-    },
-    /* create a constant and assign it to v */
-    generate_constant: function(v, vv) {
-        this.nullary("generate_constant", v, vv);
-    },
-    boolean_to_jsval: function(b, v, vv) {
-        this.unary("boolean_to_jsval", b, v, vv);
-    },
-    string_to_jsval: function(s, v, vv) {
-        this.unary("string_to_jsval", s, v, vv);
-    },    
-    object_to_jsval: function(o, v, vv) {
-        this.unary("object_to_jsval", o, v, vv);
-    },
-    id_to_jsval: function(id, v, vv) {
-        this.unary("id_to_jsval", id, v, vv);
-    },
-    guard_jsdouble_is_int_and_int_fits_in_jsval: function(d, i, vv, g) {
-        this.unary("guard_jsdouble_is_int_and_fits_in_jsval", d, i, vv).guard = g;
-    },
-    int_to_jsval: function(i, v, vv) {
-        this.unary("int_to_jsval", i, v, vv);
-    },
-    new_double_in_rooted_value: function(d, v, vv) {
-        this.unary("new_double_in_rooted_value", d, v, vv);
-    },
-    guard_int_fits_in_jsval: function(i, g) {
-        this.unary("guard_int_fits_in_jsval", i).guard = g;
-    },
-    int_to_double: function(i, d, vv) {
-        this.unary("int_to_double", i, d, vv);
-    },
-    guard_uint_fits_in_jsval: function(u, g) {
-        this.unary("guard_uint_fits_in_jsval", u).guard = g;
-    },
-    uint_to_jsval: function(u, v, vv) {
-        this.unary(u, v, vv);
-    },
-    uint_to_double: function(u, d, vv) {
-        this.unary("uint_to_double", u, d, vv);
-    },
-    guard_jsval_is_int: function(v, g) {
-        this.unary("guard_jsval_is_int", v).guard = g;
-    },
-    jsval_to_int: function(v, i, vv) {
-        this.unary("jsval_to_int", v, i, vv);
-    },
-    guard_jsval_is_double: function(v, g) {
-        this.unary("guard_jsval_is_double", v).guard = g;
-    },
-    jsval_to_double: function(v, d, vv) {
-        this.unary("jsval_to_double", v, d, vv);
-    },
-    ValueToNumber: function(v, d, vv) {
-        this.unary("ValueToNumber", v, d, vv);
-    },
-    guard_jsval_is_null: function(v, g) {
-        this.unary("guard_jsval_is_null", v).guard = g;
-    },
-    ValueToECMAInt32: function(v, i, vv) {
-        this.unary("ValueToECMAInt32", v, i, vv);
-    },
-    int_to_uint: function(i, u, vv) {
-        this.unary("int_to_uint", i, u, vv);
-    },
-    ValueToECMAUint32: function(v, u, vv) {
-        this.unary("ValueToECMAUint32", v, u, vv);
-    },
-    generate_boolean_constant: function(b, c) {
-        this.nullary("boolean_constant", b, c);
-    },
-    guard_jsval_is_boolean: function(v, g) {
-        this.unary("guard_jsval_is_boolean", v).guard = g;
-    },
-    jsval_to_boolean: function(v, b, vv) {
-        this.unary("jsval_to_boolean", v, b, vv);
-    },
-    ValueToBoolean: function(v, b, vv) {
-        this.unary("ValueToBoolean", v, b, vv);
-    },
-    guard_jsval_is_primitive: function(v, g) {
-        this.unary("guard_jsval_is_primitive", v).guard = g;
-    },
-    jsval_to_object: function(v, obj, vv) {
-        this.unary("jsval_to_object", v, obj, vv);
-    },
-    guard_obj_is_null: function(obj, g) {
-        this.unary("guard_obj_is_null", obj).guard = g;
-    },
-    ValueToNonNullObject: function(v, obj, vv) {
-        this.unary("ValueToNonNullObject", v, obj, vv);
-    },
-    call_obj_default_value: function(obj, hint, v, vv) {
-        this.binary("obj_to_default_value", obj, hint, v, vv);
-    },
-    dadd: function(a, b, r, vv) {
-        this.binary("dadd", a, b, r, vv);
-    },
-    dsub: function(a, b, r, vv) {
-        this.binary("dsub", a, b, r, vv);
-    },
-    dmul: function(a, b, r, vv) {
-        this.binary("dmul", a, b, r, vv);
-    },
-    ior: function(a, b, r, vv) {
-        this.binary("ior", a, b, r, vv);
-    },
-    ixor: function(a, b, r, vv) {
-        this.binary("ixor", a, b, r, vv);
-    },
-    iand: function(a, b, r, vv) {
-        this.binary("iand", a, b, r, vv);
-    },
-    ilsh: function(a, b, r, vv) {
-        this.binary("ilsh", a, b, r, vv);
-    },
-    irsh: function(a, b, r, vv) {
-        this.binary("irsh", a, b, r, vv);
-    },
-    ursh: function(a, b, r, vv) {
-        this.binary("ursh", a, b, r, vv);
-    },
-    guard_boolean_is_true: function(b, g) {
-        this.unary("guard_boolean_is_true", b).guard = g;
-    },
-    icmp_lt: function(a, b,     r, vv) {
-        this.binary("icmp_lt", a, b, r, vv);
-    },
-    icmp_le: function(a, b, r, vv) {
-        this.binary("icmp_le", a, b, r, vv);
-    },
-    icmp_gt: function(a, b, r, vv) {
-        this.binary("icmp_gt", a, b, r, vv);
-    },
-    icmp_ge: function(a, b, r, vv) {
-        this.binary("icmp_ge", a, b, r, vv);
-    },
-    dcmp_lt: function(a, b, r, vv) {
-        this.binary("dcmp_lt", a, b, r, vv);
-    },
-    dcmp_le: function(a, b, r, vv) {
-        this.binary("dcmp_le", a, b, r, vv);
-    },
-    dcmp_gt: function(a, b, r, vv) {
-        this.binary("dcmp_gt", a, b, r, vv);
-    },
-    dcmp_ge: function(a, b, r, vv) {
-        this.binary("dcmp_ge", a, b, r, vv);
-    },
-    dcmp_lt_ifnan: function(a, b, r, vv) {
-        this.binary("dcmp_lt", a, b, r, vv).if_nan = true;
-    },
-    dcmp_le_ifnan: function(a, b, r, vv) {
-        this.binary("dcmp_le", a, b, r, vv).if_nan = true;
-    },
-    dcmp_gt_ifnan: function(a, b, r, vv) {
-        this.binary("dcmp_gt", a, b, r, vv).if_nan = true;
-    },
-    dcmp_ge_ifnan: function(a, b, r, vv) {
-        this.binary("dcmp_ge", a, b, r, vv).if_nan = true;
-    },
-    generate_int_constant: function(i, c) {
-        this.nullary("generate_int_constant", i, c);
-    },
-    jsval_to_string: function(v, s, vv) {
-        this.unary("jsval_to_string", v, s, vv);
-    },
-    CompareStrings: function(a, b, r, vv) {
-        this.binary("CompareStrings", a, b, r, vv);
-    },
-    guard_both_jsvals_are_int: function(a, b, g) {
-        this.binary("guard_both_jsvals_are_int", a, b).guard = g;
-    },
-    guard_both_jsvals_are_strings: function(a, b, g) {
-        this.binary("guard_both_jsvals_are_strings", a, b).guard = g;
-    },
-    guard_can_do_fast_inc_dec: function(v, g) {
-        this.unary("guard_can_do_fast_inc_dec", v).guard = g;
-    },
-    generate_double_constant: function(d, c) {
-        this.nullary("generate_double_constant", d, c);
-    },
-    do_fast_inc: function(a, r, vv) {
-        this.unary("do_fast_inc", a, r, vv);
-    },
-    do_fast_dec: function(a, r, vv) {
-        this.unary("do_fast_dec", a, r, vv);
-    }
-});