Bug 666094 - InitJitFrameLatePrologue should do a better modeling generatePrologue (r=dvander)
authorLuke Wagner <luke@mozilla.com>
Fri, 24 Jun 2011 16:11:21 -0700
changeset 72382 f4237a8313ead69733e80dd3e01100f539645232
parent 72381 d1cdf42956267738495e3244a32208551a895497
child 72383 e77af15dc4d410e0fa21683d3fb9d53f3a882a74
push id159
push usereakhgari@mozilla.com
push dateTue, 16 Aug 2011 17:53:11 +0000
treeherdermozilla-beta@8786e3e49240 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs666094
milestone7.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
Bug 666094 - InitJitFrameLatePrologue should do a better modeling generatePrologue (r=dvander)
js/src/jit-test/tests/basic/testCompileScript.js
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MonoIC.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testCompileScript.js
@@ -0,0 +1,38 @@
+// |jit-test| mjitalways
+
+var nlocals = 50;
+var localstr = "";
+for (var i = 0; i < nlocals; ++i)
+    localstr += "var x" + i + "; ";
+
+/*
+ * Attempt to test, in a stack-parameter-independent manner, ComileFunction
+ * hitting a stack-commit boundary (which is not an OOM, but requires checking
+ * and updating the stack limit).
+ */
+var arr = [function() {return 0}, function() {return 1}, function() {return 2}];
+var arg = "x";
+var body = localstr +
+           "if (x == 0) return; " +
+           "arr[3] = (new Function(arg, body));" +
+           "for (var i = 0; i < 4; ++i) arr[i](x-1);";
+(new Function(arg, body))(1000);
+
+/*
+ * Also check for OOM in CompileFunction. To avoid taking 5 seconds, use a
+ * monster apply to chew up most the stack.
+ */
+var gotIn = false;
+var threwOut = false;
+try {
+    (function() {
+        gotIn = true;
+        (new Function(arg, body))(10000000);
+     }).apply(null, new Array(500 * 1024));
+} catch(e) {
+    assertEq(""+e, "InternalError: too much recursion");
+    threwOut = true;
+}
+assertEq(threwOut, true);
+/* If tweaking some stack parameter makes this fail, shrink monster apply. */
+assertEq(gotIn, true);
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -278,17 +278,18 @@ stubs::CompileFunction(VMFrame &f, uint3
 
     if (nactual != fp->numFormalArgs()) {
         fp = (StackFrame *)FixupArity(f, nactual);
         if (!fp)
             return NULL;
     }
 
     /* Finish frame initialization. */
-    fp->initJitFrameLatePrologue();
+    if (!fp->initJitFrameLatePrologue(cx, &f.stackLimit))
+        THROWV(NULL);
 
     /* These would have been initialized by the prologue. */
     f.regs.prepareToRun(*fp, script);
 
     if (fun->isHeavyweight() && !js::CreateFunCallObject(cx, fp))
         THROWV(NULL);
 
     CompileStatus status = CanMethodJIT(cx, script, fp, CompileRequest_JIT);
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -651,17 +651,22 @@ class CallCompiler : public BaseCompiler
         masm.loadPtr(Address(t0, offset), t0);
         Jump hasCode = masm.branchPtr(Assembler::Above, t0, ImmPtr(JS_UNJITTABLE_SCRIPT));
 
         /* Try and compile. On success we get back the nmap pointer. */
         masm.storePtr(JSFrameReg, FrameAddress(VMFrame::offsetOfFp));
         void *compilePtr = JS_FUNC_TO_DATA_PTR(void *, stubs::CompileFunction);
         if (ic.frameSize.isStatic()) {
             masm.move(Imm32(ic.frameSize.staticArgc()), Registers::ArgReg1);
-            masm.fallibleVMCall(compilePtr, script->code, ic.frameSize.staticLocalSlots());
+            /*
+             * CompileFunction doesn't use 'sp', so we could leave it
+             * uninitialized. However, it does wind up calling tryBumpLimit
+             * wanting to assert about regs.sp, so set regs.sp = fp->slots().
+             */
+            masm.fallibleVMCall(compilePtr, script->code, 0);
         } else {
             masm.load32(FrameAddress(offsetof(VMFrame, u.call.dynamicArgc)), Registers::ArgReg1);
             masm.fallibleVMCall(compilePtr, script->code, -1);
         }
         masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg);
 
         Jump notCompiled = masm.branchTestPtr(Assembler::Zero, Registers::ReturnReg,
                                               Registers::ReturnReg);
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -153,35 +153,47 @@ StackFrame::initJitFrameCallerHalf(JSCon
                          UNDERFLOW_ARGS)) == 0);
 
     flags_ = FUNCTION | flags;
     prev_ = cx->fp();
     ncode_ = ncode;
 }
 
 /*
- * The "early prologue" refers to the members that are stored for the benefit
- * of slow paths before initializing the rest of the members.
+ * The "early prologue" refers to either the fast path or arity check path up
+ * to the "late prologue".
  */
 inline void
 StackFrame::initJitFrameEarlyPrologue(JSFunction *fun, uint32 nactual)
 {
     exec.fun = fun;
     if (flags_ & (OVERFLOW_ARGS | UNDERFLOW_ARGS))
         args.nactual = nactual;
 }
 
 /*
- * The "late prologue" refers to the members that are stored after having
- * checked for stack overflow and formal/actual arg mismatch.
+ * The "late prologue" (in generatePrologue) extends from the join point of the
+ * fast path and arity check to where the call object is (possibly) created.
  */
-inline void
-StackFrame::initJitFrameLatePrologue()
+inline bool
+StackFrame::initJitFrameLatePrologue(JSContext *cx, Value **limit)
 {
+    uintN nvals = script()->nslots + VALUES_PER_STACK_FRAME;
+    Value *required = (Value *)this + nvals;
+    if (required >= *limit) {
+        ContextStack &stack = cx->stack;
+        if (!stack.space().tryBumpLimit(NULL, slots(), nvals, limit)) {
+            stack.popFrameAfterOverflow();
+            js_ReportOverRecursed(cx);
+            return false;
+        }
+    }
+
     SetValueRangeToUndefined(slots(), script()->nfixed);
+    return true;
 }
 
 inline Value &
 StackFrame::canonicalActualArg(uintN i) const
 {
     if (i < numFormalArgs())
         return formalArg(i);
     JS_ASSERT(i < numActualArgs());
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -384,17 +384,17 @@ class StackFrame
                        JSScript *script, uint32 nactual, StackFrame::Flags flags);
 
     /* Used for SessionInvoke. */
     void resetCallFrame(JSScript *script);
 
     /* Called by jit stubs and serve as a specification for jit-code. */
     void initJitFrameCallerHalf(JSContext *cx, StackFrame::Flags flags, void *ncode);
     void initJitFrameEarlyPrologue(JSFunction *fun, uint32 nactual);
-    void initJitFrameLatePrologue();
+    bool initJitFrameLatePrologue(JSContext *cx, Value **limit);
 
     /* Used for eval. */
     void initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs,
                           const Value &thisv, JSObject &scopeChain, ExecuteType type);
 
     /* Used when activating generators. */
     void stealFrameAndSlots(Value *vp, StackFrame *otherfp, Value *othervp, Value *othersp);