aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/compiler/qv4codegen.cpp5
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp234
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h24
-rw-r--r--src/qml/doc/src/javascript/finetuning.qdoc8
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks.cpp103
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks_p.h9
-rw-r--r--src/qmlcompiler/qqmljscompiler.cpp2
7 files changed, 261 insertions, 124 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index d6f2716bbc..f9c4398490 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -3414,8 +3414,9 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalPara
if (showCode) {
qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict
<< "register count" << _context->registerCountInFunction << "implicit return" << requiresReturnValue;
- QV4::Moth::dumpBytecode(_context->code, _context->locals.size(), _context->arguments.size(),
- _context->line, _context->lineAndStatementNumberMapping);
+ qDebug().noquote() << QV4::Moth::dumpBytecode(
+ _context->code, _context->locals.size(), _context->arguments.size(),
+ _context->line, _context->lineAndStatementNumberMapping);
qDebug();
}
}
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index eae076012a..4fda5b1b19 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -48,20 +48,18 @@ static QByteArray rawBytes(const char *data, int n)
}
#define ABSOLUTE_OFFSET() \
- (code - start + offset)
+ (code + beginOffset - start + offset)
#define MOTH_BEGIN_INSTR(instr) \
{ \
INSTR_##instr(MOTH_DECODE_WITH_BASE) \
- QDebug d = qDebug(); \
- d.noquote(); \
- d.nospace(); \
if (static_cast<int>(Instr::Type::instr) >= 0x100) \
--base_ptr; \
- d << alignedLineNumber(line) << alignedNumber(codeOffset).constData() << ": " \
+ s << alignedLineNumber(line) << alignedNumber(beginOffset + codeOffset).constData() << ": " \
<< rawBytes(base_ptr, int(code - base_ptr)) << #instr << " ";
#define MOTH_END_INSTR(instr) \
+ s << "\n"; \
continue; \
}
@@ -106,24 +104,37 @@ QString dumpArguments(int argc, int argv, int nFormals)
return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")");
}
-void dumpBytecode(
+QString dumpBytecode(
const char *code, int len, int nLocals, int nFormals, int /*startLine*/,
const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping)
{
+ return dumpBytecode(code, len, nLocals, nFormals, 0, len - 1, lineAndStatementNumberMapping);
+}
+
+QString dumpBytecode(
+ const char *code, int len, int nLocals, int nFormals, int beginOffset, int endOffset,
+ const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping)
+{
+ Q_ASSERT(beginOffset <= endOffset && 0 <= beginOffset && endOffset <= len);
+
MOTH_JUMP_TABLE;
auto findLine = [](const CompiledData::CodeOffsetToLineAndStatement &entry, uint offset) {
return entry.codeOffset < offset;
};
+ QString output;
+ QTextStream s{ &output };
+
int lastLine = -1;
+ code += beginOffset;
const char *start = code;
- const char *end = code + len;
+ const char *end = code + (endOffset - beginOffset) + 1;
while (code < end) {
const auto codeToLine = std::lower_bound(
lineAndStatementNumberMapping.constBegin(),
lineAndStatementNumberMapping.constEnd(),
- static_cast<uint>(code - start) + 1, findLine) - 1;
+ static_cast<uint>(code - start + beginOffset) + 1, findLine) - 1;
int line = int(codeToLine->line);
if (line != lastLine)
lastLine = line;
@@ -135,23 +146,23 @@ void dumpBytecode(
MOTH_DISPATCH()
MOTH_BEGIN_INSTR(LoadReg)
- d << dumpRegister(reg, nFormals);
+ s << dumpRegister(reg, nFormals);
MOTH_END_INSTR(LoadReg)
MOTH_BEGIN_INSTR(StoreReg)
- d << dumpRegister(reg, nFormals);
+ s << dumpRegister(reg, nFormals);
MOTH_END_INSTR(StoreReg)
MOTH_BEGIN_INSTR(MoveReg)
- d << dumpRegister(destReg, nFormals) << ", " << dumpRegister(srcReg, nFormals);
+ s << dumpRegister(destReg, nFormals) << ", " << dumpRegister(srcReg, nFormals);
MOTH_END_INSTR(MoveReg)
MOTH_BEGIN_INSTR(LoadImport)
- d << "i" << index;
+ s << "i" << index;
MOTH_END_INSTR(LoadImport)
MOTH_BEGIN_INSTR(LoadConst)
- d << "C" << index;
+ s << "C" << index;
MOTH_END_INSTR(LoadConst)
MOTH_BEGIN_INSTR(LoadNull)
@@ -170,111 +181,111 @@ void dumpBytecode(
MOTH_END_INSTR(LoadUndefined)
MOTH_BEGIN_INSTR(LoadInt)
- d << value;
+ s << value;
MOTH_END_INSTR(LoadInt)
MOTH_BEGIN_INSTR(MoveConst)
- d << dumpRegister(destTemp, nFormals) << ", C" << constIndex;
+ s << dumpRegister(destTemp, nFormals) << ", C" << constIndex;
MOTH_END_INSTR(MoveConst)
MOTH_BEGIN_INSTR(LoadLocal)
if (index < nLocals)
- d << "l" << index;
+ s << "l" << index;
else
- d << "a" << (index - nLocals);
+ s << "a" << (index - nLocals);
MOTH_END_INSTR(LoadLocal)
MOTH_BEGIN_INSTR(StoreLocal)
if (index < nLocals)
- d << "l" << index;
+ s << "l" << index;
else
- d << "a" << (index - nLocals);
+ s << "a" << (index - nLocals);
MOTH_END_INSTR(StoreLocal)
MOTH_BEGIN_INSTR(LoadScopedLocal)
if (index < nLocals)
- d << "l" << index << "@" << scope;
+ s << "l" << index << "@" << scope;
else
- d << "a" << (index - nLocals) << "@" << scope;
+ s << "a" << (index - nLocals) << "@" << scope;
MOTH_END_INSTR(LoadScopedLocal)
MOTH_BEGIN_INSTR(StoreScopedLocal)
if (index < nLocals)
- d << ", " << "l" << index << "@" << scope;
+ s << ", " << "l" << index << "@" << scope;
else
- d << ", " << "a" << (index - nLocals) << "@" << scope;
+ s << ", " << "a" << (index - nLocals) << "@" << scope;
MOTH_END_INSTR(StoreScopedLocal)
MOTH_BEGIN_INSTR(LoadRuntimeString)
- d << stringId;
+ s << stringId;
MOTH_END_INSTR(LoadRuntimeString)
MOTH_BEGIN_INSTR(MoveRegExp)
- d << dumpRegister(destReg, nFormals) << ", " <<regExpId;
+ s << dumpRegister(destReg, nFormals) << ", " <<regExpId;
MOTH_END_INSTR(MoveRegExp)
MOTH_BEGIN_INSTR(LoadClosure)
- d << value;
+ s << value;
MOTH_END_INSTR(LoadClosure)
MOTH_BEGIN_INSTR(LoadName)
- d << name;
+ s << name;
MOTH_END_INSTR(LoadName)
MOTH_BEGIN_INSTR(LoadGlobalLookup)
- d << index;
+ s << index;
MOTH_END_INSTR(LoadGlobalLookup)
MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
- d << index;
+ s << index;
MOTH_END_INSTR(LoadQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(StoreNameSloppy)
- d << name;
+ s << name;
MOTH_END_INSTR(StoreNameSloppy)
MOTH_BEGIN_INSTR(StoreNameStrict)
- d << name;
+ s << name;
MOTH_END_INSTR(StoreNameStrict)
MOTH_BEGIN_INSTR(LoadElement)
- d << dumpRegister(base, nFormals) << "[acc]";
+ s << dumpRegister(base, nFormals) << "[acc]";
MOTH_END_INSTR(LoadElement)
MOTH_BEGIN_INSTR(StoreElement)
- d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
+ s << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
MOTH_END_INSTR(StoreElement)
MOTH_BEGIN_INSTR(LoadProperty)
- d << "acc[" << name << "]";
+ s << "acc[" << name << "]";
MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(LoadOptionalProperty)
- d << "acc[" << name << "], jump(" << ABSOLUTE_OFFSET() << ")";
+ s << "acc[" << name << "], jump(" << ABSOLUTE_OFFSET() << ")";
MOTH_END_INSTR(LoadOptionalProperty)
MOTH_BEGIN_INSTR(GetLookup)
- d << "acc(" << index << ")";
+ s << "acc(" << index << ")";
MOTH_END_INSTR(GetLookup)
MOTH_BEGIN_INSTR(GetOptionalLookup)
- d << "acc(" << index << "), jump(" << ABSOLUTE_OFFSET() << ")";
+ s << "acc(" << index << "), jump(" << ABSOLUTE_OFFSET() << ")";
MOTH_END_INSTR(GetOptionalLookup)
MOTH_BEGIN_INSTR(StoreProperty)
- d << dumpRegister(base, nFormals) << "[" << name<< "]";
+ s << dumpRegister(base, nFormals) << "[" << name<< "]";
MOTH_END_INSTR(StoreProperty)
MOTH_BEGIN_INSTR(SetLookup)
- d << dumpRegister(base, nFormals) << "(" << index << ")";
+ s << dumpRegister(base, nFormals) << "(" << index << ")";
MOTH_END_INSTR(SetLookup)
MOTH_BEGIN_INSTR(LoadSuperProperty)
- d << dumpRegister(property, nFormals);
+ s << dumpRegister(property, nFormals);
MOTH_END_INSTR(LoadSuperProperty)
MOTH_BEGIN_INSTR(StoreSuperProperty)
- d << dumpRegister(property, nFormals);
+ s << dumpRegister(property, nFormals);
MOTH_END_INSTR(StoreSuperProperty)
MOTH_BEGIN_INSTR(Yield)
@@ -284,73 +295,73 @@ void dumpBytecode(
MOTH_END_INSTR(YieldStar)
MOTH_BEGIN_INSTR(Resume)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
MOTH_END_INSTR(Resume)
MOTH_BEGIN_INSTR(CallValue)
- d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals);
+ s << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallValue)
MOTH_BEGIN_INSTR(CallWithReceiver)
- d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals)
+ s << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals)
<< dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallWithReceiver)
MOTH_BEGIN_INSTR(CallProperty)
- d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
+ s << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
;
MOTH_END_INSTR(CallProperty)
MOTH_BEGIN_INSTR(CallPropertyLookup)
- d << dumpRegister(base, nFormals) << "." << lookupIndex
+ s << dumpRegister(base, nFormals) << "." << lookupIndex
<< dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallPropertyLookup)
MOTH_BEGIN_INSTR(CallName)
- d << name << dumpArguments(argc, argv, nFormals);
+ s << name << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallName)
MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
- d << dumpArguments(argc, argv, nFormals);
+ s << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallPossiblyDirectEval)
MOTH_BEGIN_INSTR(CallGlobalLookup)
- d << index << dumpArguments(argc, argv, nFormals);
+ s << index << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallGlobalLookup)
MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
- d << index << dumpArguments(argc, argv, nFormals);
+ s << index << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(CallWithSpread)
- d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals)
+ s << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals)
<< dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallWithSpread)
MOTH_BEGIN_INSTR(Construct)
- d << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
+ s << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(Construct)
MOTH_BEGIN_INSTR(ConstructWithSpread)
- d << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
+ s << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(ConstructWithSpread)
MOTH_BEGIN_INSTR(SetUnwindHandler)
if (offset)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
else
- d << "<null>";
+ s << "<null>";
MOTH_END_INSTR(SetUnwindHandler)
MOTH_BEGIN_INSTR(UnwindDispatch)
MOTH_END_INSTR(UnwindDispatch)
MOTH_BEGIN_INSTR(UnwindToLabel)
- d << "(" << level << ") " << ABSOLUTE_OFFSET();
+ s << "(" << level << ") " << ABSOLUTE_OFFSET();
MOTH_END_INSTR(UnwindToLabel)
MOTH_BEGIN_INSTR(DeadTemporalZoneCheck)
- d << name;
+ s << name;
MOTH_END_INSTR(DeadTemporalZoneCheck)
MOTH_BEGIN_INSTR(ThrowException)
@@ -366,21 +377,21 @@ void dumpBytecode(
MOTH_END_INSTR(CreateCallContext)
MOTH_BEGIN_INSTR(PushCatchContext)
- d << index << ", " << name;
+ s << index << ", " << name;
MOTH_END_INSTR(PushCatchContext)
MOTH_BEGIN_INSTR(PushWithContext)
MOTH_END_INSTR(PushWithContext)
MOTH_BEGIN_INSTR(PushBlockContext)
- d << index;
+ s << index;
MOTH_END_INSTR(PushBlockContext)
MOTH_BEGIN_INSTR(CloneBlockContext)
MOTH_END_INSTR(CloneBlockContext)
MOTH_BEGIN_INSTR(PushScriptContext)
- d << index;
+ s << index;
MOTH_END_INSTR(PushScriptContext)
MOTH_BEGIN_INSTR(PopScriptContext)
@@ -390,55 +401,55 @@ void dumpBytecode(
MOTH_END_INSTR(PopContext)
MOTH_BEGIN_INSTR(GetIterator)
- d << iterator;
+ s << iterator;
MOTH_END_INSTR(GetIterator)
MOTH_BEGIN_INSTR(IteratorNext)
- d << dumpRegister(value, nFormals) << ", " << dumpRegister(done, nFormals);
+ s << dumpRegister(value, nFormals) << ", " << dumpRegister(done, nFormals);
MOTH_END_INSTR(IteratorNext)
MOTH_BEGIN_INSTR(IteratorNextForYieldStar)
- d << dumpRegister(iterator, nFormals) << ", " << dumpRegister(object, nFormals);
+ s << dumpRegister(iterator, nFormals) << ", " << dumpRegister(object, nFormals);
MOTH_END_INSTR(IteratorNextForYieldStar)
MOTH_BEGIN_INSTR(IteratorClose)
- d << dumpRegister(done, nFormals);
+ s << dumpRegister(done, nFormals);
MOTH_END_INSTR(IteratorClose)
MOTH_BEGIN_INSTR(DestructureRestElement)
MOTH_END_INSTR(DestructureRestElement)
MOTH_BEGIN_INSTR(DeleteProperty)
- d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
+ s << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
MOTH_END_INSTR(DeleteProperty)
MOTH_BEGIN_INSTR(DeleteName)
- d << name;
+ s << name;
MOTH_END_INSTR(DeleteName)
MOTH_BEGIN_INSTR(TypeofName)
- d << name;
+ s << name;
MOTH_END_INSTR(TypeofName)
MOTH_BEGIN_INSTR(TypeofValue)
MOTH_END_INSTR(TypeofValue)
MOTH_BEGIN_INSTR(DeclareVar)
- d << isDeletable << ", " << varName;
+ s << isDeletable << ", " << varName;
MOTH_END_INSTR(DeclareVar)
MOTH_BEGIN_INSTR(DefineArray)
- d << dumpRegister(args, nFormals) << ", " << argc;
+ s << dumpRegister(args, nFormals) << ", " << argc;
MOTH_END_INSTR(DefineArray)
MOTH_BEGIN_INSTR(DefineObjectLiteral)
- d << internalClassId
+ s << internalClassId
<< ", " << argc
<< ", " << dumpRegister(args, nFormals);
MOTH_END_INSTR(DefineObjectLiteral)
MOTH_BEGIN_INSTR(CreateClass)
- d << classIndex
+ s << classIndex
<< ", " << dumpRegister(heritage, nFormals)
<< ", " << dumpRegister(computedNames, nFormals);
MOTH_END_INSTR(CreateClass)
@@ -450,7 +461,7 @@ void dumpBytecode(
MOTH_END_INSTR(CreateUnmappedArgumentsObject)
MOTH_BEGIN_INSTR(CreateRestParameter)
- d << argIndex;
+ s << argIndex;
MOTH_END_INSTR(CreateRestParameter)
MOTH_BEGIN_INSTR(ConvertThisToObject)
@@ -463,23 +474,23 @@ void dumpBytecode(
MOTH_END_INSTR(ToObject)
MOTH_BEGIN_INSTR(Jump)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
MOTH_END_INSTR(Jump)
MOTH_BEGIN_INSTR(JumpTrue)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpTrue)
MOTH_BEGIN_INSTR(JumpFalse)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpFalse)
MOTH_BEGIN_INSTR(JumpNotUndefined)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpNotUndefined)
MOTH_BEGIN_INSTR(JumpNoException)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpNoException)
MOTH_BEGIN_INSTR(CheckException)
@@ -492,43 +503,43 @@ void dumpBytecode(
MOTH_END_INSTR(CmpNeNull)
MOTH_BEGIN_INSTR(CmpEqInt)
- d << lhs;
+ s << lhs;
MOTH_END_INSTR(CmpEq)
MOTH_BEGIN_INSTR(CmpNeInt)
- d << lhs;
+ s << lhs;
MOTH_END_INSTR(CmpNeInt)
MOTH_BEGIN_INSTR(CmpEq)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpEq)
MOTH_BEGIN_INSTR(CmpNe)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpNe)
MOTH_BEGIN_INSTR(CmpGt)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpGt)
MOTH_BEGIN_INSTR(CmpGe)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpGe)
MOTH_BEGIN_INSTR(CmpLt)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpLt)
MOTH_BEGIN_INSTR(CmpLe)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpLe)
MOTH_BEGIN_INSTR(CmpStrictEqual)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpStrictEqual)
MOTH_BEGIN_INSTR(CmpStrictNotEqual)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpStrictNotEqual)
MOTH_BEGIN_INSTR(UNot)
@@ -550,87 +561,87 @@ void dumpBytecode(
MOTH_END_INSTR(Decrement)
MOTH_BEGIN_INSTR(Add)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Add)
MOTH_BEGIN_INSTR(BitAnd)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(BitAnd)
MOTH_BEGIN_INSTR(BitOr)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(BitOr)
MOTH_BEGIN_INSTR(BitXor)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(BitXor)
MOTH_BEGIN_INSTR(UShr)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(UShr)
MOTH_BEGIN_INSTR(Shr)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Shr)
MOTH_BEGIN_INSTR(Shl)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Shl)
MOTH_BEGIN_INSTR(BitAndConst)
- d << "acc, " << rhs;
+ s << "acc, " << rhs;
MOTH_END_INSTR(BitAndConst)
MOTH_BEGIN_INSTR(BitOrConst)
- d << "acc, " << rhs;
+ s << "acc, " << rhs;
MOTH_END_INSTR(BitOr)
MOTH_BEGIN_INSTR(BitXorConst)
- d << "acc, " << rhs;
+ s << "acc, " << rhs;
MOTH_END_INSTR(BitXor)
MOTH_BEGIN_INSTR(UShrConst)
- d << "acc, " << rhs;
+ s << "acc, " << rhs;
MOTH_END_INSTR(UShrConst)
MOTH_BEGIN_INSTR(ShrConst)
- d << "acc, " << rhs;
+ s << "acc, " << rhs;
MOTH_END_INSTR(ShrConst)
MOTH_BEGIN_INSTR(ShlConst)
- d << "acc, " << rhs;
+ s << "acc, " << rhs;
MOTH_END_INSTR(ShlConst)
MOTH_BEGIN_INSTR(Exp)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Exp)
MOTH_BEGIN_INSTR(Mul)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Mul)
MOTH_BEGIN_INSTR(Div)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Div)
MOTH_BEGIN_INSTR(Mod)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Mod)
MOTH_BEGIN_INSTR(Sub)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Sub)
MOTH_BEGIN_INSTR(As)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Sub)
MOTH_BEGIN_INSTR(CmpIn)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(CmpIn)
MOTH_BEGIN_INSTR(CmpInstanceOf)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(CmpInstanceOf)
MOTH_BEGIN_INSTR(Ret)
@@ -640,20 +651,21 @@ void dumpBytecode(
MOTH_END_INSTR(Debug)
MOTH_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
- d << dumpRegister(firstReg, nFormals) << ", " << count;
+ s << dumpRegister(firstReg, nFormals) << ", " << count;
MOTH_END_INSTR(InitializeBlockDeadTemporalZone)
MOTH_BEGIN_INSTR(ThrowOnNullOrUndefined)
MOTH_END_INSTR(ThrowOnNullOrUndefined)
MOTH_BEGIN_INSTR(GetTemplateObject)
- d << index;
+ s << index;
MOTH_END_INSTR(GetTemplateObject)
MOTH_BEGIN_INSTR(TailCall)
- d << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals);
+ s << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(TailCall)
}
+ return output;
}
}
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index c61db9a6ce..7ebf849f3e 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -497,14 +497,22 @@ inline bool operator!=(const StackSlot &l, const StackSlot &r) { return l.stackS
// When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h
-void dumpBytecode(const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1,
- const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping
- = QVector<CompiledData::CodeOffsetToLineAndStatement>());
-inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1,
- const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping
- = QVector<CompiledData::CodeOffsetToLineAndStatement>()) {
- dumpBytecode(bytecode.constData(), bytecode.size(), nLocals, nFormals, startLine,
- lineAndStatementNumberMapping);
+Q_QML_PRIVATE_EXPORT
+QString dumpBytecode(
+ const char *bytecode, int len, int nLocals, int nFormals, int beginOffset, int endOffset,
+ const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping =
+ QVector<CompiledData::CodeOffsetToLineAndStatement>());
+QString dumpBytecode(
+ const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1,
+ const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping =
+ QVector<CompiledData::CodeOffsetToLineAndStatement>());
+inline QString dumpBytecode(
+ const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1,
+ const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping =
+ QVector<CompiledData::CodeOffsetToLineAndStatement>())
+{
+ return dumpBytecode(bytecode.constData(), bytecode.size(), nLocals, nFormals, startLine,
+ lineAndStatementNumberMapping);
}
union Instr
diff --git a/src/qml/doc/src/javascript/finetuning.qdoc b/src/qml/doc/src/javascript/finetuning.qdoc
index 14b4c9538d..e2d754eb83 100644
--- a/src/qml/doc/src/javascript/finetuning.qdoc
+++ b/src/qml/doc/src/javascript/finetuning.qdoc
@@ -82,6 +82,14 @@ Running JavaScript code can be influenced by a few environment variables, partic
\li Outputs the IR bytecode generated by Qt to the console.
Has to be combined with \c{QML_DISABLE_DISK_CACHE} or already cached bytecode will not
be shown.
+ \row
+ \li \c{QV4_DUMP_BASIC_BLOCKS}
+ \li Outputs the basic blocks of each function compiled ahead of time. The details of the
+ blocks are printed to the console. Additionally, control flow graphs with the byte code
+ for each block are generated in the DOT format for each compiled function. The value of
+ \c {QV4_DUMP_BASIC_BLOCKS} is used as the path to the folder where the DOT files should
+ be generated. If the path is any of ["-", "1", "true"] or if files can't be opened,
+ the graphs are dumped to stdout instead.
\endtable
\l{The QML Disk Cache} accepts further environment variables that allow fine tuning its behavior.
diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp
index e819313b54..95e7ca8d55 100644
--- a/src/qmlcompiler/qqmljsbasicblocks.cpp
+++ b/src/qmlcompiler/qqmljsbasicblocks.cpp
@@ -3,8 +3,105 @@
#include "qqmljsbasicblocks_p.h"
+#include <QtQml/private/qv4instr_moth_p.h>
+
QT_BEGIN_NAMESPACE
+using namespace Qt::Literals::StringLiterals;
+
+DEFINE_BOOL_CONFIG_OPTION(qv4DumpBasicBlocks, QV4_DUMP_BASIC_BLOCKS)
+
+void QQmlJSBasicBlocks::dumpBasicBlocks()
+{
+ 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 << ":\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 (auto type : block.readTypes) {
+ debug << type->augmentedInternalName() << ", ";
+ }
+ debug << "\n jumpTarget: " << block.jumpTarget;
+ debug << "\n jumpIsUnConditional: " << block.jumpIsUnconditional;
+ }
+ qDebug() << "\n";
+}
+
+void QQmlJSBasicBlocks::dumpDOTGraph()
+{
+ auto isBackEdge = [](const BasicBlock &originBlock, int originOffset, int destinationOffset) {
+ return originOffset > destinationOffset && originBlock.jumpIsUnconditional;
+ };
+
+ QString output;
+ QTextStream s{ &output };
+ s << "=== Basic Blocks Graph in DOT format for \"%1\" (spaces are encoded as"
+ " &#160; to preserve formatting)\n"_L1.arg(m_context->name);
+ s << "digraph BasicBlocks {\n"_L1;
+
+ std::map<int, BasicBlock> blocks{ m_basicBlocks.begin(), m_basicBlocks.end() };
+ for (const auto &[blockOffset, block] : blocks) {
+ for (int originOffset : block.jumpOrigins) {
+ int originBlockOffset;
+ auto originBlockIt = blocks.find(originOffset);
+ if (originBlockIt != blocks.end())
+ originBlockOffset = originOffset;
+ else
+ originBlockOffset = std::prev(blocks.lower_bound(originOffset))->first;
+ s << " %1 -> %2%3\n"_L1.arg(QString::number(originBlockOffset))
+ .arg(QString::number(blockOffset))
+ .arg(isBackEdge(originBlockIt->second, originOffset, blockOffset)
+ ? " [color=blue]"_L1
+ : ""_L1);
+ }
+ }
+
+ for (const auto &[blockOffset, block] : blocks) {
+ int beginOffset = std::max(0, blockOffset);
+ 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(), beginOffset, nextBlockOffset - 1,
+ m_context->lineAndStatementNumberMapping);
+ dump = dump.replace(" "_L1, "&#160;"_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();
+ }
+ }
+}
+
template<typename Container>
void deduplicate(Container &container)
{
@@ -62,6 +159,12 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run(
populateBasicBlocks();
populateReaderLocations();
adjustTypes();
+
+ if (qv4DumpBasicBlocks()) {
+ dumpBasicBlocks();
+ dumpDOTGraph();
+ }
+
return std::move(m_annotations);
}
diff --git a/src/qmlcompiler/qqmljsbasicblocks_p.h b/src/qmlcompiler/qqmljsbasicblocks_p.h
index 2b4fc108fc..b641928f32 100644
--- a/src/qmlcompiler/qqmljsbasicblocks_p.h
+++ b/src/qmlcompiler/qqmljsbasicblocks_p.h
@@ -31,9 +31,10 @@ public:
bool jumpIsUnconditional = false;
};
- QQmlJSBasicBlocks(const QV4::Compiler::JSUnitGenerator *unitGenerator,
+ QQmlJSBasicBlocks(const QV4::Compiler::Context *context,
+ const QV4::Compiler::JSUnitGenerator *unitGenerator,
const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
- : QQmlJSCompilePass(unitGenerator, typeResolver, logger)
+ : QQmlJSCompilePass(unitGenerator, typeResolver, logger), m_context{ context }
{
}
@@ -84,6 +85,10 @@ private:
void adjustTypes();
bool canMove(int instructionOffset, const RegisterAccess &access) const;
+ void dumpBasicBlocks();
+ void dumpDOTGraph();
+
+ const QV4::Compiler::Context *m_context;
InstructionAnnotations m_annotations;
QFlatMap<int, BasicBlock> m_basicBlocks;
QHash<int, RegisterAccess> m_readerLocations;
diff --git a/src/qmlcompiler/qqmljscompiler.cpp b/src/qmlcompiler/qqmljscompiler.cpp
index 0a0c97926a..314e241520 100644
--- a/src/qmlcompiler/qqmljscompiler.cpp
+++ b/src/qmlcompiler/qqmljscompiler.cpp
@@ -784,7 +784,7 @@ QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
if (error->isValid())
return compileError();
- QQmlJSBasicBlocks basicBlocks(m_unitGenerator, &m_typeResolver, m_logger);
+ QQmlJSBasicBlocks basicBlocks(context, m_unitGenerator, &m_typeResolver, m_logger);
typePropagationResult = basicBlocks.run(function, typePropagationResult, error);
if (error->isValid())
return compileError();