[JAEGER] Stub call exits now work.
authorDavid Anderson <danderson@mozilla.com>
Sat, 29 May 2010 23:33:51 -0700
changeset 52619 d5dc3fe19ac50dd07f822549a2a4179a882ab236
parent 52618 83549638bab1605cee82c934da90a802a097159f
child 52620 2663395e7749b8bd4fb3169e050403cc0b2cd1e5
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)
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
[JAEGER] Stub call exits now work.
js/src/methodjit/FrameState-inl.h
js/src/methodjit/FrameState.cpp
js/src/methodjit/FrameState.h
js/src/methodjit/StubCompiler.cpp
js/src/methodjit/StubCompiler.h
js/src/methodjit/nunbox/FastOps.cpp
--- a/js/src/methodjit/FrameState-inl.h
+++ b/js/src/methodjit/FrameState-inl.h
@@ -110,23 +110,30 @@ FrameState::pop()
         forgetReg(fe->type.reg());
     if (fe->data.inRegister())
         forgetReg(fe->data.reg());
 }
 
 inline void
 FrameState::freeReg(RegisterID reg)
 {
-    JS_ASSERT(!regstate[reg].fe);
     forgetReg(reg);
 }
 
 inline void
 FrameState::forgetReg(RegisterID reg)
 {
+#ifdef DEBUG
+    if (regstate[reg].fe) {
+        if (regstate[reg].type == RematInfo::TYPE)
+            regstate[reg].fe->type.invalidate();
+        else
+            regstate[reg].fe->data.invalidate();
+    }
+#endif
     freeRegs.putReg(reg);
 }
 
 inline void
 FrameState::forgetEverything(uint32 newStackDepth)
 {
     forgetEverything();
     sp = spBase + newStackDepth;
@@ -240,13 +247,19 @@ FrameState::syncData(const FrameEntry *f
            masm.storeValue(fe->getValue(), addressOf(fe));
        else
            masm.storeData32(Imm32(fe->getPayload32()), addressOf(fe));
     } else {
         masm.storeData32(fe->data.reg(), addressOf(fe));
     }
 }
 
+inline void
+FrameState::learnType(FrameEntry *fe, uint32 tag)
+{
+    fe->setTypeTag(tag);
+}
+
 } /* namspace mjit */
 } /* namspace js */
 
 #endif /* include */
 
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -173,16 +173,36 @@ FrameState::assertValidRegisterState() c
         }
     }
 
     JS_ASSERT(checkedFreeRegs == freeRegs);
 }
 #endif
 
 void
