Add reference counting to the VM functions
authorSimon Hausmann <simon.hausmann@digia.com>
Fri, 9 Aug 2013 14:45:02 +0000 (16:45 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 12 Aug 2013 11:29:27 +0000 (13:29 +0200)
This reduces memory pressure, keep engine->functions small and thus makes back
trace lookup faster. It became visible for example in the QtQuickControls
auto-tests that use plenty of loaders and we ended up with 30k+ functions.

Change-Id: Iaa5981f44e1e49ad9417a50c1e6a74946090dd28
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/qml/compiler/qv4isel_p.cpp
src/qml/jsruntime/qv4engine.cpp
src/qml/jsruntime/qv4function.cpp
src/qml/jsruntime/qv4function_p.h
src/qml/jsruntime/qv4functionobject.cpp
src/qml/jsruntime/qv4functionobject_p.h
src/qml/jsruntime/qv4script.cpp
src/qml/jsruntime/qv4script_p.h

index 03c0ee7..c864378 100644 (file)
@@ -90,7 +90,7 @@ QV4::Function *EvalInstructionSelection::createFunctionMapping(QV4::Function *ou
     vmFunction->sourceFile = irFunction->sourceFile;
 
     if (outer)
-        outer->nestedFunctions.append(vmFunction);
+        outer->addNestedFunction(vmFunction);
 
     foreach (const QString *formal, irFunction->formals)
         if (formal)
index 0b7c850..acc6d9e 100644 (file)
@@ -96,7 +96,7 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory)
     MemoryManager::GCBlocker gcBlocker(memoryManager);
 
     if (!factory) {
-#ifdef V4_ENABLE_JIT
+#if 0
         factory = new QQmlJS::MASM::ISelFactory;
 #else // !V4_ENABLE_JIT
         factory = new QQmlJS::Moth::ISelFactory;
@@ -280,7 +280,6 @@ ExecutionEngine::~ExecutionEngine()
     delete bumperPointerAllocator;
     delete regExpCache;
     UnwindHelper::deregisterFunctions(functions);
-    qDeleteAll(functions);
     delete regExpAllocator;
     delete executableAllocator;
 }
@@ -379,7 +378,7 @@ ExecutionContext *ExecutionEngine::pushGlobalContext()
 
 Function *ExecutionEngine::newFunction(const QString &name)
 {
-    Function *f = new Function(newIdentifier(name));
+    Function *f = new Function(this, newIdentifier(name));
     functions.append(f);
     functionsNeedSort = true;
     return f;
index bf633a9..8c303a2 100644 (file)
@@ -43,6 +43,9 @@
 #include "qv4managed_p.h"
 #include "qv4string_p.h"
 #include "qv4value_p.h"
+#include "qv4engine_p.h"
+#include "qv4lookup_p.h"
+#include "qv4unwindhelper_p.h"
 
 QT_BEGIN_NAMESPACE
 
@@ -50,7 +53,14 @@ using namespace QV4;
 
 Function::~Function()
 {
+    engine->functions.remove(engine->functions.indexOf(this));
+    UnwindHelper::deregisterFunction(this);
+
+    Q_ASSERT(!refCount);
     delete[] codeData;
+    delete[] lookups;
+    foreach (Function *f, nestedFunctions)
+        f->deref();
 }
 
 void Function::mark()
index 3ff31ed..612bbb1 100644 (file)
@@ -87,6 +87,7 @@ struct LineNumberMapping
 };
 
 struct Function {
+    int refCount;
     String *name;
 
     Value (*code)(ExecutionContext *, const uchar *);
@@ -111,8 +112,11 @@ struct Function {
     QString sourceFile;
     QVector<LineNumberMapping> lineNumberMappings;
 
-    Function(String *name)
-        : name(name)
+    ExecutionEngine *engine;
+
+    Function(ExecutionEngine *engine, String *name)
+        : refCount(0)
+        , name(name)
         , code(0)
         , codeData(0)
         , codeSize(0)
@@ -122,9 +126,19 @@ struct Function {
         , usesArgumentsObject(false)
         , isStrict(false)
         , isNamedExpression(false)
+        , engine(engine)
     {}
     ~Function();
 
+    void ref() { ++refCount; }
+    void deref() { if (!--refCount) delete this; }
+
+    void addNestedFunction(Function *f)
+    {
+        f->ref();
+        nestedFunctions.append(f);
+    }
+
     inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; }
 
     void mark();
index ffdd08e..e441b1c 100644 (file)
@@ -93,6 +93,12 @@ FunctionObject::FunctionObject(ExecutionContext *scope, String *name)
          defineReadonlyProperty(scope->engine->id_name, Value::fromString(name));
 }
 
+FunctionObject::~FunctionObject()
+{
+    if (function)
+        function->deref();
+}
+
 Value FunctionObject::newInstance()
 {
     return construct(0, 0);
@@ -318,6 +324,7 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function)
 {
     vtbl = &static_vtbl;
     this->function = function;
+    this->function->ref();
     assert(function);
     assert(function->code);
 
index 8465142..71691ba 100644 (file)
@@ -108,6 +108,7 @@ struct Q_QML_EXPORT FunctionObject: Object {
     Function *function;
 
     FunctionObject(ExecutionContext *scope, String *name = 0);
+    ~FunctionObject();
 
     Value newInstance();
 
@@ -124,6 +125,8 @@ protected:
     static const ManagedVTable static_vtbl;
     static void markObjects(Managed *that);
     static bool hasInstance(Managed *that, const Value &value);
+    static void destroy(Managed *that)
+    { static_cast<FunctionObject*>(that)->~FunctionObject(); }
 };
 
 struct FunctionCtor: FunctionObject
index 3de218a..8521501 100644 (file)
@@ -67,6 +67,7 @@ struct QmlBindingWrapper : FunctionObject
     {
         vtbl = &static_vtbl;
         function = f;
+        function->ref();
         usesArgumentsObject = function->usesArgumentsObject;
         needsActivation = function->needsActivation();
         defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1));
@@ -112,6 +113,10 @@ Value QmlBindingWrapper::call(Managed *that, const Value &, Value *, int)
 }
 
 
+Script::~Script()
+{
+}
+
 void Script::parse()
 {
     if (parsed)
@@ -174,8 +179,9 @@ void Script::parse()
         QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(v4, &module));
         if (inheritContext)
             isel->setUseFastLookups(false);
-        if (globalIRCode)
+        if (globalIRCode) {
             vmFunction = isel->vmFunction(globalIRCode);
+        }
     }
 
     if (!vmFunction)
index 274a87d..20f9285 100644 (file)
@@ -59,6 +59,7 @@ struct Q_QML_EXPORT Script {
         : sourceFile(source), line(line), column(column), sourceCode(sourceCode)
         , scope(engine->rootContext), strictMode(false), inheritContext(true), parsed(false)
         , qml(Value::fromObject(qml)), vmFunction(0), parseAsBinding(true) {}
+    ~Script();
     QString sourceFile;
     int line;
     int column;