Improve performance of arithmetic operators
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Aug 2008 09:48:10 +0000 (09:48 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Aug 2008 09:48:10 +0000 (09:48 +0000)
Reviewed by Cameron Zwarich

Added a fast (non-virtual) mechanism to determine if a non-immediate JSValue*
is a JSNumberCell.  We then use this to allow improved specialisation in many
arithmetic operators.  SunSpider reports a 2.5% progression overall, with greater
than 10% progressions on a number of arithmetic heavy tests.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@35639 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JavaScriptCore/ChangeLog
JavaScriptCore/VM/Machine.cpp
JavaScriptCore/kjs/JSNumberCell.h
JavaScriptCore/kjs/collector.cpp
JavaScriptCore/kjs/collector.h

index ca93130..7844ce9 100644 (file)
@@ -1,3 +1,31 @@
+2008-08-08  Oliver Hunt  <oliver@apple.com>
+
+        Reviewed by Cameron Zwarich.
+
+        Improve performance of arithmetic operators
+
+        Added a fast (non-virtual) mechanism to determine if a non-immediate JSValue*
+        is a JSNumberCell.  We then use this to allow improved specialisation in many
+        arithmetic operators.  SunSpider reports a 2.5% progression overall, with greater
+        than 10% progressions on a number of arithmetic heavy tests.
+
+        * VM/Machine.cpp:
+        (KJS::fastIsNumber):
+        (KJS::fastToInt32):
+        (KJS::fastToUInt32):
+        (KJS::jsLess):
+        (KJS::jsLessEq):
+        (KJS::jsAdd):
+        (KJS::Machine::privateExecute):
+        * kjs/JSNumberCell.h:
+        (KJS::JSNumberCell::fastToInt32):
+        (KJS::JSNumberCell::fastToUInt32):
+        * kjs/collector.cpp:
+        (KJS::allocateBlock):
+        (KJS::Heap::heapAllocate):
+        * kjs/collector.h:
+        (KJS::Heap::fastIsNumber):
+
 2008-08-06  Adam Roben  <aroben@apple.com>
 
         Try to fix the Windows build bots
index f034cd2..aef2b1e 100644 (file)
@@ -96,6 +96,40 @@ static int depth(ScopeChain& sc)
     return scopeDepth;
 }
 
