Bug 1093573 part 12 - Inline INITIALYIELD and YIELD in Baseline. r=wingo
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 13 Nov 2014 17:39:49 +0100
changeset 215528 5e645894f6bf0b32c5d17823b44c91863c1b3beb
parent 215527 e2be5d0449c5eea1f4395c737c53baf3f4e4780a
child 215529 8792056f152cf79804df03bc7d130ad68ae0ded7
push id27818
push userryanvm@gmail.com
push dateThu, 13 Nov 2014 20:19:09 +0000
treeherdermozilla-central@292ed84594c1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswingo
bugs1093573
milestone36.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 1093573 part 12 - Inline INITIALYIELD and YIELD in Baseline. r=wingo
js/src/jit/BaselineCompiler.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/vm/GeneratorObject.cpp
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -3291,65 +3291,117 @@ BaselineCompiler::addYieldOffset()
     }
 
     static_assert(JSOP_INITIALYIELD_LENGTH == JSOP_YIELD_LENGTH,
                   "code below assumes INITIALYIELD and YIELD have same length");
     yieldOffsets_[yieldIndex] = script->pcToOffset(pc + JSOP_YIELD_LENGTH);
     return true;
 }
 
-typedef bool (*InitialSuspendFn)(JSContext *, HandleObject, BaselineFrame *, jsbytecode *);
-static const VMFunction InitialSuspendInfo = FunctionInfo<InitialSuspendFn>(jit::InitialSuspend);
-
 bool
 BaselineCompiler::emit_JSOP_INITIALYIELD()
 {
     if (!addYieldOffset())
         return false;
 
     frame.syncStack(0);
-
-    // Store generator in R0, BaselineFrame pointer in R1.
-    masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
-    masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
-
-    prepareVMCall();
-    pushArg(ImmPtr(pc));
-    pushArg(R1.scratchReg());
-    pushArg(R0.scratchReg());
-
-    if (!callVM(InitialSuspendInfo))
-        return false;
-
-    masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), JSReturnOperand);
+    MOZ_ASSERT(frame.stackDepth() == 1);
+
+    Register genObj = R2.scratchReg();
+    masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), genObj);
+
+    MOZ_ASSERT(GET_UINT24(pc) == 0);
+    masm.storeValue(Int32Value(0), Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()));
+
+    Register scopeObj = R0.scratchReg();
+    Address scopeChainSlot(genObj, GeneratorObject::offsetOfScopeChainSlot());
+    masm.loadPtr(frame.addressOfScopeChain(), scopeObj);
+    masm.patchableCallPreBarrier(scopeChainSlot, MIRType_Value);
+    masm.storeValue(JSVAL_TYPE_OBJECT, scopeObj, scopeChainSlot);
+
+#ifdef JSGC_GENERATIONAL
+    Register temp = R1.scratchReg();
+    Label skipBarrier;
+    masm.branchPtrInNurseryRange(Assembler::Equal, genObj, temp, &skipBarrier);
+    masm.branchPtrInNurseryRange(Assembler::NotEqual, scopeObj, temp, &skipBarrier);
+    masm.push(genObj);
+    MOZ_ASSERT(genObj == R2.scratchReg());
+    masm.call(&postBarrierSlot_);
+    masm.pop(genObj);
+    masm.bind(&skipBarrier);
+#endif
+
+    masm.tagValue(JSVAL_TYPE_OBJECT, genObj, JSReturnOperand);
     return emitReturn();
 }
 
 typedef bool (*NormalSuspendFn)(JSContext *, HandleObject, BaselineFrame *, jsbytecode *, uint32_t);
 static const VMFunction NormalSuspendInfo = FunctionInfo<NormalSuspendFn>(jit::NormalSuspend);
 
 bool
 BaselineCompiler::emit_JSOP_YIELD()
 {
     if (!addYieldOffset())
         return false;
 
-    // Store generator in R0, BaselineFrame pointer in R1.
+    // Store generator in R0.
     frame.popRegsAndSync(1);
-    masm.unboxObject(R0, R0.scratchReg());
-    masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
-
-    prepareVMCall();
-    pushArg(Imm32(frame.stackDepth()));
-    pushArg(ImmPtr(pc));
-    pushArg(R1.scratchReg());
-    pushArg(R0.scratchReg());
-
-    if (!callVM(NormalSuspendInfo))
-        return false;
+
+    Register genObj = R2.scratchReg();
+    masm.unboxObject(R0, genObj);
+
+    MOZ_ASSERT(frame.stackDepth() >= 1);
+
+    if (frame.stackDepth() == 1) {
+        // If the expression stack is empty, we can inline the YIELD. For legacy
+        // generators we normally check if we're in the closing state and throw
+        // an exception, but we don't have to do anything here as the expression
+        // stack is never empty in finally blocks. In debug builds, we assert
+        // we're not in the closing state.
+#ifdef DEBUG
+        if (script->isLegacyGenerator()) {
+            Label ok;
+            masm.unboxInt32(Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()),
+                            R0.scratchReg());
+            masm.branch32(Assembler::NotEqual, R0.scratchReg(),
+                          Imm32(GeneratorObject::YIELD_INDEX_CLOSING), &ok);
+            masm.assumeUnreachable("Inline yield with closing generator");
+            masm.bind(&ok);
+        }
+#endif
+        masm.storeValue(Int32Value(GET_UINT24(pc)),
+                        Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()));
+
+        Register scopeObj = R0.scratchReg();
+        Address scopeChainSlot(genObj, GeneratorObject::offsetOfScopeChainSlot());
+        masm.loadPtr(frame.addressOfScopeChain(), scopeObj);
+        masm.patchableCallPreBarrier(scopeChainSlot, MIRType_Value);
+        masm.storeValue(JSVAL_TYPE_OBJECT, scopeObj, scopeChainSlot);
+
+#ifdef JSGC_GENERATIONAL
+        Register temp = R1.scratchReg();
+        Label skipBarrier;
+        masm.branchPtrInNurseryRange(Assembler::Equal, genObj, temp, &skipBarrier);
+        masm.branchPtrInNurseryRange(Assembler::NotEqual, scopeObj, temp, &skipBarrier);
+        MOZ_ASSERT(genObj == R2.scratchReg());
+        masm.call(&postBarrierSlot_);
+        masm.bind(&skipBarrier);
+#endif
+    } else {
+        masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
+
+        prepareVMCall();
+        pushArg(Imm32(frame.stackDepth()));
+        pushArg(ImmPtr(pc));
+        pushArg(R1.scratchReg());
+        pushArg(genObj);
+
+        if (!callVM(NormalSuspendInfo))
+            return false;
+    }
 
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), JSReturnOperand);
     return emitReturn();
 }
 
 typedef bool (*FinalSuspendFn)(JSContext *, HandleObject, BaselineFrame *, jsbytecode *);
 static const VMFunction FinalSuspendInfo = FunctionInfo<FinalSuspendFn>(jit::FinalSuspend);
 
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -827,23 +827,16 @@ DebugEpilogue(JSContext *cx, BaselineFra
 
 JSObject *
 CreateGenerator(JSContext *cx, BaselineFrame *frame)
 {
     return GeneratorObject::create(cx, frame);
 }
 
 bool
-InitialSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc)
-{
-    MOZ_ASSERT(*pc == JSOP_INITIALYIELD);
-    return GeneratorObject::initialSuspend(cx, obj, frame, pc);
-}
-
-bool
 NormalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc,
               uint32_t stackDepth)
 {
     MOZ_ASSERT(*pc == JSOP_YIELD);
 
     // Return value is still on the stack.
     MOZ_ASSERT(stackDepth >= 1);
 
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -702,17 +702,16 @@ void PostGlobalWriteBarrier(JSRuntime *r
 
 uint32_t GetIndexFromString(JSString *str);
 
 bool DebugPrologue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn);
 bool DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok);
 bool DebugEpilogueOnBaselineReturn(JSContext *cx, BaselineFrame *frame, jsbytecode *pc);
 
 JSObject *CreateGenerator(JSContext *cx, BaselineFrame *frame);
