6#include <QtQml/private/qv4instr_moth_p.h>
14void QQmlJSBasicBlocks::dumpBasicBlocks()
16 qDebug().noquote() <<
"=== Basic Blocks for \"%1\""_L1.arg(m_context->
name);
17 for (
const auto &[blockOffset, block] : m_basicBlocks) {
19 debug <<
"Block " << blockOffset <<
":\n";
20 debug <<
" jumpOrigins[" << block.jumpOrigins.size() <<
"]: ";
21 for (
auto origin : block.jumpOrigins) {
22 debug << origin <<
", ";
24 debug <<
"\n readRegisters[" << block.readRegisters.size() <<
"]: ";
25 for (
auto reg : block.readRegisters) {
28 debug <<
"\n readTypes[" << block.readTypes.size() <<
"]: ";
29 for (
auto type : block.readTypes) {
30 debug <<
type->augmentedInternalName() <<
", ";
32 debug <<
"\n jumpTarget: " << block.jumpTarget;
33 debug <<
"\n jumpIsUnConditional: " << block.jumpIsUnconditional;
38void QQmlJSBasicBlocks::dumpDOTGraph()
40 auto isBackEdge = [](
const BasicBlock &originBlock,
int originOffset,
int destinationOffset) {
41 return originOffset > destinationOffset && originBlock.jumpIsUnconditional;
46 s <<
"=== Basic Blocks Graph in DOT format for \"%1\" (spaces are encoded as"
47 "   to preserve formatting)\n"_L1.arg(m_context->
name);
48 s <<
"digraph BasicBlocks {\n"_L1;
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;
58 originBlockOffset = std::prev(blocks.lower_bound(originOffset))->first;
61 .arg(isBackEdge(originBlockIt->second, originOffset, blockOffset)
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;
73 m_context->
formals->
length(), beginOffset, nextBlockOffset - 1,
77 s <<
" %1 [shape=record, fontname=\"Monospace\", label=\"{Block %1: | %2}\"]\n"_L1
84 static int functionCount = 0;
91 QFile dumpFile(dumpFolderPath + (dumpFolderPath.endsWith(
"/"_L1) ?
""_L1 :
"/"_L1) +
fileName);
93 if (dumpFolderPath ==
"-"_L1 || dumpFolderPath ==
"1"_L1 || dumpFolderPath ==
"true"_L1) {
97 qDebug() <<
"Error: Could not open file to dump the basic blocks into";
99 dumpFile.write((
"//"_L1 +
output).toLatin1().toStdString().c_str());
105template<
typename Container>
108 std::sort(container.begin(), container.end());
109 auto erase = std::unique(container.begin(), container.end());
110 container.erase(
erase, container.end());
119 m_annotations = annotations;
140 if (m_hadBackJumps) {
146 for (
auto it = m_basicBlocks.begin(),
end = m_basicBlocks.end();
it !=
end; ++
it) {
147 it->second.jumpTarget = -1;
148 it->second.jumpIsUnconditional =
false;
151 m_skipUntilNextLabel =
false;
155 for (
auto it = m_basicBlocks.begin(),
end = m_basicBlocks.end();
it !=
end; ++
it)
159 populateBasicBlocks();
160 populateReaderLocations();
163 if (qv4DumpBasicBlocks()) {
168 return std::move(m_annotations);
174 if (
it != m_basicBlocks.end()) {
175 m_skipUntilNextLabel =
false;
185 if (m_skipUntilNextLabel)
188 if (
it != m_basicBlocks.end())
194 processJump(
offset, Unconditional);
199 processJump(
offset, Conditional);
204 processJump(
offset, Conditional);
209 processJump(
offset, Conditional);
214 processJump(
offset, Conditional);
219 m_skipUntilNextLabel =
true;
224 m_skipUntilNextLabel =
true;
232 m_objectAndArrayDefinitions.
append({
245void QQmlJSBasicBlocks::processJump(
int offset, JumpMode
mode)
248 m_hadBackJumps =
true;
254 currentBlock->second.jumpTarget = jumpTarget;
255 currentBlock->second.jumpIsUnconditional = (
mode == Unconditional);
257 if (
mode == Unconditional)
258 m_skipUntilNextLabel =
true;
263template<
typename ContainerA,
typename ContainerB>
266 for (
const auto &element :
elements) {
267 if (container.contains(element))
273template<
typename ContainerA,
typename ContainerB>
276 for (
const auto &element :
elements) {
277 if (!container.contains(element))
283template<
class Key,
class T,
class Compare = std::less<Key>,
284 class KeyContainer = QList<Key>,
class MappedContainer = QList<T>>
291 keys.append(
i.key());
314void QQmlJSBasicBlocks::populateReaderLocations()
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())) {
325 if (
it->second.isRename) {
335 it->second.changedRegister.storedType(),
344 m_readerLocations.
erase(reader);
347 if (!
it->second.hasSideEffects) {
348 if (!
it->second.readRegisters.
isEmpty()) {
349 it->second.readRegisters.
clear();
350 erasedReaders =
true;
352 if (m_basicBlocks.find(
it.key()) == m_basicBlocks.end())
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;
364 newAnnotations.appendOrdered(writeIt);
368 RegisterAccess &
access = m_readerLocations[writeIt.key()];
369 access.trackedRegister = writtenRegister;
370 if (writeIt->second.changedRegister.isConversion()) {
374 access.trackedTypes = writeIt->second.changedRegister.conversionOrigins();
376 access.trackedTypes.append(
380 auto blockIt = m_basicBlocks.lower_bound(writeIt.key());
381 if (blockIt == m_basicBlocks.end() || blockIt->first != writeIt.key())
386 bool isFirstBlock =
true;
396 auto nextBlock = m_basicBlocks.find(block.
start);
397 auto currentBlock = nextBlock++;
401 const auto blockEnd = (nextBlock == m_basicBlocks.end())
402 ? m_annotations.
end()
403 : m_annotations.
find(nextBlock->first);
405 auto blockInstr = isFirstBlock
407 : m_annotations.find(currentBlock->
first);
408 for (; blockInstr != blockEnd; ++blockInstr) {
410 && blockInstr->second.typeConversions.contains(writtenRegister)) {
411 conversions.
append(blockInstr.key());
414 for (
auto readIt = blockInstr->second.readRegisters.constBegin(),
415 end = blockInstr->second.readRegisters.constEnd();
416 readIt !=
end; ++readIt) {
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();
424 if (registerActive && readIt->first == writtenRegister)
425 access.registerReadersAndConversions[blockInstr.key()] = conversions;
428 if (blockInstr->second.changedRegisterIndex == writtenRegister) {
430 registerActive =
false;
434 auto scheduleBlock = [&](
int blockStart) {
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});
446 if (!currentBlock->second.jumpIsUnconditional && nextBlock != m_basicBlocks.end())
447 scheduleBlock(nextBlock->first);
449 const int jumpTarget = currentBlock->second.jumpTarget;
450 if (jumpTarget != -1)
451 scheduleBlock(jumpTarget);
454 isFirstBlock =
false;
457 if (!eraseDeadStore(writeIt))
458 newAnnotations.appendOrdered(writeIt);
460 m_annotations = newAnnotations.take();
462 while (erasedReaders) {
463 erasedReaders =
false;
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);
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()))
479 typeIt = readers->typeReaders.erase(typeIt);
482 for (
auto registerIt = readers->registerReadersAndConversions.begin();
483 registerIt != readers->registerReadersAndConversions.end();) {
484 if (m_annotations.contains(registerIt.key()))
487 registerIt = readers->registerReadersAndConversions.erase(registerIt);
491 if (!eraseDeadStore(
it))
492 newAnnotations.appendOrdered(
it);
495 m_annotations = newAnnotations.take();
499bool QQmlJSBasicBlocks::canMove(
int instructionOffset,
const RegisterAccess &
access)
const
501 if (
access.registerReadersAndConversions.size() != 1)
503 const auto basicBlockForInstruction = [
this](
int instruction) {
504 auto block = m_basicBlocks.lower_bound(instruction);
505 if (block == m_basicBlocks.end() || block.key() == instruction)
507 Q_ASSERT(block.key() > instruction);
508 if (block == m_basicBlocks.begin())
509 return m_basicBlocks.end();
512 return basicBlockForInstruction(instructionOffset)
513 == basicBlockForInstruction(
access.registerReadersAndConversions.begin().key());
524 if (conversions.
size() == 1)
529 if (!
types.isEmpty())
537void QQmlJSBasicBlocks::adjustTypes()
544 const auto handleRegisterReadersAndConversions
546 for (
auto conversions =
it->registerReadersAndConversions.
constBegin(),
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);
565 auto adjustArray = [&](
int instructionOffset) {
566 auto it = m_readerLocations.
find(instructionOffset);
567 if (
it == m_readerLocations.
end())
570 const InstructionAnnotation &annotation = m_annotations[instructionOffset];
574 Q_ASSERT(!annotation.readRegisters.isEmpty());
589 transformRegister(content);
592 handleRegisterReadersAndConversions(
it);
598 const auto adjustObject = [&](
const ObjectOrArrayDefinition &
object) {
599 auto it = m_readerLocations.
find(
object.instructionOffset);
600 if (
it == m_readerLocations.
end())
603 const InstructionAnnotation &annotation = m_annotations[
object.instructionOffset];
607 Q_ASSERT(!annotation.readRegisters.isEmpty());
617 for (
int i = 0;
i < classSize; ++
i) {
642 transformRegister(content);
651 for (
auto it = m_objectAndArrayDefinitions.
crbegin(),
end = m_objectAndArrayDefinitions.
crend();
653 if (
it->internalClassId != -1)
656 adjustArray(
it->instructionOffset);
660 handleRegisterReadersAndConversions(
it);
664 if (
it->trackedTypes.
size() != 1)
673 NewVirtualRegisters newRegisters;
674 for (
auto i = m_annotations.
begin(), iEnd = m_annotations.
end();
i != iEnd; ++
i) {
676 transformRegister(
i->second.changedRegister);
678 for (
auto conversion =
i->second.typeConversions.begin(),
679 conversionEnd =
i->second.typeConversions.end(); conversion != conversionEnd;
681 if (!liveConversions[
i.key()].contains(conversion.key()))
685 const auto conversionOrigins = conversion->second.content.conversionOrigins();
687 for (
const auto &origin : conversionOrigins)
691 transformRegister(conversion->second.content);
692 newRegisters.appendOrdered(conversion);
694 i->second.typeConversions = newRegisters.take();
696 for (
int movable :
std::as_const(movableReads[
i.
key()]))
697 i->second.readRegisters[movable].canMove = true;
701void QQmlJSBasicBlocks::populateBasicBlocks()
703 for (
auto blockNext = m_basicBlocks.begin(), blockEnd = m_basicBlocks.end();
704 blockNext != blockEnd;) {
706 const auto blockIt = blockNext++;
707 BasicBlock &block = blockIt->second;
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();
718 if (!instruction.isRename) {
721 it->second.content.conversionOrigins()) {
723 block.readTypes.append(origin);
727 block.readRegisters.append(
it->first);
732 if (!instruction.isRename) {
734 instruction.changedRegister));
736 writtenRegisters.
append(instruction.changedRegisterIndex);
void appendOrdered(const typename OriginalFlatMap::iterator &i)
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
iterator find(const Key &key)
KeyContainer key_container_type
MappedContainer mapped_container_type
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
iterator erase(const_iterator it)
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
qsizetype size() const noexcept
bool isEmpty() const noexcept
const_reverse_iterator crbegin() const noexcept
void append(parameter_type t)
const_reverse_iterator crend() const noexcept
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)
int firstRegisterIndex() const
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
QList< T > values() const
const_iterator constBegin() const noexcept
const_iterator constEnd() const noexcept
iterator find(const T &value)
\macro QT_RESTRICTED_CAST_FROM_ASCII
QString & replace(qsizetype i, qsizetype len, QChar after)
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
int currentInstructionOffset() const
int absoluteOffset(int relativeOffset) const
int nextInstructionOffset() 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
qsizetype erase(QByteArray &ba, const T &t)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError * error
GLenum GLsizei GLsizei GLint * values
[15]
GLsizei GLenum GLenum * types
GLenum GLuint GLintptr offset
#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)
static QString dump(const QByteArray &)
QLatin1StringView QLatin1String
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
static const QTextHtmlElement elements[Html_NumElements]
QT_BEGIN_NAMESPACE typedef uchar * output
bool contains(const AT &t) const noexcept
QQmlJSRegisterContent changedRegister
QQmlJS::AST::FormalParameterList * formals
QVector< CompiledData::CodeOffsetToLineAndStatement > lineAndStatementNumberMapping
QString jsClassMember(int jsClassId, int member) const
int jsClassSize(int jsClassId) const