[JAEGER] Finished initial stub-call mechanism, added BITAND.
authorDavid Anderson <danderson@mozilla.com>
Sun, 30 May 2010 01:01:41 -0700
changeset 52620 2663395e7749b8bd4fb3169e050403cc0b2cd1e5
parent 52619 d5dc3fe19ac50dd07f822549a2a4179a882ab236
child 52621 929279efc6313a967710148e81aa7d5c27e9be28
push idunknown
push userunknown
push dateunknown
milestone1.9.3a5pre
[JAEGER] Finished initial stub-call mechanism, added BITAND.
js/src/methodjit/FrameState-inl.h
js/src/methodjit/FrameState.cpp
js/src/methodjit/FrameState.h
js/src/methodjit/StubCompiler.cpp
js/src/methodjit/nunbox/FastOps.cpp
--- a/js/src/methodjit/FrameState-inl.h
+++ b/js/src/methodjit/FrameState-inl.h
@@ -180,29 +180,28 @@ inline void
 FrameState::push(Address address)
 {
     FrameEntry *fe = rawPush();
 
     /* :XXX: X64 */
     fe->resetUnsynced();
 
     RegisterID reg = alloc(fe, RematInfo::DATA, true);
-    masm.loadData32(addressOf(fe), reg);
+    masm.loadData32(address, reg);
     fe->data.setRegister(reg);
     
     reg = alloc(fe, RematInfo::TYPE, true);
-    masm.loadTypeTag(addressOf(fe), reg);
+    masm.loadTypeTag(address, reg);
     fe->type.setRegister(reg);
 }
 
 inline void
 FrameState::pushTypedPayload(uint32 tag, RegisterID payload)
 {
     JS_ASSERT(!freeRegs.hasReg(payload));
-    JS_ASSERT(!regstate[payload].fe);
 
     FrameEntry *fe = rawPush();
 
     fe->resetUnsynced();
     fe->setTypeTag(tag);
     fe->data.setRegister(payload);
     regstate[payload] = RegisterState(fe, RematInfo::DATA, true);
 }
@@ -217,16 +216,59 @@ FrameState::tempRegForType(FrameEntry *f
 
     /* :XXX: X64 */
 
     RegisterID reg = alloc(fe, RematInfo::TYPE, true);
     masm.loadTypeTag(addressOf(fe), reg);
     return reg;
 }
 
+inline JSC::MacroAssembler::RegisterID
+FrameState::tempRegForData(FrameEntry *fe)
+{
+    JS_ASSERT(!fe->data.isConstant());
+
+    if (fe->data.inRegister())
+        return fe->data.reg();
+
+    /* :XXX: X64 */
+
+    RegisterID reg = alloc(fe, RematInfo::DATA, true);
+    masm.loadData32(addressOf(fe), reg);
+    return reg;
+}
+
+inline JSC::MacroAssembler::RegisterID
+FrameState::ownRegForData(FrameEntry *fe)
+{
+    JS_ASSERT(!fe->data.isConstant());
+
+    if (fe->data.inRegister()) {
+        /* Remove ownership of this register. */
+        RegisterID reg = fe->data.reg();
+        JS_ASSERT(regstate[reg].fe == fe);
+        JS_ASSERT(regstate[reg].type == RematInfo::DATA);
+        regstate[reg].fe = NULL;
+        fe->data.invalidate();
+        return reg;
+    }
+
+    /* :XXX: X64 */
+
+    RegisterID reg = alloc(fe, RematInfo::DATA, true);
+    masm.loadData32(addressOf(fe), reg);
+    return reg;
+}
+
+inline bool
+FrameState::shouldAvoidDataRemat(FrameEntry *fe)
+{
+    return fe->data.inMemory();
+}
+
 inline void
 FrameState::syncType(const FrameEntry *fe, Assembler &masm) const
 {
     JS_ASSERT(!fe->type.synced());
     JS_ASSERT(fe->type.inRegister() || fe->type.isConstant());
 
     if (fe->type.isConstant()) {
         JS_ASSERT(fe->isTypeKnown());
@@ -253,13 +295,21 @@ FrameState::syncData(const FrameEntry *f
 }
 
 inline void
 FrameState::learnType(FrameEntry *fe, uint32 tag)
 {
     fe->setTypeTag(tag);
 }
 
+inline JSC::MacroAssembler::Address
+FrameState::addressOf(const FrameEntry *fe) const
+{
+    uint32 index = (fe - entries);
+    JS_ASSERT(index >= nargs);
+    return Address(Assembler::FpReg, sizeof(JSStackFrame) + sizeof(Value) * index);
+}
+
 } /* namspace mjit */
 } /* namspace js */
 
 #endif /* include */
 
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -227,8 +227,25 @@ FrameState::syncAndKill(uint32 mask)
         if (fe->type.inRegister() && kill.hasReg(fe->type.reg())) {
             JS_ASSERT(fe->type.synced());
             forgetReg(fe->type.reg());
             fe->type.setMemory();
         }
     }
 }
 