-bool InitialSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc);
 bool NormalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc,
                    uint32_t stackDepth);
 bool FinalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc);
 bool InterpretResume(JSContext *cx, HandleObject obj, HandleValue val, HandlePropertyName kind,
                      MutableHandleValue rval);
 
 bool StrictEvalPrologue(JSContext *cx, BaselineFrame *frame);
 bool HeavyweightFunPrologue(JSContext *cx, BaselineFrame *frame);
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -65,17 +65,17 @@ GeneratorObject::suspend(JSContext *cx, 
     MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD);
 
     Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
     MOZ_ASSERT(!genObj->hasExpressionStack());
 
     if (*pc == JSOP_YIELD && genObj->isClosing()) {
         MOZ_ASSERT(genObj->is<LegacyGeneratorObject>());
         RootedValue val(cx, ObjectValue(*frame.callee()));
-        js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, JSDVG_SEARCH_STACK, val, NullPtr());
+        js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, JSDVG_IGNORE_STACK, val, NullPtr());
         return false;
     }
 
     uint32_t yieldIndex = GET_UINT24(pc);
     MOZ_ASSERT((*pc == JSOP_INITIALYIELD) == (yieldIndex == 0)); // isNewborn() relies on this.
 
     genObj->setYieldIndex(yieldIndex);
     genObj->setScopeChain(*frame.scopeChain());