aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qmlcompiler/CMakeLists.txt3
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks.cpp397
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks_p.h102
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp196
-rw-r--r--src/qmlcompiler/qqmljscodegenerator_p.h1
-rw-r--r--src/qmlcompiler/qqmljscompiler.cpp4
-rw-r--r--src/qmlcompiler/qqmljsshadowcheck.cpp8
-rw-r--r--src/qmlcompiler/qqmljsstoragegeneralizer.cpp31
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp6
9 files changed, 650 insertions, 98 deletions
diff --git a/src/qmlcompiler/CMakeLists.txt b/src/qmlcompiler/CMakeLists.txt
index 2688f723ac..59d8bf8a98 100644
--- a/src/qmlcompiler/CMakeLists.txt
+++ b/src/qmlcompiler/CMakeLists.txt
@@ -8,7 +8,8 @@ qt_internal_add_module(QmlCompilerPrivate
SOURCES
qcoloroutput_p.h qcoloroutput.cpp
qdeferredpointer_p.h
- qqmljsannotation_p.h qqmljsannotation.cpp
+ qqmljsannotation.cpp qqmljsannotation_p.h
+ qqmljsbasicblocks.cpp qqmljsbasicblocks_p.h
qqmljscodegenerator.cpp qqmljscodegenerator_p.h
qqmljscompilepass_p.h
qqmljscompiler.cpp qqmljscompiler_p.h
diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp
new file mode 100644
index 0000000000..ebd0458897
--- /dev/null
+++ b/src/qmlcompiler/qqmljsbasicblocks.cpp
@@ -0,0 +1,397 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmljsbasicblocks_p.h"
+
+QT_BEGIN_NAMESPACE
+
+template<typename Container>
+void deduplicate(Container &container)
+{
+ std::sort(container.begin(), container.end());
+ auto erase = std::unique(container.begin(), container.end());
+ container.erase(erase, container.end());
+}
+
+static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
+{
+ using Type = QV4::Moth::Instr::Type;
+ switch (type) {
+ case Type::PopContext:
+ case Type::PopScriptContext:
+ case Type::CreateCallContext:
+ case Type::CreateCallContext_Wide:
+ case Type::PushCatchContext:
+ case Type::PushCatchContext_Wide:
+ case Type::PushWithContext:
+ case Type::PushWithContext_Wide:
+ case Type::PushBlockContext:
+ case Type::PushBlockContext_Wide:
+ case Type::CloneBlockContext:
+ case Type::CloneBlockContext_Wide:
+ case Type::PushScriptContext:
+ case Type::PushScriptContext_Wide:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run(
+ const Function *function,
+ const InstructionAnnotations &annotations)
+{
+ m_annotations = annotations;
+ m_basicBlocks.insert_or_assign(0, BasicBlock());
+
+ const QByteArray byteCode = function->code;
+ decode(byteCode.constData(), static_cast<uint>(byteCode.length()));
+ if (m_hadBackJumps) {
+ // We may have missed some connections between basic blocks if there were back jumps.
+ // Fill them in via a second pass.
+ reset();
+ decode(byteCode.constData(), static_cast<uint>(byteCode.length()));
+ for (auto it = m_basicBlocks.begin(), end = m_basicBlocks.end(); it != end; ++it)
+ deduplicate(it->second.jumpOrigins);
+ }
+
+ populateBasicBlocks();
+ populateReaderLocations();
+ adjustTypes();
+ return std::move(m_annotations);
+}
+
+QV4::Moth::ByteCodeHandler::Verdict QQmlJSBasicBlocks::startInstruction(QV4::Moth::Instr::Type type)
+{
+ auto it = m_basicBlocks.find(currentInstructionOffset());
+ if (it != m_basicBlocks.end()) {
+ if (!m_skipUntilNextLabel && it != m_basicBlocks.begin())
+ it->second.jumpOrigins.append((--it)->first);
+ m_skipUntilNextLabel = false;
+ } else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) {
+ return SkipInstruction;
+ }
+
+ return ProcessInstruction;
+}
+
+void QQmlJSBasicBlocks::endInstruction(QV4::Moth::Instr::Type)
+{
+}
+
+void QQmlJSBasicBlocks::generate_Jump(int offset)
+{
+ processJump(offset, Unconditional);
+}
+
+void QQmlJSBasicBlocks::generate_JumpTrue(int offset)
+{
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_JumpFalse(int offset)
+{
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_JumpNoException(int offset)
+{
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_JumpNotUndefined(int offset)
+{
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_Ret()
+{
+ m_skipUntilNextLabel = true;
+}
+
+void QQmlJSBasicBlocks::generate_ThrowException()
+{
+ m_skipUntilNextLabel = true;
+}
+
+void QQmlJSBasicBlocks::processJump(int offset, JumpMode mode)
+{
+ if (offset < 0)
+ m_hadBackJumps = true;
+ const int jumpTarget = absoluteOffset(offset);
+ Q_ASSERT(!m_basicBlocks.isEmpty());
+ auto currentBlock = m_basicBlocks.lower_bound(currentInstructionOffset());
+ if (currentBlock == m_basicBlocks.end() || currentBlock->first != currentInstructionOffset())
+ --currentBlock;
+ currentBlock->second.jumpTarget = jumpTarget;
+ currentBlock->second.jumpIsUnconditional = (mode == Unconditional);
+ m_basicBlocks[jumpTarget].jumpOrigins.append(currentInstructionOffset());
+ if (mode == Unconditional)
+ m_skipUntilNextLabel = true;
+ else
+ m_basicBlocks[nextInstructionOffset()].jumpOrigins.append(currentInstructionOffset());
+}
+
+template<typename ContainerA, typename ContainerB>
+static bool containsAny(const ContainerA &container, const ContainerB &elements)
+{
+ for (const auto &element : elements) {
+ if (container.contains(element))
+ return true;
+ }
+ return false;
+}
+
+template<class Key, class T, class Compare = std::less<Key>,
+ class KeyContainer = QList<Key>, class MappedContainer = QList<T>>
+class NewFlatMap
+{
+public:
+ using OriginalFlatMap = QFlatMap<Key, T, Compare, KeyContainer, MappedContainer>;
+
+ void appendOrdered(const typename OriginalFlatMap::iterator &i) {
+ keys.append(i.key());
+ values.append(i.value());
+ }
+
+ OriginalFlatMap take() {
+ OriginalFlatMap result(Qt::OrderedUniqueRange, std::move(keys), std::move(values));
+ keys.clear();
+ values.clear();
+ return result;
+ }
+
+private:
+ typename OriginalFlatMap::key_container_type keys;
+ typename OriginalFlatMap::mapped_container_type values;
+};
+
+void QQmlJSBasicBlocks::populateReaderLocations()
+{
+ using NewInstructionAnnotations = NewFlatMap<int, InstructionAnnotation>;
+
+ bool erasedReaders = false;
+ auto eraseDeadStore = [&](const InstructionAnnotations::iterator &it) {
+ auto reader = m_readerLocations.find(it.key());
+ if (reader != m_readerLocations.end() && reader->readers.isEmpty()) {
+
+ // void the output, rather than deleting it. We still need its variant.
+ m_typeResolver->adjustTrackedType(
+ it->second.changedRegister.storedType(),
+ m_typeResolver->voidType());
+ m_typeResolver->adjustTrackedType(
+ m_typeResolver->containedType(it->second.changedRegister),
+ m_typeResolver->voidType());
+ m_readerLocations.erase(reader);
+
+ // If it's not a label and has no side effects, we can drop the instruction.
+ if (!it->second.hasSideEffects && m_basicBlocks.find(it.key()) == m_basicBlocks.end()) {
+ if (!it->second.readRegisters.isEmpty()) {
+ it->second.readRegisters.clear();
+ erasedReaders = true;
+ }
+ return true;
+ }
+ }
+ return false;
+ };
+
+ NewInstructionAnnotations newAnnotations;
+ for (auto writeIt = m_annotations.begin(), writeEnd = m_annotations.end();
+ writeIt != writeEnd; ++writeIt) {
+ const int writtenRegister = writeIt->second.changedRegisterIndex;
+ if (writtenRegister == InvalidRegister) {
+ newAnnotations.appendOrdered(writeIt);
+ continue;
+ }
+
+ RegisterAccess &access = m_readerLocations[writeIt.key()];
+ if (writeIt->second.changedRegister.isConversion()) {
+ // If it's a conversion, we have to check for all readers of the conversion origins.
+ // This happens at jump targets where different types are merged. A StoreReg or similar
+ // instruction must be optimized out if none of the types it can hold is read anymore.
+ access.trackedTypes = writeIt->second.changedRegister.conversionOrigins();
+ } else {
+ access.trackedTypes.append(
+ m_typeResolver->trackedContainedType(writeIt->second.changedRegister));
+ }
+
+ auto blockIt = m_basicBlocks.lower_bound(writeIt.key());
+ if (blockIt == m_basicBlocks.end() || blockIt->first != writeIt.key())
+ --blockIt;
+
+ QList<int> blocks = { blockIt->first };
+ QList<int> processedBlocks;
+ bool isFirstBlock = true;
+
+ while (!blocks.isEmpty()) {
+ auto nextBlock = m_basicBlocks.find(blocks.takeLast());
+ auto currentBlock = nextBlock++;
+
+ if (isFirstBlock
+ || containsAny(currentBlock->second.readRegisters, access.trackedTypes)) {
+ const auto blockEnd = (nextBlock == m_basicBlocks.end())
+ ? m_annotations.end()
+ : m_annotations.find(nextBlock->first);
+
+ auto blockInstr = isFirstBlock
+ ? (writeIt + 1)
+ : m_annotations.find(currentBlock->first);
+ for (; blockInstr != blockEnd; ++blockInstr) {
+ for (auto readIt = blockInstr->second.readRegisters.constBegin(),
+ end = blockInstr->second.readRegisters.constEnd();
+ readIt != end; ++readIt) {
+ Q_ASSERT(readIt->second.isConversion());
+ if (containsAny(readIt->second.conversionOrigins(), access.trackedTypes))
+ access.readers[blockInstr.key()] = readIt->second.conversionResult();
+ }
+ }
+ }
+
+ if (!currentBlock->second.jumpIsUnconditional && nextBlock != m_basicBlocks.end()
+ && !processedBlocks.contains(nextBlock->first)) {
+ blocks.append(nextBlock->first);
+ }
+
+ const int jumpTarget = currentBlock->second.jumpTarget;
+ if (jumpTarget != -1 && !processedBlocks.contains(jumpTarget))
+ blocks.append(jumpTarget);
+
+ // We can re-enter the first block from the beginning.
+ // We will then find any reads before the write we're currently examining.
+ if (isFirstBlock)
+ isFirstBlock = false;
+ else
+ processedBlocks.append(currentBlock->first);
+ }
+
+ if (!eraseDeadStore(writeIt))
+ newAnnotations.appendOrdered(writeIt);
+ }
+ m_annotations = newAnnotations.take();
+
+ while (erasedReaders) {
+ erasedReaders = false;
+
+ for (auto it = m_annotations.begin(), end = m_annotations.end(); it != end; ++it) {
+ InstructionAnnotation &instruction = it->second;
+ if (instruction.changedRegisterIndex < InvalidRegister) {
+ newAnnotations.appendOrdered(it);
+ continue;
+ }
+
+ auto readers = m_readerLocations.find(it.key());
+ if (readers != m_readerLocations.end()) {
+ for (auto it = readers->readers.begin(); it != readers->readers.end();) {
+ if (m_annotations.contains(it.key()))
+ ++it;
+ else
+ it = readers->readers.erase(it);
+ }
+ }
+
+ if (!eraseDeadStore(it))
+ newAnnotations.appendOrdered(it);
+ }
+
+ m_annotations = newAnnotations.take();
+ }
+}
+
+void QQmlJSBasicBlocks::adjustTypes()
+{
+ using NewVirtualRegisters = NewFlatMap<int, QQmlJSRegisterContent>;
+
+ for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) {
+ // There is always one first occurrence of any tracked type. Conversions don't change
+ // the type.
+ if (it->trackedTypes.length() != 1)
+ continue;
+
+ m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->readers.values());
+ }
+
+ const auto transformRegister = [&](QQmlJSRegisterContent &content) {
+ m_typeResolver->adjustTrackedType(
+ content.storedType(),
+ m_typeResolver->storedType(m_typeResolver->containedType(content)));
+ };
+
+ NewVirtualRegisters newRegisters;
+ for (auto i = m_annotations.begin(), iEnd = m_annotations.end(); i != iEnd; ++i) {
+ if (i->second.changedRegisterIndex != InvalidRegister)
+ transformRegister(i->second.changedRegister);
+ for (auto content : i->second.typeConversions) {
+ QQmlJSScope::ConstPtr conversionResult = content.second.conversionResult();
+ const auto conversionOrigins = content.second.conversionOrigins();
+ QQmlJSScope::ConstPtr newResult;
+ for (const auto &origin : conversionOrigins)
+ newResult = m_typeResolver->merge(newResult, origin);
+ m_typeResolver->adjustTrackedType(conversionResult, newResult);
+ transformRegister(content.second);
+ }
+ i->second.typeConversions = newRegisters.take();
+ }
+}
+
+void QQmlJSBasicBlocks::populateBasicBlocks()
+{
+ for (auto blockNext = m_basicBlocks.begin(), blockEnd = m_basicBlocks.end();
+ blockNext != blockEnd;) {
+
+ const auto blockIt = blockNext++;
+ BasicBlock &block = blockIt->second;
+ QList<QQmlJSScope::ConstPtr> writtenRegisters;
+
+ const auto instrEnd = (blockNext == blockEnd)
+ ? m_annotations.end()
+ : m_annotations.find(blockNext->first);
+ for (auto instrIt = m_annotations.find(blockIt->first); instrIt != instrEnd; ++instrIt) {
+ const InstructionAnnotation &instruction = instrIt->second;
+ for (auto it = instruction.readRegisters.begin(), end = instruction.readRegisters.end();
+ it != end; ++it) {
+ Q_ASSERT(it->second.isConversion());
+ for (const QQmlJSScope::ConstPtr &origin : it->second.conversionOrigins()) {
+ if (!writtenRegisters.contains(origin))
+ block.readRegisters.append(origin);
+ }
+ }
+
+ // If it's just a renaming, the type has existed in a different register before.
+ if (instruction.changedRegisterIndex != InvalidRegister && !instruction.isRename) {
+ writtenRegisters.append(m_typeResolver->trackedContainedType(
+ instruction.changedRegister));
+ }
+ }
+
+ deduplicate(block.readRegisters);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsbasicblocks_p.h b/src/qmlcompiler/qqmljsbasicblocks_p.h
new file mode 100644
index 0000000000..76f3216ce8
--- /dev/null
+++ b/src/qmlcompiler/qqmljsbasicblocks_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLJSBASICBLOCKS_P_H
+#define QQMLJSBASICBLOCKS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+
+#include <private/qqmljscompilepass_p.h>
+#include <private/qflatmap_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlJSBasicBlocks : public QQmlJSCompilePass
+{
+public:
+ struct BasicBlock {
+ QList<int> jumpOrigins;
+ QList<QQmlJSScope::ConstPtr> readRegisters;
+ int jumpTarget = -1;
+ bool jumpIsUnconditional = false;
+ };
+
+ QQmlJSBasicBlocks(const QV4::Compiler::JSUnitGenerator *unitGenerator,
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
+ : QQmlJSCompilePass(unitGenerator, typeResolver, logger)
+ {
+ }
+
+ ~QQmlJSBasicBlocks() = default;
+
+ InstructionAnnotations run(const Function *function, const InstructionAnnotations &annotations);
+
+private:
+ struct RegisterAccess
+ {
+ QList<QQmlJSScope::ConstPtr> trackedTypes;
+ QHash<int, QQmlJSScope::ConstPtr> readers;
+ };
+
+ QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type type) override;
+ void endInstruction(QV4::Moth::Instr::Type type) override;
+
+ void generate_Jump(int offset) override;
+ void generate_JumpTrue(int offset) override;
+ void generate_JumpFalse(int offset) override;
+ void generate_JumpNoException(int offset) override;
+ void generate_JumpNotUndefined(int offset) override;
+
+ void generate_Ret() override;
+ void generate_ThrowException() override;
+
+ enum JumpMode { Unconditional, Conditional };
+ void processJump(int offset, JumpMode mode);
+ void populateBasicBlocks();
+ void populateReaderLocations();
+ void adjustTypes();
+
+ InstructionAnnotations m_annotations;
+ QFlatMap<int, BasicBlock> m_basicBlocks;
+ QHash<int, RegisterAccess> m_readerLocations;
+ bool m_skipUntilNextLabel = false;
+ bool m_hadBackJumps = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSBASICBLOCKS_P_H
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index 874f0918bd..f7fa9ba403 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -477,9 +477,14 @@ void QQmlJSCodeGenerator::generate_LoadConst(int index)
auto encodedConst = m_jsUnitGenerator->constant(index);
double value = QV4::StaticValue::fromReturnedValue(encodedConst).doubleValue();
m_body += m_state.accumulatorVariableOut;
- m_body += u" = "_qs;
- m_body += toNumericString(value); // ### handle other types
+
+ // ### handle other types?
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->intType(), m_state.accumulatorOut().storedType(),
+ toNumericString(value));
+
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_LoadZero()
@@ -487,8 +492,10 @@ void QQmlJSCodeGenerator::generate_LoadZero()
INJECT_TRACE_INFO(generate_LoadZero);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = 0"_qs;
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->intType(), m_state.accumulatorOut().storedType(), u"0"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_LoadTrue()
@@ -496,8 +503,10 @@ void QQmlJSCodeGenerator::generate_LoadTrue()
INJECT_TRACE_INFO(generate_LoadTrue);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = true"_qs;
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), u"true"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_LoadFalse()
@@ -505,36 +514,32 @@ void QQmlJSCodeGenerator::generate_LoadFalse()
INJECT_TRACE_INFO(generate_LoadFalse);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = false"_qs;
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), u"false"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
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;
+ generateOutputVariantConversion(m_typeResolver->nullType());
}
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;
+ generateOutputVariantConversion(m_typeResolver->voidType());
}
void QQmlJSCodeGenerator::generate_LoadInt(int value)
@@ -546,6 +551,7 @@ void QQmlJSCodeGenerator::generate_LoadInt(int value)
m_body += conversion(m_typeResolver->intType(), m_state.accumulatorOut().storedType(),
QString::number(value));
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
@@ -563,35 +569,34 @@ void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
m_jsUnitGenerator->constant(constIndex));
const auto changed = m_state.changedRegister().storedType();
+ QQmlJSScope::ConstPtr contained;
+ QString input;
m_body += var + u" = "_qs;
if (v4Value.isNull()) {
- m_body += conversion(m_typeResolver->nullType(), changed, QString());
+ contained = m_typeResolver->nullType();
} else if (v4Value.isUndefined()) {
- m_body += conversion(m_typeResolver->voidType(), changed, QString());
+ contained = m_typeResolver->voidType();
} else if (v4Value.isBoolean()) {
- m_body += conversion(m_typeResolver->boolType(), changed,
- v4Value.booleanValue() ? u"true"_qs : u"false"_qs);
+ contained = m_typeResolver->boolType();
+ input = v4Value.booleanValue() ? u"true"_qs : u"false"_qs;
} else if (v4Value.isInteger()) {
- m_body += conversion(m_typeResolver->intType(), changed,
- QString::number(v4Value.int_32()));
+ contained = m_typeResolver->intType();
+ input = QString::number(v4Value.int_32());
} else if (v4Value.isDouble()) {
- m_body += conversion(m_typeResolver->realType(), changed,
- toNumericString(v4Value.doubleValue()));
+ contained = m_typeResolver->realType();
+ input = toNumericString(v4Value.doubleValue());
} else {
reject(u"unknown const type"_qs);
}
- m_body += u";\n"_qs;
+ m_body += conversion(contained, changed, input) + u";\n"_qs;
+ generateOutputVariantConversion(contained);
}
void QQmlJSCodeGenerator::generate_LoadReg(int reg)
{
INJECT_TRACE_INFO(generate_LoadReg);
- // We won't emit any code for loading undefined.
- // See also generate_LoadUndefined()
- if (m_typeResolver->registerContains(m_state.accumulatorOut(), m_typeResolver->voidType()))
- return;
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
m_body += conversion(registerType(reg), m_state.accumulatorOut(), use(registerVariable(reg)));
@@ -672,6 +677,8 @@ void QQmlJSCodeGenerator::generate_LoadRuntimeString(int stringId)
m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut().storedType(),
QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId)));
m_body += u";\n"_qs;
+
+ generateOutputVariantConversion(m_typeResolver->stringType());
}
void QQmlJSCodeGenerator::generate_MoveRegExp(int regExpId, int destReg)
@@ -697,6 +704,8 @@ void QQmlJSCodeGenerator::generate_LoadGlobalLookup(int index)
{
INJECT_TRACE_INFO(generate_LoadGlobalLookup);
+ AccumulatorConverter registers(this);
+
const QString lookup = u"aotContext->loadGlobalLookup("_qs + QString::number(index)
+ u", &"_qs + m_state.accumulatorVariableOut + u", "_qs
+ metaTypeFromType(m_state.accumulatorOut().storedType()) + u')';
@@ -709,9 +718,6 @@ void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(int index)
{
INJECT_TRACE_INFO(generate_LoadQmlContextPropertyLookup);
- if (m_state.accumulatorVariableOut.isEmpty())
- return;
-
AccumulatorConverter registers(this);
const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
@@ -827,6 +833,8 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
return;
}
+ AccumulatorConverter registers(this);
+
const QString baseName = use(registerVariable(base));
const QString indexName = use(m_state.accumulatorVariableIn);
@@ -971,6 +979,26 @@ void QQmlJSCodeGenerator::generateTypeLookup(int index)
}
}
+void QQmlJSCodeGenerator::generateOutputVariantConversion(const QQmlJSScope::ConstPtr &containedType)
+{
+ if (changedRegisterVariable().isEmpty())
+ return;
+
+ const QQmlJSRegisterContent changed = m_state.changedRegister();
+ if (!m_typeResolver->equals(changed.storedType(), m_typeResolver->varType()))
+ return;
+
+ const QQmlJSScope::ConstPtr target = m_typeResolver->containedType(changed);
+ if (m_typeResolver->equals(target, containedType)
+ || m_typeResolver->equals(target, m_typeResolver->varType())) {
+ return;
+ }
+
+ // If we could store the type directly, we would not wrap it in a QVariant.
+ // Therefore, our best bet here is metaTypeFromName().
+ m_body += changedRegisterVariable() + u".convert("_qs + metaTypeFromName(target) + u");\n"_qs;
+}
+
void QQmlJSCodeGenerator::generate_GetLookup(int index)
{
INJECT_TRACE_INFO(generate_GetLookup);
@@ -1471,6 +1499,8 @@ void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int a
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
reject(u"call to untyped JavaScript function"_qs);
+ AccumulatorConverter registers(this);
+
const QQmlJSRegisterContent baseType = registerType(base);
if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
const QString name = m_jsUnitGenerator->stringForIndex(
@@ -1541,6 +1571,8 @@ void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(int index, int a
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
reject(u"call to untyped JavaScript function"_qs);
+ AccumulatorConverter registers(this);
+
m_body.setHasSideEffects(true);
const QString indexString = QString::number(index);
@@ -1761,16 +1793,11 @@ void QQmlJSCodeGenerator::generate_DefineArray(int argc, int 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;
+ generateOutputVariantConversion(m_typeResolver->emptyListType());
}
void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
@@ -1893,25 +1920,30 @@ void QQmlJSCodeGenerator::generate_CmpEqNull()
{
INJECT_TRACE_INFO(generate_CmlEqNull);
- m_body += m_state.accumulatorVariableOut;
- m_body += u" = QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs;
- m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
- use(m_state.accumulatorVariableIn));
- m_body += u')';
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ u"QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs
+ + conversion(
+ m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
+ use(m_state.accumulatorVariableIn)) + u')');
m_body += u";\n"_qs;
-
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_CmpNeNull()
{
INJECT_TRACE_INFO(generate_CmlNeNull);
- m_body += m_state.accumulatorVariableOut;
- m_body += u" = !QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs;
- m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
- use(m_state.accumulatorVariableIn));
- m_body += u')';
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ u"!QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs
+ + conversion(
+ m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
+ use(m_state.accumulatorVariableIn)) + u')');
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
@@ -1990,16 +2022,20 @@ void QQmlJSCodeGenerator::generate_CmpEqInt(int lhsConst)
{
INJECT_TRACE_INFO(generate_CmpEqInt);
- m_body += m_state.accumulatorVariableOut + u" = "_qs + eqIntExpression(lhsConst)
- + u";\n"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ eqIntExpression(lhsConst)) + u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_CmpNeInt(int lhsConst)
{
INJECT_TRACE_INFO(generate_CmpNeInt);
- m_body += m_state.accumulatorVariableOut + u" = !("_qs + eqIntExpression(lhsConst)
- + u");\n"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ u"!("_qs + eqIntExpression(lhsConst) + u')') + u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_CmpEq(int lhs)
@@ -2234,6 +2270,8 @@ void QQmlJSCodeGenerator::generate_Mod(int lhs)
m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut().storedType(),
u'(' + lhsVar + u" % "_qs + rhsVar + u')');
m_body += u";\n"_qs;
+
+ generateOutputVariantConversion(m_typeResolver->jsPrimitiveType());
}
void QQmlJSCodeGenerator::generate_Sub(int lhs)
@@ -2341,7 +2379,7 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
// 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)
+ if (!m_state.hasSideEffects() && changedRegisterVariable().isEmpty())
return SkipInstruction;
return ProcessInstruction;
@@ -2392,40 +2430,42 @@ void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &func
const auto primitive = m_typeResolver->jsPrimitiveType();
if (m_typeResolver->equals(lhsType, rhsType) && !m_typeResolver->equals(lhsType, primitive)) {
- m_body += use(registerVariable(lhs));
- m_body += (invert ? u" != "_qs : u" == "_qs);
- m_body += use(m_state.accumulatorVariableIn);
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ use(registerVariable(lhs)) + (invert ? u" != "_qs : u" == "_qs)
+ + use(m_state.accumulatorVariableIn));
} else {
- if (invert)
- m_body += u'!';
- m_body += conversion(registerType(lhs).storedType(), primitive, use(registerVariable(lhs)));
- m_body += u'.';
- m_body += function;
- m_body += u'(';
- m_body += conversion(m_state.accumulatorIn().storedType(), primitive,
- use(m_state.accumulatorVariableIn));
- m_body += u')';
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ (invert ? u"!"_qs : QString())
+ + conversion(registerType(lhs).storedType(), primitive,
+ use(registerVariable(lhs)))
+ + u'.' + function + u'(' + conversion(
+ m_state.accumulatorIn().storedType(), primitive,
+ use(m_state.accumulatorVariableIn))
+ + u')');
}
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOperator)
{
- m_body += m_state.accumulatorVariableOut;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
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->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,
- use(m_state.accumulatorVariableIn));
+
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ conversion(registerType(lhs).storedType(), compareType, use(registerVariable(lhs)))
+ + u' ' + cppOperator + u' '
+ + conversion(m_state.accumulatorIn().storedType(), compareType,
+ use(m_state.accumulatorVariableIn)));
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cppOperator)
@@ -2437,13 +2477,14 @@ void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cp
Q_ASSERT(!lhsVar.isEmpty());
Q_ASSERT(!rhsVar.isEmpty());
+ const QQmlJSRegisterContent originalOut = m_typeResolver->original(m_state.accumulatorOut());
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
m_body += conversion(
- m_typeResolver->original(m_state.accumulatorOut()),
- m_state.accumulatorOut(),
+ originalOut, m_state.accumulatorOut(),
u'(' + lhsVar + u' ' + cppOperator + u' ' + rhsVar + u')');
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->containedType(originalOut));
}
void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator)
@@ -2467,6 +2508,8 @@ void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator)
m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion(
m_typeResolver->original(m_state.accumulatorOut()),
m_state.accumulatorOut(), cppOperator + var) + u";\n"_qs;
+
+ generateOutputVariantConversion(m_typeResolver->containedType(original));
}
void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator)
@@ -2492,6 +2535,7 @@ void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator)
m_typeResolver->original(m_state.accumulatorOut()),
m_state.accumulatorOut(), cppOperator + u"converted"_qs) + u";\n"_qs;
m_body += u"}\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->containedType(original));
}
void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization,
@@ -2814,6 +2858,9 @@ QQmlJSCodeGenerator::AccumulatorConverter::AccumulatorConverter(QQmlJSCodeGenera
, accumulatorVariableOut(generator->m_state.accumulatorVariableOut)
, generator(generator)
{
+ if (accumulatorVariableOut.isEmpty())
+ return;
+
const QQmlJSTypeResolver *resolver = generator->m_typeResolver;
const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(accumulatorOut);
const QQmlJSScope::ConstPtr origStored = resolver->originalType(accumulatorOut.storedType());
@@ -2846,9 +2893,12 @@ QQmlJSCodeGenerator::AccumulatorConverter::~AccumulatorConverter()
generator->m_body += accumulatorVariableOut + u" = "_qs + generator->conversion(
generator->m_state.accumulatorOut(), accumulatorOut,
generator->m_state.accumulatorVariableOut) + u";\n"_qs;
+ const auto contained = generator->m_typeResolver->containedType(
+ generator->m_state.accumulatorOut());
generator->m_body += u"}\n"_qs;
generator->m_state.setRegister(Accumulator, accumulatorOut);
generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
+ generator->generateOutputVariantConversion(contained);
} else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
generator->m_body += u"}\n"_qs;
generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h
index 687e22ed19..5128b585d7 100644
--- a/src/qmlcompiler/qqmljscodegenerator_p.h
+++ b/src/qmlcompiler/qqmljscodegenerator_p.h
@@ -347,6 +347,7 @@ private:
void generateInPlaceOperation(const QString &cppOperator);
void generateMoveOutVar(const QString &outVar);
void generateTypeLookup(int index);
+ void generateOutputVariantConversion(const QQmlJSScope::ConstPtr &containedType);
QString eqIntExpression(int lhsConst);
QString argumentsList(int argc, int argv, QString *outVar);
diff --git a/src/qmlcompiler/qqmljscompiler.cpp b/src/qmlcompiler/qqmljscompiler.cpp
index 30b74fe6ee..c765a1c94e 100644
--- a/src/qmlcompiler/qqmljscompiler.cpp
+++ b/src/qmlcompiler/qqmljscompiler.cpp
@@ -29,6 +29,7 @@
#include "qqmljscompiler_p.h"
#include <private/qqmlirbuilder_p.h>
+#include <private/qqmljsbasicblocks_p.h>
#include <private/qqmljscodegenerator_p.h>
#include <private/qqmljsfunctioninitializer_p.h>
#include <private/qqmljsimportvisitor_p.h>
@@ -768,6 +769,9 @@ QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
if (error->isValid())
return compileError();
+ QQmlJSBasicBlocks basicBlocks(m_unitGenerator, &m_typeResolver, m_logger);
+ typePropagationResult = basicBlocks.run(function, typePropagationResult);
+
QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger);
shadowCheck.run(&typePropagationResult, function, error);
if (error->isValid())
diff --git a/src/qmlcompiler/qqmljsshadowcheck.cpp b/src/qmlcompiler/qqmljsshadowcheck.cpp
index 79f197750c..283553f758 100644
--- a/src/qmlcompiler/qqmljsshadowcheck.cpp
+++ b/src/qmlcompiler/qqmljsshadowcheck.cpp
@@ -64,12 +64,16 @@ void QQmlJSShadowCheck::run(
void QQmlJSShadowCheck::generate_LoadProperty(int nameIndex)
{
- checkShadowing(m_state.accumulatorIn(), m_jsUnitGenerator->stringForIndex(nameIndex));
+ auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn != m_state.registers.end())
+ checkShadowing(accumulatorIn.value(), m_jsUnitGenerator->stringForIndex(nameIndex));
}
void QQmlJSShadowCheck::generate_GetLookup(int index)
{
- checkShadowing(m_state.accumulatorIn(), m_jsUnitGenerator->lookupName(index));
+ auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn != m_state.registers.end())
+ checkShadowing(accumulatorIn.value(), m_jsUnitGenerator->lookupName(index));
}
void QQmlJSShadowCheck::generate_StoreProperty(int nameIndex, int base)
diff --git a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
index 889150f2dd..ce07a333d5 100644
--- a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
+++ b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
@@ -61,38 +61,25 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSStorageGeneralizer::run(
}
}
- 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 transformRegister = [&](QQmlJSRegisterContent &content) {
+ if (const QQmlJSScope::ConstPtr &specific = content.storedType())
+ m_typeResolver->generalizeType(specific);
};
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;
+ = [&](QFlatMap<int, QQmlJSRegisterContent> &registers) {
+ for (auto j = registers.begin(), jEnd = registers.end(); j != jEnd; ++j)
+ transformRegister(j.value());
};
for (QQmlJSRegisterContent &argument : function->argumentTypes) {
Q_ASSERT(argument.isValid());
- transformRegister(argument, 0);
+ transformRegister(argument);
}
for (auto i = annotations.begin(), iEnd = annotations.end(); i != iEnd; ++i) {
- if (!transformRegister(i->second.changedRegister, i.key()))
- return InstructionAnnotations();
- if (!transformRegisters(i->second.typeConversions, i.key()))
- return InstructionAnnotations();
+ transformRegister(i->second.changedRegister);
+ transformRegisters(i->second.typeConversions);
}
return annotations;
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index 277f45512f..1b0520e70d 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -587,6 +587,12 @@ QQmlJSRegisterContent QQmlJSTypeResolver::merge(const QQmlJSRegisterContent &a,
QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
const QQmlJSScope::ConstPtr &b) const
{
+ if (a.isNull())
+ return b;
+
+ if (b.isNull())
+ return a;
+
const auto commonBaseType = [this](
const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) {
for (QQmlJSScope::ConstPtr aBase = a; aBase; aBase = aBase->baseType()) {