aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2016-12-22 21:24:27 +0100
committerLars Knoll <lars.knoll@qt.io>2017-01-25 08:30:36 +0000
commita325b21e1bd51263551829089e9f31e2156bc641 (patch)
tree9df265b8821254a01e66abef31e4009b18a37c4c
parent90f055dbb44846b9ed37be8b9114573fcf2cd8ff (diff)
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 <simon.hausmann@qt.io>
-rw-r--r--src/qml/jsruntime/qv4context.cpp38
-rw-r--r--src/qml/jsruntime/qv4context_p.h14
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp22
-rw-r--r--src/qml/memory/qv4mm.cpp45
-rw-r--r--src/qml/memory/qv4mm_p.h52
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<WithContext>(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<const WithContext *>(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<QV4::CallContext *>(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<QV4::CallContext *>(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<typename T>
+StackAllocator<T>::StackAllocator(ChunkAllocator *chunkAlloc)
+ : chunkAllocator(chunkAlloc)
+{
+ chunks.push_back(chunkAllocator->allocate());
+ firstInChunk = chunks.back()->first();
+ nextFree = firstInChunk;
+ lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots;
+}
+
+template<typename T>
+void StackAllocator<T>::freeAll()
+{
+ for (auto c : chunks)
+ chunkAllocator->free(c);
+}
+
+template<typename T>
+void StackAllocator<T>::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<typename T>
+void QV4::StackAllocator<T>::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<Heap::CallContext>;
+
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<typename T>
+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<T>();
+ 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<Chunk *> 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<typename ManagedType>
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<Heap::CallContext> stackAllocator;
QScopedPointer<Data> m_d;
PersistentValueStorage *m_persistentValues;
PersistentValueStorage *m_weakValues;