diff options
Diffstat (limited to 'src/qmlcompiler/qqmljsbasicblocks.cpp')
-rw-r--r-- | src/qmlcompiler/qqmljsbasicblocks.cpp | 610 |
1 files changed, 204 insertions, 406 deletions
diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp index 3583aa2405..392a8b9ba7 100644 --- a/src/qmlcompiler/qqmljsbasicblocks.cpp +++ b/src/qmlcompiler/qqmljsbasicblocks.cpp @@ -2,48 +2,113 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "qqmljsbasicblocks_p.h" +#include "qqmljsutils_p.h" + +#include <QtQml/private/qv4instr_moth_p.h> QT_BEGIN_NAMESPACE -template<typename Container> -void deduplicate(Container &container) +using namespace Qt::Literals::StringLiterals; + +DEFINE_BOOL_CONFIG_OPTION(qv4DumpBasicBlocks, QV4_DUMP_BASIC_BLOCKS) +DEFINE_BOOL_CONFIG_OPTION(qv4ValidateBasicBlocks, QV4_VALIDATE_BASIC_BLOCKS) + +void QQmlJSBasicBlocks::dumpBasicBlocks() { - std::sort(container.begin(), container.end()); - auto erase = std::unique(container.begin(), container.end()); - container.erase(erase, container.end()); + qDebug().noquote() << "=== Basic Blocks for \"%1\""_L1.arg(m_context->name); + for (const auto &[blockOffset, block] : m_basicBlocks) { + QDebug debug = qDebug().nospace(); + debug << "Block " << (blockOffset < 0 ? "Function prolog"_L1 : QString::number(blockOffset)) + << ":\n"; + debug << " jumpOrigins[" << block.jumpOrigins.size() << "]: "; + for (auto origin : block.jumpOrigins) { + debug << origin << ", "; + } + debug << "\n readRegisters[" << block.readRegisters.size() << "]: "; + for (auto reg : block.readRegisters) { + debug << reg << ", "; + } + debug << "\n readTypes[" << block.readTypes.size() << "]: "; + for (const auto &type : block.readTypes) { + debug << type->augmentedInternalName() << ", "; + } + debug << "\n jumpTarget: " << block.jumpTarget; + debug << "\n jumpIsUnConditional: " << block.jumpIsUnconditional; + debug << "\n isReturnBlock: " << block.isReturnBlock; + debug << "\n isThrowBlock: " << block.isThrowBlock; + } + qDebug() << "\n"; } -static bool instructionManipulatesContext(QV4::Moth::Instr::Type type) +void QQmlJSBasicBlocks::dumpDOTGraph() { - 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; + QString output; + QTextStream s{ &output }; + s << "=== Basic Blocks Graph in DOT format for \"%1\" (spaces are encoded as" + "   to preserve formatting)\n"_L1.arg(m_context->name); + s << "digraph BasicBlocks {\n"_L1; + + QFlatMap<int, BasicBlock> blocks{ m_basicBlocks }; + for (const auto &[blockOffset, block] : blocks) { + for (int originOffset : block.jumpOrigins) { + const auto originBlockIt = basicBlockForInstruction(blocks, originOffset); + const auto isBackEdge = originOffset > blockOffset && originBlockIt->second.jumpIsUnconditional; + s << " %1 -> %2%3\n"_L1.arg(QString::number(originBlockIt.key())) + .arg(QString::number(blockOffset)) + .arg(isBackEdge ? " [color=blue]"_L1 : ""_L1); + } + } + + for (const auto &[blockOffset, block] : blocks) { + if (blockOffset < 0) { + s << " %1 [shape=record, fontname=\"Monospace\", label=\"Function Prolog\"]\n"_L1 + .arg(QString::number(blockOffset)); + continue; + } + + auto nextBlockIt = blocks.lower_bound(blockOffset + 1); + int nextBlockOffset = nextBlockIt == blocks.end() ? m_context->code.size() : nextBlockIt->first; + QString dump = QV4::Moth::dumpBytecode( + m_context->code.constData(), m_context->code.size(), m_context->locals.size(), + m_context->formals->length(), blockOffset, nextBlockOffset - 1, + m_context->lineAndStatementNumberMapping); + dump = dump.replace(" "_L1, " "_L1); // prevent collapse of extra whitespace for formatting + dump = dump.replace("\n"_L1, "\\l"_L1); // new line + left aligned + s << " %1 [shape=record, fontname=\"Monospace\", label=\"{Block %1: | %2}\"]\n"_L1 + .arg(QString::number(blockOffset)) + .arg(dump); + } + s << "}\n"_L1; + + // Have unique names to prevent overwriting of functions with the same name (eg. anonymous functions). + static int functionCount = 0; + static const auto dumpFolderPath = qEnvironmentVariable("QV4_DUMP_BASIC_BLOCKS"); + + QString expressionName = m_context->name == ""_L1 + ? "anonymous"_L1 + : QString(m_context->name).replace(" "_L1, "_"_L1); + QString fileName = "function"_L1 + QString::number(functionCount++) + "_"_L1 + expressionName + ".gv"_L1; + QFile dumpFile(dumpFolderPath + (dumpFolderPath.endsWith("/"_L1) ? ""_L1 : "/"_L1) + fileName); + + if (dumpFolderPath == "-"_L1 || dumpFolderPath == "1"_L1 || dumpFolderPath == "true"_L1) { + qDebug().noquote() << output; + } else { + if (!dumpFile.open(QIODeviceBase::Truncate | QIODevice::WriteOnly)) { + qDebug() << "Error: Could not open file to dump the basic blocks into"; + } else { + dumpFile.write(("//"_L1 + output).toLatin1().toStdString().c_str()); + dumpFile.close(); + } } - return false; } -QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run( - const Function *function, - const InstructionAnnotations &annotations) +QQmlJSCompilePass::BlocksAndAnnotations +QQmlJSBasicBlocks::run(const Function *function, QQmlJSAotCompiler::Flags compileFlags, + bool &basicBlocksValidationFailed) { + basicBlocksValidationFailed = false; + m_function = function; - m_annotations = annotations; for (int i = 0, end = function->argumentTypes.size(); i != end; ++i) { InstructionAnnotation annotation; @@ -59,7 +124,11 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run( m_annotations[-annotation.changedRegisterIndex] = annotation; } + // Insert the function prolog block followed by the first "real" block. m_basicBlocks.insert_or_assign(m_annotations.begin().key(), BasicBlock()); + BasicBlock zeroBlock; + zeroBlock.jumpOrigins.append(m_basicBlocks.begin().key()); + m_basicBlocks.insert(0, zeroBlock); const QByteArray byteCode = function->code; decode(byteCode.constData(), static_cast<uint>(byteCode.size())); @@ -74,16 +143,27 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run( it->second.jumpIsUnconditional = false; } + m_skipUntilNextLabel = false; + reset(); decode(byteCode.constData(), static_cast<uint>(byteCode.size())); for (auto it = m_basicBlocks.begin(), end = m_basicBlocks.end(); it != end; ++it) - deduplicate(it->second.jumpOrigins); + QQmlJSUtils::deduplicate(it->second.jumpOrigins); } - populateBasicBlocks(); - populateReaderLocations(); - adjustTypes(); - return std::move(m_annotations); + if (compileFlags.testFlag(QQmlJSAotCompiler::ValidateBasicBlocks) || qv4ValidateBasicBlocks()) { + if (auto validationResult = basicBlocksValidation(); !validationResult.success) { + qDebug() << "Basic blocks validation failed: %1."_L1.arg(validationResult.errorMessage); + basicBlocksValidationFailed = true; + } + } + + if (qv4DumpBasicBlocks()) { + dumpBasicBlocks(); + dumpDOTGraph(); + } + + return { std::move(m_basicBlocks), std::move(m_annotations) }; } QV4::Moth::ByteCodeHandler::Verdict QQmlJSBasicBlocks::startInstruction(QV4::Moth::Instr::Type type) @@ -132,22 +212,64 @@ void QQmlJSBasicBlocks::generate_JumpNotUndefined(int offset) processJump(offset, Conditional); } +void QQmlJSBasicBlocks::generate_IteratorNext(int value, int offset) +{ + Q_UNUSED(value); + processJump(offset, Conditional); +} + +void QQmlJSBasicBlocks::generate_GetOptionalLookup(int index, int offset) +{ + Q_UNUSED(index); + processJump(offset, Conditional); +} + void QQmlJSBasicBlocks::generate_Ret() { + auto currentBlock = basicBlockForInstruction(m_basicBlocks, currentInstructionOffset()); + currentBlock.value().isReturnBlock = true; m_skipUntilNextLabel = true; } void QQmlJSBasicBlocks::generate_ThrowException() { + auto currentBlock = basicBlockForInstruction(m_basicBlocks, currentInstructionOffset()); + currentBlock.value().isThrowBlock = true; m_skipUntilNextLabel = true; } -void QQmlJSBasicBlocks::generate_DefineArray(int argc, int) +void QQmlJSBasicBlocks::generate_DefineArray(int argc, int argv) +{ + if (argc == 0) + return; // empty array/list, nothing to do + + m_objectAndArrayDefinitions.append({ + currentInstructionOffset(), ObjectOrArrayDefinition::ArrayClassId, argc, argv + }); +} + +void QQmlJSBasicBlocks::generate_DefineObjectLiteral(int internalClassId, int argc, int argv) { if (argc == 0) + return; + + m_objectAndArrayDefinitions.append({ currentInstructionOffset(), internalClassId, argc, argv }); +} + +void QQmlJSBasicBlocks::generate_Construct(int func, int argc, int argv) +{ + Q_UNUSED(func) + if (argc == 0) return; // empty array/list, nothing to do - m_arrayDefinitions.append(currentInstructionOffset()); + m_objectAndArrayDefinitions.append({ + currentInstructionOffset(), + argc == 1 + ? ObjectOrArrayDefinition::ArrayConstruct1ArgId + : ObjectOrArrayDefinition::ArrayClassId, + argc, + argv + }); } void QQmlJSBasicBlocks::processJump(int offset, JumpMode mode) @@ -156,400 +278,76 @@ void QQmlJSBasicBlocks::processJump(int offset, JumpMode mode) 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; + auto currentBlock = basicBlockForInstruction(m_basicBlocks, currentInstructionOffset()); 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()); + m_basicBlocks.insert(nextInstructionOffset(), BasicBlock()); } -template<typename ContainerA, typename ContainerB> -static bool containsAny(const ContainerA &container, const ContainerB &elements) +QQmlJSCompilePass::BasicBlocks::iterator QQmlJSBasicBlocks::basicBlockForInstruction( + QFlatMap<int, BasicBlock> &container, int instructionOffset) { - for (const auto &element : elements) { - if (container.contains(element)) - return true; - } - return false; + auto block = container.lower_bound(instructionOffset); + if (block == container.end() || block->first != instructionOffset) + --block; + return block; } -template<typename ContainerA, typename ContainerB> -static bool containsAll(const ContainerA &container, const ContainerB &elements) +QQmlJSCompilePass::BasicBlocks::const_iterator QQmlJSBasicBlocks::basicBlockForInstruction( + const BasicBlocks &container, int instructionOffset) { - for (const auto &element : elements) { - if (!container.contains(element)) - return false; - } - return true; + return basicBlockForInstruction(const_cast<BasicBlocks &>(container), instructionOffset); } -template<class Key, class T, class Compare = std::less<Key>, - class KeyContainer = QList<Key>, class MappedContainer = QList<T>> -class NewFlatMap +QQmlJSBasicBlocks::BasicBlocksValidationResult QQmlJSBasicBlocks::basicBlocksValidation() { -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; -}; - -struct PendingBlock -{ - QList<int> conversions; - int start = -1; - bool registerActive = false; -}; - -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->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. - 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) { - 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; - if (writtenRegister == InvalidRegister) { - newAnnotations.appendOrdered(writeIt); - continue; - } - - RegisterAccess &access = m_readerLocations[writeIt.key()]; - access.trackedRegister = writtenRegister; - 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<PendingBlock> blocks = { { {}, blockIt->first, true } }; - QHash<int, PendingBlock> processedBlocks; - bool isFirstBlock = true; - - while (!blocks.isEmpty()) { - const PendingBlock block = blocks.takeLast(); - - // 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) - processedBlocks.insert(block.start, block); - - auto nextBlock = m_basicBlocks.find(block.start); - auto currentBlock = nextBlock++; - bool registerActive = block.registerActive; - QList<int> conversions = block.conversions; - - 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) { - if (registerActive - && blockInstr->second.typeConversions.contains(writtenRegister)) { - conversions.append(blockInstr.key()); - } - - for (auto readIt = blockInstr->second.readRegisters.constBegin(), - end = blockInstr->second.readRegisters.constEnd(); - readIt != end; ++readIt) { - if (!blockInstr->second.isRename && containsAny( - readIt->second.content.conversionOrigins(), access.trackedTypes)) { - Q_ASSERT(readIt->second.content.isConversion()); - access.typeReaders[blockInstr.key()] - = readIt->second.content.conversionResult(); - } - if (registerActive && readIt->first == writtenRegister) - access.registerReadersAndConversions[blockInstr.key()] = conversions; - } - - if (blockInstr->second.changedRegisterIndex == writtenRegister) { - conversions.clear(); - registerActive = false; - } - } - - auto scheduleBlock = [&](int blockStart) { - // 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()) - blocks.append({conversions, blockStart, registerActive}); - else if (registerActive && !processed->registerActive) - blocks.append({conversions, blockStart, registerActive}); - else if (!containsAll(processed->conversions, conversions)) - blocks.append({processed->conversions + conversions, blockStart, registerActive}); - }; - - if (!currentBlock->second.jumpIsUnconditional && nextBlock != m_basicBlocks.end()) - scheduleBlock(nextBlock->first); - - const int jumpTarget = currentBlock->second.jumpTarget; - if (jumpTarget != -1) - scheduleBlock(jumpTarget); - - if (isFirstBlock) - isFirstBlock = false; - } - - if (!eraseDeadStore(writeIt)) - newAnnotations.appendOrdered(writeIt); + if (m_basicBlocks.empty()) + return {}; + + const QFlatMap<int, BasicBlock> blocks{ m_basicBlocks }; + QList<QFlatMap<int, BasicBlock>::const_iterator> returnOrThrowBlocks; + for (auto it = blocks.cbegin(); it != blocks.cend(); ++it) { + if (it.value().isReturnBlock || it.value().isThrowBlock) + returnOrThrowBlocks.append(it); } - 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 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); - } - } - - if (!eraseDeadStore(it)) - newAnnotations.appendOrdered(it); - } - m_annotations = newAnnotations.take(); + // 1. Return blocks and throw blocks must not have a jump target + for (const auto &it : returnOrThrowBlocks) { + if (it.value().jumpTarget != -1) + return { false, "Return or throw block jumps to somewhere"_L1 }; } -} - -bool QQmlJSBasicBlocks::canMove(int instructionOffset, const RegisterAccess &access) const -{ - if (access.registerReadersAndConversions.size() != 1) - return false; - const auto basicBlockForInstruction = [this](int instruction) { - auto block = m_basicBlocks.lower_bound(instruction); - if (block == m_basicBlocks.end() || block.key() == instruction) - return block; - Q_ASSERT(block.key() > instruction); - if (block == m_basicBlocks.begin()) - return m_basicBlocks.end(); - return --block; - }; - return basicBlockForInstruction(instructionOffset) - == basicBlockForInstruction(access.registerReadersAndConversions.begin().key()); -} -void QQmlJSBasicBlocks::adjustTypes() -{ - using NewVirtualRegisters = NewFlatMap<int, VirtualRegister>; - - QHash<int, QList<int>> liveConversions; - QHash<int, QList<int>> movableReads; - - const auto handleRegisterReadersAndConversions - = [&](QHash<int, RegisterAccess>::const_iterator it) { - for (auto conversions = it->registerReadersAndConversions.constBegin(), - end = it->registerReadersAndConversions.constEnd(); conversions != end; - ++conversions) { - if (conversions->isEmpty() && canMove(it.key(), it.value())) - movableReads[conversions.key()].append(it->trackedRegister); - for (int conversion : *conversions) - liveConversions[conversion].append(it->trackedRegister); + // 2. The basic blocks graph must be connected. + QSet<int> visitedBlockOffsets; + QList<QFlatMap<int, BasicBlock>::const_iterator> toVisit; + toVisit.append(returnOrThrowBlocks); + + while (!toVisit.empty()) { + const auto &[offset, block] = *toVisit.takeLast(); + visitedBlockOffsets.insert(offset); + for (int originOffset : block.jumpOrigins) { + const auto originBlock = basicBlockForInstruction(blocks, originOffset); + if (visitedBlockOffsets.find(originBlock.key()) == visitedBlockOffsets.end() + && !toVisit.contains(originBlock)) + toVisit.append(originBlock); } - }; - - // Handle the array definitions first. - // Changing the array type changes the expected element types. - for (int instructionOffset : m_arrayDefinitions) { - auto it = m_readerLocations.find(instructionOffset); - if (it == m_readerLocations.end()) - continue; - - const InstructionAnnotation &annotation = m_annotations[instructionOffset]; - - Q_ASSERT(it->trackedTypes.size() == 1); - Q_ASSERT(it->trackedTypes[0] == m_typeResolver->containedType(annotation.changedRegister)); - Q_ASSERT(!annotation.readRegisters.isEmpty()); - - m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()); - - // Now we don't adjust the type we store, but rather the type we expect to read. We - // can do this because we've tracked the read type when we defined the array in - // QQmlJSTypePropagator. - if (QQmlJSScope::ConstPtr valueType = it->trackedTypes[0]->valueType()) { - m_typeResolver->adjustTrackedType( - m_typeResolver->containedType(annotation.readRegisters.begin().value().content), - valueType); - } - - handleRegisterReadersAndConversions(it); - m_readerLocations.erase(it); } - for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) { - handleRegisterReadersAndConversions(it); + if (visitedBlockOffsets.size() != blocks.size()) + return { false, "Basic blocks graph is not fully connected"_L1 }; - // There is always one first occurrence of any tracked type. Conversions don't change - // the type. - if (it->trackedTypes.size() != 1) - continue; - - m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()); + // 3. A block's jump target must be the first offset of a block. + for (const auto &[blockOffset, block] : blocks) { + auto target = block.jumpTarget; + if (target != -1 && blocks.find(target) == blocks.end()) + return { false, "Invalid jump; target is not the start of a block"_L1 }; } - 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 conversion = i->second.typeConversions.begin(), - conversionEnd = i->second.typeConversions.end(); conversion != conversionEnd; - ++conversion) { - if (!liveConversions[i.key()].contains(conversion.key())) - continue; - - QQmlJSScope::ConstPtr conversionResult = conversion->second.content.conversionResult(); - const auto conversionOrigins = conversion->second.content.conversionOrigins(); - QQmlJSScope::ConstPtr newResult; - for (const auto &origin : conversionOrigins) - newResult = m_typeResolver->merge(newResult, origin); - m_typeResolver->adjustTrackedType(conversionResult, newResult); - transformRegister(conversion->second.content); - newRegisters.appendOrdered(conversion); - } - i->second.typeConversions = newRegisters.take(); - - for (int movable : std::as_const(movableReads[i.key()])) - i->second.readRegisters[movable].canMove = true; - } -} - -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> writtenTypes; - QList<int> 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) { - if (!instruction.isRename) { - Q_ASSERT(it->second.content.isConversion()); - for (const QQmlJSScope::ConstPtr &origin : - it->second.content.conversionOrigins()) { - if (!writtenTypes.contains(origin)) - block.readTypes.append(origin); - } - } - if (!writtenRegisters.contains(it->first)) - block.readRegisters.append(it->first); - } - - // If it's just a renaming, the type has existed in a different register before. - if (instruction.changedRegisterIndex != InvalidRegister) { - if (!instruction.isRename) { - writtenTypes.append(m_typeResolver->trackedContainedType( - instruction.changedRegister)); - } - writtenRegisters.append(instruction.changedRegisterIndex); - } - } - - deduplicate(block.readTypes); - deduplicate(block.readRegisters); - } + return {}; } QT_END_NAMESPACE |