From 5f22fbd7fc4ca6a7f4629cbd34e0fc2e3c1b1cee Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 4 Dec 2012 13:40:18 +0100 Subject: [PATCH] Add a MemoryManager, which does GC for the interpreter. Todo: - stack walking for MASM - fix all TODOs/FIXMEs and hidden treasures (bugs). Change-Id: I36f8cdc3a545df7287ce1df17b3570a9c017865e Reviewed-by: Lars Knoll --- main.cpp | 23 ++-- moth/moth.pri | 2 + moth/qv4mm_moth.cpp | 81 +++++++++++ moth/qv4mm_moth.h | 59 ++++++++ moth/qv4vme_moth.cpp | 43 +++--- qmljs_engine.cpp | 136 +++++++++--------- qmljs_engine.h | 4 +- qmljs_environment.cpp | 6 +- qmljs_objects.cpp | 65 ++++++++- qmljs_objects.h | 36 ++++- qv4array_p.h | 13 +- qv4codegen.cpp | 5 +- qv4ecmaobjects.cpp | 23 ++-- qv4ir.cpp | 16 ++- qv4ir_p.h | 3 + qv4mm.cpp | 375 ++++++++++++++++++++++++++++++++++++++++++++++++++ qv4mm.h | 184 +++++++++++++++++++++++++ v4.pro | 6 +- 18 files changed, 972 insertions(+), 108 deletions(-) create mode 100644 moth/qv4mm_moth.cpp create mode 100644 moth/qv4mm_moth.h create mode 100644 qv4mm.cpp create mode 100644 qv4mm.h diff --git a/main.cpp b/main.cpp index 6f6d512..decb799 100644 --- a/main.cpp +++ b/main.cpp @@ -53,6 +53,7 @@ #include "qv4syntaxchecker_p.h" #include "qv4ecmaobjects_p.h" #include "qv4isel_p.h" +#include "qv4mm_moth.h" #include #include @@ -144,12 +145,12 @@ int executeLLVMCode(void *codePtr) void (*code)(VM::ExecutionContext *) = (void (*)(VM::ExecutionContext *)) codePtr; QScopedPointer iSelFactory(new QQmlJS::Moth::ISelFactory); - VM::ExecutionEngine vm(iSelFactory.data()); + VM::ExecutionEngine vm(0, iSelFactory.data()); VM::ExecutionContext *ctx = vm.rootContext; QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); + QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx))); void * buf = __qmljs_create_exception_handler(ctx); if (setjmp(*(jmp_buf *)buf)) { @@ -318,13 +319,17 @@ int main(int argc, char *argv[]) #endif // QMLJS_NO_LLVM case use_masm: case use_moth: { + QScopedPointer mm; QScopedPointer iSelFactory; - if (mode == use_moth) + if (mode == use_moth) { + mm.reset(new QQmlJS::Moth::MemoryManager); iSelFactory.reset(new QQmlJS::Moth::ISelFactory); - else + } else { + mm.reset(new QQmlJS::VM::MemoryManagerWithoutGC); iSelFactory.reset(new QQmlJS::MASM::ISelFactory); + } - QQmlJS::VM::ExecutionEngine vm(iSelFactory.data()); + QQmlJS::VM::ExecutionEngine vm(mm.data(), iSelFactory.data()); QScopedPointer debugger; if (enableDebugging) @@ -335,12 +340,12 @@ int main(int argc, char *argv[]) QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); + QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx))); bool errorInTestHarness = false; if (!qgetenv("IN_TEST_HARNESS").isEmpty()) globalObject->__put__(ctx, vm.identifier(QStringLiteral("$ERROR")), - QQmlJS::VM::Value::fromObject(new builtins::TestHarnessError(ctx, errorInTestHarness))); + QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::TestHarnessError(ctx, errorInTestHarness))); foreach (const QString &fn, args) { QFile file(fn); @@ -354,7 +359,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - QQmlJS::IR::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode); + QScopedPointer f(QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode)); if (!f) continue; @@ -376,6 +381,8 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } } + + mm->dumpStats(); } return EXIT_SUCCESS; } } diff --git a/moth/moth.pri b/moth/moth.pri index 4381c9a..160c1aa 100644 --- a/moth/moth.pri +++ b/moth/moth.pri @@ -4,10 +4,12 @@ HEADERS += \ $$PWD/qv4isel_moth_p.h \ $$PWD/qv4instr_moth_p.h \ $$PWD/qv4vme_moth_p.h \ + $$PWD/qv4mm_moth.h SOURCES += \ $$PWD/qv4isel_moth.cpp \ $$PWD/qv4instr_moth.cpp \ $$PWD/qv4vme_moth.cpp \ + $$PWD/qv4mm_moth.cpp #DEFINES += DO_TRACE_INSTR diff --git a/moth/qv4mm_moth.cpp b/moth/qv4mm_moth.cpp new file mode 100644 index 0000000..44e45fe --- /dev/null +++ b/moth/qv4mm_moth.cpp @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmljs_engine.h" +#include "qv4mm_moth.h" + +#include + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +MemoryManager::MemoryManager() +{ + stackFrames.reserve(64); +} + +MemoryManager::~MemoryManager() +{ +} + +VM::Value *MemoryManager::allocStackFrame(std::size_t frameSize) +{ + std::size_t size = frameSize * sizeof(VM::Value); + MMObject *m = alloc(align(size)); + stackFrames.append(m); + return reinterpret_cast(&m->data); +} + +void MemoryManager::deallocStackFrame(VM::Value *stackFrame) +{ + MMObject *o = toObject(stackFrame); + for (int i = stackFrames.size() - 1; i >= 0; --i) { + if (stackFrames[i] == o) { + stackFrames.remove(i); + dealloc(o); + return; + } + } + + Q_UNREACHABLE(); +} + +void MemoryManager::collectRootsOnStack(QVector &roots) const +{ + for (int i = 0, ei = stackFrames.size(); i < ei; ++i) { + MMObject *m = stackFrames[i]; + VM::Value *frame = reinterpret_cast(&m->data); + std::size_t frameSize = (m->info.size - align(sizeof(MMInfo))) / sizeof(VM::Value); + for (std::size_t j = 0; j < frameSize; ++j) { + if (VM::Object *o = frame[j].asObject()) { + roots.append(o); + } + } + } +} diff --git a/moth/qv4mm_moth.h b/moth/qv4mm_moth.h new file mode 100644 index 0000000..a2b1ebd --- /dev/null +++ b/moth/qv4mm_moth.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4GC_MOTH_H +#define QV4GC_MOTH_H + +#include "qv4mm.h" + +#include + +namespace QQmlJS { +namespace Moth { + +class MemoryManager: public QQmlJS::VM::MemoryManager +{ +public: + MemoryManager(); + ~MemoryManager(); + + VM::Value *allocStackFrame(std::size_t frameSize); + void deallocStackFrame(VM::Value *stackFrame); + +protected: + virtual void collectRootsOnStack(QVector &roots) const; + +private: + QVector stackFrames; +}; + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4GC_MOTH_H diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index d38d7d7..86b396f 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -2,6 +2,7 @@ #include "qv4instr_moth_p.h" #include "qmljs_value.h" #include "debugging.h" +#include "qv4mm_moth.h" #include @@ -51,7 +52,7 @@ using namespace QQmlJS::Moth; #endif -static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVector &stack, int index) +static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, VM::Value* stack, int index) { #ifdef DO_TRACE_INSTR const char *kind; @@ -87,25 +88,32 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVecto int off = index - context->variableCount(); Q_ASSERT(off >= 0); - Q_ASSERT(off < stack.size()); - return stack.data() + off; + return stack + off; } } class FunctionState: public Debugging::FunctionState { public: - FunctionState(QQmlJS::VM::ExecutionContext *context, QVector *stack, const uchar **code) + FunctionState(QQmlJS::VM::ExecutionContext *context, const uchar **code) : Debugging::FunctionState(context) - , stack(stack) + , stack(0) + , stackSize(0) , code(code) {} - virtual VM::Value *temp(unsigned idx) { return stack->data() + idx; } + ~FunctionState() + { if (stack) static_cast(context()->engine->memoryManager)->deallocStackFrame(stack); } + + virtual VM::Value *temp(unsigned idx) { return stack + idx; } + + void setStack(VM::Value *stack, unsigned stackSize) + { this->stack = stack; this->stackSize = stackSize; } private: - QVector *stack; + VM::Value *stack; + unsigned stackSize; const uchar **code; }; @@ -130,8 +138,9 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co } #endif - QVector stack; - FunctionState state(context, &stack, &code); + VM::Value *stack = 0; + unsigned stackSize = 0; + FunctionState state(context, &code); #ifdef MOTH_THREADED_INTERPRETER const Instr *genericInstr = reinterpret_cast(code); @@ -191,7 +200,9 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(Push) TRACE(inline, "stack size: %u", instr.value); - stack.resize(instr.value); + stackSize = instr.value; + stack = static_cast(context->engine->memoryManager)->allocStackFrame(stackSize); + state.setStack(stack, stackSize); MOTH_END_INSTR(Push) MOTH_BEGIN_INSTR(CallValue) @@ -207,20 +218,20 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co #endif // DO_TRACE_INSTR quint32 argStart = instr.args - context->variableCount(); TRACE(Call, "value index = %d, argStart = %d, argc = %d, result temp index = %d", instr.destIndex, argStart, instr.argc, instr.targetTempIndex); - VM::Value *args = stack.data() + argStart; + VM::Value *args = stack + argStart; TEMP(instr.targetTempIndex) = __qmljs_call_value(context, VM::Value::undefinedValue(), TEMP(instr.destIndex), args, instr.argc); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) quint32 argStart = instr.args - context->variableCount(); - VM::Value *args = stack.data() + argStart; + VM::Value *args = stack + argStart; VM::Value base = TEMP(instr.baseTemp); TEMP(instr.targetTempIndex) = __qmljs_call_property(context, base, instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallBuiltin) quint32 argStart = instr.args - context->variableCount(); - VM::Value *args = stack.data() + argStart; + VM::Value *args = stack + argStart; void *buf; switch (instr.builtin) { case Instr::instr_callBuiltin::builtin_typeof: @@ -300,20 +311,20 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(CreateValue) quint32 argStart = instr.args - context->variableCount(); - VM::Value *args = stack.data() + argStart; + VM::Value *args = stack + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) quint32 argStart = instr.args - context->variableCount(); - VM::Value *args = stack.data() + argStart; + VM::Value *args = stack + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_property(context, TEMP(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(CreateActivationProperty) TRACE(inline, "property name = %s, argc = %d", instr.name->toQString().toUtf8().constData(), instr.argc); quint32 argStart = instr.args - context->variableCount(); - VM::Value *args = stack.data() + argStart; + VM::Value *args = stack + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); MOTH_END_INSTR(CreateActivationProperty) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 6617810..ea6b32a 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -41,6 +41,7 @@ #include #include #include +#include "qv4mm.h" namespace QQmlJS { namespace VM { @@ -49,6 +50,9 @@ struct StringPool { QHash strings; + ~StringPool() + { qDeleteAll(strings.values()); } + String *newString(const QString &s) { QHash::const_iterator it = strings.find(s); @@ -60,11 +64,18 @@ struct StringPool } }; -ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) - : iselFactory(factory) +ExecutionEngine::ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory *factory) + : memoryManager(memoryManager) + , iselFactory(factory) , debugger(0) + , globalObject(Value::nullValue()) + , exception(Value::nullValue()) { + MemoryManager::GCBlocker gcBlocker(memoryManager); + stringPool = new StringPool; + memoryManager->setStringPool(stringPool); + memoryManager->setExecutionEngine(this); rootContext = newContext(); rootContext->init(this); @@ -75,21 +86,21 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) id_arguments = identifier(QStringLiteral("arguments")); id___proto__ = identifier(QStringLiteral("__proto__")); - objectPrototype = new ObjectPrototype(); - stringPrototype = new StringPrototype(rootContext); - numberPrototype = new NumberPrototype(); - booleanPrototype = new BooleanPrototype(); - arrayPrototype = new ArrayPrototype(); - datePrototype = new DatePrototype(); - functionPrototype = new FunctionPrototype(rootContext); - regExpPrototype = new RegExpPrototype(); - errorPrototype = new ErrorPrototype(); - evalErrorPrototype = new EvalErrorPrototype(rootContext); - rangeErrorPrototype = new RangeErrorPrototype(rootContext); - referenceErrorPrototype = new ReferenceErrorPrototype(rootContext); - syntaxErrorPrototype = new SyntaxErrorPrototype(rootContext); - typeErrorPrototype = new TypeErrorPrototype(rootContext); - uRIErrorPrototype = new URIErrorPrototype(rootContext); + objectPrototype = new (memoryManager) ObjectPrototype(); + stringPrototype = new (memoryManager) StringPrototype(rootContext); + numberPrototype = new (memoryManager) NumberPrototype(); + booleanPrototype = new (memoryManager) BooleanPrototype(); + arrayPrototype = new (memoryManager) ArrayPrototype(); + datePrototype = new (memoryManager) DatePrototype(); + functionPrototype = new (memoryManager) FunctionPrototype(rootContext); + regExpPrototype = new (memoryManager) RegExpPrototype(); + errorPrototype = new (memoryManager) ErrorPrototype(); + evalErrorPrototype = new (memoryManager) EvalErrorPrototype(rootContext); + rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(rootContext); + referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(rootContext); + syntaxErrorPrototype = new (memoryManager) SyntaxErrorPrototype(rootContext); + typeErrorPrototype = new (memoryManager) TypeErrorPrototype(rootContext); + uRIErrorPrototype = new (memoryManager) URIErrorPrototype(rootContext); stringPrototype->prototype = objectPrototype; numberPrototype->prototype = objectPrototype; @@ -106,21 +117,21 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) typeErrorPrototype->prototype = errorPrototype; uRIErrorPrototype->prototype = errorPrototype; - objectCtor = Value::fromObject(new ObjectCtor(rootContext)); - stringCtor = Value::fromObject(new StringCtor(rootContext)); - numberCtor = Value::fromObject(new NumberCtor(rootContext)); - booleanCtor = Value::fromObject(new BooleanCtor(rootContext)); - arrayCtor = Value::fromObject(new ArrayCtor(rootContext)); - functionCtor = Value::fromObject(new FunctionCtor(rootContext)); - dateCtor = Value::fromObject(new DateCtor(rootContext)); - regExpCtor = Value::fromObject(new RegExpCtor(rootContext)); - errorCtor = Value::fromObject(new ErrorCtor(rootContext)); - evalErrorCtor = Value::fromObject(new EvalErrorCtor(rootContext)); - rangeErrorCtor = Value::fromObject(new RangeErrorCtor(rootContext)); - referenceErrorCtor = Value::fromObject(new ReferenceErrorCtor(rootContext)); - syntaxErrorCtor = Value::fromObject(new SyntaxErrorCtor(rootContext)); - typeErrorCtor = Value::fromObject(new TypeErrorCtor(rootContext)); - uRIErrorCtor = Value::fromObject(new URIErrorCtor(rootContext)); + objectCtor = Value::fromObject(new (memoryManager) ObjectCtor(rootContext)); + stringCtor = Value::fromObject(new (memoryManager) StringCtor(rootContext)); + numberCtor = Value::fromObject(new (memoryManager) NumberCtor(rootContext)); + booleanCtor = Value::fromObject(new (memoryManager) BooleanCtor(rootContext)); + arrayCtor = Value::fromObject(new (memoryManager) ArrayCtor(rootContext)); + functionCtor = Value::fromObject(new (memoryManager) FunctionCtor(rootContext)); + dateCtor = Value::fromObject(new (memoryManager) DateCtor(rootContext)); + regExpCtor = Value::fromObject(new (memoryManager) RegExpCtor(rootContext)); + errorCtor = Value::fromObject(new (memoryManager) ErrorCtor(rootContext)); + evalErrorCtor = Value::fromObject(new (memoryManager) EvalErrorCtor(rootContext)); + rangeErrorCtor = Value::fromObject(new (memoryManager) RangeErrorCtor(rootContext)); + referenceErrorCtor = Value::fromObject(new (memoryManager) ReferenceErrorCtor(rootContext)); + syntaxErrorCtor = Value::fromObject(new (memoryManager) SyntaxErrorCtor(rootContext)); + typeErrorCtor = Value::fromObject(new (memoryManager) TypeErrorCtor(rootContext)); + uRIErrorCtor = Value::fromObject(new (memoryManager) URIErrorCtor(rootContext)); stringCtor.objectValue()->prototype = functionPrototype; numberCtor.objectValue()->prototype = functionPrototype; @@ -190,19 +201,19 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) pd.value = Value::fromDouble(INFINITY); glo->__defineOwnProperty__(rootContext, identifier(QStringLiteral("Infinity")), &pd); - glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext))); + glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new (memoryManager) EvalFunction(rootContext))); // TODO: parseInt [15.1.2.2] // TODO: parseFloat [15.1.2.3] - glo->__put__(rootContext, identifier(QStringLiteral("isNaN")), Value::fromObject(new IsNaNFunction(rootContext))); // isNaN [15.1.2.4] - glo->__put__(rootContext, identifier(QStringLiteral("isFinite")), Value::fromObject(new IsFiniteFunction(rootContext))); // isFinite [15.1.2.5] + glo->__put__(rootContext, identifier(QStringLiteral("isNaN")), Value::fromObject(new (memoryManager) IsNaNFunction(rootContext))); // isNaN [15.1.2.4] + glo->__put__(rootContext, identifier(QStringLiteral("isFinite")), Value::fromObject(new (memoryManager) IsFiniteFunction(rootContext))); // isFinite [15.1.2.5] } ExecutionEngine::~ExecutionEngine() { delete globalObject.asObject(); delete rootContext; - delete stringPool; // the String pointers should get GC-ed. + delete stringPool; } ExecutionContext *ExecutionEngine::newContext() @@ -220,14 +231,16 @@ String *ExecutionEngine::identifier(const QString &s) FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) { - NativeFunction *f = new NativeFunction(scope, name, code); + NativeFunction *f = new (memoryManager) NativeFunction(scope, name, code); f->prototype = scope->engine->functionPrototype; return f; } FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, IR::Function *function) { - ScriptFunction *f = new ScriptFunction(scope, function); + MemoryManager::GCBlocker gcBlocker(memoryManager); + + ScriptFunction *f = new (memoryManager) ScriptFunction(scope, function); Object *proto = scope->engine->newObject(); proto->__put__(scope, scope->engine->id_constructor, Value::fromObject(f)); f->__put__(scope, scope->engine->id_prototype, Value::fromObject(proto)); @@ -237,14 +250,14 @@ FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, IR:: Object *ExecutionEngine::newObject() { - Object *object = new Object(); + Object *object = new (memoryManager) Object(); object->prototype = objectPrototype; return object; } FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx) { - return new ObjectCtor(ctx); + return new (memoryManager) ObjectCtor(ctx); } String *ExecutionEngine::newString(const QString &s) @@ -254,76 +267,76 @@ String *ExecutionEngine::newString(const QString &s) Object *ExecutionEngine::newStringObject(const Value &value) { - StringObject *object = new StringObject(value); + StringObject *object = new (memoryManager) StringObject(value); object->prototype = stringPrototype; return object; } FunctionObject *ExecutionEngine::newStringCtor(ExecutionContext *ctx) { - return new StringCtor(ctx); + return new (memoryManager) StringCtor(ctx); } Object *ExecutionEngine::newNumberObject(const Value &value) { - NumberObject *object = new NumberObject(value); + NumberObject *object = new (memoryManager) NumberObject(value); object->prototype = numberPrototype; return object; } FunctionObject *ExecutionEngine::newNumberCtor(ExecutionContext *ctx) { - return new NumberCtor(ctx); + return new (memoryManager) NumberCtor(ctx); } Object *ExecutionEngine::newBooleanObject(const Value &value) { - Object *object = new BooleanObject(value); + Object *object = new (memoryManager) BooleanObject(value); object->prototype = booleanPrototype; return object; } FunctionObject *ExecutionEngine::newBooleanCtor(ExecutionContext *ctx) { - return new BooleanCtor(ctx); + return new (memoryManager) BooleanCtor(ctx); } Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx) { - Object *object = new FunctionObject(ctx); + Object *object = new (memoryManager) FunctionObject(ctx); object->prototype = functionPrototype; return object; } ArrayObject *ExecutionEngine::newArrayObject() { - ArrayObject *object = new ArrayObject(); + ArrayObject *object = new (memoryManager) ArrayObject(); object->prototype = arrayPrototype; return object; } ArrayObject *ExecutionEngine::newArrayObject(const Array &value) { - ArrayObject *object = new ArrayObject(value); + ArrayObject *object = new (memoryManager) ArrayObject(value); object->prototype = arrayPrototype; return object; } FunctionObject *ExecutionEngine::newArrayCtor(ExecutionContext *ctx) { - return new ArrayCtor(ctx); + return new (memoryManager) ArrayCtor(ctx); } Object *ExecutionEngine::newDateObject(const Value &value) { - Object *object = new DateObject(value); + Object *object = new (memoryManager) DateObject(value); object->prototype = datePrototype; return object; } FunctionObject *ExecutionEngine::newDateCtor(ExecutionContext *ctx) { - return new DateCtor(ctx); + return new (memoryManager) DateCtor(ctx); } Object *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) @@ -335,61 +348,60 @@ Object *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) if (flags & IR::RegExp::RegExp_Multiline) options |= QRegularExpression::MultilineOption; - Object *object = new RegExpObject(QRegularExpression(pattern, options), global); + Object *object = new (memoryManager) RegExpObject(QRegularExpression(pattern, options), global); object->prototype = regExpPrototype; return object; } FunctionObject *ExecutionEngine::newRegExpCtor(ExecutionContext *ctx) { - return new RegExpCtor(ctx); + return new (memoryManager) RegExpCtor(ctx); } Object *ExecutionEngine::newErrorObject(const Value &value) { - ErrorObject *object = new ErrorObject(value); + ErrorObject *object = new (memoryManager) ErrorObject(value); object->prototype = errorPrototype; return object; } Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) { - SyntaxErrorObject *object = new SyntaxErrorObject(ctx, message); + SyntaxErrorObject *object = new (memoryManager) SyntaxErrorObject(ctx, message); object->prototype = syntaxErrorPrototype; return object; } Object *ExecutionEngine::newReferenceErrorObject(ExecutionContext *ctx, const QString &message) { - ReferenceErrorObject *object = new ReferenceErrorObject(ctx, message); + ReferenceErrorObject *object = new (memoryManager) ReferenceErrorObject(ctx, message); object->prototype = referenceErrorPrototype; return object; } Object *ExecutionEngine::newTypeErrorObject(ExecutionContext *ctx, const QString &message) { - TypeErrorObject *object = new TypeErrorObject(ctx, message); + TypeErrorObject *object = new (memoryManager) TypeErrorObject(ctx, message); object->prototype = typeErrorPrototype; return object; } Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) { - MathObject *object = new MathObject(ctx); + MathObject *object = new (memoryManager) MathObject(ctx); object->prototype = objectPrototype; return object; } Object *ExecutionEngine::newActivationObject() { - return new Object(); + return new (memoryManager) Object(); } Object *ExecutionEngine::newForEachIteratorObject(Object *o) { - return new ForEachIteratorObject(o); + return new (memoryManager) ForEachIteratorObject(o); } - } // namespace VM } // namespace QQmlJS diff --git a/qmljs_engine.h b/qmljs_engine.h index 2837adb..520acab 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -68,6 +68,7 @@ struct ErrorObject; struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; +class MemoryManager; struct ObjectPrototype; struct StringPrototype; @@ -87,6 +88,7 @@ struct URIErrorPrototype; struct ExecutionEngine { + MemoryManager *memoryManager; EvalISelFactory *iselFactory; ExecutionContext *current; ExecutionContext *rootContext; @@ -148,7 +150,7 @@ struct ExecutionEngine struct StringPool *stringPool; - ExecutionEngine(EvalISelFactory *iselFactory); + ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory *iselFactory); ~ExecutionEngine(); ExecutionContext *newContext(); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 0127b04..0d6b200 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -44,6 +44,7 @@ #include #include #include +#include "qv4mm.h" namespace QQmlJS { namespace VM { @@ -266,7 +267,6 @@ void ExecutionContext::setProperty(String *name, Value value) Value ExecutionContext::getProperty(String *name) { - PropertyDescriptor tmp; for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { if (ctx->withObject) { With *w = ctx->withObject; @@ -286,7 +286,7 @@ Value ExecutionContext::getProperty(String *name) if (ctx->activation && ctx->activation->__hasProperty__(ctx, name)) return ctx->activation->__get__(ctx, name); if (name->isEqualTo(ctx->engine->id_arguments)) { - Value arguments = Value::fromObject(new ArgumentsObject(this)); + Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this)); createMutableBinding(ctx->engine->id_arguments, false); setMutableBinding(this, ctx->engine->id_arguments, arguments); return arguments; @@ -345,6 +345,8 @@ void ExecutionContext::throwReferenceError(Value value) void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) { + MemoryManager::GCBlocker blockGC(parent->engine->memoryManager); + engine = parent->engine; this->parent = parent; thisObject = that; diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 7fdb86a..d74a501 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -43,6 +43,7 @@ #include "qv4ir_p.h" #include "qv4isel_p.h" #include "qv4ecmaobjects_p.h" +#include "qv4mm.h" #include #include @@ -59,6 +60,28 @@ using namespace QQmlJS::VM; + +Managed::~Managed() +{ +} + +void *Managed::operator new(size_t size, MemoryManager *mm) +{ + assert(mm); + + return mm->allocManaged(size); +} + +void Managed::operator delete(void *ptr) +{ + if (!ptr) + return; + + Managed *m = reinterpret_cast(ptr); + assert(m->mm); + m->mm->deallocManaged(m); +} + // // Object // @@ -107,6 +130,20 @@ bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ct return inplaceBinOp(rhs, name, op, ctx); } +void Object::getCollectables(QVector &objects) +{ + if (prototype) + objects.append(prototype); + + if (members) { + for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) { + if ((*it)->descriptor.isData()) + if (Object *o = (*it)->descriptor.value.asObject()) + objects.append(o); + } + } +} + // Section 8.12.1 PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, String *name) { @@ -349,6 +386,15 @@ String *ForEachIteratorObject::nextPropertyName() } } +void ForEachIteratorObject::getCollectables(QVector &objects) +{ + Object::getCollectables(objects); + if (object) + objects.append(object); + if (current) + objects.append(current); +} + Value ArrayObject::__get__(ExecutionContext *ctx, String *name) { if (name->isEqualTo(ctx->engine->id_length)) @@ -368,6 +414,12 @@ bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContex return Object::inplaceBinOp(rhs, index, op, ctx); } +void ArrayObject::getCollectables(QVector &objects) +{ + Object::getCollectables(objects); + value.getCollectables(objects); +} + bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value) { if (! value.isObject()) { @@ -490,7 +542,7 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value bool directCall = true; const QString code = args[0].stringValue()->toQString(); - QQmlJS::IR::Function *f = parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); + QScopedPointer f(parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode)); if (!f) return Value::undefinedValue(); @@ -534,6 +586,8 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct { using namespace QQmlJS; + MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); + VM::ExecutionEngine *vm = ctx->engine; IR::Module module; IR::Function *globalCode = 0; @@ -654,6 +708,13 @@ void ErrorObject::setNameProperty(ExecutionContext *ctx) __put__(ctx, QLatin1String("name"), Value::fromString(ctx, className())); } +void ErrorObject::getCollectables(QVector &objects) +{ + Object::getCollectables(objects); + if (Object *o = value.asObject()) + objects.append(o); +} + SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) : ErrorObject(ctx->argument(0)) , msg(message) @@ -675,7 +736,6 @@ Value ScriptFunction::construct(VM::ExecutionContext *ctx) return ctx->thisObject; } - Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name) { if (name->isEqualTo(ctx->engine->id_length)) @@ -698,7 +758,6 @@ PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(ExecutionContext return Object::__getPropertyDescriptor__(ctx, name, to_fill); } - NativeFunction::NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) : FunctionObject(scope) , code(code) diff --git a/qmljs_objects.h b/qmljs_objects.h index fc8deed..9daa841 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -75,6 +75,7 @@ struct ErrorObject; struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; +class MemoryManager; struct ObjectPrototype; struct StringPrototype; @@ -92,6 +93,29 @@ struct SyntaxErrorPrototype; struct TypeErrorPrototype; struct URIErrorPrototype; +struct Managed +{ +private: + Managed(const Managed &other); + void operator = (const Managed &other); + +protected: + Managed() {} + +public: + virtual ~Managed(); + + void *operator new(size_t size, MemoryManager *mm); + void operator delete(void *ptr); + +protected: + virtual void getCollectables(QVector &objects) = 0; + +private: + friend class MemoryManager; + MemoryManager *mm; +}; + struct String { inline bool isEqualTo(const String *other) const { if (this == other) @@ -395,7 +419,7 @@ private: int _allocated: 27; }; -struct Object { +struct Object: Managed { Object *prototype; String *klass; PropertyTable *members; @@ -440,6 +464,9 @@ struct Object { Value getValue(ExecutionContext *ctx, PropertyDescriptor *p) const; bool inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); + +protected: + virtual void getCollectables(QVector &objects); }; struct ForEachIteratorObject: Object { @@ -450,6 +477,9 @@ struct ForEachIteratorObject: Object { virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); } String *nextPropertyName(); + +protected: + virtual void getCollectables(QVector &objects); }; struct BooleanObject: Object { @@ -489,6 +519,9 @@ struct ArrayObject: Object { virtual Value __get__(ExecutionContext *ctx, String *name); virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); + +protected: + virtual void getCollectables(QVector &objects); }; struct FunctionObject: Object { @@ -591,6 +624,7 @@ struct ErrorObject: Object { protected: void setNameProperty(ExecutionContext *ctx); + virtual void getCollectables(QVector &objects); }; struct EvalErrorObject: ErrorObject { diff --git a/qv4array_p.h b/qv4array_p.h index 20022af..22c29f9 100644 --- a/qv4array_p.h +++ b/qv4array_p.h @@ -74,8 +74,11 @@ public: Array &other); inline void push(const Value &value); + void getCollectables(QVector &objects); + private: - std::deque *to_vector; + typedef std::deque ToVectorType; + ToVectorType *to_vector; }; class ArrayElementLessThan @@ -194,6 +197,14 @@ inline void Array::push(const Value &value) to_vector->push_back(value); } +inline void Array::getCollectables(QVector &objects) +{ + for (ToVectorType::const_iterator it = to_vector->begin(), eit = to_vector->end(); it != eit; ++it) { + if (Object *o = it->asObject()) + objects.append(o); + } +} + inline void Array::splice(double start, double deleteCount, const QVector &items, Array &other) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 34c785d..8b3361b 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -151,7 +151,7 @@ void liveness(IR::Function *function) computeUseDef(s); } - dfs(function->basicBlocks.first(), &V, &blocks); + dfs(function->basicBlocks.at(0), &V, &blocks); bool changed; do { @@ -1471,6 +1471,9 @@ void Codegen::linearize(IR::Function *function) exitBlock->index = trace.size(); trace.append(exitBlock); + foreach (IR::BasicBlock *b, function->basicBlocks) + if (!trace.contains(b)) + delete b; function->basicBlocks = trace; #ifndef QV4_NO_LIVENESS diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 607c1f1..6eca545 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -42,6 +42,7 @@ #include "qv4ecmaobjects_p.h" #include "qv4array_p.h" +#include "qv4mm.h" #include #include #include @@ -1860,6 +1861,8 @@ FunctionCtor::FunctionCtor(ExecutionContext *scope) // 15.3.2 Value FunctionCtor::construct(ExecutionContext *ctx) { + MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); + QString args; QString body; if (ctx->argumentCount > 0) @@ -1899,7 +1902,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx) isel->run(irf); delete isel; - ctx->thisObject = Value::fromObject(new ScriptFunction(ctx->engine->rootContext, irf)); + ctx->thisObject = Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, irf)); return ctx->thisObject; } @@ -2593,7 +2596,7 @@ Value RegExpCtor::construct(ExecutionContext *ctx) if (!f.isUndefined()) ctx->throwTypeError(); - return Value::fromObject(new RegExpObject(re->value, false)); + return Value::fromObject(new (ctx->engine->memoryManager) RegExpObject(re->value, false)); } if (r.isUndefined()) @@ -2623,7 +2626,7 @@ Value RegExpCtor::construct(ExecutionContext *ctx) if (!re.isValid()) ctx->throwTypeError(); - ctx->thisObject = Value::fromObject(new RegExpObject(re, global)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) RegExpObject(re, global)); return ctx->thisObject; } @@ -2715,7 +2718,7 @@ ErrorCtor::ErrorCtor(ExecutionContext *scope) Value ErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new ErrorObject(ctx->argument(0))); + ctx->thisObject = Value::fromObject(ctx->engine->newErrorObject(ctx->argument(0))); return ctx->thisObject; } @@ -2732,37 +2735,37 @@ Value ErrorCtor::call(ExecutionContext *ctx) Value EvalErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new EvalErrorObject(ctx)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx)); return ctx->thisObject; } Value RangeErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new RangeErrorObject(ctx)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx)); return ctx->thisObject; } Value ReferenceErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new ReferenceErrorObject(ctx)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx)); return ctx->thisObject; } Value SyntaxErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new SyntaxErrorObject(ctx, 0)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0)); return ctx->thisObject; } Value TypeErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new TypeErrorObject(ctx)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx)); return ctx->thisObject; } Value URIErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new URIErrorObject(ctx)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx)); return ctx->thisObject; } diff --git a/qv4ir.cpp b/qv4ir.cpp index 7646b37..2a9414b 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -391,8 +391,20 @@ Function *Module::newFunction(const QString &name) return f; } +Module::~Module() +{ + foreach (Function *f, functions) + f->releaseModuleManagedData(); +} + Function::~Function() { + delete[] codeData; +} + + +void Function::releaseModuleManagedData() +{ // destroy the Stmt::Data blocks manually, because memory pool cleanup won't // call the Stmt destructors. foreach (IR::BasicBlock *b, basicBlocks) @@ -400,9 +412,11 @@ Function::~Function() s->destroyData(); qDeleteAll(basicBlocks); - delete[] codeData; + pool = 0; + module = 0; } + const QString *Function::newString(const QString &text) { return &*strings.insert(text); diff --git a/qv4ir_p.h b/qv4ir_p.h index 92276d0..14c73e5 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -587,6 +587,8 @@ struct Module { QVector functions; Function *newFunction(const QString &name); + + ~Module(); }; struct Function { @@ -626,6 +628,7 @@ struct Function { { this->name = newString(name); } ~Function(); + void releaseModuleManagedData(); enum BasicBlockInsertMode { InsertBlock, diff --git a/qv4mm.cpp b/qv4mm.cpp new file mode 100644 index 0000000..4be7219 --- /dev/null +++ b/qv4mm.cpp @@ -0,0 +1,375 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmljs_engine.h" +#include "qmljs_objects.h" +#include "qv4ecmaobjects_p.h" +#include "qv4mm.h" + +#include +#include +#include + +#include + +using namespace QQmlJS::VM; + +static const std::size_t CHUNK_SIZE = 65536; + +struct MemoryManager::Data +{ + bool enableGC; + bool gcBlocked; + bool scribble; + bool aggressiveGC; + ExecutionEngine *engine; + StringPool *stringPool; + + // FIXME: this freeList will get out of hand if there is one allocation+deallocation of, say, 16M. + // TODO: turn the freeList into a fixed length array which can hold the most common sizes (e.g. up to 64K), then use a tree for anything afterwards. + // TODO: this requires that the interaction with the freeList is factored out first into separate methods. + QVector freeList; + QLinkedList > heapChunks; + + // statistics: +#ifdef DETAILED_MM_STATS + QVector allocSizeCounters; +#endif // DETAILED_MM_STATS + + Data(bool enableGC) + : enableGC(enableGC) + , gcBlocked(false) + , engine(0) + , stringPool(0) + , freeList(0) + { + scribble = qgetenv("MM_NO_SCRIBBLE").isEmpty(); + aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty(); + } + + ~Data() + { + for (QLinkedList >::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) + delete[] i->first; + } +}; + +MemoryManager::MemoryManager() + : m_d(new Data(true)) +{ +} + +MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) +{ + if (m_d->aggressiveGC) + runGC(); +#ifdef DETAILED_MM_STATS + willAllocate(size); +#endif // DETAILED_MM_STATS + + size += sizeof(MMInfo); + assert(size >= 16); + assert(size % 16 == 0); + + for (std::size_t s = size / 16, es = m_d->freeList.size(); s < es; ++s) { + if (MMObject *m = m_d->freeList.at(s)) { + m_d->freeList[s] = m->info.next; + + if (s != size / 16) { + MMObject *tail = reinterpret_cast(reinterpret_cast(m) + size); + assert(m->info.size == s * 16); + tail->info.inUse = 0; + tail->info.markBit = 0; + tail->info.size = m->info.size - size; + MMObject *&f = m_d->freeList[tail->info.size / 16]; + tail->info.next = f; + f = tail; + m->info.size = size; + } + + m->info.inUse = 1; + m->info.markBit = 0; + scribble(m, 0xaa); +// qDebug("alloc(%lu) -> %p", size, m); + return m; + } + } + + if (!m_d->aggressiveGC) + if (runGC() >= size) + return alloc(size - sizeof(MMInfo)); + + std::size_t allocSize = std::max(size, CHUNK_SIZE); + char *ptr = new char[allocSize]; + m_d->heapChunks.append(qMakePair(ptr, allocSize)); +// qDebug("Allocated new chunk of %lu bytes @ %p", allocSize, ptr); + + if (allocSize > size) { + MMObject *m = reinterpret_cast(ptr + size); + m->info.size = allocSize - size; + std::size_t off = m->info.size / 16; + if (((std::size_t) m_d->freeList.size()) <= off) + m_d->freeList.resize(off + 1); + MMObject *&f = m_d->freeList[off]; + m->info.next = f; + f = m; + } + + MMObject *m = reinterpret_cast(ptr); + m->info.inUse = 1; + m->info.markBit = 0; + m->info.size = size; + scribble(m, 0xaa); +// qDebug("alloc(%lu) -> %p", size, ptr); + return m; +} + +void MemoryManager::dealloc(MMObject *ptr) +{ + if (!ptr) + return; + + assert(ptr->info.size >= 16); + assert(ptr->info.size % 16 == 0); + +// qDebug("dealloc %p (%lu)", ptr, ptr->info.size); + + std::size_t off = ptr->info.size / 16; + if (((std::size_t) m_d->freeList.size()) <= off) + m_d->freeList.resize(off + 1); + MMObject *&f = m_d->freeList[off]; + ptr->info.next = f; + ptr->info.inUse = 0; + ptr->info.markBit = 0; + ptr->info.needsManagedDestructorCall = 0; + f = ptr; + scribble(ptr, 0x55); +} + +void MemoryManager::scribble(MemoryManager::MMObject *obj, int c) const +{ + if (m_d->scribble) + ::memset(&obj->data, c, obj->info.size - sizeof(MMInfo)); +} + +std::size_t MemoryManager::mark(const QVector &objects) +{ + std::size_t marks = 0; + + QVector kids; + kids.reserve(32); + + foreach (Object *o, objects) { + if (!o) + continue; + + MMObject *obj = toObject(o); + assert(obj->info.inUse); + if (obj->info.markBit == 0) { + obj->info.markBit = 1; + ++marks; + static_cast(o)->getCollectables(kids); + marks += mark(kids); + kids.resize(0); + } + } + + return marks; +} + +std::size_t MemoryManager::sweep(std::size_t &largestFreedBlock) +{ + std::size_t freedCount = 0; + + for (QLinkedList >::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) + freedCount += sweep(i->first, i->second, largestFreedBlock); + + return freedCount; +} + +std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, std::size_t &largestFreedBlock) +{ +// qDebug("chunkStart @ %p", chunkStart); + std::size_t freedCount = 0; + + for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize; chunk < chunkEnd; ) { + MMObject *m = reinterpret_cast(chunk); +// qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s", +// chunk, m->info.size, (m->info.inUse ? "yes" : "no"), (m->info.markBit ? "true" : "false")); + + assert((intptr_t) chunk % 16 == 0); + assert(m->info.size >= 16); + assert(m->info.size % 16 == 0); + + chunk = chunk + m->info.size; + if (m->info.inUse) { + if (m->info.markBit) { + m->info.markBit = 0; + } else { +// qDebug("-- collecting it."); + if (m->info.needsManagedDestructorCall) + reinterpret_cast(&m->data)->~Managed(); + dealloc(m); + largestFreedBlock = std::max(largestFreedBlock, m->info.size); + ++freedCount; + } + } + } + + return freedCount; +} + +bool MemoryManager::isGCBlocked() const +{ + return m_d->gcBlocked; +} + +void MemoryManager::setGCBlocked(bool blockGC) +{ + m_d->gcBlocked = blockGC; +} + +std::size_t MemoryManager::runGC() +{ + if (!m_d->enableGC || m_d->gcBlocked) { +// qDebug() << "Not running GC."; + return 0; + } + +// QTime t; t.start(); + + QVector roots; + collectRoots(roots); +// std::cerr << "GC: found " << roots.size() +// << " roots in " << t.elapsed() +// << "ms" << std::endl; + +// t.restart(); + /*std::size_t marks =*/ mark(roots); +// std::cerr << "GC: marked " << marks +// << " objects in " << t.elapsed() +// << "ms" << std::endl; + +// t.restart(); + std::size_t freedCount = 0, largestFreedBlock = 0; + freedCount = sweep(largestFreedBlock); +// std::cerr << "GC: sweep freed " << freedCount +// << " objects in " << t.elapsed() +// << "ms" << std::endl; + + return largestFreedBlock; +} + +void MemoryManager::setEnableGC(bool enableGC) +{ + m_d->enableGC = enableGC; +} + +MemoryManager::~MemoryManager() +{ + std::size_t dummy = 0; + sweep(dummy); +} + +static inline void add(QVector &values, const Value &v) +{ + if (Object *o = v.asObject()) + values.append(o); +} + +void MemoryManager::setExecutionEngine(ExecutionEngine *engine) +{ + m_d->engine = engine; +} + +void MemoryManager::setStringPool(StringPool *stringPool) +{ + m_d->stringPool = stringPool; +} + +void MemoryManager::dumpStats() const +{ + std::cerr << "=================" << std::endl; + std::cerr << "Allocation stats:" << std::endl; +#ifdef DETAILED_MM_STATS + std::cerr << "Requests for each chunk size:" << std::endl; + for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) { + if (unsigned count = m_d->allocSizeCounters[i]) { + std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl; + } + } +#endif // DETAILED_MM_STATS +} + +ExecutionEngine *MemoryManager::engine() const +{ + return m_d->engine; +} + +#ifdef DETAILED_MM_STATS +void MemoryManager::willAllocate(std::size_t size) +{ + unsigned alignedSize = (size + 15) >> 4; + QVector &counters = m_d->allocSizeCounters; + if ((unsigned) counters.size() < alignedSize + 1) + counters.resize(alignedSize + 1); + counters[alignedSize]++; +} +#endif // DETAILED_MM_STATS + +void MemoryManager::collectRoots(QVector &roots) const +{ + add(roots, m_d->engine->globalObject); + add(roots, m_d->engine->exception); + + for (ExecutionContext *ctxt = engine()->current; ctxt; ctxt = ctxt->parent) { + add(roots, ctxt->thisObject); + if (ctxt->function) + roots.append(ctxt->function); + for (unsigned arg = 0, lastArg = ctxt->formalCount(); arg < lastArg; ++arg) + add(roots, ctxt->arguments[arg]); + for (unsigned local = 0, lastLocal = ctxt->variableCount(); local < lastLocal; ++local) + add(roots, ctxt->locals[local]); + if (ctxt->activation) + roots.append(ctxt->activation); + for (ExecutionContext::With *it = ctxt->withObject; it; it = it->next) + if (it->object) + roots.append(it->object); + } + + collectRootsOnStack(roots); +} + +MemoryManagerWithoutGC::~MemoryManagerWithoutGC() +{} + +void MemoryManagerWithoutGC::collectRootsOnStack(QVector &roots) const +{ + Q_UNUSED(roots); +} diff --git a/qv4mm.h b/qv4mm.h new file mode 100644 index 0000000..ece3765 --- /dev/null +++ b/qv4mm.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4GC_H +#define QV4GC_H + +#include "qmljs_objects.h" + +#include + +#define DETAILED_MM_STATS + +namespace QQmlJS { +namespace VM { + +class MemoryManager +{ + MemoryManager(const MemoryManager &); + MemoryManager &operator=(const MemoryManager&); + + struct Data; + +public: + class GCBlocker + { + public: + GCBlocker(MemoryManager *mm) + : mm(mm) + , wasBlocked(mm->isGCBlocked()) + { + mm->setGCBlocked(true); + } + + ~GCBlocker() + { + mm->setGCBlocked(wasBlocked); + } + + private: + MemoryManager *mm; + bool wasBlocked; + }; + +public: + MemoryManager(); + virtual ~MemoryManager() = 0; + + // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries). + // Note: all occurances of "16" in alloc/dealloc are also due to the alignment. + static inline std::size_t align(std::size_t size) + { return (size + 15) & ~0xf; } + + inline Managed *allocManaged(std::size_t size) + { + size = align(size); + MMObject *o = alloc(size); + o->info.needsManagedDestructorCall = 1; + Managed *ptr = reinterpret_cast(&o->data); + ptr->mm = this; + return ptr; + } + + inline void deallocManaged(Managed *m) + { + if (!m) + return; + + assert(m->mm == this); + dealloc(toObject(m)); + } + + bool isGCBlocked() const; + void setGCBlocked(bool blockGC); + std::size_t runGC(); + + void setEnableGC(bool enableGC); + void setExecutionEngine(ExecutionEngine *engine); + void setStringPool(StringPool *stringPool); + + void dumpStats() const; + +protected: +#if 1 // 64bit and x86: + struct MMObject; + struct MMInfo { + std::size_t inUse : 1; + std::size_t markBit : 1; + std::size_t needsManagedDestructorCall : 1; + std::size_t size : 61; + MMObject *next; + }; + struct MMObject { + MMInfo info; + std::size_t data; + }; +#endif +#if 0 // for 32bits: + // untested! + struct MMInfo { + std::size_t inUse : 1; + std::size_t markBit : 1; + std::size_t size : 30; + }; + struct MMObject { + MMInfo info; + union { + struct MMObject *next; + char data[1]; + } + }; +#endif + +protected: + static inline MMObject *toObject(void *ptr) { return reinterpret_cast(reinterpret_cast(ptr) - sizeof(MMInfo)); } + + /// expects size to be aligned + // TODO: try to inline + MMObject *alloc(std::size_t size); + + // TODO: try to inline + void dealloc(MMObject *ptr); + + void scribble(MMObject *obj, int c) const; + + virtual void collectRootsOnStack(QVector &roots) const = 0; + + ExecutionEngine *engine() const; + +#ifdef DETAILED_MM_STATS + void willAllocate(std::size_t size); +#endif // DETAILED_MM_STATS + +private: + void collectRoots(QVector &roots) const; + static std::size_t mark(const QVector &objects); + std::size_t sweep(std::size_t &largestFreedBlock); + std::size_t sweep(char *chunkStart, std::size_t chunkSize, std::size_t &largestFreedBlock); + +private: + QScopedPointer m_d; +}; + +class MemoryManagerWithoutGC: public MemoryManager +{ +public: + MemoryManagerWithoutGC() + { setEnableGC(false); } + + virtual ~MemoryManagerWithoutGC(); + +protected: + virtual void collectRootsOnStack(QVector &roots) const; +}; + +} // namespace VM +} // namespace QQmlJS + +#endif // QV4GC_H diff --git a/v4.pro b/v4.pro index 24ab7c0..ce1409b 100644 --- a/v4.pro +++ b/v4.pro @@ -24,7 +24,8 @@ SOURCES += main.cpp \ qv4isel_masm.cpp \ llvm_runtime.cpp \ qv4isel_p.cpp \ - debugging.cpp + debugging.cpp \ + qv4mm.cpp HEADERS += \ qv4codegen_p.h \ @@ -41,7 +42,8 @@ HEADERS += \ qv4isel_masm_p.h \ qv4isel_p.h \ qv4isel_util_p.h \ - debugging.h + debugging.h \ + qv4mm.h llvm { -- 2.7.4