+static bool fastIsNumber(JSValue* value, double& arg) {
+    if (JSImmediate::isNumber(value))
+        arg = JSImmediate::getTruncatedInt32(value);
+    else if (Heap::fastIsNumber(static_cast<JSCell*>(value)))
+        arg = static_cast<JSNumberCell*>(value)->value();
+    else
+        return false;
+    return true;
+}
+
+static bool fastToInt32(JSValue* value, int32_t& arg) {
+    if (JSImmediate::isNumber(value))
+        arg = JSImmediate::getTruncatedInt32(value);
+    else if (Heap::fastIsNumber(static_cast<JSCell*>(value)))
+        arg = static_cast<JSNumberCell*>(value)->fastToInt32();
+    else
+        return false;
+    return true;
+}
+
+static ALWAYS_INLINE bool fastToUInt32(JSValue* value, uint32_t& arg) {
+    if (JSImmediate::isNumber(value)) {
+        if (JSImmediate::getTruncatedUInt32(value, arg))
+            return true;
+        bool scratch;
+        arg = JSValue::toUInt32SlowCase(JSImmediate::getTruncatedInt32(value), scratch);
+        return true;
+    } else if (Heap::fastIsNumber(static_cast<JSCell*>(value)))
+        arg = static_cast<JSNumberCell*>(value)->fastToUInt32();
+    else
+        return false;
+    return true;
+}
+
 static inline bool jsLess(ExecState* exec, JSValue* v1, JSValue* v2)
 {
     if (JSImmediate::areBothImmediateNumbers(v1, v2))
@@ -103,6 +137,9 @@ static inline bool jsLess(ExecState* exec, JSValue* v1, JSValue* v2)
 
     double n1;
     double n2;
+    if (fastIsNumber(v1, n1) && fastIsNumber(v2, n2))
+        return n1 < n2;
+
     JSValue* p1;
     JSValue* p2;
     bool wasNotString1 = v1->getPrimitiveNumber(exec, n1, p1);
@@ -121,6 +158,9 @@ static inline bool jsLessEq(ExecState* exec, JSValue* v1, JSValue* v2)
 
     double n1;
     double n2;
+    if (fastIsNumber(v1, n1) && fastIsNumber(v2, n2))
+        return n1 <= n2;
+
     JSValue* p1;
     JSValue* p2;
     bool wasNotString1 = v1->getPrimitiveNumber(exec, n1, p1);
@@ -159,12 +199,16 @@ static JSValue* jsAddSlowCase(ExecState* exec, JSValue* v1, JSValue* v2)
 
 static inline JSValue* jsAdd(ExecState* exec, JSValue* v1, JSValue* v2)
 {
+    double left;
+    double right;
+    
+    if (fastIsNumber(v1, left) && fastIsNumber(v2, right))
+        return jsNumber(exec, left + right);
+    
     JSType t1 = v1->type();
     JSType t2 = v2->type();
     const unsigned bothTypes = (t1 << 3) | t2;
-
-    if (bothTypes == ((NumberType << 3) | NumberType))
-        return jsNumber(exec, v1->uncheckedGetNumber() + v2->uncheckedGetNumber());
+    ASSERT(bothTypes != ((NumberType << 3) | NumberType));
     if (bothTypes == ((StringType << 3) | StringType)) {
         UString value = static_cast<JSString*>(v1)->value() + static_cast<JSString*>(v2)->value();
         if (value.isNull())
@@ -1358,10 +1402,15 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
            result in register dst.
         */
         int dst = (++vPC)->u.operand;
-        int src = (++vPC)->u.operand;
-        JSValue* result = jsNumber(exec, -r[src].jsValue(exec)->toNumber(exec));
-        VM_CHECK_EXCEPTION();
-        r[dst] = result;
+        JSValue* src = r[(++vPC)->u.operand].jsValue(exec);
+        double v;
+        if (fastIsNumber(src, v))
+            r[dst] = jsNumber(exec, -v);
+        else {
+            JSValue* result = jsNumber(exec, -src->toNumber(exec));
+            VM_CHECK_EXCEPTION();
+            r[dst] = result;
+        }
 
         ++vPC;
         NEXT_OPCODE;
@@ -1395,9 +1444,15 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
         int dst = (++vPC)->u.operand;
         JSValue* src1 = r[(++vPC)->u.operand].jsValue(exec);
         JSValue* src2 = r[(++vPC)->u.operand].jsValue(exec);
-        JSValue* result = jsNumber(exec, src1->toNumber(exec) * src2->toNumber(exec));
-        VM_CHECK_EXCEPTION();
-        r[dst] = result;
+        double left;
+        double right;
+        if (fastIsNumber(src1, left) && fastIsNumber(src2, right))
+            r[dst] = jsNumber(exec, left * right);
+        else {
+            JSValue* result = jsNumber(exec, src1->toNumber(exec) * src2->toNumber(exec));
+            VM_CHECK_EXCEPTION();
+            r[dst] = result;
+        }
 
         ++vPC;
         NEXT_OPCODE;
@@ -1410,11 +1465,17 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
            quotient in register dst.
         */
         int dst = (++vPC)->u.operand;
-        int dividend = (++vPC)->u.operand;
-        int divisor = (++vPC)->u.operand;
-        JSValue* result = jsNumber(exec, r[dividend].jsValue(exec)->toNumber(exec) / r[divisor].jsValue(exec)->toNumber(exec));
-        VM_CHECK_EXCEPTION();
-        r[dst] = result;
+        JSValue* dividend = r[(++vPC)->u.operand].jsValue(exec);
+        JSValue* divisor = r[(++vPC)->u.operand].jsValue(exec);
+        double left;
+        double right;
+        if (fastIsNumber(dividend, left) && fastIsNumber(divisor, right))
+            r[dst] = jsNumber(exec, left / right);
+        else {
+            JSValue* result = jsNumber(exec, dividend->toNumber(exec) / divisor->toNumber(exec));
+            VM_CHECK_EXCEPTION();
+            r[dst] = result;
+        }
         ++vPC;
         NEXT_OPCODE;
     }
@@ -1455,8 +1516,12 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
         int dst = (++vPC)->u.operand;
         JSValue* src1 = r[(++vPC)->u.operand].jsValue(exec);
         JSValue* src2 = r[(++vPC)->u.operand].jsValue(exec);
+        double left;
+        double right;
         if (JSImmediate::canDoFastAdditiveOperations(src1) && JSImmediate::canDoFastAdditiveOperations(src2))
             r[dst] = JSImmediate::subImmediateNumbers(src1, src2);
+        else if (fastIsNumber(src1, left) && fastIsNumber(src2, right))
+            r[dst] = jsNumber(exec, left - right);
         else {
             JSValue* result = jsNumber(exec, src1->toNumber(exec) - src2->toNumber(exec));
             VM_CHECK_EXCEPTION();
@@ -1475,8 +1540,12 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
         int dst = (++vPC)->u.operand;
         JSValue* val = r[(++vPC)->u.operand].jsValue(exec);
         JSValue* shift = r[(++vPC)->u.operand].jsValue(exec);
+        int32_t left;
+        uint32_t right;
         if (JSImmediate::areBothImmediateNumbers(val, shift))
             r[dst] = jsNumber(exec, JSImmediate::getTruncatedInt32(val) << (JSImmediate::toTruncatedUInt32(shift) & 0x1f));
+        else if (fastToInt32(val, left) && fastToUInt32(shift, right))
+            r[dst] = jsNumber(exec, left << (right & 0x1f));
         else {
             JSValue* result = jsNumber(exec, (val->toInt32(exec)) << (shift->toUInt32(exec) & 0x1f));
             VM_CHECK_EXCEPTION();
@@ -1496,8 +1565,12 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
         int dst = (++vPC)->u.operand;
         JSValue* val = r[(++vPC)->u.operand].jsValue(exec);
         JSValue* shift = r[(++vPC)->u.operand].jsValue(exec);
+        int32_t left;
+        uint32_t right;
         if (JSImmediate::areBothImmediateNumbers(val, shift))
             r[dst] = JSImmediate::rightShiftImmediateNumbers(val, shift);
+        else if (fastToInt32(val, left) && fastToUInt32(shift, right))
+            r[dst] = jsNumber(exec, left >> (right & 0x1f));
         else {
             JSValue* result = jsNumber(exec, (val->toInt32(exec)) >> (shift->toUInt32(exec) & 0x1f));
             VM_CHECK_EXCEPTION();
@@ -1538,8 +1611,12 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
         int dst = (++vPC)->u.operand;
         JSValue* src1 = r[(++vPC)->u.operand].jsValue(exec);
         JSValue* src2 = r[(++vPC)->u.operand].jsValue(exec);
+        int32_t left;
+        int32_t right;
         if (JSImmediate::areBothImmediateNumbers(src1, src2))
             r[dst] = JSImmediate::andImmediateNumbers(src1, src2);
+        else if (fastToInt32(src1, left) && fastToInt32(src2, right))
+            r[dst] = jsNumber(exec, left & right);
         else {
             JSValue* result = jsNumber(exec, src1->toInt32(exec) & src2->toInt32(exec));
             VM_CHECK_EXCEPTION();
@@ -1559,8 +1636,12 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
         int dst = (++vPC)->u.operand;
         JSValue* src1 = r[(++vPC)->u.operand].jsValue(exec);
         JSValue* src2 = r[(++vPC)->u.operand].jsValue(exec);
+        int32_t left;
+        int32_t right;
         if (JSImmediate::areBothImmediateNumbers(src1, src2))
             r[dst] = JSImmediate::xorImmediateNumbers(src1, src2);
+        else if (fastToInt32(src1, left) && fastToInt32(src2, right))
+            r[dst] = jsNumber(exec, left ^ right);
         else {
             JSValue* result = jsNumber(exec, src1->toInt32(exec) ^ src2->toInt32(exec));
             VM_CHECK_EXCEPTION();
@@ -1580,8 +1661,12 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
         int dst = (++vPC)->u.operand;
         JSValue* src1 = r[(++vPC)->u.operand].jsValue(exec);
         JSValue* src2 = r[(++vPC)->u.operand].jsValue(exec);
+        int32_t left;
+        int32_t right;
         if (JSImmediate::areBothImmediateNumbers(src1, src2))
             r[dst] = JSImmediate::orImmediateNumbers(src1, src2);
+        else if (fastToInt32(src1, left) && fastToInt32(src2, right))
+            r[dst] = jsNumber(exec, left | right);
         else {
             JSValue* result = jsNumber(exec, src1->toInt32(exec) | src2->toInt32(exec));
             VM_CHECK_EXCEPTION();
@@ -1598,11 +1683,15 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
            and puts the result in register dst.
         */
         int dst = (++vPC)->u.operand;
-        int src = (++vPC)->u.operand;
-        JSValue* result = jsNumber(exec, ~r[src].jsValue(exec)->toInt32(exec));
-        VM_CHECK_EXCEPTION();
-        r[dst] = result;
-
+        JSValue* src = r[(++vPC)->u.operand].jsValue(exec);
+        int32_t value;
+        if (fastToInt32(src, value))
+            r[dst] = jsNumber(exec, ~value);
+        else {
+            JSValue* result = jsNumber(exec, ~src->toInt32(exec));
+            VM_CHECK_EXCEPTION();
+            r[dst] = result;
+        }
         ++vPC;
         NEXT_OPCODE;
     }
index 40c6b7c..e9b00a0 100644 (file)
@@ -58,6 +58,8 @@ namespace KJS {
         virtual UString toThisString(ExecState*) const;
         virtual JSObject* toThisObject(ExecState*) const;
         virtual JSValue* getJSNumber();
+        int32_t fastToInt32() const;
+        uint32_t fastToUInt32() const;
 
         void* operator new(size_t size, ExecState* exec)
         {
@@ -146,6 +148,22 @@ namespace KJS {
         return JSImmediate::isImmediate(this) ? JSImmediate::toDouble(this) : static_cast<const JSNumberCell*>(this)->value();
     }
 
+    inline int32_t JSNumberCell::fastToInt32() const
+    {
+        if (m_value >= -2147483648.0 && m_value < 2147483648.0)
+            return static_cast<int32_t>(m_value);
+        bool scratch;
+        return JSValue::toInt32SlowCase(m_value, scratch);
+    }
+
+    inline uint32_t JSNumberCell::fastToUInt32() const
+    {
+        if (m_value >= 0.0 && m_value < 4294967296.0)
+            return static_cast<uint32_t>(m_value);
+        bool scratch;
+        return JSValue::toUInt32SlowCase(m_value, scratch);
+    }
+
     ALWAYS_INLINE JSValue* JSValue::toJSNumber(ExecState* exec) const
     {
         return JSImmediate::isNumber(this) ? const_cast<JSValue*>(this) : jsNumber(exec, this->toNumber(exec));
index 61e7352..75c3b76 100644 (file)
@@ -159,6 +159,7 @@ Heap::~Heap()
 #endif
 }
 
+template <Heap::HeapType heapType>
 static NEVER_INLINE CollectorBlock* allocateBlock()
 {
 #if PLATFORM(DARWIN)
@@ -199,7 +200,7 @@ static NEVER_INLINE CollectorBlock* allocateBlock()
     address += adjust;
     memset(reinterpret_cast<void*>(address), 0, BLOCK_SIZE);
 #endif
-
+    reinterpret_cast<CollectorBlock*>(address)->type = heapType;
     return reinterpret_cast<CollectorBlock*>(address);
 }
 
@@ -346,7 +347,7 @@ collect:
             heap.blocks = static_cast<CollectorBlock**>(fastRealloc(heap.blocks, numBlocks * sizeof(CollectorBlock*)));
         }
 
-        targetBlock = (Block*)allocateBlock();
+        targetBlock = (Block*)allocateBlock<heapType>();
         targetBlock->freeList = targetBlock->cells;
         targetBlock->heap = this;
         targetBlockUsedCells = 0;
index 8c5550c..32a0f9f 100644 (file)
@@ -105,7 +105,7 @@ namespace KJS {
         HashSet<ArgList*>& markListSet() { if (!m_markListSet) m_markListSet = new HashSet<ArgList*>; return *m_markListSet; }
 
         JSGlobalData* globalData() const { return m_globalData; }
-
+        static bool fastIsNumber(JSCell*);
     private:
         template <Heap::HeapType heapType> void* heapAllocate(size_t);
         template <Heap::HeapType heapType> size_t sweep();
@@ -202,6 +202,7 @@ namespace KJS {
         CollectorCell* freeList;
         CollectorBitmap marked;
         Heap* heap;
+        Heap::HeapType type;
     };
 
     class SmallCellCollectorBlock {
@@ -211,7 +212,14 @@ namespace KJS {
         SmallCollectorCell* freeList;
         CollectorBitmap marked;
         Heap* heap;
+        Heap::HeapType type;
     };
+    
+    inline bool Heap::fastIsNumber(JSCell* cell)
+    {
+        CollectorBlock* block = Heap::cellBlock(cell);
+        return block && block->type == NumberHeap;
+    }
 
     inline const CollectorBlock* Heap::cellBlock(const JSCell* cell)
     {