[JAEGER] Merge from Tracemonkey.
authorDavid Mandelin <dmandelin@mozilla.com>
Mon, 16 Aug 2010 15:31:14 -0700
changeset 53438 4ed66568e4608ad8bdd3a2ffef02d954852611dc
parent 53437 c69ffb5d8d0687f41ccdea693594e19d7705c323 (current diff)
parent 51055 7e03c169adee3f32f86b32839385351ca2b0bbbe (diff)
child 53439 3a51962c5475713e1002c793b94f7e67c89afd10
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b4pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
[JAEGER] Merge from Tracemonkey.
js/src/jsbuiltins.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jspropertycache.cpp
js/src/jsscript.cpp
js/src/jstracer.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/StubCalls.cpp
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -344,17 +344,17 @@ js_PopInterpFrame(JSContext* cx, TracerS
      * MISMATCH_EXIT.
      */
     if (fp->hasHookData())
         return JS_FALSE;
     if (cx->version != fp->getCallerVersion())
         return JS_FALSE;
     if (fp->flags & JSFRAME_CONSTRUCTING)
         return JS_FALSE;
-    if (fp->imacpc)
+    if (fp->hasIMacroPC())
         return JS_FALSE;
     if (fp->hasBlockChain())
         return JS_FALSE;
 
     fp->putActivationObjects(cx);
     
     /* Pop the frame and its memory. */
     cx->stack().popInlineFrame(cx, fp, fp->down);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1929,33 +1929,35 @@ js_GetCurrentBytecodePC(JSContext* cx)
         imacpc = cx->bailExit->imacpc;
     } else
 #endif
     {
         JS_ASSERT_NOT_ON_TRACE(cx);  /* for static analysis */
         pc = cx->regs ? cx->regs->pc : NULL;
         if (!pc)
             return NULL;
-        imacpc = cx->fp->imacpc;
+        imacpc = cx->fp->maybeIMacroPC();
     }
 
     /*
      * If we are inside GetProperty_tn or similar, return a pointer to the
      * current instruction in the script, not the CALL instruction in the
      * imacro, for the benefit of callers doing bytecode inspection.
      */
     return (*pc == JSOP_CALL && imacpc) ? imacpc : pc;
 }
 
 bool
 js_CurrentPCIsInImacro(JSContext *cx)
 {
 #ifdef JS_TRACER
     VOUCH_DOES_NOT_REQUIRE_STACK();
-    return (JS_ON_TRACE(cx) ? cx->bailExit->imacpc : cx->fp->imacpc) != NULL;
+    if (JS_ON_TRACE(cx))
+        return cx->bailExit->imacpc != NULL;
+    return cx->fp->hasIMacroPC();
 #else
     return false;
 #endif
 }
 
 void
 DSTOffsetCache::purge()
 {
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -2427,23 +2427,23 @@ class AutoCheckRequestDepth {
 
 #else
 # define CHECK_REQUEST(cx)       ((void)0)
 #endif
 
 static inline uintN
 FramePCOffset(JSContext *cx, JSStackFrame* fp)
 {
-    return uintN((fp->imacpc ? fp->imacpc : fp->pc(cx)) - fp->script->code);
+    return uintN((fp->hasIMacroPC() ? fp->getIMacroPC() : fp->pc(cx)) - fp->script->code);
 }
 
 static inline JSAtom **
 FrameAtomBase(JSContext *cx, JSStackFrame *fp)
 {
-    return fp->imacpc
+    return fp->hasIMacroPC()
            ? COMMON_ATOMS_START(&cx->runtime->atomState)
            : fp->script->atomMap.vector;
 }
 
 namespace js {
 
 class AutoGCRooter {
   public:
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -365,16 +365,17 @@ StackSpace::pushInlineFrame(JSContext *c
 
 JS_REQUIRES_STACK JS_ALWAYS_INLINE void
 StackSpace::popInlineFrame(JSContext *cx, JSStackFrame *up, JSStackFrame *down)
 {
     JS_ASSERT(isCurrentAndActive(cx));
     JS_ASSERT(cx->hasActiveSegment());
     JS_ASSERT(cx->fp == up && up->down == down);
     JS_ASSERT(up->savedPC == JSStackFrame::sInvalidPC);
+    JS_ASSERT(!up->hasIMacroPC());
 
     JSFrameRegs *regs = cx->regs;
     regs->pc = down->savedPC;
 #ifdef DEBUG
     down->savedPC = JSStackFrame::sInvalidPC;
 #endif
     cx->setCurrentFrame(down);
 }
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -541,18 +541,18 @@ InvokeCommon(JSContext *cx, JSFunction *
     fp->script = script;
     fp->fun = fun;
     fp->argc = args.argc();
     fp->argv = args.argv();
     fp->rval = (flags & JSINVOKE_CONSTRUCT) ? fp->thisv : UndefinedValue();
     fp->setAnnotation(NULL);
     fp->setScopeChain(NULL);
     fp->setBlockChain(NULL);
-    fp->imacpc = NULL;
     fp->flags = flags;
+    JS_ASSERT(!fp->hasIMacroPC());
 
     /* Initialize regs. */
     JSFrameRegs &regs = frame.getRegs();
     if (script) {
         regs.pc = script->code;
         regs.sp = fp->slots() + script->nfixed;
     } else {
         regs.pc = NULL;
@@ -914,18 +914,18 @@ Execute(JSContext *cx, JSObject *chain, 
         fp->setScopeChain(innerizedChain);
 
         initialVarObj = (cx->options & JSOPTION_VAROBJFIX)
                         ? chain->getGlobal()
                         : chain;
     }
     JS_ASSERT(!initialVarObj->getOps()->defineProperty);
 
+    JS_ASSERT(!fp->hasIMacroPC());
     fp->script = script;
-    fp->imacpc = NULL;
     fp->rval.setUndefined();
     fp->setBlockChain(NULL);
 
     /* Initialize regs. */
     regs.pc = script->code;
     regs.sp = fp->base();
 
     /* Officially push |fp|. |frame|'s destructor pops. */
@@ -1482,17 +1482,17 @@ js_TraceOpcode(JSContext *cx)
                 js_FileEscapedString(tracefp, str, 0);
             }
             fputc(' ', tracefp);
         }
         fputc('\n', tracefp);
     }
 
     fprintf(tracefp, "%4u: ",
-            js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : regs->pc));
+            js_PCToLineNumber(cx, fp->script, fp->hasIMacroPC() ? fp->getIMacroPC() : regs->pc));
     js_Disassemble1(cx, fp->script, regs->pc,
                     regs->pc - fp->script->code,
                     JS_FALSE, tracefp);
     op = (JSOp) *regs->pc;
     nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc);
     if (nuses != 0) {
         for (n = -nuses; n < 0; n++) {
             char *bytes = DecompileValueGenerator(cx, n, regs->sp[n], NULL);
@@ -2264,22 +2264,22 @@ Interpret(JSContext *cx, JSStackFrame *e
      * GET_FULL_INDEX macros below. As a register we use a pointer based on
      * the atom map to turn frequently executed LOAD_ATOM into simple array
      * access. For less frequent object and regexp loads we have to recover
      * the segment from atoms pointer first.
      */
     JSAtom **atoms = script->atomMap.vector;
 
 #ifdef JS_METHODJIT
-    bool leaveOnTracePoint = (fp->flags & JSFRAME_BAILING) && !fp->imacpc;
+    bool leaveOnTracePoint = (fp->flags & JSFRAME_BAILING) && !fp->hasIMacroPC();
 #endif
 
 #define LOAD_ATOM(PCOFF, atom)                                                \
     JS_BEGIN_MACRO                                                            \
-        JS_ASSERT(fp->imacpc                                                  \
+        JS_ASSERT(fp->hasIMacroPC()                                           \
                   ? atoms == COMMON_ATOMS_START(&rt->atomState) &&            \
                     GET_INDEX(regs.pc + PCOFF) < js_common_atom_count         \
                   : (size_t)(atoms - script->atomMap.vector) <                \
                     (size_t)(script->atomMap.length -                         \
                              GET_INDEX(regs.pc + PCOFF)));                    \
         atom = atoms[GET_INDEX(regs.pc + PCOFF)];                             \
     JS_END_MACRO
 
@@ -2358,19 +2358,19 @@ Interpret(JSContext *cx, JSStackFrame *e
             goto error;                                                       \
     JS_END_MACRO
 
 #ifndef TRACE_RECORDER
 #define TRACE_RECORDER(cx) (false)
 #endif
 
 #ifdef JS_METHODJIT
-# define LEAVE_ON_TRACE_POINT()                                                \
+# define LEAVE_ON_TRACE_POINT()                                               \
     do {                                                                      \
-        if (leaveOnTracePoint && !fp->imacpc &&                               \
+        if (leaveOnTracePoint && !fp->hasIMacroPC() &&                        \
             script->nmap && script->nmap[regs.pc - script->code]) {           \
             interpReturnOK = true;                                            \
             goto stop_recording;                                              \
         }                                                                     \
     } while (0)
 #else
 # define LEAVE_ON_TRACE_POINT() /* nop */
 #endif
@@ -2396,17 +2396,17 @@ Interpret(JSContext *cx, JSStackFrame *e
         LEAVE_ON_TRACE_POINT();                                               \
         DO_OP();                                                              \
     JS_END_MACRO
 
     MUST_FLOW_THROUGH("exit");
 
 #ifdef JS_TRACER
     bool wasRecording = !!(fp->flags & JSFRAME_RECORDING);
-    bool wasImacroRun = fp->imacpc && !TRACE_RECORDER(cx);
+    bool wasImacroRun = fp->hasIMacroPC() && !TRACE_RECORDER(cx);
 #endif
 
     ++cx->interpLevel;
 
     /*
      * Optimized Get and SetVersion for proper script language versioning.
      *
      * If any native method or Class/JSObjectOps hook calls js_SetVersion
@@ -2472,17 +2472,17 @@ Interpret(JSContext *cx, JSStackFrame *e
      */
     if (wasRecording) {
         JS_ASSERT(TRACE_RECORDER(cx));
         ENABLE_INTERRUPTS();
     } else if (TRACE_RECORDER(cx)) {
         AbortRecording(cx, "attempt to reenter interpreter while recording");
     }
 
-    if (fp->imacpc)
+    if (fp->hasIMacroPC())
         atoms = COMMON_ATOMS_START(&rt->atomState);
 #endif
 
     /* State communicated between non-local jumps: */
     JSBool interpReturnOK;
     JSAtom *atomNotDefined;
 
     /*
@@ -2701,25 +2701,26 @@ BEGIN_CASE(JSOP_STOP)
     /*
      * When the inlined frame exits with an exception or an error, ok will be
      * false after the inline_return label.
      */
     ASSERT_NOT_THROWING(cx);
     CHECK_BRANCH();
 
 #ifdef JS_TRACER
-    if (fp->imacpc) {
+    if (fp->hasIMacroPC()) {
         /*
          * If we are at the end of an imacro, return to its caller in the
          * current frame.
          */
         JS_ASSERT(op == JSOP_STOP);
         JS_ASSERT((uintN)(regs.sp - fp->slots()) <= script->nslots);
-        regs.pc = fp->imacpc + js_CodeSpec[*fp->imacpc].length;
-        fp->imacpc = NULL;
+        jsbytecode *imacpc = fp->getIMacroPC();
+        regs.pc = imacpc + js_CodeSpec[*imacpc].length;
+        fp->clearIMacroPC();
 # ifdef JS_METHODJIT
         if ((wasImacroRun || wasRecording) && !TRACE_RECORDER(cx)) {
             if (script->nmap && script->nmap[regs.pc - script->code]) {
                 interpReturnOK = true;
                 goto stop_recording;
             }
             leaveOnTracePoint = true;
         }
@@ -3293,18 +3294,18 @@ BEGIN_CASE(JSOP_BINDNAME)
         if (!obj)
             goto error;
     } while (0);
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_BINDNAME)
 
 BEGIN_CASE(JSOP_IMACOP)
-    JS_ASSERT(JS_UPTRDIFF(fp->imacpc, script->code) < script->length);
-    op = JSOp(*fp->imacpc);
+    JS_ASSERT(JS_UPTRDIFF(fp->getIMacroPC(), script->code) < script->length);
+    op = JSOp(*fp->getIMacroPC());
     DO_OP();
 
 #define BITWISE_OP(OP)                                                        \
     JS_BEGIN_MACRO                                                            \
         int32_t i, j;                                                         \
         if (!ValueToECMAInt32(cx, regs.sp[-2], &i))                           \
             goto error;                                                       \
         if (!ValueToECMAInt32(cx, regs.sp[-1], &j))                           \
@@ -4100,26 +4101,26 @@ BEGIN_CASE(JSOP_GETXPROP)
                 } else if (entry->vword.isSlot()) {
                     uint32 slot = entry->vword.toSlot();
                     JS_ASSERT(slot < obj2->scope()->freeslot);
                     rval = obj2->lockedGetSlot(slot);
                 } else {
                     JS_ASSERT(entry->vword.isSprop());
                     JSScopeProperty *sprop = entry->vword.toSprop();
                     NATIVE_GET(cx, obj, obj2, sprop,
-                               fp->imacpc ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
+                               fp->hasIMacroPC() ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
                                &rval);
                 }
                 break;
             }
 
             jsid id = ATOM_TO_JSID(atom);
             if (JS_LIKELY(!aobj->getOps()->getProperty)
                 ? !js_GetPropertyHelper(cx, obj, id,
-                                        (fp->imacpc ||
+                                        (fp->hasIMacroPC() ||
                                          regs.pc[JSOP_GETPROP_LENGTH + i] == JSOP_IFEQ)
                                         ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
                                         : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
                                         &rval)
                 : !obj->getProperty(cx, id, &rval)) {
                 goto error;
             }
         } while (0);
@@ -4728,17 +4729,17 @@ BEGIN_CASE(JSOP_APPLY)
             newfp->rval.setUndefined();
             newfp->setAnnotation(NULL);
             newfp->setScopeChain(obj->getParent());
             newfp->flags = flags;
             newfp->setBlockChain(NULL);
             JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
             JS_ASSERT_IF(!vp[1].isPrimitive(), IsSaneThisObject(vp[1].toObject()));
             newfp->thisv = vp[1];
-            newfp->imacpc = NULL;
+            JS_ASSERT(!newfp->hasIMacroPC());
 
             /* Push void to initialize local variables. */
             Value *newsp = newfp->base();
             SetValueRangeToUndefined(newfp->slots(), newsp);
 
             /* Switch version if currentVersion wasn't overridden. */
             newfp->setCallerVersion((JSVersion) cx->version);
             if (JS_LIKELY(cx->version == currentVersion)) {
@@ -4993,17 +4994,17 @@ END_CASE(JSOP_INDEXBASE3)
 
 BEGIN_CASE(JSOP_RESETBASE0)
 BEGIN_CASE(JSOP_RESETBASE)
     atoms = script->atomMap.vector;
 END_CASE(JSOP_RESETBASE)
 
 BEGIN_CASE(JSOP_DOUBLE)
 {
-    JS_ASSERT(!fp->imacpc);
+    JS_ASSERT(!fp->hasIMacroPC());
     JS_ASSERT(size_t(atoms - script->atomMap.vector) <= script->atomMap.length);
     double dbl;
     LOAD_DOUBLE(0, dbl);
     PUSH_DOUBLE(dbl);
 }
 END_CASE(JSOP_DOUBLE)
 
 BEGIN_CASE(JSOP_STRING)
@@ -5151,17 +5152,17 @@ BEGIN_CASE(JSOP_LOOKUPSWITCHX)
 BEGIN_CASE(JSOP_LOOKUPSWITCH)
     off = JUMP_OFFSET_LEN;
 
   do_lookup_switch:
     /*
      * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if any atom
      * index in it would exceed 64K limit.
      */
-    JS_ASSERT(!fp->imacpc);
+    JS_ASSERT(!fp->hasIMacroPC());
     JS_ASSERT(atoms == script->atomMap.vector);
     jsbytecode *pc2 = regs.pc;
 
     Value lval = regs.sp[-1];
     regs.sp--;
 
     if (!lval.isPrimitive())
         goto end_lookup_switch;
@@ -6924,25 +6925,26 @@ END_CASE(JSOP_ARRAYPUSH)
 #if !JS_THREADED_INTERP
         } /* switch (op) */
     } /* for (;;) */
 #endif /* !JS_THREADED_INTERP */
 
   error:
     JS_ASSERT(cx->regs == &regs);
 #ifdef JS_TRACER
-    if (fp->imacpc && cx->throwing) {
+    if (fp->hasIMacroPC() && cx->throwing) {
         // Handle other exceptions as if they came from the imacro-calling pc.
-        regs.pc = fp->imacpc;
-        fp->imacpc = NULL;
+        regs.pc = fp->getIMacroPC();
+        fp->clearIMacroPC();
         atoms = script->atomMap.vector;
     }
 #endif
 
-    JS_ASSERT((size_t)((fp->imacpc ? fp->imacpc : regs.pc) - script->code) < script->length);
+    JS_ASSERT(size_t((fp->hasIMacroPC() ? fp->getIMacroPC() : regs.pc) - script->code) <
+              script->length);
 
 #ifdef JS_TRACER
     /*
      * This abort could be weakened to permit tracing through exceptions that
      * are thrown and caught within a loop, with the co-operation of the tracer.
      * For now just bail on any sign of trouble.
      */
     if (TRACE_RECORDER(cx))
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -52,29 +52,30 @@
 
 typedef struct JSFrameRegs {
     js::Value       *sp;            /* stack pointer */
     jsbytecode      *pc;            /* program counter */
 } JSFrameRegs;
 
 /* JS stack frame flags. */
 enum JSFrameFlags {
-    JSFRAME_CONSTRUCTING       =  0x01, /* frame is for a constructor invocation */
-    JSFRAME_OVERRIDE_ARGS      =  0x02, /* overridden arguments local variable */
-    JSFRAME_ASSIGNING          =  0x04, /* a complex (not simplex JOF_ASSIGNING) op
+    JSFRAME_CONSTRUCTING       =   0x01, /* frame is for a constructor invocation */
+    JSFRAME_OVERRIDE_ARGS      =   0x02, /* overridden arguments local variable */
+    JSFRAME_ASSIGNING          =   0x04, /* a complex (not simplex JOF_ASSIGNING) op
                                            is currently assigning to a property */
-    JSFRAME_DEBUGGER           =  0x08, /* frame for JS_EvaluateInStackFrame */
-    JSFRAME_EVAL               =  0x10, /* frame for obj_eval */
-    JSFRAME_FLOATING_GENERATOR =  0x20, /* frame copy stored in a generator obj */
-    JSFRAME_YIELDING           =  0x40, /* js_Interpret dispatched JSOP_YIELD */
-    JSFRAME_GENERATOR          =  0x80, /* frame belongs to generator-iterator */
-    JSFRAME_BAILING            = 0x100, /* walking out of a method JIT'd frame */
-    JSFRAME_RECORDING          = 0x200, /* recording a trace */
-    JSFRAME_BAILED_AT_RETURN   = 0x400, /* bailed at JSOP_RETURN */
-    JSFRAME_DUMMY              = 0x800, /* frame is a dummy frame */
+    JSFRAME_DEBUGGER           =   0x08, /* frame for JS_EvaluateInStackFrame */
+    JSFRAME_EVAL               =   0x10, /* frame for obj_eval */
+    JSFRAME_FLOATING_GENERATOR =   0x20, /* frame copy stored in a generator obj */
+    JSFRAME_YIELDING           =   0x40, /* js_Interpret dispatched JSOP_YIELD */
+    JSFRAME_GENERATOR          =   0x80, /* frame belongs to generator-iterator */
+    JSFRAME_BAILING            =  0x100, /* walking out of a method JIT'd frame */
+    JSFRAME_RECORDING          =  0x200, /* recording a trace */
+    JSFRAME_BAILED_AT_RETURN   =  0x400, /* bailed at JSOP_RETURN */
+    JSFRAME_DUMMY              =  0x800, /* frame is a dummy frame */
+    JSFRAME_IN_IMACRO          = 0x1000, /* frame has imacpc value available */
 	
     JSFRAME_SPECIAL            = JSFRAME_DEBUGGER | JSFRAME_EVAL
 };
 
 /*
  * JS stack frame, may be allocated on the C stack by native callers.  Always
  * allocated on cx->stackPool for calls from the interpreter to an interpreted
  * function.
@@ -82,19 +83,19 @@ enum JSFrameFlags {
  * NB: This struct is manually initialized in jsinterp.c and jsiter.c.  If you
  * add new members, update both files.
  */
 struct JSStackFrame
 {
   private:
     JSObject            *callobj;       /* lazily created Call object */
     JSObject            *argsobj;       /* lazily created arguments object */
+    jsbytecode          *imacpc;        /* null or interpreter macro call pc */
 
   public:
-    jsbytecode          *imacpc;        /* null or interpreter macro call pc */
     JSScript            *script;        /* script being interpreted */
 	
     /*
      * The value of |this| in this stack frame, or JSVAL_NULL if |this|
      * is to be computed lazily on demand.
      *
      * thisv is eagerly initialized for non-function-call frames and
      * qualified method calls, but lazily initialized in most unqualified
@@ -329,16 +330,43 @@ struct JSStackFrame
     JSVersion getCallerVersion() const {
         return callerVersion;
     }
 
     void setCallerVersion(JSVersion version) {
         callerVersion = version;
     }
 
+    /* IMacroPC accessors. */
+
+    bool hasIMacroPC() const { return flags & JSFRAME_IN_IMACRO; }
+
+    /*
+     * @pre     hasIMacroPC
+     * @return  The PC at which an imacro started executing (guaranteed non-null. The PC of the
+     *          executing imacro must be in regs.pc, so the displaced
+     *          original value is stored here.
+     */
+    jsbytecode *getIMacroPC() const {
+        JS_ASSERT(flags & JSFRAME_IN_IMACRO);
+        return imacpc;
+    }
+
+    /* @return  The imacro pc if hasIMacroPC; otherwise, NULL. */
+    jsbytecode *maybeIMacroPC() const { return hasIMacroPC() ? getIMacroPC() : NULL; }
+
+    void clearIMacroPC() { flags &= ~JSFRAME_IN_IMACRO; }
+
+    void setIMacroPC(jsbytecode *newIMacPC) {
+        JS_ASSERT(newIMacPC);
+        JS_ASSERT(!(flags & JSFRAME_IN_IMACRO));
+        imacpc = newIMacPC;
+        flags |= JSFRAME_IN_IMACRO;
+    }
+
     /* Other accessors */
 
     void putActivationObjects(JSContext *cx) {
         /*
          * The order of calls here is important as js_PutCallObject needs to
          * access argsobj.
          */
         if (hasCallObj()) {
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1167,17 +1167,16 @@ js_NewGenerator(JSContext *cx)
     gen->savedRegs.pc = cx->regs->pc;
     JS_ASSERT(cx->regs->sp == fp->slots() + fp->script->nfixed);
     gen->savedRegs.sp = slots + fp->script->nfixed;
     gen->vplen = vplen;
     gen->enumerators = NULL;
     gen->liveFrame = newfp;
 
     /* Copy generator's stack frame copy in from |cx->fp|. */
-    newfp->imacpc = NULL;
     newfp->setCallObj(fp->maybeCallObj());
     if (fp->hasCallObj()) {      /* Steal call object. */
         fp->getCallObj()->setPrivate(newfp);
         fp->setCallObj(NULL);
     }
     newfp->setArgsObj(fp->maybeArgsObj());
     if (fp->hasArgsObj()) {      /* Steal args object. */
         fp->getArgsObj()->setPrivate(newfp);
@@ -1189,16 +1188,17 @@ js_NewGenerator(JSContext *cx)
     newfp->argc = fp->argc;
     newfp->argv = vp + 2;
     newfp->rval = fp->rval;
     newfp->setAnnotation(NULL);
     newfp->setScopeChain(fp->maybeScopeChain());
     JS_ASSERT(!fp->hasBlockChain());
     newfp->setBlockChain(NULL);
     newfp->flags = fp->flags | JSFRAME_GENERATOR | JSFRAME_FLOATING_GENERATOR;
+    JS_ASSERT(!newfp->hasIMacroPC());
 
     /* Copy in arguments and slots. */
     memcpy(vp, fp->argv - 2, vplen * sizeof(Value));
     memcpy(slots, fp->slots(), fp->script->nfixed * sizeof(Value));
 
     obj->setPrivate(gen);
     return obj;
 }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2692,17 +2692,17 @@ Detecting(JSContext *cx, jsbytecode *pc)
     JSScript *script;
     jsbytecode *endpc;
     JSOp op;
     JSAtom *atom;
 
     script = cx->fp->script;
     endpc = script->code + script->length;
     for (;; pc += js_CodeSpec[op].length) {
-        JS_ASSERT_IF(!cx->fp->imacpc, script->code <= pc && pc < endpc);
+        JS_ASSERT_IF(!cx->fp->hasIMacroPC(), script->code <= pc && pc < endpc);
 
         /* General case: a branch or equality op follows the access. */
         op = js_GetOpcode(cx, script, pc);
         if (js_CodeSpec[op].format & JOF_DETECTING)
             return JS_TRUE;
 
         switch (op) {
           case JSOP_NULL:
@@ -6374,19 +6374,19 @@ js_DumpStackFrame(JSContext *cx, JSStack
         if (fp->script)
             fprintf(stderr, "file %s line %u\n", fp->script->filename, (unsigned) fp->script->lineno);
 
         if (jsbytecode *pc = i.pc()) {
             if (!fp->script) {
                 fprintf(stderr, "*** pc && !script, skipping frame\n\n");
                 continue;
             }
-            if (fp->imacpc) {
+            if (fp->hasIMacroPC()) {
                 fprintf(stderr, "  pc in imacro at %p\n  called from ", pc);
-                pc = fp->imacpc;
+                pc = fp->getIMacroPC();
             } else {
                 fprintf(stderr, "  ");
             }
             fprintf(stderr, "pc = %p\n", pc);
             fprintf(stderr, "  current op: %s\n", js_CodeName[*pc]);
         }
         Value *sp = i.sp();
         fprintf(stderr, "  slots: %p\n", (void *) fp->slots());
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -5120,17 +5120,17 @@ js_DecompileValueGenerator(JSContext *cx
     while (!i.done() && !i.fp()->script)
         ++i;
 
     if (i.done() || !i.pc() || i.fp()->script->nslots == 0)
         goto do_fallback;
 
     fp = i.fp();
     script = fp->script;
-    pc = fp->imacpc ? fp->imacpc : i.pc();
+    pc = fp->hasIMacroPC() ? fp->getIMacroPC() : i.pc();
     JS_ASSERT(pc >= script->main && pc < script->code + script->length);
 
     if (spindex != JSDVG_IGNORE_STACK) {
         jsbytecode **pcstack;
 
         /*
          * Prepare computing pcstack containing pointers to opcodes that
          * populated interpreter's stack with its current content.
@@ -5183,41 +5183,41 @@ js_DecompileValueGenerator(JSContext *cx
       release_pcstack:
         cx->free(pcstack);
         if (pcdepth < 0)
             goto do_fallback;
     }
 
     {
         jsbytecode* savepc = i.pc();
-        jsbytecode* imacpc = fp->imacpc;
-        if (imacpc) {
+        jsbytecode* savedIMacroPC = fp->maybeIMacroPC();
+        if (savedIMacroPC) {
             if (fp == cx->fp)
-                cx->regs->pc = imacpc;
+                cx->regs->pc = savedIMacroPC;
             else
-                fp->savedPC = imacpc;
-            fp->imacpc = NULL;
+                fp->savedPC = savedIMacroPC;
+            fp->clearIMacroPC();
         }
 
         /*
          * FIXME: bug 489843. Stack reconstruction may have returned a pc
          * value *inside* an imacro; this would confuse the decompiler.
          */
         char *name;
-        if (imacpc && size_t(pc - script->code) >= script->length)
+        if (savedIMacroPC && size_t(pc - script->code) >= script->length)
             name = FAILED_EXPRESSION_DECOMPILER;
         else
             name = DecompileExpression(cx, script, fp->fun, pc);
 
-        if (imacpc) {
+        if (savedIMacroPC) {
             if (fp == cx->fp)
-                cx->regs->pc = imacpc;
+                cx->regs->pc = savedIMacroPC;
             else
                 fp->savedPC = savepc;
-            fp->imacpc = imacpc;
+            fp->setIMacroPC(savedIMacroPC);
         }
 
         if (name != FAILED_EXPRESSION_DECOMPILER)
             return name;
     }
 
   do_fallback:
     if (!fallback) {
@@ -5493,18 +5493,18 @@ ReconstructImacroPCStack(JSContext *cx, 
                          jsbytecode *imacstart, jsbytecode *target,
                          jsbytecode **pcstack)
 {
     /*
      * Begin with a recursive call back to ReconstructPCStack to pick up
      * the state-of-the-world at the *start* of the imacro.
      */
     JSStackFrame *fp = js_GetScriptedCaller(cx, NULL);
-    JS_ASSERT(fp->imacpc);
-    intN pcdepth = ReconstructPCStack(cx, script, fp->imacpc, pcstack);
+    JS_ASSERT(fp->hasIMacroPC());
+    intN pcdepth = ReconstructPCStack(cx, script, fp->getIMacroPC(), pcstack);
     if (pcdepth < 0)
         return pcdepth;
     return SimulateImacroCFG(cx, script, pcdepth, imacstart, target, pcstack);
 }
 
 extern jsbytecode* js_GetImacroStart(jsbytecode* pc);
 #endif
 
--- a/js/src/jspropertycache.cpp
+++ b/js/src/jspropertycache.cpp
@@ -329,17 +329,17 @@ GetAtomFromBytecode(JSContext *cx, jsbyt
 JS_REQUIRES_STACK JSAtom *
 PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp,
                         PropertyCacheEntry *entry)
 {
     JSObject *obj, *pobj, *tmp;
     uint32 vcap;
 
     JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
-    JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
+    JS_ASSERT(uintN((cx->fp->hasIMacroPC() ? cx->fp->getIMacroPC() : pc) - cx->fp->script->code)
               < cx->fp->script->length);
 
     JSOp op = js_GetOpcode(cx, cx->fp->script, pc);
     const JSCodeSpec &cs = js_CodeSpec[op];
 
     obj = *objp;
     vcap = entry->vcap;
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1396,17 +1396,17 @@ js_GetSrcNoteCached(JSContext *cx, JSScr
     }
 
     return result;
 }
 
 uintN
 js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp)
 {
-    return js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : fp->pc(cx));
+    return js_PCToLineNumber(cx, fp->script, fp->hasIMacroPC() ? fp->getIMacroPC() : fp->pc(cx));
 }
 
 uintN
 js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
     JSOp op;
     JSFunction *fun;
     uintN lineno;
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -4245,17 +4245,17 @@ TraceRecorder::snapshot(ExitType exitTyp
      * Check if we already have a matching side exit; if so we can return that
      * side exit instead of creating a new one.
      */
     VMSideExit** exits = tree->sideExits.data();
     unsigned nexits = tree->sideExits.length();
     if (exitType == LOOP_EXIT) {
         for (unsigned n = 0; n < nexits; ++n) {
             VMSideExit* e = exits[n];
-            if (e->pc == pc && e->imacpc == fp->imacpc &&
+            if (e->pc == pc && (e->imacpc == fp->maybeIMacroPC()) &&
                 ngslots == e->numGlobalSlots &&
                 !memcmp(exits[n]->fullTypeMap(), typemap, typemap_size)) {
                 AUDIT(mergedLoopExits);
 #if defined JS_JIT_SPEW
                 TreevisLogExit(cx, e);
 #endif
                 return e;
             }
@@ -4274,17 +4274,17 @@ TraceRecorder::snapshot(ExitType exitTyp
     exit->numStackSlotsBelowCurrentFrame = cx->fp->argv ?
                                            nativeStackOffset(&cx->fp->argv[-2]) / sizeof(double) :
                                            0;
     exit->exitType = exitType;
     exit->block = fp->maybeBlockChain();
     if (fp->hasBlockChain())
         tree->gcthings.addUnique(ObjectValue(*fp->getBlockChain()));
     exit->pc = pc;
-    exit->imacpc = fp->imacpc;
+    exit->imacpc = fp->maybeIMacroPC();
     exit->sp_adj = (stackSlots * sizeof(double)) - tree->nativeStackBase;
     exit->rp_adj = exit->calldepth * sizeof(FrameInfo*);
     exit->nativeCalleeWord = 0;
     exit->lookupFlags = js_InferFlags(cx, 0);
     memcpy(exit->fullTypeMap(), typemap, typemap_size);
 
 #if defined JS_JIT_SPEW
     TreevisLogExit(cx, exit);
@@ -4864,17 +4864,17 @@ TraceRecorder::closeLoop(SlotMap& slotMa
 {
     /*
      * We should have arrived back at the loop header, and hence we don't want
      * to be in an imacro here and the opcode should be either JSOP_TRACE or, in
      * case this loop was blacklisted in the meantime, JSOP_NOP.
      */
     JS_ASSERT((*cx->regs->pc == JSOP_TRACE || *cx->regs->pc == JSOP_NOP ||
                *cx->regs->pc == JSOP_RETURN || *cx->regs->pc == JSOP_STOP) &&
-              !cx->fp->imacpc);
+              !cx->fp->hasIMacroPC());
 
     if (callDepth != 0) {
         debug_only_print0(LC_TMTracer,
                           "Blacklisted: stack depth mismatch, possible recursion.\n");
         Blacklist((jsbytecode*)tree->ip);
         trashSelf = true;
         return ARECORD_STOP;
     }
@@ -5479,17 +5479,17 @@ TraceRecorder::checkTraceEnd(jsbytecode 
     if (IsLoopEdge(pc, (jsbytecode*)tree->ip)) {
         /*
          * If we compile a loop, the trace should have a zero stack balance at
          * the loop edge. Currently we are parked on a comparison op or
          * IFNE/IFEQ, so advance pc to the loop header and adjust the stack
          * pointer and pretend we have reached the loop header.
          */
         if (pendingLoop) {
-            JS_ASSERT(!cx->fp->imacpc && (pc == cx->regs->pc || pc == cx->regs->pc + 1));
+            JS_ASSERT(!cx->fp->hasIMacroPC() && (pc == cx->regs->pc || pc == cx->regs->pc + 1));
             JSFrameRegs orig = *cx->regs;
 
             cx->regs->pc = (jsbytecode*)tree->ip;
             cx->regs->sp = cx->fp->base() + tree->spOffsetAtEntry;
 
             JSContext* localcx = cx;
             AbortableRecordingStatus ars = closeLoop();
             *localcx->regs = orig;
@@ -5598,17 +5598,17 @@ bool JS_REQUIRES_STACK
 TraceRecorder::startRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* f,
                              unsigned stackSlots, unsigned ngslots,
                              JSValueType* typeMap, VMSideExit* expectedInnerExit,
                              jsbytecode* outer, uint32 outerArgc, RecordReason recordReason,
                              bool speculate)
 {
     TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
     JS_ASSERT(!tm->needFlush);
-    JS_ASSERT_IF(cx->fp->imacpc, f->root != f);
+    JS_ASSERT_IF(cx->fp->hasIMacroPC(), f->root != f);
 
     tm->recorder = new TraceRecorder(cx, anchor, f, stackSlots, ngslots, typeMap,
                                      expectedInnerExit, outer, outerArgc, recordReason,
                                      speculate);
 
     if (!tm->recorder || tm->outOfMemory() || OverfullJITCache(tm)) {
         ResetJIT(cx, FR_OOM);
         return false;
@@ -5667,17 +5667,18 @@ SynthesizeFrame(JSContext* cx, const Fra
     JSScript* newscript = fun->u.i.script;
     Value* sp = fp->slots() + fi.spdist;
     uintN argc = fi.get_argc();
     Value* vp = sp - (2 + argc);
 
     /* Fixup |fp| using |fi|. */
     cx->regs->sp = sp;
     cx->regs->pc = fi.pc;
-    fp->imacpc = fi.imacpc;
+    if (fi.imacpc)
+        fp->setIMacroPC(fi.imacpc);
     fp->setBlockChain(fi.block);
 
     /*
      * Get pointer to new frame/slots, without changing global state.
      * Initialize missing args if there are any. (Copied from js_Interpret.)
      *
      * StackSpace::getInlineFrame calls js_ReportOutOfScriptQuota if there is
      * no space (which will try to deep bail, which is bad), however we already
@@ -5710,17 +5711,17 @@ SynthesizeFrame(JSContext* cx, const Fra
     newfp->argv[-1].setMagic(JS_THIS_POISON);
 #endif
     newfp->rval = UndefinedValue();
     newfp->setAnnotation(NULL);
     newfp->setScopeChain(NULL); // will be updated in FlushNativeStackFrame
     newfp->flags = fi.is_constructing() ? JSFRAME_CONSTRUCTING : 0;
     newfp->setBlockChain(NULL);
     newfp->thisv.setNull(); // will be updated in FlushNativeStackFrame
-    newfp->imacpc = NULL;
+    JS_ASSERT(!newfp->hasIMacroPC());
 
     /*
      * Note that fp->script is still the caller's script; set the callee
      * inline frame's idea of caller version from its version.
      */
     newfp->setCallerVersion((JSVersion) fp->script->version);
 
     /* Push inline frame. (Copied from js_Interpret.) */
@@ -5768,29 +5769,29 @@ SynthesizeSlowNativeFrame(TracerState& s
 
 #ifdef DEBUG
     JSObject *callee = &state.nativeVp[0].toObject();
     JSFunction *fun = GET_FUNCTION_PRIVATE(cx, callee);
     JS_ASSERT(!fun->isInterpreted() && !fun->isFastNative());
     JS_ASSERT(fun->u.n.extra == 0);
 #endif
 
-    fp->imacpc = NULL;
     fp->setCallObj(NULL);
     fp->setArgsObj(NULL);
     fp->script = NULL;
     fp->thisv = state.nativeVp[1];
     fp->argc = state.nativeVpLen - 2;
     fp->argv = state.nativeVp + 2;
     fp->fun = GET_FUNCTION_PRIVATE(cx, fp->callee());
     fp->rval = UndefinedValue();
     fp->setAnnotation(NULL);
     fp->setScopeChain(cx->fp->getScopeChain());
     fp->setBlockChain(NULL);
     fp->flags = exit->constructing() ? JSFRAME_CONSTRUCTING : 0;
+    JS_ASSERT(!fp->hasIMacroPC());
 
     state.bailedSlowNativeRegs = *cx->regs;
 
     cx->stack().pushSynthesizedSlowNativeFrame(cx, seg, fp, state.bailedSlowNativeRegs);
 
     state.bailedSlowNativeRegs.pc = NULL;
     state.bailedSlowNativeRegs.sp = fp->slots();
 }
@@ -6897,17 +6898,17 @@ LeaveTree(TraceMonitor *tm, TracerState&
                 regs->pc += JSOP_SETELEM_LENGTH;
                 op = JSOP_POP;
             }
 
             const JSCodeSpec& cs = js_CodeSpec[op];
             regs->sp -= (cs.format & JOF_INVOKE) ? GET_ARGC(regs->pc) + 2 : cs.nuses;
             regs->sp += cs.ndefs;
             regs->pc += cs.length;
-            JS_ASSERT_IF(!cx->fp->imacpc,
+            JS_ASSERT_IF(!cx->fp->hasIMacroPC(),
                          cx->fp->slots() + cx->fp->script->nfixed +
                          js_ReconstructStackDepth(cx, cx->fp->script, regs->pc) ==
                          regs->sp);
 
             /*
              * If there's a tree call around the point that we deep exited at,
              * then state.sp and state.rp were restored to their original
              * values before the tree call and sp might be less than deepBailSp,
@@ -7015,35 +7016,38 @@ LeaveTree(TraceMonitor *tm, TracerState&
 
     fp->setBlockChain(innermost->block);
 
     /*
      * If we are not exiting from an inlined frame, the state->sp is spbase.
      * Otherwise spbase is whatever slots frames around us consume.
      */
     cx->regs->pc = innermost->pc;
-    fp->imacpc = innermost->imacpc;
+    if (innermost->imacpc)
+        fp->setIMacroPC(innermost->imacpc);
+    else
+        fp->clearIMacroPC();
     cx->regs->sp = fp->base() + (innermost->sp_adj / sizeof(double)) - calldepth_slots;
-    JS_ASSERT_IF(!fp->imacpc,
+    JS_ASSERT_IF(!fp->hasIMacroPC(),
                  fp->slots() + fp->script->nfixed +
                  js_ReconstructStackDepth(cx, fp->script, cx->regs->pc) == cx->regs->sp);
 
 #ifdef EXECUTE_TREE_TIMER
     uint64 cycles = rdtsc() - state.startTime;
 #elif defined(JS_JIT_SPEW)
     uint64 cycles = 0;
 #endif
 
     debug_only_printf(LC_TMTracer,
                       "leaving trace at %s:%u@%u, op=%s, lr=%p, exitType=%s, sp=%lld, "
                       "calldepth=%d, cycles=%llu\n",
                       fp->script->filename,
                       js_FramePCToLineNumber(cx, fp),
                       FramePCOffset(cx, fp),
-                      js_CodeName[fp->imacpc ? *fp->imacpc : *cx->regs->pc],
+                      js_CodeName[fp->hasIMacroPC() ? *fp->getIMacroPC() : *cx->regs->pc],
                       (void*)lr,
                       getExitName(lr->exitType),
                       (long long int)(cx->regs->sp - fp->base()),
                       calldepth,
                       (unsigned long long int)cycles);
 
     /*
      * If this trace is part of a tree, later branches might have added
@@ -7394,32 +7398,32 @@ TraceRecorder::monitorRecording(JSOp op)
                                         snapshot(BRANCH_EXIT));
         set(pendingUnboxSlot, unboxed_ins);
         pendingUnboxSlot = 0;
     }
 
     debug_only_stmt(
         if (LogController.lcbits & LC_TMRecorder) {
             js_Disassemble1(cx, cx->fp->script, cx->regs->pc,
-                            cx->fp->imacpc
+                            cx->fp->hasIMacroPC()
                                 ? 0 : cx->regs->pc - cx->fp->script->code,
-                            !cx->fp->imacpc, stdout);
+                            !cx->fp->hasIMacroPC(), 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.
      */
 
     AbortableRecordingStatus status;
 #ifdef DEBUG
-    bool wasInImacro = (cx->fp->imacpc != NULL);
+    bool wasInImacro = (cx->fp->hasIMacroPC());
 #endif
     switch (op) {
       default:
           AbortRecording(cx, "unsupported opcode");
           status = ARECORD_ERROR;
           break;
 # define OPDEF(x,val,name,token,length,nuses,ndefs,prec,format)               \
       case x:                                                                 \
@@ -7428,17 +7432,17 @@ TraceRecorder::monitorRecording(JSOp op)
 # include "jsopcode.tbl"
 # undef OPDEF
     }
 
     /* N.B. |this| may have been deleted. */
 
     if (!JSOP_IS_IMACOP(op)) {
         JS_ASSERT(status != ARECORD_IMACRO);
-        JS_ASSERT_IF(!wasInImacro, localcx->fp->imacpc == NULL);
+        JS_ASSERT_IF(!wasInImacro, !localcx->fp->hasIMacroPC());
     }
 
     if (localtm.recorder) {
         JS_ASSERT(status != ARECORD_ABORTED);
         JS_ASSERT(localtm.recorder == this);
 
         /* |this| recorder completed, but a new one started; keep recording. */
         if (status == ARECORD_COMPLETED)
@@ -8071,17 +8075,17 @@ TraceRecorder::stackval(int n) const
 {
     return cx->regs->sp[n];
 }
 
 JS_REQUIRES_STACK void
 TraceRecorder::updateAtoms()
 {
     atoms = FrameAtomBase(cx, cx->fp);
-    consts = cx->fp->imacpc || cx->fp->script->constOffset == 0
+    consts = cx->fp->hasIMacroPC() || cx->fp->script->constOffset == 0
            ? 0 
            : cx->fp->script->consts()->vector;
 }
 
 JS_REQUIRES_STACK void
 TraceRecorder::updateAtoms(JSScript *script)
 {
     atoms = script->atomMap.vector;
@@ -8637,32 +8641,32 @@ TraceRecorder::stringify(const Value& v)
     guard(false, lir->insEqP_0(v_ins), OOM_EXIT);
     return v_ins;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::canCallImacro() const
 {
     /* We cannot nest imacros. */
-    return !cx->fp->imacpc;
+    return !cx->fp->hasIMacroPC();
 }
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::callImacro(jsbytecode* imacro)
 {
     return canCallImacro() ? callImacroInfallibly(imacro) : RECORD_STOP;
 }
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::callImacroInfallibly(jsbytecode* imacro)
 {
     JSStackFrame* fp = cx->fp;
-    JS_ASSERT(!fp->imacpc);
+    JS_ASSERT(!fp->hasIMacroPC());
     JSFrameRegs* regs = cx->regs;
-    fp->imacpc = regs->pc;
+    fp->setIMacroPC(regs->pc);
     regs->pc = imacro;
     updateAtoms();
     return RECORD_IMACRO;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::ifop()
 {
@@ -10344,17 +10348,17 @@ IsTraceableRecursion(JSContext *cx)
     if (!down)
         return false;
     if (down->script != fp->script)
         return false;
     if (down->argc != fp->argc)
         return false;
     if (fp->argc != fp->fun->nargs)
         return false;
-    if (fp->imacpc || down->imacpc)
+    if (fp->hasIMacroPC() || down->hasIMacroPC())
         return false;
     if ((fp->flags & JSFRAME_CONSTRUCTING) || (down->flags & JSFRAME_CONSTRUCTING))
         return false;
     if (fp->hasBlockChain() || down->hasBlockChain())
         return false;
     if (*fp->script->code != JSOP_TRACE)
         return false;
     return true;
@@ -11306,18 +11310,18 @@ TraceRecorder::callSpecializedNative(JSN
             } else if (argtype == 'p') {
                 CHECK_STATUS(getClassPrototype(&fval.toObject(), *argp));
             } else if (argtype == 'R') {
                 *argp = INS_CONSTPTR(cx->runtime);
             } else if (argtype == 'P') {
                 // FIXME: Set pc to imacpc when recording JSOP_CALL inside the
                 //        JSOP_GETELEM imacro (bug 476559).
                 if ((*pc == JSOP_CALL) &&
-                    fp->imacpc && *fp->imacpc == JSOP_GETELEM)
-                    *argp = INS_CONSTPTR(fp->imacpc);
+                    fp->hasIMacroPC() && *fp->getIMacroPC() == JSOP_GETELEM)
+                    *argp = INS_CONSTPTR(fp->getIMacroPC());
                 else
                     *argp = INS_CONSTPTR(pc);
             } else if (argtype == 'D') { /* this, as a number */
                 if (!tval.isNumber())
                     goto next_specialization;
                 *argp = this_ins;
             } else {
                 JS_NOT_REACHED("unknown prefix arg type");
@@ -13394,17 +13398,17 @@ TraceRecorder::interpretedFunctionCall(V
 
     JS_ASSERT(argc < FrameInfo::CONSTRUCTING_FLAG);
 
     tree->gcthings.addUnique(fval);
     fi->block = fp->maybeBlockChain();
     if (fp->hasBlockChain())
         tree->gcthings.addUnique(ObjectValue(*fp->getBlockChain()));
     fi->pc = cx->regs->pc;
-    fi->imacpc = fp->imacpc;
+    fi->imacpc = fp->maybeIMacroPC();
     fi->spdist = cx->regs->sp - fp->slots();
     fi->set_argc(uint16(argc), constructing);
     fi->callerHeight = stackSlots - (2 + argc);
     fi->callerArgc = fp->argc;
 
     if (callDepth >= tree->maxCallDepth)
         tree->maxCallDepth = callDepth + 1;
 
@@ -13426,17 +13430,17 @@ TraceRecorder::interpretedFunctionCall(V
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_CALL()
 {
     uintN argc = GET_ARGC(cx->regs->pc);
     cx->assertValidStackDepth(argc + 2);
     return InjectStatus(functionCall(argc,
-                                     (cx->fp->imacpc && *cx->fp->imacpc == JSOP_APPLY)
+                                     (cx->fp->hasIMacroPC() && *cx->fp->getIMacroPC() == JSOP_APPLY)
                                         ? JSOP_APPLY
                                         : JSOP_CALL));
 }
 
 static jsbytecode* apply_imacro_table[] = {
     apply_imacros.apply0,
     apply_imacros.apply1,
     apply_imacros.apply2,
@@ -13467,17 +13471,17 @@ TraceRecorder::record_JSOP_APPLY()
     uintN argc = GET_ARGC(pc);
     cx->assertValidStackDepth(argc + 2);
 
     Value* vp = cx->regs->sp - (argc + 2);
     jsuint length = 0;
     JSObject* aobj = NULL;
     LIns* aobj_ins = NULL;
 
-    JS_ASSERT(!cx->fp->imacpc);
+    JS_ASSERT(!cx->fp->hasIMacroPC());
 
     if (!IsFunctionObject(vp[0]))
         return record_JSOP_CALL();
     RETURN_IF_XML_A(vp[0]);
 
     JSObject* obj = &vp[0].toObject();
     JSFunction* fun = GET_FUNCTION_PRIVATE(cx, obj);
     if (FUN_INTERPRETED(fun))
@@ -13855,17 +13859,17 @@ TraceRecorder::propTail(JSObject* obj, L
      * as a property value other than a callee. Note that shapes cover method
      * value as well as other property attributes and order, so this condition
      * is trace-invariant.
      *
      * We do not impose the method read barrier if in an imacro, assuming any
      * property gets it does (e.g., for 'toString' from JSOP_NEW) will not be
      * leaked to the calling script.
      */
-    if (isMethod && !cx->fp->imacpc) {
+    if (isMethod && !cx->fp->hasIMacroPC()) {
         enterDeepBailCall();
         LIns* args[] = { v_ins, INS_CONSTSPROP(sprop), obj_ins, cx_ins };
         v_ins = lir->insCall(&MethodReadBarrier_ci, args);
         leaveDeepBailCall();
     }
 
     if (slotp) {
         *slotp = slot;
@@ -14331,17 +14335,17 @@ JS_REQUIRES_STACK AbortableRecordingStat
 TraceRecorder::record_JSOP_LOCALDEC()
 {
     return InjectStatus(inc(varval(GET_SLOTNO(cx->regs->pc)), -1, false));
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_IMACOP()
 {
-    JS_ASSERT(cx->fp->imacpc);
+    JS_ASSERT(cx->fp->hasIMacroPC());
     return ARECORD_CONTINUE;
 }
 
 static JSBool FASTCALL
 ObjectToIterator(JSContext* cx, JSObject *obj, int32 flags, JSObject **objp)
 {
     AutoValueRooter tvr(cx, ObjectValue(*obj));
     bool ok = js_ValueToIterator(cx, flags, tvr.addr());
@@ -15709,17 +15713,17 @@ TraceRecorder::record_JSOP_STOP()
     if (callDepth == 0 && IsTraceableRecursion(cx) &&
         tree->recursion != Recursion_Disallowed &&
         tree->script == cx->fp->script) {
         return InjectStatus(upRecursion());
     }
 #endif
     JSStackFrame *fp = cx->fp;
 
-    if (fp->imacpc) {
+    if (fp->hasIMacroPC()) {
         /*
          * End of imacro, so return true to the interpreter immediately. The
          * interpreter's JSOP_STOP case will return from the imacro, back to
          * the pc after the calling op, still in the same JSStackFrame.
          */
         updateAtoms(fp->script);
         return ARECORD_CONTINUE;
     }
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -220,17 +220,17 @@ CreateFrame(VMFrame &f, uint32 flags, ui
     newfp->argv = vp + 2;
     newfp->rval.setUndefined();
     newfp->setAnnotation(NULL);
     newfp->setScopeChain(funobj->getParent());
     newfp->flags = flags;
     newfp->setBlockChain(NULL);
     JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
     newfp->thisv = vp[1];
-    newfp->imacpc = NULL;
+    JS_ASSERT(!fp->hasIMacroPC());
 
     /* Push void to initialize local variables. */
     Value *newslots = newfp->slots();
     Value *newsp = newslots + fun->u.i.nvars;
     for (Value *v = newslots; v != newsp; ++v)
         v->setUndefined();
 
     /* Scope with a call object parented by callee's parent. */
@@ -509,18 +509,18 @@ CreateLightFrame(VMFrame &f, uint32 flag
     newfp->argv = vp + 2;
     newfp->rval.setUndefined();
     newfp->setAnnotation(NULL);
     newfp->setScopeChain(funobj->getParent());
     newfp->flags = flags;
     newfp->setBlockChain(NULL);
     JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
     newfp->thisv = vp[1];
-    newfp->imacpc = NULL;
     newfp->setHookData(NULL);
+    JS_ASSERT(!fp->hasIMacroPC());
 
 #if 0
     /* :TODO: Switch version if currentVersion wasn't overridden. */
     newfp->setCallerVersion((JSVersion)cx->version);
 #endif
 
 #ifdef DEBUG
     newfp->savedPC = JSStackFrame::sInvalidPC;
@@ -675,23 +675,23 @@ SwallowErrors(VMFrame &f, JSStackFrame *
     JSContext *cx = f.cx;
 
     /* Remove the bottom frame. */
     bool ok = false;
     for (;;) {
         JSStackFrame *fp = cx->fp;
 
         /* Look for an imacro with hard-coded exception handlers. */
-        if (fp->imacpc && cx->throwing) {
-            cx->regs->pc = fp->imacpc;
-            fp->imacpc = NULL;
+        if (fp->hasIMacroPC() && cx->throwing) {
+            cx->regs->pc = fp->getIMacroPC();
+            fp->clearIMacroPC();
             if (ok)
                 break;
         }
-        JS_ASSERT(!fp->imacpc);
+        JS_ASSERT(!fp->hasIMacroPC());
 
         /* If there's an exception and a handler, set the pc and leave. */
         jsbytecode *pc = FindExceptionHandler(cx);
         if (pc) {
             cx->regs->pc = pc;
             ok = true;
             break;
         }
@@ -711,34 +711,34 @@ SwallowErrors(VMFrame &f, JSStackFrame *
     JS_ASSERT_IF(!ok, cx->fp == stopFp);
     return ok;
 }
 
 static inline bool
 AtSafePoint(JSContext *cx)
 {
     JSStackFrame *fp = cx->fp;
-    if (fp->imacpc)
+    if (fp->hasIMacroPC())
         return false;
 
     JSScript *script = fp->script;
     if (!script->nmap)
         return false;
 
     JS_ASSERT(cx->regs->pc >= script->code && cx->regs->pc < script->code + script->length);
     return !!script->nmap[cx->regs->pc - script->code];
 }
 
 static inline JSBool
 PartialInterpret(VMFrame &f)
 {
     JSContext *cx = f.cx;
     JSStackFrame *fp = cx->fp;
 
-    JS_ASSERT(fp->imacpc || !fp->script->nmap ||
+    JS_ASSERT(fp->hasIMacroPC() || !fp->script->nmap ||
               !fp->script->nmap[cx->regs->pc - fp->script->code]);
 
     JSBool ok = JS_TRUE;
     fp->flags |= JSFRAME_BAILING;
     ok = Interpret(cx, fp);
     fp->flags &= ~JSFRAME_BAILING;
 
     f.fp = cx->fp;
@@ -781,17 +781,17 @@ RemoveExcessFrames(VMFrame &f, JSStackFr
             if (!PartialInterpret(f)) {
                 if (!SwallowErrors(f, entryFrame))
                     return false;
             } else {
                 /*
                  * Partial interpret could have dropped us anywhere. Deduce the
                  * edge case: at a RETURN, needing to pop a frame.
                  */
-                if (!cx->fp->imacpc && FrameIsFinished(cx)) {
+                if (!cx->fp->hasIMacroPC() && FrameIsFinished(cx)) {
                     JSOp op = JSOp(*cx->regs->pc);
                     if (op == JSOP_RETURN && !(cx->fp->flags & JSFRAME_BAILED_AT_RETURN))
                         fp->rval = f.regs.sp[-1];
                     InlineReturn(f, JS_TRUE);
                     AdvanceReturnPC(cx);
                 }
             }
         }
@@ -867,17 +867,17 @@ RunTracer(VMFrame &f)
 
     switch (tpa) {
       case TPA_Nothing:
         return NULL;
 
       case TPA_Error:
         if (!SwallowErrors(f, entryFrame))
             THROWV(NULL);
-        JS_ASSERT(!cx->fp->imacpc);
+        JS_ASSERT(!cx->fp->hasIMacroPC());
         break;
 
       case TPA_RanStuff:
       case TPA_Recorded:
         break;
     }
 
     /*
@@ -903,17 +903,17 @@ RunTracer(VMFrame &f)
 
   restart:
     /* Step 1. Initial removal of excess frames. */
     if (!RemoveExcessFrames(f, entryFrame))
         THROWV(NULL);
 
     /* Step 2. If there's an imacro on the entry frame, remove it. */
     entryFrame->flags &= ~JSFRAME_RECORDING;
-    while (entryFrame->imacpc) {
+    while (entryFrame->hasIMacroPC()) {
         if (!PartialInterpret(f)) {
             if (!SwallowErrors(f, entryFrame))
                 THROWV(NULL);
         }
 
         /* After partial interpreting, we could have more frames again. */
         goto restart;
     }
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1984,26 +1984,26 @@ InlineGetProp(VMFrame &f)
             } else if (entry->vword.isSlot()) {
                 uint32 slot = entry->vword.toSlot();
                 JS_ASSERT(slot < obj2->scope()->freeslot);
                 rval = obj2->lockedGetSlot(slot);
             } else {
                 JS_ASSERT(entry->vword.isSprop());
                 JSScopeProperty *sprop = entry->vword.toSprop();
                 NATIVE_GET(cx, obj, obj2, sprop,
-                        f.fp->imacpc ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
+                        f.fp->hasIMacroPC() ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
                         &rval, return false);
             }
             break;
         }
 
         jsid id = ATOM_TO_JSID(atom);
         if (JS_LIKELY(!aobj->getOps()->getProperty)
                 ? !js_GetPropertyHelper(cx, obj, id,
-                    f.fp->imacpc
+                    f.fp->hasIMacroPC()
                     ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
                     : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
                     &rval)
                 : !obj->getProperty(cx, id, &rval)) {
             return false;
         }
     } while(0);