aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-02-02 19:08:29 +0100
committerUlf Hermann <ulf.hermann@qt.io>2022-02-15 15:03:30 +0100
commitfac9448361d3f5fcbdc8d6b95096c74fb41f0ff4 (patch)
treef45f080653c4b72c7850e4c108db93e66689e91b
parent9b56042f8445fa8c8119cf2d980d8a1698950483 (diff)
QmlCompiler: Improve register tracking
You can only change one register per instruction, and it doesn't have to be the accumulator. We don't have to store the full register set in every instruction annotation. It's enough to store the changed register. This simplifies the logic around choosing the in/out accumulator types. Now you can rely on the "in" accumulator to always be in the register set, and the "out" accumulator to be the changed register if applicable. There will typically be less than 10 registers active at any given instruction. Therefore, use QFlatMap to store them, rather than QHash. Also, rename the storage for the type conversions so that we can actually see what they are. Change-Id: I694ac2bce7911a4c290c1dccef34d28cde50a1d3 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp289
-rw-r--r--src/qmlcompiler/qqmljscodegenerator_p.h1
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h80
-rw-r--r--src/qmlcompiler/qqmljsshadowcheck.cpp6
-rw-r--r--src/qmlcompiler/qqmljsstoragegeneralizer.cpp32
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp390
-rw-r--r--src/qmlcompiler/qqmljstypepropagator_p.h3
7 files changed, 403 insertions, 398 deletions
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index f8d585002b..8f07baf0c0 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -99,26 +99,27 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(
m_error = error;
QSet<QString> registerNames;
- for (const InstructionAnnotation &annotation : *m_annotations) {
- for (auto regIt = annotation.registers.constBegin(),
- regEnd = annotation.registers.constEnd();
- regIt != regEnd;
- ++regIt) {
- int registerIndex = regIt.key();
- const QQmlJSScope::ConstPtr seenType = regIt.value().storedType();
- // Don't generate any variables for registers that are initialized with undefined.
- if (seenType.isNull())
- continue;
+ auto addVariable = [&](int registerIndex, const QQmlJSScope::ConstPtr &seenType) {
+ // Don't generate any variables for registers that are initialized with undefined.
+ if (registerIndex == InvalidRegister || seenType.isNull())
+ return;
+
+ auto &typesForRegisters = m_registerVariables[registerIndex];
+ if (!typesForRegisters.contains(seenType)) {
+ QString variableName = u"r%1"_qs.arg(registerIndex);
+ if (registerNames.contains(variableName))
+ variableName += u'_' + QString::number(typesForRegisters.count());
+ registerNames.insert(variableName);
+ typesForRegisters[seenType] = variableName;
+ }
+ };
- auto &typesForRegisters = m_registerVariables[registerIndex];
- if (!typesForRegisters.contains(seenType)) {
- QString variableName = u"r%1"_qs.arg(registerIndex);
- if (registerNames.contains(variableName))
- variableName += u'_' + QString::number(typesForRegisters.count());
- registerNames.insert(variableName);
- typesForRegisters[seenType] = variableName;
- }
+ for (const InstructionAnnotation &annotation : *m_annotations) {
+ addVariable(annotation.changedRegisterIndex, annotation.changedRegister.storedType());
+ for (auto it = annotation.typeConversions.begin(), end = annotation.typeConversions.end();
+ it != end; ++it) {
+ addVariable(it.key(), it.value().storedType());
}
}
@@ -387,19 +388,19 @@ void QQmlJSCodeGenerator::generate_Ret()
const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_qs;
if (!m_state.accumulatorVariableIn.isEmpty()) {
const QString in = use(m_state.accumulatorVariableIn);
- if (m_state.accumulatorIn.storedType() == m_typeResolver->varType()) {
+ if (m_state.accumulatorIn().storedType() == m_typeResolver->varType()) {
m_body += u"if (!"_qs + in + u".isValid())\n"_qs;
m_body += u" "_qs + signalUndefined;
- } else if (m_state.accumulatorIn.storedType() == m_typeResolver->jsPrimitiveType()) {
+ } else if (m_state.accumulatorIn().storedType() == m_typeResolver->jsPrimitiveType()) {
m_body += u"if ("_qs + in
+ u".type() == QJSPrimitiveValue::Undefined)\n"_qs;
m_body += u" "_qs + signalUndefined;
- } else if (m_state.accumulatorIn.storedType() == m_typeResolver->jsValueType()) {
+ } else if (m_state.accumulatorIn().storedType() == m_typeResolver->jsValueType()) {
m_body += u"if ("_qs + in + u".isUndefined())\n"_qs;
m_body += u" "_qs + signalUndefined;
}
m_body += u"return "_qs
- + conversion(m_state.accumulatorIn.storedType(), m_function->returnType, in);
+ + conversion(m_state.accumulatorIn().storedType(), m_function->returnType, in);
} else if (m_function->returnType != m_typeResolver->voidType()
&& m_function->returnType->internalName() != u"void"_qs) {
if (m_function->returnType->internalName().trimmed().endsWith(u'*')
@@ -490,7 +491,7 @@ void QQmlJSCodeGenerator::generate_LoadInt(int value)
{
INJECT_TRACE_INFO(generate_LoadInt);
- Q_ASSERT(m_typeResolver->registerContains(m_state.accumulatorOut, m_typeResolver->intType()));
+ Q_ASSERT(m_typeResolver->registerContains(m_state.accumulatorOut(), m_typeResolver->intType()));
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
m_body += QString::number(value);
@@ -501,7 +502,9 @@ void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
{
INJECT_TRACE_INFO(generate_MoveConst);
- auto var = registerVariable(destTemp);
+ Q_ASSERT(destTemp == m_state.changedRegisterIndex());
+
+ auto var = changedRegisterVariable();
if (var.isEmpty())
return; // Do not load 'undefined'
@@ -510,7 +513,7 @@ void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
m_jsUnitGenerator->constant(constIndex));
if (v4Value.isNull()) {
- const auto type = registerType(destTemp).storedType();
+ const auto type = m_state.changedRegister().storedType();
m_body += var + u" = "_qs;
if (type == m_typeResolver->jsPrimitiveType()) {
m_body += u"QJSPrimitiveNull()"_qs;
@@ -521,7 +524,7 @@ void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
} else if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
m_body += u"nullptr"_qs;
} else {
- setError(u"Cannot load null into %1"_qs.arg(m_state.accumulatorOut.descriptiveName()));
+ setError(u"Cannot load null into %1"_qs.arg(m_state.changedRegister().descriptiveName()));
}
m_body += u";\n"_qs;
@@ -546,7 +549,7 @@ void QQmlJSCodeGenerator::generate_LoadReg(int reg)
// We won't emit any code for loading undefined.
// See also generate_LoadUndefined()
- if (m_typeResolver->registerContains(m_state.accumulatorOut, m_typeResolver->voidType()))
+ if (m_typeResolver->registerContains(m_state.accumulatorOut(), m_typeResolver->voidType()))
return;
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
@@ -558,14 +561,15 @@ void QQmlJSCodeGenerator::generate_StoreReg(int reg)
{
INJECT_TRACE_INFO(generate_StoreReg);
- Q_ASSERT(m_state.accumulatorIn.isValid());
- const QString var = registerVariable(reg);
+ Q_ASSERT(m_state.changedRegisterIndex() == reg);
+ Q_ASSERT(m_state.accumulatorIn().isValid());
+ const QString var = changedRegisterVariable();
m_body.setWriteRegister(var);
if (var.isEmpty())
return; // don't store "undefined"
m_body += var;
m_body += u" = "_qs;
- m_body += conversion(m_state.accumulatorIn, registerType(reg),
+ m_body += conversion(m_state.accumulatorIn(), m_state.changedRegister(),
use(m_state.accumulatorVariableIn));
m_body += u";\n"_qs;
}
@@ -574,7 +578,8 @@ void QQmlJSCodeGenerator::generate_MoveReg(int srcReg, int destReg)
{
INJECT_TRACE_INFO(generate_MoveReg);
- const QString destRegName = registerVariable(destReg);
+ Q_ASSERT(m_state.changedRegisterIndex() == destReg);
+ const QString destRegName = changedRegisterVariable();
m_body.setWriteRegister(destRegName);
m_body += destRegName;
m_body += u" = "_qs;
@@ -649,7 +654,7 @@ void QQmlJSCodeGenerator::generate_LoadGlobalLookup(int index)
const QString lookup = u"aotContext->loadGlobalLookup("_qs + QString::number(index)
+ u", &"_qs + m_state.accumulatorVariableOut + u", "_qs
- + metaTypeFromType(m_state.accumulatorOut.storedType()) + u')';
+ + metaTypeFromType(m_state.accumulatorOut().storedType()) + u')';
const QString initialization = u"aotContext->initLoadGlobalLookup("_qs
+ QString::number(index) + u')';
generateLookup(lookup, initialization);
@@ -664,47 +669,47 @@ void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(int index)
const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::JavaScriptGlobal) {
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptGlobal) {
m_body += m_state.accumulatorVariableOut + u" = "_qs
+ conversion(
- m_typeResolver->jsValueType(), m_state.accumulatorOut.storedType(),
+ m_typeResolver->jsValueType(), m_state.accumulatorOut().storedType(),
u"aotContext->javaScriptGlobalProperty("_qs + QString::number(nameIndex) + u")")
+ u";\n"_qs;
return;
}
const QString indexString = QString::number(index);
- if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::ObjectById) {
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectById) {
const QString lookup = u"aotContext->loadContextIdLookup("_qs
+ indexString + u", "_qs
- + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString initialization = u"aotContext->initLoadContextIdLookup("_qs
+ indexString + u')';
generateLookup(lookup, initialization);
return;
}
- const bool isProperty = m_state.accumulatorOut.isProperty();
- const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut.scopeType();
- const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut.storedType();
+ const bool isProperty = m_state.accumulatorOut().isProperty();
+ const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut().scopeType();
+ const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
if (isProperty) {
m_body += u"{\n"_qs;
- const auto lookupType = contentType(m_state.accumulatorOut, m_state.accumulatorVariableOut);
+ const auto lookupType = contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut);
const QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_qs
+ indexString + u", "_qs
- + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString initialization
= u"aotContext->initLoadScopeObjectPropertyLookup("_qs
+ indexString + u", "_qs
+ lookupType + u')';
const QString preparation = getLookupPreparation(
- m_state.accumulatorOut, m_state.accumulatorVariableOut, index);
+ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
m_body += u"}\n"_qs;
- } else if (m_state.accumulatorOut.isType() || m_state.accumulatorOut.isImportNamespace()) {
+ } else if (m_state.accumulatorOut().isType() || m_state.accumulatorOut().isImportNamespace()) {
generateTypeLookup(index);
} else {
Q_UNREACHABLE();
@@ -727,10 +732,10 @@ void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
switch (type.variant()) {
case QQmlJSRegisterContent::ScopeProperty:
case QQmlJSRegisterContent::ExtensionScopeProperty: {
- if (type.property().type() != m_typeResolver->containedType(m_state.accumulatorIn)) {
+ if (type.property().type() != m_typeResolver->containedType(m_state.accumulatorIn())) {
m_body += u"{\n"_qs;
m_body += u"auto converted = "_qs
- + conversion(m_state.accumulatorIn, type, use(m_state.accumulatorVariableIn))
+ + conversion(m_state.accumulatorIn(), type, use(m_state.accumulatorVariableIn))
+ u";\n"_qs;
m_body += u"aotContext->storeNameSloppy("_qs + QString::number(nameIndex)
+ u", "_qs + contentPointer(type, u"converted"_qs)
@@ -740,9 +745,9 @@ void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
} else {
m_body += u"aotContext->storeNameSloppy("_qs + QString::number(nameIndex)
+ u", "_qs
- + contentPointer(m_state.accumulatorIn, use(m_state.accumulatorVariableIn))
+ + contentPointer(m_state.accumulatorIn(), use(m_state.accumulatorVariableIn))
+ u", "_qs
- + contentType(m_state.accumulatorIn, use(m_state.accumulatorVariableIn)) + u')';
+ + contentType(m_state.accumulatorIn(), use(m_state.accumulatorVariableIn)) + u')';
m_body += u";\n"_qs;
}
break;
@@ -768,7 +773,7 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
const QQmlJSRegisterContent baseType = registerType(base);
- if (!m_typeResolver->isNumeric(m_state.accumulatorIn) || !baseType.isList()) {
+ if (!m_typeResolver->isNumeric(m_state.accumulatorIn()) || !baseType.isList()) {
reject(u"LoadElement with non-list base type or non-numeric arguments"_qs);
return;
}
@@ -790,7 +795,7 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
+ u" < "_qs + baseName + u".count(&"_qs + baseName
+ u"))\n"_qs;
m_body += u" "_qs + m_state.accumulatorVariableOut + u" = "_qs +
- conversion(elementType, m_state.accumulatorOut,
+ conversion(elementType, m_state.accumulatorOut(),
baseName + u".at(&"_qs + baseName + u", "_qs
+ indexName + u')') + u";\n"_qs;
m_body += u"else\n"_qs;
@@ -829,7 +834,7 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
+ u"))\n"_qs;
m_body += u" "_qs + baseName + u".replace(&"_qs + baseName
+ u", "_qs + indexName + u", "_qs;
- m_body += conversion(m_state.accumulatorIn, elementType, use(m_state.accumulatorVariableIn))
+ m_body += conversion(m_state.accumulatorIn(), elementType, use(m_state.accumulatorVariableIn))
+ u");\n"_qs;
}
@@ -848,14 +853,14 @@ void QQmlJSCodeGenerator::generate_LoadOptionalProperty(int name, int offset)
void QQmlJSCodeGenerator::generateEnumLookup(int index)
{
- const QString enumMember = m_state.accumulatorOut.enumMember();
+ const QString enumMember = m_state.accumulatorOut().enumMember();
// If we're referring to the type, there's nothing to do.
if (enumMember.isEmpty())
return;
// If the metaenum has the value, just use it and skip all the rest.
- const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut.enumeration();
+ const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut().enumeration();
if (metaEnum.hasValues()) {
m_body += m_state.accumulatorVariableOut + u" = "_qs
+ QString::number(metaEnum.value(enumMember));
@@ -863,7 +868,7 @@ void QQmlJSCodeGenerator::generateEnumLookup(int index)
return;
}
- const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut.scopeType();
+ const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut().scopeType();
// Otherwise we would have found an enum with values.
Q_ASSERT(!scopeType->isComposite());
@@ -881,12 +886,13 @@ void QQmlJSCodeGenerator::generateEnumLookup(int index)
void QQmlJSCodeGenerator::generateTypeLookup(int index)
{
const QString indexString = QString::number(index);
+ const QQmlJSRegisterContent accumulatorIn = m_state.registers.value(Accumulator);
const QString namespaceString
- = m_state.accumulatorIn.isImportNamespace()
- ? QString::number(m_state.accumulatorIn.importNamespace())
+ = accumulatorIn.isImportNamespace()
+ ? QString::number(accumulatorIn.importNamespace())
: u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_qs;
- switch (m_state.accumulatorOut.variant()) {
+ switch (m_state.accumulatorOut().variant()) {
case QQmlJSRegisterContent::Singleton: {
const QString lookup = u"aotContext->loadSingletonLookup("_qs + indexString
+ u", &"_qs + m_state.accumulatorVariableOut + u')';
@@ -926,29 +932,29 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
{
INJECT_TRACE_INFO(generate_GetLookup);
- if (m_state.accumulatorOut.isMethod()) {
+ if (m_state.accumulatorOut().isMethod()) {
reject(u"lookup of function property."_qs);
return;
}
- if (m_state.accumulatorOut.isEnumeration()) {
+ if (m_state.accumulatorOut().isEnumeration()) {
generateEnumLookup(index);
return;
}
- if (m_state.accumulatorOut.isImportNamespace()) {
+ if (m_state.accumulatorOut().isImportNamespace()) {
m_body.setWriteRegister(QString());
return; // Nothing to do. We've resolved the prefix already.
}
const QString indexString = QString::number(index);
- const QString namespaceString = m_state.accumulatorIn.isImportNamespace()
- ? QString::number(m_state.accumulatorIn.importNamespace())
+ const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
+ ? QString::number(m_state.accumulatorIn().importNamespace())
: u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_qs;
- const auto storedType = m_state.accumulatorIn.storedType();
+ const auto storedType = m_state.accumulatorIn().storedType();
const bool isReferenceType
= (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
- if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::ObjectAttached) {
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectAttached) {
if (!isReferenceType) {
// This can happen on incomplete type information. We contextually know that the
// type must be a QObject, but we cannot construct the inheritance chain. Then we
@@ -965,7 +971,7 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
+ use(m_state.accumulatorVariableIn) + u')';
generateLookup(lookup, initialization);
return;
- } else if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::Singleton) {
+ } else if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::Singleton) {
const QString lookup = u"aotContext->loadSingletonLookup("_qs + indexString
+ u", &"_qs + m_state.accumulatorVariableOut + u')';
const QString initialization = u"aotContext->initLoadSingletonLookup("_qs
@@ -974,21 +980,21 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
return;
}
- Q_ASSERT(m_state.accumulatorOut.isProperty());
+ Q_ASSERT(m_state.accumulatorOut().isProperty());
- const QQmlJSScope::ConstPtr out = m_state.accumulatorOut.storedType();
+ const QQmlJSScope::ConstPtr out = m_state.accumulatorOut().storedType();
if (isReferenceType) {
m_body += u"{\n"_qs;
protectAccumulator();
const QString lookup = u"aotContext->getObjectLookup("_qs + indexString
+ u", "_qs + use(m_state.accumulatorVariableIn) + u", "_qs
- + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString initialization = u"aotContext->initGetObjectLookup("_qs
+ indexString + u", "_qs + use(m_state.accumulatorVariableIn)
- + u", "_qs + contentType(m_state.accumulatorOut, m_state.accumulatorVariableOut)
+ + u", "_qs + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut)
+ u')';
const QString preparation = getLookupPreparation(
- m_state.accumulatorOut, m_state.accumulatorVariableOut, index);
+ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
m_body += u"}\n"_qs;
} else if ((storedType == m_typeResolver->stringType()
@@ -1006,16 +1012,17 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
m_body += u"{\n"_qs;
protectAccumulator();
const QString lookup = u"aotContext->getValueLookup("_qs + indexString
- + u", "_qs + contentPointer(m_state.accumulatorIn,
+ + u", "_qs + contentPointer(m_state.accumulatorIn(),
use(m_state.accumulatorVariableIn))
- + u", "_qs + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut)
+ + u", "_qs + contentPointer(m_state.accumulatorOut(),
+ m_state.accumulatorVariableOut)
+ u')';
const QString initialization = u"aotContext->initGetValueLookup("_qs
+ indexString + u", "_qs
- + metaObject(m_state.accumulatorOut.scopeType()) + u", "_qs
- + contentType(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + metaObject(m_state.accumulatorOut().scopeType()) + u", "_qs
+ + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString preparation = getLookupPreparation(
- m_state.accumulatorOut, m_state.accumulatorVariableOut, index);
+ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
m_body += u"}\n"_qs;
}
@@ -1059,7 +1066,7 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
m_body.setWriteRegister(QString());
const QString indexString = QString::number(index);
- const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn.storedType();
+ const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType();
const QQmlJSRegisterContent callBase = registerType(baseReg);
const QQmlJSScope::ConstPtr objectType = callBase.storedType();
const QQmlJSRegisterContent specific = m_typeResolver->memberType(
@@ -1073,9 +1080,9 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
QString variableInType;
QString preparation;
QString argType;
- if (property != m_state.accumulatorIn) {
+ if (property != m_state.accumulatorIn()) {
m_body += u"auto converted = "_qs
- + conversion(m_state.accumulatorIn, property, use(m_state.accumulatorVariableIn))
+ + conversion(m_state.accumulatorIn(), property, use(m_state.accumulatorVariableIn))
+ u";\n"_qs;
variableIn = contentPointer(property, u"converted"_qs);
variableInType = contentType(property, u"converted"_qs);
@@ -1182,12 +1189,12 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
QString args;
QString conversions;
- if (m_typeResolver->containedType(m_state.accumulatorOut) == m_typeResolver->voidType()) {
+ if (m_typeResolver->containedType(m_state.accumulatorOut()) == m_typeResolver->voidType()) {
types = u"QMetaType()"_qs;
args = u"nullptr"_qs;
} else {
*outVar = u"callResult"_qs;
- const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut.storedType();
+ const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType();
m_body += outType->internalName();
if (outType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
m_body += u" *"_qs;
@@ -1195,7 +1202,7 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
m_body += u' ';
m_body += *outVar + u";\n";
- types = metaTypeFromType(m_state.accumulatorOut.storedType());
+ types = metaTypeFromType(m_state.accumulatorOut().storedType());
args = u'&' + *outVar;
}
@@ -1273,7 +1280,7 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar
addInclude(u"qalgorithms.h"_qs);
addInclude(u"qrandom.h"_qs);
- if (m_state.accumulatorOut.storedType() != m_typeResolver->realType())
+ if (m_state.accumulatorOut().storedType() != m_typeResolver->realType())
return false;
m_body += u"{\n"_qs;
@@ -1542,7 +1549,7 @@ void QQmlJSCodeGenerator::generate_ThrowException()
generateSetInstructionPointer();
m_body += u"aotContext->engine->throwError("_qs
- + conversion(m_state.accumulatorIn, m_typeResolver->globalType(
+ + conversion(m_state.accumulatorIn(), m_typeResolver->globalType(
m_typeResolver->jsValueType()),
use(m_state.accumulatorVariableIn)) + u");\n"_qs;
m_body += u"return "_qs + errorReturnValue() + u";\n"_qs;
@@ -1746,7 +1753,7 @@ void QQmlJSCodeGenerator::generate_JumpTrue(int offset)
m_body.setWriteRegister(QString());
m_body += u"if ("_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->boolType(),
+ m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
use(m_state.accumulatorVariableIn));
m_body += u") "_qs;
generateJumpCodeWithTypeConversions(offset, JumpMode::Conditional);
@@ -1761,7 +1768,7 @@ void QQmlJSCodeGenerator::generate_JumpFalse(int offset)
m_body.setWriteRegister(QString());
m_body += u"if (!"_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->boolType(),
+ m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
use(m_state.accumulatorVariableIn));
m_body += u") "_qs;
generateJumpCodeWithTypeConversions(offset, JumpMode::Conditional);
@@ -1802,7 +1809,7 @@ void QQmlJSCodeGenerator::generate_CmpEqNull()
m_body += m_state.accumulatorVariableOut;
m_body += u" = QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
+ m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
use(m_state.accumulatorVariableIn));
m_body += u')';
m_body += u";\n"_qs;
@@ -1815,7 +1822,7 @@ void QQmlJSCodeGenerator::generate_CmpNeNull()
m_body += m_state.accumulatorVariableOut;
m_body += u" = !QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
+ m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
use(m_state.accumulatorVariableIn));
m_body += u')';
m_body += u";\n"_qs;
@@ -1823,19 +1830,19 @@ void QQmlJSCodeGenerator::generate_CmpNeNull()
QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
{
- if (m_state.accumulatorIn.storedType() == m_typeResolver->intType())
+ if (m_state.accumulatorIn().storedType() == m_typeResolver->intType())
return QString::number(lhsConst) + u" == "_qs + use(m_state.accumulatorVariableIn);
- if (m_state.accumulatorIn.storedType() == m_typeResolver->boolType()) {
+ if (m_state.accumulatorIn().storedType() == m_typeResolver->boolType()) {
return QString::number(lhsConst) + u" == "_qs
- + conversion(m_state.accumulatorIn.storedType(), m_typeResolver->intType(),
+ + conversion(m_state.accumulatorIn().storedType(), m_typeResolver->intType(),
use(m_state.accumulatorVariableIn));
}
- if (m_typeResolver->isNumeric(m_state.accumulatorIn)) {
+ if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
return conversion(m_typeResolver->intType(), m_typeResolver->realType(),
QString::number(lhsConst)) + u" == "_qs
- + conversion(m_state.accumulatorIn.storedType(), m_typeResolver->realType(),
+ + conversion(m_state.accumulatorIn().storedType(), m_typeResolver->realType(),
use(m_state.accumulatorVariableIn));
}
@@ -1843,7 +1850,7 @@ QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
result += conversion(m_typeResolver->intType(), m_typeResolver->jsPrimitiveType(),
QString::number(lhsConst));
result += u".equals("_qs;
- result += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
+ result += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
use(m_state.accumulatorVariableIn));
result += u')';
return result;
@@ -1974,17 +1981,17 @@ void QQmlJSCodeGenerator::generate_As(int lhs)
INJECT_TRACE_INFO(generate_As);
const QString input = use(registerVariable(lhs));
- const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(m_state.accumulatorOut);
+ const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(m_state.accumulatorOut());
m_body += m_state.accumulatorVariableOut + u" = "_qs;
- if (m_state.accumulatorIn.storedType() == m_typeResolver->metaObjectType()
+ if (m_state.accumulatorIn().storedType() == m_typeResolver->metaObjectType()
&& contained->isComposite()) {
m_body += conversion(
- m_typeResolver->genericType(contained), m_state.accumulatorOut.storedType(),
+ m_typeResolver->genericType(contained), m_state.accumulatorOut().storedType(),
use(m_state.accumulatorVariableIn) + u"->cast("_qs + input + u')');
} else {
m_body += conversion(
- m_typeResolver->genericType(contained), m_state.accumulatorOut.storedType(),
+ m_typeResolver->genericType(contained), m_state.accumulatorOut().storedType(),
u'(' + metaObject(contained) + u")->cast("_qs + input + u')');
}
m_body += u";\n"_qs;
@@ -1995,7 +2002,7 @@ void QQmlJSCodeGenerator::generate_UNot()
INJECT_TRACE_INFO(generate_UNot);
m_body += m_state.accumulatorVariableOut;
m_body += u" = !"_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->boolType(),
+ m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
use(m_state.accumulatorVariableIn));
m_body += u";\n"_qs;
}
@@ -2005,7 +2012,7 @@ void QQmlJSCodeGenerator::generate_UPlus()
INJECT_TRACE_INFO(generate_UPlus);
m_body += m_state.accumulatorVariableOut;
m_body += u"= +"_qs;
- m_body += conversion(m_state.accumulatorIn, m_state.accumulatorOut,
+ m_body += conversion(m_state.accumulatorIn(), m_state.accumulatorOut(),
use(m_state.accumulatorVariableIn));
m_body += u";\n"_qs;
}
@@ -2015,7 +2022,7 @@ void QQmlJSCodeGenerator::generate_UMinus()
INJECT_TRACE_INFO(generate_UMinus);
m_body += m_state.accumulatorVariableOut;
m_body += u"= -"_qs;
- m_body += conversion(m_state.accumulatorIn, m_state.accumulatorOut,
+ m_body += conversion(m_state.accumulatorIn(), m_state.accumulatorOut(),
use(m_state.accumulatorVariableIn));
m_body += u";\n"_qs;
}
@@ -2030,7 +2037,7 @@ void QQmlJSCodeGenerator::generate_Increment()
INJECT_TRACE_INFO(generate_Increment);
if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
m_body += m_state.accumulatorVariableOut + u" = "_qs
- + conversion(m_state.accumulatorIn, m_state.accumulatorOut,
+ + conversion(m_state.accumulatorIn(), m_state.accumulatorOut(),
use(m_state.accumulatorVariableIn)) + u"; "_qs;
// No line break, to allow removal of the whole thing
}
@@ -2042,7 +2049,7 @@ void QQmlJSCodeGenerator::generate_Decrement()
INJECT_TRACE_INFO(generate_Decrement);
if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
m_body += m_state.accumulatorVariableOut + u" = "_qs
- + conversion(m_state.accumulatorIn, m_state.accumulatorOut,
+ + conversion(m_state.accumulatorIn(), m_state.accumulatorOut(),
use(m_state.accumulatorVariableIn)) + u"; "_qs;
// No line break, to allow removal of the whole thing
}
@@ -2153,14 +2160,14 @@ void QQmlJSCodeGenerator::generate_Mod(int lhs)
registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(),
use(registerVariable(lhs)));
const auto rhsVar = conversion(
- m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
+ m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
use(m_state.accumulatorVariableIn));
Q_ASSERT(!lhsVar.isEmpty());
Q_ASSERT(!rhsVar.isEmpty());
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut.storedType(),
+ m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut().storedType(),
u'(' + lhsVar + u" % "_qs + rhsVar + u')');
m_body += u";\n"_qs;
}
@@ -2218,9 +2225,14 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
QV4::Moth::Instr::Type type)
{
m_state.State::operator=(nextStateFromAnnotations(m_state, *m_annotations));
- m_state.accumulatorVariableIn = m_registerVariables.value(Accumulator)
- .value(m_state.accumulatorIn.storedType());
- Q_ASSERT(!m_state.accumulatorIn.isValid() || !m_state.accumulatorVariableIn.isEmpty());
+ const auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn != m_state.registers.end()) {
+ m_state.accumulatorVariableIn = m_registerVariables.value(Accumulator)
+ .value(accumulatorIn.value().storedType());
+ Q_ASSERT(!m_state.accumulatorVariableIn.isEmpty());
+ } else {
+ m_state.accumulatorVariableIn.clear();
+ }
auto labelIt = m_labels.constFind(currentInstructionOffset());
if (labelIt != m_labels.constEnd()) {
@@ -2235,14 +2247,20 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
nextSection();
- m_state.accumulatorVariableOut = registerVariable(QQmlJSTypePropagator::Accumulator);
+ if (m_state.changedRegisterIndex() == Accumulator)
+ m_state.accumulatorVariableOut = changedRegisterVariable();
+ else
+ m_state.accumulatorVariableOut.clear();
+
if (!m_state.accumulatorVariableOut.isEmpty())
m_body.setWriteRegister(m_state.accumulatorVariableOut);
// If the accumulator type is valid, we want an accumulator variable.
// If not, we don't want one.
- Q_ASSERT(m_state.accumulatorOut.isValid() || m_state.accumulatorVariableOut.isEmpty());
- Q_ASSERT(!m_state.accumulatorOut.isValid() || !m_state.accumulatorVariableOut.isEmpty());
+ Q_ASSERT(m_state.changedRegisterIndex() == Accumulator
+ || m_state.accumulatorVariableOut.isEmpty());
+ Q_ASSERT(m_state.changedRegisterIndex() != Accumulator
+ || !m_state.accumulatorVariableOut.isEmpty());
const int currentLine = currentSourceLocation().startLine;
if (currentLine != m_lastLineNumberUsed) {
@@ -2287,12 +2305,12 @@ void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &func
const QQmlJSRegisterContent lhsContent = registerType(lhs);
auto isComparable = [&]() {
if (m_typeResolver->isPrimitive(lhsContent)
- && m_typeResolver->isPrimitive(m_state.accumulatorIn)) {
+ && m_typeResolver->isPrimitive(m_state.accumulatorIn())) {
return true;
}
- if (m_typeResolver->isNumeric(lhsContent) && m_state.accumulatorIn.isEnumeration())
+ if (m_typeResolver->isNumeric(lhsContent) && m_state.accumulatorIn().isEnumeration())
return true;
- if (m_typeResolver->isNumeric(m_state.accumulatorIn) && lhsContent.isEnumeration())
+ if (m_typeResolver->isNumeric(m_state.accumulatorIn()) && lhsContent.isEnumeration())
return true;
return false;
};
@@ -2301,7 +2319,7 @@ void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &func
reject(u"equality comparison on non-primitive types"_qs);
const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType();
- const QQmlJSScope::ConstPtr rhsType = m_state.accumulatorIn.storedType();
+ const QQmlJSScope::ConstPtr rhsType = m_state.accumulatorIn().storedType();
m_body += m_state.accumulatorVariableOut + u" = "_qs;
@@ -2317,7 +2335,7 @@ void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &func
m_body += u'.';
m_body += function;
m_body += u'(';
- m_body += conversion(m_state.accumulatorIn.storedType(), primitive,
+ m_body += conversion(m_state.accumulatorIn().storedType(), primitive,
use(m_state.accumulatorVariableIn));
m_body += u')';
}
@@ -2330,24 +2348,24 @@ void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOp
const auto lhsType = registerType(lhs);
const QQmlJSScope::ConstPtr compareType =
- m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(m_state.accumulatorIn)
- ? m_typeResolver->merge(lhsType, m_state.accumulatorIn).storedType()
+ m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(m_state.accumulatorIn())
+ ? m_typeResolver->merge(lhsType, m_state.accumulatorIn()).storedType()
: m_typeResolver->jsPrimitiveType();
m_body += u" = "_qs;
m_body += conversion(registerType(lhs).storedType(), compareType, use(registerVariable(lhs)));
m_body += u' ';
m_body += cppOperator;
m_body += u' ';
- m_body += conversion(m_state.accumulatorIn.storedType(), compareType,
+ m_body += conversion(m_state.accumulatorIn().storedType(), compareType,
use(m_state.accumulatorVariableIn));
m_body += u";\n"_qs;
}
void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cppOperator)
{
- const auto lhsVar = conversion(registerType(lhs), m_state.accumulatorOut,
+ const auto lhsVar = conversion(registerType(lhs), m_state.accumulatorOut(),
use(registerVariable(lhs)));
- const auto rhsVar = conversion(m_state.accumulatorIn, m_state.accumulatorOut,
+ const auto rhsVar = conversion(m_state.accumulatorIn(), m_state.accumulatorOut(),
use(m_state.accumulatorVariableIn));
Q_ASSERT(!lhsVar.isEmpty());
Q_ASSERT(!rhsVar.isEmpty());
@@ -2382,7 +2400,7 @@ void QQmlJSCodeGenerator::protectAccumulator()
// prepare the output QVariant, and afterwards use the input variant. Therefore we need to move
// the input out of the way first.
if (m_state.accumulatorVariableIn == m_state.accumulatorVariableOut
- && m_state.accumulatorOut.storedType() == m_typeResolver->varType()) {
+ && m_state.accumulatorOut().storedType() == m_typeResolver->varType()) {
m_state.accumulatorVariableIn = use(m_state.accumulatorVariableIn) + u"_moved"_qs;
m_body += u"QVariant "_qs + m_state.accumulatorVariableIn
+ u" = std::move("_qs + m_state.accumulatorVariableOut + u");\n"_qs;
@@ -2399,30 +2417,41 @@ void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(
const auto annotation = m_annotations->constFind(absoluteOffset);
if (annotation != m_annotations->constEnd()) {
- const auto &currentTypes = (*m_annotations)[currentInstructionOffset()].registers;
- const auto &conversions = annotation->expectedTargetTypesBeforeJump;
+ const auto &conversions = annotation->typeConversions;
for (auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
regIt != regEnd; ++regIt) {
int registerIndex = regIt.key();
const QQmlJSRegisterContent targetType = regIt.value();
- if (!targetType.isValid() || !currentTypes.contains(registerIndex))
+ if (!targetType.isValid())
continue;
- const QQmlJSRegisterContent currentType = currentTypes.value(registerIndex);
+
+ QQmlJSRegisterContent currentType;
+ QString currentVariable;
+ if (registerIndex == m_state.changedRegisterIndex()) {
+ currentType = m_state.changedRegister();
+ currentVariable = changedRegisterVariable();
+ } else {
+ auto it = m_state.registers.find(registerIndex);
+ if (it == m_state.registers.end())
+ continue;
+ currentType = it.value();
+ currentVariable = registerVariable(registerIndex);
+ }
+
if (!currentType.isValid() || currentType == targetType)
continue;
Q_ASSERT(m_registerVariables.contains(registerIndex));
const auto &currentRegisterVariables = m_registerVariables[registerIndex];
const auto variable = currentRegisterVariables.constFind(targetType.storedType());
- const QString oldVar = registerVariable(registerIndex);
- if (variable == currentRegisterVariables.end() || *variable == oldVar)
+ if (variable == currentRegisterVariables.end() || *variable == currentVariable)
continue;
nextSection();
m_body.setWriteRegister(*variable);
m_body += *variable;
m_body += u" = "_qs;
- m_body += conversion(currentTypes.value(registerIndex), targetType, use(oldVar));
+ m_body += conversion(currentType, targetType, use(currentVariable));
m_body += u";\n"_qs;
}
}
@@ -2450,6 +2479,12 @@ QString QQmlJSCodeGenerator::registerVariable(int index) const
return m_registerVariables.value(index).value(registerType(index).storedType());
}
+QString QQmlJSCodeGenerator::changedRegisterVariable() const
+{
+ return m_registerVariables.value(m_state.changedRegisterIndex()).value(
+ m_state.changedRegister().storedType());
+}
+
QQmlJSRegisterContent QQmlJSCodeGenerator::registerType(int index) const
{
if (index >= QV4::CallData::OffsetCount && index < firstRegisterIndex()) {
diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h
index 60d052d7f7..1b00cc3507 100644
--- a/src/qmlcompiler/qqmljscodegenerator_p.h
+++ b/src/qmlcompiler/qqmljscodegenerator_p.h
@@ -299,6 +299,7 @@ protected:
void generateEnumLookup(int index);
QString registerVariable(int index) const;
+ QString changedRegisterVariable() const;
QQmlJSRegisterContent registerType(int index) const;
Section m_body;
diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h
index d7f9336ee9..3b3c1ee59d 100644
--- a/src/qmlcompiler/qqmljscompilepass_p.h
+++ b/src/qmlcompiler/qqmljscompilepass_p.h
@@ -46,6 +46,7 @@
#include <private/qqmljstyperesolver_p.h>
#include <private/qv4bytecodehandler_p.h>
#include <private/qv4compiler_p.h>
+#include <private/qflatmap_p.h>
QT_BEGIN_NAMESPACE
@@ -54,6 +55,7 @@ class QQmlJSCompilePass : public QV4::Moth::ByteCodeHandler
Q_DISABLE_COPY_MOVE(QQmlJSCompilePass)
public:
enum RegisterShortcuts {
+ InvalidRegister = -1,
Accumulator = QV4::CallData::Accumulator,
FirstArgument = QV4::CallData::OffsetCount
};
@@ -61,12 +63,13 @@ public:
using SourceLocationTable = QV4::Compiler::Context::SourceLocationTable;
// map from register index to expected type
- using VirtualRegisters = QHash<int, QQmlJSRegisterContent>;
+ using VirtualRegisters = QFlatMap<int, QQmlJSRegisterContent>;
struct InstructionAnnotation
{
- VirtualRegisters registers;
- VirtualRegisters expectedTargetTypesBeforeJump;
+ VirtualRegisters typeConversions;
+ QQmlJSRegisterContent changedRegister;
+ int changedRegisterIndex = InvalidRegister;
};
using InstructionAnnotations = QHash<int, InstructionAnnotation>;
@@ -86,8 +89,43 @@ public:
struct State
{
VirtualRegisters registers;
- QQmlJSRegisterContent accumulatorIn;
- QQmlJSRegisterContent accumulatorOut;
+
+ const QQmlJSRegisterContent &accumulatorIn() const
+ {
+ auto it = registers.find(Accumulator);
+ Q_ASSERT(it != registers.end());
+ return it.value();
+ };
+
+ const QQmlJSRegisterContent &accumulatorOut() const
+ {
+ Q_ASSERT(m_changedRegisterIndex == Accumulator);
+ return m_changedRegister;
+ };
+
+ void setRegister(int registerIndex, QQmlJSRegisterContent content)
+ {
+ m_changedRegister = std::move(content);
+ m_changedRegisterIndex = registerIndex;
+ }
+
+ void setAccumulator(QQmlJSRegisterContent content)
+ {
+ setRegister(Accumulator, std::move(content));
+ }
+
+ void clearChangedRegister()
+ {
+ m_changedRegisterIndex = InvalidRegister;
+ m_changedRegister = QQmlJSRegisterContent();
+ }
+
+ int changedRegisterIndex() const { return m_changedRegisterIndex; }
+ const QQmlJSRegisterContent &changedRegister() const { return m_changedRegister; }
+
+ private:
+ QQmlJSRegisterContent m_changedRegister;
+ int m_changedRegisterIndex = InvalidRegister;
};
QQmlJSCompilePass(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
@@ -120,23 +158,25 @@ protected:
{
State newState;
+ const auto instruction = annotations.constFind(currentInstructionOffset());
+ if (instruction == annotations.constEnd())
+ return newState;
+
+ newState.registers = oldState.registers;
+
// Usually the initial accumulator type is the output of the previous instruction, but ...
- newState.accumulatorIn = oldState.accumulatorOut;
+ if (oldState.changedRegisterIndex() != InvalidRegister)
+ newState.registers[oldState.changedRegisterIndex()] = oldState.changedRegister();
- const auto instruction = annotations.constFind(currentInstructionOffset());
- if (instruction != annotations.constEnd()) {
- const auto target = instruction->expectedTargetTypesBeforeJump.constFind(Accumulator);
- if (target != instruction->expectedTargetTypesBeforeJump.constEnd()) {
- // ... the initial type of the accumulator is given in expectedTargetTypesBeforeJump
- // if the current instruction can be jumped to.
- newState.accumulatorIn = *target;
- }
-
- newState.registers = instruction->registers;
- newState.accumulatorOut = instruction->registers[Accumulator];
- } else {
- newState.registers = VirtualRegisters();
- newState.accumulatorOut = QQmlJSRegisterContent();
+ for (auto it = instruction->typeConversions.begin(),
+ end = instruction->typeConversions.end(); it != end; ++it) {
+ Q_ASSERT(it.key() != InvalidRegister);
+ newState.registers[it.key()] = it.value();
+ }
+
+ if (instruction->changedRegisterIndex != InvalidRegister) {
+ newState.setRegister(instruction->changedRegisterIndex,
+ instruction->changedRegister);
}
return newState;
diff --git a/src/qmlcompiler/qqmljsshadowcheck.cpp b/src/qmlcompiler/qqmljsshadowcheck.cpp
index e1b98deda0..bdcad2a21e 100644
--- a/src/qmlcompiler/qqmljsshadowcheck.cpp
+++ b/src/qmlcompiler/qqmljsshadowcheck.cpp
@@ -64,12 +64,12 @@ void QQmlJSShadowCheck::run(
void QQmlJSShadowCheck::generate_LoadProperty(int nameIndex)
{
- checkShadowing(m_state.accumulatorIn, m_jsUnitGenerator->stringForIndex(nameIndex));
+ checkShadowing(m_state.accumulatorIn(), m_jsUnitGenerator->stringForIndex(nameIndex));
}
void QQmlJSShadowCheck::generate_GetLookup(int index)
{
- checkShadowing(m_state.accumulatorIn, m_jsUnitGenerator->lookupName(index));
+ checkShadowing(m_state.accumulatorIn(), m_jsUnitGenerator->lookupName(index));
}
void QQmlJSShadowCheck::generate_StoreProperty(int nameIndex, int base)
@@ -122,7 +122,7 @@ void QQmlJSShadowCheck::checkShadowing(
}
setError(u"Member %1 of %2 can be shadowed"_qs
- .arg(memberName, m_state.accumulatorIn.descriptiveName()));
+ .arg(memberName, m_state.accumulatorIn().descriptiveName()));
return;
}
default:
diff --git a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
index 4512d596e1..0f91a46f38 100644
--- a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
+++ b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
@@ -73,26 +73,32 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSStorageGeneralizer::run(
}
}
- const auto transformRegisters = [&](QHash<int, QQmlJSRegisterContent> &registers, int offset) {
- for (auto j = registers.begin(), jEnd = registers.end(); j != jEnd; ++j) {
- const QQmlJSRegisterContent &content = *j;
- if (QQmlJSScope::ConstPtr specific = content.storedType()) {
- if (QQmlJSScope::ConstPtr generic = m_typeResolver->genericType(specific)) {
- *j = content.storedIn(generic);
- } else {
- setError(QStringLiteral("Cannot store the register type %1.")
- .arg(specific->internalName()), offset);
- return false;
- }
+ const auto transformRegister = [&](QQmlJSRegisterContent &content, int offset) {
+ if (QQmlJSScope::ConstPtr specific = content.storedType()) {
+ if (QQmlJSScope::ConstPtr generic = m_typeResolver->genericType(specific)) {
+ content = content.storedIn(generic);
+ } else {
+ setError(QStringLiteral("Cannot store the register type %1.")
+ .arg(specific->internalName()), offset);
+ return false;
}
}
return true;
};
+ const auto transformRegisters
+ = [&](QFlatMap<int, QQmlJSRegisterContent> &registers, int offset) {
+ for (auto j = registers.begin(), jEnd = registers.end(); j != jEnd; ++j) {
+ if (!transformRegister(j.value(), offset))
+ return false;
+ }
+ return true;
+ };
+
for (auto i = annotations.begin(), iEnd = annotations.end(); i != iEnd; ++i) {
- if (!transformRegisters(i->registers, i.key()))
+ if (!transformRegister(i->changedRegister, i.key()))
return InstructionAnnotations();
- if (!transformRegisters(i->expectedTargetTypesBeforeJump, i.key()))
+ if (!transformRegisters(i->typeConversions, i.key()))
return InstructionAnnotations();
}
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index d63c939681..f347c0a46e 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -88,21 +88,21 @@ void QQmlJSTypePropagator::generate_Ret()
{
if (m_function->isSignalHandler) {
// Signal handlers cannot return anything.
- } else if (!m_returnType.isValid() && m_state.accumulatorIn.isValid()
- && m_typeResolver->containedType(m_state.accumulatorIn)
+ } else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()
+ && m_typeResolver->containedType(m_state.accumulatorIn())
!= m_typeResolver->voidType()) {
setError(u"function without type annotation returns %1"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
- } else if (m_state.accumulatorIn != m_returnType
- && !canConvertFromTo(m_state.accumulatorIn, m_returnType)) {
+ } else if (m_state.accumulatorIn() != m_returnType
+ && !canConvertFromTo(m_state.accumulatorIn(), m_returnType)) {
setError(u"cannot convert from %1 to %2"_qs
- .arg(m_state.accumulatorIn.descriptiveName(),
+ .arg(m_state.accumulatorIn().descriptiveName(),
m_returnType.descriptiveName()));
m_logger->log(u"Cannot assign binding of type %1 to %2"_qs.arg(
- m_typeResolver->containedTypeName(m_state.accumulatorIn),
- m_typeResolver->containedTypeName(m_returnType)),
+ m_typeResolver->containedTypeName(m_state.accumulatorIn()),
+ m_typeResolver->containedTypeName(m_returnType)),
Log_Type, getCurrentBindingSourceLocation());
return;
}
@@ -118,58 +118,60 @@ void QQmlJSTypePropagator::generate_Debug()
void QQmlJSTypePropagator::generate_LoadConst(int index)
{
auto encodedConst = m_jsUnitGenerator->constant(index);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst));
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
}
void QQmlJSTypePropagator::generate_LoadZero()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->intType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
}
void QQmlJSTypePropagator::generate_LoadTrue()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_LoadFalse()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_LoadNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->nullType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->nullType()));
}
void QQmlJSTypePropagator::generate_LoadUndefined()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->voidType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->voidType()));
}
void QQmlJSTypePropagator::generate_LoadInt(int)
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->intType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
}
void QQmlJSTypePropagator::generate_MoveConst(int constIndex, int destTemp)
{
auto encodedConst = m_jsUnitGenerator->constant(constIndex);
- setRegister(destTemp, m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
+ m_state.setRegister(
+ destTemp, m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
}
void QQmlJSTypePropagator::generate_LoadReg(int reg)
{
- m_state.accumulatorOut = checkedInputRegister(reg);
+ m_state.setAccumulator(checkedInputRegister(reg));
}
void QQmlJSTypePropagator::generate_StoreReg(int reg)
{
- setRegister(reg, m_state.accumulatorIn);
+ m_state.setRegister(reg, m_state.accumulatorIn());
}
void QQmlJSTypePropagator::generate_MoveReg(int srcReg, int destReg)
{
- setRegister(destReg, m_state.registers[srcReg]);
+ Q_ASSERT(destReg != InvalidRegister);
+ m_state.setRegister(destReg, m_state.registers[srcReg]);
}
void QQmlJSTypePropagator::generate_LoadImport(int index)
@@ -181,7 +183,7 @@ void QQmlJSTypePropagator::generate_LoadImport(int index)
void QQmlJSTypePropagator::generate_LoadLocal(int index)
{
Q_UNUSED(index);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_StoreLocal(int index)
@@ -207,7 +209,7 @@ void QQmlJSTypePropagator::generate_StoreScopedLocal(int scope, int index)
void QQmlJSTypePropagator::generate_LoadRuntimeString(int stringId)
{
Q_UNUSED(stringId)
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->stringType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
// m_state.accumulatorOut.m_state.value = m_jsUnitGenerator->stringForIndex(stringId);
}
@@ -227,8 +229,8 @@ void QQmlJSTypePropagator::generate_LoadClosure(int value)
void QQmlJSTypePropagator::generate_LoadName(int nameIndex)
{
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- m_state.accumulatorOut = m_typeResolver->scopedType(m_function->qmlScope, name);
- if (!m_state.accumulatorOut.isValid())
+ m_state.setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
+ if (!m_state.accumulatorOut().isValid())
setError(u"Cannot find name "_qs + name);
}
@@ -413,15 +415,16 @@ bool QQmlJSTypePropagator::checkRestricted(const QString &propertyName) const
{
QString restrictedKind;
- if (!m_state.accumulatorIn.isValid())
+ const auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn == m_state.registers.end())
return false;
- if (m_state.accumulatorIn.isList() && propertyName != u"length") {
+ if (accumulatorIn.value().isList() && propertyName != u"length") {
restrictedKind = u"a list"_qs;
- } else if (m_state.accumulatorIn.isEnumeration()
- && !m_state.accumulatorIn.enumeration().hasKey(propertyName)) {
+ } else if (accumulatorIn.value().isEnumeration()
+ && !accumulatorIn.value().enumeration().hasKey(propertyName)) {
restrictedKind = u"an enum"_qs;
- } else if (m_state.accumulatorIn.isMethod()) {
+ } else if (accumulatorIn.value().isMethod()) {
restrictedKind = u"a method"_qs;
}
@@ -435,36 +438,28 @@ bool QQmlJSTypePropagator::checkRestricted(const QString &propertyName) const
void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
{
+ // LoadQmlContextPropertyLookup does not use accumulatorIn. It always refers to the scope.
+ // Any import namespaces etc. are handled via LoadProperty or GetLookup.
+
const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- m_state.accumulatorOut = m_typeResolver->scopedType(
- m_function->qmlScope,
- m_state.accumulatorIn.isImportNamespace()
- ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn.importNamespace())
- + u'.' + name
- : name);
-
- if (!m_state.accumulatorOut.isValid() && m_typeResolver->isPrefix(name)) {
- const QQmlJSRegisterContent inType = m_state.accumulatorIn.isValid()
- ? m_state.accumulatorIn
- : m_typeResolver->globalType(m_function->qmlScope);
- m_state.accumulatorOut = QQmlJSRegisterContent::create(
+ m_state.setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
+
+ if (!m_state.accumulatorOut().isValid() && m_typeResolver->isPrefix(name)) {
+ const QQmlJSRegisterContent inType = m_typeResolver->globalType(m_function->qmlScope);
+ m_state.setAccumulator(QQmlJSRegisterContent::create(
inType.storedType(), nameIndex, QQmlJSRegisterContent::ScopeModulePrefix,
- m_typeResolver->containedType(inType));
+ m_typeResolver->containedType(inType)));
return;
}
checkDeprecated(m_function->qmlScope, name, false);
- bool isRestricted = checkRestricted(name);
-
- if (!m_state.accumulatorOut.isValid()) {
+ if (!m_state.accumulatorOut().isValid()) {
setError(u"Cannot access value for name "_qs + name);
-
- if (!isRestricted)
- handleUnqualifiedAccess(name);
- } else if (m_typeResolver->genericType(m_state.accumulatorOut.storedType()).isNull()) {
+ handleUnqualifiedAccess(name);
+ } else if (m_typeResolver->genericType(m_state.accumulatorOut().storedType()).isNull()) {
// It should really be valid.
// We get the generic type from aotContext->loadQmlContextPropertyIdLookup().
setError(u"Cannot determine generic type for "_qs + name);
@@ -495,9 +490,9 @@ void QQmlJSTypePropagator::generate_StoreNameSloppy(int nameIndex)
return;
}
- if (!canConvertFromTo(m_state.accumulatorIn, type)) {
+ if (!canConvertFromTo(m_state.accumulatorIn(), type)) {
setError(u"cannot convert from %1 to %2"_qs
- .arg(m_state.accumulatorIn.descriptiveName(), type.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName(), type.descriptiveName()));
}
}
@@ -510,13 +505,13 @@ void QQmlJSTypePropagator::generate_StoreNameStrict(int name)
void QQmlJSTypePropagator::generate_LoadElement(int base)
{
const QQmlJSRegisterContent baseRegister = m_state.registers[base];
- if (!m_typeResolver->registerContains(m_state.accumulatorIn, m_typeResolver->intType())
+ if (!m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->intType())
|| baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
return;
}
- m_state.accumulatorOut = m_typeResolver->valueType(baseRegister);
+ m_state.setAccumulator(m_typeResolver->valueType(baseRegister));
}
void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
@@ -527,24 +522,24 @@ void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
{
- m_state.accumulatorOut =
+ m_state.setAccumulator(
m_typeResolver->memberType(
- m_state.accumulatorIn,
- m_state.accumulatorIn.isImportNamespace()
- ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn.importNamespace())
+ m_state.accumulatorIn(),
+ m_state.accumulatorIn().isImportNamespace()
+ ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn().importNamespace())
+ u'.' + propertyName
- : propertyName);
+ : propertyName));
if (m_typeInfo != nullptr
- && m_state.accumulatorIn.variant() == QQmlJSRegisterContent::ScopeAttached) {
- QQmlJSScope::ConstPtr attachedType = m_state.accumulatorIn.scopeType();
+ && m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ScopeAttached) {
+ QQmlJSScope::ConstPtr attachedType = m_state.accumulatorIn().scopeType();
for (QQmlJSScope::ConstPtr scope = m_function->qmlScope->parentScope(); !scope.isNull();
scope = scope->parentScope()) {
if (m_typeInfo->usedAttachedTypes.values(scope).contains(attachedType)) {
// Ignore enum accesses, as these will not cause the attached object to be created
- if (m_state.accumulatorOut.isValid() && m_state.accumulatorOut.isEnumeration())
+ if (m_state.accumulatorOut().isValid() && m_state.accumulatorOut().isEnumeration())
continue;
const QString id = m_function->addressableScopes.id(scope);
@@ -572,7 +567,7 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
m_logger->log(
u"Using attached type %1 already initialized in a parent scope."_qs.arg(
- m_state.accumulatorIn.scopeType()->internalName()),
+ m_state.accumulatorIn().scopeType()->internalName()),
Log_AttachedPropertyReuse, getCurrentSourceLocation(), true, true,
suggestion);
}
@@ -580,42 +575,42 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
m_typeInfo->usedAttachedTypes.insert(m_function->qmlScope, attachedType);
}
- if (!m_state.accumulatorOut.isValid()) {
+ if (!m_state.accumulatorOut().isValid()) {
if (m_typeResolver->isPrefix(propertyName)) {
- Q_ASSERT(m_state.accumulatorIn.isValid());
- m_state.accumulatorOut = QQmlJSRegisterContent::create(
- m_state.accumulatorIn.storedType(),
+ Q_ASSERT(m_state.accumulatorIn().isValid());
+ m_state.setAccumulator(QQmlJSRegisterContent::create(
+ m_state.accumulatorIn().storedType(),
m_jsUnitGenerator->getStringId(propertyName),
QQmlJSRegisterContent::ObjectModulePrefix,
- m_typeResolver->containedType(m_state.accumulatorIn));
+ m_typeResolver->containedType(m_state.accumulatorIn())));
return;
}
- if (m_state.accumulatorIn.isImportNamespace())
+ if (m_state.accumulatorIn().isImportNamespace())
m_logger->log(u"Type not found in namespace"_qs, Log_Type, getCurrentSourceLocation());
- } else if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::Singleton
- && m_state.accumulatorIn.variant() == QQmlJSRegisterContent::ObjectModulePrefix) {
+ } else if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::Singleton
+ && m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ObjectModulePrefix) {
m_logger->log(u"Cannot load singleton as property of object"_qs, Log_Type,
getCurrentSourceLocation());
- m_state.accumulatorOut = QQmlJSRegisterContent();
+ m_state.setAccumulator(QQmlJSRegisterContent());
}
bool isRestricted = checkRestricted(propertyName);
- if (!m_state.accumulatorOut.isValid()) {
+ if (!m_state.accumulatorOut().isValid()) {
setError(u"Cannot load property %1 from %2."_qs
- .arg(propertyName, m_state.accumulatorIn.descriptiveName()));
+ .arg(propertyName, m_state.accumulatorIn().descriptiveName()));
if (isRestricted)
return;
- const QString typeName = m_typeResolver->containedTypeName(m_state.accumulatorIn);
+ const QString typeName = m_typeResolver->containedTypeName(m_state.accumulatorIn());
if (typeName == u"QVariant")
return;
- if (m_state.accumulatorIn.isList() && propertyName == u"length")
+ if (m_state.accumulatorIn().isList() && propertyName == u"length")
return;
- auto baseType = m_typeResolver->containedType(m_state.accumulatorIn);
+ auto baseType = m_typeResolver->containedType(m_state.accumulatorIn());
// Warn separately when a property is only not found because of a missing type
if (auto property = baseType->property(propertyName); property.isValid()) {
@@ -654,22 +649,23 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
return;
}
- if (m_state.accumulatorOut.isMethod() && m_state.accumulatorOut.method().length() != 1) {
+ if (m_state.accumulatorOut().isMethod() && m_state.accumulatorOut().method().length() != 1) {
setError(u"Cannot determine overloaded method on loadProperty"_qs);
return;
}
- if (m_state.accumulatorOut.isProperty()) {
- if (m_typeResolver->registerContains(m_state.accumulatorOut, m_typeResolver->voidType())) {
+ if (m_state.accumulatorOut().isProperty()) {
+ if (m_typeResolver->registerContains(
+ m_state.accumulatorOut(), m_typeResolver->voidType())) {
setError(u"Type %1 does not have a property %2 for reading"_qs
- .arg(m_state.accumulatorIn.descriptiveName(), propertyName));
+ .arg(m_state.accumulatorIn().descriptiveName(), propertyName));
return;
}
- if (!m_state.accumulatorOut.property().type()) {
+ if (!m_state.accumulatorOut().property().type()) {
m_logger->log(
- QString::fromLatin1("Type of property \"%2\" not found").arg(propertyName),
- Log_Type, getCurrentSourceLocation());
+ QString::fromLatin1("Type of property \"%2\" not found").arg(propertyName),
+ Log_Type, getCurrentSourceLocation());
}
}
}
@@ -719,9 +715,9 @@ void QQmlJSTypePropagator::generate_StoreProperty(int nameIndex, int base)
return;
}
- if (!canConvertFromTo(m_state.accumulatorIn, property)) {
+ if (!canConvertFromTo(m_state.accumulatorIn(), property)) {
setError(u"cannot convert from %1 to %2"_qs
- .arg(m_state.accumulatorIn.descriptiveName(), property.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName(), property.descriptiveName()));
return;
}
}
@@ -784,7 +780,7 @@ void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int ar
const auto containedType = m_typeResolver->containedType(callBase);
if (containedType == m_typeResolver->jsValueType()
|| containedType == m_typeResolver->varType()) {
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
return;
}
@@ -886,9 +882,9 @@ void QQmlJSTypePropagator::propagateCall(const QList<QQmlJSMetaMethod> &methods,
}
const auto returnType = match.returnType();
- m_state.accumulatorOut = m_typeResolver->globalType(
- returnType ? QQmlJSScope::ConstPtr(returnType) : m_typeResolver->voidType());
- if (!m_state.accumulatorOut.isValid())
+ m_state.setAccumulator(m_typeResolver->globalType(
+ returnType ? QQmlJSScope::ConstPtr(returnType) : m_typeResolver->voidType()));
+ if (!m_state.accumulatorOut().isValid())
setError(u"Cannot store return type of method %1()."_qs.arg(match.methodName()));
}
@@ -932,7 +928,7 @@ void QQmlJSTypePropagator::propagateScopeLookupCall(const QString &functionName,
}
setError(u"method %1 cannot be resolved."_qs.arg(functionName));
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
setError(u"Cannot find function '%1'"_qs.arg(functionName));
handleUnqualifiedAccess(functionName);
@@ -975,7 +971,7 @@ void QQmlJSTypePropagator::generate_Construct(int func, int argc, int argv)
Q_UNUSED(argc)
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_ConstructWithSpread(int func, int argc, int argv)
@@ -1012,7 +1008,7 @@ void QQmlJSTypePropagator::generate_DeadTemporalZoneCheck(int name)
void QQmlJSTypePropagator::generate_ThrowException()
{
- m_state.accumulatorOut = QQmlJSRegisterContent();
+ m_state.setAccumulator(QQmlJSRegisterContent());
}
void QQmlJSTypePropagator::generate_GetException()
@@ -1027,7 +1023,6 @@ void QQmlJSTypePropagator::generate_SetException()
void QQmlJSTypePropagator::generate_CreateCallContext()
{
- m_state.accumulatorOut = m_state.accumulatorIn;
}
void QQmlJSTypePropagator::generate_PushCatchContext(int index, int name)
@@ -1066,7 +1061,6 @@ void QQmlJSTypePropagator::generate_PopScriptContext()
void QQmlJSTypePropagator::generate_PopContext()
{
- m_state.accumulatorOut = m_state.accumulatorIn;
}
void QQmlJSTypePropagator::generate_GetIterator(int iterator)
@@ -1116,12 +1110,12 @@ void QQmlJSTypePropagator::generate_DeleteName(int name)
void QQmlJSTypePropagator::generate_TypeofName(int name)
{
Q_UNUSED(name);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->stringType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
}
void QQmlJSTypePropagator::generate_TypeofValue()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->stringType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
}
void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
@@ -1134,9 +1128,9 @@ void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
void QQmlJSTypePropagator::generate_DefineArray(int argc, int args)
{
Q_UNUSED(args);
- m_state.accumulatorOut = m_typeResolver->globalType(argc == 0
- ? m_typeResolver->emptyListType()
- : m_typeResolver->jsValueType());
+ m_state.setAccumulator(m_typeResolver->globalType(argc == 0
+ ? m_typeResolver->emptyListType()
+ : m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
@@ -1146,7 +1140,7 @@ void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int
Q_UNUSED(internalClassId)
Q_UNUSED(argc)
Q_UNUSED(args)
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_CreateClass(int classIndex, int heritage, int computedNames)
@@ -1191,17 +1185,15 @@ void QQmlJSTypePropagator::generate_ToObject()
void QQmlJSTypePropagator::generate_Jump(int offset)
{
saveRegisterStateForJump(offset);
- m_state.accumulatorIn = QQmlJSRegisterContent();
- m_state.accumulatorOut = QQmlJSRegisterContent();
m_state.skipInstructionsUntilNextJumpTarget = true;
}
void QQmlJSTypePropagator::generate_JumpTrue(int offset)
{
- if (!canConvertFromTo(m_state.accumulatorIn,
+ if (!canConvertFromTo(m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->boolType()))) {
setError(u"cannot convert from %1 to boolean"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
}
saveRegisterStateForJump(offset);
@@ -1209,10 +1201,10 @@ void QQmlJSTypePropagator::generate_JumpTrue(int offset)
void QQmlJSTypePropagator::generate_JumpFalse(int offset)
{
- if (!canConvertFromTo(m_state.accumulatorIn,
+ if (!canConvertFromTo(m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->boolType()))) {
setError(u"cannot convert from %1 to boolean"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
}
saveRegisterStateForJump(offset);
@@ -1231,33 +1223,32 @@ void QQmlJSTypePropagator::generate_JumpNotUndefined(int offset)
void QQmlJSTypePropagator::generate_CheckException()
{
- m_state.accumulatorOut = m_state.accumulatorIn;
}
void QQmlJSTypePropagator::generate_CmpEqNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_CmpNeNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_CmpEqInt(int lhsConst)
{
Q_UNUSED(lhsConst)
- m_state.accumulatorOut = QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
+ m_state.setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::Equal, m_typeResolver->globalType(m_typeResolver->intType()),
- m_state.accumulatorIn));
+ m_state.accumulatorIn())));
}
void QQmlJSTypePropagator::generate_CmpNeInt(int lhsConst)
{
Q_UNUSED(lhsConst)
- m_state.accumulatorOut = QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
+ m_state.setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::NotEqual, m_typeResolver->globalType(m_typeResolver->intType()),
- m_state.accumulatorIn));
+ m_state.accumulatorIn())));
}
void QQmlJSTypePropagator::generate_CmpEq(int lhs)
@@ -1316,13 +1307,13 @@ void QQmlJSTypePropagator::generate_As(int lhs)
const QQmlJSRegisterContent input = checkedInputRegister(lhs);
QQmlJSScope::ConstPtr contained;
- switch (m_state.accumulatorIn.variant()) {
+ switch (m_state.accumulatorIn().variant()) {
case QQmlJSRegisterContent::ScopeAttached:
case QQmlJSRegisterContent::MetaType:
- contained = m_state.accumulatorIn.scopeType();
+ contained = m_state.accumulatorIn().scopeType();
break;
default:
- contained = m_typeResolver->containedType(m_state.accumulatorIn);
+ contained = m_typeResolver->containedType(m_state.accumulatorIn());
break;
}
@@ -1330,33 +1321,33 @@ void QQmlJSTypePropagator::generate_As(int lhs)
!= QQmlJSScope::AccessSemantics::Reference
|| contained->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
setError(u"invalid cast from %1 to %2. You can only cast object types."_qs
- .arg(input.descriptiveName(), m_state.accumulatorIn.descriptiveName()));
+ .arg(input.descriptiveName(), m_state.accumulatorIn().descriptiveName()));
} else {
- m_state.accumulatorOut = m_typeResolver->globalType(contained);
+ m_state.setAccumulator(m_typeResolver->globalType(contained));
}
}
void QQmlJSTypePropagator::generate_UNot()
{
- if (!canConvertFromTo(m_state.accumulatorIn,
+ if (!canConvertFromTo(m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->boolType()))) {
setError(u"cannot convert from %1 to boolean"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
}
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_UPlus()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Plus, m_state.accumulatorIn);
+ m_state.setAccumulator(m_typeResolver->typeForUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Plus, m_state.accumulatorIn()));
}
void QQmlJSTypePropagator::generate_UMinus()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Minus, m_state.accumulatorIn);
+ m_state.setAccumulator(m_typeResolver->typeForUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Minus, m_state.accumulatorIn()));
}
void QQmlJSTypePropagator::generate_UCompl()
@@ -1366,14 +1357,14 @@ void QQmlJSTypePropagator::generate_UCompl()
void QQmlJSTypePropagator::generate_Increment()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Increment, m_state.accumulatorIn);
+ m_state.setAccumulator(m_typeResolver->typeForUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Increment, m_state.accumulatorIn()));
}
void QQmlJSTypePropagator::generate_Decrement()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Decrement, m_state.accumulatorIn);
+ m_state.setAccumulator(m_typeResolver->typeForUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Decrement, m_state.accumulatorIn()));
}
void QQmlJSTypePropagator::generate_Add(int lhs)
@@ -1408,15 +1399,15 @@ void QQmlJSTypePropagator::generate_UShr(int lhs)
void QQmlJSTypePropagator::generate_Shr(int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::RShift, lhsRegister, m_state.accumulatorIn);
+ m_state.setAccumulator(m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::RShift, lhsRegister, m_state.accumulatorIn()));
}
void QQmlJSTypePropagator::generate_Shl(int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::LShift, lhsRegister, m_state.accumulatorIn);
+ m_state.setAccumulator(m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::LShift, lhsRegister, m_state.accumulatorIn()));
}
void QQmlJSTypePropagator::generate_BitAndConst(int rhs)
@@ -1447,18 +1438,18 @@ void QQmlJSTypePropagator::generate_ShrConst(int rhsConst)
{
Q_UNUSED(rhsConst)
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::RShift, m_state.accumulatorIn,
- m_typeResolver->globalType(m_typeResolver->intType()));
+ m_state.setAccumulator(m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::RShift, m_state.accumulatorIn(),
+ m_typeResolver->globalType(m_typeResolver->intType())));
}
void QQmlJSTypePropagator::generate_ShlConst(int rhsConst)
{
Q_UNUSED(rhsConst)
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::LShift, m_state.accumulatorIn,
- m_typeResolver->globalType(m_typeResolver->intType()));
+ m_state.setAccumulator(m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::LShift, m_state.accumulatorIn(),
+ m_typeResolver->globalType(m_typeResolver->intType())));
}
void QQmlJSTypePropagator::generate_Exp(int lhs)
@@ -1506,7 +1497,7 @@ void QQmlJSTypePropagator::generate_GetTemplateObject(int index)
}
QV4::Moth::ByteCodeHandler::Verdict
-QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
+QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type)
{
if (m_error->isValid())
return SkipInstruction;
@@ -1516,34 +1507,11 @@ QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
else if (m_state.skipInstructionsUntilNextJumpTarget)
return SkipInstruction;
- bool instructionWritesAccumulatorWithoutReading = false;
- switch (instr) {
- case QV4::Moth::Instr::Type::LoadReg:
- case QV4::Moth::Instr::Type::LoadZero:
- case QV4::Moth::Instr::Type::LoadTrue:
- case QV4::Moth::Instr::Type::LoadFalse:
- case QV4::Moth::Instr::Type::LoadConst:
- case QV4::Moth::Instr::Type::LoadInt:
- case QV4::Moth::Instr::Type::LoadUndefined:
- case QV4::Moth::Instr::Type::LoadName:
- case QV4::Moth::Instr::Type::LoadRuntimeString:
- case QV4::Moth::Instr::Type::LoadLocal:
- case QV4::Moth::Instr::Type::LoadQmlContextPropertyLookup:
- case QV4::Moth::Instr::Type::LoadGlobalLookup:
- case QV4::Moth::Instr::Type::CallQmlContextPropertyLookup:
- case QV4::Moth::Instr::Type::CallGlobalLookup:
- case QV4::Moth::Instr::Type::CallPropertyLookup:
- instructionWritesAccumulatorWithoutReading = true;
- break;
- default:
- break;
- }
-
const int currentOffset = currentInstructionOffset();
// If we reach an instruction that is a target of a jump earlier, then we must check that the
// register state at the origin matches the current state. If not, then we may have to inject
- // conversion code (communicated to code gen via m_state.expectedTargetTypesBeforeJump). For
+ // conversion code (communicated to code gen via m_state.typeConversions). For
// example:
//
// function blah(x: number) { return x > 10 ? 10 : x}
@@ -1562,11 +1530,6 @@ QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
registerIt != end; ++registerIt) {
const int registerIndex = registerIt.key();
- // AccumulatorOut will be the "in" for this (upcoming) instruction, so if that one
- // merely writes to it, we don't care about it's value at the origin of the jump.
- if (registerIndex == Accumulator && instructionWritesAccumulatorWithoutReading)
- continue;
-
auto newType = registerIt.value();
if (!newType.isValid()) {
setError(u"When reached from offset %1, %2 is undefined"_qs
@@ -1577,16 +1540,12 @@ QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
auto currentRegister = m_state.registers.find(registerIndex);
if (currentRegister != m_state.registers.end()) {
- // Careful with accessing the hash iterator later inside this block,
- // some operations may invalidate it.
- auto currentRegisterType = currentRegister;
-
- if (*currentRegisterType != newType) {
- auto merged = m_typeResolver->merge(newType, *currentRegisterType);
+ if (currentRegister.value() != newType) {
+ auto merged = m_typeResolver->merge(newType, currentRegister.value());
Q_ASSERT(merged.isValid());
m_state.annotations[currentInstructionOffset()]
- .expectedTargetTypesBeforeJump[registerIndex] = merged;
- setRegister(registerIndex, merged);
+ .typeConversions[registerIndex] = merged;
+ m_state.registers[registerIndex] = merged;
} else {
// Clear the constant value as this from a jump that might be merging two
// different value
@@ -1596,41 +1555,15 @@ QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
}
}
- // Most instructions require a valid accumulator as input
- switch (instr) {
- case QV4::Moth::Instr::Type::Jump:
- case QV4::Moth::Instr::Type::LoadReg:
- case QV4::Moth::Instr::Type::LoadName:
- case QV4::Moth::Instr::Type::LoadRuntimeString:
- case QV4::Moth::Instr::Type::LoadInt:
- case QV4::Moth::Instr::Type::LoadNull:
- case QV4::Moth::Instr::Type::TypeofName:
- case QV4::Moth::Instr::Type::CallProperty:
- case QV4::Moth::Instr::Type::CallName:
- case QV4::Moth::Instr::Type::MoveReg:
- case QV4::Moth::Instr::Type::MoveConst:
- case QV4::Moth::Instr::Type::DefineArray:
- case QV4::Moth::Instr::Type::DefineObjectLiteral:
- case QV4::Moth::Instr::Type::CheckException:
- case QV4::Moth::Instr::Type::CreateCallContext:
- case QV4::Moth::Instr::Type::PopContext:
- case QV4::Moth::Instr::Type::JumpNoException:
- case QV4::Moth::Instr::Type::SetUnwindHandler:
- case QV4::Moth::Instr::Type::PushCatchContext:
- case QV4::Moth::Instr::Type::UnwindDispatch:
- break;
- default:
- if (instructionWritesAccumulatorWithoutReading)
- m_state.accumulatorIn = QQmlJSRegisterContent();
- else
- m_state.accumulatorIn = checkedInputRegister(Accumulator);
- }
-
return ProcessInstruction;
}
void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
{
+ InstructionAnnotation &currentInstruction = m_state.annotations[currentInstructionOffset()];
+ currentInstruction.changedRegister = m_state.changedRegister();
+ currentInstruction.changedRegisterIndex = m_state.changedRegisterIndex();
+
switch (instr) {
// the following instructions are not expected to produce output in the accumulator
case QV4::Moth::Instr::Type::Ret:
@@ -1652,20 +1585,25 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
case QV4::Moth::Instr::Type::SetUnwindHandler:
case QV4::Moth::Instr::Type::PushCatchContext:
case QV4::Moth::Instr::Type::UnwindDispatch:
+ if (m_state.changedRegisterIndex() == Accumulator && !m_error->isValid()) {
+ setError(u"Instruction is not expected to populate the accumulator"_qs);
+ return;
+ }
break;
default:
// If the instruction is expected to produce output, save it in the register set
// for the next instruction.
- if (m_state.accumulatorOut.isValid()) {
- setRegister(Accumulator, m_state.accumulatorOut);
- m_state.accumulatorOut = QQmlJSRegisterContent();
- } else if (!m_error->isValid()) {
+ if ((!m_state.changedRegister().isValid() || m_state.changedRegisterIndex() != Accumulator)
+ && !m_error->isValid()) {
setError(u"Instruction is expected to populate the accumulator"_qs);
return;
}
}
- m_state.annotations[currentInstructionOffset()].registers = m_state.registers;
+ if (m_state.changedRegisterIndex() != InvalidRegister) {
+ m_state.registers[m_state.changedRegisterIndex()] = m_state.changedRegister();
+ m_state.clearChangedRegister();
+ }
}
void QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op, int lhs)
@@ -1674,8 +1612,8 @@ void QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op, int lhs)
if (!lhsRegister.isValid())
return;
- m_state.accumulatorOut =
- m_typeResolver->typeForBinaryOperation(op, lhsRegister, m_state.accumulatorIn);
+ m_state.setAccumulator(
+ m_typeResolver->typeForBinaryOperation(op, lhsRegister, m_state.accumulatorIn()));
}
void QQmlJSTypePropagator::saveRegisterStateForJump(int offset)
@@ -1691,8 +1629,10 @@ void QQmlJSTypePropagator::saveRegisterStateForJump(int offset)
const auto registerStates =
m_jumpOriginRegisterStateByTargetInstructionOffset.equal_range(jumpToOffset);
for (auto it = registerStates.first; it != registerStates.second; ++it) {
- if (it->registers == state.registers)
+ if (it->registers.keys() == state.registers.keys()
+ && it->registers.values() == state.registers.values()) {
return; // We've seen the same register state before. No need for merging.
+ }
}
// The register state at the target offset needs to be resolved in a further pass.
@@ -1714,28 +1654,14 @@ QString QQmlJSTypePropagator::registerName(int registerIndex) const
registerIndex - FirstArgument - m_function->argumentTypes.count());
}
-// As the source register content may also be a register, we expect a copy here,
-// rather than a reference. Otherwise you might pass a reference to another entry
-// of m_state.registers, which then becomes invalid when making space for the new
-// entry in the hash.
-void QQmlJSTypePropagator::setRegister(int index, QQmlJSRegisterContent content)
-{
- m_state.registers[index] = std::move(content);
-}
-
-void QQmlJSTypePropagator::setRegister(int index, const QQmlJSScope::ConstPtr &content)
-{
- m_state.registers[index] = m_typeResolver->globalType(content);
-}
-
QQmlJSRegisterContent QQmlJSTypePropagator::checkedInputRegister(int reg)
{
- VirtualRegisters::ConstIterator regIt = m_state.registers.find(reg);
- if (regIt == m_state.registers.constEnd()) {
+ const auto regIt = m_state.registers.find(reg);
+ if (regIt == m_state.registers.end()) {
setError(u"Type error: could not infer the type of an expression"_qs);
return {};
}
- return *regIt;
+ return regIt.value();
}
bool QQmlJSTypePropagator::canConvertFromTo(const QQmlJSRegisterContent &from,
diff --git a/src/qmlcompiler/qqmljstypepropagator_p.h b/src/qmlcompiler/qqmljstypepropagator_p.h
index 1b3af43f60..f8f8e4c53d 100644
--- a/src/qmlcompiler/qqmljstypepropagator_p.h
+++ b/src/qmlcompiler/qqmljstypepropagator_p.h
@@ -223,9 +223,6 @@ private:
QString registerName(int registerIndex) const;
- void setRegister(int index, QQmlJSRegisterContent content);
- void setRegister(int index, const QQmlJSScope::ConstPtr &content);
-
QQmlJSRegisterContent checkedInputRegister(int reg);
QQmlJSMetaMethod bestMatchForCall(const QList<QQmlJSMetaMethod> &methods, int argc, int argv,
QStringList *errors);