Implement Lookup::indexedSetter
authorLars Knoll <lars.knoll@digia.com>
Fri, 24 Jan 2014 12:16:55 +0000 (13:16 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 31 Jan 2014 10:13:48 +0000 (11:13 +0100)
use this instead of the generic runtime method. This gives
around 10% speedup for array heavy Javascript such
as crypto.js.

Change-Id: Ic8f478c5b18893f2ef49e3f658b1ef24ae22e64f
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
src/qml/compiler/qv4compileddata.cpp
src/qml/compiler/qv4compileddata_p.h
src/qml/compiler/qv4compiler.cpp
src/qml/compiler/qv4compiler_p.h
src/qml/compiler/qv4isel_masm.cpp
src/qml/compiler/qv4isel_masm_p.h
src/qml/compiler/qv4isel_p.h
src/qml/jsruntime/qv4lookup.cpp
src/qml/jsruntime/qv4lookup_p.h
src/qml/jsruntime/qv4object_p.h

index 717e7f4..367ada1 100644 (file)
@@ -95,21 +95,24 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
         for (uint i = 0; i < data->lookupTableSize; ++i) {
             QV4::Lookup *l = runtimeLookups + i;
 
-            if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_Getter)
+            Lookup::Type type = Lookup::Type(compiledLookups[i].type_and_flags);
+            if (type == CompiledData::Lookup::Type_Getter)
                 l->getter = QV4::Lookup::getterGeneric;
-            else if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_Setter)
+            else if (type == CompiledData::Lookup::Type_Setter)
                 l->setter = QV4::Lookup::setterGeneric;
-            else if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_GlobalGetter)
+            else if (type == CompiledData::Lookup::Type_GlobalGetter)
                 l->globalGetter = QV4::Lookup::globalGetterGeneric;
-            else if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_IndexedGetter)
+            else if (type == CompiledData::Lookup::Type_IndexedGetter)
                 l->indexedGetter = QV4::Lookup::indexedGetterGeneric;
+            else if (type == CompiledData::Lookup::Type_IndexedSetter)
+                l->indexedSetter = QV4::Lookup::indexedSetterGeneric;
 
             for (int j = 0; j < QV4::Lookup::Size; ++j)
                 l->classList[j] = 0;
             l->level = -1;
             l->index = UINT_MAX;
             l->name = runtimeStrings[compiledLookups[i].nameIndex].asString();
-            if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_IndexedGetter)
+            if (type == CompiledData::Lookup::Type_IndexedGetter || type == CompiledData::Lookup::Type_IndexedSetter)
                 l->engine = engine;
         }
     }
index 29b6fd5..451bf42 100644 (file)
@@ -114,7 +114,8 @@ struct Lookup
         Type_Getter = 0x0,
         Type_Setter = 0x1,
         Type_GlobalGetter = 2,
-        Type_IndexedGetter = 3
+        Type_IndexedGetter = 3,
+        Type_IndexedSetter = 4
     };
 
     quint32 type_and_flags;
index d18c43a..7fb10d5 100644 (file)
@@ -81,6 +81,15 @@ uint QV4::Compiler::JSUnitGenerator::registerIndexedGetterLookup()
     return lookups.size() - 1;
 }
 
