From a325b21e1bd51263551829089e9f31e2156bc641 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 22 Dec 2016 21:24:27 +0100 Subject: Allocate simple call contexts from a special allocator We used to allocate those on the C stack, but this doesn't work anymore with the new GC, as the mark bit is not stored inside the object anymore. Instead use a special allocator for these contexts that operates like a stack. Change-Id: I381ac3914ca866945312a1e79883aefe72662d2c Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4context.cpp | 38 +++++++++++++++++------- src/qml/jsruntime/qv4context_p.h | 14 ++------- src/qml/jsruntime/qv4functionobject.cpp | 22 +++++++------- src/qml/memory/qv4mm.cpp | 45 ++++++++++++++++++++++++++++ src/qml/memory/qv4mm_p.h | 52 +++++++++++++++++++++++++++++++++ 5 files changed, 139 insertions(+), 32 deletions(-) diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 544d39339b..0954d621a6 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -95,6 +95,21 @@ Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData return c; } +Heap::CallContext *Heap::CallContext::createSimpleContext(ExecutionEngine *v4) +{ + Heap::CallContext *ctxt = v4->memoryManager->allocSimpleCallContext(); + memset(ctxt, 0, sizeof(Heap::CallContext)); + ctxt->mm_data = 0; + ctxt->setVtable(QV4::CallContext::staticVTable()); + ctxt->init(v4); + return ctxt; +} + +void Heap::CallContext::freeSimpleCallContext() +{ + engine->memoryManager->freeSimpleCallContext(); +} + Heap::WithContext *ExecutionContext::newWithContext(Heap::Object *with) { return d()->engine->memoryManager->alloc(d(), with); @@ -325,26 +340,27 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio ExecutionContextSaver ctxSaver(scope); - CallContext::Data ctx = CallContext::Data::createOnStack(scope.engine); + CallContext::Data *ctx = CallContext::Data::createSimpleContext(scope.engine); - ctx.strictMode = function->isStrict(); - ctx.callData = callData; - ctx.v4Function = function; - ctx.compilationUnit = function->compilationUnit; - ctx.lookups = function->compilationUnit->runtimeLookups; - ctx.constantTable = function->compilationUnit->constants; - ctx.outer = this->d(); - ctx.locals = scope.alloc(function->compiledFunction->nLocals); + ctx->strictMode = function->isStrict(); + ctx->callData = callData; + ctx->v4Function = function; + ctx->compilationUnit = function->compilationUnit; + ctx->lookups = function->compilationUnit->runtimeLookups; + ctx->constantTable = function->compilationUnit->constants; + ctx->outer = this->d(); + ctx->locals = scope.alloc(function->compiledFunction->nLocals); for (int i = callData->argc; i < (int)function->nFormals; ++i) callData->args[i] = Encode::undefined(); - scope.engine->pushContext(&ctx); - Q_ASSERT(scope.engine->current == &ctx); + scope.engine->pushContext(ctx); + Q_ASSERT(scope.engine->current == ctx); scope.result = Q_V4_PROFILE(scope.engine, function); if (function->hasQmlDependencies) QQmlPropertyCapture::registerQmlDependencies(function->compiledFunction, scope); + ctx->freeSimpleCallContext(); } void ExecutionContext::setProperty(String *name, const Value &value) diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index c985fdb24d..996de174b4 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -135,7 +135,8 @@ struct ExecutionContext : Base { V4_ASSERT_IS_TRIVIAL(ExecutionContext) struct CallContext : ExecutionContext { - static CallContext createOnStack(ExecutionEngine *v4); + static CallContext *createSimpleContext(ExecutionEngine *v4); + void freeSimpleCallContext(); void init(ExecutionEngine *engine, ContextType t = Type_SimpleCallContext) { @@ -247,6 +248,7 @@ struct Q_QML_EXPORT CallContext : public ExecutionContext inline ReturnedValue argument(int i) const; bool needsOwnArguments() const; + }; inline ReturnedValue CallContext::argument(int i) const { @@ -289,16 +291,6 @@ inline const WithContext *ExecutionContext::asWithContext() const return d()->type == Heap::ExecutionContext::Type_WithContext ? static_cast(this) : 0; } -inline Heap::CallContext Heap::CallContext::createOnStack(ExecutionEngine *v4) -{ - Heap::CallContext ctxt; - memset(&ctxt, 0, sizeof(Heap::CallContext)); - ctxt.mm_data = 0; - ctxt.setVtable(QV4::CallContext::staticVTable()); - ctxt.init(v4); - return ctxt; -} - } // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 64f7b98618..1ac0e28a1b 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -484,13 +484,14 @@ void BuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData ExecutionContextSaver ctxSaver(scope); - CallContext::Data ctx = CallContext::Data::createOnStack(v4); - ctx.strictMode = f->scope()->strictMode; // ### needed? scope or parent context? - ctx.callData = callData; - v4->pushContext(&ctx); - Q_ASSERT(v4->current == &ctx); + CallContext::Data *ctx = CallContext::Data::createSimpleContext(v4); + ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context? + ctx->callData = callData; + v4->pushContext(ctx); + Q_ASSERT(v4->current == ctx); scope.result = f->d()->code(static_cast(v4->currentContext)); + ctx->freeSimpleCallContext(); } void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) @@ -505,13 +506,14 @@ void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *c ExecutionContextSaver ctxSaver(scope); - CallContext::Data ctx = CallContext::Data::createOnStack(v4); - ctx.strictMode = f->scope()->strictMode; // ### needed? scope or parent context? - ctx.callData = callData; - v4->pushContext(&ctx); - Q_ASSERT(v4->current == &ctx); + CallContext::Data *ctx = CallContext::Data::createSimpleContext(v4); + ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context? + ctx->callData = callData; + v4->pushContext(ctx); + Q_ASSERT(v4->current == ctx); scope.result = f->d()->code(static_cast(v4->currentContext), f->d()->index); + ctx->freeSimpleCallContext(); } DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 92d8170ece..96875c70f6 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -246,6 +246,49 @@ void ChunkAllocator::free(Chunk *chunk, size_t size) } +template +StackAllocator::StackAllocator(ChunkAllocator *chunkAlloc) + : chunkAllocator(chunkAlloc) +{ + chunks.push_back(chunkAllocator->allocate()); + firstInChunk = chunks.back()->first(); + nextFree = firstInChunk; + lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots; +} + +template +void StackAllocator::freeAll() +{ + for (auto c : chunks) + chunkAllocator->free(c); +} + +template +void StackAllocator::nextChunk() { + Q_ASSERT(nextFree == lastInChunk); + ++currentChunk; + if (currentChunk >= chunks.size()) { + Chunk *newChunk = chunkAllocator->allocate(); + chunks.push_back(newChunk); + } + firstInChunk = chunks.at(currentChunk)->first(); + nextFree = firstInChunk; + lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots; +} + +template +void QV4::StackAllocator::prevChunk() { + Q_ASSERT(nextFree == firstInChunk); + Q_ASSERT(chunks.at(currentChunk) == nextFree->chunk()); + Q_ASSERT(currentChunk > 0); + --currentChunk; + firstInChunk = chunks.at(currentChunk)->first(); + lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots; + nextFree = lastInChunk; +} + +template struct StackAllocator; + struct MemoryManager::Data { const size_t pageSize; @@ -384,6 +427,7 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec MemoryManager::MemoryManager(ExecutionEngine *engine) : engine(engine) , chunkAllocator(new ChunkAllocator) + , stackAllocator(chunkAllocator) , m_d(new Data) , m_persistentValues(new PersistentValueStorage(engine)) , m_weakValues(new PersistentValueStorage(engine)) @@ -770,6 +814,7 @@ MemoryManager::~MemoryManager() delete m_persistentValues; sweep(/*lastSweep*/true); + stackAllocator.freeAll(); delete m_weakValues; #ifdef V4_USE_VALGRIND diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index f15b0fb62c..52d95aca0c 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -64,12 +64,58 @@ #define QV4_MM_MAX_CHUNK_SIZE "QV4_MM_MAX_CHUNK_SIZE" #define QV4_MM_STATS "QV4_MM_STATS" +#define MM_DEBUG 0 + QT_BEGIN_NAMESPACE namespace QV4 { struct ChunkAllocator; +template +struct StackAllocator { + Q_STATIC_ASSERT(sizeof(T) < Chunk::DataSize); + static const uint requiredSlots = (sizeof(T) + sizeof(HeapItem) - 1)/sizeof(HeapItem); + + StackAllocator(ChunkAllocator *chunkAlloc); + + T *allocate() { + T *m = nextFree->as(); + if (Q_UNLIKELY(nextFree == lastInChunk)) { + nextChunk(); + } else { + nextFree += requiredSlots; + } +#if MM_DEBUG + Chunk *c = m->chunk(); + Chunk::setBit(c->objectBitmap, m - c->realBase()); +#endif + return m; + } + void free() { +#if MM_DEBUG + Chunk::clearBit(item->chunk()->objectBitmap, item - item->chunk()->realBase()); +#endif + if (Q_UNLIKELY(nextFree == firstInChunk)) { + prevChunk(); + } else { + nextFree -= requiredSlots; + } + } + + void nextChunk(); + void prevChunk(); + + void freeAll(); + + ChunkAllocator *chunkAllocator; + HeapItem *nextFree = 0; + HeapItem *firstInChunk = 0; + HeapItem *lastInChunk = 0; + std::vector chunks; + uint currentChunk = 0; +}; + class Q_QML_EXPORT MemoryManager { Q_DISABLE_COPY(MemoryManager); @@ -86,6 +132,11 @@ public: static inline std::size_t align(std::size_t size) { return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); } + QV4::Heap::CallContext *allocSimpleCallContext() + { return stackAllocator.allocate(); } + void freeSimpleCallContext() + { stackAllocator.free(); } + template inline typename ManagedType::Data *allocManaged(std::size_t size, std::size_t unmanagedSize = 0) { @@ -317,6 +368,7 @@ private: public: QV4::ExecutionEngine *engine; ChunkAllocator *chunkAllocator; + StackAllocator stackAllocator; QScopedPointer m_d; PersistentValueStorage *m_persistentValues; PersistentValueStorage *m_weakValues; -- cgit v1.2.3