[INFER] Only allow expansion of all inline frames in a compartment, bug 675251.
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 01 Aug 2011 09:09:39 -0700
changeset 77398 d763fda00eb9a264e53e67f0188581757b81f0e8
parent 77397 e5b57c9ebbe94042069d978567a2ba217eee0670
child 77399 674160662e80b4537796dec10668fb3117fd41db
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs675251
milestone8.0a1
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
[INFER] Only allow expansion of all inline frames in a compartment, bug 675251.
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsinfer.cpp
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jswrapper.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/Retcon.cpp
js/src/methodjit/StubCalls.cpp
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1263,17 +1263,17 @@ TriggerAllOperationCallbacks(JSRuntime *
 }
 
 } /* namespace js */
 
 StackFrame *
 js_GetScriptedCaller(JSContext *cx, StackFrame *fp)
 {
     if (!fp)
-        fp = js_GetTopStackFrame(cx, FRAME_EXPAND_TOP);
+        fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
     while (fp && fp->isDummyFrame())
         fp = fp->prev();
     JS_ASSERT_IF(fp, fp->isScriptFrame());
     return fp;
 }
 
 jsbytecode*
 js_GetCurrentBytecodePC(JSContext* cx)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -2309,39 +2309,42 @@ namespace js {
 extern JS_FORCES_STACK JS_FRIEND_API(void)
 LeaveTrace(JSContext *cx);
 
 extern bool
 CanLeaveTrace(JSContext *cx);
 
 #ifdef JS_METHODJIT
 namespace mjit {
-    void ExpandInlineFrames(JSCompartment *compartment, bool all);
+    void ExpandInlineFrames(JSCompartment *compartment);
 }
 #endif
 
 } /* namespace js */
 
+/* How much expansion of inlined frames to do when inspecting the stack. */
+enum FrameExpandKind {
+    FRAME_EXPAND_NONE = 0,
+    FRAME_EXPAND_ALL = 1
+};
+
 /*
  * Get the current frame, first lazily instantiating stack frames if needed.
  * (Do not access cx->fp() directly except in JS_REQUIRES_STACK code.)
  *
  * LeaveTrace is defined in jstracer.cpp if JS_TRACER is defined.
- *
- * If the stack contains frames inlined by the method JIT, kind specifies
- * which ones to expand.
  */
 static JS_FORCES_STACK JS_INLINE js::StackFrame *
