inline get_element calls
authorLars Knoll <lars.knoll@digia.com>
Sun, 13 Oct 2013 19:03:44 +0000 (21:03 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 14 Oct 2013 19:59:59 +0000 (21:59 +0200)
Inline calls to get_element if the base is an
object with a simple array structure, and the index
is an integer number.

Implemented for 64bit only for now, saves ~25% on crypto.js

Change-Id: I3e34a6409169d90d3937f62264707d52a6c2f9f7
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
src/qml/compiler/qv4isel_masm.cpp
src/qml/compiler/qv4isel_masm_p.h
src/qml/jsruntime/qv4managed.cpp
src/qml/jsruntime/qv4managed_p.h
src/qml/jsruntime/qv4object.cpp
src/qml/jsruntime/qv4sequenceobject.cpp
src/qml/qml/qqmllistwrapper.cpp
src/qml/types/qqmldelegatemodel.cpp
src/quick/items/context2d/qquickcontext2d.cpp

index 842b261..1155322 100644 (file)
@@ -671,7 +671,6 @@ void InstructionSelection::run(int functionIndex)
                 << JSC::X86Registers::r15;
 #endif
         static const QVector<int> fpRegisters = QVector<int>()
-                << JSC::X86Registers::xmm1
                 << JSC::X86Registers::xmm2
                 << JSC::X86Registers::xmm3
                 << JSC::X86Registers::xmm4
@@ -1102,6 +1101,89 @@ void InstructionSelection::setProperty(V4IR::Expr *source, V4IR::Expr *targetBas
 
 void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target)
 {
+#if QT_POINTER_SIZE == 8
+    V4IR::Temp *tbase = base->asTemp();
+    V4IR::Temp *tindex = index->asTemp();
+    if (tbase && tindex &&
+        tbase->kind != V4IR::Temp::PhysicalRegister) {
+        Assembler::Pointer addr = _as->loadTempAddress(Assembler::ReturnValueRegister, tbase);
+        _as->load64(addr, Assembler::ScratchRegister);
+        _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
+        _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsManaged_Shift), Assembler::ReturnValueRegister);
+        Assembler::Jump notManaged = _as->branch64(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm64(0));
+        // check whether we have an object with a simple array
+        Assembler::Address managedType(Assembler::ScratchRegister, qOffsetOf(QV4::Managed, flags));
+        _as->load8(managedType, Assembler::ReturnValueRegister);
+        _as->and32(Assembler::TrustedImm32(QV4::Managed::SimpleArray), Assembler::ReturnValueRegister);
+        Assembler::Jump notSimple = _as->branch32(Assembler::Equal, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0));
+
+        Assembler::Jump fallback;
+        if (tindex->kind == V4IR::Temp::PhysicalRegister) {
+            if (tindex->type == V4IR::SInt32Type) {
+                _as->move((Assembler::RegisterID) tindex->index, Assembler::ScratchRegister);
+            } else {
+                // double, convert and check if it's a int
+                _as->truncateDoubleToUint32((Assembler::FPRegisterID) tindex->index, Assembler::ScratchRegister);
+                _as->convertInt32ToDouble(Assembler::ScratchRegister, Assembler::FPGpr0);
+                fallback = _as->branchDouble(Assembler::DoubleNotEqual, Assembler::FPGpr0, (Assembler::FPRegisterID) tindex->index);
+            }
+        } else {
+            Assembler::Pointer indexAddr = _as->loadTempAddress(Assembler::ReturnValueRegister, tindex);
+            _as->load64(indexAddr, Assembler::ScratchRegister);
+            _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
+            _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsNumber_Shift), Assembler::ReturnValueRegister);
+            Assembler::Jump isInteger = _as->branch64(Assembler::Equal, Assembler::ReturnValueRegister, Assembler::TrustedImm64(1));
+
+            // other type, convert to double and check if it's a int
+            // this check is ok to do even if the type is something else than a double, as
+            // that would result in a NaN
+            _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ReturnValueRegister);
+            _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
+            _as->move64ToDouble(Assembler::ReturnValueRegister, Assembler::FPGpr0);
+            _as->truncateDoubleToUint32(Assembler::FPGpr0, Assembler::ScratchRegister);
+            _as->convertInt32ToDouble(Assembler::ScratchRegister, Assembler::FPGpr1);
+            fallback = _as->branchDouble(Assembler::DoubleNotEqualOrUnordered, Assembler::FPGpr0, Assembler::FPGpr1);
+
+            isInteger.link(_as);
+            _as->or32(Assembler::TrustedImm32(0), Assembler::ScratchRegister);
+        }
+
+        // get data, ScratchRegister holds index
+        addr = _as->loadTempAddress(Assembler::ReturnValueRegister, tbase);
+        _as->load64(addr, Assembler::ReturnValueRegister);
+        Address arrayDataLen(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayDataLen));
+        Assembler::Jump outOfRange = _as->branch32(Assembler::GreaterThanOrEqual, Assembler::ScratchRegister, arrayDataLen);
+        Address arrayData(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData));
+        _as->load64(arrayData, Assembler::ReturnValueRegister);
+        Q_ASSERT(sizeof(Property) == (1<<4));
+        _as->lshift64(Assembler::TrustedImm32(4), Assembler::ScratchRegister);
+        _as->add64(Assembler::ReturnValueRegister, Assembler::ScratchRegister);
+        Address value(Assembler::ScratchRegister, qOffsetOf(Property, value));
+        _as->load64(value, Assembler::ReturnValueRegister);
+
+        // check that the value is not empty
+        _as->move(Assembler::ReturnValueRegister, Assembler::ScratchRegister);
+        _as->urshift64(Assembler::TrustedImm32(32), Assembler::ScratchRegister);
+        Assembler::Jump emptyValue = _as->branch32(Assembler::Equal, Assembler::TrustedImm32(QV4::Value::Empty_Type), Assembler::ScratchRegister);
+        _as->storeReturnValue(target);
+
+        Assembler::Jump done = _as->jump();
+
+        emptyValue.link(_as);
+        outOfRange.link(_as);
+        if (fallback.isSet())
+            fallback.link(_as);
+        notSimple.link(_as);
+        notManaged.link(_as);
+
+        generateFunctionCall(target, __qmljs_get_element, Assembler::ContextRegister,
+                             Assembler::PointerToValue(base), Assembler::PointerToValue(index));
+
+        done.link(_as);
+        return;
+    }
+#endif
+
     generateFunctionCall(target, __qmljs_get_element, Assembler::ContextRegister,
                          Assembler::PointerToValue(base), Assembler::PointerToValue(index));
 }
