diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp index 0aedcbbd3c..67d331e9ff 100644 --- a/src/qmlcompiler/qqmljsbasicblocks.cpp +++ b/src/qmlcompiler/qqmljsbasicblocks.cpp @@ -311,16 +311,6 @@ static bool containsAny(const ContainerA &container, const ContainerB &elements) return false; } -template -static bool containsAll(const ContainerA &container, const ContainerB &elements) -{ - for (const auto &element : elements) { - if (!container.contains(element)) - return false; - } - return true; -} - template, class KeyContainer = QList, class MappedContainer = QList> class NewFlatMap @@ -347,7 +337,7 @@ private: struct PendingBlock { - QList conversions; + QQmlJSBasicBlocks::Conversions conversions; int start = -1; bool registerActive = false; }; @@ -434,7 +424,7 @@ void QQmlJSBasicBlocks::populateReaderLocations() auto nextBlock = m_basicBlocks.find(block.start); auto currentBlock = nextBlock++; bool registerActive = block.registerActive; - QList conversions = block.conversions; + Conversions conversions = block.conversions; const auto blockEnd = (nextBlock == m_basicBlocks.end()) ? m_annotations.end() @@ -446,7 +436,7 @@ void QQmlJSBasicBlocks::populateReaderLocations() for (; blockInstr != blockEnd; ++blockInstr) { if (registerActive && blockInstr->second.typeConversions.contains(writtenRegister)) { - conversions.append(blockInstr.key()); + conversions.insert(blockInstr.key()); } for (auto readIt = blockInstr->second.readRegisters.constBegin(), @@ -473,12 +463,26 @@ void QQmlJSBasicBlocks::populateReaderLocations() // If we find that an already processed block has the register activated by this jump, // we need to re-evaluate it. We also need to propagate any newly found conversions. const auto processed = processedBlocks.find(blockStart); - if (processed == processedBlocks.end()) + if (processed == processedBlocks.end()) { blocks.append({conversions, blockStart, registerActive}); - else if (registerActive && !processed->registerActive) + } else if (registerActive && !processed->registerActive) { blocks.append({conversions, blockStart, registerActive}); - else if (!containsAll(processed->conversions, conversions)) - blocks.append({processed->conversions + conversions, blockStart, registerActive}); + } else { + + // TODO: Use unite() once it is fixed. + // We don't use unite() here since it would be more expensive. unite() + // effectively loops on only insert() and insert() does a number of checks + // each time. We trade those checks for calculating the hash twice on each + // iteration. Calculating the hash is very cheap for integers. + Conversions merged = processed->conversions; + for (const int conversion : std::as_const(conversions)) { + if (!merged.contains(conversion)) + merged.insert(conversion); + } + + if (merged.size() > processed->conversions.size()) + blocks.append({std::move(merged), blockStart, registerActive}); + } }; if (!currentBlock->second.jumpIsUnconditional && nextBlock != m_basicBlocks.end()) diff --git a/src/qmlcompiler/qqmljsbasicblocks_p.h b/src/qmlcompiler/qqmljsbasicblocks_p.h index 303070a2f2..346c91b951 100644 --- a/src/qmlcompiler/qqmljsbasicblocks_p.h +++ b/src/qmlcompiler/qqmljsbasicblocks_p.h @@ -24,6 +24,8 @@ QT_BEGIN_NAMESPACE class Q_QMLCOMPILER_EXPORT QQmlJSBasicBlocks : public QQmlJSCompilePass { public: + using Conversions = QSet; + struct BasicBlock { QList jumpOrigins; QList readRegisters; @@ -55,7 +57,7 @@ private: { QList trackedTypes; QHash typeReaders; - QHash> registerReadersAndConversions; + QHash registerReadersAndConversions; int trackedRegister; };