+uint QV4::Compiler::JSUnitGenerator::registerIndexedSetterLookup()
+{
+    CompiledData::Lookup l;
+    l.type_and_flags = CompiledData::Lookup::Type_IndexedSetter;
+    l.nameIndex = 0;
+    lookups << l;
+    return lookups.size() - 1;
+}
+
 uint QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name)
 {
     CompiledData::Lookup l;
index 4779b5d..6baefae 100644 (file)
@@ -71,6 +71,7 @@ struct Q_QML_EXPORT JSUnitGenerator {
     uint registerSetterLookup(const QString &name);
     uint registerGlobalGetterLookup(const QString &name);
     uint registerIndexedGetterLookup();
+    uint registerIndexedSetterLookup();
 
     int registerRegExp(QQmlJS::V4IR::RegExp *regexp);
 
index b8b16a4..427a6b7 100644 (file)
@@ -1023,107 +1023,19 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR:
         return;
     }
 
-#if 0 // 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
-        // ### need to check we have an object first!
-        Assembler::Address managedType(Assembler::ScratchRegister, qOffsetOf(QV4::Object, arrayData) + qOffsetOf(QV4::ArrayData, flags));
-        _as->load8(managedType, Assembler::ReturnValueRegister);
-        _as->and32(Assembler::TrustedImm32(QV4::ArrayData::SimpleArray), Assembler::ReturnValueRegister);
-        Assembler::Jump notSimple = _as->branch32(Assembler::Equal, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0));
-
-        bool needNegativeCheck = false;
-        Assembler::Jump fallback, fallback2;
-        if (tindex->kind == V4IR::Temp::PhysicalRegister) {
-            if (tindex->type == V4IR::SInt32Type) {
-                fallback = _as->branch32(Assembler::LessThan, (Assembler::RegisterID)tindex->index, Assembler::TrustedImm32(0));
-                _as->move((Assembler::RegisterID) tindex->index, Assembler::ScratchRegister);
-                needNegativeCheck = true;
-            } else {
-                // double, convert and check if it's a int
-                fallback2 = _as->branchTruncateDoubleToUint32((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);
-            fallback2 = _as->branchTruncateDoubleToUint32(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);
-            needNegativeCheck = true;
-        }
-
-        // get data, ScratchRegister holds index
-        addr = _as->loadTempAddress(Assembler::ReturnValueRegister, tbase);
-        _as->load64(addr, Assembler::ReturnValueRegister);
-        Address dataLen(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData) + qOffsetOf(ArrayData, length));
-        Assembler::Jump outOfRange;
-        if (needNegativeCheck)
-            outOfRange = _as->branch32(Assembler::LessThan, Assembler::ScratchRegister, Assembler::TrustedImm32(0));
-        Assembler::Jump outOfRange2 = _as->branch32(Assembler::GreaterThanOrEqual, Assembler::ScratchRegister, dataLen);
-        Address arrayData(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData) + qOffsetOf(ArrayData, data));
-        _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);
-        if (outOfRange.isSet())
-            outOfRange.link(_as);
-        outOfRange2.link(_as);
-        if (fallback.isSet())
-            fallback.link(_as);
-        if (fallback2.isSet())
-            fallback2.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));
 }
 
 void InstructionSelection::setElement(V4IR::Expr *source, V4IR::Expr *targetBase, V4IR::Expr *targetIndex)
 {
+    if (useFastLookups) {
+        uint lookup = registerIndexedSetterLookup();
+        generateLookupCall(Assembler::Void, lookup, qOffsetOf(QV4::Lookup, indexedSetter),
+                           Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex),
+                           Assembler::PointerToValue(source));
+        return;
+    }
     generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister,
                          Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex),
                          Assembler::PointerToValue(source));
index a146220..f22e093 100644 (file)
@@ -1580,8 +1580,8 @@ private:
     int prepareVariableArguments(V4IR::ExprList* args);
     int prepareCallData(V4IR::ExprList* args, V4IR::Expr *thisObject);
 
-    template <typename Retval, typename Arg1, typename Arg2>
-    void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2)
+    template <typename Retval, typename Arg1, typename Arg2, typename Arg3>
+    void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3)
     {
         Assembler::RegisterID lookupRegister;
 #if CPU(ARM)
@@ -1595,13 +1595,13 @@ private:
         getterSetter.offset += getterSetterOffset;
 
          _as->generateFunctionCallImp(retval, "lookup getter/setter",
-                                      RelativeCall(getterSetter), lookupAddr, arg1, arg2);
+                                      RelativeCall(getterSetter), lookupAddr, arg1, arg2, arg3);
     }
 
-    template <typename Arg1>
-    void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1)
+    template <typename Retval, typename Arg1, typename Arg2>
+    void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2)
     {
-        generateLookupCall(index, getterSetterOffset, arg1, Assembler::VoidType());
+        generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, Assembler::VoidType());
     }
 
     /// This is a temporary method, and will be removed when registers are fully supported.
index 6f33098..5722267 100644 (file)
@@ -73,6 +73,7 @@ public:
 
     int registerString(const QString &str) { return jsGenerator->registerString(str); }
     uint registerIndexedGetterLookup() { return jsGenerator->registerIndexedGetterLookup(); }
