diff options
| author | Olivier De Cannière <olivier.decanniere@qt.io> | 2024-07-02 10:38:49 +0200 |
|---|---|---|
| committer | Olivier De Cannière <olivier.decanniere@qt.io> | 2024-07-02 13:54:28 +0200 |
| commit | d16499ecdbb10debaa1f53c9c1da88dde35a1bba (patch) | |
| tree | 65f388f85062b5e1ce4e58e115ef22f1ba043919 /src/qmlcompiler/qqmljsoptimizations.cpp | |
| parent | e14c148261a4511e1c1a4f3b934ec97b782e832c (diff) | |
Compiler: Weaken god function populateReaderLocations
The function populateReaderLocations does too much and is very hard to
understand. This patch breaks up its functionality into several pieces.
populateReaderLocations keeps the code related to what its name
suggests it does: determining, for each instruction, what it reads and
writes. The dead store elimination part is extracted into a separate
function.
The TODO for the merging of conversions types using QSet::unite() was
also removed. This function was recently optimized in
92acc94fa98d19929d28feb5d543acf8c50c2290 and can be used again.
Change-Id: Iad0870e08d09b47acbc51afa96e582f95e61b18f
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qmlcompiler/qqmljsoptimizations.cpp')
| -rw-r--r-- | src/qmlcompiler/qqmljsoptimizations.cpp | 142 |
1 files changed, 75 insertions, 67 deletions
diff --git a/src/qmlcompiler/qqmljsoptimizations.cpp b/src/qmlcompiler/qqmljsoptimizations.cpp index d542331be6..e0c9296bd4 100644 --- a/src/qmlcompiler/qqmljsoptimizations.cpp +++ b/src/qmlcompiler/qqmljsoptimizations.cpp @@ -17,6 +17,7 @@ QQmlJSCompilePass::BlocksAndAnnotations QQmlJSOptimizations::run(const Function populateBasicBlocks(); populateReaderLocations(); + removeDeadStoresUntilStable(); adjustTypes(); return { std::move(m_basicBlocks), std::move(m_annotations) }; @@ -68,46 +69,13 @@ private: void QQmlJSOptimizations::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->typeReaders.isEmpty() || reader->registerReadersAndConversions.isEmpty())) { - - if (it->second.isRename) { - // If it's a rename, it doesn't "own" its output type. The type may - // still be read elsewhere, even if this register isn't. However, we're - // not interested in the variant or any other details of the register. - // Therefore just delete it. - it->second.changedRegisterIndex = InvalidRegister; - it->second.changedRegister = QQmlJSRegisterContent(); - } else { - // void the output, rather than deleting it. We still need its variant. - const bool adjusted = m_typeResolver->adjustTrackedType( - it->second.changedRegister.containedType(), - m_typeResolver->voidType()); - Q_ASSERT(adjusted); // Can always convert to void - } - m_readerLocations.erase(reader); - - // If it's not a label and has no side effects, we can drop the instruction. - if (!it->second.hasSideEffects) { - if (!it->second.readRegisters.isEmpty()) { - it->second.readRegisters.clear(); - erasedReaders = true; - } - if (m_basicBlocks.find(it.key()) == m_basicBlocks.end()) - 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; + + // Instructions that don't write can't be dead stores, no need to populate reader locations if (writtenRegister == InvalidRegister) { newAnnotations.appendOrdered(writeIt); continue; @@ -186,17 +154,8 @@ void QQmlJSOptimizations::populateReaderLocations() } else if (registerActive && !processed->registerActive) { blocks.append({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); - } + merged.unite(conversions); if (merged.size() > processed->conversions.size()) blocks.append({std::move(merged), blockStart, registerActive}); @@ -214,41 +173,67 @@ void QQmlJSOptimizations::populateReaderLocations() isFirstBlock = false; } - if (!eraseDeadStore(writeIt)) - newAnnotations.appendOrdered(writeIt); + newAnnotations.appendOrdered(writeIt); } m_annotations = newAnnotations.take(); +} + +bool QQmlJSOptimizations::eraseDeadStore(const InstructionAnnotations::iterator &it, + bool &erasedReaders) +{ + auto reader = m_readerLocations.find(it.key()); + if (reader != m_readerLocations.end() + && (reader->typeReaders.isEmpty() || reader->registerReadersAndConversions.isEmpty())) { + + if (it->second.isRename) { + // If it's a rename, it doesn't "own" its output type. The type may + // still be read elsewhere, even if this register isn't. However, we're + // not interested in the variant or any other details of the register. + // Therefore just delete it. + it->second.changedRegisterIndex = InvalidRegister; + it->second.changedRegister = QQmlJSRegisterContent(); + } else { + // void the output, rather than deleting it. We still need its variant. + const bool adjusted = m_typeResolver->adjustTrackedType( + it->second.changedRegister.containedType(), m_typeResolver->voidType()); + Q_ASSERT(adjusted); // Can always convert to void + } + m_readerLocations.erase(reader); + + // If it's not a label and has no side effects, we can drop the instruction. + if (!it->second.hasSideEffects) { + if (!it->second.readRegisters.isEmpty()) { + it->second.readRegisters.clear(); + erasedReaders = true; + } + if (m_basicBlocks.find(it.key()) == m_basicBlocks.end()) + return true; + } + } + return false; +} +void QQmlJSOptimizations::removeDeadStoresUntilStable() +{ + using NewInstructionAnnotations = NewFlatMap<int, InstructionAnnotation>; + NewInstructionAnnotations newAnnotations; + + bool erasedReaders = true; while (erasedReaders) { erasedReaders = false; for (auto it = m_annotations.begin(), end = m_annotations.end(); it != end; ++it) { InstructionAnnotation &instruction = it->second; + + // Don't touch the function prolog instructions if (instruction.changedRegisterIndex < InvalidRegister) { newAnnotations.appendOrdered(it); continue; } - auto readers = m_readerLocations.find(it.key()); - if (readers != m_readerLocations.end()) { - for (auto typeIt = readers->typeReaders.begin(); - typeIt != readers->typeReaders.end();) { - if (m_annotations.contains(typeIt.key())) - ++typeIt; - else - typeIt = readers->typeReaders.erase(typeIt); - } + removeReadsFromErasedInstructions(it); - for (auto registerIt = readers->registerReadersAndConversions.begin(); - registerIt != readers->registerReadersAndConversions.end();) { - if (m_annotations.contains(registerIt.key())) - ++registerIt; - else - registerIt = readers->registerReadersAndConversions.erase(registerIt); - } - } - - if (!eraseDeadStore(it)) + if (!eraseDeadStore(it, erasedReaders)) newAnnotations.appendOrdered(it); } @@ -256,6 +241,29 @@ void QQmlJSOptimizations::populateReaderLocations() } } +void QQmlJSOptimizations::removeReadsFromErasedInstructions( + const QFlatMap<int, InstructionAnnotation>::const_iterator &it) +{ + auto readers = m_readerLocations.find(it.key()); + if (readers == m_readerLocations.end()) + return; + + for (auto typeIt = readers->typeReaders.begin(); typeIt != readers->typeReaders.end();) { + if (m_annotations.contains(typeIt.key())) + ++typeIt; + else + typeIt = readers->typeReaders.erase(typeIt); + } + + for (auto registerIt = readers->registerReadersAndConversions.begin(); + registerIt != readers->registerReadersAndConversions.end();) { + if (m_annotations.contains(registerIt.key())) + ++registerIt; + else + registerIt = readers->registerReadersAndConversions.erase(registerIt); + } +} + bool QQmlJSOptimizations::canMove(int instructionOffset, const QQmlJSOptimizations::RegisterAccess &access) const { |