+void
+FrameState::merge(Assembler &masm, uint32 iVD) const
+{
+    for (uint32 i = 0; i < tracker.nentries; i++) {
+        uint32 index = tracker[i];
+
+        if (index >= tos())
+            continue;
+
+        FrameEntry *fe = &entries[index];
+        if (fe->data.inRegister())
+            masm.loadData32(addressOf(fe), fe->data.reg());
+        if (fe->type.inRegister())
+            masm.loadTypeTag(addressOf(fe), fe->type.reg());
+    }
+}
+
--- a/js/src/methodjit/FrameState.h
+++ b/js/src/methodjit/FrameState.h
@@ -50,16 +50,24 @@ namespace mjit {
 
 /*
  * The FrameState keeps track of values on the frame during compilation.
  * The compiler can query FrameState for information about arguments, locals,
  * and stack slots (all hereby referred to as "slots"). Slot information can
  * be requested in constant time. For each slot there is a FrameEntry *. If
  * this is non-NULL, it contains valid information and can be returned.
  *
+ * The register allocator keeps track of registers as being in one of two
+ * states. These are:
+ *
+ * 1) Unowned. Some code in the compiler is working on a register.
+ * 2) Owned. The FrameState owns the register, and may spill it at any time.
+ *
+ * ------------------ Implementation Details ------------------
+ * 
  * Observations:
  *
  * 1) We totally blow away known information quite often; branches, merge points.
  * 2) Every time we need a slow call, we must sync everything.
  * 3) Efficient side-exits need to quickly deltize state snapshots.
  * 4) Syncing is limited to constants and registers.
  * 5) Once a value is tracked, there is no reason to "forget" it until #1.
  * 
@@ -164,16 +172,35 @@ class FrameState
     inline void popn(uint32 n);
 
     /*
      * Allocates a temporary register for a FrameEntry's type.
      */
     inline RegisterID tempRegForType(FrameEntry *fe);
 
     /*
+     * Allocates a data register for a FrameEntry's type.
+     */
+    inline RegisterID tempRegForData(FrameEntry *fe);
+
+    /*
+     * Allocates a register for a FrameEntry's data, such that the compiler
+     * can modify it in-place. If the slot already has a temporary register,
+     * it is cleared, and thus the entry is invalidated!
+     */
+    inline RegisterID ownRegForData(FrameEntry *fe);
+
+    /*
+     * Payloads don't always have to be in registers, sometimes the compiler
+     * can use addresses and avoid spilling. If this FrameEntry has a synced
+     * address and no register, this returns true.
+     */
+    inline bool shouldAvoidDataRemat(FrameEntry *fe);
+
+    /*
      * Frees a temporary register. If this register is being tracked, then it
      * is not spilled; the backing data becomes invalidated!
      */
     inline void freeReg(RegisterID reg);
 
     /*
      * Allocates a register. If none are free, one may be spilled from the
      * tracker. If there are none available for spilling in the tracker,
@@ -188,16 +215,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);
 
     /*
+     * Restores state from a slow path.
+     */
+    void merge(Assembler &masm, uint32 ivD) const;
+
+    /*
      * 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.
      */
@@ -226,32 +258,28 @@ class FrameState
      */
     uint32 stackDepth() const { return sp - spBase; }
     uint32 tos() const { return sp - base; }
 
 #ifdef DEBUG
     void assertValidRegisterState() const;
 #endif
 
+    Address addressOf(const FrameEntry *fe) const;
+
   private:
     inline RegisterID alloc();
     inline RegisterID alloc(FrameEntry *fe, RematInfo::RematType type, bool weak);
     inline void forgetReg(RegisterID reg);
     void evictSomething();
     inline FrameEntry *rawPush();
     inline FrameEntry *addToTracker(uint32 index);
     inline void syncType(const FrameEntry *fe, Assembler &masm) const;
     inline void syncData(const FrameEntry *fe, Assembler &masm) const;
 
-    Address addressOf(const FrameEntry *fe) const {
-        uint32 index = (fe - entries);
-        JS_ASSERT(index >= nargs);
-        return Address(Assembler::FpReg, sizeof(JSStackFrame) + sizeof(Value) * index);
-    }
-
     uint32 indexOf(int32 depth) {
         return uint32((sp + depth) - base);
     }
 
   private:
     JSContext *cx;
     JSScript *script;
     uint32 nargs;
--- a/js/src/methodjit/StubCompiler.cpp
+++ b/js/src/methodjit/StubCompiler.cpp
@@ -65,18 +65,18 @@ StubCompiler::init(uint32 nargs)
  * 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 (lastGeneration == generation) {
-        Jump j = masm.jump();
-        jumpList.append(j);
+        Jump j2 = masm.jump();
+        jumpList.append(j2);
     }
     frame.sync(masm);
     lastGeneration = generation;
     exits.append(CrossPatch(j, masm.label()));
     JaegerSpew(JSpew_Insns, " ---- END SLOW MERGE CODE ---- \n");
 }
 
 void
