Implement JSOP_PICK & JSOP_SWAP (Bug 713571, r=dvander)
authorNicolas Pierron <nioclas.b.pierron@mozilla.com>
Wed, 11 Jan 2012 00:30:51 +0100
changeset 105545 2ed7cba609d7767956d82a9012b14650fa7f3b5f
parent 105544 83f23241cfaa8da292a5fc6ed517fd040d089574
child 105546 d601339c1bea7d44dab98a51c460976e17c23d26
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersdvander
bugs713571
milestone12.0a1
Implement JSOP_PICK & JSOP_SWAP (Bug 713571, r=dvander)
js/src/ion/IonBuilder.cpp
js/src/ion/MIRGraph.cpp
js/src/ion/MIRGraph.h
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -716,16 +716,24 @@ IonBuilder::inspectOpcode(JSOp op)
 
       case JSOP_DUP:
         current->pushSlot(current->stackDepth() - 1);
         return true;
 
       case JSOP_DUP2:
         return jsop_dup2();
 
+      case JSOP_SWAP:
+        current->swapAt(-1);
+        return true;
+
+      case JSOP_PICK:
+        current->pick(-GET_INT8(pc));
+        return true;
+
       case JSOP_UINT24:
         return pushConstant(Int32Value(GET_UINT24(pc)));
 
       case JSOP_INT32:
         return pushConstant(Int32Value(GET_INT32(pc)));
 
       case JSOP_LOOPHEAD:
         // JSOP_LOOPHEAD is handled when processing the loop header.
--- a/js/src/ion/MIRGraph.cpp
+++ b/js/src/ion/MIRGraph.cpp
@@ -429,16 +429,94 @@ MBasicBlock::pop()
     }
 
     // The slot cannot have live copies if it is being removed.
     JS_ASSERT(!slot.isCopied());
 
     return slot.def;
 }
 
+void
+MBasicBlock::pick(int32 depth)
+{
+    // pick take an element and move it to the top.
+    // pick(-2):
+    //   A B C D E
+    //   A B D C E [ swapAt(-2) ]
+    //   A B D E C [ swapAt(-1) ]
+    for (; depth < 0; depth++)
+        swapAt(depth);
+}
+
+void
+MBasicBlock::swapAt(int32 depth)
+{
+    uint32 lhsDepth = stackPosition_ + depth - 1;
+    uint32 rhsDepth = stackPosition_ + depth;
+
+    JS_ASSERT(depth < 0);
+    JS_ASSERT(lhsDepth >= info_.firstStackSlot());
+
+    StackSlot &lhs = slots_[lhsDepth];
+    StackSlot &rhs = slots_[rhsDepth];
+
+    // Exit if the swap is a no-op.
+    if (rhs.isCopy()) {
+        if (rhs.copyOf == lhsDepth)
+            return;
+        if (lhs.isCopy() && rhs.copyOf == lhs.copyOf)
+            return;
+    }
+
+    // Update linked lists to new locations.
+    updateIndexes(lhs, lhsDepth, rhsDepth);
+    updateIndexes(rhs, rhsDepth, lhsDepth);
+
+    // Swap values.
+    StackSlot tmp = lhs;
+    lhs = rhs;
+    rhs = tmp;
+}
+
+void
+MBasicBlock::updateIndexes(StackSlot &elem, uint32 oldIdx, uint32 newIdx)
+{
+    // This implementation does not handle the case where the element need to
+    // change location in the chained list.
+    JS_ASSERT(oldIdx == newIdx + 1 || oldIdx == newIdx - 1);
+    JS_ASSERT(&elem == &slots_[oldIdx] || &elem == &slots_[newIdx]);
+    JS_ASSERT_IF(slots_[oldIdx].isCopy() || slots_[newIdx].isCopy(),
+                 slots_[oldIdx].copyOf != newIdx &&
+                 slots_[oldIdx].copyOf != slots_[newIdx].copyOf &&
+                 oldIdx != slots_[newIdx].copyOf);
+
+    if (elem.isCopy()) {
+        // We iterate since the beginning to find and update the element
+        // pointing to this one.
+        JS_ASSERT(slots_[elem.copyOf].isCopied());
+        if (slots_[elem.copyOf].firstCopy == oldIdx) {
+            slots_[elem.copyOf].firstCopy = newIdx;
+        } else {
+            uint32 copyIndex = slots_[elem.copyOf].firstCopy;
+            while (slots_[copyIndex].nextCopy != oldIdx)
+                copyIndex = slots_[copyIndex].nextCopy;
+            slots_[copyIndex].nextCopy = newIdx;
+        }
+    } else if (elem.isCopied()) {
+        // This element is copied, so we iterate over all copies to update the
+        // copyOf index.
+        uint32 copyIndex = elem.firstCopy;
+        while (copyIndex != NotACopy) {
+            JS_ASSERT(slots_[copyIndex].copyOf == oldIdx);
+            slots_[copyIndex].copyOf = newIdx;
+            copyIndex = slots_[copyIndex].nextCopy;
+        }
+    }
+}
+
 MDefinition *
 MBasicBlock::peek(int32 depth)
 {
     JS_ASSERT(depth < 0);
     JS_ASSERT(stackPosition_ + depth >= info_.firstStackSlot());
     return getSlot(stackPosition_ + depth);
 }
 
--- a/js/src/ion/MIRGraph.h
+++ b/js/src/ion/MIRGraph.h
@@ -110,16 +110,21 @@ class MBasicBlock : public TempObject, p
 
     // Pushes a copy of a local variable or argument.
     void pushVariable(uint32 slot);
 
     // Sets a variable slot to the top of the stack, correctly creating copies
     // as needed.
     void setVariable(uint32 slot);
 
+    // Update the index of the linked list of stack slot during swapAt
+    // operations.  The value must have been copied from the source to the
+    // destination before calling this function.
+    void updateIndexes(StackSlot &elem, uint32 oldIdx, uint32 newIdx);
+
   public:
     ///////////////////////////////////////////////////////
     ////////// BEGIN GRAPH BUILDING INSTRUCTIONS //////////
     ///////////////////////////////////////////////////////
 
     // Creates a new basic block for a MIR generator. If |pred| is not NULL,
     // its slots and stack depth are initialized from |pred|.
     static MBasicBlock *New(MIRGraph &graph, CompileInfo &info,
@@ -127,16 +132,22 @@ class MBasicBlock : public TempObject, p
     static MBasicBlock *NewPendingLoopHeader(MIRGraph &graph, CompileInfo &info,
                                              MBasicBlock *pred, jsbytecode *entryPc);
     static MBasicBlock *NewSplitEdge(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred);
 
     void setId(uint32 id) {
         id_ = id;
     }
 
+    // Move the definition to the top of the stack.
+    void pick(int32 depth);
+
+    // Exchange 2 stack slots at the defined depth
+    void swapAt(int32 depth);
+
     // Gets the instruction associated with various slot types.
     MDefinition *peek(int32 depth);
 
     // Initializes a slot value; must not be called for normal stack
     // operations, as it will not create new SSA names for copies.
     void initSlot(uint32 index, MDefinition *ins);
 
     // In an OSR block, set all MOsrValues to use the MResumePoint attached to