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 readTypes[" << block.readTypes.size() <<
"]: ";
32 for (
const auto &
type : block.readTypes) {
33 debug <<
type->augmentedInternalName() <<
", ";
35 debug <<
"\n jumpTarget: " << block.jumpTarget;
36 debug <<
"\n jumpIsUnConditional: " << block.jumpIsUnconditional;
37 debug <<
"\n isReturnBlock: " << block.isReturnBlock;
38 debug <<
"\n isThrowBlock: " << block.isThrowBlock;
43void QQmlJSBasicBlocks::dumpDOTGraph()
47 s <<
"=== Basic Blocks Graph in DOT format for \"%1\" (spaces are encoded as"
48 "   to preserve formatting)\n"_L1.arg(m_context->
name);
49 s <<
"digraph BasicBlocks {\n"_L1;
52 for (
const auto &[blockOffset, block] : blocks) {
53 for (
int originOffset : block.jumpOrigins) {
55 const auto isBackEdge = originOffset > blockOffset && originBlockIt->second.jumpIsUnconditional;
58 .arg(isBackEdge ?
" [color=blue]"_L1 :
""_L1);
62 for (
const auto &[blockOffset, block] : blocks) {
63 if (blockOffset < 0) {
64 s <<
" %1 [shape=record, fontname=\"Monospace\", label=\"Function Prolog\"]\n"_L1
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(), blockOffset, nextBlockOffset - 1,
75 dump =
dump.replace(
" "_L1,
" "_L1);
76 dump =
dump.replace(
"\n"_L1,
"\\l"_L1);
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());
107 bool &basicBlocksValidationFailed)
109 basicBlocksValidationFailed =
false;
113 for (
int i = 0,
end = function->argumentTypes.size();
i !=
end; ++
i) {
120 for (
int i = 0,
end = function->registerTypes.size();
i !=
end; ++
i) {
135 if (m_hadBackJumps) {
142 it->second.jumpTarget = -1;
143 it->second.jumpIsUnconditional =
false;
146 m_skipUntilNextLabel =
false;
156 qDebug() <<
"Basic blocks validation failed: %1."_L1.arg(validationResult.errorMessage);
157 basicBlocksValidationFailed =
true;
161 if (qv4DumpBasicBlocks()) {
173 m_skipUntilNextLabel =
false;
183 if (m_skipUntilNextLabel)
192 processJump(
offset, Unconditional);
197 processJump(
offset, Conditional);
202 processJump(
offset, Conditional);
207 processJump(
offset, Conditional);
212 processJump(
offset, Conditional);
218 processJump(
offset, Conditional);
224 processJump(
offset, Conditional);
230 currentBlock.value().isReturnBlock =
true;
231 m_skipUntilNextLabel =
true;
237 currentBlock.value().isThrowBlock =
true;
238 m_skipUntilNextLabel =
true;
246 m_objectAndArrayDefinitions.
append({
265 m_objectAndArrayDefinitions.
append({
268 ? ObjectOrArrayDefinition::ArrayConstruct1ArgId
269 : ObjectOrArrayDefinition::ArrayClassId,
275void QQmlJSBasicBlocks::processJump(
int offset, JumpMode
mode)
278 m_hadBackJumps =
true;
282 currentBlock->second.jumpTarget = jumpTarget;
283 currentBlock->second.jumpIsUnconditional = (
mode == Unconditional);
285 if (
mode == Unconditional)
286 m_skipUntilNextLabel =
true;
292 QFlatMap<int, BasicBlock> &container,
int instructionOffset)
294 auto block = container.lower_bound(instructionOffset);
295 if (block == container.end() || block->first != instructionOffset)
301 const BasicBlocks &container,
int instructionOffset)
312 QList<QFlatMap<int, BasicBlock>::const_iterator> returnOrThrowBlocks;
314 if (
it.value().isReturnBlock ||
it.value().isThrowBlock)
315 returnOrThrowBlocks.append(
it);
319 for (
const auto &
it : returnOrThrowBlocks) {
320 if (
it.value().jumpTarget != -1)
321 return {
false,
"Return or throw block jumps to somewhere"_L1 };
325 QSet<int> visitedBlockOffsets;
326 QList<QFlatMap<int, BasicBlock>::const_iterator> toVisit;
327 toVisit.append(returnOrThrowBlocks);
329 while (!toVisit.empty()) {
330 const auto &[
offset, block] = *toVisit.takeLast();
332 for (
int originOffset : block.jumpOrigins) {
334 if (visitedBlockOffsets.find(originBlock.key()) == visitedBlockOffsets.end()
335 && !toVisit.contains(originBlock))
336 toVisit.append(originBlock);
340 if (visitedBlockOffsets.size() != blocks.size())
341 return {
false,
"Basic blocks graph is not fully connected"_L1 };
344 for (
const auto &[blockOffset, block] : blocks) {
345 auto target = block.jumpTarget;
346 if (
target != -1 && blocks.find(
target) == blocks.end())
347 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