Speed up object literal construction
authorSimon Hausmann <simon.hausmann@digia.com>
Tue, 23 Apr 2013 05:31:02 +0000 (07:31 +0200)
committerLars Knoll <lars.knoll@digia.com>
Sun, 28 Apr 2013 19:18:09 +0000 (21:18 +0200)
Define the internal class at compile (isel) time, so that creation
of the object at run-time is very fast.

Change-Id: Ie153cda695cefde9d7118a7a65f1ff7e78f120cc
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
17 files changed:
src/qml/qml/v4vm/moth/qv4instr_moth_p.h
src/qml/qml/v4vm/moth/qv4isel_moth.cpp
src/qml/qml/v4vm/moth/qv4isel_moth_p.h
src/qml/qml/v4vm/moth/qv4vme_moth.cpp
src/qml/qml/v4vm/qv4codegen.cpp
src/qml/qml/v4vm/qv4engine.cpp
src/qml/qml/v4vm/qv4engine_p.h
src/qml/qml/v4vm/qv4isel_masm.cpp
src/qml/qml/v4vm/qv4isel_masm_p.h
src/qml/qml/v4vm/qv4isel_p.cpp
src/qml/qml/v4vm/qv4isel_p.h
src/qml/qml/v4vm/qv4jsir.cpp
src/qml/qml/v4vm/qv4jsir_p.h
src/qml/qml/v4vm/qv4object.cpp
src/qml/qml/v4vm/qv4object_p.h
src/qml/qml/v4vm/qv4runtime.cpp
src/qml/qml/v4vm/qv4runtime_p.h

index 1c2dd6f..ed762af 100644 (file)
@@ -46,6 +46,7 @@
     F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \
     F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \
     F(CallBuiltinDefineArray, callBuiltinDefineArray) \
+    F(CallBuiltinDefineObjectLiteral, callBuiltinDefineObjectLiteral) \
     F(CreateValue, createValue) \
     F(CreateProperty, createProperty) \
     F(CreateActivationProperty, createActivationProperty) \
@@ -376,6 +377,12 @@ union Instr
         quint32 args;
         Param result;
     };
+    struct instr_callBuiltinDefineObjectLiteral {
+        MOTH_INSTR_HEADER
+        QV4::InternalClass *internalClass;
+        quint32 args;
+        Param result;
+    };
     struct instr_createValue {
         MOTH_INSTR_HEADER
         quint32 argc;
@@ -487,6 +494,7 @@ union Instr
     instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter;
     instr_callBuiltinDefineProperty callBuiltinDefineProperty;
     instr_callBuiltinDefineArray callBuiltinDefineArray;
+    instr_callBuiltinDefineObjectLiteral callBuiltinDefineObjectLiteral;
     instr_createValue createValue;
     instr_createProperty createProperty;
     instr_createActivationProperty createActivationProperty;
index 2425008..678127a 100644 (file)
@@ -750,6 +750,46 @@ void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::Expr
     addInstruction(call);
 }
 
+void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args)
+{
+    int argLocation = outgoingArgumentTempStart();
+
+    QV4::InternalClass *klass = engine()->emptyClass;
+    V4IR::ExprList *it = args;
+    while (it) {
+        V4IR::Name *name = it->expr->asName();
+        it = it->next;
+
+        bool isData = it->expr->asConst()->value;
+        it = it->next;
+        klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor);
+
+        Instruction::MoveTemp move;
+        move.source = getParam(it->expr);
+        move.result = Instr::Param::createTemp(argLocation);
+        addInstruction(move);
+        ++argLocation;
+
+        if (!isData) {
+            it = it->next;
+
+            Instruction::MoveTemp move;
+            move.source = getParam(it->expr);
+            move.result = Instr::Param::createTemp(argLocation);
+            addInstruction(move);
+            ++argLocation;
+        }
+
+        it = it->next;
+    }
+
+    Instruction::CallBuiltinDefineObjectLiteral call;
+    call.internalClass = klass;
+    call.args = outgoingArgumentTempStart();
+    call.result = getResultParam(result);
+    addInstruction(call);
+}
+
 ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr)
 {
 #ifdef MOTH_THREADED_INTERPRETER
index 8bbcc2f..23ca930 100644 (file)
@@ -53,6 +53,7 @@ protected:
     virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter);
     virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value);
     virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args);
+    virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args);
     virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result);
     virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result);
     virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result);
index a4dde96..4a7e189 100644 (file)
@@ -434,6 +434,11 @@ QV4::Value VME::run(QV4::ExecutionContext *context, const uchar *&code,
         __qmljs_builtin_define_array(context, VALUEPTR(instr.result), args, instr.argc);
     MOTH_END_INSTR(CallBuiltinDefineArray)
 
+    MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral)
+        QV4::Value *args = stack + instr.args;
+        __qmljs_builtin_define_object_literal(context, VALUEPTR(instr.result), args, instr.internalClass);
+    MOTH_END_INSTR(CallBuiltinDefineObjectLiteral)
+
     MOTH_BEGIN_INSTR(CreateValue)
         Q_ASSERT(instr.args + instr.argc <= stackSize);
         QV4::Value *args = stack + instr.args;
