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 <lars.knoll@digia.com>
This commit is contained in:
Erik Verbruggen 2012-12-04 13:40:18 +01:00 committed by Lars Knoll
parent 3b3f3bebcd
commit 5f22fbd7fc
18 changed files with 972 additions and 108 deletions

View File

@ -53,6 +53,7 @@
#include "qv4syntaxchecker_p.h"
#include "qv4ecmaobjects_p.h"
#include "qv4isel_p.h"
#include "qv4mm_moth.h"
#include <QtCore>
#include <private/qqmljsengine_p.h>
@ -144,12 +145,12 @@ int executeLLVMCode(void *codePtr)
void (*code)(VM::ExecutionContext *) = (void (*)(VM::ExecutionContext *)) codePtr;
QScopedPointer<QQmlJS::EvalISelFactory> 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<QQmlJS::VM::MemoryManager> mm;
QScopedPointer<QQmlJS::EvalISelFactory> 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<QQmlJS::Debugging::Debugger> 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<QQmlJS::IR::Function> 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;
}
}

View File

@ -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

81
moth/qv4mm_moth.cpp Normal file
View File

@ -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 <QList>
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<VM::Value *>(&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<VM::Object *> &roots) const
{
for (int i = 0, ei = stackFrames.size(); i < ei; ++i) {
MMObject *m = stackFrames[i];
VM::Value *frame = reinterpret_cast<VM::Value *>(&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);
}
}
}
}

59
moth/qv4mm_moth.h Normal file
View File

@ -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 <QVector>
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<VM::Object *> &roots) const;
private:
QVector<MMObject *> stackFrames;
};
} // namespace Moth
} // namespace QQmlJS
#endif // QV4GC_MOTH_H

View File

@ -2,6 +2,7 @@
#include "qv4instr_moth_p.h"
#include "qmljs_value.h"
#include "debugging.h"
#include "qv4mm_moth.h"
#include <iostream>
@ -51,7 +52,7 @@ using namespace QQmlJS::Moth;
#endif
static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVector<VM::Value> &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<VM::Value> *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<MemoryManager *>(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<VM::Value> *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<VM::Value> 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<const Instr *>(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<MemoryManager *>(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)

View File

@ -41,6 +41,7 @@
#include <qmljs_engine.h>
#include <qmljs_objects.h>
#include <qv4ecmaobjects_p.h>
#include "qv4mm.h"
namespace QQmlJS {
namespace VM {
@ -49,6 +50,9 @@ struct StringPool
{
QHash<QString, String*> strings;
~StringPool()
{ qDeleteAll(strings.values()); }
String *newString(const QString &s)
{
QHash<QString, String*>::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

View File

@ -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();

View File

@ -44,6 +44,7 @@
#include <qmljs_environment.h>
#include <qmljs_objects.h>
#include <qv4ecmaobjects_p.h>
#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;

View File

@ -43,6 +43,7 @@
#include "qv4ir_p.h"
#include "qv4isel_p.h"
#include "qv4ecmaobjects_p.h"
#include "qv4mm.h"
#include <private/qqmljsengine_p.h>
#include <private/qqmljslexer_p.h>
@ -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<Managed *>(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<Object *> &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<Object *> &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<Object *> &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<QQmlJS::IR::Function> 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<Object *> &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)

View File

@ -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<Object *> &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<Object *> &objects);
};
struct ForEachIteratorObject: Object {
@ -450,6 +477,9 @@ struct ForEachIteratorObject: Object {
virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); }
String *nextPropertyName();
protected:
virtual void getCollectables(QVector<Object *> &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<Object *> &objects);
};
struct FunctionObject: Object {
@ -591,6 +624,7 @@ struct ErrorObject: Object {
protected:
void setNameProperty(ExecutionContext *ctx);
virtual void getCollectables(QVector<Object *> &objects);
};
struct EvalErrorObject: ErrorObject {

View File

@ -74,8 +74,11 @@ public:
Array &other);
inline void push(const Value &value);
void getCollectables(QVector<Object *> &objects);
private:
std::deque<Value> *to_vector;
typedef std::deque<Value> 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<Object *> &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<Value> &items,
Array &other)

View File

@ -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

View File

@ -42,6 +42,7 @@
#include "qv4ecmaobjects_p.h"
#include "qv4array_p.h"
#include "qv4mm.h"
#include <QtCore/qnumeric.h>
#include <QtCore/qmath.h>
#include <QtCore/QDateTime>
@ -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;
}

View File

@ -391,7 +391,19 @@ 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.
@ -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);

View File

@ -587,6 +587,8 @@ struct Module {
QVector<Function *> 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,

375
qv4mm.cpp Normal file
View File

@ -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 <QTime>
#include <QVector>
#include <QLinkedList>
#include <iostream>
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<MMObject *> freeList;
QLinkedList<QPair<char *, std::size_t> > heapChunks;
// statistics:
#ifdef DETAILED_MM_STATS
QVector<unsigned> 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<QPair<char *, std::size_t> >::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<MMObject *>(reinterpret_cast<char *>(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<MMObject *>(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<MMObject *>(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<Object *> &objects)
{
std::size_t marks = 0;
QVector<Object *> 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<Managed *>(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<QPair<char *, std::size_t> >::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<MMObject *>(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<VM::Managed *>(&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<Object *> 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<Object *> &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<unsigned> &counters = m_d->allocSizeCounters;
if ((unsigned) counters.size() < alignedSize + 1)
counters.resize(alignedSize + 1);
counters[alignedSize]++;
}
#endif // DETAILED_MM_STATS
void MemoryManager::collectRoots(QVector<VM::Object *> &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<VM::Object *> &roots) const
{
Q_UNUSED(roots);
}

184
qv4mm.h Normal file
View File

@ -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 <QScopedPointer>
#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<Managed *>(&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<MMObject *>(reinterpret_cast<char *>(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<VM::Object *> &roots) const = 0;
ExecutionEngine *engine() const;
#ifdef DETAILED_MM_STATS
void willAllocate(std::size_t size);
#endif // DETAILED_MM_STATS
private:
void collectRoots(QVector<VM::Object *> &roots) const;
static std::size_t mark(const QVector<Object *> &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<Data> m_d;
};
class MemoryManagerWithoutGC: public MemoryManager
{
public:
MemoryManagerWithoutGC()
{ setEnableGC(false); }
virtual ~MemoryManagerWithoutGC();
protected:
virtual void collectRootsOnStack(QVector<VM::Object *> &roots) const;
};
} // namespace VM
} // namespace QQmlJS
#endif // QV4GC_H

6
v4.pro
View File

@ -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 {