+    uint registerIndexedSetterLookup() { return jsGenerator->registerIndexedSetterLookup(); }
     uint registerGetterLookup(const QString &name) { return jsGenerator->registerGetterLookup(name); }
     uint registerSetterLookup(const QString &name) { return jsGenerator->registerSetterLookup(name); }
     uint registerGlobalGetterLookup(const QString &name) { return jsGenerator->registerGlobalGetterLookup(name); }
index c9d2252..4a75272 100644 (file)
@@ -180,6 +180,63 @@ ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, const ValueRef object, c
     return indexedGetterFallback(l, object, index);
 }
 
+void Lookup::indexedSetterGeneric(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef v)
+{
+    if (object->isObject()) {
+        Object *o = object->objectValue();
+        if (o->arrayData && o->arrayData->type == ArrayData::Simple && index->asArrayIndex() < UINT_MAX) {
+            l->indexedSetter = indexedSetterObjectInt;
+            indexedSetterObjectInt(l, object, index, v);
+            return;
+        }
+    }
+    indexedSetterFallback(l, object, index, v);
+}
+
+void Lookup::indexedSetterFallback(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef value)
+{
+    ExecutionContext *ctx = l->engine->currentContext();
+    Scope scope(ctx);
+    ScopedObject o(scope, object->toObject(ctx));
+    if (scope.engine->hasException)
+        return;
+
+    uint idx = index->asArrayIndex();
+    if (idx < UINT_MAX) {
+        if (o->arrayData && o->arrayData->type == ArrayData::Simple) {
+            SimpleArrayData *s = static_cast<SimpleArrayData *>(o->arrayData);
+            if (s && idx < s->len && !s->data[idx].isEmpty()) {
+                s->data[idx] = value;
+                return;
+            }
+        }
+        o->putIndexed(idx, value);
+        return;
+    }
+
+    ScopedString name(scope, index->toString(ctx));
+    o->put(name, value);
+}
+
+void Lookup::indexedSetterObjectInt(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef v)
+{
+    uint idx = index->asArrayIndex();
+    if (idx == UINT_MAX || !object->isObject()) {
+        indexedSetterGeneric(l, object, index, v);
+        return;
+    }
+
+    Object *o = object->objectValue();
+    if (o->arrayData && o->arrayData->type == ArrayData::Simple) {
+        SimpleArrayData *s = static_cast<SimpleArrayData *>(o->arrayData);
+        if (idx < s->len && !s->data[idx].isEmpty()) {
+            s->data[idx] = v;
+            return;
+        }
+    }
+    indexedSetterFallback(l, object, index, v);
+}
+
 ReturnedValue Lookup::getterGeneric(QV4::Lookup *l, const ValueRef object)
 {
     if (Object *o = object->asObject())
index 5087441..7f107bf 100644 (file)
@@ -56,6 +56,7 @@ struct Lookup {
     enum { Size = 4 };
     union {
         ReturnedValue (*indexedGetter)(Lookup *l, const ValueRef object, const ValueRef index);
+        void (*indexedSetter)(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef v);
         ReturnedValue (*getter)(Lookup *l, const ValueRef object);
         ReturnedValue (*globalGetter)(Lookup *l, ExecutionContext *ctx);
         void (*setter)(Lookup *l, const ValueRef object, const ValueRef v);
@@ -78,6 +79,10 @@ struct Lookup {
     static ReturnedValue indexedGetterFallback(Lookup *l, const ValueRef object, const ValueRef index);
     static ReturnedValue indexedGetterObjectInt(Lookup *l, const ValueRef object, const ValueRef index);
 
+    static void indexedSetterGeneric(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef v);
+    static void indexedSetterFallback(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef value);
+    static void indexedSetterObjectInt(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef v);
+
     static ReturnedValue getterGeneric(Lookup *l, const ValueRef object);
     static ReturnedValue getter0(Lookup *l, const ValueRef object);
     static ReturnedValue getter1(Lookup *l, const ValueRef object);
index ca54388..7541178 100644 (file)
@@ -206,7 +206,7 @@ public:
     void push_back(const ValueRef v);
 
     ArrayData::Type arrayType() const {
-        return arrayData ? (ArrayData::Type)arrayData->type : ArrayData::Simple;
+        return arrayData ? arrayData->type : ArrayData::Simple;
     }
     // ### remove me
     void setArrayType(ArrayData::Type t) {