index 6942761..6196a03 100644 (file)
@@ -821,6 +821,14 @@ protected:
 
     virtual bool visit(ObjectLiteral *ast)
     {
+        int argc = 0;
+        for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
+            ++argc;
+            if (AST::cast<AST::PropertyGetterSetter *>(it->assignment))
+                ++argc;
+        }
+        _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc);
+
         TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
         Node::accept(ast->properties, this);
         return false;
@@ -2070,8 +2078,6 @@ bool Codegen::visit(ObjectLiteral *ast)
 {
     QMap<QString, ObjectPropertyValue> valueMap;
 
-    const unsigned t = _block->newTemp();
-    move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn)));
     for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
         if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) {
             QString name = propertyName(nv->name);
@@ -2101,6 +2107,65 @@ bool Codegen::visit(ObjectLiteral *ast)
             Q_UNREACHABLE();
         }
     }
+
+    V4IR::ExprList *args = 0;
+
+    if (!valueMap.isEmpty()) {
+        V4IR::ExprList *current;
+        for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(); it != valueMap.end(); ) {
+            if (QV4::String(it.key()).asArrayIndex() != UINT_MAX) {
+                ++it;
+                continue;
+            }
+
+            if (!args) {
+                args = _function->New<V4IR::ExprList>();
+                current = args;
+            } else {
+                current->next = _function->New<V4IR::ExprList>();
+                current = current->next;
+            }
+
+            current->expr = _block->NAME(it.key(), 0, 0);
+
+            if (it->value) {
+                current->next = _function->New<V4IR::ExprList>();
+                current = current->next;
+                current->expr = _block->CONST(V4IR::BoolType, true);
+
+                unsigned value = _block->newTemp();
+                move(_block->TEMP(value), it->value);
+
+                current->next = _function->New<V4IR::ExprList>();
+                current = current->next;
+                current->expr = _block->TEMP(value);
+            } else {
+                current->next = _function->New<V4IR::ExprList>();
+                current = current->next;
+                current->expr = _block->CONST(V4IR::BoolType, false);
+
+                unsigned getter = _block->newTemp();
+                unsigned setter = _block->newTemp();
+                move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(V4IR::UndefinedType, 0));
+                move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(V4IR::UndefinedType, 0));
+
+                current->next = _function->New<V4IR::ExprList>();
+                current = current->next;
+                current->expr = _block->TEMP(getter);
+                current->next = _function->New<V4IR::ExprList>();
+                current = current->next;
+                current->expr = _block->TEMP(setter);
+            }
+
+            it = valueMap.erase(it);
+        }
+    }
+
+    const unsigned t = _block->newTemp();
+    move(_block->TEMP(t), _block->CALL(_block->NAME(V4IR::Name::builtin_define_object_literal,
+         ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args));
+
+    // What's left are array entries
     if (!valueMap.isEmpty()) {
         unsigned value = 0;
         unsigned getter = 0;
index 9e9a7fc..ac28069 100644 (file)
@@ -363,6 +363,13 @@ Object *ExecutionEngine::newObject()
     return object;
 }
 
+Object *ExecutionEngine::newObject(InternalClass *internalClass)
+{
+    Object *object = new (memoryManager) Object(this, internalClass);
+    object->prototype = objectPrototype;
+    return object;
+}
+
 String *ExecutionEngine::newString(const QString &s)
 {
     return new (memoryManager) String(s);
index 02e470d..dc9783f 100644 (file)
@@ -216,6 +216,7 @@ struct Q_QML_EXPORT ExecutionEngine
     BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs);
 
     Object *newObject();
+    Object *newObject(InternalClass *internalClass);
 
     String *newString(const QString &s);
     String *newIdentifier(const QString &text);
index 9e6ba84..fd034a5 100644 (file)
@@ -813,6 +813,37 @@ void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::Expr
                          baseAddressForCallArguments(), Assembler::TrustedImm32(length));
 }
 
+void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args)
+{
+    int argc = 0;
+
+    InternalClass *klass = engine()->emptyClass;
+    V4IR::ExprList *it = args;
+    while (it) {
+        V4IR::Name *name = it->expr->asName();
+        it = it->next;
+
+        bool isData = it->expr->asConst()->value;
+        it = it->next;
+        klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor);
+
+        _as->copyValue(argumentAddressForCall(argc++), it->expr);
+
+        if (!isData) {
+            it = it->next;
+            _as->copyValue(argumentAddressForCall(argc++), it->expr);
+        }
+
+        it = it->next;
+    }
+
+    _as->move(Assembler::TrustedImmPtr(klass), Assembler::ReturnValueRegister);
+
+    generateFunctionCall(Assembler::Void, __qmljs_builtin_define_object_literal, Assembler::ContextRegister,
+                         Assembler::PointerToValue(result), baseAddressForCallArguments(),
+                         Assembler::ReturnValueRegister);
+}
+
 void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result)
 {
     int argc = prepareVariableArguments(args);
index 505cafc..8e1740e 100644 (file)
@@ -813,6 +813,7 @@ protected:
     virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter);
     virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value);
     virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args);
