aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2017-04-03 13:35:26 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2017-04-03 13:35:30 +0200
commit0eeb7ada04cc81d0ab1b61747bdf92fd7c33e1ec (patch)
treebe4d0201b81b098a2976e857b5c6642f9c96e6ac /src/qml/compiler
parent349d3400c11c0ad1c9aaec01c44b174dbb6ebf9a (diff)
parente4894fe13d178b6aa8b5580b402df2d1b4f2615c (diff)
Merge remote-tracking branch 'origin/dev' into wip/scenegraphng
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp26
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h4
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp8
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h4
-rw-r--r--src/qml/compiler/qv4codegen.cpp16
-rw-r--r--src/qml/compiler/qv4compilationunitmapper.cpp9
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_p.h4
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_unix.cpp5
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_win.cpp4
-rw-r--r--src/qml/compiler/qv4compileddata.cpp66
-rw-r--r--src/qml/compiler/qv4compileddata_p.h32
-rw-r--r--src/qml/compiler/qv4compiler.cpp4
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h2
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp75
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h2
-rw-r--r--src/qml/compiler/qv4jsir.cpp6
-rw-r--r--src/qml/compiler/qv4jsir_p.h40
-rw-r--r--src/qml/compiler/qv4ssa.cpp195
-rw-r--r--src/qml/compiler/qv4ssa_p.h13
19 files changed, 318 insertions, 197 deletions
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 030f485504..218f5675dc 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -1361,7 +1361,7 @@ bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *prope
return QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr);
}
-QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, QQmlEngine *engine, const QV4::CompiledData::ResolvedTypeReferenceMap &dependentTypes)
+QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
{
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit;
QV4::CompiledData::Unit *jsUnit = compilationUnit->createUnitData(&output);
@@ -1404,17 +1404,16 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, QQmlEngine
qmlUnit->stringTableSize = output.jsGenerator.stringTable.stringCount();
#ifndef V4_BOOTSTRAP
- if (!dependentTypes.isEmpty()) {
+ if (dependencyHasher) {
QCryptographicHash hash(QCryptographicHash::Md5);
- if (dependentTypes.addToHash(&hash, engine)) {
+ 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(dependentTypes);
- Q_UNUSED(engine);
+ Q_UNUSED(dependencyHasher);
#endif
// write imports
@@ -2116,7 +2115,8 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias;
object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias;
-
+ object->flags = serializedObject->flags;
+ object->id = serializedObject->id;
object->location = serializedObject->location;
object->locationOfIdProperty = serializedObject->locationOfIdProperty;
@@ -2175,6 +2175,15 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
object->properties->append(p);
}
+ {
+ const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable();
+ for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) {
+ QmlIR::Alias *a = pool->New<QmlIR::Alias>();
+ *static_cast<QV4::CompiledData::Alias*>(a) = *serializedAlias;
+ object->aliases->append(a);
+ }
+ }
+
QQmlJS::Engine *jsParserEngine = &output->jsParserEngine;
const QV4::CompiledData::LEUInt32 *functionIdx = serializedObject->functionOffsetTable();
@@ -2205,6 +2214,11 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
const QString name = unit->stringAt(compiledFunction->nameIndex);
f->functionDeclaration = new(pool) QQmlJS::AST::FunctionDeclaration(jsParserEngine->newStringRef(name), paramList, /*body*/0);
+ f->formals.allocate(pool, int(compiledFunction->nFormals));
+ formalNameIdx = compiledFunction->formalsTable();
+ for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx)
+ f->formals[i] = *formalNameIdx;
+
object->functions->append(f);
}
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 2022112e07..64bf111d9a 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -548,7 +548,7 @@ public:
struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator
{
- QV4::CompiledData::Unit *generate(Document &output, QQmlEngine *engine, const QV4::CompiledData::ResolvedTypeReferenceMap &dependentTypes);
+ QV4::CompiledData::Unit *generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher());
private:
typedef bool (Binding::*BindingFilter)() const;
@@ -622,7 +622,7 @@ private:
int _importedScriptsTemp;
};
-struct IRLoader {
+struct Q_QML_PRIVATE_EXPORT IRLoader {
IRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output);
void load();
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index 85267225be..a3b8784fc8 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -58,10 +58,11 @@ QT_BEGIN_NAMESPACE
QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData,
QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache)
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
: resolvedTypes(resolvedTypeCache)
, engine(engine)
, typeData(typeData)
+ , dependencyHasher(dependencyHasher)
, typeNameCache(typeNameCache)
, document(parsedQML)
{
@@ -156,7 +157,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
// Generate QML compiled type data structures
QmlIR::QmlUnitGenerator qmlGenerator;
- QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document, QQmlEnginePrivate::get(engine), resolvedTypes);
+ QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document, dependencyHasher);
Q_ASSERT(document->javaScriptCompilationUnit);
// The js unit owns the data and will free the qml unit.
@@ -1108,10 +1109,9 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv
continue;
}
- // Try again later and resolve the target alias first.
- _objectsWithAliases.append(objectIndex);
// restore
alias->idIndex = idIndex;
+ // Try again later and resolve the target alias first.
break;
}
}
diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h
index 2b59e7e42f..11261e3099 100644
--- a/src/qml/compiler/qqmltypecompiler_p.h
+++ b/src/qml/compiler/qqmltypecompiler_p.h
@@ -89,7 +89,8 @@ struct QQmlTypeCompiler
{
Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler)
public:
- QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache);
+ QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache,
+ const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
// --- interface used by QQmlPropertyCacheCreator
typedef QmlIR::Object CompiledObject;
@@ -139,6 +140,7 @@ private:
QList<QQmlError> errors;
QQmlEnginePrivate *engine;
QQmlTypeData *typeData;
+ const QV4::CompiledData::DependentTypesHasher &dependencyHasher;
QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
QmlIR::Document *document;
// index is string index of type name (use obj->inheritedTypeNameIndex)
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 0afc97e4bf..3234e7ee63 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -1497,7 +1497,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col)
IR::Function *f = _function;
while (f && e->parent) {
- if (f->insideWithOrCatch || (f->isNamedExpression && f->name == name))
+ if (f->insideWithOrCatch || (f->isNamedExpression && QStringRef(f->name) == name))
return _block->NAME(name, line, col);
int index = e->findMember(name);
@@ -1508,7 +1508,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col)
al->isArgumentsOrEval = true;
return al;
}
- const int argIdx = f->indexOfArgument(&name);
+ const int argIdx = f->indexOfArgument(QStringRef(&name));
if (argIdx != -1)
return _block->ARG(argIdx, scope);
@@ -2269,7 +2269,7 @@ bool Codegen::visit(DoWhileStatement *ast)
_block = loopbody;
statement(ast->statement);
- _block->JUMP(loopcond);
+ setLocation(_block->JUMP(loopcond), ast->statement->lastSourceLocation());
_block = loopcond;
condition(ast->expression, loopbody, loopend);
@@ -2334,7 +2334,7 @@ bool Codegen::visit(ForEachStatement *ast)
return false;
move(*init, _block->TEMP(temp));
statement(ast->statement);
- _block->JUMP(foreachin);
+ setLocation(_block->JUMP(foreachin), ast->lastSourceLocation());
_block = foreachin;
@@ -2373,7 +2373,7 @@ bool Codegen::visit(ForStatement *ast)
_block = forbody;
statement(ast->statement);
- _block->JUMP(forstep);
+ setLocation(_block->JUMP(forstep), ast->lastSourceLocation());
_block = forstep;
statement(ast->expression);
@@ -2473,7 +2473,7 @@ bool Codegen::visit(LocalForEachStatement *ast)
int temp = _block->newTemp();
move(identifier(ast->declaration->name.toString()), _block->TEMP(temp));
statement(ast->statement);
- _block->JUMP(foreachin);
+ setLocation(_block->JUMP(foreachin), ast->lastSourceLocation());
_block = foreachin;
@@ -2512,7 +2512,7 @@ bool Codegen::visit(LocalForStatement *ast)
_block = forbody;
statement(ast->statement);
- _block->JUMP(forstep);
+ setLocation(_block->JUMP(forstep), ast->lastSourceLocation());
_block = forstep;
statement(ast->expression);
@@ -2813,7 +2813,7 @@ bool Codegen::visit(WhileStatement *ast)
_block = whilebody;
statement(ast->statement);
- _block->JUMP(whilecond);
+ setLocation(_block->JUMP(whilecond), ast->lastSourceLocation());
_block = whileend;
leaveLoop();
diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp
index 2e1213464c..d94f7ac238 100644
--- a/src/qml/compiler/qv4compilationunitmapper.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper.cpp
@@ -59,7 +59,7 @@ CompilationUnitMapper::~CompilationUnitMapper()
close();
}
-bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, const QString &sourcePath, QString *errorString)
+bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, QDateTime sourceTimeStamp, QString *errorString)
{
if (strncmp(header->magic, CompiledData::magic_str, sizeof(header->magic))) {
*errorString = QStringLiteral("Magic bytes in the header do not match");
@@ -76,12 +76,7 @@ bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, const
return false;
}
- {
- QFileInfo sourceCode(sourcePath);
- QDateTime sourceTimeStamp;
- if (sourceCode.exists())
- sourceTimeStamp = sourceCode.lastModified();
-
+ if (header->sourceTimeStamp) {
// Files from the resource system do not have any time stamps, so fall back to the application
// executable.
if (!sourceTimeStamp.isValid())
diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/compiler/qv4compilationunitmapper_p.h
index 5b6939f1cf..b24f98df7c 100644
--- a/src/qml/compiler/qv4compilationunitmapper_p.h
+++ b/src/qml/compiler/qv4compilationunitmapper_p.h
@@ -68,11 +68,11 @@ public:
CompilationUnitMapper();
~CompilationUnitMapper();
- CompiledData::Unit *open(const QString &cacheFilePath, const QString &sourcePath, QString *errorString);
+ CompiledData::Unit *open(const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString);
void close();
private:
- static bool verifyHeader(const QV4::CompiledData::Unit *header, const QString &sourcePath, QString *errorString);
+ static bool verifyHeader(const QV4::CompiledData::Unit *header, QDateTime sourceTimeStamp, QString *errorString);
#if defined(Q_OS_UNIX)
size_t length;
diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
index 1aa3e05f5f..38dabc41cf 100644
--- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
@@ -43,6 +43,7 @@
#include <functional>
#include <private/qcore_unix_p.h>
#include <private/qdeferredcleanup_p.h>
+#include <QDateTime>
#include "qv4compileddata_p.h"
@@ -50,7 +51,7 @@ QT_BEGIN_NAMESPACE
using namespace QV4;
-CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QString &sourcePath, QString *errorString)
+CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QDateTime &sourceTimeStamp, QString *errorString)
{
close();
@@ -72,7 +73,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
return nullptr;
}
- if (!verifyHeader(&header, sourcePath, errorString))
+ if (!verifyHeader(&header, sourceTimeStamp, errorString))
return nullptr;
// Data structure and qt version matched, so now we can access the rest of the file safely.
diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp
index 37cac846a0..d7a93ae233 100644
--- a/src/qml/compiler/qv4compilationunitmapper_win.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp
@@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE
using namespace QV4;
-CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QString &sourcePath, QString *errorString)
+CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QDateTime &sourceTimeStamp, QString *errorString)
{
close();
@@ -87,7 +87,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
return nullptr;
}
- if (!verifyHeader(&header, sourcePath, errorString))
+ if (!verifyHeader(&header, sourceTimeStamp, errorString))
return nullptr;
const uint mappingFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 8f8d374e24..c56f08c2f0 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -77,13 +77,7 @@ namespace QV4 {
namespace CompiledData {
-#ifdef V4_BOOTSTRAP
-static QString cacheFilePath(const QString &localSourcePath)
-{
- const QString localCachePath = localSourcePath + QLatin1Char('c');
- return localCachePath;
-}
-#else
+#if !defined(V4_BOOTSTRAP)
static QString cacheFilePath(const QUrl &url)
{
const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
@@ -102,7 +96,6 @@ static QString cacheFilePath(const QUrl &url)
CompilationUnit::CompilationUnit()
: data(0)
, engine(0)
- , runtimeStrings(0)
, runtimeLookups(0)
, runtimeRegularExpressions(0)
, runtimeClasses(0)
@@ -132,8 +125,10 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
runtimeStrings = (QV4::Heap::String **)malloc(data->stringTableSize * sizeof(QV4::Heap::String*));
// memset the strings to 0 in case a GC run happens while we're within the loop below
memset(runtimeStrings, 0, data->stringTableSize * sizeof(QV4::Heap::String*));
- for (uint i = 0; i < data->stringTableSize; ++i)
+ for (uint i = 0; i < data->stringTableSize; ++i) {
runtimeStrings[i] = engine->newIdentifier(data->stringAt(i));
+ runtimeStrings[i]->setMarkBit();
+ }
runtimeRegularExpressions = new QV4::Value[data->regexpTableSize];
// memset the regexps to 0 in case a GC run happens while we're within the loop below
@@ -147,7 +142,14 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
flags |= IR::RegExp::RegExp_IgnoreCase;
if (re->flags & CompiledData::RegExp::RegExp_Multiline)
flags |= IR::RegExp::RegExp_Multiline;
- runtimeRegularExpressions[i] = engine->newRegExpObject(data->stringAt(re->stringIndex), flags);
+ QV4::Heap::RegExpObject *ro = engine->newRegExpObject(data->stringAt(re->stringIndex), flags);
+ runtimeRegularExpressions[i] = ro;
+#if WRITEBARRIER(steele)
+ if (engine->memoryManager->nextGCIsIncremental) {
+ ro->setMarkBit();
+ ro->setGrayBit();
+ }
+#endif
}
if (data->lookupTableSize) {
@@ -174,8 +176,6 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
l->level = -1;
l->index = UINT_MAX;
l->nameIndex = compiledLookups[i].nameIndex;
- if (type == CompiledData::Lookup::Type_IndexedGetter || type == CompiledData::Lookup::Type_IndexedSetter)
- l->engine = engine;
}
}
@@ -331,10 +331,9 @@ void CompilationUnit::finalize(QQmlEnginePrivate *engine)
totalObjectCount = objectCount;
}
-bool CompilationUnit::verifyChecksum(QQmlEngine *engine,
- const ResolvedTypeReferenceMap &dependentTypes) const
+bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHasher) const
{
- if (dependentTypes.isEmpty()) {
+ if (!dependencyHasher) {
for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) {
if (data->dependencyMD5Checksum[i] != 0)
return false;
@@ -342,7 +341,7 @@ bool CompilationUnit::verifyChecksum(QQmlEngine *engine,
return true;
}
QCryptographicHash hash(QCryptographicHash::Md5);
- if (!dependentTypes.addToHash(&hash, engine))
+ if (!dependencyHasher(&hash))
return false;
QByteArray checksum = hash.result();
Q_ASSERT(checksum.size() == sizeof(data->dependencyMD5Checksum));
@@ -350,7 +349,7 @@ bool CompilationUnit::verifyChecksum(QQmlEngine *engine,
sizeof(data->dependencyMD5Checksum)) == 0;
}
-bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString)
+bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, EvalISelFactory *iselFactory, QString *errorString)
{
if (!QQmlFile::isLocalFile(url)) {
*errorString = QStringLiteral("File has to be a local file.");
@@ -360,14 +359,14 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory
const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url);
QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper());
- CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString);
+ CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourceTimeStamp, errorString);
if (!mappedUnit)
return false;
const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr;
QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit);
- if (sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
+ if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
*errorString = QStringLiteral("QML source file has moved to a different location.");
return false;
}
@@ -408,27 +407,29 @@ bool CompilationUnit::memoryMapCode(QString *errorString)
#endif // V4_BOOTSTRAP
#if defined(V4_BOOTSTRAP)
-bool CompilationUnit::saveToDisk(const QString &unitUrl, QString *errorString)
+bool CompilationUnit::saveToDisk(const QString &outputFileName, QString *errorString)
#else
bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
#endif
{
errorString->clear();
+#if !defined(V4_BOOTSTRAP)
if (data->sourceTimeStamp == 0) {
*errorString = QStringLiteral("Missing time stamp for source file");
return false;
}
-#if !defined(V4_BOOTSTRAP)
if (!QQmlFile::isLocalFile(unitUrl)) {
*errorString = QStringLiteral("File has to be a local file.");
return false;
}
+ const QString outputFileName = cacheFilePath(unitUrl);
#endif
+#if QT_CONFIG(temporaryfile)
// Foo.qml -> Foo.qmlc
- QSaveFile cacheFile(cacheFilePath(unitUrl));
+ QSaveFile cacheFile(outputFileName);
if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
*errorString = cacheFile.errorString();
return false;
@@ -459,6 +460,10 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
}
return true;
+#else
+ *errorString = QStringLiteral("features.temporaryfile is disabled.");
+ return false;
+#endif // QT_CONFIG(temporaryfile)
}
void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit)
@@ -480,10 +485,22 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable);
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = irDocument->javaScriptCompilationUnit;
- QV4::CompiledData::Unit *jsUnit = const_cast<QV4::CompiledData::Unit*>(irDocument->javaScriptCompilationUnit->data);
+ QV4::CompiledData::Unit *jsUnit = const_cast<QV4::CompiledData::Unit*>(compilationUnit->data);
+ auto ensureWritableUnit = [&jsUnit, &compilationUnit]() {
+ if (jsUnit == compilationUnit->data) {
+ char *unitCopy = (char*)malloc(jsUnit->unitSize);
+ memcpy(unitCopy, jsUnit, jsUnit->unitSize);
+ jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitCopy);
+ }
+ };
QV4::Compiler::StringTableGenerator &stringTable = irDocument->jsGenerator.stringTable;
+ if (jsUnit->sourceFileIndex == quint32(0) || jsUnit->stringAt(jsUnit->sourceFileIndex) != irDocument->jsModule.fileName) {
+ ensureWritableUnit();
+ jsUnit->sourceFileIndex = stringTable.registerString(irDocument->jsModule.fileName);
+ }
+
// Collect signals that have had a change in signature (from onClicked to onClicked(mouse) for example)
// and now need fixing in the QV4::CompiledData. Also register strings at the same time, to finalize
// the string table.
@@ -546,6 +563,7 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
}
if (!signalParameterNameTable.isEmpty()) {
+ ensureWritableUnit();
Q_ASSERT(jsUnit != compilationUnit->data);
const uint signalParameterTableSize = signalParameterNameTable.count() * sizeof(quint32);
uint newSize = jsUnit->unitSize + signalParameterTableSize;
@@ -764,7 +782,7 @@ void Unit::generateChecksum()
#ifndef V4_BOOTSTRAP
QCryptographicHash hash(QCryptographicHash::Md5);
- const int checksummableDataOffset = qOffsetOf(QV4::CompiledData::Unit, md5Checksum) + sizeof(md5Checksum);
+ const int checksummableDataOffset = offsetof(QV4::CompiledData::Unit, md5Checksum) + sizeof(md5Checksum);
const char *dataPtr = reinterpret_cast<const char *>(this) + checksummableDataOffset;
hash.addData(dataPtr, unitSize - checksummableDataOffset);
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 13a0c4b075..6e9121b5e3 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -71,7 +71,7 @@
QT_BEGIN_NAMESPACE
// Bump this whenever the compiler data structures change in an incompatible way.
-#define QV4_DATA_STRUCTURE_VERSION 0x09
+#define QV4_DATA_STRUCTURE_VERSION 0x11
class QIODevice;
class QQmlPropertyCache;
@@ -211,7 +211,8 @@ struct Function
HasDirectEval = 0x2,
UsesArgumentsObject = 0x4,
IsNamedExpression = 0x8,
- HasCatchOrWith = 0x10
+ HasCatchOrWith = 0x10,
+ CanUseSimpleCall = 0x20
};
// Absolute offset into file where the code for this function is located. Only used when the function
@@ -786,8 +787,10 @@ struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*>
{
bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const;
};
+
+using DependentTypesHasher = std::function<bool(QCryptographicHash *)>;
#else
-struct ResolvedTypeReferenceMap {};
+struct DependentTypesHasher {};
#endif
// index is per-object binding index
@@ -795,11 +798,15 @@ typedef QVector<QQmlPropertyData*> BindingPropertyData;
// This is how this hooks into the existing structures:
-//VM::Function
-// CompilationUnit * (for functions that need to clean up)
-// CompiledData::Function *compiledFunction
+struct Q_QML_PRIVATE_EXPORT CompilationUnitBase
+{
+ QV4::Heap::String **runtimeStrings = 0; // Array
+};
+
+Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value);
+Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0);
-struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount
+struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public QQmlRefCount
{
#ifdef V4_BOOTSTRAP
CompilationUnit()
@@ -818,11 +825,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount
#ifndef V4_BOOTSTRAP
ExecutionEngine *engine;
-#endif
-
- QV4::Heap::String **runtimeStrings; // Array
-#ifndef V4_BOOTSTRAP
QString fileName() const { return data->stringAt(data->sourceFileIndex); }
QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; }
@@ -860,8 +863,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount
QVector<QQmlScriptData *> dependentScripts;
ResolvedTypeReferenceMap resolvedTypes;
- bool verifyChecksum(QQmlEngine *engine,
- const ResolvedTypeReferenceMap &dependentTypes) const;
+ bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const;
int metaTypeId;
int listMetaTypeId;
@@ -899,7 +901,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount
void destroy() Q_DECL_OVERRIDE;
- bool loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString);
+ bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, EvalISelFactory *iselFactory, QString *errorString);
protected:
virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0;
@@ -908,7 +910,7 @@ protected:
public:
#if defined(V4_BOOTSTRAP)
- bool saveToDisk(const QString &unitUrl, QString *errorString);
+ bool saveToDisk(const QString &outputFileName, QString *errorString);
#else
bool saveToDisk(const QUrl &unitUrl, QString *errorString);
#endif
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 9cfac4a676..b81d724fe7 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -296,6 +296,8 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i
function->flags |= CompiledData::Function::IsNamedExpression;
if (irFunction->hasTry || irFunction->hasWith)
function->flags |= CompiledData::Function::HasCatchOrWith;
+ if (irFunction->canUseSimpleCall())
+ function->flags |= CompiledData::Function::CanUseSimpleCall;
function->nFormals = irFunction->formals.size();
function->formalsOffset = currentOffset;
currentOffset += function->nFormals * sizeof(quint32);
@@ -425,7 +427,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
}
unit.indexOfRootFunction = -1;
unit.sourceFileIndex = getStringId(irModule->fileName);
- unit.sourceTimeStamp = irModule->sourceTimeStamp;
+ unit.sourceTimeStamp = irModule->sourceTimeStamp.isValid() ? irModule->sourceTimeStamp.toMSecsSinceEpoch() : 0;
unit.nImports = 0;
unit.offsetToImports = 0;
unit.nObjects = 0;
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 53d9956315..fbd6ac8f99 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -690,7 +690,7 @@ union Instr
};
struct instr_binop {
MOTH_INSTR_HEADER
- uint alu; // offset inside the runtime methods
+ int alu; // QV4::Runtime::RuntimeMethods enum value
Param lhs;
Param rhs;
Param result;
diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp
index 04844302d9..aefb084971 100644
--- a/src/qml/compiler/qv4isel_moth.cpp
+++ b/src/qml/compiler/qv4isel_moth.cpp
@@ -55,70 +55,70 @@ using namespace QV4::Moth;
namespace {
-inline uint aluOpFunction(IR::AluOp op)
+inline QV4::Runtime::RuntimeMethods aluOpFunction(IR::AluOp op)
{
switch (op) {
case IR::OpInvalid:
- return 0;
+ return QV4::Runtime::InvalidRuntimeMethod;
case IR::OpIfTrue:
- return 0;
+ return QV4::Runtime::InvalidRuntimeMethod;
case IR::OpNot:
- return 0;
+ return QV4::Runtime::InvalidRuntimeMethod;
case IR::OpUMinus:
- return 0;
+ return QV4::Runtime::InvalidRuntimeMethod;
case IR::OpUPlus:
- return 0;
+ return QV4::Runtime::InvalidRuntimeMethod;
case IR::OpCompl:
- return 0;
+ return QV4::Runtime::InvalidRuntimeMethod;
case IR::OpBitAnd:
- return offsetof(QV4::Runtime, bitAnd);
+ return QV4::Runtime::bitAnd;
case IR::OpBitOr:
- return offsetof(QV4::Runtime, bitOr);
+ return QV4::Runtime::bitOr;
case IR::OpBitXor:
- return offsetof(QV4::Runtime, bitXor);
+ return QV4::Runtime::bitXor;
case IR::OpAdd:
- return 0;
+ return QV4::Runtime::InvalidRuntimeMethod;
case IR::OpSub:
- return offsetof(QV4::Runtime, sub);
+ return QV4::Runtime::sub;
case IR::OpMul:
- return offsetof(QV4::Runtime, mul);
+ return QV4::Runtime::mul;
case IR::OpDiv:
- return offsetof(QV4::Runtime, div);
+ return QV4::Runtime::div;
case IR::OpMod:
- return offsetof(QV4::Runtime, mod);
+ return QV4::Runtime::mod;
case IR::OpLShift:
- return offsetof(QV4::Runtime, shl);
+ return QV4::Runtime::shl;
case IR::OpRShift:
- return offsetof(QV4::Runtime, shr);
+ return QV4::Runtime::shr;
case IR::OpURShift:
- return offsetof(QV4::Runtime, ushr);
+ return QV4::Runtime::ushr;
case IR::OpGt:
- return offsetof(QV4::Runtime, greaterThan);
+ return QV4::Runtime::greaterThan;
case IR::OpLt:
- return offsetof(QV4::Runtime, lessThan);
+ return QV4::Runtime::lessThan;
case IR::OpGe:
- return offsetof(QV4::Runtime, greaterEqual);
+ return QV4::Runtime::greaterEqual;
case IR::OpLe:
- return offsetof(QV4::Runtime, lessEqual);
+ return QV4::Runtime::lessEqual;
case IR::OpEqual:
- return offsetof(QV4::Runtime, equal);
+ return QV4::Runtime::equal;
case IR::OpNotEqual:
- return offsetof(QV4::Runtime, notEqual);
+ return QV4::Runtime::notEqual;
case IR::OpStrictEqual:
- return offsetof(QV4::Runtime, strictEqual);
+ return QV4::Runtime::strictEqual;
case IR::OpStrictNotEqual:
- return offsetof(QV4::Runtime, strictNotEqual);
+ return QV4::Runtime::strictNotEqual;
case IR::OpInstanceof:
- return 0;
+ return QV4::Runtime::InvalidRuntimeMethod;
case IR::OpIn:
- return 0;
+ return QV4::Runtime::InvalidRuntimeMethod;
case IR::OpAnd:
- return 0;
+ return QV4::Runtime::InvalidRuntimeMethod;
case IR::OpOr:
- return 0;
+ return QV4::Runtime::InvalidRuntimeMethod;
default:
Q_ASSERT(!"Unknown AluOp");
- return 0;
+ return QV4::Runtime::InvalidRuntimeMethod;
}
};
@@ -889,24 +889,25 @@ Param InstructionSelection::binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR
if (oper == IR::OpInstanceof || oper == IR::OpIn || oper == IR::OpAdd) {
Instruction::BinopContext binop;
if (oper == IR::OpInstanceof)
- binop.alu = offsetof(QV4::Runtime, instanceof);
+ binop.alu = QV4::Runtime::instanceof;
else if (oper == IR::OpIn)
- binop.alu = offsetof(QV4::Runtime, in);
+ binop.alu = QV4::Runtime::in;
else
- binop.alu = offsetof(QV4::Runtime, add);
+ binop.alu = QV4::Runtime::add;
binop.lhs = getParam(leftSource);
binop.rhs = getParam(rightSource);
binop.result = getResultParam(target);
- Q_ASSERT(binop.alu);
+ Q_ASSERT(binop.alu != QV4::Runtime::InvalidRuntimeMethod);
addInstruction(binop);
return binop.result;
} else {
+ auto binopFunc = aluOpFunction(oper);
+ Q_ASSERT(binopFunc != QV4::Runtime::InvalidRuntimeMethod);
Instruction::Binop binop;
- binop.alu = aluOpFunction(oper);
+ binop.alu = binopFunc;
binop.lhs = getParam(leftSource);
binop.rhs = getParam(rightSource);
binop.result = getResultParam(target);
- Q_ASSERT(binop.alu);
addInstruction(binop);
return binop.result;
}
diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h
index 41469f1985..4b84bd2831 100644
--- a/src/qml/compiler/qv4isel_moth_p.h
+++ b/src/qml/compiler/qv4isel_moth_p.h
@@ -179,7 +179,7 @@ private:
int scratchTempIndex() const { return _function->tempCount; }
int callDataStart() const { return scratchTempIndex() + 1; }
- int outgoingArgumentTempStart() const { return callDataStart() + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value); }
+ int outgoingArgumentTempStart() const { return callDataStart() + offsetof(QV4::CallData, args)/sizeof(QV4::Value); }
int frameSize() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; }
template <int Instr>
diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp
index 5687834b00..cc2f9b7cf2 100644
--- a/src/qml/compiler/qv4jsir.cpp
+++ b/src/qml/compiler/qv4jsir.cpp
@@ -348,11 +348,7 @@ Module::~Module()
void Module::setFileName(const QString &name)
{
- if (fileName.isEmpty())
- fileName = name;
- else {
- Q_ASSERT(fileName == name);
- }
+ fileName = name;
}
Function::Function(Module *module, Function *outer, const QString &name)
diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h
index 04bc3d86e5..f7c7b76ea8 100644
--- a/src/qml/compiler/qv4jsir_p.h
+++ b/src/qml/compiler/qv4jsir_p.h
@@ -61,6 +61,7 @@
#include <QtCore/QBitArray>
#include <QtCore/qurl.h>
#include <QtCore/QVarLengthArray>
+#include <QtCore/QDateTime>
#include <qglobal.h>
#if defined(CONST) && defined(Q_OS_WIN)
@@ -507,6 +508,16 @@ struct Q_AUTOTEST_EXPORT Temp: Expr {
, memberResolver(0)
{}
+ Temp(Type type, Kind kind, unsigned index)
+ : Expr(TempExpr)
+ , index(index)
+ , isReadOnly(0)
+ , kind(kind)
+ , memberResolver(0)
+ {
+ this->type = type;
+ }
+
void init(unsigned kind, unsigned index)
{
this->index = index;
@@ -932,7 +943,7 @@ struct Q_QML_PRIVATE_EXPORT Module {
QVector<Function *> functions;
Function *rootFunction;
QString fileName;
- qint64 sourceTimeStamp;
+ QDateTime sourceTimeStamp;
bool isQmlModule; // implies rootFunction is always 0
uint unitFlags; // flags merged into CompiledData::Unit::flags
#ifdef QT_NO_QML_DEBUGGER
@@ -945,7 +956,6 @@ struct Q_QML_PRIVATE_EXPORT Module {
Module(bool debugMode)
: rootFunction(0)
- , sourceTimeStamp(0)
, isQmlModule(false)
, unitFlags(0)
#ifndef QT_NO_QML_DEBUGGER
@@ -1342,6 +1352,31 @@ struct Function {
int getNewStatementId() { return _statementCount++; }
int statementCount() const { return _statementCount; }
+ bool canUseSimpleCall() const {
+ return nestedFunctions.isEmpty() &&
+ locals.isEmpty() && formals.size() <= QV4::Global::ReservedArgumentCount &&
+ !hasTry && !hasWith && !isNamedExpression && !usesArgumentsObject && !hasDirectEval;
+ }
+
+ bool argLocalRequiresWriteBarrier(ArgLocal *al) const {
+ uint scope = al->scope;
+ const IR::Function *f = this;
+ while (scope) {
+ f = f->outer;
+ --scope;
+ }
+ return !f->canUseSimpleCall();
+ }
+ int localsCountForScope(ArgLocal *al) const {
+ uint scope = al->scope;
+ const IR::Function *f = this;
+ while (scope) {
+ f = f->outer;
+ --scope;
+ }
+ return f->locals.size();
+ }
+
private:
BasicBlock *getOrCreateBasicBlock(int index);
void setStatementCount(int cnt);
@@ -1410,6 +1445,7 @@ public:
ArgLocal *newArgLocal = f->New<ArgLocal>();
newArgLocal->init(argLocal->kind, argLocal->index, argLocal->scope);
newArgLocal->type = argLocal->type;
+ newArgLocal->isArgumentsOrEval = argLocal->isArgumentsOrEval;
return newArgLocal;
}
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
index 10f0bbcf8f..cc542e94e7 100644
--- a/src/qml/compiler/qv4ssa.cpp
+++ b/src/qml/compiler/qv4ssa.cpp
@@ -3581,16 +3581,43 @@ public:
, _replacement(0)
{}
- void operator()(Temp *toReplace, Expr *replacement, StatementWorklist &W, QVector<Stmt *> *newUses = 0)
+ bool operator()(Temp *toReplace, Expr *replacement, StatementWorklist &W, QVector<Stmt *> *newUses = 0)
{
Q_ASSERT(replacement->asTemp() || replacement->asConst() || replacement->asName());
-// qout << "Replacing ";toReplace->dump(qout);qout<<" by ";replacement->dump(qout);qout<<endl;
-
qSwap(_toReplace, toReplace);
qSwap(_replacement, replacement);
const QVector<Stmt *> &uses = _defUses.uses(*_toReplace);
+
+ // Prevent the following:
+ // L3:
+ // %1 = phi L1: %2, L2: %3
+ // %4 = phi L1: %5, L2: %6
+ // %6 = %1
+ // From turning into:
+ // L3:
+ // %1 = phi L1: %2, L2: %3
+ // %4 = phi L1: %5, L2: %1
+ //
+ // Because both phi nodes are "executed in parallel", we cannot replace %6 by %1 in the
+ // second phi node. So, if the defining statement for a temp is a phi node, and one of the
+ // uses of the to-be-replaced statement is a phi node in the same block as the defining
+ // statement, bail out.
+ if (Temp *r = _replacement->asTemp()) {
+ if (_defUses.defStmt(*r)->asPhi()) {
+ BasicBlock *replacementDefBlock = _defUses.defStmtBlock(*r);
+ for (Stmt *use : uses) {
+ if (Phi *usePhi = use->asPhi()) {
+ if (_defUses.defStmtBlock(*usePhi->targetTemp) == replacementDefBlock)
+ return false;
+ }
+ }
+ }
+ }
+
+// qout << "Replacing ";toReplace->dump(qout);qout<<" by ";replacement->dump(qout);qout<<endl;
+
if (newUses)
newUses->reserve(uses.size());
@@ -3606,6 +3633,7 @@ public:
qSwap(_replacement, replacement);
qSwap(_toReplace, toReplace);
+ return true;
}
private:
@@ -4082,11 +4110,12 @@ void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df)
// copy propagation:
if (Temp *sourceTemp = m->source->asTemp()) {
QVector<Stmt *> newT2Uses;
- replaceUses(targetTemp, sourceTemp, W, &newT2Uses);
- defUses.removeUse(s, *sourceTemp);
- defUses.addUses(*sourceTemp, newT2Uses);
- defUses.removeDef(*targetTemp);
- W.remove(s);
+ if (replaceUses(targetTemp, sourceTemp, W, &newT2Uses)) {
+ defUses.removeUse(s, *sourceTemp);
+ defUses.addUses(*sourceTemp, newT2Uses);
+ defUses.removeDef(*targetTemp);
+ W.remove(s);
+ }
continue;
}
@@ -5635,25 +5664,97 @@ void MoveMapping::add(Expr *from, Temp *to) {
_moves.append(m);
}
+// Order the moves that are generated when resolving edges during register allocation (see [Wimmer1]
+// section 6 for details). Now these moves form one or more graphs, so we have to output them in
+// such an order that values don't get overwritten:
+// r1 <- r0
+// r2 <- r1
+// That input has to be ordered as follows in order to prevent the value in r1 from being lost:
+// r2 <- r1
+// r1 <- r0
+//
+// So, the algorithm is to output the leaves first, and take them out of the input. This will result
+// in some moves to become leaves (in the above example: when leaf r2 <- r1 is generated and taken
+// away, the r1 <- r0 is now a leaf), so we can output those and take those out, and repeat until
+// there are no more leafs.
+//
+// The tricky part is that there might be cycles:
+// r4 <- r5
+// r5 <- r4
+// These have to be turned into a "register swap":
+// r4 <=> r5
+//
+// So after running the above algorithm where we progressively remove the leaves, we are left with
+// zero or more cycles. To resolve those, we break one of the edges of the cycle, and for all other
+// edges we generate swaps. Note that the swaps will always occur as the last couple of moves,
+// because otherwise they might clobber sources for moves:
+// r4 <=> r5
+// r6 <- r5
+// Here, the value of r5 is already overwritten with the one in r4, so the correct order is:
+// r6 <- r5
+// r4 <=> r5
void MoveMapping::order()
{
- QList<Move> todo = _moves;
- QList<Move> output, swaps;
+ QList<Move> output;
output.reserve(_moves.size());
- QList<Move> delayed;
- delayed.reserve(_moves.size());
- while (!todo.isEmpty()) {
- const Move m = todo.first();
- todo.removeFirst();
- schedule(m, todo, delayed, output, swaps);
- }
+ while (!_moves.isEmpty()) {
+ // Take out all leaf edges, because we can output them without any problems.
+ int nextLeaf = findLeaf();
+ if (nextLeaf == -1)
+ break; // No more leafs left, we're done here.
+ output.append(_moves.takeAt(nextLeaf));
+ // Now there might be new leaf edges: any move that had the input of the previously found
+ // leaf as an output, so loop around.
+ }
+
+ while (!_moves.isEmpty()) {
+ // We're now left with one or more cycles.
+ // Step one: break the/a cycle.
+ _moves.removeFirst();
+ // Step two: find the other edges of the cycle, starting with the one of that is now a leaf.
+ while (!_moves.isEmpty()) {
+ int nextLeaf = findLeaf();
+ if (nextLeaf == -1)
+ break; // We're done with this cycle.
+ Move m = _moves.takeAt(nextLeaf);
+ // Step three: get the edges from the cycle and turn it into a swap
+ m.needsSwap = true;
+ output.append(m);
+ // Because we took out a leaf, find the next one.
+ }
+ // We're done with the cycle, let's see if there are more.
+ }
+
+ _moves = output;
+}
+
+int MoveMapping::findLeaf() const
+{
+ for (int i = 0, e = _moves.size(); i != e; ++i) {
+ // Take an edge from the list...
+ const Temp *target = _moves.at(i).to;
+ // ... and see if its target is used as a source...
+ bool targetUsedAsSource = false;
+ for (int j = 0; j != e; ++j) {
+ if (i == j)
+ continue;
- output += swaps;
+ Expr *source = _moves.at(j).from;
+ if (const Temp *sourceTemp = source->asTemp()) {
+ if (overlappingStorage(*target, *sourceTemp)) {
+ targetUsedAsSource = true;
+ break;
+ }
+ }
+ }
+ // ... if not, we have a leaf edge ...
+ if (!targetUsedAsSource)
+ return i;
+ // .. otherwise we try the next one.
+ }
- Q_ASSERT(todo.isEmpty());
- Q_ASSERT(delayed.isEmpty());
- qSwap(_moves, output);
+ return -1; // No leaf found
}
QList<IR::Move *> MoveMapping::insertMoves(BasicBlock *bb, IR::Function *function, bool atEnd) const
@@ -5695,60 +5796,12 @@ void MoveMapping::dump() const
}
}
-MoveMapping::Action MoveMapping::schedule(const Move &m, QList<Move> &todo, QList<Move> &delayed,
- QList<Move> &output, QList<Move> &swaps) const
-{
- const Moves usages = sourceUsages(m.to, todo) + sourceUsages(m.to, delayed);
- for (const Move &dependency : usages) {
- if (!output.contains(dependency)) {
- if (delayed.contains(dependency)) {
- // We have a cycle! Break it by swapping instead of assigning.
- if (DebugMoveMapping) {
- delayed += m;
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream out(&buf);
- IRPrinter printer(&out);
- out<<"we have a cycle! temps:" << endl;
- for (const Move &m : qAsConst(delayed)) {
- out<<"\t";
- printer.print(m.to);
- out<<" <- ";
- printer.print(m.from);
- out<<endl;
- }
- qDebug("%s", buf.data().constData());
- delayed.removeOne(m);
- }
-
- return NeedsSwap;
- } else {
- delayed.append(m);
- todo.removeOne(dependency);
- Action action = schedule(dependency, todo, delayed, output, swaps);
- delayed.removeOne(m);
- Move mm(m);
- if (action == NeedsSwap) {
- mm.needsSwap = true;
- swaps.append(mm);
- } else {
- output.append(mm);
- }
- return action;
- }
- }
- }
-
- output.append(m);
- return NormalMove;
-}
-
// References:
// [Wimmer1] C. Wimmer and M. Franz. Linear Scan Register Allocation on SSA Form. In Proceedings of
-// CGO’10, ACM Press, 2010
+// CGO'10, ACM Press, 2010
// [Wimmer2] C. Wimmer and H. Mossenbock. Optimized Interval Splitting in a Linear Scan Register
// Allocator. In Proceedings of the ACM/USENIX International Conference on Virtual
-// Execution Environments, pages 132–141. ACM Press, 2005.
+// Execution Environments, pages 132-141. ACM Press, 2005.
// [Briggs] P. Briggs, K.D. Cooper, T.J. Harvey, and L.T. Simpson. Practical Improvements to the
// Construction and Destruction of Static Single Assignment Form.
// [Appel] A.W. Appel. Modern Compiler Implementation in Java. Second edition, Cambridge
diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h
index c07abd04c4..24257e99e9 100644
--- a/src/qml/compiler/qv4ssa_p.h
+++ b/src/qml/compiler/qv4ssa_p.h
@@ -265,15 +265,18 @@ private:
QHash<BasicBlock *, BasicBlock *> startEndLoops;
};
-class MoveMapping
+class Q_AUTOTEST_EXPORT MoveMapping
{
+#ifdef V4_AUTOTEST
+public:
+#endif
struct Move {
Expr *from;
Temp *to;
bool needsSwap;
- Move(Expr *from, Temp *to)
- : from(from), to(to), needsSwap(false)
+ Move(Expr *from, Temp *to, bool needsSwap = false)
+ : from(from), to(to), needsSwap(needsSwap)
{}
bool operator==(const Move &other) const
@@ -293,9 +296,7 @@ public:
void dump() const;
private:
- enum Action { NormalMove, NeedsSwap };
- Action schedule(const Move &m, QList<Move> &todo, QList<Move> &delayed, QList<Move> &output,
- QList<Move> &swaps) const;
+ int findLeaf() const;
};
/*