diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2016-10-22 04:08:27 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2016-10-23 13:36:47 +0000 |
commit | 161473721dcdfd45e6618457a2c0a6e6b10bfada (patch) | |
tree | f7cc34578c4509f22ee97a1ce2910833bef4ae58 | |
parent | e13eece273195a9f39d29712a233a8dd00ddf71b (diff) |
Add the ability to explicitly enable the code cache in qmljs
This also requires mapping the label pointers in the byte code back to
the instruction enum. Fortunately this reverse mapping is only needed in
the qmljs case. Normally in the QML engine we persist the byte-code to
disk before linking the compilation unit to the engine (where we map the
enum to goto labels).
Change-Id: If0b79288274bb1031161841b63a85f164502aaec
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 1 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth.cpp | 37 | ||||
-rw-r--r-- | tools/qmljs/qmljs.cpp | 45 |
3 files changed, 74 insertions, 9 deletions
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index b83bcdb83b..ca4e0b73d4 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -253,6 +253,7 @@ union Instr { enum Type { FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) + LastInstruction }; struct instr_common { diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index cd47f22205..ca6319ef3c 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -1477,6 +1477,17 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit QByteArray padding; +#ifdef MOTH_THREADED_INTERPRETER + // Map from instruction label back to instruction type. Only needed when persisting + // already linked compilation units; + QHash<void*, int> reverseInstructionMapping; + if (engine) { + void **instructions = VME::instructionJumpTable(); + for (int i = 0; i < Instr::LastInstruction; ++i) + reverseInstructionMapping.insert(instructions[i], i); + } +#endif + for (int i = 0; i < codeRefs.size(); ++i) { const CompiledData::Function *compiledFunction = unit->functionAt(i); @@ -1493,8 +1504,30 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit return false; } - const void *codePtr = codeRefs.at(i).constData(); - written = device->write(reinterpret_cast<const char *>(codePtr), compiledFunction->codeSize); + QByteArray code = codeRefs.at(i); + +#ifdef MOTH_THREADED_INTERPRETER + if (!reverseInstructionMapping.isEmpty()) { + char *codePtr = code.data(); // detaches + int index = 0; + while (index < code.size()) { + Instr *genericInstr = reinterpret_cast<Instr *>(codePtr + index); + + genericInstr->common.instructionType = reverseInstructionMapping.value(genericInstr->common.code); + + switch (genericInstr->common.instructionType) { + #define REVERSE_INSTRUCTION(InstructionType, Member) \ + case Instr::InstructionType: \ + index += InstrMeta<(int)Instr::InstructionType>::Size; \ + break; + + FOR_EACH_MOTH_INSTR(REVERSE_INSTRUCTION) + } + } + } +#endif + + written = device->write(code.constData(), compiledFunction->codeSize); if (written != qint64(compiledFunction->codeSize)) { *errorString = device->errorString(); return false; diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp index b4ffc9be16..4b63357363 100644 --- a/tools/qmljs/qmljs.cpp +++ b/tools/qmljs/qmljs.cpp @@ -51,6 +51,8 @@ QT_REQUIRE_CONFIG(qml_interpreter); #include <QtCore/QCoreApplication> #include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QDateTime> #include <private/qqmljsengine_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> @@ -147,6 +149,7 @@ int main(int argc, char *argv[]) #endif bool runAsQml = false; + bool cache = false; if (!args.isEmpty()) { if (args.first() == QLatin1String("--jit")) { @@ -166,6 +169,11 @@ int main(int argc, char *argv[]) args.removeFirst(); } + if (args.first() == QLatin1String("--cache")) { + cache = true; + args.removeFirst(); + } + if (args.first() == QLatin1String("--help")) { std::cerr << "Usage: qmljs [|--jit|--interpret|--qml] file..." << std::endl; return EXIT_SUCCESS; @@ -199,15 +207,38 @@ int main(int argc, char *argv[]) for (const QString &fn : qAsConst(args)) { QFile file(fn); if (file.open(QFile::ReadOnly)) { - const QString code = QString::fromUtf8(file.readAll()); - file.close(); + QScopedPointer<QV4::Script> script; + if (cache && QFile::exists(fn + QLatin1Char('c'))) { + QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = iSelFactory->createUnitForLoading(); + QString error; + if (unit->loadFromDisk(QUrl::fromLocalFile(fn), iSelFactory, &error)) { + script.reset(new QV4::Script(&vm, nullptr, unit)); + } else { + std::cout << "Error loading" << qPrintable(fn) << "from disk cache:" << qPrintable(error) << std::endl; + } + } + if (!script) { + const QString code = QString::fromUtf8(file.readAll()); + file.close(); + script.reset(new QV4::Script(ctx, code, fn)); + script->parseAsBinding = runAsQml; + script->parse(); + } QV4::ScopedValue result(scope); - QV4::Script script(ctx, code, fn); - script.parseAsBinding = runAsQml; - script.parse(); - if (!scope.engine->hasException) - result = script.run(); + if (!scope.engine->hasException) { + const auto unit = script->compilationUnit; + if (cache && unit && !(unit->data->flags & QV4::CompiledData::Unit::StaticData)) { + if (unit->data->sourceTimeStamp == 0) { + const_cast<QV4::CompiledData::Unit*>(unit->data)->sourceTimeStamp = QFileInfo(fn).lastModified().toMSecsSinceEpoch(); + } + QString saveError; + if (!unit->saveToDisk(QUrl::fromLocalFile(fn), &saveError)) { + std::cout << "Error saving JS cache file: " << qPrintable(saveError) << std::endl; + } + } + result = script->run(); + } if (scope.engine->hasException) { QV4::StackTrace trace; QV4::ScopedValue ex(scope, scope.engine->catchException(&trace)); |