+    virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args);
     virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result);
     virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result);
     virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result);
index e190833..478fea4 100644 (file)
@@ -387,6 +387,10 @@ void InstructionSelection::callBuiltin(V4IR::Call *call, V4IR::Temp *result)
         callBuiltinDefineArray(result, call->args);
         return;
 
+    case V4IR::Name::builtin_define_object_literal:
+        callBuiltinDefineObjectLiteral(result, call->args);
+        return;
+
     default:
         break;
     }
index 59a4b76..b3efacc 100644 (file)
@@ -124,6 +124,7 @@ public: // to implement by subclasses:
     virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) = 0;
     virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) = 0;
     virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) = 0;
+    virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) = 0;
     virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0;
     virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0;
     virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) = 0;
index 3dad094..e94aa1b 100644 (file)
@@ -388,6 +388,8 @@ static const char *builtin_to_string(Name::Builtin b)
         return "builtin_define_array";
     case V4IR::Name::builtin_define_getter_setter:
         return "builtin_define_getter_setter";
+    case V4IR::Name::builtin_define_object_literal:
+        return "builtin_define_object_literal";
     }
     return "builtin_(###FIXME)";
 };
index 1285c71..cc0f2e9 100644 (file)
@@ -299,7 +299,8 @@ struct Name: Expr {
         builtin_declare_vars,
         builtin_define_property,
         builtin_define_array,
-        builtin_define_getter_setter
+        builtin_define_getter_setter,
+        builtin_define_object_literal
     };
 
     const QString *id;
index fc54899..42ec8f2 100644 (file)
@@ -89,6 +89,22 @@ Object::Object(ExecutionContext *context)
     type = Type_Object;
 }
 
+Object::Object(ExecutionEngine *engine, InternalClass *internalClass)
+    : prototype(0)
+    , internalClass(internalClass)
+    , memberDataAlloc(InlinePropertySize), memberData(inlineProperties)
+    , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0)
+    , externalResource(0)
+{
+    vtbl = &static_vtbl;
+    type = Type_Object;
+
+    if (internalClass->size >= memberDataAlloc) {
+        memberDataAlloc = internalClass->size;
+        memberData = new Property[memberDataAlloc];
+    }
+}
+
 Object::~Object()
 {
     delete externalResource;
index a273a47..675c149 100644 (file)
@@ -130,6 +130,7 @@ struct Q_QML_EXPORT Object: Managed {
 
     Object(ExecutionEngine *engine);
     Object(ExecutionContext *context);
+    Object(ExecutionEngine *engine, InternalClass *internalClass);
     ~Object();
 
     Property *__getOwnProperty__(String *name, PropertyAttributes *attrs = 0);
index 0adb99d..b510d4c 100644 (file)
@@ -1263,6 +1263,24 @@ void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &ob
     pd->setSetter(setter ? setter->asFunctionObject() : 0);
 }
 
+void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, QV4::InternalClass *klass)
+{
+    Object *o = ctx->engine->newObject(klass);
+
+    for (int i = 0; i < klass->size; ++i) {
+        if (klass->propertyData[i].isData())
+            o->memberData[i].value = *args++;
+        else {
+            o->memberData[i].setGetter(args->asFunctionObject());
+            args++;
+            o->memberData[i].setSetter(args->asFunctionObject());
+            args++;
+        }
+    }
+
+    *result = Value::fromObject(o);
+}
+
 void __qmljs_increment(Value *result, const Value &value)
 {
     TRACE1(value);
index 755e8b7..61a6bb0 100644 (file)
@@ -86,6 +86,7 @@ struct RegExpObject;
 struct ArrayObject;
 struct ErrorObject;
 struct ExecutionEngine;
+struct InternalClass;
 
 struct Q_QML_EXPORT Exception {
     explicit Exception(ExecutionContext *throwingContext, const Value &exceptionValue);
@@ -144,6 +145,7 @@ void __qmljs_builtin_declare_var(QV4::ExecutionContext *ctx, bool deletable, QV4
 void __qmljs_builtin_define_property(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, QV4::Value *val);
 void __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, QV4::Value *array, QV4::Value *values, uint length);
 void __qmljs_builtin_define_getter_setter(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, const QV4::Value *getter, const QV4::Value *setter);
+void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, QV4::InternalClass *klass);
 
 // constructors
 void __qmljs_init_closure(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::Function *clos);