Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qqmljsbasicblocks.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
5
6#include <QtQml/private/qv4instr_moth_p.h>
7
9
10using namespace Qt::Literals::StringLiterals;
11
12DEFINE_BOOL_CONFIG_OPTION(qv4DumpBasicBlocks, QV4_DUMP_BASIC_BLOCKS)
13
14void QQmlJSBasicBlocks::dumpBasicBlocks()
15{
16 qDebug().noquote() << "=== Basic Blocks for \"%1\""_L1.arg(m_context->name);
17 for (const auto &[blockOffset, block] : m_basicBlocks) {
18 QDebug debug = qDebug().nospace();
19 debug << "Block " << blockOffset << ":\n";
20 debug << " jumpOrigins[" << block.jumpOrigins.size() << "]: ";
21 for (auto origin : block.jumpOrigins) {
22 debug << origin << ", ";
23 }
24 debug << "\n readRegisters[" << block.readRegisters.size() << "]: ";
25 for (auto reg : block.readRegisters) {
26 debug << reg << ", ";
27 }
28 debug << "\n readTypes[" << block.readTypes.size() << "]: ";
29 for (auto type : block.readTypes) {
30 debug << type->augmentedInternalName() << ", ";
31 }
32 debug << "\n jumpTarget: " << block.jumpTarget;
33 debug << "\n jumpIsUnConditional: " << block.jumpIsUnconditional;
34 }
35 qDebug() << "\n";
36}
37
38void QQmlJSBasicBlocks::dumpDOTGraph()
39{
40 auto isBackEdge = [](const BasicBlock &originBlock, int originOffset, int destinationOffset) {
41 return originOffset > destinationOffset && originBlock.jumpIsUnconditional;
42 };
43
46 s << "=== Basic Blocks Graph in DOT format for \"%1\" (spaces are encoded as"
47 " &#160; to preserve formatting)\n"_L1.arg(m_context->name);
48 s << "digraph BasicBlocks {\n"_L1;
49
50 std::map<int, BasicBlock> blocks{ m_basicBlocks.begin(), m_basicBlocks.end() };
51 for (const auto &[blockOffset, block] : blocks) {
52 for (int originOffset : block.jumpOrigins) {
53 int originBlockOffset;
54 auto originBlockIt = blocks.find(originOffset);
55 if (originBlockIt != blocks.end())
56 originBlockOffset = originOffset;
57 else
58 originBlockOffset = std::prev(blocks.lower_bound(originOffset))->first;
59 s << " %1 -> %2%3\n"_L1.arg(QString::number(originBlockOffset))
60 .arg(QString::number(blockOffset))
61 .arg(isBackEdge(originBlockIt->second, originOffset, blockOffset)
62 ? " [color=blue]"_L1
63 : ""_L1);
64 }
65 }
66
67 for (const auto &[blockOffset, block] : blocks) {
68 int beginOffset = std::max(0, blockOffset);
69 auto nextBlockIt = blocks.lower_bound(blockOffset + 1);
70 int nextBlockOffset = nextBlockIt == blocks.end() ? m_context->code.size() : nextBlockIt->first;
72 m_context->code.constData(), m_context->code.size(), m_context->locals.size(),
73 m_context->formals->length(), beginOffset, nextBlockOffset - 1,
75 dump = dump.replace(" "_L1, "&#160;"_L1); // prevent collapse of extra whitespace for formatting
76 dump = dump.replace("\n"_L1, "\\l"_L1); // new line + left aligned
77 s << " %1 [shape=record, fontname=\"Monospace\", label=\"{Block %1: | %2}\"]\n"_L1
78 .arg(QString::number(blockOffset))
79 .arg(dump);
80 }
81 s << "}\n"_L1;
82
83 // Have unique names to prevent overwriting of functions with the same name (eg. anonymous functions).
84 static int functionCount = 0;
85 static const auto dumpFolderPath = qEnvironmentVariable("QV4_DUMP_BASIC_BLOCKS");
86
87 QString expressionName = m_context->name == ""_L1
88 ? "anonymous"_L1
89 : QString(m_context->name).replace(" "_L1, "_"_L1);
90 QString fileName = "function"_L1 + QString::number(functionCount++) + "_"_L1 + expressionName + ".gv"_L1;
91 QFile dumpFile(dumpFolderPath + (dumpFolderPath.endsWith("/"_L1) ? ""_L1 : "/"_L1) + fileName);
92
93 if (dumpFolderPath == "-"_L1 || dumpFolderPath == "1"_L1 || dumpFolderPath == "true"_L1) {
94 qDebug().noquote() << output;
95 } else {
96 if (!dumpFile.open(QIODeviceBase::Truncate | QIODevice::WriteOnly)) {
97 qDebug() << "Error: Could not open file to dump the basic blocks into";
98 } else {
99 dumpFile.write(("//"_L1 + output).toLatin1().toStdString().c_str());
100 dumpFile.close();
101 }
102 }
103}
104
105template<typename Container>
106void deduplicate(Container &container)
107{
108 std::sort(container.begin(), container.end());
109 auto erase = std::unique(container.begin(), container.end());
110 container.erase(erase, container.end());
111}
112
114 const Function *function,
115 const InstructionAnnotations &annotations,
117{
119 m_annotations = annotations;
120 m_error = error;
121
122 for (int i = 0, end = function->argumentTypes.size(); i != end; ++i) {
123 InstructionAnnotation annotation;
124 annotation.changedRegisterIndex = FirstArgument + i;
125 annotation.changedRegister = function->argumentTypes[i];
126 m_annotations[-annotation.changedRegisterIndex] = annotation;
127 }
128
129 for (int i = 0, end = function->registerTypes.size(); i != end; ++i) {
130 InstructionAnnotation annotation;
132 annotation.changedRegister = function->registerTypes[i];
133 m_annotations[-annotation.changedRegisterIndex] = annotation;
134 }
135
136 m_basicBlocks.insert_or_assign(m_annotations.begin().key(), BasicBlock());
137
138 const QByteArray byteCode = function->code;
139 decode(byteCode.constData(), static_cast<uint>(byteCode.size()));
140 if (m_hadBackJumps) {
141 // We may have missed some connections between basic blocks if there were back jumps.
142 // Fill them in via a second pass.
143
144 // We also need to re-calculate the jump targets then because the basic block boundaries
145 // may have shifted.
146 for (auto it = m_basicBlocks.begin(), end = m_basicBlocks.end(); it != end; ++it) {
147 it->second.jumpTarget = -1;
148 it->second.jumpIsUnconditional = false;
149 }
150
151 m_skipUntilNextLabel = false;
152
153 reset();
154 decode(byteCode.constData(), static_cast<uint>(byteCode.size()));
155 for (auto it = m_basicBlocks.begin(), end = m_basicBlocks.end(); it != end; ++it)
156 deduplicate(it->second.jumpOrigins);
157 }
158
159 populateBasicBlocks();
160 populateReaderLocations();
161 adjustTypes();
162
163 if (qv4DumpBasicBlocks()) {
164 dumpBasicBlocks();
165 dumpDOTGraph();
166 }
167
168 return std::move(m_annotations);
169}
170
172{
173 auto it = m_basicBlocks.find(currentInstructionOffset());
174 if (it != m_basicBlocks.end()) {
175 m_skipUntilNextLabel = false;
176 } else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) {
177 return SkipInstruction;
178 }
179
180 return ProcessInstruction;
181}
182
184{
185 if (m_skipUntilNextLabel)
186 return;
187 auto it = m_basicBlocks.find(nextInstructionOffset());
188 if (it != m_basicBlocks.end())
189 it->second.jumpOrigins.append(currentInstructionOffset());
190}
191
193{
194 processJump(offset, Unconditional);
195}
196
198{
199 processJump(offset, Conditional);
200}
201
203{
204 processJump(offset, Conditional);
205}
206
208{
209 processJump(offset, Conditional);
210}
211
213{
214 processJump(offset, Conditional);
215}
216
218{
219 m_skipUntilNextLabel = true;
220}
221
223{
224 m_skipUntilNextLabel = true;
225}
226
228{
229 if (argc == 0)
230 return; // empty array/list, nothing to do
231
232 m_objectAndArrayDefinitions.append({
233 currentInstructionOffset(), ObjectOrArrayDefinition::arrayClassId, argc, argv
234 });
235}
236
237void QQmlJSBasicBlocks::generate_DefineObjectLiteral(int internalClassId, int argc, int argv)
238{
239 if (argc == 0)
240 return;
241
242 m_objectAndArrayDefinitions.append({ currentInstructionOffset(), internalClassId, argc, argv });
243}
244
245void QQmlJSBasicBlocks::processJump(int offset, JumpMode mode)
246{
247 if (offset < 0)
248 m_hadBackJumps = true;
249 const int jumpTarget = absoluteOffset(offset);
250 Q_ASSERT(!m_basicBlocks.isEmpty());
251 auto currentBlock = m_basicBlocks.lower_bound(currentInstructionOffset());
252 if (currentBlock == m_basicBlocks.end() || currentBlock->first != currentInstructionOffset())
253 --currentBlock;
254 currentBlock->second.jumpTarget = jumpTarget;
255 currentBlock->second.jumpIsUnconditional = (mode == Unconditional);
256 m_basicBlocks[jumpTarget].jumpOrigins.append(currentInstructionOffset());
257 if (mode == Unconditional)
258 m_skipUntilNextLabel = true;
259 else
260 m_basicBlocks.insert(nextInstructionOffset(), BasicBlock());
261}
262
263template<typename ContainerA, typename ContainerB>
264static bool containsAny(const ContainerA &container, const ContainerB &elements)
265{
266 for (const auto &element : elements) {
267 if (container.contains(element))
268 return true;
269 }
270 return false;
271}
272
273template<typename ContainerA, typename ContainerB>
274static bool containsAll(const ContainerA &container, const ContainerB &elements)
275{
276 for (const auto &element : elements) {
277 if (!container.contains(element))
278 return false;
279 }
280 return true;
281}
282
283template<class Key, class T, class Compare = std::less<Key>,
284 class KeyContainer = QList<Key>, class MappedContainer = QList<T>>
286{
287public:
289
291 keys.append(i.key());
292 values.append(i.value());
293 }
294
296 OriginalFlatMap result(Qt::OrderedUniqueRange, std::move(keys), std::move(values));
297 keys.clear();
298 values.clear();
299 return result;
300 }
301
302private:
305};
306
308{
310 int start = -1;
311 bool registerActive = false;
312};
313
314void QQmlJSBasicBlocks::populateReaderLocations()
315{
316 using NewInstructionAnnotations = NewFlatMap<int, InstructionAnnotation>;
317
318 bool erasedReaders = false;
319 auto eraseDeadStore = [&](const InstructionAnnotations::iterator &it) {
320 auto reader = m_readerLocations.find(it.key());
321 if (reader != m_readerLocations.end()
322 && (reader->typeReaders.isEmpty()
323 || reader->registerReadersAndConversions.isEmpty())) {
324
325 if (it->second.isRename) {
326 // If it's a rename, it doesn't "own" its output type. The type may
327 // still be read elsewhere, even if this register isn't. However, we're
328 // not interested in the variant or any other details of the register.
329 // Therefore just delete it.
330 it->second.changedRegisterIndex = InvalidRegister;
331 it->second.changedRegister = QQmlJSRegisterContent();
332 } else {
333 // void the output, rather than deleting it. We still need its variant.
334 bool adjusted = m_typeResolver->adjustTrackedType(
335 it->second.changedRegister.storedType(),
337 Q_ASSERT(adjusted); // Can always convert to void
338
340 m_typeResolver->containedType(it->second.changedRegister),
342 Q_ASSERT(adjusted); // Can always convert to void
343 }
344 m_readerLocations.erase(reader);
345
346 // If it's not a label and has no side effects, we can drop the instruction.
347 if (!it->second.hasSideEffects) {
348 if (!it->second.readRegisters.isEmpty()) {
349 it->second.readRegisters.clear();
350 erasedReaders = true;
351 }
352 if (m_basicBlocks.find(it.key()) == m_basicBlocks.end())
353 return true;
354 }
355 }
356 return false;
357 };
358
359 NewInstructionAnnotations newAnnotations;
360 for (auto writeIt = m_annotations.begin(), writeEnd = m_annotations.end();
361 writeIt != writeEnd; ++writeIt) {
362 const int writtenRegister = writeIt->second.changedRegisterIndex;
363 if (writtenRegister == InvalidRegister) {
364 newAnnotations.appendOrdered(writeIt);
365 continue;
366 }
367
368 RegisterAccess &access = m_readerLocations[writeIt.key()];
369 access.trackedRegister = writtenRegister;
370 if (writeIt->second.changedRegister.isConversion()) {
371 // If it's a conversion, we have to check for all readers of the conversion origins.
372 // This happens at jump targets where different types are merged. A StoreReg or similar
373 // instruction must be optimized out if none of the types it can hold is read anymore.
374 access.trackedTypes = writeIt->second.changedRegister.conversionOrigins();
375 } else {
376 access.trackedTypes.append(
377 m_typeResolver->trackedContainedType(writeIt->second.changedRegister));
378 }
379
380 auto blockIt = m_basicBlocks.lower_bound(writeIt.key());
381 if (blockIt == m_basicBlocks.end() || blockIt->first != writeIt.key())
382 --blockIt;
383
384 QList<PendingBlock> blocks = { { {}, blockIt->first, true } };
385 QHash<int, PendingBlock> processedBlocks;
386 bool isFirstBlock = true;
387
388 while (!blocks.isEmpty()) {
389 const PendingBlock block = blocks.takeLast();
390
391 // We can re-enter the first block from the beginning.
392 // We will then find any reads before the write we're currently examining.
393 if (!isFirstBlock)
394 processedBlocks.insert(block.start, block);
395
396 auto nextBlock = m_basicBlocks.find(block.start);
397 auto currentBlock = nextBlock++;
398 bool registerActive = block.registerActive;
399 QList<int> conversions = block.conversions;
400
401 const auto blockEnd = (nextBlock == m_basicBlocks.end())
402 ? m_annotations.end()
403 : m_annotations.find(nextBlock->first);
404
405 auto blockInstr = isFirstBlock
406 ? (writeIt + 1)
407 : m_annotations.find(currentBlock->first);
408 for (; blockInstr != blockEnd; ++blockInstr) {
409 if (registerActive
410 && blockInstr->second.typeConversions.contains(writtenRegister)) {
411 conversions.append(blockInstr.key());
412 }
413
414 for (auto readIt = blockInstr->second.readRegisters.constBegin(),
415 end = blockInstr->second.readRegisters.constEnd();
416 readIt != end; ++readIt) {
417 if (!blockInstr->second.isRename && containsAny(
418 readIt->second.content.conversionOrigins(), access.trackedTypes)) {
419 Q_ASSERT(readIt->second.content.isConversion());
420 Q_ASSERT(readIt->second.content.conversionResult());
421 access.typeReaders[blockInstr.key()]
422 = readIt->second.content.conversionResult();
423 }
424 if (registerActive && readIt->first == writtenRegister)
425 access.registerReadersAndConversions[blockInstr.key()] = conversions;
426 }
427
428 if (blockInstr->second.changedRegisterIndex == writtenRegister) {
429 conversions.clear();
430 registerActive = false;
431 }
432 }
433
434 auto scheduleBlock = [&](int blockStart) {
435 // If we find that an already processed block has the register activated by this jump,
436 // we need to re-evaluate it. We also need to propagate any newly found conversions.
437 const auto processed = processedBlocks.find(blockStart);
438 if (processed == processedBlocks.end())
439 blocks.append({conversions, blockStart, registerActive});
440 else if (registerActive && !processed->registerActive)
441 blocks.append({conversions, blockStart, registerActive});
442 else if (!containsAll(processed->conversions, conversions))
443 blocks.append({processed->conversions + conversions, blockStart, registerActive});
444 };
445
446 if (!currentBlock->second.jumpIsUnconditional && nextBlock != m_basicBlocks.end())
447 scheduleBlock(nextBlock->first);
448
449 const int jumpTarget = currentBlock->second.jumpTarget;
450 if (jumpTarget != -1)
451 scheduleBlock(jumpTarget);
452
453 if (isFirstBlock)
454 isFirstBlock = false;
455 }
456
457 if (!eraseDeadStore(writeIt))
458 newAnnotations.appendOrdered(writeIt);
459 }
460 m_annotations = newAnnotations.take();
461
462 while (erasedReaders) {
463 erasedReaders = false;
464
465 for (auto it = m_annotations.begin(), end = m_annotations.end(); it != end; ++it) {
466 InstructionAnnotation &instruction = it->second;
467 if (instruction.changedRegisterIndex < InvalidRegister) {
468 newAnnotations.appendOrdered(it);
469 continue;
470 }
471
472 auto readers = m_readerLocations.find(it.key());
473 if (readers != m_readerLocations.end()) {
474 for (auto typeIt = readers->typeReaders.begin();
475 typeIt != readers->typeReaders.end();) {
476 if (m_annotations.contains(typeIt.key()))
477 ++typeIt;
478 else
479 typeIt = readers->typeReaders.erase(typeIt);
480 }
481
482 for (auto registerIt = readers->registerReadersAndConversions.begin();
483 registerIt != readers->registerReadersAndConversions.end();) {
484 if (m_annotations.contains(registerIt.key()))
485 ++registerIt;
486 else
487 registerIt = readers->registerReadersAndConversions.erase(registerIt);
488 }
489 }
490
491 if (!eraseDeadStore(it))
492 newAnnotations.appendOrdered(it);
493 }
494
495 m_annotations = newAnnotations.take();
496 }
497}
498
499bool QQmlJSBasicBlocks::canMove(int instructionOffset, const RegisterAccess &access) const
500{
501 if (access.registerReadersAndConversions.size() != 1)
502 return false;
503 const auto basicBlockForInstruction = [this](int instruction) {
504 auto block = m_basicBlocks.lower_bound(instruction);
505 if (block == m_basicBlocks.end() || block.key() == instruction)
506 return block;
507 Q_ASSERT(block.key() > instruction);
508 if (block == m_basicBlocks.begin())
509 return m_basicBlocks.end();
510 return --block;
511 };
512 return basicBlockForInstruction(instructionOffset)
513 == basicBlockForInstruction(access.registerReadersAndConversions.begin().key());
514}
515
517 const QQmlJSScope::ConstPtr &origin, const QQmlJSScope::ConstPtr &conversion) {
518 return QLatin1String("Cannot convert from ")
519 + origin->internalName() + QLatin1String(" to ") + conversion->internalName();
520}
521
523 const QQmlJSScope::ConstPtr &origin, const QList<QQmlJSScope::ConstPtr> &conversions) {
524 if (conversions.size() == 1)
525 return adjustErrorMessage(origin, conversions[0]);
526
528 for (const QQmlJSScope::ConstPtr &type : conversions) {
529 if (!types.isEmpty())
530 types += QLatin1String(", ");
531 types += type->internalName();
532 }
533 return QLatin1String("Cannot convert from ")
534 + origin->internalName() + QLatin1String(" to union of ") + types;
535}
536
537void QQmlJSBasicBlocks::adjustTypes()
538{
539 using NewVirtualRegisters = NewFlatMap<int, VirtualRegister>;
540
541 QHash<int, QList<int>> liveConversions;
542 QHash<int, QList<int>> movableReads;
543
544 const auto handleRegisterReadersAndConversions
546 for (auto conversions = it->registerReadersAndConversions.constBegin(),
547 end = it->registerReadersAndConversions.constEnd(); conversions != end;
548 ++conversions) {
549 if (conversions->isEmpty() && canMove(it.key(), it.value()))
550 movableReads[conversions.key()].append(it->trackedRegister);
551 for (int conversion : *conversions)
552 liveConversions[conversion].append(it->trackedRegister);
553 }
554 };
555
556 const auto transformRegister = [&](const QQmlJSRegisterContent &content) {
557 const QQmlJSScope::ConstPtr conversion
559 if (!m_typeResolver->adjustTrackedType(content.storedType(), conversion))
560 setError(adjustErrorMessage(content.storedType(), conversion));
561 };
562
563 // Handle the array definitions first.
564 // Changing the array type changes the expected element types.
565 auto adjustArray = [&](int instructionOffset) {
566 auto it = m_readerLocations.find(instructionOffset);
567 if (it == m_readerLocations.end())
568 return;
569
570 const InstructionAnnotation &annotation = m_annotations[instructionOffset];
571
572 Q_ASSERT(it->trackedTypes.size() == 1);
573 Q_ASSERT(it->trackedTypes[0] == m_typeResolver->containedType(annotation.changedRegister));
574 Q_ASSERT(!annotation.readRegisters.isEmpty());
575
576 if (!m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()))
577 setError(adjustErrorMessage(it->trackedTypes[0], it->typeReaders.values()));
578
579 // Now we don't adjust the type we store, but rather the type we expect to read. We
580 // can do this because we've tracked the read type when we defined the array in
581 // QQmlJSTypePropagator.
582 if (QQmlJSScope::ConstPtr valueType = it->trackedTypes[0]->valueType()) {
583 const QQmlJSRegisterContent content = annotation.readRegisters.begin().value().content;
584 const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
585 if (!m_typeResolver->adjustTrackedType(contained, valueType))
586 setError(adjustErrorMessage(contained, valueType));
587
588 // We still need to adjust the stored type, too.
589 transformRegister(content);
590 }
591
592 handleRegisterReadersAndConversions(it);
593 m_readerLocations.erase(it);
594 };
595
596 // Handle the object definitions.
597 // Changing the object type changes the expected property types.
598 const auto adjustObject = [&](const ObjectOrArrayDefinition &object) {
599 auto it = m_readerLocations.find(object.instructionOffset);
600 if (it == m_readerLocations.end())
601 return;
602
603 const InstructionAnnotation &annotation = m_annotations[object.instructionOffset];
604
605 Q_ASSERT(it->trackedTypes.size() == 1);
606 Q_ASSERT(it->trackedTypes[0] == m_typeResolver->containedType(annotation.changedRegister));
607 Q_ASSERT(!annotation.readRegisters.isEmpty());
608
609 if (!m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()))
610 setError(adjustErrorMessage(it->trackedTypes[0], it->typeReaders.values()));
611
612 QQmlJSScope::ConstPtr resultType = it->trackedTypes[0];
613
614 const int classSize = m_jsUnitGenerator->jsClassSize(object.internalClassId);
615 Q_ASSERT(object.argc >= classSize);
616
617 for (int i = 0; i < classSize; ++i) {
618 // Now we don't adjust the type we store, but rather the types we expect to read. We
619 // can do this because we've tracked the read types when we defined the object in
620 // QQmlJSTypePropagator.
621
622 const QString propName = m_jsUnitGenerator->jsClassMember(object.internalClassId, i);
623 const QQmlJSMetaProperty property = resultType->property(propName);
624 if (!property.isValid()) {
625 setError(
626 resultType->internalName()
627 + QLatin1String(" has no property called ")
628 + propName);
629 continue;
630 }
631 const QQmlJSScope::ConstPtr propType = property.type();
632 if (propType.isNull()) {
633 setError(QLatin1String("Cannot resolve type of property ") + propName);
634 continue;
635 }
636 const QQmlJSRegisterContent content = annotation.readRegisters[object.argv + i].content;
637 const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
638 if (!m_typeResolver->adjustTrackedType(contained, propType))
639 setError(adjustErrorMessage(contained, propType));
640
641 // We still need to adjust the stored type, too.
642 transformRegister(content);
643 }
644
645 // The others cannot be adjusted. We don't know their names, yet.
646 // But we might still be able to use the variants.
647 };
648
649 // Iterate in reverse so that we can have nested lists and objects and the types are propagated
650 // from the outer lists/objects to the inner ones.
651 for (auto it = m_objectAndArrayDefinitions.crbegin(), end = m_objectAndArrayDefinitions.crend();
652 it != end; ++it) {
653 if (it->internalClassId != -1)
654 adjustObject(*it);
655 else
656 adjustArray(it->instructionOffset);
657 }
658
659 for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) {
660 handleRegisterReadersAndConversions(it);
661
662 // There is always one first occurrence of any tracked type. Conversions don't change
663 // the type.
664 if (it->trackedTypes.size() != 1)
665 continue;
666
667 if (!m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()))
668 setError(adjustErrorMessage(it->trackedTypes[0], it->typeReaders.values()));
669 }
670
671
672
673 NewVirtualRegisters newRegisters;
674 for (auto i = m_annotations.begin(), iEnd = m_annotations.end(); i != iEnd; ++i) {
675 if (i->second.changedRegisterIndex != InvalidRegister)
676 transformRegister(i->second.changedRegister);
677
678 for (auto conversion = i->second.typeConversions.begin(),
679 conversionEnd = i->second.typeConversions.end(); conversion != conversionEnd;
680 ++conversion) {
681 if (!liveConversions[i.key()].contains(conversion.key()))
682 continue;
683
684 QQmlJSScope::ConstPtr conversionResult = conversion->second.content.conversionResult();
685 const auto conversionOrigins = conversion->second.content.conversionOrigins();
686 QQmlJSScope::ConstPtr newResult;
687 for (const auto &origin : conversionOrigins)
688 newResult = m_typeResolver->merge(newResult, origin);
689 if (!m_typeResolver->adjustTrackedType(conversionResult, newResult))
690 setError(adjustErrorMessage(conversionResult, newResult));
691 transformRegister(conversion->second.content);
692 newRegisters.appendOrdered(conversion);
693 }
694 i->second.typeConversions = newRegisters.take();
695
696 for (int movable : std::as_const(movableReads[i.key()]))
697 i->second.readRegisters[movable].canMove = true;
698 }
699}
700
701void QQmlJSBasicBlocks::populateBasicBlocks()
702{
703 for (auto blockNext = m_basicBlocks.begin(), blockEnd = m_basicBlocks.end();
704 blockNext != blockEnd;) {
705
706 const auto blockIt = blockNext++;
707 BasicBlock &block = blockIt->second;
708 QList<QQmlJSScope::ConstPtr> writtenTypes;
709 QList<int> writtenRegisters;
710
711 const auto instrEnd = (blockNext == blockEnd)
712 ? m_annotations.end()
713 : m_annotations.find(blockNext->first);
714 for (auto instrIt = m_annotations.find(blockIt->first); instrIt != instrEnd; ++instrIt) {
715 const InstructionAnnotation &instruction = instrIt->second;
716 for (auto it = instruction.readRegisters.begin(), end = instruction.readRegisters.end();
717 it != end; ++it) {
718 if (!instruction.isRename) {
719 Q_ASSERT(it->second.content.isConversion());
720 for (const QQmlJSScope::ConstPtr &origin :
721 it->second.content.conversionOrigins()) {
722 if (!writtenTypes.contains(origin))
723 block.readTypes.append(origin);
724 }
725 }
726 if (!writtenRegisters.contains(it->first))
727 block.readRegisters.append(it->first);
728 }
729
730 // If it's just a renaming, the type has existed in a different register before.
731 if (instruction.changedRegisterIndex != InvalidRegister) {
732 if (!instruction.isRename) {
734 instruction.changedRegister));
735 }
736 writtenRegisters.append(instruction.changedRegisterIndex);
737 }
738 }
739
740 deduplicate(block.readTypes);
741 deduplicate(block.readRegisters);
742 }
743}
744
void appendOrdered(const typename OriginalFlatMap::iterator &i)
OriginalFlatMap take()
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
\inmodule QtCore
\inmodule QtCore
Definition qfile.h:93
const Key & key() const
Definition qflatmap_p.h:254
iterator begin()
Definition qflatmap_p.h:769
iterator end()
Definition qflatmap_p.h:773
iterator find(const Key &key)
Definition qflatmap_p.h:816
KeyContainer key_container_type
Definition qflatmap_p.h:120
MappedContainer mapped_container_type
Definition qflatmap_p.h:121
\inmodule QtCore
Definition qhash.h:1135
\inmodule QtCore
Definition qhash.h:818
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1202
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1258
iterator erase(const_iterator it)
Definition qhash.h:1223
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
T & first()
Definition qlist.h:628
const_reverse_iterator crbegin() const noexcept
Definition qlist.h:621
value_type takeLast()
Definition qlist.h:550
void append(parameter_type t)
Definition qlist.h:441
void clear()
Definition qlist.h:417
const_reverse_iterator crend() const noexcept
Definition qlist.h:622
void endInstruction(QV4::Moth::Instr::Type type) override
void generate_JumpNoException(int offset) override
QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type type) override
void generate_DefineObjectLiteral(int internalClassId, int argc, int args) override
void generate_Ret() override
void generate_JumpNotUndefined(int offset) override
void generate_Jump(int offset) override
void generate_JumpFalse(int offset) override
void generate_ThrowException() override
void generate_DefineArray(int argc, int argv) override
void generate_JumpTrue(int offset) override
InstructionAnnotations run(const Function *function, const InstructionAnnotations &annotations, QQmlJS::DiagnosticMessage *error)
const QV4::Compiler::JSUnitGenerator * m_jsUnitGenerator
static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
const Function * m_function
const QQmlJSTypeResolver * m_typeResolver
QQmlJS::DiagnosticMessage * m_error
void setError(const QString &message, int instructionOffset)
QString internalName() const
QQmlJSMetaProperty property(const QString &name) const
QQmlJSScope::ConstPtr trackedContainedType(const QQmlJSRegisterContent &container) const
QQmlJSScope::ConstPtr storedType(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr containedType(const QQmlJSRegisterContent &container) const
bool adjustTrackedType(const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const
QQmlJSScope::ConstPtr voidType() const
qsizetype size() const
Definition qset.h:50
QList< T > values() const
Definition qset.h:297
iterator begin()
Definition qset.h:136
bool isEmpty() const
Definition qset.h:52
const_iterator constBegin() const noexcept
Definition qset.h:139
const_iterator constEnd() const noexcept
Definition qset.h:143
void clear()
Definition qset.h:61
iterator find(const T &value)
Definition qset.h:159
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
\inmodule QtCore
int absoluteOffset(int relativeOffset) const
list append(new Employee("Blackpool", "Stephen"))
QSet< QString >::iterator it
Combined button and popup list for selecting options.
QString dumpBytecode(const char *code, int len, int nLocals, int nFormals, int, const QVector< CompiledData::CodeOffsetToLineAndStatement > &lineAndStatementNumberMapping)
constexpr OrderedUniqueRange_t OrderedUniqueRange
Definition qflatmap_p.h:61
qsizetype erase(QByteArray &ba, const T &t)
Definition qbytearray.h:695
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError * error
#define qDebug
[1]
Definition qlogging.h:160
GLenum GLsizei GLsizei GLint * values
[15]
GLenum mode
GLuint64 key
GLuint GLuint end
GLsizei GLenum GLenum * types
GLuint object
[3]
GLenum type
GLenum access
GLuint start
GLenum GLuint GLintptr offset
GLint first
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
#define DEFINE_BOOL_CONFIG_OPTION(name, var)
static bool containsAny(const ContainerA &container, const ContainerB &elements)
static bool containsAll(const ContainerA &container, const ContainerB &elements)
static QString adjustErrorMessage(const QQmlJSScope::ConstPtr &origin, const QQmlJSScope::ConstPtr &conversion)
void deduplicate(Container &container)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static QString dump(const QByteArray &)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
static const QTextHtmlElement elements[Html_NumElements]
unsigned int uint
Definition qtypes.h:29
#define decode(x)
QT_BEGIN_NAMESPACE typedef uchar * output
const char property[13]
Definition qwizard.cpp:101
QList< int > conversions
bool contains(const AT &t) const noexcept
Definition qlist.h:44
QQmlJS::AST::FormalParameterList * formals
QVector< CompiledData::CodeOffsetToLineAndStatement > lineAndStatementNumberMapping
QString jsClassMember(int jsClassId, int member) const
int jsClassSize(int jsClassId) const