-js_GetTopStackFrame(JSContext *cx, js::FrameExpandKind expand)
+js_GetTopStackFrame(JSContext *cx, FrameExpandKind expand)
 {
     js::LeaveTrace(cx);
 
 #ifdef JS_METHODJIT
-    if (expand != js::FRAME_EXPAND_NONE)
-        js::mjit::ExpandInlineFrames(cx->compartment, expand == js::FRAME_EXPAND_ALL);
+    if (expand)
+        js::mjit::ExpandInlineFrames(cx->compartment);
 #endif
 
     return cx->maybefp();
 }
 
 static JS_INLINE JSBool
 js_IsPropertyCacheDisabled(JSContext *cx)
 {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1547,17 +1547,17 @@ types::MarkArgumentsCreated(JSContext *c
                         OBJECT_FLAG_CREATED_ARGUMENTS | OBJECT_FLAG_UNINLINEABLE);
 
     if (!script->usedLazyArgs)
         return;
 
     AutoEnterTypeInference enter(cx);
 
 #ifdef JS_METHODJIT
-    mjit::ExpandInlineFrames(cx->compartment, true);
+    mjit::ExpandInlineFrames(cx->compartment);
 #endif
 
     if (!script->ensureRanBytecode(cx))
         return;
 
     ScriptAnalysis *analysis = script->analysis();
 
     for (FrameRegsIter iter(cx); !iter.done(); ++iter) {
@@ -1950,17 +1950,17 @@ TypeCompartment::processPendingRecompile
     /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
     Vector<JSScript*> *pending = pendingRecompiles;
     pendingRecompiles = NULL;
 
     JS_ASSERT(!pending->empty());
 
 #ifdef JS_METHODJIT
 
-    mjit::ExpandInlineFrames(cx->compartment, true);
+    mjit::ExpandInlineFrames(cx->compartment);
 
     for (unsigned i = 0; i < pending->length(); i++) {
         JSScript *script = (*pending)[i];
         mjit::Recompiler recompiler(cx, script);
         if (script->hasJITCode())
             recompiler.recompile();
     }
 
@@ -2020,17 +2020,17 @@ TypeCompartment::nukeTypes(JSContext *cx
          cl != &cx->runtime->contextList;
          cl = cl->next) {
         JSContext *cx = js_ContextFromLinkField(cl);
         cx->setCompartment(cx->compartment);
     }
 
 #ifdef JS_METHODJIT
 
-    mjit::ExpandInlineFrames(cx->compartment, true);
+    mjit::ExpandInlineFrames(cx->compartment);
 
     /* Throw away all JIT code in the compartment, but leave everything else alone. */
     for (JSCList *cursor = compartment->scripts.next;
          cursor != &compartment->scripts;
          cursor = cursor->next) {
         JSScript *script = reinterpret_cast<JSScript *>(cursor);
         if (script->hasJITCode()) {
             mjit::Recompiler recompiler(cx, script);
@@ -2782,19 +2782,23 @@ TypeObject::markSlotReallocation(JSConte
 void
 TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags)
 {
     if ((this->flags & flags) == flags)
         return;
 
     AutoEnterTypeInference enter(cx);
 
-    /* Sets of CREATED_ARGUMENTS should go through MarkArgumentsCreated. */
-    JS_ASSERT_IF(flags & OBJECT_FLAG_CREATED_ARGUMENTS,
-                 (flags & OBJECT_FLAG_UNINLINEABLE) && functionScript->createdArgs);
+    if (singleton) {
+        /* Make sure flags are consistent with persistent object state. */
+        JS_ASSERT_IF(flags & OBJECT_FLAG_CREATED_ARGUMENTS,
+                     (flags & OBJECT_FLAG_UNINLINEABLE) && functionScript->createdArgs);
+        JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE, functionScript->uninlineable);
+        JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED, singleton->flags & JSObject::ITERATED);
+    }
 
     this->flags |= flags;
 
     InferSpew(ISpewOps, "%s: setFlags %u", name(), flags);
 
     ObjectStateChange(cx, this, false, false);
 }
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5758,17 +5758,17 @@ js_GetMethod(JSContext *cx, JSObject *ob
         return js_GetXMLMethod(cx, obj, id, vp);
 #endif
     return op(cx, obj, obj, id, vp);
 }
 
 JS_FRIEND_API(bool)
 js_CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname)
 {
-    StackFrame *const fp = js_GetTopStackFrame(cx, FRAME_EXPAND_TOP);
+    StackFrame *const fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
     if (!fp)
         return true;
 
     /* If neither cx nor the code is strict, then no check is needed. */
     if (!(fp->isScriptFrame() && fp->script()->strictModeCode) &&
         !cx->hasStrictOption()) {
         return true;
     }
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -5067,17 +5067,17 @@ js_DecompileValueGenerator(JSContext *cx
               spindex == JSDVG_IGNORE_STACK ||
               spindex == JSDVG_SEARCH_STACK);
 
     LeaveTrace(cx);
     
     if (!cx->hasfp() || !cx->fp()->isScriptFrame())
         goto do_fallback;
 
-    fp = js_GetTopStackFrame(cx, FRAME_EXPAND_TOP);
+    fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
     script = fp->script();
     pc = fp->hasImacropc() ? fp->imacropc() : cx->regs().pc;
     JS_ASSERT(script->code <= pc && pc < script->code + script->length);
 
     if (pc < script->main)
         goto do_fallback;
     
     if (spindex != JSDVG_IGNORE_STACK) {
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -431,16 +431,20 @@ AutoCompartment::~AutoCompartment()
 
 bool
 AutoCompartment::enter()
 {
     JS_ASSERT(!entered);
     if (origin != destination) {
         LeaveTrace(context);
 
+#ifdef JS_METHODJIT
+        mjit::ExpandInlineFrames(context->compartment);
+#endif
+
         JSObject *scopeChain = target->getGlobal();
         JS_ASSERT(scopeChain->isNative());
 
         frame.construct();
 
         /*
          * Set the compartment eagerly so that pushDummyFrame associates the
          * resource allocation request with 'destination' instead of 'origin'.
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -361,17 +361,17 @@ UncachedInlineCall(VMFrame &f, InitialFr
 
     /*
      * Otherwise, run newscript in the interpreter. Expand any inlined frame we
      * are calling from, as the new frame is not associated with the VMFrame
      * and will not have its prevpc info updated if frame expansion is
      * triggered while interpreting.
      */
     if (f.regs.inlined()) {
-        ExpandInlineFrames(cx->compartment, false);
+        ExpandInlineFrames(cx->compartment);
         JS_ASSERT(!f.regs.inlined());
         regs.fp()->resetInlinePrev(f.fp(), f.regs.pc);
     }
 
     bool ok = !!Interpret(cx, cx->fp());
     f.cx->stack.popInlineFrame(regs);
 
     if (ok)
@@ -606,17 +606,17 @@ js_InternalThrow(VMFrame &f)
         cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished);
 
         /*
          * Expanding inline frames will ensure that prevpc values are filled in
          * for all frames on this VMFrame, without needing to walk the entire
          * stack: downFramesExpanded() on a StackFrame also means the prevpc()
          * values are also filled in.
          */
-        ExpandInlineFrames(cx->compartment, true);
+        ExpandInlineFrames(cx->compartment);
 
         if (!script->ensureRanBytecode(cx)) {
             js_ReportOutOfMemory(cx);
             return NULL;
         }
 
         analyze::AutoEnterAnalysis enter(cx);
 
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -669,19 +669,19 @@ void JS_FASTCALL
 ProfileStubCall(VMFrame &f);
 
 CompileStatus JS_NEVER_INLINE
 TryCompile(JSContext *cx, StackFrame *fp);
 
 void
 ReleaseScriptCode(JSContext *cx, JSScript *script, bool normal);
 
-// Expand either the topmost stack frame or all stack frames inlined by the JIT.
+// Expand all stack frames inlined by the JIT within a compartment.
 void
-ExpandInlineFrames(JSCompartment *compartment, bool all);
+ExpandInlineFrames(JSCompartment *compartment);
 
 // Return all VMFrames in a compartment to the interpreter. This must be
 // followed by destroying all JIT code in the compartment.
 void
 ClearAllFrames(JSCompartment *compartment);
 
 // Information about a frame inlined during compilation.
 struct InlineFrame
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -324,28 +324,21 @@ Recompiler::expandInlineFrames(JSCompart
         if (JITCodeReturnAddress(*addr)) {
             innerfp->setRejoin(ScriptedRejoin(inlined->pcOffset));
             *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpolineScripted);
         }
     }
 }
 
 void
-ExpandInlineFrames(JSCompartment *compartment, bool all)
+ExpandInlineFrames(JSCompartment *compartment)
 {
     if (!compartment || !compartment->hasJaegerCompartment())
         return;
 
-    if (!all) {
-        VMFrame *f = compartment->jaegerCompartment()->activeFrame();
-        if (f && f->regs.inlined())
-            mjit::Recompiler::expandInlineFrames(compartment, f->fp(), f->regs.inlined(), NULL, f);
-        return;
-    }
-
     for (VMFrame *f = compartment->jaegerCompartment()->activeFrame();
          f != NULL;
          f = f->previous) {
 
         if (f->regs.inlined())
             mjit::Recompiler::expandInlineFrames(compartment, f->fp(), f->regs.inlined(), NULL, f);
 
         StackFrame *end = f->entryfp->prev();
@@ -372,17 +365,17 @@ ExpandInlineFrames(JSCompartment *compar
 }
 
 void
 ClearAllFrames(JSCompartment *compartment)
 {
     if (!compartment || !compartment->hasJaegerCompartment())
         return;
 
-    ExpandInlineFrames(compartment, true);
+    ExpandInlineFrames(compartment);
 
     for (VMFrame *f = compartment->jaegerCompartment()->activeFrame();
          f != NULL;
          f = f->previous) {
 
         Recompiler::patchFrame(compartment, f, f->fp()->script());
 
         // Clear ncode values from all frames associated with the VMFrame.
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1235,17 +1235,17 @@ stubs::Interrupt(VMFrame &f, jsbytecode 
 {
     if (!js_HandleExecutionInterrupt(f.cx))
         THROW();
 }
 
 void JS_FASTCALL
 stubs::RecompileForInline(VMFrame &f)
 {
-    ExpandInlineFrames(f.cx->compartment, true);
+    ExpandInlineFrames(f.cx->compartment);
     Recompiler recompiler(f.cx, f.script());
     recompiler.recompile(/* resetUses */ false);
 }
 
 void JS_FASTCALL
 stubs::Trap(VMFrame &f, uint32 trapTypes)
 {
     Value rval;
@@ -2490,17 +2490,17 @@ stubs::InvariantFailure(VMFrame &f, void
     void **frameAddr = f.returnAddressLocation();
     *frameAddr = repatchCode;
 
     /* Recompile the outermost script, and don't hoist any bounds checks. */
     JSScript *script = f.fp()->script();
     JS_ASSERT(!script->failedBoundsCheck);
     script->failedBoundsCheck = true;
 
-    ExpandInlineFrames(f.cx->compartment, true);
+    ExpandInlineFrames(f.cx->compartment);
 
     Recompiler recompiler(f.cx, script);
     recompiler.recompile();
 
     /* Return the same value (if any) as the call triggering the invariant failure. */
     return rval;
 }
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -570,20 +570,37 @@ Value *
 ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
                           MaybeExtend extend, bool *pushedSeg)
 {
     Value *firstUnused = space().firstUnused();
 
 #ifdef JS_METHODJIT
     /*
      * The only calls made by inlined methodjit frames can be to other JIT
-     * frames associated with the same VMFrame.
+     * frames associated with the same VMFrame. If we try to Invoke(),
+     * Execute() or so forth, any topmost inline frame will need to be
+     * expanded (along with other inline frames in the compartment).
+     * To avoid pathological behavior here, make sure to mark any topmost
+     * function as uninlineable, which will expand inline frames if there are
+     * any and prevent the function from being inlined in the future.
      */
-    if (cx->hasfp() && cx->regs().inlined())
-        mjit::ExpandInlineFrames(cx->compartment, false);
+    if (cx->hasfp() && cx->fp()->isFunctionFrame() && cx->fp()->fun()->isInterpreted()) {
+        if (report) {
+            /*
+             * N.B. if we can't report errors then cx->compartment may not be
+             * consistent with the function's compartment (cross-compartment
+             * call or SaveFrameChain). In such cases the caller must have
+             * already expanded inline frames.
+             */
+            cx->fp()->fun()->script()->uninlineable = true;
+            types::MarkTypeObjectFlags(cx, cx->fp()->fun(),
+                                       types::OBJECT_FLAG_UNINLINEABLE);
+        }
+        JS_ASSERT(!cx->regs().inlined());
+    }
 #endif
 
     if (onTop() && extend) {
         if (!space().ensureSpace(cx, report, firstUnused, nvars))
             return NULL;
         return firstUnused;
     }
 
@@ -812,16 +829,20 @@ ContextStack::popGeneratorFrame(const Ge
 
     /* ~FrameGuard/popFrame will finish the popping. */
     JS_ASSERT(ImplicitCast<const FrameGuard>(gfg).pushed());
 }
 
 bool
 ContextStack::saveFrameChain()
 {
+#ifdef JS_METHODJIT
+    mjit::ExpandInlineFrames(cx_->compartment);
+#endif
+
     /*
      * The StackSpace uses the context's current compartment to determine
      * whether to allow access to the privileged end-of-stack buffer.
      * However, we always want saveFrameChain to have access to this privileged
      * buffer since it gets used to prepare calling trusted JS. To force this,
      * we clear the current compartment (which is interpreted by ensureSpace as
      * 'trusted') and either restore it on OOM or let resetCompartment()
      * clobber it.
@@ -1080,17 +1101,17 @@ StackIter::settleOnNewState()
     }
 }
 
 StackIter::StackIter(JSContext *cx, SavedOption savedOption)
   : cx_(cx),
     savedOption_(savedOption)
 {
 #ifdef JS_METHODJIT
-    mjit::ExpandInlineFrames(cx->compartment, true);
+    mjit::ExpandInlineFrames(cx->compartment);
 #endif
 
     LeaveTrace(cx);
 
     if (StackSegment *seg = cx->stack.seg_) {
         startOnSegment(seg);
         settleOnNewState();
     } else {
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1748,23 +1748,16 @@ class GeneratorFrameGuard : public Frame
     JSGenerator *gen_;
     Value *stackvp_;
   public:
     ~GeneratorFrameGuard() { if (pushed()) stack_->popGeneratorFrame(*this); }
 };
 
 /*****************************************************************************/
 
-/* How much expansion of inlined frames to do when inspecting the stack. */
-enum FrameExpandKind {
-    FRAME_EXPAND_NONE,
-    FRAME_EXPAND_TOP,
-    FRAME_EXPAND_ALL
-};
-
 /*
  * Iterate through the callstack of the given context. Each element of said
  * callstack can either be the execution of a script (scripted function call,
  * global code, eval code, debugger code) or the invocation of a (C++) native.
  * Example usage:
  *
  *   for (Stackiter i(cx); !i.done(); ++i) {
  *     if (i.isScript()) {