diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2018-07-27 11:47:53 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2018-08-01 09:42:06 +0000 |
commit | e99038b24d8b9f4a8cc503d1e3f789a93a3e8330 (patch) | |
tree | 05a43cf4833f5eba65f54e7fbb61fa49575ba14d /src/qml/compiler/qqmlirbuilder.cpp | |
parent | 73fc89bb0dbe9a866e7b1cf47a42a85e5042fead (diff) |
Reduce memory consumption when loading AOT generated cache files
Separate the qml data (objects/imports) from the general compilation
unit data. It's only the former that needs to be re-generated as part of
the type re-compilation and by separating it we can allocate memory just
for that and keep using the mmap'ed general unit data for everything
else (including byte code).
Another upside of this change is that it allows eliminating the recently
introduced concept of a backing unit again.
Saves ~149K RAM with the QQC1 gallery.
Task-number: QTBUG-69588
Change-Id: Ie88a4286feb7e2f472f58a28fa5dd6ff0a91c4b6
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/qml/compiler/qqmlirbuilder.cpp')
-rw-r--r-- | src/qml/compiler/qqmlirbuilder.cpp | 156 |
1 files changed, 75 insertions, 81 deletions
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index e544559e03..e9685597f6 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1553,34 +1553,70 @@ bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *prope void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) { + output.jsGenerator.stringTable.registerString(output.jsModule.fileName); + output.jsGenerator.stringTable.registerString(output.jsModule.finalUrl); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit; const QV4::CompiledData::Unit *jsUnit = nullptr; - uint jsUnitSize = 0; + std::function<QV4::CompiledData::QmlUnit *(QV4::CompiledData::QmlUnit *, uint)> unitFinalizer + = [](QV4::CompiledData::QmlUnit *unit, uint) { + return unit; + }; // We may already have unit data if we're loading an ahead-of-time generated cache file. if (compilationUnit->data) { - jsUnit = const_cast<QV4::CompiledData::Unit*>(compilationUnit->data); - // Discard the old QML tables we will re-create further down anyway. - quint32 unitSizeWithoutQMLTables = jsUnit->offsetToImports; - jsUnitSize = unitSizeWithoutQMLTables; + jsUnit = compilationUnit->data; +#ifndef V4_BOOTSTRAP + output.javaScriptCompilationUnit->dynamicStrings = output.jsGenerator.stringTable.allStrings(); +#endif } else { - jsUnit = output.jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable); - jsUnitSize = jsUnit->unitSize; - } - - output.jsGenerator.stringTable.registerString(output.jsModule.fileName); - output.jsGenerator.stringTable.registerString(output.jsModule.finalUrl); + QV4::CompiledData::Unit *createdUnit; + jsUnit = createdUnit = output.jsGenerator.generateUnit(); - // No more new strings after this point, we're calculating offsets. - output.jsGenerator.stringTable.freeze(); + // enable flag if we encountered pragma Singleton + for (Pragma *p : qAsConst(output.pragmas)) { + if (p->type == Pragma::PragmaSingleton) { + createdUnit->flags |= QV4::CompiledData::Unit::IsSingleton; + break; + } + } + // This unit's memory was allocated with malloc on the heap, so it's + // definitely not suitable for StaticData access. + createdUnit->flags &= ~QV4::CompiledData::Unit::StaticData; - const bool writeStringTable = #ifndef V4_BOOTSTRAP - output.javaScriptCompilationUnit->backingUnit == nullptr; + if (dependencyHasher) { + QCryptographicHash hash(QCryptographicHash::Md5); + if (dependencyHasher(&hash)) { + QByteArray checksum = hash.result(); + Q_ASSERT(checksum.size() == sizeof(createdUnit->dependencyMD5Checksum)); + memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(), sizeof(createdUnit->dependencyMD5Checksum)); + } + } #else - true; + Q_UNUSED(dependencyHasher); #endif + createdUnit->sourceFileIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.fileName); + createdUnit->finalUrlIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.finalUrl); + + // Combine the qml data into the general unit data. + unitFinalizer = [&jsUnit](QV4::CompiledData::QmlUnit *qmlUnit, uint qmlDataSize) { + void *ptr = const_cast<QV4::CompiledData::Unit*>(jsUnit); + QV4::CompiledData::Unit *newUnit = (QV4::CompiledData::Unit *)realloc(ptr, jsUnit->unitSize + qmlDataSize); + jsUnit = newUnit; + newUnit->offsetToQmlUnit = newUnit->unitSize; + newUnit->unitSize += qmlDataSize; + memcpy(const_cast<QV4::CompiledData::QmlUnit *>(newUnit->qmlUnit()), qmlUnit, qmlDataSize); + free(const_cast<QV4::CompiledData::QmlUnit*>(qmlUnit)); + qmlUnit = nullptr; + newUnit->generateChecksum(); + return const_cast<QV4::CompiledData::QmlUnit*>(newUnit->qmlUnit()); + }; + } + + // No more new strings after this point, we're calculating offsets. + output.jsGenerator.stringTable.freeze(); const int importSize = sizeof(QV4::CompiledData::Import) * output.imports.count(); const int objectOffsetTableSize = output.objects.count() * sizeof(quint32); @@ -1589,7 +1625,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen int objectsSize = 0; for (Object *o : qAsConst(output.objects)) { - objectOffsets.insert(o, jsUnitSize + importSize + objectOffsetTableSize + objectsSize); + objectOffsets.insert(o, sizeof(QV4::CompiledData::QmlUnit) + importSize + objectOffsetTableSize + objectsSize); objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); int signalTableSize = 0; @@ -1605,43 +1641,14 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen objectsSize += enumTableSize; } - const int totalSize = jsUnitSize + importSize + objectOffsetTableSize + objectsSize + (writeStringTable ? output.jsGenerator.stringTable.sizeOfTableAndData() : 0); + const uint totalSize = sizeof(QV4::CompiledData::QmlUnit) + importSize + objectOffsetTableSize + objectsSize; char *data = (char*)malloc(totalSize); - memcpy(data, jsUnit, jsUnitSize); - memset(data + jsUnitSize, 0, totalSize - jsUnitSize); - jsUnit = nullptr; - - QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(data); - qmlUnit->unitSize = totalSize; - // This unit's memory was allocated with malloc on the heap, so it's - // definitely not suitable for StaticData access. - qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; - qmlUnit->offsetToImports = jsUnitSize; + memset(data, 0, totalSize); + QV4::CompiledData::QmlUnit *qmlUnit = reinterpret_cast<QV4::CompiledData::QmlUnit *>(data); + qmlUnit->offsetToImports = sizeof(*qmlUnit); qmlUnit->nImports = output.imports.count(); - qmlUnit->offsetToObjects = jsUnitSize + importSize; + qmlUnit->offsetToObjects = qmlUnit->offsetToImports + importSize; qmlUnit->nObjects = output.objects.count(); - if (writeStringTable) { - qmlUnit->offsetToStringTable = totalSize - output.jsGenerator.stringTable.sizeOfTableAndData(); - qmlUnit->stringTableSize = output.jsGenerator.stringTable.stringCount(); - } else { - qmlUnit->offsetToStringTable = 0; - qmlUnit->stringTableSize = 0; - } - qmlUnit->sourceFileIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.fileName); - qmlUnit->finalUrlIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.finalUrl); - -#ifndef V4_BOOTSTRAP - if (dependencyHasher) { - QCryptographicHash hash(QCryptographicHash::Md5); - if (dependencyHasher(&hash)) { - QByteArray checksum = hash.result(); - Q_ASSERT(checksum.size() == sizeof(qmlUnit->dependencyMD5Checksum)); - memcpy(qmlUnit->dependencyMD5Checksum, checksum.constData(), sizeof(qmlUnit->dependencyMD5Checksum)); - } - } -#else - Q_UNUSED(dependencyHasher); -#endif // write imports char *importPtr = data + qmlUnit->offsetToImports; @@ -1775,46 +1782,31 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen objectPtr += enumTableSize; } - // enable flag if we encountered pragma Singleton - for (Pragma *p : qAsConst(output.pragmas)) { - if (p->type == Pragma::PragmaSingleton) { - qmlUnit->flags |= QV4::CompiledData::Unit::IsSingleton; - break; - } - } - - if (writeStringTable) - output.jsGenerator.stringTable.serialize(qmlUnit); -#ifndef V4_BOOTSTRAP - else - output.javaScriptCompilationUnit->dynamicStrings = output.jsGenerator.stringTable.allStrings(); -#endif - - qmlUnit->generateChecksum(); + qmlUnit = unitFinalizer(qmlUnit, totalSize); static const bool showStats = qEnvironmentVariableIsSet("QML_SHOW_UNIT_STATS"); if (showStats) { - qDebug() << "Generated QML unit that is" << qmlUnit->unitSize << "bytes big contains:"; - qDebug() << " " << qmlUnit->functionTableSize << "functions"; - qDebug() << " " << jsUnitSize << "for JS unit"; + qDebug() << "Generated QML unit that is" << totalSize << "bytes big contains:"; + qDebug() << " " << jsUnit->functionTableSize << "functions"; + qDebug() << " " << jsUnit->unitSize << "for JS unit"; qDebug() << " " << importSize << "for imports"; qDebug() << " " << objectsSize << "for" << qmlUnit->nObjects << "objects"; quint32 totalBindingCount = 0; for (quint32 i = 0; i < qmlUnit->nObjects; ++i) - totalBindingCount += qmlUnit->objectAtInternal(i)->nBindings; + totalBindingCount += qmlUnit->objectAt(i)->nBindings; qDebug() << " " << totalBindingCount << "bindings"; quint32 totalCodeSize = 0; - for (quint32 i = 0; i < qmlUnit->functionTableSize; ++i) - totalCodeSize += qmlUnit->functionAt(i)->codeSize; + for (quint32 i = 0; i < jsUnit->functionTableSize; ++i) + totalCodeSize += jsUnit->functionAt(i)->codeSize; qDebug() << " " << totalCodeSize << "bytes total byte code"; - qDebug() << " " << qmlUnit->stringTableSize << "strings"; + qDebug() << " " << jsUnit->stringTableSize << "strings"; quint32 totalStringSize = 0; - for (quint32 i = 0; i < qmlUnit->stringTableSize; ++i) - totalStringSize += QV4::CompiledData::String::calculateSize(qmlUnit->stringAtInternal(i)); + for (quint32 i = 0; i < jsUnit->stringTableSize; ++i) + totalStringSize += QV4::CompiledData::String::calculateSize(jsUnit->stringAtInternal(i)); qDebug() << " " << totalStringSize << "bytes total strings"; } - compilationUnit->setUnitData(qmlUnit); + compilationUnit->setUnitData(jsUnit, qmlUnit, output.jsModule.fileName, output.jsModule.finalUrl); } char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const @@ -2388,8 +2380,10 @@ void IRLoader::load() { output->jsGenerator.stringTable.initializeFromBackingUnit(unit); - for (quint32 i = 0; i < unit->nImports; ++i) - output->imports << unit->importAtInternal(i); + const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit(); + + for (quint32 i = 0; i < qmlUnit->nImports; ++i) + output->imports << qmlUnit->importAt(i); if (unit->flags & QV4::CompiledData::Unit::IsSingleton) { QmlIR::Pragma *p = New<QmlIR::Pragma>(); @@ -2398,8 +2392,8 @@ void IRLoader::load() output->pragmas << p; } - for (uint i = 0; i < unit->nObjects; ++i) { - const QV4::CompiledData::Object *serializedObject = unit->objectAtInternal(i); + for (uint i = 0; i < qmlUnit->nObjects; ++i) { + const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i); QmlIR::Object *object = loadObject(serializedObject); output->objects.append(object); } |