@@ -1313,8 +1395,8 @@ Assembler::Jump InstructionSelection::genInlineBinop(V4IR::AluOp oper, V4IR::Exp
     //       register.
     switch (oper) {
     case V4IR::OpAdd: {
-        Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 1);
-        Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 3);
+        Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 2);
+        Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 4);
         Assembler::Jump leftIsNoDbl = genTryDoubleConversion(leftSource, lReg);
         Assembler::Jump rightIsNoDbl = genTryDoubleConversion(rightSource, rReg);
 
@@ -1328,8 +1410,8 @@ Assembler::Jump InstructionSelection::genInlineBinop(V4IR::AluOp oper, V4IR::Exp
             rightIsNoDbl.link(_as);
     } break;
     case V4IR::OpMul: {
-        Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 1);
-        Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 3);
+        Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 2);
+        Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 4);
         Assembler::Jump leftIsNoDbl = genTryDoubleConversion(leftSource, lReg);
         Assembler::Jump rightIsNoDbl = genTryDoubleConversion(rightSource, rReg);
 
@@ -1343,8 +1425,8 @@ Assembler::Jump InstructionSelection::genInlineBinop(V4IR::AluOp oper, V4IR::Exp
             rightIsNoDbl.link(_as);
     } break;
     case V4IR::OpSub: {
-        Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 1);
-        Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 3);
+        Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 2);
+        Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 4);
         Assembler::Jump leftIsNoDbl = genTryDoubleConversion(leftSource, lReg);
         Assembler::Jump rightIsNoDbl = genTryDoubleConversion(rightSource, rReg);
 
@@ -1358,8 +1440,8 @@ Assembler::Jump InstructionSelection::genInlineBinop(V4IR::AluOp oper, V4IR::Exp
             rightIsNoDbl.link(_as);
     } break;
     case V4IR::OpDiv: {
-        Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 1);
-        Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 3);
+        Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 2);
+        Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 4);
         Assembler::Jump leftIsNoDbl = genTryDoubleConversion(leftSource, lReg);
         Assembler::Jump rightIsNoDbl = genTryDoubleConversion(rightSource, rReg);
 
index fb990d0..4d1114e 100644 (file)
@@ -136,6 +136,7 @@ public:
     static const RegisterID ScratchRegister = JSC::X86Registers::r10;
     static const RegisterID IntegerOpRegister = JSC::X86Registers::eax;
     static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0;
+    static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1;
 
     static const int RegisterSize = 8;
 
index 03608d2..31bb8f2 100644 (file)
@@ -84,6 +84,7 @@ void Managed::operator delete(void *ptr)
     Managed *m = static_cast<Managed *>(ptr);
     m->vtbl = 0;
     m->_data = 0;
+    m->markBit = 0;
     m->~Managed();
 }
 
index e77c724..b4469c8 100644 (file)
@@ -159,7 +159,7 @@ private:
 protected:
     Managed(InternalClass *internal)
         : _data(0), vtbl(&static_vtbl), internalClass(internal)