@@ -87,22 +87,20 @@ StubCompiler::leave()
     jumpList.clear();
 }
 
 void
 StubCompiler::rejoin(uint32 invalidationDepth)
 {
     JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW RESTORE CODE ---- \n");
 
-#if 0
-    frame.merge(masm, snapshot, invalidationDepth);
+    frame.merge(masm, invalidationDepth);
 
     Jump j = masm.jump();
     joins.append(CrossPatch(j, cc.getLabel()));
-#endif
 
     JaegerSpew(JSpew_Insns, " ---- END SLOW RESTORE CODE ---- \n");
 }
 
 typedef JSC::MacroAssembler::RegisterID RegisterID;
 typedef JSC::MacroAssembler::ImmPtr ImmPtr;
 typedef JSC::MacroAssembler::Imm32 Imm32;
 
--- a/js/src/methodjit/nunbox/FastOps.cpp
+++ b/js/src/methodjit/nunbox/FastOps.cpp
@@ -95,12 +95,61 @@ mjit::Compiler::jsop_bitop(JSOp op)
         Jump lhsFail = masm.branch32(Assembler::NotEqual, reg, Imm32(JSVAL_MASK32_INT32));
         stubcc.linkExit(lhsFail);
         frame.freeReg(reg);
     }
 
     stubcc.leave();
     stubcc.call(stubs::BitAnd);
 
+    if (lhs->isConstant() && rhs->isConstant()) {
+        int32 L = lhs->getValue().asInt32();
+        int32 R = rhs->getValue().asInt32();
+
+        frame.popn(2);
+        switch (op) {
+          case JSOP_BITAND:
+            frame.push(Value(Int32Tag(L & R)));
+            return;
+
+          default:
+            JS_NOT_REACHED("say wat");
+        }
+    }
+
+    RegisterID reg;
+
+    switch (op) {
+      case JSOP_BITAND:
+      {
+        /* Commutative, and we're guaranteed both are ints. */
+        if (lhs->isConstant()) {
+            JS_ASSERT(!rhs->isConstant());
+            FrameEntry *temp = rhs;
+            rhs = lhs;
+            lhs = temp;
+        }
+
+        reg = frame.ownRegForData(lhs);
+        if (rhs->isConstant()) {
+            masm.and32(Imm32(rhs->getValue().asInt32()), reg);
+        } else if (frame.shouldAvoidDataRemat(rhs)) {
+            masm.and32(masm.payloadOf(frame.addressOf(rhs)), reg);
+        } else {
+            RegisterID rhsReg = frame.tempRegForData(rhs);
+            masm.and32(rhsReg, reg);
+        }
+
+        break;
+      }
+
+      default:
+        JS_NOT_REACHED("NYI");
+        return;
+    }
+
     frame.pop();
     frame.pop();
+    frame.pushTypedPayload(JSVAL_MASK32_INT32, reg);
+
+    stubcc.rejoin(2);
 }