7#include <QtQml/private/qv4instr_moth_p.h>
16void QQmlJSBasicBlocks::dumpBasicBlocks()
18 qDebug().noquote() <<
"=== Basic Blocks for \"%1\""_L1.arg(m_context->
name);
23 debug <<
" jumpOrigins[" << block.jumpOrigins.size() <<
"]: ";
24 for (
auto origin : block.jumpOrigins) {
25 debug << origin <<
", ";
27 debug <<
"\n readRegisters[" << block.readRegisters.size() <<
"]: ";
28 for (
auto reg : block.readRegisters) {
31 debug <<
"\n jumpTarget: " << block.jumpTarget;
32 debug <<
"\n jumpIsUnConditional: " << block.jumpIsUnconditional;
33 debug <<
"\n isReturnBlock: " << block.isReturnBlock;
34 debug <<
"\n isThrowBlock: " << block.isThrowBlock;
39void QQmlJSBasicBlocks::dumpDOTGraph()
43 s <<
"=== Basic Blocks Graph in DOT format for \"%1\" (spaces are encoded as"
44 "   to preserve formatting)\n"_L1.arg(m_context->
name);
45 s <<
"digraph BasicBlocks {\n"_L1;
48 for (
const auto &[blockOffset, block] : blocks) {
49 for (
int originOffset : block.jumpOrigins) {
51 const auto isBackEdge = originOffset > blockOffset && originBlockIt->second.jumpIsUnconditional;
54 .arg(isBackEdge ?
" [color=blue]"_L1 :
""_L1);
58 for (
const auto &[blockOffset, block] : blocks) {
59 if (blockOffset < 0) {
60 s <<
" %1 [shape=record, fontname=\"Monospace\", label=\"Function Prolog\"]\n"_L1
65 auto nextBlockIt = blocks.lower_bound(blockOffset + 1);
66 int nextBlockOffset = nextBlockIt == blocks.end() ? m_context->
code.
size() : nextBlockIt->first;
69 m_context->
formals->
length(), blockOffset, nextBlockOffset - 1,
71 dump =
dump.replace(
" "_L1,
" "_L1);
72 dump =
dump.replace(
"\n"_L1,
"\\l"_L1);
73 s <<
" %1 [shape=record, fontname=\"Monospace\", label=\"{Block %1: | %2}\"]\n"_L1
80 static int functionCount = 0;
87 QFile dumpFile(dumpFolderPath + (dumpFolderPath.endsWith(
"/"_L1) ?
""_L1 :
"/"_L1) +
fileName);
89 if (dumpFolderPath ==
"-"_L1 || dumpFolderPath ==
"1"_L1 || dumpFolderPath ==
"true"_L1) {
93 qDebug() <<
"Error: Could not open file to dump the basic blocks into";
95 dumpFile.write((
"//"_L1 +
output).toLatin1().toStdString().c_str());
103 bool &basicBlocksValidationFailed)
105 basicBlocksValidationFailed =
false;
109 for (
int i = 0,
end = function->argumentTypes.size();
i !=
end; ++
i) {
116 for (
int i = 0,
end = function->registerTypes.size();
i !=
end; ++
i) {
131 if (m_hadBackJumps) {
138 it->second.jumpTarget = -1;
139 it->second.jumpIsUnconditional =
false;
142 m_skipUntilNextLabel =
false;
152 qDebug() <<
"Basic blocks validation failed: %1."_L1.arg(validationResult.errorMessage);
153 basicBlocksValidationFailed =
true;
157 if (qv4DumpBasicBlocks()) {
169 m_skipUntilNextLabel =
false;
179 if (m_skipUntilNextLabel)
188 processJump(
offset, Unconditional);
193 processJump(
offset, Conditional);
198 processJump(
offset, Conditional);
203 processJump(
offset, Conditional);
208 processJump(
offset, Conditional);
214 processJump(
offset, Conditional);
220 processJump(
offset, Conditional);
226 currentBlock.value().isReturnBlock =
true;
227 m_skipUntilNextLabel =
true;
233 currentBlock.value().isThrowBlock =
true;
234 m_skipUntilNextLabel =
true;
242 m_objectAndArrayDefinitions.
append({
261 m_objectAndArrayDefinitions.
append({
264 ? ObjectOrArrayDefinition::ArrayConstruct1ArgId
265 : ObjectOrArrayDefinition::ArrayClassId,
271void QQmlJSBasicBlocks::processJump(
int offset, JumpMode
mode)
274 m_hadBackJumps =
true;
278 currentBlock->second.jumpTarget = jumpTarget;
279 currentBlock->second.jumpIsUnconditional = (
mode == Unconditional);
281 if (
mode == Unconditional)
282 m_skipUntilNextLabel =
true;
288 QFlatMap<int, BasicBlock> &container,
int instructionOffset)
290 auto block = container.lower_bound(instructionOffset);
291 if (block == container.end() || block->first != instructionOffset)
297 const BasicBlocks &container,
int instructionOffset)
308 QList<QFlatMap<int, BasicBlock>::const_iterator> returnOrThrowBlocks;
310 if (
it.value().isReturnBlock ||
it.value().isThrowBlock)
311 returnOrThrowBlocks.append(
it);
315 for (
const auto &
it : returnOrThrowBlocks) {
316 if (
it.value().jumpTarget != -1)
317 return {
false,
"Return or throw block jumps to somewhere"_L1 };
321 QSet<int> visitedBlockOffsets;
322 QList<QFlatMap<int, BasicBlock>::const_iterator> toVisit;
323 toVisit.append(returnOrThrowBlocks);
325 while (!toVisit.empty()) {
326 const auto &[
offset, block] = *toVisit.takeLast();
328 for (
int originOffset : block.jumpOrigins) {
330 if (visitedBlockOffsets.find(originBlock.key()) == visitedBlockOffsets.end()
331 && !toVisit.contains(originBlock))
332 toVisit.append(originBlock);
336 if (visitedBlockOffsets.size() != blocks.size())
337 return {
false,
"Basic blocks graph is not fully connected"_L1 };
340 for (
const auto &[blockOffset, block] : blocks) {
341 auto target = block.jumpTarget;
342 if (
target != -1 && blocks.find(
target) == blocks.end())
343 return {
false,
"Invalid jump; target is not the start of a block"_L1 };
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.
bool isEmpty() const noexcept
bool empty() const noexcept
std::pair< iterator, bool > insert_or_assign(const Key &key, M &&obj)
iterator find(const Key &key)
std::pair< iterator, bool > insert(const Key &key, const T &value)
void append(parameter_type t)
void endInstruction(QV4::Moth::Instr::Type type) override
void generate_JumpNoException(int offset) override
BasicBlocksValidationResult basicBlocksValidation()
QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type type) override
void generate_GetOptionalLookup(int index, int offset) override
void generate_DefineObjectLiteral(int internalClassId, int argc, int args) override
QQmlJSCompilePass::BlocksAndAnnotations run(const Function *function, QQmlJSAotCompiler::Flags compileFlags, bool &basicBlocksValidationFailed)
void generate_Ret() override
void generate_JumpNotUndefined(int offset) override
void generate_Jump(int offset) override
static BasicBlocks::iterator basicBlockForInstruction(QFlatMap< int, BasicBlock > &container, int instructionOffset)
void generate_JumpFalse(int offset) override
void generate_IteratorNext(int value, int offset) override
void generate_ThrowException() override
void generate_DefineArray(int argc, int argv) override
void generate_JumpTrue(int offset) override
void generate_Construct(int func, int argc, int argv) override
int firstRegisterIndex() const
static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
const Function * m_function
InstructionAnnotations m_annotations
BasicBlocks m_basicBlocks
QFlatMap< int, BasicBlock > BasicBlocks
const_iterator cend() const noexcept
const_iterator cbegin() const noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
QString & replace(qsizetype i, qsizetype len, QChar after)
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
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)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
GLenum GLuint GLintptr offset
#define DEFINE_BOOL_CONFIG_OPTION(name, var)
static QString dump(const QByteArray &)
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
QT_BEGIN_NAMESPACE typedef uchar * output
QQmlJSRegisterContent changedRegister
static void deduplicate(Container &container)
QQmlJS::AST::FormalParameterList * formals
QVector< CompiledData::CodeOffsetToLineAndStatement > lineAndStatementNumberMapping