aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-02-16 14:01:52 +0100
committerUlf Hermann <ulf.hermann@qt.io>2022-03-02 23:17:27 +0100
commit2c410317b6077fdcfb2cdeb4b730c1b0c544147e (patch)
treef533cae53a00bcf30c174b12a023fb9049969779
parent83a2e80971af69c0b54edc79877016abfd3478b9 (diff)
QmlCompiler: Prepare code generator for post-analysis type adjustment
The basic blocks pass will specialize types of registers for their read locations. Therefore, we have to explicitly convert the results of all instructions. We cannot rely on the implicit type given by the instruction to be the same as the output accumulator type anymore. Change-Id: Ibf1f1d2ebaddafbbd916aeb2e24dcb3ca71010b2 Reviewed-by: Andrei Golubev <andrei.golubev@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp386
-rw-r--r--src/qmlcompiler/qqmljscodegenerator_p.h20
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h5
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp12
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 &currentRegisterNames = 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 &registerTypes : 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 &currentRegisterVariables = 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> &sections);
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()));
}