From 7c2adbbb6cccefd202164049dc9144b4d13aec0a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 9 Aug 2013 16:45:02 +0200 Subject: Add reference counting to the VM functions 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 --- src/qml/compiler/qv4isel_p.cpp | 2 +- src/qml/jsruntime/qv4engine.cpp | 5 ++--- src/qml/jsruntime/qv4function.cpp | 10 ++++++++++ src/qml/jsruntime/qv4function_p.h | 18 ++++++++++++++++-- src/qml/jsruntime/qv4functionobject.cpp | 7 +++++++ src/qml/jsruntime/qv4functionobject_p.h | 3 +++ src/qml/jsruntime/qv4script.cpp | 8 +++++++- src/qml/jsruntime/qv4script_p.h | 1 + 8 files changed, 47 insertions(+), 7 deletions(-) (limited to 'src/qml') diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 03c0ee750e..c864378844 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -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) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 0b7c850aa6..acc6d9e132 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -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; diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index bf633a9b41..8c303a21ab 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -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() diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 3ff31ed2e3..612bbb122e 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -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 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(); diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index ffdd08ed0a..e441b1cf0e 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -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); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 8465142616..71691ba1c4 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -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(that)->~FunctionObject(); } }; struct FunctionCtor: FunctionObject diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 3de218a451..8521501e57 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -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 isel(v4->iselFactory->create(v4, &module)); if (inheritContext) isel->setUseFastLookups(false); - if (globalIRCode) + if (globalIRCode) { vmFunction = isel->vmFunction(globalIRCode); + } } if (!vmFunction) diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index 274a87db26..20f9285879 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -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; -- cgit v1.2.3