diff options
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 5 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth.cpp | 234 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 24 | ||||
-rw-r--r-- | src/qml/doc/src/javascript/finetuning.qdoc | 8 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsbasicblocks.cpp | 103 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsbasicblocks_p.h | 9 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljscompiler.cpp | 2 |
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" + "   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, " "_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(); |