diff options
-rw-r--r-- | src/qmlcompiler/qqmljscodegenerator.cpp | 386 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljscodegenerator_p.h | 20 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljscompilepass_p.h | 5 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver.cpp | 12 |
4 files changed, 280 insertions, 143 deletions
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index b3b4ba4984..1eacfdc25a 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -34,6 +34,7 @@ #include <private/qqmljsscope_p.h> #include <private/qqmljsutils_p.h> #include <private/qv4compilerscanfunctions_p.h> +#include <private/qduplicatetracker_p.h> #include <QtCore/qdir.h> #include <QtCore/qfileinfo.h> @@ -95,6 +96,14 @@ QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType) return QString(); } +static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type) +{ + return !type.isNull() + && !resolver->equals(type, resolver->nullType()) + && !resolver->equals(type, resolver->emptyListType()) + && !resolver->equals(type, resolver->voidType()); +} + QQmlJSAotFunction QQmlJSCodeGenerator::run( const Function *function, const InstructionAnnotations *annotations, QQmlJS::DiagnosticMessage *error) @@ -103,20 +112,20 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run( m_function = function; m_error = error; - QSet<QString> registerNames; + QHash<int, QHash<QQmlJSScope::ConstPtr, QString>> registerNames; 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()) + if (registerIndex == InvalidRegister || !isTypeStorable(m_typeResolver, seenType)) 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 ¤tRegisterNames = registerNames[registerIndex]; + QString &name = currentRegisterNames[m_typeResolver->comparableType(seenType)]; + if (name.isEmpty()) + name = u"r%1_%2"_qs.arg(registerIndex).arg(currentRegisterNames.count()); + typesForRegisters[seenType] = name; } }; @@ -145,16 +154,15 @@ QT_WARNING_POP QQmlJSAotFunction result; result.includes.swap(m_includes); + QDuplicateTracker<QString> generatedVariables; for (const auto ®isterTypes : qAsConst(m_registerVariables)) { for (auto registerTypeIt = registerTypes.constBegin(), end = registerTypes.constEnd(); registerTypeIt != end; ++registerTypeIt) { const QQmlJSScope::ConstPtr storedType = registerTypeIt.key(); - if (m_typeResolver->equals(storedType, m_typeResolver->nullType()) - || m_typeResolver->equals(storedType, m_typeResolver->emptyListType()) - || m_typeResolver->equals(storedType, m_typeResolver->voidType())) { + + if (generatedVariables.hasSeen(registerTypeIt.value())) continue; - } result.code += storedType->internalName(); @@ -353,7 +361,8 @@ void QQmlJSCodeGenerator::eliminateDeadStores() // Sort the offsets in reverse order so that we can pop from the back std::sort(toErase.begin(), toErase.end()); - int eraseIndex = toErase.length() - 1; + auto end = std::unique(toErase.begin(), toErase.end()); + int eraseIndex = (end - toErase.begin()) - 1; for (int i = m_sections.length() - 1; i >= 0 && eraseIndex >= 0; --i) { if (i != toErase[eraseIndex]) continue; @@ -416,15 +425,13 @@ void QQmlJSCodeGenerator::generate_Ret() } m_body += u"return "_qs + conversion(m_state.accumulatorIn().storedType(), m_function->returnType, in); - } else if (m_function->returnType->internalName() != u"void"_qs) { - if (m_function->returnType->internalName().trimmed().endsWith(u'*') - || m_function->returnType->internalName().trimmed().endsWith(u'&')) { - setError(u"Not all paths return a value"_qs); - return; + } else { + if (m_typeResolver->equals(m_state.accumulatorIn().storedType(), + m_typeResolver->voidType())) { + m_body += signalUndefined; } - - m_body += signalUndefined; - m_body += u"return "_qs + m_function->returnType->internalName() + u"()"_qs; + m_body += u"return "_qs + conversion( + m_state.accumulatorIn().storedType(), m_function->returnType, QString()); } } else { m_body += u"return"_qs; @@ -505,23 +512,39 @@ void QQmlJSCodeGenerator::generate_LoadFalse() void QQmlJSCodeGenerator::generate_LoadNull() { INJECT_TRACE_INFO(generate_LoadNull); + // No need to generate code. We don't store std::nullptr_t. + if (m_typeResolver->equals(m_state.accumulatorOut().storedType(), m_typeResolver->nullType())) + return; + + m_body += m_state.accumulatorVariableOut + u" = "_qs; + m_body += conversion(m_typeResolver->nullType(), m_state.accumulatorOut().storedType(), + u"nullptr"_qs); + m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_LoadUndefined() { INJECT_TRACE_INFO(generate_LoadUndefined); + // No need to generate code. We don't store void. + if (m_typeResolver->equals(m_state.accumulatorOut().storedType(), m_typeResolver->voidType())) + return; + + m_body += m_state.accumulatorVariableOut + u" = "_qs; + m_body += conversion(m_typeResolver->voidType(), m_state.accumulatorOut().storedType(), + QString()); + m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_LoadInt(int value) { INJECT_TRACE_INFO(generate_LoadInt); - 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); + m_body += conversion(m_typeResolver->intType(), m_state.accumulatorOut().storedType(), + QString::number(value)); m_body += u";\n"_qs; } @@ -571,7 +594,7 @@ void QQmlJSCodeGenerator::generate_LoadReg(int reg) return; m_body += m_state.accumulatorVariableOut; m_body += u" = "_qs; - m_body += use(registerVariable(reg)); + m_body += conversion(registerType(reg), m_state.accumulatorOut(), use(registerVariable(reg))); m_body += u";\n"_qs; } @@ -603,7 +626,8 @@ void QQmlJSCodeGenerator::generate_MoveReg(int srcReg, int destReg) return; // don't store things we cannot store. m_body += destRegName; m_body += u" = "_qs; - m_body += use(registerVariable(srcReg)); + m_body += conversion(registerType(srcReg), m_state.changedRegister(), + use(registerVariable(srcReg))); m_body += u";\n"_qs; } @@ -645,7 +669,8 @@ void QQmlJSCodeGenerator::generate_LoadRuntimeString(int stringId) m_body += m_state.accumulatorVariableOut; m_body += u" = "_qs; - m_body += QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId)); + m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut().storedType(), + QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId))); m_body += u";\n"_qs; } @@ -687,6 +712,8 @@ void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(int index) if (m_state.accumulatorVariableOut.isEmpty()) return; + AccumulatorConverter registers(this); + const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index); const QString name = m_jsUnitGenerator->stringForIndex(nameIndex); if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptGlobal) { @@ -713,8 +740,6 @@ void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(int index) 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 QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_qs @@ -728,7 +753,6 @@ void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(int 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()) { generateTypeLookup(index); } else { @@ -922,7 +946,6 @@ void QQmlJSCodeGenerator::generateTypeLookup(int index) break; } case QQmlJSRegisterContent::ScopeModulePrefix: - m_body += m_state.accumulatorVariableOut + u" = aotContext->qmlScopeObject;\n"_qs; break; case QQmlJSRegisterContent::ScopeAttached: { const QString lookup = u"aotContext->loadAttachedLookup("_qs + indexString @@ -957,14 +980,25 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index) return; } - if (m_state.accumulatorOut().isEnumeration()) { - generateEnumLookup(index); + if (m_state.accumulatorOut().isImportNamespace()) { + Q_ASSERT(m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectModulePrefix); + // If we have an object module prefix, we need to pass through the original object. + if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) { + m_body += m_state.accumulatorVariableOut + u" = "_qs + + conversion(m_state.accumulatorIn(), m_state.accumulatorOut(), + use(m_state.accumulatorVariableIn)) + + u";\n"_qs; + } else { + m_body.setWriteRegister(QString()); + } return; } - if (m_state.accumulatorOut().isImportNamespace()) { - m_body.setWriteRegister(QString()); - return; // Nothing to do. We've resolved the prefix already. + AccumulatorConverter registers(this); + + if (m_state.accumulatorOut().isEnumeration()) { + generateEnumLookup(index); + return; } const QString indexString = QString::number(index); @@ -1019,8 +1053,6 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index) 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')'; @@ -1031,7 +1063,6 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index) const QString preparation = getLookupPreparation( m_state.accumulatorOut(), m_state.accumulatorVariableOut, index); generateLookup(lookup, initialization, preparation); - m_body += u"}\n"_qs; } else if ((m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->stringType()) || accumulatorIn.storedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) @@ -1045,8 +1076,6 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index) } else if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->jsValueType())) { reject(u"lookup in QJSValue"_qs); } else { - m_body += u"{\n"_qs; - protectAccumulator(); const QString lookup = u"aotContext->getValueLookup("_qs + indexString + u", "_qs + contentPointer(m_state.accumulatorIn(), use(m_state.accumulatorVariableIn)) @@ -1060,7 +1089,6 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index) const QString preparation = getLookupPreparation( m_state.accumulatorOut(), m_state.accumulatorVariableOut, index); generateLookup(lookup, initialization, preparation); - m_body += u"}\n"_qs; } } @@ -1225,7 +1253,9 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar) QString args; QString conversions; - if (m_typeResolver->registerContains(m_state.accumulatorOut(), m_typeResolver->voidType())) { + if (m_state.changedRegisterIndex() == InvalidRegister || + m_typeResolver->registerContains( + m_state.accumulatorOut(), m_typeResolver->voidType())) { types = u"QMetaType()"_qs; args = u"nullptr"_qs; } else { @@ -1268,13 +1298,10 @@ void QQmlJSCodeGenerator::generateMoveOutVar(const QString &outVar) { if (m_state.accumulatorVariableOut.isEmpty() || outVar.isEmpty()) return; - // Generate a new section to set m_state.accumulatorVariableOut, - // so that m_state.accumulatorVariableOut can be optimized away. nextSection(); m_body.setWriteRegister(m_state.accumulatorVariableOut); m_body += m_state.accumulatorVariableOut + u" = "_qs; - m_body += u"std::move(" + outVar + u");\n"; nextSection(); @@ -1315,8 +1342,10 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar addInclude(u"qalgorithms.h"_qs); addInclude(u"qrandom.h"_qs); - if (!m_typeResolver->registerIsStoredIn(m_state.accumulatorOut(), m_typeResolver->realType())) - return false; + // If the result is not stored, we don't need to generate any code. All the math methods are + // conceptually pure functions. + if (m_state.changedRegisterIndex() != Accumulator) + return true; m_body += u"{\n"_qs; for (int i = 0; i < argc; ++i) { @@ -1330,101 +1359,106 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar const QString inf = u"std::numeric_limits<double>::infinity()"_qs; m_body += m_state.accumulatorVariableOut + u" = "_qs; + QString expression; + if (name == u"abs" && argc == 1) { - m_body += u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_qs; + expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_qs; } else if (name == u"acos"_qs && argc == 1) { - m_body += u"arg1 > 1.0 ? %1 : std::acos(arg1)"_qs.arg(qNaN); + expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_qs.arg(qNaN); } else if (name == u"acosh"_qs && argc == 1) { - m_body += u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_qs.arg(qNaN); + expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_qs.arg(qNaN); } else if (name == u"asin"_qs && argc == 1) { - m_body += u"arg1 > 1.0 ? %1 : std::asin(arg1)"_qs.arg(qNaN); + expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_qs.arg(qNaN); } else if (name == u"asinh"_qs && argc == 1) { - m_body += u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_qs; + expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_qs; } else if (name == u"atan"_qs && argc == 1) { - m_body += u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_qs; + expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_qs; } else if (name == u"atanh"_qs && argc == 1) { - m_body += u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_qs; + expression = u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_qs; } else if (name == u"atan2"_qs) { // TODO: complicated return false; } else if (name == u"cbrt"_qs && argc == 1) { - m_body += u"std::cbrt(arg1)"_qs; + expression = u"std::cbrt(arg1)"_qs; } else if (name == u"ceil"_qs && argc == 1) { - m_body += u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_qs; + expression = u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_qs; } else if (name == u"clz32"_qs && argc == 1) { - m_body += u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_qs; + expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_qs; } else if (name == u"cos"_qs && argc == 1) { - m_body += u"std::cos(arg1)"_qs; + expression = u"std::cos(arg1)"_qs; } else if (name == u"cosh"_qs && argc == 1) { - m_body += u"std::cosh(arg1)"_qs; + expression = u"std::cosh(arg1)"_qs; } else if (name == u"exp"_qs && argc == 1) { - m_body += u"std::isinf(arg1) " + expression = u"std::isinf(arg1) " "? (std::copysign(1.0, arg1) == -1 ? 0.0 : %1) " ": std::exp(arg1)"_qs.arg(inf); } else if (name == u"expm1"_qs) { // TODO: complicated return false; } else if (name == u"floor"_qs && argc == 1) { - m_body += u"std::floor(arg1)"_qs; + expression = u"std::floor(arg1)"_qs; } else if (name == u"fround"_qs && argc == 1) { - m_body += u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) " + expression = u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) " "? arg1 " ": double(float(arg1))"_qs; } else if (name == u"hypot"_qs) { // TODO: complicated return false; } else if (name == u"imul"_qs && argc == 2) { - m_body += u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) " + expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) " "* quint32(QJSNumberCoercion::toInteger(arg2)))"_qs; } else if (name == u"log"_qs && argc == 1) { - m_body += u"arg1 < 0.0 ? %1 : std::log(arg1)"_qs.arg(qNaN); + expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_qs.arg(qNaN); } else if (name == u"log10"_qs && argc == 1) { - m_body += u"arg1 < 0.0 ? %1 : std::log10(arg1)"_qs.arg(qNaN); + expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_qs.arg(qNaN); } else if (name == u"log1p"_qs && argc == 1) { - m_body += u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_qs.arg(qNaN); + expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_qs.arg(qNaN); } else if (name == u"log2"_qs && argc == 1) { - m_body += u"arg1 < -0.0 ? %1 : std::log2(arg1)"_qs.arg(qNaN); + expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_qs.arg(qNaN); } else if (name == u"max"_qs && argc == 2) { - m_body += u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) " + expression = u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) " "? arg2 " ": ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1)"_qs; } else if (name == u"min"_qs && argc == 2) { - m_body += u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) " + expression = u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) " "? arg2 " ": ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1)"_qs; } else if (name == u"pow"_qs) { // TODO: complicated return false; } else if (name == u"random"_qs && argc == 0) { - m_body += u"QRandomGenerator::global()->generateDouble()"_qs; + expression = u"QRandomGenerator::global()->generateDouble()"_qs; } else if (name == u"round"_qs && argc == 1) { - m_body += u"std::isfinite(arg1) " + expression = u"std::isfinite(arg1) " "? ((arg1 < 0.5 && arg1 >= -0.5) " "? std::copysign(0.0, arg1) " ": std::floor(arg1 + 0.5)) " ": arg1"_qs; } else if (name == u"sign"_qs && argc == 1) { - m_body += u"std::isnan(arg1) " + expression = u"std::isnan(arg1) " "? %1 " ": (qIsNull(arg1) " "? arg1 " ": (std::signbit(arg1) ? -1.0 : 1.0))"_qs.arg(qNaN); } else if (name == u"sin"_qs && argc == 1) { - m_body += u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_qs; + expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_qs; } else if (name == u"sinh"_qs && argc == 1) { - m_body += u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_qs; + expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_qs; } else if (name == u"sqrt"_qs && argc == 1) { - m_body += u"std::sqrt(arg1)"_qs; + expression = u"std::sqrt(arg1)"_qs; } else if (name == u"tan"_qs && argc == 1) { - m_body += u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_qs; + expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_qs; } else if (name == u"tanh"_qs && argc == 1) { - m_body += u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_qs; + expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_qs; } else if (name == u"trunc"_qs && argc == 1) { - m_body += u"std::trunc(arg1)"_qs; + expression = u"std::trunc(arg1)"_qs; } else { return false; } + m_body += conversion( + m_typeResolver->realType(), m_state.accumulatorOut().storedType(), expression); + m_body += u";\n"_qs; m_body += u"}\n"_qs; return true; @@ -1441,7 +1475,7 @@ void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int a if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) { const QString name = m_jsUnitGenerator->stringForIndex( m_jsUnitGenerator->lookupNameIndex(index)); - if (m_typeResolver->registerContains(baseType, mathObject())) { + if (m_typeResolver->equals(m_typeResolver->originalContainedType(baseType), mathObject())) { if (inlineMathMethod(name, argc, argv)) return; } @@ -1726,6 +1760,17 @@ void QQmlJSCodeGenerator::generate_DefineArray(int argc, int args) Q_UNUSED(args); if (argc > 0) reject(u"DefineArray"_qs); + + // No need to generate code. We don't store empty lists. + if (m_typeResolver->equals(m_state.accumulatorOut().storedType(), + m_typeResolver->emptyListType())) { + return; + } + + m_body += m_state.accumulatorVariableOut + u" = "_qs; + m_body += conversion(m_typeResolver->emptyListType(), m_state.accumulatorOut().storedType(), + QString()); + m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args) @@ -2022,7 +2067,8 @@ 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.readRegister(lhs)); m_body += m_state.accumulatorVariableOut + u" = "_qs; if (m_typeResolver->equals( @@ -2042,31 +2088,19 @@ void QQmlJSCodeGenerator::generate_As(int lhs) 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(), - use(m_state.accumulatorVariableIn)); - m_body += u";\n"_qs; + generateUnaryOperation(u"!"_qs); } 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(), - use(m_state.accumulatorVariableIn)); - m_body += u";\n"_qs; + generateUnaryOperation(u"+"_qs); } 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(), - use(m_state.accumulatorVariableIn)); - m_body += u";\n"_qs; + generateUnaryOperation(u"-"_qs); } void QQmlJSCodeGenerator::generate_UCompl() @@ -2077,25 +2111,13 @@ void QQmlJSCodeGenerator::generate_UCompl() 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(), - use(m_state.accumulatorVariableIn)) + u"; "_qs; - // No line break, to allow removal of the whole thing - } - m_body += u"++"_qs + use(m_state.accumulatorVariableOut) + u";\n"_qs; + generateInPlaceOperation(u"++"_qs); } 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(), - use(m_state.accumulatorVariableIn)) + u"; "_qs; - // No line break, to allow removal of the whole thing - } - m_body += u"--"_qs + use(m_state.accumulatorVariableOut) + u";\n"_qs; + generateInPlaceOperation(u"--"_qs); } void QQmlJSCodeGenerator::generate_Add(int lhs) @@ -2268,7 +2290,8 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction( { m_state.State::operator=(nextStateFromAnnotations(m_state, *m_annotations)); const auto accumulatorIn = m_state.registers.find(Accumulator); - if (accumulatorIn != m_state.registers.end()) { + if (accumulatorIn != m_state.registers.end() + && isTypeStorable(m_typeResolver, accumulatorIn.value().storedType())) { m_state.accumulatorVariableIn = m_registerVariables.value(Accumulator) .value(accumulatorIn.value().storedType()); Q_ASSERT(!m_state.accumulatorVariableIn.isEmpty()); @@ -2302,7 +2325,8 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction( Q_ASSERT(m_state.changedRegisterIndex() == Accumulator || m_state.accumulatorVariableOut.isEmpty()); Q_ASSERT(m_state.changedRegisterIndex() != Accumulator - || !m_state.accumulatorVariableOut.isEmpty()); + || !m_state.accumulatorVariableOut.isEmpty() + || !isTypeStorable(m_typeResolver, m_state.changedRegister().storedType())); const int currentLine = currentSourceLocation().startLine; if (currentLine != m_lastLineNumberUsed) { @@ -2314,6 +2338,12 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction( } m_lastLineNumberUsed = currentLine; } + + // If the instruction has no side effects and doesn't write any register, it's dead. + // We might still need the label, though, and the source code comment. + if (!m_state.hasSideEffects() && m_state.changedRegisterIndex() == InvalidRegister) + return SkipInstruction; + return ProcessInstruction; } @@ -2350,8 +2380,10 @@ void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &func return false; }; - if (!isComparable()) - reject(u"equality comparison on non-primitive types"_qs); + if (!isComparable()) { + reject(u"equality comparison on non-primitive types %1 and %2"_qs.arg( + m_state.accumulatorIn().descriptiveName(), lhsContent.descriptiveName())); + } const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType(); const QQmlJSScope::ConstPtr rhsType = m_state.accumulatorIn().storedType(); @@ -2398,23 +2430,70 @@ void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOp 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.readRegister(lhs), use(registerVariable(lhs))); - const auto rhsVar = conversion(m_state.accumulatorIn(), m_state.accumulatorOut(), + const auto rhsVar = conversion(m_state.accumulatorIn(), m_state.readAccumulator(), use(m_state.accumulatorVariableIn)); Q_ASSERT(!lhsVar.isEmpty()); Q_ASSERT(!rhsVar.isEmpty()); m_body += m_state.accumulatorVariableOut; m_body += u" = "_qs; - m_body += lhsVar; - m_body += u' '; - m_body += cppOperator; - m_body += u' '; - m_body += rhsVar; + m_body += conversion( + m_typeResolver->original(m_state.accumulatorOut()), + m_state.accumulatorOut(), + u'(' + lhsVar + u' ' + cppOperator + u' ' + rhsVar + u')'); m_body += u";\n"_qs; } +void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator) +{ + const auto var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(), + use(m_state.accumulatorVariableIn)); + + if (var == m_state.accumulatorVariableOut) { + m_body += m_state.accumulatorVariableOut + u" = "_qs + cppOperator + var + u";\n"_qs; + return; + } + + const auto original = m_typeResolver->original(m_state.accumulatorOut()); + if (m_state.accumulatorOut() == original) { + m_body += m_state.accumulatorVariableOut + u" = "_qs + var + u";\n"_qs; + m_body += m_state.accumulatorVariableOut + u" = "_qs + + cppOperator + m_state.accumulatorVariableOut + u";\n"_qs; + return; + } + + m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion( + m_typeResolver->original(m_state.accumulatorOut()), + m_state.accumulatorOut(), cppOperator + var) + u";\n"_qs; +} + +void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator) +{ + const auto var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(), + use(m_state.accumulatorVariableIn)); + + if (var == m_state.accumulatorVariableOut) { + m_body += cppOperator + var + u";\n"_qs; + return; + } + + const auto original = m_typeResolver->original(m_state.accumulatorOut()); + if (m_state.accumulatorOut() == original) { + m_body += m_state.accumulatorVariableOut + u" = "_qs + var + u";\n"_qs; + m_body += cppOperator + m_state.accumulatorVariableOut + u";\n"_qs; + return; + } + + m_body += u"{\n"_qs; + m_body += u"auto converted = "_qs + var + u";\n"_qs; + m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion( + m_typeResolver->original(m_state.accumulatorOut()), + m_state.accumulatorOut(), cppOperator + u"converted"_qs) + u";\n"_qs; + m_body += u"}\n"_qs; +} + void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization, const QString &resultPreparation) { @@ -2429,20 +2508,6 @@ void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &i m_body += u"}\n"_qs; } -void QQmlJSCodeGenerator::protectAccumulator() -{ - // If both m_state.accumulatorIn and m_state.accumulatorOut are QVariant, we will need to - // 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_typeResolver->registerIsStoredIn( - m_state.accumulatorOut(), 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; - } -} - void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions( int relativeOffset, JumpMode mode) { @@ -2476,8 +2541,11 @@ void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions( } // Actually == here. We want the jump code also for equal types - if (!currentType.isValid() || currentType == targetType) + if (currentType == targetType + || !isTypeStorable(m_typeResolver, targetType.storedType())) { continue; + } + Q_ASSERT(m_registerVariables.contains(registerIndex)); const auto ¤tRegisterVariables = m_registerVariables[registerIndex]; const auto variable = currentRegisterVariables.constFind(targetType.storedType()); @@ -2583,7 +2651,7 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from, if (!zero.isEmpty()) return zero; if (m_typeResolver->equals(to, m_typeResolver->stringType())) - return u"null"_qs; + return QQmlJSUtils::toLiteral(u"null"_qs); if (m_typeResolver->equals(from, to)) return QString(); reject(u"Conversion from null to %1"_qs.arg(to->internalName())); @@ -2742,4 +2810,52 @@ void QQmlJSCodeGenerator::reject(const QString &thing) setError(u"Cannot generate efficient code for %1"_qs.arg(thing)); } +QQmlJSCodeGenerator::AccumulatorConverter::AccumulatorConverter(QQmlJSCodeGenerator *generator) + : accumulatorOut(generator->m_state.accumulatorOut()) + , accumulatorVariableIn(generator->m_state.accumulatorVariableIn) + , accumulatorVariableOut(generator->m_state.accumulatorVariableOut) + , generator(generator) +{ + const QQmlJSTypeResolver *resolver = generator->m_typeResolver; + const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(accumulatorOut); + const QQmlJSScope::ConstPtr origStored = resolver->originalType(accumulatorOut.storedType()); + + if (!resolver->equals(origContained, resolver->containedType(accumulatorOut)) + || !resolver->equals(origStored, accumulatorOut.storedType())) { + generator->m_state.accumulatorVariableOut = u"retrieved"_qs; + generator->m_state.setRegister(Accumulator, resolver->original(accumulatorOut)); + generator->m_body += u"{\n"_qs; + generator->m_body += origStored->augmentedInternalName() + u' ' + + generator->m_state.accumulatorVariableOut + u";\n"; + } else if (generator->m_state.accumulatorVariableIn == generator->m_state.accumulatorVariableOut + && generator->m_state.readsRegister(Accumulator) + && resolver->registerIsStoredIn( + generator->m_state.accumulatorOut(), resolver->varType())) { + // If both m_state.accumulatorIn and m_state.accumulatorOut are QVariant, we will need to + // prepare the output QVariant, and afterwards use the input variant. Therefore we need to + // move the input out of the way first. + generator->m_state.accumulatorVariableIn = generator->use( + generator->m_state.accumulatorVariableIn) + u"_moved"_qs; + generator->m_body += u"{\n"_qs; + generator->m_body += u"QVariant "_qs + generator->m_state.accumulatorVariableIn + + u" = std::move("_qs + generator->m_state.accumulatorVariableOut + u");\n"_qs; + } +} + +QQmlJSCodeGenerator::AccumulatorConverter::~AccumulatorConverter() +{ + if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) { + generator->m_body += accumulatorVariableOut + u" = "_qs + generator->conversion( + generator->m_state.accumulatorOut(), accumulatorOut, + generator->m_state.accumulatorVariableOut) + u";\n"_qs; + generator->m_body += u"}\n"_qs; + generator->m_state.setRegister(Accumulator, accumulatorOut); + generator->m_state.accumulatorVariableOut = accumulatorVariableOut; + } else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) { + generator->m_body += u"}\n"_qs; + generator->m_state.accumulatorVariableIn = accumulatorVariableIn; + } +} + + QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h index 1b00cc3507..687e22ed19 100644 --- a/src/qmlcompiler/qqmljscodegenerator_p.h +++ b/src/qmlcompiler/qqmljscodegenerator_p.h @@ -117,6 +117,22 @@ protected: QString accumulatorVariableOut; }; + // This is an RAII helper we can use to automatically convert the result of "inflexible" + // operations to the desired type. For example GetLookup can only retrieve the type of + // the property we're looking up. If we want to store a different type, we need to convert. + struct AccumulatorConverter + { + Q_DISABLE_COPY_MOVE(AccumulatorConverter); + AccumulatorConverter(QQmlJSCodeGenerator *generator); + ~AccumulatorConverter(); + + private: + const QQmlJSRegisterContent accumulatorOut; + const QString accumulatorVariableIn; + const QString accumulatorVariableOut; + QQmlJSCodeGenerator *generator = nullptr; + }; + virtual QString metaObject(const QQmlJSScope::ConstPtr &objectType); void generate_Ret() override; @@ -327,6 +343,8 @@ private: void generateCompareOperation(int lhs, const QString &cppOperator); void generateArithmeticOperation(int lhs, const QString &cppOperator); void generateJumpCodeWithTypeConversions(int relativeOffset, JumpMode mode); + void generateUnaryOperation(const QString &cppOperator); + void generateInPlaceOperation(const QString &cppOperator); void generateMoveOutVar(const QString &outVar); void generateTypeLookup(int index); @@ -334,8 +352,6 @@ private: QString argumentsList(int argc, int argv, QString *outVar); QString castTargetName(const QQmlJSScope::ConstPtr &type) const; - void protectAccumulator(); - QList<BasicBlock> findBasicBlocks(const QList<Section> §ions); RequiredRegisters dropPreserveCycles( const QList<BasicBlock> &basicBlocks, const RequiredRegisters &requiredRegisters); diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h index 44cf5bc543..97ec018ae6 100644 --- a/src/qmlcompiler/qqmljscompilepass_p.h +++ b/src/qmlcompiler/qqmljscompilepass_p.h @@ -153,6 +153,11 @@ public: return readRegister(Accumulator); } + bool readsRegister(int registerIndex) const + { + return m_readRegisters.contains(registerIndex); + } + bool hasSideEffects() const { return m_hasSideEffects; } void setHasSideEffects(bool hasSideEffects) { m_hasSideEffects = hasSideEffects; } diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp index 6112373e25..277f45512f 100644 --- a/src/qmlcompiler/qqmljstyperesolver.cpp +++ b/src/qmlcompiler/qqmljstyperesolver.cpp @@ -395,7 +395,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::transformed( { if (origin.isType()) { return QQmlJSRegisterContent::create( - origin.storedType(), (this->*op)(origin.type()), + (this->*op)(origin.storedType()), (this->*op)(origin.type()), origin.variant(), (this->*op)(origin.scopeType())); } @@ -405,7 +405,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::transformed( if (prop.isList()) trackListPropertyType(prop.type()); return QQmlJSRegisterContent::create( - origin.storedType(), prop, + (this->*op)(origin.storedType()), prop, origin.variant(), (this->*op)(origin.scopeType())); } @@ -413,25 +413,25 @@ QQmlJSRegisterContent QQmlJSTypeResolver::transformed( QQmlJSMetaEnum enumeration = origin.enumeration(); enumeration.setType((this->*op)(enumeration.type())); return QQmlJSRegisterContent::create( - origin.storedType(), enumeration, origin.enumMember(), + (this->*op)(origin.storedType()), enumeration, origin.enumMember(), origin.variant(), (this->*op)(origin.scopeType())); } if (origin.isMethod()) { return QQmlJSRegisterContent::create( - origin.storedType(), origin.method(), origin.variant(), + (this->*op)(origin.storedType()), origin.method(), origin.variant(), (this->*op)(origin.scopeType())); } if (origin.isImportNamespace()) { return QQmlJSRegisterContent::create( - origin.storedType(), origin.importNamespace(), + (this->*op)(origin.storedType()), origin.importNamespace(), origin.variant(), (this->*op)(origin.scopeType())); } if (origin.isConversion()) { return QQmlJSRegisterContent::create( - origin.storedType(), origin.conversionOrigins(), + (this->*op)(origin.storedType()), origin.conversionOrigins(), (this->*op)(origin.conversionResult()), origin.variant(), (this->*op)(origin.scopeType())); } |