-    { inUse = 1; extensible = 1; hasAccessorProperty = 0; }
+    { inUse = 1; extensible = 1; }
 
 public:
     void *operator new(size_t size, MemoryManager *mm);
@@ -282,20 +282,24 @@ public:
 
     ReturnedValue asReturnedValue() { return Value::fromManaged(this).asReturnedValue(); }
 
+    enum {
+        SimpleArray = 1
+    };
+
     union {
         uint _data;
         struct {
-            uint markBit :  1;
-            uint inUse   :  1;
-            uint extensible : 1; // used by Object
-            uint isNonStrictArgumentsObject : 1;
-            uint needsActivation : 1; // used by FunctionObject
-            uint strictMode : 1; // used by FunctionObject
-            uint bindingKeyFlag : 1;
-            uint hasAccessorProperty : 1;
-            uint type : 8;
-            mutable uint subtype : 8;
-            uint unused : 8;
+            uchar markBit :  1;
+            uchar inUse   :  1;
+            uchar extensible : 1; // used by Object
+            uchar isNonStrictArgumentsObject : 1;
+            uchar needsActivation : 1; // used by FunctionObject
+            uchar strictMode : 1; // used by FunctionObject
+            uchar bindingKeyFlag : 1;
+            uchar hasAccessorProperty : 1;
+            uchar type;
+            mutable uchar subtype;
+            uchar flags;
         };
     };
 
index 0ccd8e6..795362e 100644 (file)
@@ -76,6 +76,7 @@ Object::Object(ExecutionEngine *engine)
 {
     vtbl = &static_vtbl;
     type = Type_Object;
+    flags = SimpleArray;
     memset(memberData, 0, sizeof(Property)*memberDataAlloc);
 }
 
@@ -86,6 +87,7 @@ Object::Object(InternalClass *internalClass)
 {
     vtbl = &static_vtbl;
     type = Type_Object;
+    flags = SimpleArray;
 
     if (internalClass->size >= memberDataAlloc) {
         memberDataAlloc = internalClass->size;
@@ -1078,6 +1080,7 @@ void Object::copyArrayData(Object *other)
     arrayOffset = 0;
 
     if (other->sparseArray) {
+        flags &= ~SimpleArray;
         sparseArray = new SparseArray(*other->sparseArray);
         arrayFreeList = other->arrayFreeList;
     }
@@ -1226,6 +1229,7 @@ void Object::arraySort(ExecutionContext *context, ObjectRef thisObject, const Va
 void Object::initSparse()
 {
     if (!sparseArray) {
+        flags &= ~SimpleArray;
         sparseArray = new SparseArray;
         for (int i = 0; i < arrayDataLen; ++i) {
             if (!((arrayAttributes && arrayAttributes[i].isGeneric()) || arrayData[i].value.isEmpty())) {
@@ -1302,6 +1306,7 @@ void Object::ensureArrayAttributes()
     if (arrayAttributes)
         return;
 
+    flags &= ~SimpleArray;
     arrayAttributes = new PropertyAttributes[arrayAlloc];
     for (uint i = 0; i < arrayDataLen; ++i)
         arrayAttributes[i] = Attr_Data;
index 05f51be..3206468 100644 (file)
@@ -175,6 +175,7 @@ public:
     {
         type = Type_QmlSequence;
         vtbl = &static_vtbl;
+        flags &= ~SimpleArray;
         QV4::Scope scope(engine);
         QV4::ScopedObject protectThis(scope, this);
         init();
@@ -188,6 +189,7 @@ public:
     {
         type = Type_QmlSequence;
         vtbl = &static_vtbl;
+        flags &= ~SimpleArray;
         QV4::Scope scope(engine);
         QV4::ScopedObject protectThis(scope, this);
         loadReference();
index c1ec5dc..0364659 100644 (file)
@@ -57,6 +57,7 @@ QmlListWrapper::QmlListWrapper(QV8Engine *engine)
       v8(engine)
 {
     vtbl = &static_vtbl;
+    flags &= ~SimpleArray;
 }
 
 QmlListWrapper::~QmlListWrapper()
index 4719fbe..179dcfe 100644 (file)
@@ -3187,6 +3187,7 @@ public:
         : Object(engine)
     {
         vtbl = &static_vtbl;
+        flags &= ~SimpleArray;
     }
     virtual ~QQmlDelegateModelGroupChangeArray() {}
 
index f770430..b9d8730 100644 (file)
@@ -871,6 +871,7 @@ struct QQuickJSContext2DPixelData : public QV4::Object
         : QV4::Object(engine)
     {
         vtbl = &static_vtbl;
+        flags &= ~SimpleArray;
     }
 
     static void destroy(QV4::Managed *that) {