+FrameState::sync(Assembler &masm) const
+{
+    for (uint32 i = 0; i < tracker.nentries; i++) {
+        uint32 index = tracker[i];
+
+        if (index >= tos())
+            continue;
+
+        FrameEntry *fe = &entries[index];
+        if (!fe->data.synced()) {
+            syncData(fe, masm);
+            if (fe->isConstant())
+                continue;
+        }
+        if (!fe->type.synced())
+            syncType(fe, masm);
+    }
+}
+
+void
 FrameState::syncAndKill(uint32 mask)
 {
     Registers kill(mask);
 
     for (uint32 i = 0; i < tracker.nentries; i++) {
         uint32 index = tracker[i];
 
         if (index >= tos())
--- a/js/src/methodjit/FrameState.h
+++ b/js/src/methodjit/FrameState.h
@@ -188,16 +188,21 @@ class FrameState
 
     /*
      * Fully stores a FrameEntry at an arbitrary address. popHint specifies
      * how hard the register allocator should try to keep the FE in registers.
      */
     void storeTo(FrameEntry *fe, Address address, bool popHint);
 
     /*
+     * Writes unsynced stores to an arbitrary buffer.
+     */
+    void sync(Assembler &masm) const;
+
+    /*
      * Syncs all outstanding stores to memory and possibly kills regs in the
      * process.
      */
     void syncAndKill(uint32 mask); 
 
     /*
      * Clear all tracker entries, syncing all outstanding stores in the process.
      * The stack depth is in case some merge points' edges did not immediately
@@ -207,16 +212,21 @@ class FrameState
 
     /*
      * Same as above, except the stack depth is not changed. This is used for
      * branching opcodes.
      */
     void forgetEverything();
 
     /*
+     * Mark an existing slot with a type.
+     */
+    inline void learnType(FrameEntry *fe, uint32 tag);
+
+    /*
      * Returns the current stack depth of the frame.
      */
     uint32 stackDepth() const { return sp - spBase; }
     uint32 tos() const { return sp - base; }
 
 #ifdef DEBUG
     void assertValidRegisterState() const;
 #endif
--- a/js/src/methodjit/StubCompiler.cpp
+++ b/js/src/methodjit/StubCompiler.cpp
@@ -42,76 +42,54 @@
 #include "Compiler.h"
 #include "assembler/assembler/LinkBuffer.h"
 #include "FrameState-inl.h"
 
 using namespace js;
 using namespace mjit;
 
 StubCompiler::StubCompiler(JSContext *cx, mjit::Compiler &cc, FrameState &frame, JSScript *script)
-  : cx(cx), cc(cc), frame(frame), script(script), generation(1), lastGeneration(0), hasJump(false),
-    exits(SystemAllocPolicy()), joins(SystemAllocPolicy())
+  : cx(cx), cc(cc), frame(frame), script(script), generation(1), lastGeneration(0),
+    exits(SystemAllocPolicy()), joins(SystemAllocPolicy()), jumpList(SystemAllocPolicy())
 {
 }
 
 bool
 StubCompiler::init(uint32 nargs)
 {
     return true;
 }
 
 /*
- * So, the tough thing about stub calls is that multiple exits from the fast
- * want to have one rejoin point. Roughly like this:
- *
- *  FAST                           | SLOW                           |
- *  -------------------------------+--------------------------------|
- *  pop value               .--->  | sink value                     |
- *  test type --------------' .->  | sink value                     |                                
- *  pop value                 |    | sink everything else           |  
- *  test type ----------------'    | do slow stuff                  |
- *  do fast stuff                  | restore  --.
- *  next opcode  <------------------------------'
- *
- * While testing for fast paths, the FrameState can change in a few ways:
- *  1) Anything might be spilled or moved to a new register.
- *     For example, local variable X might have register EAX for exit #1,
- *     and be spilled to the stack for exit #2. The common call point
- *     won't include a spill for EAX, so #1 must know to do this.
- *
- *  2) Operating on stack slots can add information.
- *
- * We solve problem #1 by looking for deltas in between snapshots, and
- * appending changes to the previous exit path. We should only ever encounter
- * a change for a given register once, since that slot will have become
- * sunk on the fast-path.
+ * The "slow path" generation is interleaved with the main compilation phase,
+ * though it is generated into a separate buffer. The fast path can "exit"
+ * into the slow path, and the slow path rejoins into the fast path. The slow
+ * path is kept in a separate buffer, but appended to the main one, for ideal
+ * icache locality.
  */
-
 void
 StubCompiler::linkExit(Jump j)
 {
     JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW MERGE CODE ---- \n");
-#if 0
     if (lastGeneration == generation) {
-        if (hasJump)
-            lastJump.linkTo(masm.label(), &masm);
-        frame.deltize(masm, snapshot);
-        lastJump = masm.jump();
-        hasJump = true;
+        Jump j = masm.jump();
+        jumpList.append(j);
     }
-    frame.snapshot(snapshot);
+    frame.sync(masm);
     lastGeneration = generation;
     exits.append(CrossPatch(j, masm.label()));
-#endif
     JaegerSpew(JSpew_Insns, " ---- END SLOW MERGE CODE ---- \n");
 }
 
 void
 StubCompiler::leave()
 {
+    for (size_t i = 0; i < jumpList.length(); i++)
+        jumpList[i].linkTo(masm.label(), &masm);
+    jumpList.clear();
 }
 
 void
 StubCompiler::rejoin(uint32 invalidationDepth)
 {
     JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW RESTORE CODE ---- \n");
 
 #if 0
@@ -127,17 +105,16 @@ StubCompiler::rejoin(uint32 invalidation
 typedef JSC::MacroAssembler::RegisterID RegisterID;
 typedef JSC::MacroAssembler::ImmPtr ImmPtr;
 typedef JSC::MacroAssembler::Imm32 Imm32;
 
 JSC::MacroAssembler::Call
 StubCompiler::stubCall(void *ptr)
 {
     generation++;
-    hasJump = false;
     JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW CALL CODE ---- \n");
     Call cl = masm.stubCall(ptr, cc.getPC(), frame.stackDepth() + script->nfixed);
     JaegerSpew(JSpew_Insns, " ---- END SLOW CALL CODE ---- \n");
     return cl;
 }
 
 void
 StubCompiler::fixCrossJumps(uint8 *ncode, size_t offset, size_t total)
--- a/js/src/methodjit/StubCompiler.h
+++ b/js/src/methodjit/StubCompiler.h
@@ -70,22 +70,21 @@ class StubCompiler
 
     JSContext *cx;
     Compiler &cc;
     FrameState &frame;
     JSScript *script;
     Assembler masm;
     uint32 generation;
     uint32 lastGeneration;
-    bool hasJump;
-    Jump lastJump;
 
     /* :TODO: oom check */
     Vector<CrossPatch, 64, SystemAllocPolicy> exits;
     Vector<CrossPatch, 64, SystemAllocPolicy> joins;
+    Vector<Jump, 8, SystemAllocPolicy> jumpList;
 
   public:
     StubCompiler(JSContext *cx, mjit::Compiler &cc, FrameState &frame, JSScript *script);
 
     bool init(uint32 nargs);
 
     size_t size() {
         return masm.size();
--- a/js/src/methodjit/nunbox/FastOps.cpp
+++ b/js/src/methodjit/nunbox/FastOps.cpp
@@ -82,27 +82,25 @@ mjit::Compiler::jsop_bitop(JSOp op)
         return;
     }
            
     /* Test the types. */
     if (!rhs->isTypeKnown()) {
         RegisterID reg = frame.tempRegForType(rhs);
         Jump rhsFail = masm.branch32(Assembler::NotEqual, reg, Imm32(JSVAL_MASK32_INT32));
         stubcc.linkExit(rhsFail);
-        if (lhs->isTypeKnown())
-            stubcc.leave();
         frame.freeReg(reg);
-        //rhs->setTypeTag(JSVAL_MASK32_INT32);
+        frame.learnType(rhs, JSVAL_MASK32_INT32);
     }
     if (!lhs->isTypeKnown()) {
         RegisterID reg = frame.tempRegForType(lhs);
         Jump lhsFail = masm.branch32(Assembler::NotEqual, reg, Imm32(JSVAL_MASK32_INT32));
         stubcc.linkExit(lhsFail);
-        stubcc.leave();
         frame.freeReg(reg);
     }
 
+    stubcc.leave();
     stubcc.call(stubs::BitAnd);
 
     frame.pop();
     frame.pop();
 }