aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2017-06-22 10:01:17 +0200
committerErik Verbruggen <erik.verbruggen@qt.io>2017-06-30 11:58:44 +0000
commit29e41a9ee61a05274f77f89e9ffd8875f90d3308 (patch)
tree7de81ef4ad3bb0b2b0cd3313ee4dd03a76e9c681 /src/qml/compiler
parent3a9f4d3ae701c7119016a0bf8b4e65ceb17864b0 (diff)
Remove now unused files
Remove all files from the old compiler pipeline that are now unused. This includes the whole IR, JIT code generation, and the old Moth Isel. Change-Id: I50d06abfbcf0e9755a54ed94638f8bb74f9512b1 Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/compiler.pri9
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp7
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp2
-rw-r--r--src/qml/compiler/qv4bytecodegenerator.cpp1
-rw-r--r--src/qml/compiler/qv4codegen_p.h13
-rw-r--r--src/qml/compiler/qv4compileddata.cpp10
-rw-r--r--src/qml/compiler/qv4compiler.cpp17
-rw-r--r--src/qml/compiler/qv4compiler_p.h7
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp1451
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h218
-rw-r--r--src/qml/compiler/qv4isel_p.cpp446
-rw-r--r--src/qml/compiler/qv4isel_p.h220
-rw-r--r--src/qml/compiler/qv4isel_util_p.h241
-rw-r--r--src/qml/compiler/qv4jsir.cpp994
-rw-r--r--src/qml/compiler/qv4jsir_p.h1807
-rw-r--r--src/qml/compiler/qv4ssa.cpp5848
-rw-r--r--src/qml/compiler/qv4ssa_p.h472
17 files changed, 15 insertions, 11748 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index afd6361206..a8eeb2a1fa 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -7,11 +7,8 @@ HEADERS += \
$$PWD/qv4compiler_p.h \
$$PWD/qv4compilationunit_moth_p.h \
$$PWD/qv4codegen_p.h \
- $$PWD/qv4jsir_p.h \
- $$PWD/qv4isel_util_p.h \
$$PWD/qqmlirbuilder_p.h \
- $$PWD/qqmltypecompiler_p.h \
- $$PWD/qv4jssimplifier_p.h
+ $$PWD/qqmltypecompiler_p.h
SOURCES += \
$$PWD/qv4bytecodegenerator.cpp \
@@ -19,9 +16,7 @@ SOURCES += \
$$PWD/qv4compiler.cpp \
$$PWD/qv4compilationunit_moth.cpp \
$$PWD/qv4codegen.cpp \
- $$PWD/qv4jsir.cpp \
- $$PWD/qqmlirbuilder.cpp \
- $$PWD/qv4jssimplifier.cpp
+ $$PWD/qqmlirbuilder.cpp
!qmldevtools_build {
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index c6c6634a8c..bb55cfe6ec 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -60,7 +60,7 @@ QT_USE_NAMESPACE
static const quint32 emptyStringIndex = 0;
-#ifndef V4_BOOTSTRAP
+#if 0 //ndef V4_BOOTSTRAP
DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS);
#endif // V4_BOOTSTRAP
@@ -1671,8 +1671,9 @@ enum MetaObjectResolverFlags {
ResolveTypeInformationOnly = 0x8
};
-static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject);
#if 0
+static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject);
+
static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlType *qmlType, int index);
static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
@@ -1788,7 +1789,6 @@ static void initImportNamespaceResolver(QV4::IR::MemberExpressionResolver *resol
resolver->extraData = imports;
resolver->flags = 0;
}
-#endif
static QV4::IR::DiscoveredType resolveMetaObjectProperty(
QQmlEnginePrivate *qmlEngine, const QV4::IR::MemberExpressionResolver *resolver,
@@ -1881,7 +1881,6 @@ static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver,
resolver->flags = 0;
}
-#if 0
static QV4::IR::DiscoveredType resolveScopedEnum(QQmlEnginePrivate *qmlEngine,
const QV4::IR::MemberExpressionResolver *resolver,
QV4::IR::Member *member)
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index 9a96dbcbfc..f51546739b 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -46,7 +46,7 @@
#include <private/qqmlcomponent_p.h>
#include "qqmlpropertycachecreator_p.h"
-#include "qv4jssimplifier_p.h"
+//#include "qv4jssimplifier_p.h"
#define COMPILE_EXCEPTION(token, desc) \
{ \
diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp
index 1bbd0fdd78..650c3b5fc1 100644
--- a/src/qml/compiler/qv4bytecodegenerator.cpp
+++ b/src/qml/compiler/qv4bytecodegenerator.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include <private/qv4bytecodegenerator_p.h>
-#include <private/qv4jsir_p.h>
#include <private/qqmljsastfwd_p.h>
QT_USE_NAMESPACE
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index c863159189..67a70c47d3 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -51,7 +51,6 @@
//
#include "private/qv4global_p.h"
-#include "qv4jsir_p.h"
#include <private/qqmljsastvisitor_p.h>
#include <private/qqmljsast_p.h>
#include <private/qqmljsengine_p.h>
@@ -59,6 +58,7 @@
#include <private/qv4compiler_p.h>
#include <private/qqmlrefcount_p.h>
#include <QtCore/QStringList>
+#include <QtCore/QDateTime>
#include <QStack>
#ifndef V4_BOOTSTRAP
#include <qqmlerror.h>
@@ -507,7 +507,6 @@ protected:
struct Result {
Reference result;
- QV4::IR::Expr *code;
const BytecodeGenerator::Label *iftrue;
const BytecodeGenerator::Label *iffalse;
Format format;
@@ -516,7 +515,6 @@ protected:
Result(const Reference &lrvalue)
: result(lrvalue)
- , code(nullptr)
, iftrue(nullptr)
, iffalse(nullptr)
, format(ex)
@@ -525,8 +523,7 @@ protected:
}
explicit Result(Format requested = ex)
- : code(0)
- , iftrue(0)
+ : iftrue(0)
, iffalse(0)
, format(ex)
, requested(requested) {}
@@ -534,17 +531,13 @@ protected:
explicit Result(const BytecodeGenerator::Label *iftrue,
const BytecodeGenerator::Label *iffalse,
bool trueBlockFollowsCondition)
- : code(0)
- , iftrue(iftrue)
+ : iftrue(iftrue)
, iffalse(iffalse)
, format(ex)
, requested(cx)
, trueBlockFollowsCondition(trueBlockFollowsCondition)
{}
- inline QV4::IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; }
- inline QV4::IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; }
-
bool accept(Format f)
{
if (requested == f) {
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 80bb835bbb..91bc5071ac 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qv4compileddata_p.h"
-#include "qv4jsir_p.h"
#include <private/qv4value_p.h>
#ifndef V4_BOOTSTRAP
#include <private/qv4engine_p.h>
@@ -133,14 +132,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::Value));
for (uint i = 0; i < data->regexpTableSize; ++i) {
const CompiledData::RegExp *re = data->regexpAt(i);
- int flags = 0;
- if (re->flags & CompiledData::RegExp::RegExp_Global)
- flags |= IR::RegExp::RegExp_Global;
- if (re->flags & CompiledData::RegExp::RegExp_IgnoreCase)
- flags |= IR::RegExp::RegExp_IgnoreCase;
- if (re->flags & CompiledData::RegExp::RegExp_Multiline)
- flags |= IR::RegExp::RegExp_Multiline;
- QV4::Heap::RegExpObject *ro = engine->newRegExpObject(data->stringAt(re->stringIndex), flags);
+ QV4::Heap::RegExpObject *ro = engine->newRegExpObject(data->stringAt(re->stringIndex), re->flags);
runtimeRegularExpressions[i] = ro;
}
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 2985e39765..25902df9ec 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -168,23 +168,6 @@ uint QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex)
return lookups.size() - 1;
}
-int QV4::Compiler::JSUnitGenerator::registerRegExp(QV4::IR::RegExp *regexp)
-{
- CompiledData::RegExp re;
- re.stringIndex = registerString(*regexp->value);
-
- re.flags = 0;
- if (regexp->flags & QV4::IR::RegExp::RegExp_Global)
- re.flags |= CompiledData::RegExp::RegExp_Global;
- if (regexp->flags & QV4::IR::RegExp::RegExp_IgnoreCase)
- re.flags |= CompiledData::RegExp::RegExp_IgnoreCase;
- if (regexp->flags & QV4::IR::RegExp::RegExp_Multiline)
- re.flags |= CompiledData::RegExp::RegExp_Multiline;
-
- regexps.append(re);
- return regexps.size() - 1;
-}
-
int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *regexp)
{
CompiledData::RegExp re;
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 0ed03a4c2c..50aa763452 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -51,8 +51,12 @@
//
#include <QtCore/qstring.h>
-#include "qv4jsir_p.h"
+#include <QtCore/qhash.h>
+#include <QtCore/qstringlist.h>
#include <private/qjson_p.h>
+#include <private/qv4global_p.h>
+#include <private/qqmljsastfwd_p.h>
+#include <private/qv4compileddata_p.h>
QT_BEGIN_NAMESPACE
@@ -110,7 +114,6 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
uint registerIndexedGetterLookup();
uint registerIndexedSetterLookup();
- int registerRegExp(IR::RegExp *regexp);
int registerRegExp(QQmlJS::AST::RegExpLiteral *regexp);
int registerConstant(ReturnedValue v);
diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp
deleted file mode 100644
index addeb506ee..0000000000
--- a/src/qml/compiler/qv4isel_moth.cpp
+++ /dev/null
@@ -1,1451 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4isel_util_p.h"
-#include "qv4isel_moth_p.h"
-#include "qv4ssa_p.h"
-#include <private/qv4compileddata_p.h>
-#include <wtf/MathExtras.h>
-
-#if !defined(V4_BOOTSTRAP)
-#include "qv4vme_moth_p.h"
-#include <private/qv4function_p.h>
-#endif
-
-#undef USE_TYPE_INFO
-
-using namespace QV4;
-using namespace QV4::Moth;
-
-namespace {
-
-inline QV4::Runtime::RuntimeMethods aluOpFunction(IR::AluOp op)
-{
- switch (op) {
- case IR::OpInvalid:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpIfTrue:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpNot:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpUMinus:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpUPlus:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpCompl:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpBitAnd:
- return QV4::Runtime::bitAnd;
- case IR::OpBitOr:
- return QV4::Runtime::bitOr;
- case IR::OpBitXor:
- return QV4::Runtime::bitXor;
- case IR::OpAdd:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpSub:
- return QV4::Runtime::sub;
- case IR::OpMul:
- return QV4::Runtime::mul;
- case IR::OpDiv:
- return QV4::Runtime::div;
- case IR::OpMod:
- return QV4::Runtime::mod;
- case IR::OpLShift:
- return QV4::Runtime::shl;
- case IR::OpRShift:
- return QV4::Runtime::shr;
- case IR::OpURShift:
- return QV4::Runtime::ushr;
- case IR::OpGt:
- return QV4::Runtime::greaterThan;
- case IR::OpLt:
- return QV4::Runtime::lessThan;
- case IR::OpGe:
- return QV4::Runtime::greaterEqual;
- case IR::OpLe:
- return QV4::Runtime::lessEqual;
- case IR::OpEqual:
- return QV4::Runtime::equal;
- case IR::OpNotEqual:
- return QV4::Runtime::notEqual;
- case IR::OpStrictEqual:
- return QV4::Runtime::strictEqual;
- case IR::OpStrictNotEqual:
- return QV4::Runtime::strictNotEqual;
- case IR::OpInstanceof:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpIn:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpAnd:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpOr:
- return QV4::Runtime::InvalidRuntimeMethod;
- default:
- Q_ASSERT(!"Unknown AluOp");
- return QV4::Runtime::InvalidRuntimeMethod;
- }
-};
-
-inline bool isNumberType(IR::Expr *e)
-{
- switch (e->type) {
- case IR::SInt32Type:
- case IR::UInt32Type:
- case IR::DoubleType:
- return true;
- default:
- return false;
- }
-}
-
-inline bool isIntegerType(IR::Expr *e)
-{
- switch (e->type) {
- case IR::SInt32Type:
- case IR::UInt32Type:
- return true;
- default:
- return false;
- }
-}
-
-inline bool isBoolType(IR::Expr *e)
-{
- return (e->type == IR::BoolType);
-}
-
-} // anonymous namespace
-
-InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
- : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory)
- , qmlEngine(qmlEngine)
- , _block(0)
- , _codeStart(0)
- , _codeNext(0)
- , _codeEnd(0)
- , _currentStatement(0)
- , compilationUnit(new CompilationUnit)
-{
- setUseTypeInference(false);
-}
-
-InstructionSelection::~InstructionSelection()
-{
-}
-
-void InstructionSelection::run(int functionIndex)
-{
- IR::Function *function = irModule->functions[functionIndex];
- if (!function->code.isEmpty())
- return;
-
- IR::BasicBlock *block = 0, *nextBlock = 0;
-
- QHash<IR::BasicBlock *, QVector<ptrdiff_t> > patches;
- QHash<IR::BasicBlock *, ptrdiff_t> addrs;
-
- int codeSize = 4096;
- uchar *codeStart = new uchar[codeSize];
- memset(codeStart, 0, codeSize);
- uchar *codeNext = codeStart;
- uchar *codeEnd = codeStart + codeSize;
-
- qSwap(_function, function);
- qSwap(block, _block);
- qSwap(nextBlock, _nextBlock);
- qSwap(patches, _patches);
- qSwap(addrs, _addrs);
- qSwap(codeStart, _codeStart);
- qSwap(codeNext, _codeNext);
- qSwap(codeEnd, _codeEnd);
-
- IR::Optimizer opt(_function);
- opt.run(qmlEngine, useTypeInference, /*peelLoops =*/ false);
- if (opt.isInSSA()) {
- static const bool doStackSlotAllocation =
- qEnvironmentVariableIsEmpty("QV4_NO_INTERPRETER_STACK_SLOT_ALLOCATION");
-
- if (doStackSlotAllocation) {
- IR::AllocateStackSlots(opt.lifeTimeIntervals()).forFunction(_function);
- } else {
- opt.convertOutOfSSA();
- ConvertTemps().toStackSlots(_function);
- }
- opt.showMeTheCode(_function, "After stack slot allocation");
- } else {
- ConvertTemps().toStackSlots(_function);
- }
-
- BitVector removableJumps = opt.calculateOptionalJumps();
- qSwap(_removableJumps, removableJumps);
-
- IR::Stmt *cs = 0;
- qSwap(_currentStatement, cs);
-
- int locals = frameSize();
- Q_ASSERT(locals >= 0);
-
- IR::BasicBlock *exceptionHandler = 0;
-
- Instruction::InitStackFrame init;
- init.value = quint32(locals);
- addInstruction(init);
-
- currentLine = 0;
- const QVector<IR::BasicBlock *> &basicBlocks = _function->basicBlocks();
- for (int i = 0, ei = basicBlocks.size(); i != ei; ++i) {
- blockNeedsDebugInstruction = irModule->debugMode;
- _block = basicBlocks[i];
- _nextBlock = (i < ei - 1) ? basicBlocks[i + 1] : 0;
- _addrs.insert(_block, _codeNext - _codeStart);
-
- if (_block->catchBlock != exceptionHandler) {
- Instruction::SetExceptionHandler set;
- set.offset = 0;
- if (_block->catchBlock) {
- ptrdiff_t loc = addInstruction(set) + (((const char *)&set.offset) - ((const char *)&set));
- _patches[_block->catchBlock].append(loc);
- } else {
- addInstruction(set);
- }
- exceptionHandler = _block->catchBlock;
- } else if (_block->catchBlock == nullptr && _block->index() != 0 && _block->in.isEmpty()) {
- exceptionHandler = nullptr;
- Instruction::SetExceptionHandler set;
- set.offset = 0;
- addInstruction(set);
- }
-
- for (IR::Stmt *s : _block->statements()) {
- _currentStatement = s;
-
- if (s->location.isValid()) {
- if (s->location.startLine != currentLine) {
- blockNeedsDebugInstruction = false;
- currentLine = s->location.startLine;
-#ifndef QT_NO_QML_DEBUGGER
- if (irModule->debugMode) {
- Instruction::Debug debug;
- debug.lineNumber = currentLine;
- addInstruction(debug);
- } else {
- Instruction::Line line;
- line.lineNumber = currentLine;
- addInstruction(line);
- }
-#endif
- }
- }
-
- visit(s);
- }
- }
-
- // TODO: patch stack size (the push instruction)
- patchJumpAddresses();
-
- _function->code = squeezeCode();
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
- if (showCode)
- QV4::Moth::dumpBytecode(_function->code);
-
- qSwap(_currentStatement, cs);
- qSwap(_removableJumps, removableJumps);
- qSwap(_function, function);
- qSwap(block, _block);
- qSwap(nextBlock, _nextBlock);
- qSwap(patches, _patches);
- qSwap(addrs, _addrs);
- qSwap(codeStart, _codeStart);
- qSwap(codeNext, _codeNext);
- qSwap(codeEnd, _codeEnd);
-
- delete[] codeStart;
-}
-
-QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backendCompileStep()
-{
- compilationUnit->codeRefs.resize(irModule->functions.size());
- int i = 0;
- for (IR::Function *irFunction : qAsConst(irModule->functions))
- compilationUnit->codeRefs[i++] = irFunction->code;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> result;
- result.adopt(compilationUnit.take());
- return result;
-}
-
-void InstructionSelection::callValue(IR::Expr *value, IR::ExprList * /*args*/, IR::Expr *result)
-{
- Instruction::CallValue call;
-// prepareCallArgs(args, call.argc);
-// call.callData = callDataStart();
- call.dest = getParam(value);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
- Instruction::CallScopeObjectProperty call;
- call.base = getParam(base);
- call.index = propertyIndex;
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- Instruction::CallContextObjectProperty call;
- call.base = getParam(base);
- call.index = propertyIndex;
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- } else {
- Q_ASSERT(false);
- }
-}
-
-void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args,
- IR::Expr *result)
-{
- if (useFastLookups) {
- Instruction::CallPropertyLookup call;
- call.base = getParam(base);
- call.lookupIndex = registerGetterLookup(name);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- } else {
- // call the property on the loaded base
- Instruction::CallProperty call;
- call.base = getParam(base);
- call.name = registerString(name);
-// prepareCallArgs(args, call.argc);
-// call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- }
-}
-
-void InstructionSelection::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList * /*args*/,
- IR::Expr *result)
-{
- // call the property on the loaded base
- Instruction::CallElement call;
- call.base = getParam(base);
- call.index = getParam(index);
-// prepareCallArgs(args, call.argc);
-// call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::convertType(IR::Expr *source, IR::Expr *target)
-{
- // FIXME: do something more useful with this info
- if (target->type & IR::NumberType && !(source->type & IR::NumberType))
- unop(IR::OpUPlus, source, target);
- else
- copyValue(source, target);
-}
-
-void InstructionSelection::constructActivationProperty(IR::Name *func,
- IR::ExprList *args,
- IR::Expr *target)
-{
- if (useFastLookups && func->global) {
- Instruction::ConstructGlobalLookup call;
- call.index = registerGlobalGetterLookup(*func->id);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(target);
- addInstruction(call);
- return;
- }
- Instruction::CreateActivationProperty create;
- create.name = registerString(*func->id);
- prepareCallArgs(args, create.argc);
- create.callData = callDataStart();
- create.result = getResultParam(target);
- addInstruction(create);
-}
-
-void InstructionSelection::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *target)
-{
- if (useFastLookups) {
- Instruction::ConstructPropertyLookup call;
- call.base = getParam(base);
- call.index = registerGetterLookup(name);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(target);
- addInstruction(call);
- return;
- }
- Instruction::CreateProperty create;
- create.base = getParam(base);
- create.name = registerString(name);
- prepareCallArgs(args, create.argc);
- create.callData = callDataStart();
- create.result = getResultParam(target);
- addInstruction(create);
-}
-
-void InstructionSelection::constructValue(IR::Expr *value, IR::ExprList * /*args*/, IR::Expr *target)
-{
- Instruction::CreateValue create;
- create.func = getParam(value);
-// prepareCallArgs(args, create.argc);
- create.callData = callDataStart();
- create.result = getResultParam(target);
- addInstruction(create);
-}
-
-void InstructionSelection::loadThisObject(IR::Expr *e)
-{
- Instruction::LoadThis load;
- load.result = getResultParam(e);
- addInstruction(load);
-}
-
-void InstructionSelection::loadQmlContext(IR::Expr *e)
-{
- Instruction::LoadQmlContext load;
- load.result = getResultParam(e);
- addInstruction(load);
-}
-
-void InstructionSelection::loadQmlImportedScripts(IR::Expr *e)
-{
- Instruction::LoadQmlImportedScripts load;
- load.result = getResultParam(e);
- addInstruction(load);
-}
-
-void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *e)
-{
- Instruction::LoadQmlSingleton load;
- load.result = getResultParam(e);
- load.name = registerString(name);
- addInstruction(load);
-}
-
-void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *e)
-{
- Q_UNUSED(sourceConst);
- Q_UNUSED(e);
-// Q_ASSERT(sourceConst);
-
-// Instruction::MoveConst move;
-// move.source = convertToValue(sourceConst).asReturnedValue();
-// move.result = getResultParam(e);
-// addInstruction(move);
-}
-
-void InstructionSelection::loadString(const QString &str, IR::Expr *target)
-{
- Instruction::LoadRuntimeString load;
- load.stringId = registerString(str);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target)
-{
- Instruction::LoadRegExp load;
- load.regExpId = registerRegExp(sourceRegexp);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr *target)
-{
- if (useFastLookups && name->global) {
- Instruction::GetGlobalLookup load;
- load.index = registerGlobalGetterLookup(*name->id);
- load.result = getResultParam(target);
- addInstruction(load);
- return;
- }
- Instruction::LoadName load;
- load.name = registerString(*name->id);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName)
-{
- Instruction::StoreName store;
- store.source = getParam(source);
- store.name = registerString(targetName);
- addInstruction(store);
-}
-
-void InstructionSelection::initClosure(IR::Closure *closure, IR::Expr *target)
-{
- int id = closure->value;
- Instruction::LoadClosure load;
- load.value = id;
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::Expr *target)
-{
- if (useFastLookups) {
- Instruction::GetLookup load;
- load.base = getParam(base);
- load.index = registerGetterLookup(name);
- load.result = getResultParam(target);
- addInstruction(load);
- return;
- }
- Instruction::LoadProperty load;
- load.base = getParam(base);
- load.name = registerString(name);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase,
- const QString &targetName)
-{
- if (useFastLookups) {
- Instruction::SetLookup store;
- store.base = getParam(targetBase);
- store.index = registerSetterLookup(targetName);
- store.source = getParam(source);
- addInstruction(store);
- return;
- }
- Instruction::StoreProperty store;
- store.base = getParam(targetBase);
- store.name = registerString(targetName);
- store.source = getParam(source);
- addInstruction(store);
-}
-
-void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
- Instruction::StoreScopeObjectProperty store;
- store.base = getParam(targetBase);
- store.propertyIndex = propertyIndex;
- store.source = getParam(source);
- addInstruction(store);
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- Instruction::StoreContextObjectProperty store;
- store.base = getParam(targetBase);
- store.propertyIndex = propertyIndex;
- store.source = getParam(source);
- addInstruction(store);
- } else {
- Q_ASSERT(false);
- }
-}
-
-void InstructionSelection::setQObjectProperty(IR::Expr * /*source*/, IR::Expr * /*targetBase*/, int /*propertyIndex*/)
-{
-// Instruction::StoreQObjectProperty store;
-// store.base = getParam(targetBase);
-// store.propertyIndex = propertyIndex;
-// store.source = getParam(source);
-// addInstruction(store);
-}
-
-void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool /*captureRequired*/, IR::Expr *target)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
-// Instruction::LoadScopeObjectProperty load;
-// load.base = getParam(source);
-// load.propertyIndex = index;
-// load.captureRequired = captureRequired;
-// load.result = getResultParam(target);
-// addInstruction(load);
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- Instruction::LoadContextObjectProperty load;
- load.base = getParam(source);
- load.propertyIndex = index;
-// load.captureRequired = captureRequired;
- load.result = getResultParam(target);
- addInstruction(load);
- } else if (kind == IR::Member::MemberOfIdObjectsArray) {
- Instruction::LoadIdObject load;
- load.base = getParam(source);
- load.index = index;
- load.result = getResultParam(target);
- addInstruction(load);
- } else {
- Q_ASSERT(false);
- }
-}
-
-void InstructionSelection::getQObjectProperty(IR::Expr * /*base*/, int /*propertyIndex*/, bool /*captureRequired*/, bool /*isSingletonProperty*/, int /*attachedPropertiesId*/, IR::Expr * /*target*/)
-{
-// if (attachedPropertiesId != 0) {
-// Instruction::LoadAttachedQObjectProperty load;
-// load.propertyIndex = propertyIndex;
-// load.result = getResultParam(target);
-// load.attachedPropertiesId = attachedPropertiesId;
-// addInstruction(load);
-// } else if (isSingletonProperty) {
-// Instruction::LoadSingletonQObjectProperty load;
-// load.base = getParam(base);
-// load.propertyIndex = propertyIndex;
-// load.result = getResultParam(target);
-// load.captureRequired = captureRequired;
-// addInstruction(load);
-// } else {
-// Instruction::LoadQObjectProperty load;
-// load.base = getParam(base);
-// load.propertyIndex = propertyIndex;
-// load.result = getResultParam(target);
-// load.captureRequired = captureRequired;
-// addInstruction(load);
-// }
-}
-
-void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target)
-{
- if (0 && useFastLookups) {
- Instruction::LoadElementLookup load;
- load.lookup = registerIndexedGetterLookup();
- load.base = getParam(base);
- load.index = getParam(index);
- load.result = getResultParam(target);
- addInstruction(load);
- return;
- }
- Instruction::LoadElement load;
- load.base = getParam(base);
- load.index = getParam(index);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase,
- IR::Expr *targetIndex)
-{
- if (0 && useFastLookups) {
- Instruction::StoreElementLookup store;
- store.lookup = registerIndexedSetterLookup();
- store.base = getParam(targetBase);
- store.index = getParam(targetIndex);
- store.source = getParam(source);
- addInstruction(store);
- return;
- }
- Instruction::StoreElement store;
- store.base = getParam(targetBase);
- store.index = getParam(targetIndex);
- store.source = getParam(source);
- addInstruction(store);
-}
-
-void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target)
-{
- Instruction::Move move;
- move.source = getParam(source);
- move.result = getResultParam(target);
- if (move.source != move.result)
- addInstruction(move);
-}
-
-void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target)
-{
- Instruction::SwapTemps swap;
- swap.left = getParam(source);
- swap.right = getParam(target);
- addInstruction(swap);
-}
-
-void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target)
-{
- switch (oper) {
- case IR::OpIfTrue:
- Q_ASSERT(!"unreachable"); break;
- case IR::OpNot: {
- // ### enabling this fails in some cases, where apparently the value is not a bool at runtime
- if (0 && isBoolType(source)) {
- Instruction::UNotBool unot;
- unot.source = getParam(source);
- unot.result = getResultParam(target);
- addInstruction(unot);
- return;
- }
- Instruction::UNot unot;
- unot.source = getParam(source);
- unot.result = getResultParam(target);
- addInstruction(unot);
- return;
- }
- case IR::OpUMinus: {
- Instruction::UMinus uminus;
- uminus.source = getParam(source);
- uminus.result = getResultParam(target);
- addInstruction(uminus);
- return;
- }
- case IR::OpUPlus: {
- if (isNumberType(source)) {
- // use a move
- Instruction::Move move;
- move.source = getParam(source);
- move.result = getResultParam(target);
- if (move.source != move.result)
- addInstruction(move);
- return;
- }
- Instruction::UPlus uplus;
- uplus.source = getParam(source);
- uplus.result = getResultParam(target);
- addInstruction(uplus);
- return;
- }
- case IR::OpCompl: {
- // ### enabling this fails in some cases, where apparently the value is not a int at runtime
- if (0 && isIntegerType(source)) {
- Instruction::UComplInt unot;
- unot.source = getParam(source);
- unot.result = getResultParam(target);
- addInstruction(unot);
- return;
- }
- Instruction::UCompl ucompl;
- ucompl.source = getParam(source);
- ucompl.result = getResultParam(target);
- addInstruction(ucompl);
- return;
- }
- case IR::OpPreIncrement: {
- Instruction::PreIncrement inc;
- inc.source = getParam(source);
- inc.result = getResultParam(target);
- addInstruction(inc);
- return;
- }
- case IR::OpPreDecrement: {
- Instruction::PreDecrement dec;
- dec.source = getParam(source);
- dec.result = getResultParam(target);
- addInstruction(dec);
- return;
- }
- default: break;
- } // switch
-
- Q_ASSERT(!"unreachable");
-}
-
-void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target)
-{
- binopHelper(oper, leftSource, rightSource, target);
-}
-
-Param InstructionSelection::binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target)
-{
- if (oper == IR::OpAdd) {
- Instruction::Add add;
- add.lhs = getParam(leftSource);
- add.rhs = getParam(rightSource);
- add.result = getResultParam(target);
- addInstruction(add);
- return add.result;
- }
- if (oper == IR::OpSub) {
- Instruction::Sub sub;
- sub.lhs = getParam(leftSource);
- sub.rhs = getParam(rightSource);
- sub.result = getResultParam(target);
- addInstruction(sub);
- return sub.result;
- }
- if (oper == IR::OpMul) {
- Instruction::Mul mul;
- mul.lhs = getParam(leftSource);
- mul.rhs = getParam(rightSource);
- mul.result = getResultParam(target);
- addInstruction(mul);
- return mul.result;
- }
- if (oper == IR::OpBitAnd) {
- if (leftSource->asConst())
- qSwap(leftSource, rightSource);
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::BitAndConst bitAnd;
- bitAnd.lhs = getParam(leftSource);
- bitAnd.rhs = convertToValue(c).Value::toInt32();
- bitAnd.result = getResultParam(target);
- addInstruction(bitAnd);
- return bitAnd.result;
- }
- Instruction::BitAnd bitAnd;
- bitAnd.lhs = getParam(leftSource);
- bitAnd.rhs = getParam(rightSource);
- bitAnd.result = getResultParam(target);
- addInstruction(bitAnd);
- return bitAnd.result;
- }
- if (oper == IR::OpBitOr) {
- if (leftSource->asConst())
- qSwap(leftSource, rightSource);
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::BitOrConst bitOr;
- bitOr.lhs = getParam(leftSource);
- bitOr.rhs = convertToValue(c).Value::toInt32();
- bitOr.result = getResultParam(target);
- addInstruction(bitOr);
- return bitOr.result;
- }
- Instruction::BitOr bitOr;
- bitOr.lhs = getParam(leftSource);
- bitOr.rhs = getParam(rightSource);
- bitOr.result = getResultParam(target);
- addInstruction(bitOr);
- return bitOr.result;
- }
- if (oper == IR::OpBitXor) {
- if (leftSource->asConst())
- qSwap(leftSource, rightSource);
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::BitXorConst bitXor;
- bitXor.lhs = getParam(leftSource);
- bitXor.rhs = convertToValue(c).Value::toInt32();
- bitXor.result = getResultParam(target);
- addInstruction(bitXor);
- return bitXor.result;
- }
- Instruction::BitXor bitXor;
- bitXor.lhs = getParam(leftSource);
- bitXor.rhs = getParam(rightSource);
- bitXor.result = getResultParam(target);
- addInstruction(bitXor);
- return bitXor.result;
- }
- if (oper == IR::OpRShift) {
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::ShrConst shr;
- shr.lhs = getParam(leftSource);
- shr.rhs = convertToValue(c).Value::toInt32() & 0x1f;
- shr.result = getResultParam(target);
- addInstruction(shr);
- return shr.result;
- }
- Instruction::Shr shr;
- shr.lhs = getParam(leftSource);
- shr.rhs = getParam(rightSource);
- shr.result = getResultParam(target);
- addInstruction(shr);
- return shr.result;
- }
- if (oper == IR::OpLShift) {
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::ShlConst shl;
- shl.lhs = getParam(leftSource);
- shl.rhs = convertToValue(c).Value::toInt32() & 0x1f;
- shl.result = getResultParam(target);
- addInstruction(shl);
- return shl.result;
- }
- Instruction::Shl shl;
- shl.lhs = getParam(leftSource);
- shl.rhs = getParam(rightSource);
- shl.result = getResultParam(target);
- addInstruction(shl);
- return shl.result;
- }
-
- if (oper == IR::OpInstanceof || oper == IR::OpIn || oper == IR::OpAdd) {
- Instruction::BinopContext binop;
- if (oper == IR::OpInstanceof)
- binop.alu = QV4::Runtime::instanceof;
- else if (oper == IR::OpIn)
- binop.alu = QV4::Runtime::in;
- else
- binop.alu = QV4::Runtime::add;
- binop.lhs = getParam(leftSource);
- binop.rhs = getParam(rightSource);
- binop.result = getResultParam(target);
- Q_ASSERT(binop.alu != QV4::Runtime::InvalidRuntimeMethod);
- addInstruction(binop);
- return binop.result;
- } else {
- auto binopFunc = aluOpFunction(oper);
- Q_ASSERT(binopFunc != QV4::Runtime::InvalidRuntimeMethod);
- Instruction::Binop binop;
- binop.alu = binopFunc;
- binop.lhs = getParam(leftSource);
- binop.rhs = getParam(rightSource);
- binop.result = getResultParam(target);
- addInstruction(binop);
- return binop.result;
- }
-}
-
-void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 *args)
-{
- int argLocation = outgoingArgumentTempStart();
- argc = 0;
- if (args)
- *args = argLocation;
- if (e) {
- // We need to move all the temps into the function arg array
- Q_ASSERT(argLocation >= 0);
- while (e) {
- if (IR::Const *c = e->expr->asConst()) {
- Q_UNUSED(c);
-// Instruction::MoveConst move;
-// move.source = convertToValue(c).asReturnedValue();
-// move.result = Param::createTemp(argLocation);
-// addInstruction(move);
- } else {
- Instruction::Move move;
- move.source = getParam(e->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- }
- ++argLocation;
- ++argc;
- e = e->next;
- }
- }
-}
-
-void InstructionSelection::addDebugInstruction()
-{
-#ifndef QT_NO_QML_DEBUGGER
- if (blockNeedsDebugInstruction) {
- Instruction::Debug debug;
- debug.lineNumber = -int(currentLine);
- addInstruction(debug);
- }
-#endif
-}
-
-void InstructionSelection::visitJump(IR::Jump *s)
-{
- if (s->target == _nextBlock)
- return;
- if (_removableJumps.at(_block->index()))
- return;
-
- addDebugInstruction();
-
- Instruction::Jump jump;
- jump.offset = 0;
- ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
-
- _patches[s->target].append(loc);
-}
-
-void InstructionSelection::visitCJump(IR::CJump *s)
-{
- addDebugInstruction();
-
- Param condition;
- if (IR::Temp *t = s->cond->asTemp()) {
- condition = getResultParam(t);
- } else if (IR::Binop *b = s->cond->asBinop()) {
- condition = binopHelper(b->op, b->left, b->right, /*target*/0);
- } else {
- Q_UNIMPLEMENTED();
- }
-
- if (s->iftrue == _nextBlock) {
- Instruction::JumpNe jump;
- jump.offset = 0;
- jump.condition = condition;
- ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
- _patches[s->iffalse].append(falseLoc);
- } else {
- Instruction::JumpEq jump;
- jump.offset = 0;
- jump.condition = condition;
- ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
- _patches[s->iftrue].append(trueLoc);
-
- if (s->iffalse != _nextBlock) {
- Instruction::Jump jump;
- jump.offset = 0;
- ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
- _patches[s->iffalse].append(falseLoc);
- }
- }
-}
-
-void InstructionSelection::visitRet(IR::Ret *s)
-{
- // this is required so stepOut will always be guaranteed to stop in every stack frame
- addDebugInstruction();
-
- Instruction::Ret ret;
- ret.result = getParam(s->expr);
- addInstruction(ret);
-}
-
-void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList * /*args*/, IR::Expr *result)
-{
- if (useFastLookups && func->global) {
- Instruction::CallGlobalLookup call;
- call.index = registerGlobalGetterLookup(*func->id);
-// prepareCallArgs(args, call.argc);
-// call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- return;
- }
- Instruction::CallActivationProperty call;
- call.name = registerString(*func->id);
-// prepareCallArgs(args, call.argc);
-// call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
- Instruction::CallBuiltinTypeofScopeObjectProperty call;
- call.base = getParam(base);
- call.index = propertyIndex;
- call.result = getResultParam(result);
- addInstruction(call);
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- Instruction::CallBuiltinTypeofContextObjectProperty call;
- call.base = getParam(base);
- call.index = propertyIndex;
- call.result = getResultParam(result);
- addInstruction(call);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name,
- IR::Expr *result)
-{
- Instruction::CallBuiltinTypeofMember call;
- call.base = getParam(base);
- call.member = registerString(name);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index,
- IR::Expr *result)
-{
- Instruction::CallBuiltinTypeofSubscript call;
- call.base = getParam(base);
- call.index = getParam(index);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Expr *result)
-{
- Instruction::CallBuiltinTypeofName call;
- call.name = registerString(name);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result)
-{
- Instruction::CallBuiltinTypeofValue call;
- call.value = getParam(value);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result)
-{
- Instruction::CallBuiltinDeleteMember call;
- call.base = getParam(base);
- call.member = registerString(name);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index,
- IR::Expr *result)
-{
- Instruction::CallBuiltinDeleteSubscript call;
- call.base = getParam(base);
- call.index = getParam(index);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Expr *result)
-{
- Instruction::CallBuiltinDeleteName call;
- call.name = registerString(name);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDeleteValue(IR::Expr *result)
-{
- Q_UNUSED(result);
-// Instruction::MoveConst move;
-// move.source = QV4::Encode(false);
-// move.result = getResultParam(result);
-// addInstruction(move);
-}
-
-void InstructionSelection::callBuiltinThrow(IR::Expr *arg)
-{
- Instruction::CallBuiltinThrow call;
- call.arg = getParam(arg);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinReThrow()
-{
- if (_block->catchBlock) {
- // jump to exception handler
- Instruction::Jump jump;
- jump.offset = 0;
- ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
-
- _patches[_block->catchBlock].append(loc);
- } else {
- Instruction::Ret ret;
- int idx = jsUnitGenerator()->registerConstant(QV4::Encode::undefined());
- ret.result = Param::createConstant(idx);
- addInstruction(ret);
- }
-}
-
-void InstructionSelection::callBuiltinUnwindException(IR::Expr *result)
-{
- Instruction::CallBuiltinUnwindException call;
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-
-void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName)
-{
- Instruction::CallBuiltinPushCatchScope call;
- call.name = registerString(exceptionName);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result)
-{
- Instruction::CallBuiltinForeachIteratorObject call;
- call.arg = getParam(arg);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result)
-{
- Instruction::CallBuiltinForeachNextPropertyName call;
- call.arg = getParam(arg);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg)
-{
- Instruction::CallBuiltinPushScope call;
- call.arg = getParam(arg);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinPopScope()
-{
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_GCC("-Wuninitialized")
- Instruction::CallBuiltinPopScope call;
- addInstruction(call);
- QT_WARNING_POP
-}
-
-void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name)
-{
- Instruction::CallBuiltinDeclareVar call;
- call.isDeletable = deletable;
- call.varName = registerString(name);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args)
-{
- Instruction::CallBuiltinDefineArray call;
- prepareCallArgs(args, call.argc, &call.args);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray)
-{
- int argLocation = outgoingArgumentTempStart();
-
- QVector<Compiler::JSUnitGenerator::MemberInfo> members;
-
- // Process key/value pairs first
- IR::ExprList *it = keyValuePairs;
- for (int i = 0; i < keyValuePairCount; ++i, it = it->next) {
- QString key = *it->expr->asName()->id;
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- members.append({ key, !isData });
- it = it->next;
-
- if (IR::Const *c = it->expr->asConst()) {
- Q_UNUSED(c);
-// Instruction::MoveConst move;
-// move.source = convertToValue(c).asReturnedValue();
-// move.result = Param::createTemp(argLocation);
-// addInstruction(move);
- } else {
- Instruction::Move move;
- move.source = getParam(it->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- }
- ++argLocation;
-
- if (!isData) {
- it = it->next;
-
- Instruction::Move move;
- move.source = getParam(it->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- ++argLocation;
- }
- }
-
- const int classId = registerJSClass(members);
-
- // Process array values
- uint arrayValueCount = 0;
- it = arrayEntries;
- while (it) {
- IR::Const *index = it->expr->asConst();
- Q_UNUSED(index);
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- it = it->next;
-
- if (!isData) {
- it = it->next; // getter
- it = it->next; // setter
- continue;
- }
-
- ++arrayValueCount;
-
-// Instruction::MoveConst indexMove;
-// indexMove.source = convertToValue(index).asReturnedValue();
-// indexMove.result = Param::createTemp(argLocation);
-// addInstruction(indexMove);
- ++argLocation;
-
- Instruction::Move move;
- move.source = getParam(it->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- ++argLocation;
- it = it->next;
- }
-
- // Process array getter/setter pairs
- uint arrayGetterSetterCount = 0;
- it = arrayEntries;
- while (it) {
- IR::Const *index = it->expr->asConst();
- Q_UNUSED(index);
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- it = it->next;
-
- if (isData) {
- it = it->next; // value
- continue;
- }
-
- ++arrayGetterSetterCount;
-
-// Instruction::MoveConst indexMove;
-// indexMove.source = convertToValue(index).asReturnedValue();
-// indexMove.result = Param::createTemp(argLocation);
-// addInstruction(indexMove);
- ++argLocation;
-
- // getter
- Instruction::Move moveGetter;
- moveGetter.source = getParam(it->expr);
- moveGetter.result = Param::createTemp(argLocation);
- addInstruction(moveGetter);
- ++argLocation;
- it = it->next;
-
- // setter
- Instruction::Move moveSetter;
- moveSetter.source = getParam(it->expr);
- moveSetter.result = Param::createTemp(argLocation);
- addInstruction(moveSetter);
- ++argLocation;
- it = it->next;
- }
-
- Instruction::CallBuiltinDefineObjectLiteral call;
- call.internalClassId = classId;
- call.arrayValueCount = arrayValueCount;
- call.arrayGetterSetterCountAndFlags = arrayGetterSetterCount | (needSparseArray << 30);
- call.args = outgoingArgumentTempStart();
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result)
-{
- Instruction::CallBuiltinSetupArgumentsObject call;
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-
-void QV4::Moth::InstructionSelection::callBuiltinConvertThisToObject()
-{
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_GCC("-Wuninitialized")
- Instruction::CallBuiltinConvertThisToObject call;
- addInstruction(call);
- QT_WARNING_POP
-}
-
-ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr)
-{
- instr.common.instructionType = type;
-
- int instructionSize = Instr::size(type);
- if (_codeEnd - _codeNext < instructionSize) {
- int currSize = _codeEnd - _codeStart;
- uchar *newCode = new uchar[currSize * 2];
- ::memset(newCode + currSize, 0, currSize);
- ::memcpy(newCode, _codeStart, currSize);
- _codeNext = _codeNext - _codeStart + newCode;
- delete[] _codeStart;
- _codeStart = newCode;
- _codeEnd = _codeStart + currSize * 2;
- }
-
- ::memcpy(_codeNext, reinterpret_cast<const char *>(&instr), instructionSize);
- ptrdiff_t ptrOffset = _codeNext - _codeStart;
- _codeNext += instructionSize;
-
- return ptrOffset;
-}
-
-void InstructionSelection::patchJumpAddresses()
-{
- typedef QHash<IR::BasicBlock *, QVector<ptrdiff_t> >::ConstIterator PatchIt;
- for (PatchIt i = _patches.cbegin(), ei = _patches.cend(); i != ei; ++i) {
- Q_ASSERT(_addrs.contains(i.key()));
- ptrdiff_t target = _addrs.value(i.key());
-
- const QVector<ptrdiff_t> &patchList = i.value();
- for (int ii = 0, eii = patchList.count(); ii < eii; ++ii) {
- ptrdiff_t patch = patchList.at(ii);
-
- *((ptrdiff_t *)(_codeStart + patch)) = target - patch;
- }
- }
-
- _patches.clear();
- _addrs.clear();
-}
-
-QByteArray InstructionSelection::squeezeCode() const
-{
- int codeSize = _codeNext - _codeStart;
- QByteArray squeezed;
- squeezed.resize(codeSize);
- ::memcpy(squeezed.data(), _codeStart, codeSize);
- return squeezed;
-}
-
-Param InstructionSelection::getParam(IR::Expr *e) {
- Q_ASSERT(e);
-
- if (IR::Const *c = e->asConst()) {
- int idx = jsUnitGenerator()->registerConstant(convertToValue(c).asReturnedValue());
- return Param::createConstant(idx);
- } else if (IR::Temp *t = e->asTemp()) {
- switch (t->kind) {
- case IR::Temp::StackSlot:
- return Param::createTemp(t->index);
- default:
- Q_UNREACHABLE();
- return Param();
- }
- } else if (IR::ArgLocal *al = e->asArgLocal()) {
- switch (al->kind) {
- case IR::ArgLocal::Formal:
- case IR::ArgLocal::ScopedFormal: return Param::createArgument(al->index, al->scope);
- case IR::ArgLocal::Local: return Param::createLocal(al->index);
- case IR::ArgLocal::ScopedLocal: return Param::createScopedLocal(al->index, al->scope);
- default:
- Q_UNREACHABLE();
- return Param();
- }
- } else {
- Q_UNIMPLEMENTED();
- return Param();
- }
-}
-
-QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading()
-{
- QQmlRefPointer<CompiledData::CompilationUnit> result;
- result.adopt(new Moth::CompilationUnit);
- return result;
-}
diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h
deleted file mode 100644
index d8b33e1ab1..0000000000
--- a/src/qml/compiler/qv4isel_moth_p.h
+++ /dev/null
@@ -1,218 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4ISEL_MOTH_P_H
-#define QV4ISEL_MOTH_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <private/qv4global_p.h>
-#include <private/qv4isel_p.h>
-#include <private/qv4isel_util_p.h>
-#include <private/qv4util_p.h>
-#include <private/qv4jsir_p.h>
-#include <private/qv4value_p.h>
-#include "qv4instr_moth_p.h"
-#include <private/qv4compilationunit_moth_p.h>
-
-#if !defined(V4_BOOTSTRAP)
-QT_REQUIRE_CONFIG(qml_interpreter);
-#endif
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-namespace Moth {
-
-class Q_QML_EXPORT InstructionSelection:
- public IR::IRDecoder,
- public EvalInstructionSelection
-{
-public:
- InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
- ~InstructionSelection();
-
- void run(int functionIndex) override;
-
-protected:
- QQmlRefPointer<CompiledData::CompilationUnit> backendCompileStep() override;
-
- void visitJump(IR::Jump *) override;
- void visitCJump(IR::CJump *) override;
- void visitRet(IR::Ret *) override;
-
- void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) override;
- void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) override;
- void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) override;
- void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) override;
- void callBuiltinTypeofName(const QString &name, IR::Expr *result) override;
- void callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) override;
- void callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) override;
- void callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) override;
- void callBuiltinDeleteName(const QString &name, IR::Expr *result) override;
- void callBuiltinDeleteValue(IR::Expr *result) override;
- void callBuiltinThrow(IR::Expr *arg) override;
- void callBuiltinReThrow() override;
- void callBuiltinUnwindException(IR::Expr *) override;
- void callBuiltinPushCatchScope(const QString &exceptionName) override;
- void callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) override;
- void callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) override;
- void callBuiltinPushWithScope(IR::Expr *arg) override;
- void callBuiltinPopScope() override;
- void callBuiltinDeclareVar(bool deletable, const QString &name) override;
- void callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) override;
- void callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) override;
- void callBuiltinSetupArgumentObject(IR::Expr *result) override;
- void callBuiltinConvertThisToObject() override;
- void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override;
- void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) override;
- void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) override;
- void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) override;
- void convertType(IR::Expr *source, IR::Expr *target) override;
- void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) override;
- void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) override;
- void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override;
- void loadThisObject(IR::Expr *e) override;
- void loadQmlContext(IR::Expr *e) override;
- void loadQmlImportedScripts(IR::Expr *e) override;
- void loadQmlSingleton(const QString &name, IR::Expr *e) override;
- void loadConst(IR::Const *sourceConst, IR::Expr *e) override;
- void loadString(const QString &str, IR::Expr *target) override;
- void loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) override;
- void getActivationProperty(const IR::Name *name, IR::Expr *target) override;
- void setActivationProperty(IR::Expr *source, const QString &targetName) override;
- void initClosure(IR::Closure *closure, IR::Expr *target) override;
- void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) override;
- void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) override;
- void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) override;
- void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) override;
- void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) override;
- void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) override;
- void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) override;
- void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) override;
- void copyValue(IR::Expr *source, IR::Expr *target) override;
- void swapValues(IR::Expr *source, IR::Expr *target) override;
- void unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) override;
- void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) override;
-
-private:
- Param binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target);
-
- Param getParam(IR::Expr *e);
-
- Param getResultParam(IR::Expr *result)
- {
- if (result)
- return getParam(result);
- else
- return Param::createTemp(scratchTempIndex());
- }
-
- void simpleMove(IR::Move *);
- void prepareCallArgs(IR::ExprList *, quint32 &, quint32 * = 0);
-
- int scratchTempIndex() const { return _function->tempCount; }
- int callDataStart() const { return scratchTempIndex() + 1; }
- int outgoingArgumentTempStart() const { return callDataStart() + offsetof(QV4::CallData, args)/sizeof(QV4::Value); }
- int frameSize() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; }
-
- template <int Instr>
- inline ptrdiff_t addInstruction(const InstrData<Instr> &data);
- inline void addDebugInstruction();
-
- ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr);
- void patchJumpAddresses();
- QByteArray squeezeCode() const;
-
- QQmlEnginePrivate *qmlEngine;
-
- bool blockNeedsDebugInstruction;
- uint currentLine;
- IR::BasicBlock *_block;
- IR::BasicBlock *_nextBlock;
-
- QHash<IR::BasicBlock *, QVector<ptrdiff_t> > _patches;
- QHash<IR::BasicBlock *, ptrdiff_t> _addrs;
-
- uchar *_codeStart;
- uchar *_codeNext;
- uchar *_codeEnd;
-
- BitVector _removableJumps;
- IR::Stmt *_currentStatement;
-
- QScopedPointer<CompilationUnit> compilationUnit;
-};
-
-class Q_QML_EXPORT ISelFactory: public EvalISelFactory
-{
-public:
- ISelFactory() : EvalISelFactory(QStringLiteral("moth")) {}
- virtual ~ISelFactory() {}
- EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL
- { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); }
- bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL
- { return false; }
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE;
-
-};
-
-template<int InstrT>
-ptrdiff_t InstructionSelection::addInstruction(const InstrData<InstrT> &data)
-{
- Instr genericInstr;
- InstrMeta<InstrT>::setDataNoCommon(genericInstr, data);
- return addInstructionHelper(static_cast<Instr::Type>(InstrT), genericInstr);
-}
-
-} // namespace Moth
-} // namespace QV4
-
-QT_END_NAMESPACE
-
-#endif // QV4ISEL_MOTH_P_H
diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp
deleted file mode 100644
index efcfb9bd77..0000000000
--- a/src/qml/compiler/qv4isel_p.cpp
+++ /dev/null
@@ -1,446 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtCore/QDebug>
-#include <QtCore/QBuffer>
-#include "qv4jsir_p.h"
-#include "qv4isel_p.h"
-#include "qv4isel_util_p.h"
-#include <private/qv4value_p.h>
-#ifndef V4_BOOTSTRAP
-#include <private/qqmlpropertycache_p.h>
-#endif
-
-#include <QString>
-
-using namespace QV4;
-using namespace QV4::IR;
-
-EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
- : useFastLookups(true)
- , useTypeInference(true)
- , executableAllocator(execAllocator)
- , irModule(module)
-{
- if (!jsGenerator) {
- jsGenerator = new QV4::Compiler::JSUnitGenerator(module);
- ownJSGenerator.reset(jsGenerator);
- }
- this->jsGenerator = jsGenerator;
-#ifndef V4_BOOTSTRAP
- Q_ASSERT(execAllocator);
-#endif
- Q_ASSERT(module);
- jsGenerator->codeGeneratorName = iselFactory->codeGeneratorName;
-}
-
-EvalInstructionSelection::~EvalInstructionSelection()
-{}
-
-EvalISelFactory::~EvalISelFactory()
-{}
-
-QQmlRefPointer<CompiledData::CompilationUnit> EvalInstructionSelection::compile(bool generateUnitData)
-{
- for (int i = 0; i < irModule->functions.size(); ++i)
- run(i);
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = backendCompileStep();
- if (generateUnitData)
- unit->data = jsGenerator->generateUnit();
- return unit;
-}
-
-void IRDecoder::visitMove(IR::Move *s)
-{
- if (IR::Name *n = s->target->asName()) {
- if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) {
- setActivationProperty(s->source, *n->id);
- return;
- }
- } else if (s->target->asTemp() || s->target->asArgLocal()) {
- if (IR::Name *n = s->source->asName()) {
- if (n->id && *n->id == QLatin1String("this")) // TODO: `this' should be a builtin.
- loadThisObject(s->target);
- else if (n->builtin == IR::Name::builtin_qml_context)
- loadQmlContext(s->target);
- else if (n->builtin == IR::Name::builtin_qml_imported_scripts_object)
- loadQmlImportedScripts(s->target);
- else if (n->qmlSingleton)
- loadQmlSingleton(*n->id, s->target);
- else
- getActivationProperty(n, s->target);
- return;
- } else if (IR::Const *c = s->source->asConst()) {
- loadConst(c, s->target);
- return;
- } else if (s->source->asTemp() || s->source->asArgLocal()) {
- if (s->swap)
- swapValues(s->source, s->target);
- else
- copyValue(s->source, s->target);
- return;
- } else if (IR::String *str = s->source->asString()) {
- loadString(*str->value, s->target);
- return;
- } else if (IR::RegExp *re = s->source->asRegExp()) {
- loadRegexp(re, s->target);
- return;
- } else if (IR::Closure *clos = s->source->asClosure()) {
- initClosure(clos, s->target);
- return;
- } else if (IR::New *ctor = s->source->asNew()) {
- if (Name *func = ctor->base->asName()) {
- constructActivationProperty(func, ctor->args, s->target);
- return;
- } else if (IR::Member *member = ctor->base->asMember()) {
- constructProperty(member->base, *member->name, ctor->args, s->target);
- return;
- } else if (ctor->base->asTemp() || ctor->base->asArgLocal()) {
- constructValue(ctor->base, ctor->args, s->target);
- return;
- }
- } else if (IR::Member *m = s->source->asMember()) {
- if (m->property) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
-#else
- bool captureRequired = true;
-
- Q_ASSERT(m->kind != IR::Member::MemberOfEnum && m->kind != IR::Member::MemberOfIdObjectsArray);
- const int attachedPropertiesId = m->attachedPropertiesId;
- const bool isSingletonProperty = m->kind == IR::Member::MemberOfSingletonObject;
-
- if (_function && attachedPropertiesId == 0 && !m->property->isConstant() && _function->isQmlBinding) {
- if (m->kind == IR::Member::MemberOfQmlContextObject) {
- _function->contextObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex());
- captureRequired = false;
- } else if (m->kind == IR::Member::MemberOfQmlScopeObject) {
- _function->scopeObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex());
- captureRequired = false;
- }
- }
- if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) {
- getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex(), captureRequired, s->target);
- return;
- }
- getQObjectProperty(m->base, m->property->coreIndex(), captureRequired, isSingletonProperty, attachedPropertiesId, s->target);
-#endif // V4_BOOTSTRAP
- return;
- } else if (m->kind == IR::Member::MemberOfIdObjectsArray) {
- getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->idIndex, /*captureRequired*/false, s->target);
- return;
- } else if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) {
- getProperty(m->base, *m->name, s->target);
- return;
- }
- } else if (IR::Subscript *ss = s->source->asSubscript()) {
- getElement(ss->base, ss->index, s->target);
- return;
- } else if (IR::Unop *u = s->source->asUnop()) {
- unop(u->op, u->expr, s->target);
- return;
- } else if (IR::Binop *b = s->source->asBinop()) {
- binop(b->op, b->left, b->right, s->target);
- return;
- } else if (IR::Call *c = s->source->asCall()) {
- if (c->base->asName()) {
- callBuiltin(c, s->target);
- return;
- } else if (Member *member = c->base->asMember()) {
-#ifndef V4_BOOTSTRAP
- Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray);
- if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
- callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, s->target);
- return;
- }
-#endif
- callProperty(member->base, *member->name, c->args, s->target);
- return;
- } else if (Subscript *ss = c->base->asSubscript()) {
- callSubscript(ss->base, ss->index, c->args, s->target);
- return;
- } else if (c->base->asTemp() || c->base->asArgLocal() || c->base->asConst()) {
- callValue(c->base, c->args, s->target);
- return;
- }
- } else if (IR::Convert *c = s->source->asConvert()) {
- Q_ASSERT(c->expr->asTemp() || c->expr->asArgLocal());
- convertType(c->expr, s->target);
- return;
- }
- } else if (IR::Member *m = s->target->asMember()) {
- if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) {
- if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) {
- Q_ASSERT(m->kind != IR::Member::MemberOfEnum);
- Q_ASSERT(m->kind != IR::Member::MemberOfIdObjectsArray);
- const int attachedPropertiesId = m->attachedPropertiesId;
- if (m->property && attachedPropertiesId == 0) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
-#else
- if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) {
- setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex());
- return;
- }
- setQObjectProperty(s->source, m->base, m->property->coreIndex());
-#endif
- return;
- } else {
- setProperty(s->source, m->base, *m->name);
- return;
- }
- }
- }
- } else if (IR::Subscript *ss = s->target->asSubscript()) {
- if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) {
- setElement(s->source, ss->base, ss->index);
- return;
- }
- }
-
- // For anything else...:
- Q_UNIMPLEMENTED();
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinter(&qout).print(s);
- qout << endl;
- qDebug("%s", buf.data().constData());
- Q_ASSERT(!"TODO");
-}
-
-IRDecoder::~IRDecoder()
-{
-}
-
-void IRDecoder::visitExp(IR::Exp *s)
-{
- if (IR::Call *c = s->expr->asCall()) {
- // These are calls where the result is ignored.
- if (c->base->asName()) {
- callBuiltin(c, 0);
- } else if (c->base->asTemp() || c->base->asArgLocal() || c->base->asConst()) {
- callValue(c->base, c->args, 0);
- } else if (Member *member = c->base->asMember()) {
- Q_ASSERT(member->base->asTemp() || member->base->asArgLocal());
-#ifndef V4_BOOTSTRAP
- Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray);
- if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
- callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, 0);
- return;
- }
-#endif
- callProperty(member->base, *member->name, c->args, 0);
- } else if (Subscript *s = c->base->asSubscript()) {
- callSubscript(s->base, s->index, c->args, 0);
- } else {
- Q_UNREACHABLE();
- }
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void IRDecoder::callBuiltin(IR::Call *call, Expr *result)
-{
- IR::Name *baseName = call->base->asName();
- Q_ASSERT(baseName != 0);
-
- switch (baseName->builtin) {
- case IR::Name::builtin_invalid:
- callBuiltinInvalid(baseName, call->args, result);
- return;
-
- case IR::Name::builtin_typeof: {
- if (IR::Member *member = call->args->expr->asMember()) {
-#ifndef V4_BOOTSTRAP
- Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray);
- if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
- callBuiltinTypeofQmlContextProperty(member->base,
- IR::Member::MemberKind(member->kind),
- member->property->coreIndex(), result);
- return;
- }
-#endif
- callBuiltinTypeofMember(member->base, *member->name, result);
- return;
- } else if (IR::Subscript *ss = call->args->expr->asSubscript()) {
- callBuiltinTypeofSubscript(ss->base, ss->index, result);
- return;
- } else if (IR::Name *n = call->args->expr->asName()) {
- callBuiltinTypeofName(*n->id, result);
- return;
- } else if (call->args->expr->asTemp() ||
- call->args->expr->asConst() ||
- call->args->expr->asArgLocal()) {
- callBuiltinTypeofValue(call->args->expr, result);
- return;
- }
- } break;
-
- case IR::Name::builtin_delete: {
- if (IR::Member *m = call->args->expr->asMember()) {
- callBuiltinDeleteMember(m->base, *m->name, result);
- return;
- } else if (IR::Subscript *ss = call->args->expr->asSubscript()) {
- callBuiltinDeleteSubscript(ss->base, ss->index, result);
- return;
- } else if (IR::Name *n = call->args->expr->asName()) {
- callBuiltinDeleteName(*n->id, result);
- return;
- } else if (call->args->expr->asTemp() ||
- call->args->expr->asArgLocal()) {
- // TODO: should throw in strict mode
- callBuiltinDeleteValue(result);
- return;
- }
- } break;
-
- case IR::Name::builtin_throw: {
- IR::Expr *arg = call->args->expr;
- Q_ASSERT(arg->asTemp() || arg->asConst() || arg->asArgLocal());
- callBuiltinThrow(arg);
- } return;
-
- case IR::Name::builtin_rethrow: {
- callBuiltinReThrow();
- } return;
-
- case IR::Name::builtin_unwind_exception: {
- callBuiltinUnwindException(result);
- } return;
-
- case IR::Name::builtin_push_catch_scope: {
- IR::String *s = call->args->expr->asString();
- Q_ASSERT(s);
- callBuiltinPushCatchScope(*s->value);
- } return;
-
- case IR::Name::builtin_foreach_iterator_object: {
- IR::Expr *arg = call->args->expr;
- Q_ASSERT(arg != 0);
- callBuiltinForeachIteratorObject(arg, result);
- } return;
-
- case IR::Name::builtin_foreach_next_property_name: {
- IR::Expr *arg = call->args->expr;
- Q_ASSERT(arg != 0);
- callBuiltinForeachNextPropertyname(arg, result);
- } return;
- case IR::Name::builtin_push_with_scope: {
- if (call->args->expr->asTemp() || call->args->expr->asArgLocal())
- callBuiltinPushWithScope(call->args->expr);
- else
- Q_UNIMPLEMENTED();
- } return;
-
- case IR::Name::builtin_pop_scope:
- callBuiltinPopScope();
- return;
-
- case IR::Name::builtin_declare_vars: {
- if (!call->args)
- return;
- IR::Const *deletable = call->args->expr->asConst();
- Q_ASSERT(deletable->type == IR::BoolType);
- for (IR::ExprList *it = call->args->next; it; it = it->next) {
- IR::Name *arg = it->expr->asName();
- Q_ASSERT(arg != 0);
- callBuiltinDeclareVar(deletable->value != 0, *arg->id);
- }
- } return;
-
- case IR::Name::builtin_define_array:
- callBuiltinDefineArray(result, call->args);
- return;
-
- case IR::Name::builtin_define_object_literal: {
- IR::ExprList *args = call->args;
- const int keyValuePairsCount = args->expr->asConst()->value;
- args = args->next;
-
- IR::ExprList *keyValuePairs = args;
- for (int i = 0; i < keyValuePairsCount; ++i) {
- args = args->next; // name
- bool isData = args->expr->asConst()->value;
- args = args->next; // isData flag
- args = args->next; // value or getter
- if (!isData)
- args = args->next; // setter
- }
-
- IR::ExprList *arrayEntries = args;
- bool needSparseArray = false;
- for (IR::ExprList *it = arrayEntries; it; it = it->next) {
- uint index = it->expr->asConst()->value;
- if (index > 16) {
- needSparseArray = true;
- break;
- }
- it = it->next;
- bool isData = it->expr->asConst()->value;
- it = it->next;
- if (!isData)
- it = it->next;
- }
-
- callBuiltinDefineObjectLiteral(result, keyValuePairsCount, keyValuePairs, arrayEntries, needSparseArray);
- } return;
-
- case IR::Name::builtin_setup_argument_object:
- callBuiltinSetupArgumentObject(result);
- return;
-
- case IR::Name::builtin_convert_this_to_object:
- callBuiltinConvertThisToObject();
- return;
-
- default:
- break;
- }
-
- Q_UNIMPLEMENTED();
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinter(&qout).print(call); qout << endl;
- qDebug("%s", buf.data().constData());
- Q_UNREACHABLE();
-}
diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h
deleted file mode 100644
index e27bf0e284..0000000000
--- a/src/qml/compiler/qv4isel_p.h
+++ /dev/null
@@ -1,220 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4ISEL_P_H
-#define QV4ISEL_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "private/qv4global_p.h"
-#include "qv4jsir_p.h"
-#include <private/qv4compileddata_p.h>
-#include <private/qv4compiler_p.h>
-
-#include <qglobal.h>
-#include <QHash>
-
-QT_BEGIN_NAMESPACE
-
-class QQmlEnginePrivate;
-
-namespace QV4 {
-
-class EvalISelFactory;
-class ExecutableAllocator;
-struct Function;
-
-class Q_QML_PRIVATE_EXPORT EvalInstructionSelection
-{
-public:
- EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
- virtual ~EvalInstructionSelection() = 0;
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(bool generateUnitData = true);
-
- void setUseFastLookups(bool b) { useFastLookups = b; }
- void setUseTypeInference(bool onoff) { useTypeInference = onoff; }
-
- int registerString(const QString &str) { return jsGenerator->registerString(str); }
- uint registerIndexedGetterLookup() { return jsGenerator->registerIndexedGetterLookup(); }
- uint registerIndexedSetterLookup() { return jsGenerator->registerIndexedSetterLookup(); }
- uint registerGetterLookup(const QString &name) { return jsGenerator->registerGetterLookup(name); }
- uint registerSetterLookup(const QString &name) { return jsGenerator->registerSetterLookup(name); }
- uint registerGlobalGetterLookup(const QString &name) { return jsGenerator->registerGlobalGetterLookup(name); }
- int registerRegExp(IR::RegExp *regexp) { return jsGenerator->registerRegExp(regexp); }
- int registerJSClass(const QVector<Compiler::JSUnitGenerator::MemberInfo> &members) {
- return jsGenerator->registerJSClass(members);
- }
- QV4::Compiler::JSUnitGenerator *jsUnitGenerator() const { return jsGenerator; }
-
-protected:
- virtual void run(int functionIndex) = 0;
- virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> backendCompileStep() = 0;
-
- bool useFastLookups;
- bool useTypeInference;
- QV4::ExecutableAllocator *executableAllocator;
- QV4::Compiler::JSUnitGenerator *jsGenerator;
- QScopedPointer<QV4::Compiler::JSUnitGenerator> ownJSGenerator;
- IR::Module *irModule;
-};
-
-class Q_QML_PRIVATE_EXPORT EvalISelFactory
-{
-public:
- EvalISelFactory(const QString &codeGeneratorName) : codeGeneratorName(codeGeneratorName) {}
- virtual ~EvalISelFactory() = 0;
- virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0;
- virtual bool jitCompileRegexps() const = 0;
- virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() = 0;
-
- const QString codeGeneratorName;
-};
-
-namespace IR {
-class Q_QML_PRIVATE_EXPORT IRDecoder
-{
-public:
- IRDecoder() : _function(0) {}
- virtual ~IRDecoder() = 0;
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto j = s->asJump()) {
- visitJump(j);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- } else {
- Q_UNREACHABLE();
- }
- }
-
-private: // visitor methods for StmtVisitor:
- void visitMove(IR::Move *s);
- void visitExp(IR::Exp *s);
-
-public: // to implement by subclasses:
- virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofName(const QString &name, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) = 0;
- virtual void callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) = 0;
- virtual void callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) = 0;
- virtual void callBuiltinDeleteName(const QString &name, IR::Expr *result) = 0;
- virtual void callBuiltinDeleteValue(IR::Expr *result) = 0;
- virtual void callBuiltinThrow(IR::Expr *arg) = 0;
- virtual void callBuiltinReThrow() = 0;
- virtual void callBuiltinUnwindException(IR::Expr *) = 0;
- virtual void callBuiltinPushCatchScope(const QString &exceptionName) = 0;
- virtual void callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) = 0;
- virtual void callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) = 0;
- virtual void callBuiltinPushWithScope(IR::Expr *arg) = 0;
- virtual void callBuiltinPopScope() = 0;
- virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0;
- virtual void callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) = 0;
- virtual void callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) = 0;
- virtual void callBuiltinSetupArgumentObject(IR::Expr *result) = 0;
- virtual void callBuiltinConvertThisToObject() = 0;
- virtual void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void convertType(IR::Expr *source, IR::Expr *target) = 0;
- virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void loadThisObject(IR::Expr *target) = 0;
- virtual void loadQmlContext(IR::Expr *target) = 0;
- virtual void loadQmlImportedScripts(IR::Expr *target) = 0;
- virtual void loadQmlSingleton(const QString &name, IR::Expr *target) = 0;
- virtual void loadConst(IR::Const *sourceConst, IR::Expr *target) = 0;
- virtual void loadString(const QString &str, IR::Expr *target) = 0;
- virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) = 0;
- virtual void getActivationProperty(const IR::Name *name, IR::Expr *target) = 0;
- virtual void setActivationProperty(IR::Expr *source, const QString &targetName) = 0;
- virtual void initClosure(IR::Closure *closure, IR::Expr *target) = 0;
- virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) = 0;
- virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) = 0;
- virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) = 0;
- virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) = 0;
- virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) = 0;
- virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) = 0;
- virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) = 0;
- virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) = 0;
- virtual void copyValue(IR::Expr *source, IR::Expr *target) = 0;
- virtual void swapValues(IR::Expr *source, IR::Expr *target) = 0;
- virtual void unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) = 0;
- virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) = 0;
-
-protected:
- virtual void visitJump(IR::Jump *) = 0;
- virtual void visitCJump(IR::CJump *) = 0;
- virtual void visitRet(IR::Ret *) = 0;
- virtual void visitPhi(IR::Phi *) {}
-
- virtual void callBuiltin(IR::Call *c, IR::Expr *result);
-
- IR::Function *_function; // subclass needs to set
-};
-} // namespace IR
-
-} // namespace QV4
-
-QT_END_NAMESPACE
-
-#endif // QV4ISEL_P_H
diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h
deleted file mode 100644
index e949e6f0ad..0000000000
--- a/src/qml/compiler/qv4isel_util_p.h
+++ /dev/null
@@ -1,241 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4ISEL_UTIL_P_H
-#define QV4ISEL_UTIL_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "private/qv4value_p.h"
-#include "qv4jsir_p.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-
-struct TargetPrimitive32 {
- static TargetPrimitive32 emptyValue() { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Empty) << 32; return p; }
- static TargetPrimitive32 nullValue() { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Null) << 32; return p; }
- static TargetPrimitive32 undefinedValue() { TargetPrimitive32 p; p._val = quint64(Value::Managed_Type_Internal_32) << 32; return p; }
- static TargetPrimitive32 fromBoolean(bool b) { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Boolean) << 32 | quint64(b); return p; }
- static TargetPrimitive32 fromInt32(int v) { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Integer) << 32 | quint32(v); return p; }
- static TargetPrimitive32 fromDouble(double v) {
- TargetPrimitive32 p;
- memcpy(&p._val, &v, 8);
- return p;
- }
- static TargetPrimitive32 fromUInt32(uint v) {
- if (v < INT_MAX)
- return fromInt32(qint32(v));
- return fromDouble(double(v));
- }
-
- quint32 value() const { return _val & quint64(~quint32(0)); }
- quint32 tag() const { return _val >> 32; }
-
- quint64 rawValue() const { return _val; }
-
-private:
- quint64 _val;
-};
-
-struct TargetPrimitive64 {
- static TargetPrimitive64 emptyValue() { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Empty) << 32; return p; }
- static TargetPrimitive64 nullValue() { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Null) << 32; return p; }
- static TargetPrimitive64 undefinedValue() { TargetPrimitive64 p; p._val = 0; return p; }
- static TargetPrimitive64 fromBoolean(bool b) { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Boolean) << 32 | quint64(b); return p; }
- static TargetPrimitive64 fromInt32(int v) { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Integer) << 32 | quint32(v); return p; }
- static TargetPrimitive64 fromDouble(double v) {
- TargetPrimitive64 p;
- memcpy(&p._val, &v, 8);
- p._val ^= Value::NaNEncodeMask;
- return p;
- }
- static TargetPrimitive64 fromUInt32(uint v) {
- if (v < INT_MAX)
- return fromInt32(qint32(v));
- return fromDouble(double(v));
- }
-
- quint32 value() const { return _val & quint64(~quint32(0)); }
- quint32 tag() const { return _val >> 32; }
-
- quint64 rawValue() const { return _val; }
-
-private:
- quint64 _val;
-};
-
-inline bool canConvertToSignedInteger(double value)
-{
- int ival = (int) value;
- // +0 != -0, so we need to convert to double when negating 0
- return ival == value && !(value == 0 && isNegative(value));
-}
-
-inline bool canConvertToUnsignedInteger(double value)
-{
- unsigned uval = (unsigned) value;
- // +0 != -0, so we need to convert to double when negating 0
- return uval == value && !(value == 0 && isNegative(value));
-}
-
-template <typename PrimitiveType = Primitive>
-inline PrimitiveType convertToValue(IR::Const *c)
-{
- switch (c->type) {
- case IR::MissingType:
- return PrimitiveType::emptyValue();
- case IR::NullType:
- return PrimitiveType::nullValue();
- case IR::UndefinedType:
- return PrimitiveType::undefinedValue();
- case IR::BoolType:
- return PrimitiveType::fromBoolean(c->value != 0);
- case IR::SInt32Type:
- return PrimitiveType::fromInt32(int(c->value));
- case IR::UInt32Type:
- return PrimitiveType::fromUInt32(unsigned(c->value));
- case IR::DoubleType:
- return PrimitiveType::fromDouble(c->value);
- case IR::NumberType: {
- int ival = (int)c->value;
- if (canConvertToSignedInteger(c->value)) {
- return PrimitiveType::fromInt32(ival);
- } else {
- return PrimitiveType::fromDouble(c->value);
- }
- }
- default:
- Q_UNREACHABLE();
- }
- // unreachable, but the function must return something
- return PrimitiveType::undefinedValue();
-}
-
-class ConvertTemps
-{
- void renumber(IR::Temp *t)
- {
- if (t->kind != IR::Temp::VirtualRegister)
- return;
-
- int stackSlot = _stackSlotForTemp.value(t->index, -1);
- if (stackSlot == -1) {
- stackSlot = allocateFreeSlot();
- _stackSlotForTemp[t->index] = stackSlot;
- }
-
- t->kind = IR::Temp::StackSlot;
- t->index = stackSlot;
- }
-
-protected:
- int _nextUnusedStackSlot;
- QHash<int, int> _stackSlotForTemp;
- IR::BasicBlock *_currentBasicBlock;
- virtual int allocateFreeSlot()
- {
- return _nextUnusedStackSlot++;
- }
-
- virtual void process(IR::Stmt *s)
- {
- visit(s);
- }
-
-public:
- ConvertTemps()
- : _nextUnusedStackSlot(0)
- , _currentBasicBlock(0)
- {}
-
- void toStackSlots(IR::Function *function)
- {
- _stackSlotForTemp.reserve(function->tempCount);
-
- for (IR::BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
- _currentBasicBlock = bb;
- for (IR::Stmt *s : bb->statements())
- process(s);
- }
-
- function->tempCount = _nextUnusedStackSlot;
- }
-
-protected:
- void visit(IR::Stmt *s) {
- switch (s->stmtKind) {
- case IR::Stmt::PhiStmt:
- visitPhi(s->asPhi());
- break;
- default:
- STMT_VISIT_ALL_KINDS(s);
- break;
- }
- }
-
- virtual void visitPhi(IR::Phi *)
- { Q_UNREACHABLE(); }
-
-private:
- void visit(IR::Expr *e) {
- if (auto temp = e->asTemp()) {
- renumber(temp);
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-};
-} // namespace QV4
-
-QT_END_NAMESPACE
-
-#endif // QV4ISEL_UTIL_P_H
diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp
deleted file mode 100644
index 464eb008e7..0000000000
--- a/src/qml/compiler/qv4jsir.cpp
+++ /dev/null
@@ -1,994 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4jsir_p.h"
-#include <private/qqmljsast_p.h>
-
-#ifndef V4_BOOTSTRAP
-#include <private/qqmlpropertycache_p.h>
-#endif
-
-#include <QtCore/QBuffer>
-#include <QtCore/qtextstream.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qset.h>
-#include <cmath>
-
-#include <vector>
-
-#ifdef CONST
-#undef CONST
-#endif
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-namespace IR {
-
-QString typeName(Type t)
-{
- switch (t) {
- case UnknownType: return QStringLiteral("");
- case MissingType: return QStringLiteral("missing");
- case UndefinedType: return QStringLiteral("undefined");
- case NullType: return QStringLiteral("null");
- case BoolType: return QStringLiteral("bool");
- case UInt32Type: return QStringLiteral("uint32");
- case SInt32Type: return QStringLiteral("int32");
- case DoubleType: return QStringLiteral("double");
- case NumberType: return QStringLiteral("number");
- case StringType: return QStringLiteral("string");
- case VarType: return QStringLiteral("var");
- case QObjectType: return QStringLiteral("qobject");
- default: return QStringLiteral("multiple");
- }
-}
-
-const char *opname(AluOp op)
-{
- switch (op) {
- case OpInvalid: return "?";
-
- case OpIfTrue: return "(bool)";
- case OpNot: return "not";
- case OpUMinus: return "neg";
- case OpUPlus: return "plus";
- case OpCompl: return "invert";
- case OpPreIncrement: return "pre-incr";
- case OpPreDecrement: return "pre-decr";
- case OpPostIncrement: return "post-incr";
- case OpPostDecrement: return "post-decr";
-
- case OpBitAnd: return "bitand";
- case OpBitOr: return "bitor";
- case OpBitXor: return "bitxor";
-
- case OpAdd: return "add";
- case OpSub: return "sub";
- case OpMul: return "mul";
- case OpDiv: return "div";
- case OpMod: return "mod";
-
- case OpLShift: return "shl";
- case OpRShift: return "shr";
- case OpURShift: return "asr";
-
- case OpGt: return "gt";
- case OpLt: return "lt";
- case OpGe: return "ge";
- case OpLe: return "le";
- case OpEqual: return "eq";
- case OpNotEqual: return "ne";
- case OpStrictEqual: return "se";
- case OpStrictNotEqual: return "sne";
-
- case OpInstanceof: return "instanceof";
- case OpIn: return "in";
-
- case OpAnd: return "and";
- case OpOr: return "or";
-
- default: return "?";
-
- } // switch
-}
-
-AluOp binaryOperator(int op)
-{
- switch (static_cast<QSOperator::Op>(op)) {
- case QSOperator::Add: return OpAdd;
- case QSOperator::And: return OpAnd;
- case QSOperator::BitAnd: return OpBitAnd;
- case QSOperator::BitOr: return OpBitOr;
- case QSOperator::BitXor: return OpBitXor;
- case QSOperator::Div: return OpDiv;
- case QSOperator::Equal: return OpEqual;
- case QSOperator::Ge: return OpGe;
- case QSOperator::Gt: return OpGt;
- case QSOperator::Le: return OpLe;
- case QSOperator::LShift: return OpLShift;
- case QSOperator::Lt: return OpLt;
- case QSOperator::Mod: return OpMod;
- case QSOperator::Mul: return OpMul;
- case QSOperator::NotEqual: return OpNotEqual;
- case QSOperator::Or: return OpOr;
- case QSOperator::RShift: return OpRShift;
- case QSOperator::StrictEqual: return OpStrictEqual;
- case QSOperator::StrictNotEqual: return OpStrictNotEqual;
- case QSOperator::Sub: return OpSub;
- case QSOperator::URShift: return OpURShift;
- case QSOperator::InstanceOf: return OpInstanceof;
- case QSOperator::In: return OpIn;
- default: return OpInvalid;
- }
-}
-
-class RemoveSharedExpressions
-{
- CloneExpr clone;
- std::vector<Expr *> subexpressions; // contains all the non-cloned subexpressions in the given function. sorted using std::lower_bound.
- Expr *uniqueExpr;
-
-public:
- RemoveSharedExpressions(): uniqueExpr(0) {}
-
- void operator()(IR::Function *function)
- {
- subexpressions.clear();
- subexpressions.reserve(function->basicBlockCount() * 8);
-
- for (BasicBlock *block : function->basicBlocks()) {
- if (block->isRemoved())
- continue;
- clone.setBasicBlock(block);
-
- for (Stmt *s : block->statements()) {
- visit(s);
- }
- }
- }
-
-private:
- template <typename Expr_>
- Expr_ *cleanup(Expr_ *expr)
- {
- std::vector<Expr *>::iterator it = std::lower_bound(subexpressions.begin(), subexpressions.end(), expr);
- if (it == subexpressions.end() || *it != expr) {
- subexpressions.insert(it, expr);
- IR::Expr *e = expr;
- qSwap(uniqueExpr, e);
- visit(expr);
- qSwap(uniqueExpr, e);
- return static_cast<Expr_ *>(e);
- }
-
- // the cloned expression is unique by definition
- // so we don't need to add it to `subexpressions'.
- return clone(expr);
- }
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- e->expr = cleanup(e->expr);
- } else if (auto m = s->asMove()) {
- m->target = cleanup(m->target);
- m->source = cleanup(m->source);
- } else if (auto c = s->asCJump()) {
- c->cond = cleanup(c->cond);
- } else if (auto r = s->asRet()) {
- r->expr = cleanup(r->expr);
- }
- }
-
- void visit(Expr *e)
- {
- if (auto c = e->asConvert()) {
- c->expr = cleanup(c->expr);
- } else if (auto u = e->asUnop()) {
- u->expr = cleanup(u->expr);
- } else if (auto b = e->asBinop()) {
- b->left = cleanup(b->left);
- b->right = cleanup(b->right);
- } else if (auto c = e->asCall()) {
- c->base = cleanup(c->base);
- for (IR::ExprList *it = c->args; it; it = it->next) {
- it->expr = cleanup(it->expr);
- }
- } else if (auto n = e->asNew()) {
- n->base = cleanup(n->base);
- for (IR::ExprList *it = n->args; it; it = it->next) {
- it->expr = cleanup(it->expr);
- }
- } else if (auto s = e->asSubscript()) {
- s->base = cleanup(s->base);
- s->index = cleanup(s->index);
- } else if (auto m = e->asMember()) {
- m->base = cleanup(m->base);
- }
- }
-};
-
-void Name::initGlobal(const QString *id, quint32 line, quint32 column)
-{
- this->id = id;
- this->builtin = builtin_invalid;
- this->global = true;
- this->qmlSingleton = false;
- this->freeOfSideEffects = false;
- this->line = line;
- this->column = column;
-}
-
-void Name::init(const QString *id, quint32 line, quint32 column)
-{
- this->id = id;
- this->builtin = builtin_invalid;
- this->global = false;
- this->qmlSingleton = false;
- this->freeOfSideEffects = false;
- this->line = line;
- this->column = column;
-}
-
-void Name::init(Builtin builtin, quint32 line, quint32 column)
-{
- this->id = 0;
- this->builtin = builtin;
- this->global = false;
- this->qmlSingleton = false;
- this->freeOfSideEffects = false;
- this->line = line;
- this->column = column;
-}
-
-const char *builtin_to_string(Name::Builtin b)
-{
- switch (b) {
- case Name::builtin_invalid:
- return "builtin_invalid";
- case Name::builtin_typeof:
- return "builtin_typeof";
- case Name::builtin_delete:
- return "builtin_delete";
- case Name::builtin_throw:
- return "builtin_throw";
- case Name::builtin_rethrow:
- return "builtin_rethrow";
- case Name::builtin_unwind_exception:
- return "builtin_unwind_exception";
- case Name::builtin_push_catch_scope:
- return "builtin_push_catch_scope";
- case IR::Name::builtin_foreach_iterator_object:
- return "builtin_foreach_iterator_object";
- case IR::Name::builtin_foreach_next_property_name:
- return "builtin_foreach_next_property_name";
- case IR::Name::builtin_push_with_scope:
- return "builtin_push_with_scope";
- case IR::Name::builtin_pop_scope:
- return "builtin_pop_scope";
- case IR::Name::builtin_declare_vars:
- return "builtin_declare_vars";
- case IR::Name::builtin_define_array:
- return "builtin_define_array";
- case IR::Name::builtin_define_object_literal:
- return "builtin_define_object_literal";
- case IR::Name::builtin_setup_argument_object:
- return "builtin_setup_argument_object";
- case IR::Name::builtin_convert_this_to_object:
- return "builtin_convert_this_to_object";
- case IR::Name::builtin_qml_context:
- return "builtin_qml_context";
- case IR::Name::builtin_qml_imported_scripts_object:
- return "builtin_qml_imported_scripts_object";
- }
- return "builtin_(###FIXME)";
-};
-
-bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
-{
- if (t1.kind < t2.kind) return true;
- if (t1.kind > t2.kind) return false;
- return t1.index < t2.index;
-}
-
-Function *Module::newFunction(const QString &name, Function *outer)
-{
- Function *f = new Function(this, outer, name);
- functions.append(f);
- if (!outer) {
- if (!isQmlModule) {
- Q_ASSERT(!rootFunction);
- rootFunction = f;
- }
- } else {
- outer->nestedFunctions.append(f);
- }
- return f;
-}
-
-Module::~Module()
-{
- qDeleteAll(functions);
-}
-
-void Module::setFileName(const QString &name)
-{
- fileName = name;
-}
-
-Function::Function(Module *module, Function *outer, const QString &name)
- : module(module)
- , pool(&module->pool)
- , tempCount(0)
- , maxNumberOfArguments(0)
- , outer(outer)
- , insideWithOrCatch(0)
- , hasDirectEval(false)
- , usesArgumentsObject(false)
- , isStrict(false)
- , isNamedExpression(false)
- , hasTry(false)
- , hasWith(false)
- , isQmlBinding(false)
- , unused(0)
- , line(0)
- , column(0)
- , _allBasicBlocks(0)
- , _statementCount(0)
-{
- this->name = newString(name);
- _basicBlocks.reserve(8);
-}
-
-Function::~Function()
-{
- if (_allBasicBlocks) {
- qDeleteAll(*_allBasicBlocks);
- delete _allBasicBlocks;
- } else {
- qDeleteAll(_basicBlocks);
- }
-
- pool = 0;
- module = 0;
-}
-
-
-const QString *Function::newString(const QString &text)
-{
- return &*strings.insert(text);
-}
-
-BasicBlock *Function::newBasicBlock(BasicBlock *catchBlock, BasicBlockInsertMode mode)
-{
- BasicBlock *block = new BasicBlock(this, catchBlock);
- return mode == InsertBlock ? addBasicBlock(block) : block;
-}
-
-BasicBlock *Function::addBasicBlock(BasicBlock *block)
-{
- Q_ASSERT(block->index() < 0);
- block->setIndex(_basicBlocks.size());
- _basicBlocks.append(block);
- return block;
-}
-
-void Function::removeBasicBlock(BasicBlock *block)
-{
- block->markAsRemoved();
- block->in.clear();
- block->out.clear();
-}
-
-int Function::liveBasicBlocksCount() const
-{
- int count = 0;
- for (BasicBlock *bb : basicBlocks())
- if (!bb->isRemoved())
- ++count;
- return count;
-}
-
-void Function::removeSharedExpressions()
-{
- RemoveSharedExpressions removeSharedExpressions;
- removeSharedExpressions(this);
-}
-
-int Function::indexOfArgument(const QStringRef &string) const
-{
- for (int i = formals.size() - 1; i >= 0; --i) {
- if (*formals.at(i) == string)
- return i;
- }
- return -1;
-}
-
-void Function::setScheduledBlocks(const QVector<BasicBlock *> &scheduled)
-{
- Q_ASSERT(!_allBasicBlocks);
- _allBasicBlocks = new QVector<BasicBlock *>(basicBlocks());
- _basicBlocks = scheduled;
- for (int i = 0, ei = basicBlockCount(); i != ei; ++i)
- basicBlock(i)->changeIndex(i);
-}
-
-BasicBlock *Function::getOrCreateBasicBlock(int index)
-{
- if (_basicBlocks.size() <= index) {
- const int oldSize = _basicBlocks.size();
- _basicBlocks.resize(index + 1);
- for (int i = oldSize; i <= index; ++i) {
- BasicBlock *block = new BasicBlock(this, 0);
- block->setIndex(i);
- _basicBlocks[i] = block;
- }
- }
-
- return _basicBlocks.at(index);
-}
-
-void Function::setStatementCount(int cnt)
-{
- _statementCount = cnt;
-}
-
-void BasicBlock::setStatements(const QVector<Stmt *> &newStatements)
-{
- Q_ASSERT(!isRemoved());
- Q_ASSERT(newStatements.size() >= _statements.size());
- for (Stmt *s : qAsConst(_statements)) {
- if (Phi *p = s->asPhi()) {
- if (!newStatements.contains(p)) {
- // phi-node was not copied over, so:
- p->destroyData();
- }
- } else {
- break;
- }
- }
- _statements = newStatements;
-}
-
-CloneExpr::CloneExpr(BasicBlock *block)
- : block(block), cloned(0)
-{
-}
-
-void CloneExpr::setBasicBlock(BasicBlock *block)
-{
- this->block = block;
-}
-
-ExprList *CloneExpr::clone(ExprList *list)
-{
- if (! list)
- return 0;
-
- ExprList *clonedList = block->function->New<IR::ExprList>();
- clonedList->init(clone(list->expr), clone(list->next));
- return clonedList;
-}
-
-void CloneExpr::visit(Expr *e)
-{
- if (auto c = e->asConst()) {
- cloned = cloneConst(c, block->function);
- } else if (auto s = e->asString()) {
- cloned = block->STRING(s->value);
- } else if (auto r = e->asRegExp()) {
- cloned = block->REGEXP(r->value, r->flags);
- } else if (auto n = e->asName()) {
- cloned = cloneName(n, block->function);
- } else if (auto t = e->asTemp()) {
- cloned = cloneTemp(t, block->function);
- } else if (auto a = e->asArgLocal()) {
- cloned = cloneArgLocal(a, block->function);
- } else if (auto c = e->asClosure()) {
- cloned = block->CLOSURE(c->value);
- } else if (auto c = e->asConvert()) {
- cloned = block->CONVERT(clone(c->expr), c->type);
- } else if (auto u = e->asUnop()) {
- cloned = block->UNOP(u->op, clone(u->expr));
- } else if (auto b = e->asBinop()) {
- cloned = block->BINOP(b->op, clone(b->left), clone(b->right));
- } else if (auto c = e->asCall()) {
- cloned = block->CALL(clone(c->base), clone(c->args));
- } else if (auto n = e->asNew()) {
- cloned = block->NEW(clone(n->base), clone(n->args));
- } else if (auto s = e->asSubscript()) {
- cloned = block->SUBSCRIPT(clone(s->base), clone(s->index));
- } else if (auto m = e->asMember()) {
- cloned = block->MEMBER(clone(m->base), m->name, m->property, m->kind, m->idIndex);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-IRPrinter::IRPrinter(QTextStream *out)
- : out(out)
- , positionSize(Stmt::InvalidId)
- , currentBB(0)
-{
-}
-
-IRPrinter::~IRPrinter()
-{
-}
-
-void IRPrinter::print(Stmt *s)
-{
- visit(s);
-}
-
-void IRPrinter::print(const Expr &e)
-{
- visit(const_cast<Expr *>(&e));
-}
-
-void IRPrinter::print(Expr *e)
-{
- visit(e);
-}
-
-void IRPrinter::print(Function *f)
-{
- if (positionSize == Stmt::InvalidId)
- positionSize = QString::number(f->statementCount()).size();
-
- QString n = f->name ? *f->name : QString();
- if (n.isEmpty())
- n.sprintf("%p", f);
- *out << "function " << n << '(';
-
- for (int i = 0; i < f->formals.size(); ++i) {
- if (i != 0)
- *out << ", ";
- *out << *f->formals.at(i);
- }
- *out << ')' << endl
- << '{' << endl;
-
- for (const QString *local : qAsConst(f->locals))
- *out << " local var " << *local << endl;
-
- bool needsSeperator = !f->locals.isEmpty();
- for (BasicBlock *bb : f->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- if (needsSeperator)
- *out << endl;
- else
- needsSeperator = true;
- print(bb);
- }
- *out << '}' << endl;
-}
-
-void IRPrinter::print(BasicBlock *bb)
-{
- std::swap(currentBB, bb);
- printBlockStart();
-
- for (Stmt *s : currentBB->statements()) {
- if (!s)
- continue;
-
- QByteArray str;
- QBuffer buf(&str);
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- QTextStream *prevOut = &os;
- std::swap(out, prevOut);
- addStmtNr(s);
- visit(s);
- if (s->location.startLine) {
- out->flush();
- for (int i = 58 - str.length(); i > 0; --i)
- *out << ' ';
- *out << " ; line: " << s->location.startLine << ", column: " << s->location.startColumn;
- }
-
- out->flush();
- std::swap(out, prevOut);
-
- *out << " " << str << endl;
- }
-
- std::swap(currentBB, bb);
-}
-
-void IRPrinter::visit(Stmt *s)
-{
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto j = s->asJump()) {
- visitJump(j);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void IRPrinter::visitExp(Exp *s)
-{
- *out << "void ";
- visit(s->expr);
-}
-
-void IRPrinter::visitMove(Move *s)
-{
- if (Temp *targetTemp = s->target->asTemp())
- if (!s->swap && targetTemp->type != UnknownType)
- *out << typeName(targetTemp->type) << ' ';
-
- visit(s->target);
- *out << ' ';
- if (s->swap)
- *out << "<=> ";
- else
- *out << "= ";
- visit(s->source);
-}
-
-void IRPrinter::visitJump(Jump *s)
-{
- *out << "goto L" << s->target->index();
-}
-
-void IRPrinter::visitCJump(CJump *s)
-{
- *out << "if ";
- visit(s->cond);
- *out << " goto L" << s->iftrue->index()
- << " else goto L" << s->iffalse->index();
-}
-
-void IRPrinter::visitRet(Ret *s)
-{
- *out << "return";
- if (s->expr) {
- *out << ' ';
- visit(s->expr);
- }
-}
-
-void IRPrinter::visitPhi(Phi *s)
-{
- if (s->targetTemp->type != UnknownType)
- *out << typeName(s->targetTemp->type) << ' ';
-
- visit(s->targetTemp);
- *out << " = phi ";
- for (int i = 0, ei = s->incoming.size(); i < ei; ++i) {
- if (i > 0)
- *out << ", ";
- if (currentBB)
- *out << 'L' << currentBB->in.at(i)->index() << ": ";
- if (s->incoming[i])
- visit(s->incoming[i]);
- }
-}
-
-void IRPrinter::visit(Expr *e)
-{
- if (auto c = e->asConst()) {
- visitConst(c);
- } else if (auto s = e->asString()) {
- visitString(s);
- } else if (auto r = e->asRegExp()) {
- visitRegExp(r);
- } else if (auto n = e->asName()) {
- visitName(n);
- } else if (auto t = e->asTemp()) {
- visitTemp(t);
- } else if (auto a = e->asArgLocal()) {
- visitArgLocal(a);
- } else if (auto c = e->asClosure()) {
- visitClosure(c);
- } else if (auto c = e->asConvert()) {
- visitConvert(c);
- } else if (auto u = e->asUnop()) {
- visitUnop(u);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void IRPrinter::visitConst(Const *e)
-{
- switch (e->type) {
- case QV4::IR::UndefinedType:
- *out << "undefined";
- break;
- case QV4::IR::NullType:
- *out << "null";
- break;
- case QV4::IR::BoolType:
- *out << (e->value ? "true" : "false");
- break;
- case QV4::IR::MissingType:
- *out << "missing";
- break;
- default:
- if (int(e->value) == 0 && int(e->value) == e->value) {
- if (isNegative(e->value))
- *out << "-0";
- else
- *out << "0";
- } else {
- *out << QString::number(e->value, 'g', 16);
- }
- break;
- }
-}
-
-void IRPrinter::visitString(String *e)
-{
- *out << '"' << escape(*e->value) << '"';
-}
-
-void IRPrinter::visitRegExp(RegExp *e)
-{
- char f[3];
- int i = 0;
- if (e->flags & RegExp::RegExp_Global)
- f[i++] = 'g';
- if (e->flags & RegExp::RegExp_IgnoreCase)
- f[i++] = 'i';
- if (e->flags & RegExp::RegExp_Multiline)
- f[i++] = 'm';
- f[i] = 0;
-
- *out << '/' << *e->value << '/' << f;
-}
-
-void IRPrinter::visitName(Name *e)
-{
- if (e->id) {
- if (*e->id != QLatin1String("this"))
- *out << '.';
- *out << *e->id;
- } else {
- *out << builtin_to_string(e->builtin);
- }
-}
-
-void IRPrinter::visitTemp(Temp *e)
-{
- switch (e->kind) {
- case Temp::VirtualRegister: *out << '%' << e->index; break;
- case Temp::PhysicalRegister: *out << (e->type == DoubleType ? "fp" : "r")
- << e->index; break;
- case Temp::StackSlot: *out << '&' << e->index; break;
- default: *out << "INVALID";
- }
-}
-
-void IRPrinter::visitArgLocal(ArgLocal *e)
-{
- switch (e->kind) {
- case ArgLocal::Formal: *out << '#' << e->index; break;
- case ArgLocal::ScopedFormal: *out << '#' << e->index
- << '@' << e->scope; break;
- case ArgLocal::Local: *out << '$' << e->index; break;
- case ArgLocal::ScopedLocal: *out << '$' << e->index
- << '@' << e->scope; break;
- default: *out << "INVALID";
- }
-}
-
-void IRPrinter::visitClosure(Closure *e)
-{
- QString name = e->functionName ? *e->functionName : QString();
- if (name.isEmpty())
- name.sprintf("%x", e->value);
- *out << "closure " << name;
-}
-
-void IRPrinter::visitConvert(Convert *e)
-{
- *out << "convert " << typeName(e->expr->type) << " to " << typeName(e->type) << ' ';
- visit(e->expr);
-}
-
-void IRPrinter::visitUnop(Unop *e)
-{
- *out << opname(e->op) << ' ';
- visit(e->expr);
-}
-
-void IRPrinter::visitBinop(Binop *e)
-{
- *out << opname(e->op) << ' ';
- visit(e->left);
- *out << ", ";
- visit(e->right);
-}
-
-void IRPrinter::visitCall(Call *e)
-{
- *out << "call ";
- visit(e->base);
- *out << '(';
- for (ExprList *it = e->args; it; it = it->next) {
- if (it != e->args)
- *out << ", ";
- visit(it->expr);
- }
- *out << ')';
-}
-
-void IRPrinter::visitNew(New *e)
-{
- *out << "new ";
- visit(e->base);
- *out << '(';
- for (ExprList *it = e->args; it; it = it->next) {
- if (it != e->args)
- *out << ", ";
- visit(it->expr);
- }
- *out << ')';
-}
-
-void IRPrinter::visitSubscript(Subscript *e)
-{
- visit(e->base);
- *out << '[';
- visit(e->index);
- *out << ']';
-}
-
-void IRPrinter::visitMember(Member *e)
-{
- if (e->kind != Member::MemberOfEnum && e->kind != Member::MemberOfIdObjectsArray
- && e->attachedPropertiesId != 0 && !e->base->asTemp())
- *out << "[[attached property from " << e->attachedPropertiesId << "]]";
- else
- visit(e->base);
- *out << '.' << *e->name;
-#ifndef V4_BOOTSTRAP
- if (e->property)
- *out << " (meta-property " << e->property->coreIndex()
- << " <" << QMetaType::typeName(e->property->propType())
- << ">)";
- else if (e->kind == Member::MemberOfIdObjectsArray)
- *out << "(id object " << e->idIndex << ")";
-#endif
-}
-
-QString IRPrinter::escape(const QString &s)
-{
- QString r;
- for (int i = 0; i < s.length(); ++i) {
- const QChar ch = s.at(i);
- if (ch == QLatin1Char('\n'))
- r += QLatin1String("\\n");
- else if (ch == QLatin1Char('\r'))
- r += QLatin1String("\\r");
- else if (ch == QLatin1Char('\\'))
- r += QLatin1String("\\\\");
- else if (ch == QLatin1Char('"'))
- r += QLatin1String("\\\"");
- else if (ch == QLatin1Char('\''))
- r += QLatin1String("\\'");
- else
- r += ch;
- }
- return r;
-}
-
-void IRPrinter::addStmtNr(Stmt *s)
-{
- if (s->id() >= 0)
- addJustifiedNr(s->id());
-}
-
-void IRPrinter::addJustifiedNr(int pos)
-{
- if (positionSize == Stmt::InvalidId) {
- *out << pos << ": ";
- } else {
- QString posStr;
- if (pos != Stmt::InvalidId)
- posStr = QString::number(pos);
- *out << posStr.rightJustified(positionSize);
- if (pos == Stmt::InvalidId)
- *out << " ";
- else
- *out << ": ";
- }
-}
-
-void IRPrinter::printBlockStart()
-{
- if (currentBB->isRemoved()) {
- *out << "(block has been removed)";
- return;
- }
-
- QByteArray str;
- str.append('L');
- str.append(QByteArray::number(currentBB->index()));
- str.append(':');
- if (currentBB->catchBlock) {
- str.append(" (exception handler L");
- str.append(QByteArray::number(currentBB->catchBlock->index()));
- str.append(')');
- }
- for (int i = 66 - str.length(); i; --i)
- str.append(' ');
- *out << str;
-
- *out << "; predecessors:";
- for (BasicBlock *in : qAsConst(currentBB->in))
- *out << " L" << in->index();
- if (currentBB->in.isEmpty())
- *out << " none";
- if (BasicBlock *container = currentBB->containingGroup())
- *out << ", container: L" << container->index();
- if (currentBB->isGroupStart())
- *out << ", loop_header: yes";
- *out << endl;
-}
-
-} // end of namespace IR
-} // end of namespace QV4
-
-QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h
deleted file mode 100644
index 82632c391e..0000000000
--- a/src/qml/compiler/qv4jsir_p.h
+++ /dev/null
@@ -1,1807 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QV4JSIR_P_H
-#define QV4JSIR_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "private/qv4global_p.h"
-#include <private/qqmljsmemorypool_p.h>
-#include <private/qqmljsastfwd_p.h>
-#include <private/qflagpointer_p.h>
-
-#include <QtCore/private/qnumeric_p.h>
-#include <QtCore/QVector>
-#include <QtCore/QString>
-#include <QtCore/QBitArray>
-#include <QtCore/qurl.h>
-#include <QtCore/QVarLengthArray>
-#include <QtCore/QDateTime>
-#include <qglobal.h>
-
-#if defined(CONST) && defined(Q_OS_WIN)
-# define QT_POP_CONST
-# pragma push_macro("CONST")
-# undef CONST // CONST conflicts with our own identifier
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class QTextStream;
-class QQmlType;
-class QQmlPropertyData;
-class QQmlPropertyCache;
-class QQmlEnginePrivate;
-
-namespace QV4 {
-
-inline bool isNegative(double d)
-{
- uchar *dch = (uchar *)&d;
- if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
- return (dch[0] & 0x80);
- else
- return (dch[7] & 0x80);
-
-}
-
-namespace IR {
-
-struct BasicBlock;
-struct Function;
-struct Module;
-
-struct Stmt;
-struct Expr;
-
-// expressions
-struct Const;
-struct String;
-struct RegExp;
-struct Name;
-struct Temp;
-struct ArgLocal;
-struct Closure;
-struct Convert;
-struct Unop;
-struct Binop;
-struct Call;
-struct New;
-struct Subscript;
-struct Member;
-
-// statements
-struct Exp;
-struct Move;
-struct Jump;
-struct CJump;
-struct Ret;
-struct Phi;
-
-template<class T, int Prealloc>
-class VarLengthArray: public QVarLengthArray<T, Prealloc>
-{
-public:
- bool removeOne(const T &element)
- {
- for (int i = 0; i < this->size(); ++i) {
- if (this->at(i) == element) {
- this->remove(i);
- return true;
- }
- }
-
- return false;
- }
-};
-
-// Flag pointer:
-// * The first flag indicates whether the meta object is final.
-// If final, then none of its properties themselves need to
-// be final when considering for lookups in QML.
-// * The second flag indicates whether enums should be included
-// in the lookup of properties or not. The default is false.
-typedef QFlagPointer<QQmlPropertyCache> IRMetaObject;
-
-enum AluOp {
- OpInvalid = 0,
-
- OpIfTrue,
- OpNot,
- OpUMinus,
- OpUPlus,
- OpCompl,
- OpPreIncrement,
- OpPreDecrement,
- OpPostIncrement,
- OpPostDecrement,
-
- OpBitAnd,
- OpBitOr,
- OpBitXor,
-
- OpAdd,
- OpSub,
- OpMul,
- OpDiv,
- OpMod,
-
- OpLShift,
- OpRShift,
- OpURShift,
-
- OpGt,
- OpLt,
- OpGe,
- OpLe,
- OpEqual,
- OpNotEqual,
- OpStrictEqual,
- OpStrictNotEqual,
-
- OpInstanceof,
- OpIn,
-
- OpAnd,
- OpOr,
-
- LastAluOp = OpOr
-};
-AluOp binaryOperator(int op);
-const char *opname(IR::AluOp op);
-
-enum Type : quint16 {
- UnknownType = 0,
-
- MissingType = 1 << 0,
- UndefinedType = 1 << 1,
- NullType = 1 << 2,
- BoolType = 1 << 3,
-
- SInt32Type = 1 << 4,
- UInt32Type = 1 << 5,
- DoubleType = 1 << 6,
- NumberType = SInt32Type | UInt32Type | DoubleType,
-
- StringType = 1 << 7,
- QObjectType = 1 << 8,
- VarType = 1 << 9
-};
-
-inline bool strictlyEqualTypes(Type t1, Type t2)
-{
- return t1 == t2 || ((t1 & NumberType) && (t2 & NumberType));
-}
-
-QString typeName(Type t);
-
-struct MemberExpressionResolver;
-
-struct DiscoveredType {
- int type;
- MemberExpressionResolver *memberResolver;
-
- DiscoveredType() : type(UnknownType), memberResolver(0) {}
- DiscoveredType(Type t) : type(t), memberResolver(0) { Q_ASSERT(type != QObjectType); }
- explicit DiscoveredType(int t) : type(t), memberResolver(0) { Q_ASSERT(type != QObjectType); }
- explicit DiscoveredType(MemberExpressionResolver *memberResolver)
- : type(QObjectType)
- , memberResolver(memberResolver)
- { Q_ASSERT(memberResolver); }
-
- bool test(Type t) const { return type & t; }
- bool isNumber() const { return (type & NumberType) && !(type & ~NumberType); }
-
- bool operator!=(Type other) const { return type != other; }
- bool operator==(Type other) const { return type == other; }
- bool operator==(const DiscoveredType &other) const { return type == other.type; }
- bool operator!=(const DiscoveredType &other) const { return type != other.type; }
-};
-
-struct MemberExpressionResolver
-{
- typedef DiscoveredType (*ResolveFunction)(QQmlEnginePrivate *engine,
- const MemberExpressionResolver *resolver,
- Member *member);
-
- MemberExpressionResolver()
- : resolveMember(0), data(0), extraData(0), owner(nullptr), flags(0) {}
-
- bool isValid() const { return !!resolveMember; }
- void clear() { *this = MemberExpressionResolver(); }
-
- ResolveFunction resolveMember;
- void *data; // Could be pointer to meta object, importNameSpace, etc. - depends on resolveMember implementation
- void *extraData; // Could be QQmlTypeNameCache
- Function *owner;
- unsigned int flags;
-};
-
-struct Q_AUTOTEST_EXPORT Expr {
- enum ExprKind : quint8 {
- NameExpr,
- TempExpr,
- ArgLocalExpr,
- SubscriptExpr,
- MemberExpr,
-
- LastLValue = MemberExpr,
-
- ConstExpr,
- StringExpr,
- RegExpExpr,
- ClosureExpr,
- ConvertExpr,
- UnopExpr,
- BinopExpr,
- CallExpr,
- NewExpr
- };
-
- Type type;
- const ExprKind exprKind;
-
- Expr &operator=(const Expr &other) {
- Q_ASSERT(exprKind == other.exprKind);
- type = other.type;
- return *this;
- }
-
- template <typename To>
- inline bool isa() const {
- return To::classof(this);
- }
-
- template <typename To>
- inline To *as() {
- if (isa<To>()) {
- return static_cast<To *>(this);
- } else {
- return nullptr;
- }
- }
-
- template <typename To>
- inline const To *as() const {
- if (isa<To>()) {
- return static_cast<const To *>(this);
- } else {
- return nullptr;
- }
- }
-
- Expr(ExprKind exprKind): type(UnknownType), exprKind(exprKind) {}
- bool isLValue() const;
-
- Const *asConst();
- String *asString();
- RegExp *asRegExp();
- Name *asName();
- Temp *asTemp();
- ArgLocal *asArgLocal();
- Closure *asClosure();
- Convert *asConvert();
- Unop *asUnop();
- Binop *asBinop();
- Call *asCall();
- New *asNew();
- Subscript *asSubscript();
- Member *asMember();
-};
-
-#define EXPR_VISIT_ALL_KINDS(e) \
- switch (e->exprKind) { \
- case QV4::IR::Expr::ConstExpr: \
- break; \
- case QV4::IR::Expr::StringExpr: \
- break; \
- case QV4::IR::Expr::RegExpExpr: \
- break; \
- case QV4::IR::Expr::NameExpr: \
- break; \
- case QV4::IR::Expr::TempExpr: \
- break; \
- case QV4::IR::Expr::ArgLocalExpr: \
- break; \
- case QV4::IR::Expr::ClosureExpr: \
- break; \
- case QV4::IR::Expr::ConvertExpr: { \
- auto casted = e->asConvert(); \
- visit(casted->expr); \
- } break; \
- case QV4::IR::Expr::UnopExpr: { \
- auto casted = e->asUnop(); \
- visit(casted->expr); \
- } break; \
- case QV4::IR::Expr::BinopExpr: { \
- auto casted = e->asBinop(); \
- visit(casted->left); \
- visit(casted->right); \
- } break; \
- case QV4::IR::Expr::CallExpr: { \
- auto casted = e->asCall(); \
- visit(casted->base); \
- for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \
- visit(it->expr); \
- } break; \
- case QV4::IR::Expr::NewExpr: { \
- auto casted = e->asNew(); \
- visit(casted->base); \
- for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \
- visit(it->expr); \
- } break; \
- case QV4::IR::Expr::SubscriptExpr: { \
- auto casted = e->asSubscript(); \
- visit(casted->base); \
- visit(casted->index); \
- } break; \
- case QV4::IR::Expr::MemberExpr: { \
- auto casted = e->asMember(); \
- visit(casted->base); \
- } break; \
- }
-
-struct ExprList {
- Expr *expr;
- ExprList *next;
-
- ExprList(): expr(0), next(0) {}
-
- void init(Expr *expr, ExprList *next = 0)
- {
- this->expr = expr;
- this->next = next;
- }
-};
-
-struct Const: Expr {
- double value;
-
- Const(): Expr(ConstExpr) {}
-
- void init(Type type, double value)
- {
- this->type = type;
- this->value = value;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == ConstExpr; }
-};
-
-struct String: Expr {
- const QString *value;
-
- String(): Expr(StringExpr) {}
-
- void init(const QString *value)
- {
- this->value = value;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == StringExpr; }
-};
-
-struct RegExp: Expr {
- // needs to be compatible with the flags in the lexer, and in RegExpObject
- enum Flags {
- RegExp_Global = 0x01,
- RegExp_IgnoreCase = 0x02,
- RegExp_Multiline = 0x04
- };
-
- const QString *value;
- int flags;
-
- RegExp(): Expr(RegExpExpr) {}
-
- void init(const QString *value, int flags)
- {
- this->value = value;
- this->flags = flags;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == RegExpExpr; }
-};
-
-struct Name: Expr {
- enum Builtin {
- builtin_invalid,
- builtin_typeof,
- builtin_delete,
- builtin_throw,
- builtin_rethrow,
- builtin_unwind_exception,
- builtin_push_catch_scope,
- builtin_foreach_iterator_object,
- builtin_foreach_next_property_name,
- builtin_push_with_scope,
- builtin_pop_scope,
- builtin_declare_vars,
- builtin_define_array,
- builtin_define_object_literal,
- builtin_setup_argument_object,
- builtin_convert_this_to_object,
- builtin_qml_context,
- builtin_qml_imported_scripts_object
- };
-
- const QString *id;
- Builtin builtin;
- bool global : 1;
- bool qmlSingleton : 1;
- bool freeOfSideEffects : 1;
- quint32 line;
- quint32 column;
-
- Name(): Expr(NameExpr) {}
-
- void initGlobal(const QString *id, quint32 line, quint32 column);
- void init(const QString *id, quint32 line, quint32 column);
- void init(Builtin builtin, quint32 line, quint32 column);
-
- static bool classof(const Expr *c) { return c->exprKind == NameExpr; }
-};
-
-struct Q_AUTOTEST_EXPORT Temp: Expr {
- enum Kind {
- Invalid = 0,
- VirtualRegister,
- PhysicalRegister,
- StackSlot
- };
-
- unsigned index : 28;
- unsigned isReadOnly : 1;
- unsigned kind : 3;
-
- // Used when temp is used as base in member expression
- MemberExpressionResolver *memberResolver;
-
- Temp()
- : Expr(TempExpr)
- , index((1 << 28) - 1)
- , isReadOnly(0)
- , kind(Invalid)
- , memberResolver(0)
- {}
-
- Temp(Type type, Kind kind, unsigned index)
- : Expr(TempExpr)
- , index(index)
- , isReadOnly(0)
- , kind(kind)
- , memberResolver(0)
- {
- this->type = type;
- }
-
- void init(unsigned kind, unsigned index)
- {
- this->index = index;
- this->isReadOnly = false;
- this->kind = kind;
- }
-
- bool isInvalid() const { return kind == Invalid; }
-
- static bool classof(const Expr *c) { return c->exprKind == TempExpr; }
-};
-
-inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
-{ return t1.index == t2.index && t1.kind == t2.kind && t1.type == t2.type; }
-
-inline bool operator!=(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
-{ return !(t1 == t2); }
-
-inline uint qHash(const Temp &t, uint seed = 0) Q_DECL_NOTHROW
-{ return t.index ^ t.kind ^ seed; }
-
-bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW;
-
-struct Q_AUTOTEST_EXPORT ArgLocal: Expr {
- enum Kind {
- Formal = 0,
- ScopedFormal,
- Local,
- ScopedLocal
- };
-
- unsigned index;
- unsigned scope : 29; // how many scopes outside the current one?
- unsigned kind : 2;
- unsigned isArgumentsOrEval : 1;
-
- void init(unsigned kind, unsigned index, unsigned scope)
- {
- Q_ASSERT((kind == ScopedLocal && scope != 0) ||
- (kind == ScopedFormal && scope != 0) ||
- (scope == 0));
-
- this->kind = kind;
- this->index = index;
- this->scope = scope;
- this->isArgumentsOrEval = false;
- }
-
- ArgLocal(): Expr(ArgLocalExpr) {}
-
- bool operator==(const ArgLocal &other) const
- { return index == other.index && scope == other.scope && kind == other.kind; }
-
- static bool classof(const Expr *c) { return c->exprKind == ArgLocalExpr; }
-};
-
-struct Closure: Expr {
- int value; // index in _module->functions
- const QString *functionName;
-
- Closure(): Expr(ClosureExpr) {}
-
- void init(int functionInModule, const QString *functionName)
- {
- this->value = functionInModule;
- this->functionName = functionName;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == ClosureExpr; }
-};
-
-struct Convert: Expr {
- Expr *expr;
-
- Convert(): Expr(ConvertExpr) {}
-
- void init(Expr *expr, Type type)
- {
- this->expr = expr;
- this->type = type;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == ConvertExpr; }
-};
-
-struct Unop: Expr {
- Expr *expr;
- AluOp op;
-
- Unop(): Expr(UnopExpr) {}
-
- void init(AluOp op, Expr *expr)
- {
- this->op = op;
- this->expr = expr;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == UnopExpr; }
-};
-
-struct Binop: Expr {
- Expr *left; // Temp or Const
- Expr *right; // Temp or Const
- AluOp op;
-
- Binop(): Expr(BinopExpr) {}
-
- void init(AluOp op, Expr *left, Expr *right)
- {
- this->op = op;
- this->left = left;
- this->right = right;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == BinopExpr; }
-};
-
-struct Call: Expr {
- Expr *base; // Name, Member, Temp
- ExprList *args; // List of Temps
-
- Call(): Expr(CallExpr) {}
-
- void init(Expr *base, ExprList *args)
- {
- this->base = base;
- this->args = args;
- }
-
- Expr *onlyArgument() const {
- if (args && ! args->next)
- return args->expr;
- return 0;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == CallExpr; }
-};
-
-struct New: Expr {
- Expr *base; // Name, Member, Temp
- ExprList *args; // List of Temps
-
- New(): Expr(NewExpr) {}
-
- void init(Expr *base, ExprList *args)
- {
- this->base = base;
- this->args = args;
- }
-
- Expr *onlyArgument() const {
- if (args && ! args->next)
- return args->expr;
- return 0;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == NewExpr; }
-};
-
-struct Subscript: Expr {
- Expr *base;
- Expr *index;
-
- Subscript(): Expr(SubscriptExpr) {}
-
- void init(Expr *base, Expr *index)
- {
- this->base = base;
- this->index = index;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == SubscriptExpr; }
-};
-
-struct Member: Expr {
- // Used for property dependency tracking
- enum MemberKind {
- UnspecifiedMember,
- MemberOfEnum,
- MemberOfQmlScopeObject,
- MemberOfQmlContextObject,
- MemberOfIdObjectsArray,
- MemberOfSingletonObject,
- };
-
- Expr *base;
- const QString *name;
- QQmlPropertyData *property;
- union { // depending on kind
- int attachedPropertiesId;
- int enumValue;
- int idIndex;
- };
- uchar freeOfSideEffects : 1;
-
- // This is set for example for for QObject properties. All sorts of extra behavior
- // is defined when writing to them, for example resettable properties are reset
- // when writing undefined to them, and an exception is thrown when they're missing
- // a reset function. And then there's also Qt.binding().
- uchar inhibitTypeConversionOnWrite: 1;
-
- uchar kind: 3; // MemberKind
-
- Member(): Expr(MemberExpr) {}
-
- void setEnumValue(int value) {
- kind = MemberOfEnum;
- enumValue = value;
- }
-
- void setAttachedPropertiesId(int id) {
- Q_ASSERT(kind != MemberOfEnum && kind != MemberOfIdObjectsArray);
- attachedPropertiesId = id;
- }
-
- void init(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = UnspecifiedMember, int index = 0)
- {
- this->base = base;
- this->name = name;
- this->property = property;
- this->idIndex = index;
- this->freeOfSideEffects = false;
- this->inhibitTypeConversionOnWrite = property != 0;
- this->kind = kind;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == MemberExpr; }
-};
-
-inline bool Expr::isLValue() const {
- if (auto t = as<Temp>())
- return !t->isReadOnly;
- return exprKind <= LastLValue;
-}
-
-struct Stmt {
- enum StmtKind: quint8 {
- MoveStmt,
- ExpStmt,
- JumpStmt,
- CJumpStmt,
- RetStmt,
- PhiStmt
- };
-
- template <typename To>
- inline bool isa() const {
- return To::classof(this);
- }
-
- template <typename To>
- inline To *as() {
- if (isa<To>())
- return static_cast<To *>(this);
- else
- return nullptr;
- }
-
- enum { InvalidId = -1 };
-
- QQmlJS::AST::SourceLocation location;
-
- explicit Stmt(int id, StmtKind stmtKind): _id(id), stmtKind(stmtKind) {}
-
- Stmt *asTerminator();
-
- Exp *asExp();
- Move *asMove();
- Jump *asJump();
- CJump *asCJump();
- Ret *asRet();
- Phi *asPhi();
-
- int id() const { return _id; }
-
-private: // For memory management in BasicBlock
- friend struct BasicBlock;
-
-private:
- friend struct Function;
- int _id;
-
-public:
- const StmtKind stmtKind;
-};
-
-#define STMT_VISIT_ALL_KINDS(s) \
- switch (s->stmtKind) { \
- case QV4::IR::Stmt::MoveStmt: { \
- auto casted = s->asMove(); \
- visit(casted->target); \
- visit(casted->source); \
- } break; \
- case QV4::IR::Stmt::ExpStmt: { \
- auto casted = s->asExp(); \
- visit(casted->expr); \
- } break; \
- case QV4::IR::Stmt::JumpStmt: \
- break; \
- case QV4::IR::Stmt::CJumpStmt: { \
- auto casted = s->asCJump(); \
- visit(casted->cond); \
- } break; \
- case QV4::IR::Stmt::RetStmt: { \
- auto casted = s->asRet(); \
- visit(casted->expr); \
- } break; \
- case QV4::IR::Stmt::PhiStmt: { \
- auto casted = s->asPhi(); \
- visit(casted->targetTemp); \
- for (auto *e : casted->incoming) { \
- visit(e); \
- } \
- } break; \
- }
-
-struct Exp: Stmt {
- Expr *expr;
-
- Exp(int id): Stmt(id, ExpStmt) {}
-
- void init(Expr *expr)
- {
- this->expr = expr;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == ExpStmt; }
-};
-
-struct Move: Stmt {
- Expr *target; // LHS - Temp, Name, Member or Subscript
- Expr *source;
- bool swap;
-
- Move(int id): Stmt(id, MoveStmt) {}
-
- void init(Expr *target, Expr *source)
- {
- this->target = target;
- this->source = source;
- this->swap = false;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == MoveStmt; }
-};
-
-struct Jump: Stmt {
- BasicBlock *target;
-
- Jump(int id): Stmt(id, JumpStmt) {}
-
- void init(BasicBlock *target)
- {
- this->target = target;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == JumpStmt; }
-};
-
-struct CJump: Stmt {
- Expr *cond; // Temp, Binop
- BasicBlock *iftrue;
- BasicBlock *iffalse;
- BasicBlock *parent;
-
- CJump(int id): Stmt(id, CJumpStmt) {}
-
- void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse, BasicBlock *parent)
- {
- this->cond = cond;
- this->iftrue = iftrue;
- this->iffalse = iffalse;
- this->parent = parent;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == CJumpStmt; }
-};
-
-struct Ret: Stmt {
- Expr *expr;
-
- Ret(int id): Stmt(id, RetStmt) {}
-
- void init(Expr *expr)
- {
- this->expr = expr;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == RetStmt; }
-};
-
-// Phi nodes can only occur at the start of a basic block. If there are any, they need to be
-// subsequent to eachother, and the first phi node should be the first statement in the basic-block.
-// A number of loops rely on this behavior, so they don't need to walk through the whole list
-// of instructions in a basic-block (e.g. the calls to destroyData in BasicBlock::~BasicBlock).
-struct Phi: Stmt {
- Temp *targetTemp;
- VarLengthArray<Expr *, 4> incoming;
-
- Phi(int id): Stmt(id, PhiStmt) {}
-
- static bool classof(const Stmt *c) { return c->stmtKind == PhiStmt; }
-
- void destroyData()
- { incoming.~VarLengthArray(); }
-};
-
-inline Stmt *Stmt::asTerminator()
-{
- if (auto s = asJump()) {
- return s;
- } else if (auto s = asCJump()) {
- return s;
- } else if (auto s = asRet()) {
- return s;
- } else {
- return nullptr;
- }
-}
-
-struct Q_QML_PRIVATE_EXPORT Module {
- QQmlJS::MemoryPool pool;
- QVector<Function *> functions;
- Function *rootFunction;
- QString fileName;
- QDateTime sourceTimeStamp;
- bool isQmlModule; // implies rootFunction is always 0
- uint unitFlags; // flags merged into CompiledData::Unit::flags
- QString targetABI; // fallback to QSysInfo::buildAbi() if empty
-#ifdef QT_NO_QML_DEBUGGER
- static const bool debugMode = false;
-#else
- bool debugMode;
-#endif
-
- Function *newFunction(const QString &name, Function *outer);
-
- Module(bool debugMode)
- : rootFunction(0)
- , isQmlModule(false)
- , unitFlags(0)
-#ifndef QT_NO_QML_DEBUGGER
- , debugMode(debugMode)
- {}
-#else
- { Q_UNUSED(debugMode); }
-#endif
- ~Module();
-
- void setFileName(const QString &name);
-};
-
-struct BasicBlock {
-private:
- Q_DISABLE_COPY(BasicBlock)
-
-public:
- typedef VarLengthArray<BasicBlock *, 4> IncomingEdges;
- typedef VarLengthArray<BasicBlock *, 2> OutgoingEdges;
-
- Function *function;
- BasicBlock *catchBlock;
- IncomingEdges in;
- OutgoingEdges out;
- QQmlJS::AST::SourceLocation nextLocation;
-
- BasicBlock(Function *function, BasicBlock *catcher)
- : function(function)
- , catchBlock(catcher)
- , _containingGroup(0)
- , _index(-1)
- , _isExceptionHandler(false)
- , _groupStart(false)
- , _isRemoved(false)
- {}
-
- ~BasicBlock()
- {
- for (Stmt *s : qAsConst(_statements)) {
- if (Phi *p = s->asPhi()) {
- p->destroyData();
- } else {
- break;
- }
- }
- }
-
- const QVector<Stmt *> &statements() const
- {
- Q_ASSERT(!isRemoved());
- return _statements;
- }
-
- int statementCount() const
- {
- Q_ASSERT(!isRemoved());
- return _statements.size();
- }
-
- void setStatements(const QVector<Stmt *> &newStatements);
-
- template <typename Instr> inline Instr i(Instr i)
- {
- Q_ASSERT(!isRemoved());
- appendStatement(i);
- return i;
- }
-
- void appendStatement(Stmt *statement)
- {
- Q_ASSERT(!isRemoved());
- if (nextLocation.startLine)
- statement->location = nextLocation;
- _statements.append(statement);
- }
-
- void prependStatement(Stmt *stmt)
- {
- Q_ASSERT(!isRemoved());
- _statements.prepend(stmt);
- }
-
- void prependStatements(const QVector<Stmt *> &stmts)
- {
- Q_ASSERT(!isRemoved());
- QVector<Stmt *> newStmts = stmts;
- newStmts += _statements;
- _statements = newStmts;
- }
-
- void insertStatementBefore(Stmt *before, Stmt *newStmt)
- {
- int idx = _statements.indexOf(before);
- Q_ASSERT(idx >= 0);
- _statements.insert(idx, newStmt);
- }
-
- void insertStatementBefore(int index, Stmt *newStmt)
- {
- Q_ASSERT(index >= 0);
- _statements.insert(index, newStmt);
- }
-
- void insertStatementBeforeTerminator(Stmt *stmt)
- {
- Q_ASSERT(!isRemoved());
- _statements.insert(_statements.size() - 1, stmt);
- }
-
- void replaceStatement(int index, Stmt *newStmt)
- {
- Q_ASSERT(!isRemoved());
- if (Phi *p = _statements[index]->asPhi()) {
- p->destroyData();
- }
- _statements[index] = newStmt;
- }
-
- void removeStatement(Stmt *stmt)
- {
- Q_ASSERT(!isRemoved());
- if (Phi *p = stmt->asPhi()) {
- p->destroyData();
- }
- _statements.remove(_statements.indexOf(stmt));
- }
-
- void removeStatement(int idx)
- {
- Q_ASSERT(!isRemoved());
- if (Phi *p = _statements[idx]->asPhi()) {
- p->destroyData();
- }
- _statements.remove(idx);
- }
-
- inline bool isEmpty() const {
- Q_ASSERT(!isRemoved());
- return _statements.isEmpty();
- }
-
- inline Stmt *terminator() const {
- Q_ASSERT(!isRemoved());
- if (! _statements.isEmpty() && _statements.last()->asTerminator() != 0)
- return _statements.last();
- return 0;
- }
-
- inline bool isTerminated() const {
- Q_ASSERT(!isRemoved());
- if (terminator() != 0)
- return true;
- return false;
- }
-
- enum TempForWhom {
- NewTempForCodegen,
- NewTempForOptimizer
- };
- unsigned newTemp(TempForWhom tfw = NewTempForCodegen);
-
- Temp *TEMP(unsigned kind);
- ArgLocal *ARG(unsigned index, unsigned scope);
- ArgLocal *LOCAL(unsigned index, unsigned scope);
-
- Expr *CONST(Type type, double value);
- Expr *STRING(const QString *value);
- Expr *REGEXP(const QString *value, int flags);
-
- Name *NAME(const QString &id, quint32 line, quint32 column);
- Name *NAME(Name::Builtin builtin, quint32 line, quint32 column);
-
- Name *GLOBALNAME(const QString &id, quint32 line, quint32 column);
-
- Closure *CLOSURE(int functionInModule);
-
- Expr *CONVERT(Expr *expr, Type type);
- Expr *UNOP(AluOp op, Expr *expr);
- Expr *BINOP(AluOp op, Expr *left, Expr *right);
- Expr *CALL(Expr *base, ExprList *args = 0);
- Expr *NEW(Expr *base, ExprList *args = 0);
- Expr *SUBSCRIPT(Expr *base, Expr *index);
- Expr *MEMBER(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = Member::UnspecifiedMember, int attachedPropertiesIdOrEnumValue = 0);
-
- Stmt *EXP(Expr *expr);
-
- Stmt *MOVE(Expr *target, Expr *source);
-
- Stmt *JUMP(BasicBlock *target);
- Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse);
- Stmt *RET(Expr *expr);
-
- BasicBlock *containingGroup() const
- {
- Q_ASSERT(!isRemoved());
- return _containingGroup;
- }
-
- void setContainingGroup(BasicBlock *loopHeader)
- {
- Q_ASSERT(!isRemoved());
- _containingGroup = loopHeader;
- }
-
- bool isGroupStart() const
- {
- Q_ASSERT(!isRemoved());
- return _groupStart;
- }
-
- void markAsGroupStart(bool mark = true)
- {
- Q_ASSERT(!isRemoved());
- _groupStart = mark;
- }
-
- // Returns the index of the basic-block.
- // See Function for the full description.
- int index() const
- {
- Q_ASSERT(!isRemoved());
- return _index;
- }
-
- bool isExceptionHandler() const
- { return _isExceptionHandler; }
-
- void setExceptionHandler(bool onoff)
- { _isExceptionHandler = onoff; }
-
- bool isRemoved() const
- { return _isRemoved; }
-
-private: // For Function's eyes only.
- friend struct Function;
- void setIndex(int index)
- {
- Q_ASSERT(_index < 0);
- changeIndex(index);
- }
-
- void changeIndex(int index)
- {
- Q_ASSERT(index >= 0);
- _index = index;
- }
-
- void markAsRemoved()
- {
- _isRemoved = true;
- _index = -1;
- }
-
-private:
- QVector<Stmt *> _statements;
- BasicBlock *_containingGroup;
- int _index;
- unsigned _isExceptionHandler : 1;
- unsigned _groupStart : 1;
- unsigned _isRemoved : 1;
-};
-
-template <typename T>
-class SmallSet: public QVarLengthArray<T, 8>
-{
-public:
- void insert(int value)
- {
- for (auto it : *this) {
- if (it == value)
- return;
- }
- this->append(value);
- }
-};
-
-// Map from meta property index (existence implies dependency) to notify signal index
-struct KeyValuePair
-{
- quint32 _key;
- quint32 _value;
-
- KeyValuePair(): _key(0), _value(0) {}
- KeyValuePair(quint32 key, quint32 value): _key(key), _value(value) {}
-
- quint32 key() const { return _key; }
- quint32 value() const { return _value; }
-};
-
-class PropertyDependencyMap: public QVarLengthArray<KeyValuePair, 8>
-{
-public:
- void insert(quint32 key, quint32 value)
- {
- for (auto it = begin(), eit = end(); it != eit; ++it) {
- if (it->_key == key) {
- it->_value = value;
- return;
- }
- }
- append(KeyValuePair(key, value));
- }
-};
-
-// The Function owns (manages), among things, a list of basic-blocks. All the blocks have an index,
-// which corresponds to the index in the entry/index in the vector in which they are stored. This
-// means that algorithms/classes can also store any information about a basic block in an array,
-// where the index corresponds to the index of the basic block, which can then be used to query
-// the function for a pointer to a basic block. This also means that basic-blocks cannot be removed
-// or renumbered.
-//
-// Note that currently there is one exception: after optimization and block scheduling, the
-// method setScheduledBlocks can be called once, to register a newly ordered list. For debugging
-// purposes, these blocks are not immediately renumbered, so renumberBasicBlocks should be called
-// immediately after changing the order. That will restore the property of having a corresponding
-// block-index and block-position-in-basicBlocks-vector.
-//
-// In order for optimization/transformation passes to skip uninteresting basic blocks that will be
-// removed, the block can be marked as such. After doing so, any access will result in a failing
-// assertion.
-struct Function {
- Module *module;
- QQmlJS::MemoryPool *pool;
- const QString *name;
- int currentTemp = 0;
- int tempCount;
- int maxNumberOfArguments;
- QSet<QString> strings;
- QList<const QString *> formals;
- QList<const QString *> locals;
- QVector<Function *> nestedFunctions;
- Function *outer;
- QByteArray code;
-
- int insideWithOrCatch;
-
- uint hasDirectEval: 1;
- uint usesArgumentsObject : 1;
- uint usesThis : 1;
- uint isStrict: 1;
- uint isNamedExpression : 1;
- uint hasTry: 1;
- uint hasWith: 1;
- uint isQmlBinding: 1;
- uint unused : 24;
-
- // Location of declaration in source code (0 if not specified)
- uint line;
- uint column;
-
- // Qml extension:
- SmallSet<int> idObjectDependencies;
- PropertyDependencyMap contextObjectPropertyDependencies;
- PropertyDependencyMap scopeObjectPropertyDependencies;
-
- template <typename T> T *New() { return new (pool->allocate(sizeof(T))) T(); }
- template <typename T> T *NewStmt() {
- return new (pool->allocate(sizeof(T))) T(getNewStatementId());
- }
-
- Function(Module *module, Function *outer, const QString &name);
- ~Function();
-
- enum BasicBlockInsertMode {
- InsertBlock,
- DontInsertBlock
- };
-
- BasicBlock *newBasicBlock(BasicBlock *catchBlock, BasicBlockInsertMode mode = InsertBlock);
- const QString *newString(const QString &text);
-
- void RECEIVE(const QString &name) { formals.append(newString(name)); }
- void LOCAL(const QString &name) { locals.append(newString(name)); }
-
- BasicBlock *addBasicBlock(BasicBlock *block);
- void removeBasicBlock(BasicBlock *block);
-
- const QVector<BasicBlock *> &basicBlocks() const
- { return _basicBlocks; }
-
- BasicBlock *basicBlock(int idx) const
- { return _basicBlocks.at(idx); }
-
- int basicBlockCount() const
- { return _basicBlocks.size(); }
-
- int liveBasicBlocksCount() const;
-
- void removeSharedExpressions();
-
- int indexOfArgument(const QStringRef &string) const;
-
- bool variablesCanEscape() const
- { return hasDirectEval || !nestedFunctions.isEmpty() || module->debugMode; }
-
- void setScheduledBlocks(const QVector<BasicBlock *> &scheduled);
-
- int getNewStatementId() { return _statementCount++; }
- int statementCount() const { return _statementCount; }
-
- bool canUseSimpleCall() const {
- return nestedFunctions.isEmpty() &&
- locals.isEmpty() && formals.size() <= QV4::Global::ReservedArgumentCount &&
- !hasTry && !hasWith && !isNamedExpression && !usesArgumentsObject && !hasDirectEval;
- }
-
- bool argLocalRequiresWriteBarrier(ArgLocal *al) const {
- uint scope = al->scope;
- const IR::Function *f = this;
- while (scope) {
- f = f->outer;
- --scope;
- }
- return !f->canUseSimpleCall();
- }
- int localsCountForScope(ArgLocal *al) const {
- uint scope = al->scope;
- const IR::Function *f = this;
- while (scope) {
- f = f->outer;
- --scope;
- }
- return f->locals.size();
- }
-
-private:
- BasicBlock *getOrCreateBasicBlock(int index);
- void setStatementCount(int cnt);
-
-private:
- QVector<BasicBlock *> _basicBlocks;
- QVector<BasicBlock *> *_allBasicBlocks;
- int _statementCount;
-};
-
-class CloneExpr
-{
-public:
- explicit CloneExpr(IR::BasicBlock *block = 0);
-
- void setBasicBlock(IR::BasicBlock *block);
-
- template <typename ExprSubclass>
- ExprSubclass *operator()(ExprSubclass *expr)
- {
- return clone(expr);
- }
-
- template <typename ExprSubclass>
- ExprSubclass *clone(ExprSubclass *expr)
- {
- Expr *c = expr;
- qSwap(cloned, c);
- visit(expr);
- qSwap(cloned, c);
- return static_cast<ExprSubclass *>(c);
- }
-
- static Const *cloneConst(Const *c, Function *f)
- {
- Const *newConst = f->New<Const>();
- newConst->init(c->type, c->value);
- return newConst;
- }
-
- static Name *cloneName(Name *n, Function *f)
- {
- Name *newName = f->New<Name>();
- newName->type = n->type;
- newName->id = n->id;
- newName->builtin = n->builtin;
- newName->global = n->global;
- newName->qmlSingleton = n->qmlSingleton;
- newName->freeOfSideEffects = n->freeOfSideEffects;
- newName->line = n->line;
- newName->column = n->column;
- return newName;
- }
-
- static Temp *cloneTemp(Temp *t, Function *f)
- {
- Temp *newTemp = f->New<Temp>();
- newTemp->init(t->kind, t->index);
- newTemp->type = t->type;
- newTemp->memberResolver = t->memberResolver;
- return newTemp;
- }
-
- static ArgLocal *cloneArgLocal(ArgLocal *argLocal, Function *f)
- {
- ArgLocal *newArgLocal = f->New<ArgLocal>();
- newArgLocal->init(argLocal->kind, argLocal->index, argLocal->scope);
- newArgLocal->type = argLocal->type;
- newArgLocal->isArgumentsOrEval = argLocal->isArgumentsOrEval;
- return newArgLocal;
- }
-
-private:
- IR::ExprList *clone(IR::ExprList *list);
-
- void visit(Expr *e);
-
-protected:
- IR::BasicBlock *block;
-
-private:
- IR::Expr *cloned;
-};
-
-class Q_AUTOTEST_EXPORT IRPrinter
-{
-public:
- IRPrinter(QTextStream *out);
- virtual ~IRPrinter();
-
- void print(Stmt *s);
- void print(Expr *e);
- void print(const Expr &e);
-
- virtual void print(Function *f);
- virtual void print(BasicBlock *bb);
-
- void visit(Stmt *s);
- virtual void visitExp(Exp *s);
- virtual void visitMove(Move *s);
- virtual void visitJump(Jump *s);
- virtual void visitCJump(CJump *s);
- virtual void visitRet(Ret *s);
- virtual void visitPhi(Phi *s);
-
- void visit(Expr *e);
- virtual void visitConst(Const *e);
- virtual void visitString(String *e);
- virtual void visitRegExp(RegExp *e);
- virtual void visitName(Name *e);
- virtual void visitTemp(Temp *e);
- virtual void visitArgLocal(ArgLocal *e);
- virtual void visitClosure(Closure *e);
- virtual void visitConvert(Convert *e);
- virtual void visitUnop(Unop *e);
- virtual void visitBinop(Binop *e);
- virtual void visitCall(Call *e);
- virtual void visitNew(New *e);
- virtual void visitSubscript(Subscript *e);
- virtual void visitMember(Member *e);
-
- static QString escape(const QString &s);
-
-protected:
- virtual void addStmtNr(Stmt *s);
- void addJustifiedNr(int pos);
- void printBlockStart();
-
-protected:
- QTextStream *out;
- int positionSize;
- BasicBlock *currentBB;
-};
-
-inline unsigned BasicBlock::newTemp(TempForWhom tfw)
-{
- Q_ASSERT(!isRemoved());
-
- if (tfw == NewTempForOptimizer)
- return function->tempCount++;
-
- int t = function->currentTemp++;
- if (function->tempCount < function->currentTemp)
- function->tempCount = function->currentTemp;
- return t;
-}
-
-inline Temp *BasicBlock::TEMP(unsigned index)
-{
- Q_ASSERT(!isRemoved());
- Temp *e = function->New<Temp>();
- e->init(Temp::VirtualRegister, index);
- return e;
-}
-
-inline ArgLocal *BasicBlock::ARG(unsigned index, unsigned scope)
-{
- Q_ASSERT(!isRemoved());
- ArgLocal *e = function->New<ArgLocal>();
- e->init(scope ? ArgLocal::ScopedFormal : ArgLocal::Formal, index, scope);
- return e;
-}
-
-inline ArgLocal *BasicBlock::LOCAL(unsigned index, unsigned scope)
-{
- Q_ASSERT(!isRemoved());
- ArgLocal *e = function->New<ArgLocal>();
- e->init(scope ? ArgLocal::ScopedLocal : ArgLocal::Local, index, scope);
- return e;
-}
-
-inline Expr *BasicBlock::CONST(Type type, double value)
-{
- Q_ASSERT(!isRemoved());
- Const *e = function->New<Const>();
- if (type == NumberType) {
- int ival = (int)value;
- // +0 != -0, so we need to convert to double when negating 0
- if (ival == value && !(value == 0 && isNegative(value)))
- type = SInt32Type;
- else
- type = DoubleType;
- } else if (type == NullType) {
- value = 0;
- } else if (type == UndefinedType) {
- value = qt_qnan();
- }
-
- e->init(type, value);
- return e;
-}
-
-inline Expr *BasicBlock::STRING(const QString *value)
-{
- Q_ASSERT(!isRemoved());
- String *e = function->New<String>();
- e->init(value);
- return e;
-}
-
-inline Expr *BasicBlock::REGEXP(const QString *value, int flags)
-{
- Q_ASSERT(!isRemoved());
- RegExp *e = function->New<RegExp>();
- e->init(value, flags);
- return e;
-}
-
-inline Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column)
-{
- Q_ASSERT(!isRemoved());
- Name *e = function->New<Name>();
- e->init(function->newString(id), line, column);
- return e;
-}
-
-inline Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column)
-{
- Q_ASSERT(!isRemoved());
- Name *e = function->New<Name>();
- e->initGlobal(function->newString(id), line, column);
- return e;
-}
-
-
-inline Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column)
-{
- Q_ASSERT(!isRemoved());
- Name *e = function->New<Name>();
- e->init(builtin, line, column);
- return e;
-}
-
-inline Closure *BasicBlock::CLOSURE(int functionInModule)
-{
- Q_ASSERT(!isRemoved());
- Closure *clos = function->New<Closure>();
- clos->init(functionInModule, function->module->functions.at(functionInModule)->name);
- return clos;
-}
-
-inline Expr *BasicBlock::CONVERT(Expr *expr, Type type)
-{
- Q_ASSERT(!isRemoved());
- Convert *e = function->New<Convert>();
- e->init(expr, type);
- return e;
-}
-
-inline Expr *BasicBlock::UNOP(AluOp op, Expr *expr)
-{
- Q_ASSERT(!isRemoved());
- Unop *e = function->New<Unop>();
- e->init(op, expr);
- return e;
-}
-
-inline Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right)
-{
- Q_ASSERT(!isRemoved());
- Binop *e = function->New<Binop>();
- e->init(op, left, right);
- return e;
-}
-
-inline Expr *BasicBlock::CALL(Expr *base, ExprList *args)
-{
- Q_ASSERT(!isRemoved());
- Call *e = function->New<Call>();
- e->init(base, args);
- int argc = 0;
- for (ExprList *it = args; it; it = it->next)
- ++argc;
- function->maxNumberOfArguments = qMax(function->maxNumberOfArguments, argc);
- return e;
-}
-
-inline Expr *BasicBlock::NEW(Expr *base, ExprList *args)
-{
- Q_ASSERT(!isRemoved());
- New *e = function->New<New>();
- e->init(base, args);
- return e;
-}
-
-inline Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index)
-{
- Q_ASSERT(!isRemoved());
- Subscript *e = function->New<Subscript>();
- e->init(base, index);
- return e;
-}
-
-inline Expr *BasicBlock::MEMBER(Expr *base, const QString *name, QQmlPropertyData *property, uchar kind, int attachedPropertiesIdOrEnumValue)
-{
- Q_ASSERT(!isRemoved());
- Member*e = function->New<Member>();
- e->init(base, name, property, kind, attachedPropertiesIdOrEnumValue);
- return e;
-}
-
-inline Stmt *BasicBlock::EXP(Expr *expr)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- Exp *s = function->NewStmt<Exp>();
- s->init(expr);
- appendStatement(s);
- return s;
-}
-
-inline Stmt *BasicBlock::MOVE(Expr *target, Expr *source)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- Move *s = function->NewStmt<Move>();
- s->init(target, source);
- appendStatement(s);
- return s;
-}
-
-inline Stmt *BasicBlock::JUMP(BasicBlock *target)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- Jump *s = function->NewStmt<Jump>();
- s->init(target);
- appendStatement(s);
-
- Q_ASSERT(! out.contains(target));
- out.append(target);
-
- Q_ASSERT(! target->in.contains(this));
- target->in.append(this);
-
- return s;
-}
-
-inline Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- if (iftrue == iffalse) {
- MOVE(TEMP(newTemp()), cond);
- return JUMP(iftrue);
- }
-
- CJump *s = function->NewStmt<CJump>();
- s->init(cond, iftrue, iffalse, this);
- appendStatement(s);
-
- Q_ASSERT(! out.contains(iftrue));
- out.append(iftrue);
-
- Q_ASSERT(! iftrue->in.contains(this));
- iftrue->in.append(this);
-
- Q_ASSERT(! out.contains(iffalse));
- out.append(iffalse);
-
- Q_ASSERT(! iffalse->in.contains(this));
- iffalse->in.append(this);
-
- return s;
-}
-
-inline Stmt *BasicBlock::RET(Expr *expr)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- Ret *s = function->NewStmt<Ret>();
- s->init(expr);
- appendStatement(s);
- return s;
-}
-
-inline Const *Expr::asConst() { return as<Const>(); }
-inline String *Expr::asString() { return as<String>(); }
-inline RegExp *Expr::asRegExp() { return as<RegExp>(); }
-inline Name *Expr::asName() { return as<Name>(); }
-inline Temp *Expr::asTemp() { return as<Temp>(); }
-inline ArgLocal *Expr::asArgLocal() { return as<ArgLocal>(); }
-inline Closure *Expr::asClosure() { return as<Closure>(); }
-inline Convert *Expr::asConvert() { return as<Convert>(); }
-inline Unop *Expr::asUnop() { return as<Unop>(); }
-inline Binop *Expr::asBinop() { return as<Binop>(); }
-inline Call *Expr::asCall() { return as<Call>(); }
-inline New *Expr::asNew() { return as<New>(); }
-inline Subscript *Expr::asSubscript() { return as<Subscript>(); }
-inline Member *Expr::asMember() { return as<Member>(); }
-
-inline Exp *Stmt::asExp() { return as<Exp>(); }
-inline Move *Stmt::asMove() { return as<Move>(); }
-inline Jump *Stmt::asJump() { return as<Jump>(); }
-inline CJump *Stmt::asCJump() { return as<CJump>(); }
-inline Ret *Stmt::asRet() { return as<Ret>(); }
-inline Phi *Stmt::asPhi() { return as<Phi>(); }
-
-} // end of namespace IR
-
-} // end of namespace QV4
-
-QT_END_NAMESPACE
-
-#if defined(QT_POP_CONST)
-# pragma pop_macro("CONST") // Restore peace
-# undef QT_POP_CONST
-#endif
-
-#endif // QV4IR_P_H
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
deleted file mode 100644
index 72a4a9e751..0000000000
--- a/src/qml/compiler/qv4ssa.cpp
+++ /dev/null
@@ -1,5848 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-// When building with debug code, the macro below will enable debug helpers when using libc++.
-// For example, the std::vector<T>::operator[] will use _LIBCPP_ASSERT to check if the index is
-// within the array bounds. Note that this only works reliably with OSX 10.9 or later.
-//#define _LIBCPP_DEBUG2 2
-
-#include "qv4ssa_p.h"
-#include "qv4isel_util_p.h"
-#include "qv4util_p.h"
-
-#include <QtCore/QBuffer>
-#include <QtCore/QCoreApplication>
-#include <QtCore/QStringList>
-#include <QtCore/QSet>
-#include <QtCore/QLinkedList>
-#include <QtCore/QStack>
-#include <qv4runtime_p.h>
-#include <cmath>
-#include <iostream>
-#include <cassert>
-
-QT_USE_NAMESPACE
-
-using namespace QV4;
-using namespace IR;
-
-namespace {
-
-enum { DebugMoveMapping = 0 };
-
-#ifdef QT_NO_DEBUG
-enum { DoVerification = 0 };
-#else
-enum { DoVerification = 1 };
-#endif
-
-static void showMeTheCode(IR::Function *function, const char *marker)
-{
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR");
- if (showCode) {
- qDebug() << marker;
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream stream(&buf);
- IRPrinter(&stream).print(function);
- stream << endl;
- qDebug("%s", buf.data().constData());
- }
-}
-
-class ProcessedBlocks
-{
- BitVector processed;
-
-public:
- ProcessedBlocks(IR::Function *function)
- : processed(function->basicBlockCount(), false)
- {}
-
- bool alreadyProcessed(BasicBlock *bb) const
- {
- Q_ASSERT(bb);
-
- return processed.at(bb->index());
- }
-
- void markAsProcessed(BasicBlock *bb)
- {
- processed.setBit(bb->index());
- }
-};
-
-class BasicBlockSet
-{
- typedef BitVector Flags;
-
- QVarLengthArray<int, 8> blockNumbers;
- Flags *blockFlags;
- IR::Function *function;
- enum { MaxVectorCapacity = 8 };
-
-public:
- class const_iterator
- {
- const BasicBlockSet &set;
- // ### These two members could go into a union, but clang won't compile (https://codereview.qt-project.org/#change,74259)
- QVarLengthArray<int, 8>::const_iterator numberIt;
- int flagIt;
-
- friend class BasicBlockSet;
- const_iterator(const BasicBlockSet &set, bool end)
- : set(set)
- {
- if (end || !set.function) {
- if (!set.blockFlags)
- numberIt = set.blockNumbers.end();
- else
- flagIt = set.blockFlags->size();
- } else {
- if (!set.blockFlags)
- numberIt = set.blockNumbers.begin();
- else
- findNextWithFlags(0);
- }
- }
-
- void findNextWithFlags(int start)
- {
- flagIt = set.blockFlags->findNext(start, true, /*wrapAround = */false);
- Q_ASSERT(flagIt <= set.blockFlags->size());
- }
-
- public:
- BasicBlock *operator*() const
- {
- if (!set.blockFlags) {
- return set.function->basicBlock(*numberIt);
- } else {
- Q_ASSERT(flagIt <= set.function->basicBlockCount());
- return set.function->basicBlock(flagIt);
- }
- }
-
- bool operator==(const const_iterator &other) const
- {
- if (&set != &other.set)
- return false;
- if (!set.blockFlags)
- return numberIt == other.numberIt;
- else
- return flagIt == other.flagIt;
- }
-
- bool operator!=(const const_iterator &other) const
- { return !(*this == other); }
-
- const_iterator &operator++()
- {
- if (!set.blockFlags)
- ++numberIt;
- else
- findNextWithFlags(flagIt + 1);
-
- return *this;
- }
- };
-
- friend class const_iterator;
-
-public:
- BasicBlockSet(IR::Function *f = 0): blockFlags(0), function(0)
- {
- if (f)
- init(f);
- }
-
-#ifdef Q_COMPILER_RVALUE_REFS
- BasicBlockSet(BasicBlockSet &&other): blockFlags(0)
- {
- std::swap(blockNumbers, other.blockNumbers);
- std::swap(blockFlags, other.blockFlags);
- std::swap(function, other.function);
- }
-#endif // Q_COMPILER_RVALUE_REFS
-
- BasicBlockSet(const BasicBlockSet &other)
- : blockFlags(0)
- , function(other.function)
- {
- if (other.blockFlags)
- blockFlags = new Flags(*other.blockFlags);
- blockNumbers = other.blockNumbers;
- }
-
- BasicBlockSet &operator=(const BasicBlockSet &other)
- {
- if (blockFlags) {
- delete blockFlags;
- blockFlags = 0;
- }
- function = other.function;
- if (other.blockFlags)
- blockFlags = new Flags(*other.blockFlags);
- blockNumbers = other.blockNumbers;
- return *this;
- }
-
- ~BasicBlockSet()
- {
- delete blockFlags;
- }
-
- void init(IR::Function *f)
- {
- Q_ASSERT(!function);
- Q_ASSERT(f);
- function = f;
- }
-
- bool empty() const
- {
- return begin() == end();
- }
-
- void insert(BasicBlock *bb)
- {
- Q_ASSERT(function);
-
- if (blockFlags) {
- blockFlags->setBit(bb->index());
- return;
- }
-
- for (int i = 0; i < blockNumbers.size(); ++i) {
- if (blockNumbers[i] == bb->index())
- return;
- }
-
- if (blockNumbers.size() == MaxVectorCapacity) {
- blockFlags = new Flags(function->basicBlockCount(), false);
- for (int i = 0; i < blockNumbers.size(); ++i) {
- blockFlags->setBit(blockNumbers[i]);
- }
- blockNumbers.clear();
- blockFlags->setBit(bb->index());
- } else {
- blockNumbers.append(bb->index());
- }
- }
-
- void remove(BasicBlock *bb)
- {
- Q_ASSERT(function);
-
- if (blockFlags) {
- blockFlags->clearBit(bb->index());
- return;
- }
-
- for (int i = 0; i < blockNumbers.size(); ++i) {
- if (blockNumbers[i] == bb->index()) {
- blockNumbers.remove(i);
- return;
- }
- }
- }
-
- const_iterator begin() const { return const_iterator(*this, false); }
- const_iterator end() const { return const_iterator(*this, true); }
-
- void collectValues(std::vector<BasicBlock *> &bbs) const
- {
- Q_ASSERT(function);
-
- for (const_iterator it = begin(), eit = end(); it != eit; ++it)
- bbs.push_back(*it);
- }
-
- bool contains(BasicBlock *bb) const
- {
- Q_ASSERT(function);
-
- if (blockFlags)
- return blockFlags->at(bb->index());
-
- for (int i = 0; i < blockNumbers.size(); ++i) {
- if (blockNumbers[i] == bb->index())
- return true;
- }
-
- return false;
- }
-};
-
-class DominatorTree
-{
- enum {
- DebugDominatorFrontiers = 0,
- DebugImmediateDominators = 0,
-
- DebugCodeCanUseLotsOfCpu = 0
- };
-
- typedef int BasicBlockIndex;
- enum { InvalidBasicBlockIndex = -1 };
-
- struct Data
- {
- int N;
- std::vector<int> dfnum; // BasicBlock index -> dfnum
- std::vector<int> vertex;
- std::vector<BasicBlockIndex> parent; // BasicBlock index -> parent BasicBlock index
- std::vector<BasicBlockIndex> ancestor; // BasicBlock index -> ancestor BasicBlock index
- std::vector<BasicBlockIndex> best; // BasicBlock index -> best BasicBlock index
- std::vector<BasicBlockIndex> semi; // BasicBlock index -> semi dominator BasicBlock index
- std::vector<BasicBlockIndex> samedom; // BasicBlock index -> same dominator BasicBlock index
-
- Data(): N(0) {}
- };
-
- IR::Function *function;
- QScopedPointer<Data> d;
- std::vector<BasicBlockIndex> idom; // BasicBlock index -> immediate dominator BasicBlock index
- std::vector<BasicBlockSet> DF; // BasicBlock index -> dominator frontier
-
- struct DFSTodo {
- BasicBlockIndex node, parent;
-
- DFSTodo()
- : node(InvalidBasicBlockIndex)
- , parent(InvalidBasicBlockIndex)
- {}
-
- DFSTodo(BasicBlockIndex node, BasicBlockIndex parent)
- : node(node)
- , parent(parent)
- {}
- };
-
- void DFS(BasicBlockIndex node) {
- std::vector<DFSTodo> worklist;
- worklist.reserve(d->vertex.capacity() / 2);
- DFSTodo todo(node, InvalidBasicBlockIndex);
-
- while (true) {
- BasicBlockIndex n = todo.node;
-
- if (d->dfnum[n] == 0) {
- d->dfnum[n] = d->N;
- d->vertex[d->N] = n;
- d->parent[n] = todo.parent;
- ++d->N;
- const BasicBlock::OutgoingEdges &out = function->basicBlock(n)->out;
- for (int i = out.size() - 1; i > 0; --i)
- worklist.push_back(DFSTodo(out[i]->index(), n));
-
- if (out.size() > 0) {
- todo.node = out.first()->index();
- todo.parent = n;
- continue;
- }
- }
-
- if (worklist.empty())
- break;
-
- todo = worklist.back();
- worklist.pop_back();
- }
- }
-
- BasicBlockIndex ancestorWithLowestSemi(BasicBlockIndex v, std::vector<BasicBlockIndex> &worklist) {
- worklist.clear();
- for (BasicBlockIndex it = v; it != InvalidBasicBlockIndex; it = d->ancestor[it])
- worklist.push_back(it);
-
- if (worklist.size() < 2)
- return d->best[v];
-
- BasicBlockIndex b = InvalidBasicBlockIndex;
- BasicBlockIndex last = worklist.back();
- Q_ASSERT(worklist.size() <= INT_MAX);
- for (int it = static_cast<int>(worklist.size()) - 2; it >= 0; --it) {
- BasicBlockIndex bbIt = worklist[it];
- d->ancestor[bbIt] = last;
- BasicBlockIndex &best_it = d->best[bbIt];
- if (b != InvalidBasicBlockIndex && d->dfnum[d->semi[b]] < d->dfnum[d->semi[best_it]])
- best_it = b;
- else
- b = best_it;
- }
- return b;
- }
-
- void link(BasicBlockIndex p, BasicBlockIndex n) {
- d->ancestor[n] = p;
- d->best[n] = n;
- }
-
- void calculateIDoms() {
- Q_ASSERT(function->basicBlock(0)->in.isEmpty());
-
- const int bbCount = function->basicBlockCount();
- d->vertex = std::vector<int>(bbCount, InvalidBasicBlockIndex);
- d->parent = std::vector<int>(bbCount, InvalidBasicBlockIndex);
- d->dfnum = std::vector<int>(size_t(bbCount), 0);
- d->semi = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
- d->ancestor = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
- idom = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
- d->samedom = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
- d->best = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
-
- QHash<BasicBlockIndex, std::vector<BasicBlockIndex> > bucket;
- bucket.reserve(bbCount);
-
- DFS(function->basicBlock(0)->index());
- Q_ASSERT(d->N == function->liveBasicBlocksCount());
-
- std::vector<BasicBlockIndex> worklist;
- worklist.reserve(d->vertex.capacity() / 2);
-
- for (int i = d->N - 1; i > 0; --i) {
- BasicBlockIndex n = d->vertex[i];
- BasicBlockIndex p = d->parent[n];
- BasicBlockIndex s = p;
-
- for (BasicBlock *v : function->basicBlock(n)->in) {
- BasicBlockIndex ss = InvalidBasicBlockIndex;
- if (d->dfnum[v->index()] <= d->dfnum[n])
- ss = v->index();
- else
- ss = d->semi[ancestorWithLowestSemi(v->index(), worklist)];
- if (d->dfnum[ss] < d->dfnum[s])
- s = ss;
- }
- d->semi[n] = s;
- bucket[s].push_back(n);
- link(p, n);
- if (bucket.contains(p)) {
- for (BasicBlockIndex v : bucket[p]) {
- BasicBlockIndex y = ancestorWithLowestSemi(v, worklist);
- BasicBlockIndex semi_v = d->semi[v];
- if (d->semi[y] == semi_v)
- idom[v] = semi_v;
- else
- d->samedom[v] = y;
- }
- bucket.remove(p);
- }
- }
-
- for (int i = 1; i < d->N; ++i) {
- BasicBlockIndex n = d->vertex[i];
- Q_ASSERT(n != InvalidBasicBlockIndex);
- Q_ASSERT(!bucket.contains(n));
- Q_ASSERT(d->ancestor[n] != InvalidBasicBlockIndex
- && ((d->semi[n] != InvalidBasicBlockIndex
- && d->dfnum[d->ancestor[n]] <= d->dfnum[d->semi[n]]) || d->semi[n] == n));
- BasicBlockIndex sdn = d->samedom[n];
- if (sdn != InvalidBasicBlockIndex)
- idom[n] = idom[sdn];
- }
-
- dumpImmediateDominators();
- }
-
- struct NodeProgress {
- std::vector<BasicBlockIndex> children;
- std::vector<BasicBlockIndex> todo;
- };
-
-public:
- DominatorTree(IR::Function *function)
- : function(function)
- , d(new Data)
- {
- calculateIDoms();
- d.reset();
- }
-
- void computeDF() {
- DF.resize(function->basicBlockCount());
-
- // compute children of each node in the dominator tree
- std::vector<std::vector<BasicBlockIndex> > children; // BasicBlock index -> children
- children.resize(function->basicBlockCount());
- for (BasicBlock *n : function->basicBlocks()) {
- if (n->isRemoved())
- continue;
- const BasicBlockIndex nodeIndex = n->index();
- Q_ASSERT(function->basicBlock(nodeIndex) == n);
- const BasicBlockIndex nodeDominator = idom[nodeIndex];
- if (nodeDominator == InvalidBasicBlockIndex)
- continue; // there is no dominator to add this node to as a child (e.g. the start node)
- children[nodeDominator].push_back(nodeIndex);
- }
-
- // Fill the worklist and initialize the node status for each basic-block
- std::vector<NodeProgress> nodeStatus;
- nodeStatus.resize(function->basicBlockCount());
- std::vector<BasicBlockIndex> worklist;
- worklist.reserve(function->basicBlockCount());
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
- BasicBlockIndex nodeIndex = bb->index();
- worklist.push_back(nodeIndex);
- NodeProgress &np = nodeStatus[nodeIndex];
- np.children = children[nodeIndex];
- np.todo = children[nodeIndex];
- }
-
- BitVector DF_done(function->basicBlockCount(), false);
-
- while (!worklist.empty()) {
- BasicBlockIndex node = worklist.back();
-
- if (DF_done.at(node)) {
- worklist.pop_back();
- continue;
- }
-
- NodeProgress &np = nodeStatus[node];
- std::vector<BasicBlockIndex>::iterator it = np.todo.begin();
- while (it != np.todo.end()) {
- if (DF_done.at(*it)) {
- it = np.todo.erase(it);
- } else {
- worklist.push_back(*it);
- break;
- }
- }
-
- if (np.todo.empty()) {
- BasicBlockSet &S = DF[node];
- S.init(function);
- for (BasicBlock *y : function->basicBlock(node)->out)
- if (idom[y->index()] != node)
- S.insert(y);
- for (BasicBlockIndex child : np.children) {
- const BasicBlockSet &ws = DF[child];
- for (BasicBlockSet::const_iterator it = ws.begin(), eit = ws.end(); it != eit; ++it) {
- BasicBlock *w = *it;
- const BasicBlockIndex wIndex = w->index();
- if (node == wIndex || !dominates(node, w->index()))
- S.insert(w);
- }
- }
- DF_done.setBit(node);
- worklist.pop_back();
- }
- }
-
- if (DebugDominatorFrontiers) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Dominator Frontiers:" << endl;
- for (BasicBlock *n : function->basicBlocks()) {
- if (n->isRemoved())
- continue;
-
- qout << "\tDF[" << n->index() << "]: {";
- const BasicBlockSet &SList = DF[n->index()];
- for (BasicBlockSet::const_iterator i = SList.begin(), ei = SList.end(); i != ei; ++i) {
- if (i != SList.begin())
- qout << ", ";
- qout << (*i)->index();
- }
- qout << "}" << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-
- if (DebugDominatorFrontiers && DebugCodeCanUseLotsOfCpu) {
- for (BasicBlock *n : function->basicBlocks()) {
- if (n->isRemoved())
- continue;
- const BasicBlockSet &fBlocks = DF[n->index()];
- for (BasicBlockSet::const_iterator it = fBlocks.begin(), eit = fBlocks.end(); it != eit; ++it) {
- BasicBlock *fBlock = *it;
- Q_ASSERT(!dominates(n, fBlock) || fBlock == n);
- bool hasDominatedSucc = false;
- for (BasicBlock *succ : fBlock->in) {
- if (dominates(n, succ)) {
- hasDominatedSucc = true;
- break;
- }
- }
- if (!hasDominatedSucc) {
- qDebug("%d in DF[%d] has no dominated predecessors", fBlock->index(), n->index());
- }
- Q_ASSERT(hasDominatedSucc);
- }
- }
- }
- }
-
- const BasicBlockSet &dominatorFrontier(BasicBlock *n) const {
- return DF[n->index()];
- }
-
- BasicBlock *immediateDominator(BasicBlock *bb) const {
- const BasicBlockIndex idx = idom[bb->index()];
- if (idx == -1)
- return 0;
- return function->basicBlock(idx);
- }
-
- void dumpImmediateDominators() const
- {
- if (DebugImmediateDominators) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Immediate dominators:" << endl;
- for (BasicBlock *to : function->basicBlocks()) {
- if (to->isRemoved())
- continue;
-
- qout << '\t';
- BasicBlockIndex from = idom.at(to->index());
- if (from != InvalidBasicBlockIndex)
- qout << from;
- else
- qout << "(none)";
- qout << " dominates " << to->index() << endl;
- }
- qDebug("%s", buf.data().constData());
- }
- }
-
- void setImmediateDominator(BasicBlock *bb, BasicBlock *newDominator)
- {
- Q_ASSERT(bb->index() >= 0);
- Q_ASSERT(!newDominator || newDominator->index() >= 0);
-
- if (static_cast<std::vector<BasicBlockIndex>::size_type>(bb->index()) >= idom.size()) {
- // This is a new block, probably introduced by edge splitting. So, we'll have to grow
- // the array before inserting the immediate dominator.
- idom.resize(function->basicBlockCount(), InvalidBasicBlockIndex);
- }
-
- const BasicBlockIndex newIdx = newDominator ? newDominator->index() : InvalidBasicBlockIndex;
- if (DebugImmediateDominators)
- qDebug() << "Setting idom of" << bb->index() << "from" << idom[bb->index()] << "to" << newIdx;
- idom[bb->index()] = newIdx;
- }
-
- void collectSiblings(BasicBlock *node, BasicBlockSet &siblings)
- {
- siblings.insert(node);
- const BasicBlockIndex dominator = idom[node->index()];
- if (dominator == InvalidBasicBlockIndex)
- return;
- for (size_t i = 0, ei = idom.size(); i != ei; ++i) {
- if (idom[i] == dominator) {
- BasicBlock *bb = function->basicBlock(int(i));
- if (!bb->isRemoved())
- siblings.insert(bb);
- }
- }
- }
-
- void recalculateIDoms(const BasicBlockSet &nodes, BasicBlock *limit = 0)
- {
- const BasicBlockIndex limitIndex = limit ? limit->index() : InvalidBasicBlockIndex;
- BasicBlockSet todo(nodes), postponed(function);
- while (!todo.empty())
- recalculateIDom(*todo.begin(), todo, postponed, limitIndex);
- }
-
- bool dominates(BasicBlock *dominator, BasicBlock *dominated) const {
- return dominates(dominator->index(), dominated->index());
- }
-
- struct Cmp {
- std::vector<int> *nodeDepths;
- Cmp(std::vector<int> *nodeDepths)
- : nodeDepths(nodeDepths)
- { Q_ASSERT(nodeDepths); }
- bool operator()(BasicBlock *one, BasicBlock *two) const
- {
- if (one->isRemoved())
- return false;
- if (two->isRemoved())
- return true;
- return nodeDepths->at(one->index()) > nodeDepths->at(two->index());
- }
- };
-
- // Calculate a depth-first iteration order on the nodes of the dominator tree.
- //
- // The order of the nodes in the vector is not the same as one where a recursive depth-first
- // iteration is done on a tree. Rather, the nodes are (reverse) sorted on tree depth.
- // So for the:
- // 1 dominates 2
- // 2 dominates 3
- // 3 dominates 4
- // 2 dominates 5
- // the order will be:
- // 4, 3, 5, 2, 1
- // or:
- // 4, 5, 3, 2, 1
- // So the order of nodes on the same depth is undefined, but it will be after the nodes
- // they dominate, and before the nodes that dominate them.
- //
- // The reason for this order is that a proper DFS pre-/post-order would require inverting
- // the idom vector by either building a real tree datastructure or by searching the idoms
- // for siblings and children. Both have a higher time complexity than sorting by depth.
- QVector<BasicBlock *> calculateDFNodeIterOrder() const
- {
- std::vector<int> depths = calculateNodeDepths();
- QVector<BasicBlock *> order = function->basicBlocks();
- std::sort(order.begin(), order.end(), Cmp(&depths));
- for (int i = 0; i < order.size(); ) {
- if (order[i]->isRemoved())
- order.remove(i);
- else
- ++i;
- }
- return order;
- }
-
- void mergeIntoPredecessor(BasicBlock *successor)
- {
- int succIdx = successor->index();
- if (succIdx == InvalidBasicBlockIndex) {
- return;
- }
-
- int succDom = idom[unsigned(succIdx)];
- for (BasicBlockIndex &idx : idom) {
- if (idx == succIdx) {
- idx = succDom;
- }
- }
- }
-
-private:
- bool dominates(BasicBlockIndex dominator, BasicBlockIndex dominated) const {
- // dominator can be Invalid when the dominated block has no dominator (i.e. the start node)
- Q_ASSERT(dominated != InvalidBasicBlockIndex);
-
- if (dominator == dominated)
- return false;
-
- for (BasicBlockIndex it = idom[dominated]; it != InvalidBasicBlockIndex; it = idom[it]) {
- if (it == dominator)
- return true;
- }
-
- return false;
- }
-
- // Algorithm:
- // - for each node:
- // - get the depth of a node. If it's unknown (-1):
- // - get the depth of the immediate dominator.
- // - if that's unknown too, calculate it by calling calculateNodeDepth
- // - set the current node's depth to that of immediate dominator + 1
- std::vector<int> calculateNodeDepths() const
- {
- std::vector<int> nodeDepths(size_t(function->basicBlockCount()), -1);
- nodeDepths[0] = 0;
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- int &bbDepth = nodeDepths[bb->index()];
- if (bbDepth == -1) {
- const int immDom = idom[bb->index()];
- int immDomDepth = nodeDepths[immDom];
- if (immDomDepth == -1)
- immDomDepth = calculateNodeDepth(immDom, nodeDepths);
- bbDepth = immDomDepth + 1;
- }
- }
- return nodeDepths;
- }
-
- // Algorithm:
- // - search for the first dominator of a node that has a known depth. As all nodes are
- // reachable from the start node, and that node's depth is 0, this is finite.
- // - while doing that search, put all unknown nodes in the worklist
- // - pop all nodes from the worklist, and set their depth to the previous' (== dominating)
- // node's depth + 1
- // This way every node's depth is calculated once, and the complexity is O(n).
- int calculateNodeDepth(int nodeIdx, std::vector<int> &nodeDepths) const
- {
- std::vector<int> worklist;
- worklist.reserve(8);
- int depth = -1;
-
- do {
- worklist.push_back(nodeIdx);
- nodeIdx = idom[nodeIdx];
- depth = nodeDepths[nodeIdx];
- } while (depth == -1);
-
- for (std::vector<int>::const_reverse_iterator it = worklist.rbegin(), eit = worklist.rend(); it != eit; ++it)
- nodeDepths[*it] = ++depth;
-
- return depth;
- }
-
- // The immediate-dominator recalculation is used when edges are removed from the CFG. See
- // [Ramalingam] for a description. Note that instead of calculating the priority, a recursive
- // algorithm is used: when recalculating the immediate dominator of a node by looking for the
- // least-common ancestor, and a node is hit that also needs recalculation, a recursive call
- // is done to calculate that nodes immediate dominator first.
- //
- // Note that this simplified algorithm cannot cope with back-edges. It only works for
- // non-looping edges (which is our use-case).
- void recalculateIDom(BasicBlock *node, BasicBlockSet &todo, BasicBlockSet &postponed, BasicBlockIndex limit) {
- Q_ASSERT(!postponed.contains(node));
- Q_ASSERT(todo.contains(node));
- todo.remove(node);
-
- if (node->in.size() == 1) {
- // Special case: if the node has only one incoming edge, then that is the immediate
- // dominator.
- setImmediateDominator(node, node->in.first());
- return;
- }
-
- std::vector<BasicBlockIndex> prefix;
- prefix.reserve(32);
-
- for (BasicBlock *in : node->in) {
- if (node == in) // back-edge to self
- continue;
- if (dominates(node->index(), in->index())) // a known back-edge
- continue;
-
- if (prefix.empty()) {
- calculatePrefix(node, in, prefix, todo, postponed, limit);
-
- if (!prefix.empty()) {
- std::reverse(prefix.begin(), prefix.end());
- Q_ASSERT(!prefix.empty());
- Q_ASSERT(prefix.front() == limit || limit == InvalidBasicBlockIndex);
- }
- } else {
- std::vector<BasicBlockIndex> anotherPrefix;
- anotherPrefix.reserve(prefix.size());
- calculatePrefix(node, in, anotherPrefix, todo, postponed, limit);
-
- if (!anotherPrefix.empty())
- commonPrefix(prefix, anotherPrefix);
- }
- }
-
- Q_ASSERT(!prefix.empty());
- idom[node->index()] = prefix.back();
- }
-
- void calculatePrefix(BasicBlock *node, BasicBlock *in, std::vector<BasicBlockIndex> &prefix, BasicBlockSet &todo, BasicBlockSet &postponed, BasicBlockIndex limit)
- {
- for (BasicBlockIndex it = in->index(); it != InvalidBasicBlockIndex; it = idom[it]) {
- prefix.push_back(it);
- if (it == limit)
- return;
- BasicBlock *n = function->basicBlock(it);
- if (postponed.contains(n)) { // possible back-edge, bail out.
- prefix.clear();
- return;
- }
- if (todo.contains(n)) {
- postponed.insert(node);
- recalculateIDom(n, todo, postponed, limit);
- postponed.remove(node);
- }
- }
- }
-
- // Calculate the LCA (Least Common Ancestor) by finding the longest common prefix between two
- // dominator chains. Note that "anotherPrefix" has the node's immediate dominator first, while
- // "bestPrefix" has it last (meaning: is in reverse order). The reason for this is that removing
- // nodes from "bestPrefix" is cheaper because it's done at the end of the vector, while
- // reversing all "anotherPrefix" nodes would take unnecessary time.
- static void commonPrefix(std::vector<BasicBlockIndex> &bestPrefix, const std::vector<BasicBlockIndex> &anotherPrefix)
- {
- const size_t anotherSize = anotherPrefix.size();
- size_t minLen = qMin(bestPrefix.size(), anotherPrefix.size());
- while (minLen != 0) {
- --minLen;
- if (bestPrefix[minLen] == anotherPrefix[anotherSize - minLen - 1]) {
- ++minLen;
- break;
- }
- }
- if (minLen != bestPrefix.size())
- bestPrefix.erase(bestPrefix.begin() + minLen, bestPrefix.end());
- }
-};
-
-class VariableCollector {
- std::vector<Temp> _allTemps;
- std::vector<BasicBlockSet> _defsites;
- std::vector<std::vector<int> > A_orig;
- BitVector nonLocals;
- BitVector killed;
-
- BasicBlock *currentBB;
- bool isCollectable(Temp *t) const
- {
- Q_UNUSED(t);
- Q_ASSERT(t->kind != Temp::PhysicalRegister && t->kind != Temp::StackSlot);
- return true;
- }
-
- void addDefInCurrentBlock(Temp *t)
- {
- std::vector<int> &temps = A_orig[currentBB->index()];
- if (std::find(temps.begin(), temps.end(), t->index) == temps.end())
- temps.push_back(t->index);
- }
-
- void addTemp(Temp *t)
- {
- if (_allTemps[t->index].kind == Temp::Invalid)
- _allTemps[t->index] = *t;
- }
-
-public:
- VariableCollector(IR::Function *function)
- {
- _allTemps.resize(function->tempCount);
- _defsites.resize(function->tempCount);
- for (int i = 0; i < function->tempCount; ++i)
- _defsites[i].init(function);
- nonLocals.resize(function->tempCount);
- const size_t ei = function->basicBlockCount();
- A_orig.resize(ei);
- for (size_t i = 0; i != ei; ++i)
- A_orig[i].reserve(8);
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- currentBB = bb;
- killed.assign(function->tempCount, false);
- for (Stmt *s : bb->statements())
- visit(s);
- }
- }
-
- const std::vector<Temp> &allTemps() const
- { return _allTemps; }
-
- void collectDefSites(const Temp &n, std::vector<BasicBlock *> &bbs) const {
- Q_ASSERT(!n.isInvalid());
- Q_ASSERT(n.index < _defsites.size());
- _defsites[n.index].collectValues(bbs);
- }
-
- const std::vector<int> &inBlock(BasicBlock *n) const
- {
- return A_orig.at(n->index());
- }
-
- bool isNonLocal(const Temp &var) const
- {
- Q_ASSERT(!var.isInvalid());
- Q_ASSERT(static_cast<int>(var.index) < nonLocals.size());
- return nonLocals.at(var.index);
- }
-
-private:
- void visit(Stmt *s)
- {
- if (s->asPhi()) {
- // nothing to do
- } else if (auto move = s->asMove()) {
- visit(move->source);
-
- if (Temp *t = move->target->asTemp()) {
- addTemp(t);
-
- if (isCollectable(t)) {
- _defsites[t->index].insert(currentBB);
- addDefInCurrentBlock(t);
-
- // For semi-pruned SSA:
- killed.setBit(t->index);
- }
- } else {
- visit(move->target);
- }
- } else {
- STMT_VISIT_ALL_KINDS(s)
- }
- }
-
- void visit(Expr *e)
- {
- if (auto t = e->asTemp()) {
- addTemp(t);
-
- if (isCollectable(t)) {
- if (!killed.at(t->index)) {
- nonLocals.setBit(t->index);
- }
- }
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-};
-
-struct UntypedTemp {
- Temp temp;
- UntypedTemp() {}
- UntypedTemp(const Temp &t): temp(t) {}
-};
-inline bool operator==(const UntypedTemp &t1, const UntypedTemp &t2) Q_DECL_NOTHROW
-{ return t1.temp.index == t2.temp.index && t1.temp.kind == t2.temp.kind; }
-inline bool operator!=(const UntypedTemp &t1, const UntypedTemp &t2) Q_DECL_NOTHROW
-{ return !(t1 == t2); }
-
-class DefUses
-{
-public:
- struct DefUse {
- DefUse()
- : defStmt(0)
- , blockOfStatement(0)
- { uses.reserve(8); }
- Temp temp;
- Stmt *defStmt;
- BasicBlock *blockOfStatement;
- QVector<Stmt *> uses;
-
- bool isValid() const
- { return temp.kind != Temp::Invalid; }
-
- void clear()
- { defStmt = 0; blockOfStatement = 0; uses.clear(); }
- };
-
-private:
- std::vector<DefUse> _defUses;
- typedef QVarLengthArray<Temp, 4> Temps;
- std::vector<Temps> _usesPerStatement;
-
- void ensure(Temp *newTemp)
- {
- if (_defUses.size() <= newTemp->index) {
- _defUses.resize(newTemp->index + 1);
- }
- }
-
- void ensure(Stmt *s)
- {
- Q_ASSERT(s->id() >= 0);
- if (static_cast<unsigned>(s->id()) >= _usesPerStatement.size()) {
- _usesPerStatement.resize(s->id() + 1);
- }
- }
-
- void addUseForStatement(Stmt *s, const Temp &var)
- {
- ensure(s);
- _usesPerStatement[s->id()].push_back(var);
- }
-
-public:
- DefUses(IR::Function *function)
- {
- _usesPerStatement.resize(function->statementCount());
- _defUses.resize(function->tempCount);
- }
-
- void cleanup()
- {
- for (size_t i = 0, ei = _defUses.size(); i != ei; ++i) {
- DefUse &defUse = _defUses[i];
- if (defUse.isValid() && !defUse.defStmt)
- defUse.clear();
- }
- }
-
- unsigned statementCount() const
- { return unsigned(_usesPerStatement.size()); }
-
- unsigned tempCount() const
- { return unsigned(_defUses.size()); }
-
- const Temp &temp(int idx) const
- { return _defUses[idx].temp; }
-
- void addDef(Temp *newTemp, Stmt *defStmt, BasicBlock *defBlock)
- {
- ensure(newTemp);
- DefUse &defUse = _defUses[newTemp->index];
- Q_ASSERT(!defUse.isValid());
- defUse.temp = *newTemp;
- defUse.defStmt = defStmt;
- defUse.blockOfStatement = defBlock;
- }
-
- QVector<UntypedTemp> defsUntyped() const
- {
- QVector<UntypedTemp> res;
- res.reserve(tempCount());
- for (const DefUse &du : _defUses)
- if (du.isValid())
- res.append(UntypedTemp(du.temp));
- return res;
- }
-
- std::vector<const Temp *> defs() const {
- std::vector<const Temp *> res;
- const size_t ei = _defUses.size();
- res.reserve(ei);
- for (size_t i = 0; i != ei; ++i) {
- const DefUse &du = _defUses.at(i);
- if (du.isValid())
- res.push_back(&du.temp);
- }
- return res;
- }
-
- void removeDef(const Temp &variable) {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- _defUses[variable.index].clear();
- }
-
- void addUses(const Temp &variable, const QVector<Stmt *> &newUses)
- {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- QVector<Stmt *> &uses = _defUses[variable.index].uses;
- for (Stmt *stmt : newUses)
- if (std::find(uses.begin(), uses.end(), stmt) == uses.end())
- uses.push_back(stmt);
- }
-
- void addUse(const Temp &variable, Stmt *newUse)
- {
- if (_defUses.size() <= variable.index) {
- _defUses.resize(variable.index + 1);
- DefUse &du = _defUses[variable.index];
- du.temp = variable;
- du.uses.push_back(newUse);
- addUseForStatement(newUse, variable);
- return;
- }
-
- QVector<Stmt *> &uses = _defUses[variable.index].uses;
- if (std::find(uses.begin(), uses.end(), newUse) == uses.end())
- uses.push_back(newUse);
- addUseForStatement(newUse, variable);
- }
-
- int useCount(const Temp &variable) const
- {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- return _defUses[variable.index].uses.size();
- }
-
- Stmt *defStmt(const Temp &variable) const
- {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- return _defUses[variable.index].defStmt;
- }
-
- BasicBlock *defStmtBlock(const Temp &variable) const
- {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- return _defUses[variable.index].blockOfStatement;
- }
-
- void replaceBasicBlock(BasicBlock *from, BasicBlock *to)
- {
- for (auto &du : _defUses) {
- if (du.blockOfStatement == from) {
- du.blockOfStatement = to;
- }
- }
- }
-
- void removeUse(Stmt *usingStmt, const Temp &var)
- {
- Q_ASSERT(static_cast<unsigned>(var.index) < _defUses.size());
- QVector<Stmt *> &uses = _defUses[var.index].uses;
- uses.erase(std::remove(uses.begin(), uses.end(), usingStmt), uses.end());
- }
-
- void registerNewStatement(Stmt *s)
- {
- ensure(s);
- }
-
- const Temps &usedVars(Stmt *s) const
- {
- Q_ASSERT(s->id() >= 0);
- Q_ASSERT(static_cast<unsigned>(s->id()) < _usesPerStatement.size());
- return _usesPerStatement[s->id()];
- }
-
- const QVector<Stmt *> &uses(const Temp &var) const
- {
- return _defUses[var.index].uses;
- }
-
- QVector<Stmt*> removeDefUses(Stmt *s)
- {
- QVector<Stmt*> defStmts;
- for (const Temp &usedVar : usedVars(s)) {
- if (Stmt *ds = defStmt(usedVar))
- defStmts += ds;
- removeUse(s, usedVar);
- }
- if (Move *m = s->asMove()) {
- if (Temp *t = m->target->asTemp())
- removeDef(*t);
- } else if (Phi *p = s->asPhi()) {
- removeDef(*p->targetTemp);
- }
-
- return defStmts;
- }
-
- void dump() const
- {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Defines and uses:" << endl;
- for (const DefUse &du : _defUses) {
- if (!du.isValid())
- continue;
- qout << '%' << du.temp.index;
- qout << " -> defined in block " << du.blockOfStatement->index()
- << ", statement: " << du.defStmt->id()
- << endl;
- qout << " uses:";
- for (Stmt *s : du.uses)
- qout << ' ' << s->id();
- qout << endl;
- }
- qout << "Uses per statement:" << endl;
- for (size_t i = 0, ei = _usesPerStatement.size(); i != ei; ++i) {
- qout << " " << i << ":";
- for (const Temp &t : _usesPerStatement[i])
- qout << ' ' << t.index;
- qout << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-};
-
-void insertPhiNode(const Temp &a, BasicBlock *y, IR::Function *f) {
- Phi *phiNode = f->NewStmt<Phi>();
- phiNode->targetTemp = f->New<Temp>();
- phiNode->targetTemp->init(a.kind, a.index);
- y->prependStatement(phiNode);
-
- phiNode->incoming.resize(y->in.size());
- for (int i = 0, ei = y->in.size(); i < ei; ++i) {
- Temp *t = f->New<Temp>();
- t->init(a.kind, a.index);
- phiNode->incoming[i] = t;
- }
-}
-
-// High-level (recursive) algorithm:
-// Mapping: old temp number -> new temp number
-//
-// Start:
-// Rename(start-node)
-//
-// Rename(node, mapping):
-// for each statement S in block n
-// if S not in a phi-function
-// for each use of some variable x in S
-// y = mapping[x]
-// replace the use of x with y in S
-// for each definition of some variable a in S [1]
-// a_new = generate new/unique temp
-// mapping[a] = a_new
-// replace definition of a with definition of a_new in S
-// for each successor Y of block n
-// Suppose n is the j-th predecessor of Y
-// for each phi function in Y
-// suppose the j-th operand of the phi-function is a
-// i = mapping[a]
-// replace the j-th operand with a_i
-// for each child X of n [2]
-// Rename(X)
-// for each newly generated temp from step [1] restore the old value [3]
-//
-// This algorithm can run out of CPU stack space when there are lots of basic-blocks, like in a
-// switch statement with 8000 cases that all fall-through. The iterativer version below uses a
-// work-item stack, where step [1] from the algorithm above also pushes an "undo mapping change",
-// and step [2] pushes a "rename(X)" action. This eliminates step [3].
-//
-// Iterative version:
-// Mapping: old temp number -> new temp number
-//
-// The stack can hold two kinds of actions:
-// "Rename basic block n"
-// "Restore count for temp"
-//
-// Start:
-// counter = 0
-// push "Rename start node" onto the stack
-// while the stack is not empty:
-// take the last item, and process it
-//
-// Rename(n) =
-// for each statement S in block n
-// if S not in a phi-function
-// for each use of some variable x in S
-// y = mapping[x]
-// replace the use of x with y in S
-// for each definition of some variable a in S
-// old = mapping[a]
-// push Undo(a, old)
-// counter = counter + 1
-// new = counter;
-// mapping[a] = new
-// replace definition of a with definition of a_new in S
-// for each successor Y of block n
-// Suppose n is the j-th predecessor of Y
-// for each phi function in Y
-// suppose the j-th operand of the phi-function is a
-// i = mapping[a]
-// replace the j-th operand with a_i
-// for each child X of n
-// push Rename(X)
-//
-// Undo(t, c) =
-// mapping[t] = c
-class VariableRenamer
-{
- Q_DISABLE_COPY(VariableRenamer)
-
- IR::Function *function;
- DefUses &defUses;
- unsigned tempCount;
-
- typedef std::vector<int> Mapping; // maps from existing/old temp number to the new and unique temp number.
- enum { Absent = -1 };
- Mapping vregMapping;
- ProcessedBlocks processed;
-
- BasicBlock *currentBB;
- Stmt *currentStmt;
-
- struct TodoAction {
- enum { RestoreVReg, Rename } action;
- union {
- struct {
- unsigned temp;
- int previous;
- } restoreData;
- struct {
- BasicBlock *basicBlock;
- } renameData;
- };
-
- bool isValid() const { return action != Rename || renameData.basicBlock != 0; }
-
- TodoAction()
- {
- action = Rename;
- renameData.basicBlock = 0;
- }
-
- TodoAction(const Temp &t, int prev)
- {
- Q_ASSERT(t.kind == Temp::VirtualRegister);
-
- action = RestoreVReg;
- restoreData.temp = t.index;
- restoreData.previous = prev;
- }
-
- TodoAction(BasicBlock *bb)
- {
- Q_ASSERT(bb);
-
- action = Rename;
- renameData.basicBlock = bb;
- }
- };
-
- QVector<TodoAction> todo;
-
-public:
- VariableRenamer(IR::Function *f, DefUses &defUses)
- : function(f)
- , defUses(defUses)
- , tempCount(0)
- , processed(f)
- {
- vregMapping.assign(f->tempCount, Absent);
- todo.reserve(f->basicBlockCount());
- }
-
- void run() {
- todo.append(TodoAction(function->basicBlock(0)));
-
- while (!todo.isEmpty()) {
- TodoAction todoAction = todo.back();
- Q_ASSERT(todoAction.isValid());
- todo.pop_back();
-
- switch (todoAction.action) {
- case TodoAction::Rename:
- rename(todoAction.renameData.basicBlock);
- break;
- case TodoAction::RestoreVReg:
- restore(vregMapping, todoAction.restoreData.temp, todoAction.restoreData.previous);
- break;
- default:
- Q_UNREACHABLE();
- }
- }
-
- function->tempCount = tempCount;
- }
-
-private:
- static inline void restore(Mapping &mapping, int temp, int previous)
- {
- mapping[temp] = previous;
- }
-
- void rename(BasicBlock *bb)
- {
- while (bb && !processed.alreadyProcessed(bb)) {
- renameStatementsAndPhis(bb);
- processed.markAsProcessed(bb);
-
- BasicBlock *next = 0;
- for (BasicBlock *out : bb->out) {
- if (processed.alreadyProcessed(out))
- continue;
- if (!next)
- next = out;
- else
- todo.append(TodoAction(out));
- }
- bb = next;
- }
- }
-
- void renameStatementsAndPhis(BasicBlock *bb)
- {
- currentBB = bb;
-
- for (Stmt *s : bb->statements()) {
- currentStmt = s;
- visit(s);
- }
-
- for (BasicBlock *Y : bb->out) {
- const int j = Y->in.indexOf(bb);
- Q_ASSERT(j >= 0 && j < Y->in.size());
- for (Stmt *s : Y->statements()) {
- if (Phi *phi = s->asPhi()) {
- Temp *t = phi->incoming[j]->asTemp();
- unsigned newTmp = currentNumber(*t);
-// qDebug()<<"I: replacing phi use"<<a<<"with"<<newTmp<<"in L"<<Y->index;
- t->index = newTmp;
- t->kind = Temp::VirtualRegister;
- defUses.addUse(*t, phi);
- } else {
- break;
- }
- }
- }
- }
-
- unsigned currentNumber(const Temp &t)
- {
- int nr = Absent;
- switch (t.kind) {
- case Temp::VirtualRegister:
- nr = vregMapping[t.index];
- break;
- default:
- Q_UNREACHABLE();
- nr = Absent;
- break;
- }
- if (nr == Absent) {
- // Special case: we didn't prune the Phi nodes yet, so for proper temps (virtual
- // registers) the SSA algorithm might insert superfluous Phis that have uses without
- // definition. E.g.: if a temporary got introduced in the "then" clause, it "could"
- // reach the "end-if" block, so there will be a phi node for that temp. A later pass
- // will clean this up by looking for uses-without-defines in phi nodes. So, what we do
- // is to generate a new unique number, and leave it dangling.
- nr = nextFreeTemp(t);
- }
-
- return nr;
- }
-
- unsigned nextFreeTemp(const Temp &t)
- {
- unsigned newIndex = tempCount++;
- Q_ASSERT(newIndex <= INT_MAX);
- int oldIndex = Absent;
-
- switch (t.kind) {
- case Temp::VirtualRegister:
- oldIndex = vregMapping[t.index];
- vregMapping[t.index] = newIndex;
- break;
- default:
- Q_UNREACHABLE();
- }
-
- todo.append(TodoAction(t, oldIndex));
-
- return newIndex;
- }
-
-private:
- void visit(Stmt *s)
- {
- if (auto move = s->asMove()) {
- // uses:
- visit(move->source);
-
- // defs:
- if (Temp *t = move->target->asTemp()) {
- renameTemp(t);
- } else {
- visit(move->target);
- }
- } else if (auto phi = s->asPhi()) {
- renameTemp(phi->targetTemp);
- } else {
- STMT_VISIT_ALL_KINDS(s);
- }
- }
-
- void visit(Expr *e)
- {
- if (auto temp = e->asTemp()) {
- temp->index = currentNumber(*temp);
- temp->kind = Temp::VirtualRegister;
- defUses.addUse(*temp, currentStmt);
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-
- void renameTemp(Temp *t) { // only called for defs, not uses
- const int newIdx = nextFreeTemp(*t);
-// qDebug()<<"I: replacing def of"<<a<<"with"<<newIdx;
- t->kind = Temp::VirtualRegister;
- t->index = newIdx;
- defUses.addDef(t, currentStmt, currentBB);
- }
-};
-
-// This function converts the IR to semi-pruned SSA form. For details about SSA and the algorightm,
-// see [Appel]. For the changes needed for semi-pruned SSA form, and for its advantages, see [Briggs].
-void convertToSSA(IR::Function *function, const DominatorTree &df, DefUses &defUses)
-{
- // Collect all applicable variables:
- VariableCollector variables(function);
-
- // Prepare for phi node insertion:
- std::vector<BitVector > A_phi;
- const size_t ei = function->basicBlockCount();
- A_phi.resize(ei);
- for (size_t i = 0; i != ei; ++i)
- A_phi[i].assign(function->tempCount, false);
-
- std::vector<BasicBlock *> W;
- W.reserve(8);
-
- // Place phi functions:
- for (const Temp &a : variables.allTemps()) {
- if (a.isInvalid())
- continue;
- if (!variables.isNonLocal(a))
- continue; // for semi-pruned SSA
-
- W.clear();
- variables.collectDefSites(a, W);
- while (!W.empty()) {
- BasicBlock *n = W.back();
- W.pop_back();
- const BasicBlockSet &dominatorFrontierForN = df.dominatorFrontier(n);
- for (BasicBlockSet::const_iterator it = dominatorFrontierForN.begin(), eit = dominatorFrontierForN.end();
- it != eit; ++it) {
- BasicBlock *y = *it;
- if (!A_phi.at(y->index()).at(a.index)) {
- insertPhiNode(a, y, function);
- A_phi[y->index()].setBit(a.index);
- const std::vector<int> &varsInBlockY = variables.inBlock(y);
- if (std::find(varsInBlockY.begin(), varsInBlockY.end(), a.index) == varsInBlockY.end())
- W.push_back(y);
- }
- }
- }
- }
-
- // Rename variables:
- VariableRenamer(function, defUses).run();
-}
-
-/// Calculate if a phi node result is used only by other phi nodes, and if those uses are
-/// in turn also used by other phi nodes.
-bool hasPhiOnlyUses(Phi *phi, const DefUses &defUses, QBitArray &collectedPhis)
-{
- collectedPhis.setBit(phi->id());
-
- for (Stmt *use : defUses.uses(*phi->targetTemp)) {
- Phi *dependentPhi = use->asPhi();
- if (!dependentPhi)
- return false; // there is a use by a non-phi node
-
- if (collectedPhis.at(dependentPhi->id()))
- continue; // we already found this node
-
- if (!hasPhiOnlyUses(dependentPhi, defUses, collectedPhis))
- return false;
- }
-
- return true;
-}
-
-void cleanupPhis(DefUses &defUses)
-{
- QBitArray toRemove(defUses.statementCount());
- QBitArray collectedPhis(defUses.statementCount());
- std::vector<Phi *> allPhis;
- allPhis.reserve(32);
-
- for (const Temp *def : defUses.defs()) {
- Stmt *defStmt = defUses.defStmt(*def);
- if (!defStmt)
- continue;
-
- Phi *phi = defStmt->asPhi();
- if (!phi)
- continue;
- allPhis.push_back(phi);
- if (toRemove.at(phi->id()))
- continue;
-
- collectedPhis.fill(false);
- if (hasPhiOnlyUses(phi, defUses, collectedPhis))
- toRemove |= collectedPhis;
- }
-
- for (Phi *phi : allPhis) {
- if (!toRemove.at(phi->id()))
- continue;
-
- const Temp &targetVar = *phi->targetTemp;
- defUses.defStmtBlock(targetVar)->removeStatement(phi);
-
- for (const Temp &usedVar : defUses.usedVars(phi))
- defUses.removeUse(phi, usedVar);
- defUses.removeDef(targetVar);
- }
-
- defUses.cleanup();
-}
-
-class StatementWorklist
-{
- IR::Function *theFunction;
- std::vector<Stmt *> stmts;
- BitVector worklist;
- unsigned worklistSize;
- std::vector<int> replaced;
- BitVector removed;
-
- Q_DISABLE_COPY(StatementWorklist)
-
-public:
- StatementWorklist(IR::Function *function)
- : theFunction(function)
- , stmts(function->statementCount(), static_cast<Stmt *>(0))
- , worklist(function->statementCount(), false)
- , worklistSize(0)
- , replaced(function->statementCount(), Stmt::InvalidId)
- , removed(function->statementCount())
- {
- grow();
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- for (Stmt *s : bb->statements()) {
- if (!s)
- continue;
-
- stmts[s->id()] = s;
- worklist.setBit(s->id());
- ++worklistSize;
- }
- }
- }
-
- void reset()
- {
- worklist.assign(worklist.size(), false);
- worklistSize = 0;
-
- for (Stmt *s : stmts) {
- if (!s)
- continue;
-
- worklist.setBit(s->id());
- ++worklistSize;
- }
-
- replaced.assign(replaced.size(), Stmt::InvalidId);
- removed.assign(removed.size(), false);
- }
-
- void remove(Stmt *stmt)
- {
- replaced[stmt->id()] = Stmt::InvalidId;
- removed.setBit(stmt->id());
- if (worklist.at(stmt->id())) {
- worklist.clearBit(stmt->id());
- Q_ASSERT(worklistSize > 0);
- --worklistSize;
- }
- }
-
- void replace(Stmt *oldStmt, Stmt *newStmt)
- {
- Q_ASSERT(oldStmt);
- Q_ASSERT(replaced[oldStmt->id()] == Stmt::InvalidId);
- Q_ASSERT(removed.at(oldStmt->id()) == false);
-
- Q_ASSERT(newStmt);
- registerNewStatement(newStmt);
- Q_ASSERT(replaced[newStmt->id()] == Stmt::InvalidId);
- Q_ASSERT(removed.at(newStmt->id()) == false);
-
- replaced[oldStmt->id()] = newStmt->id();
- worklist.clearBit(oldStmt->id());
- }
-
- void applyToFunction()
- {
- for (BasicBlock *bb : theFunction->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- for (int i = 0; i < bb->statementCount();) {
- Stmt *stmt = bb->statements().at(i);
-
- int id = stmt->id();
- Q_ASSERT(id != Stmt::InvalidId);
- Q_ASSERT(static_cast<unsigned>(stmt->id()) < stmts.size());
-
- for (int replacementId = replaced[id]; replacementId != Stmt::InvalidId; replacementId = replaced[replacementId])
- id = replacementId;
- Q_ASSERT(id != Stmt::InvalidId);
- Q_ASSERT(static_cast<unsigned>(stmt->id()) < stmts.size());
-
- if (removed.at(id)) {
- bb->removeStatement(i);
- } else {
- if (id != stmt->id())
- bb->replaceStatement(i, stmts[id]);
-
- ++i;
- }
- }
- }
-
- replaced.assign(replaced.size(), Stmt::InvalidId);
- removed.assign(removed.size(), false);
- }
-
- StatementWorklist &operator+=(const QVector<Stmt *> &stmts)
- {
- for (Stmt *s : stmts)
- this->operator+=(s);
-
- return *this;
- }
-
- StatementWorklist &operator+=(Stmt *s)
- {
- if (!s)
- return *this;
-
- Q_ASSERT(s->id() >= 0);
- Q_ASSERT(s->id() < worklist.size());
-
- if (!worklist.at(s->id())) {
- worklist.setBit(s->id());
- ++worklistSize;
- }
-
- return *this;
- }
-
- StatementWorklist &operator-=(Stmt *s)
- {
- Q_ASSERT(s->id() >= 0);
- Q_ASSERT(s->id() < worklist.size());
-
- if (worklist.at(s->id())) {
- worklist.clearBit(s->id());
- Q_ASSERT(worklistSize > 0);
- --worklistSize;
- }
-
- return *this;
- }
-
- unsigned size() const
- {
- return worklistSize;
- }
-
- Stmt *takeNext(Stmt *last)
- {
- if (worklistSize == 0)
- return 0;
-
- const int startAt = last ? last->id() + 1 : 0;
- Q_ASSERT(startAt >= 0);
- Q_ASSERT(startAt <= worklist.size());
-
- Q_ASSERT(static_cast<size_t>(worklist.size()) == stmts.size());
-
- int pos = worklist.findNext(startAt, true, /*wrapAround = */true);
-
- worklist.clearBit(pos);
- Q_ASSERT(worklistSize > 0);
- --worklistSize;
- Stmt *s = stmts.at(pos);
- Q_ASSERT(s);
-
- if (removed.at(s->id()))
- return takeNext(s);
-
- return s;
- }
-
- IR::Function *function() const
- {
- return theFunction;
- }
-
- void registerNewStatement(Stmt *s)
- {
- Q_ASSERT(s->id() >= 0);
- if (static_cast<unsigned>(s->id()) >= stmts.size()) {
- if (static_cast<unsigned>(s->id()) >= stmts.capacity())
- grow();
-
- int newSize = s->id() + 1;
- stmts.resize(newSize, 0);
- worklist.resize(newSize);
- replaced.resize(newSize, Stmt::InvalidId);
- removed.resize(newSize);
- }
-
- stmts[s->id()] = s;
- }
-
-private:
- void grow()
- {
- Q_ASSERT(stmts.capacity() < INT_MAX / 2);
- int newCapacity = ((static_cast<int>(stmts.capacity()) + 1) * 3) / 2;
- stmts.reserve(newCapacity);
- worklist.reserve(newCapacity);
- replaced.reserve(newCapacity);
- removed.reserve(newCapacity);
- }
-};
-
-class SideEffectsChecker
-{
- bool _sideEffect;
-
-public:
- SideEffectsChecker()
- : _sideEffect(false)
- {}
-
- ~SideEffectsChecker()
- {}
-
- bool hasSideEffects(Expr *expr)
- {
- bool sideEffect = false;
- qSwap(_sideEffect, sideEffect);
- visit(expr);
- qSwap(_sideEffect, sideEffect);
- return sideEffect;
- }
-
-protected:
- void markAsSideEffect()
- {
- _sideEffect = true;
- }
-
- bool seenSideEffects() const { return _sideEffect; }
-
- void visit(Expr *e)
- {
- if (auto n = e->asName()) {
- visitName(n);
- } else if (auto t = e->asTemp()) {
- visitTemp(t);
- } else if (auto c = e->asClosure()) {
- visitClosure(c);
- } else if (auto c = e->asConvert()) {
- visitConvert(c);
- } else if (auto u = e->asUnop()) {
- visitUnop(u);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- }
- }
-
- virtual void visitTemp(Temp *) {}
-
-private:
- void visitName(Name *e) {
- if (e->freeOfSideEffects)
- return;
- // TODO: maybe we can distinguish between built-ins of which we know that they do not have
- // a side-effect.
- if (e->builtin == Name::builtin_invalid || (e->id && *e->id != QLatin1String("this")))
- markAsSideEffect();
- }
-
- void visitClosure(Closure *) {
- markAsSideEffect();
- }
-
- void visitConvert(Convert *e) {
- visit(e->expr);
-
- switch (e->expr->type) {
- case QObjectType:
- case StringType:
- case VarType:
- markAsSideEffect();
- break;
- default:
- break;
- }
- }
-
- void visitUnop(Unop *e) {
- visit(e->expr);
-
- switch (e->op) {
- case OpUPlus:
- case OpUMinus:
- case OpNot:
- case OpPreIncrement:
- case OpPreDecrement:
- if (e->expr->type == VarType || e->expr->type == StringType || e->expr->type == QObjectType)
- markAsSideEffect();
- break;
-
- default:
- break;
- }
- }
-
- void visitBinop(Binop *e) {
- // TODO: prune parts that don't have a side-effect. For example, in:
- // function f(x) { +x+1; return 0; }
- // we can prune the binop and leave the unop/conversion.
- _sideEffect = hasSideEffects(e->left);
- _sideEffect |= hasSideEffects(e->right);
-
- if (e->left->type == VarType || e->left->type == StringType || e->left->type == QObjectType
- || e->right->type == VarType || e->right->type == StringType || e->right->type == QObjectType)
- markAsSideEffect();
- }
-
- void visitSubscript(Subscript *e) {
- visit(e->base);
- visit(e->index);
- markAsSideEffect();
- }
-
- void visitMember(Member *e) {
- visit(e->base);
- if (e->freeOfSideEffects)
- return;
- markAsSideEffect();
- }
-
- void visitCall(Call *e) {
- visit(e->base);
- for (ExprList *args = e->args; args; args = args->next)
- visit(args->expr);
- markAsSideEffect(); // TODO: there are built-in functions that have no side effect.
- }
-
- void visitNew(New *e) {
- visit(e->base);
- for (ExprList *args = e->args; args; args = args->next)
- visit(args->expr);
- markAsSideEffect(); // TODO: there are built-in types that have no side effect.
- }
-};
-
-class EliminateDeadCode: public SideEffectsChecker
-{
- DefUses &_defUses;
- StatementWorklist &_worklist;
- QVarLengthArray<Temp *, 8> _collectedTemps;
-
-public:
- EliminateDeadCode(DefUses &defUses, StatementWorklist &worklist)
- : _defUses(defUses)
- , _worklist(worklist)
- {}
-
- void run(Expr *&expr, Stmt *stmt) {
- _collectedTemps.clear();
- if (!hasSideEffects(expr)) {
- expr = 0;
- for (Temp *t : _collectedTemps) {
- _defUses.removeUse(stmt, *t);
- _worklist += _defUses.defStmt(*t);
- }
- }
- }
-
-protected:
- void visitTemp(Temp *e) Q_DECL_OVERRIDE Q_DECL_FINAL
- {
- _collectedTemps.append(e);
- }
-};
-
-class PropagateTempTypes
-{
- const DefUses &defUses;
- UntypedTemp theTemp;
- DiscoveredType newType;
-
-public:
- PropagateTempTypes(const DefUses &defUses)
- : defUses(defUses)
- {}
-
- void run(const UntypedTemp &temp, const DiscoveredType &type)
- {
- newType = type;
- theTemp = temp;
- if (Stmt *defStmt = defUses.defStmt(temp.temp))
- visit(defStmt);
- for (Stmt *use : defUses.uses(temp.temp))
- visit(use);
- }
-
-private:
- void visit(Stmt *s)
- {
- STMT_VISIT_ALL_KINDS(s);
- }
-
- void visit(Expr *e)
- {
- if (auto temp = e->asTemp()) {
- if (theTemp == UntypedTemp(*temp)) {
- temp->type = static_cast<Type>(newType.type);
- temp->memberResolver = newType.memberResolver;
- }
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-};
-
-class TypeInference
-{
- enum { DebugTypeInference = 0 };
-
- QQmlEnginePrivate *qmlEngine;
- const DefUses &_defUses;
- typedef std::vector<DiscoveredType> TempTypes;
- TempTypes _tempTypes;
- StatementWorklist *_worklist;
- struct TypingResult {
- DiscoveredType type;
- bool fullyTyped;
-
- TypingResult(const DiscoveredType &type = DiscoveredType()) {
-#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 6
- // avoid optimization bug in gcc 4.6.3 armhf
- ((int volatile &) this->type.type) = type.type;
-#endif
- this->type = type;
- fullyTyped = type.type != UnknownType;
- }
- explicit TypingResult(MemberExpressionResolver *memberResolver)
- : type(memberResolver)
- , fullyTyped(true)
- {}
- };
- TypingResult _ty;
- Stmt *_currentStmt;
-
-public:
- TypeInference(QQmlEnginePrivate *qmlEngine, const DefUses &defUses)
- : qmlEngine(qmlEngine)
- , _defUses(defUses)
- , _tempTypes(_defUses.tempCount())
- , _worklist(0)
- , _ty(UnknownType)
- , _currentStmt(nullptr)
- {}
-
- void run(StatementWorklist &w) {
- _worklist = &w;
-
- Stmt *s = 0;
- while ((s = _worklist->takeNext(s))) {
- if (s->asJump())
- continue;
-
- if (DebugTypeInference) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout<<"Typing stmt ";
- IRPrinter(&qout).print(s);
- qout.flush();
- qDebug("%s", buf.data().constData());
-
- qDebug("%u left in the worklist", _worklist->size());
- }
-
- if (!run(s)) {
- *_worklist += s;
- if (DebugTypeInference) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout<<"Pushing back stmt: ";
- IRPrinter(&qout).print(s);
- qout.flush();
- qDebug("%s", buf.data().constData());
- }
- } else {
- if (DebugTypeInference) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout<<"Finished: ";
- IRPrinter(&qout).print(s);
- qout.flush();
- qDebug("%s", buf.data().constData());
- }
- }
- }
-
- PropagateTempTypes propagator(_defUses);
- for (size_t i = 0, ei = _tempTypes.size(); i != ei; ++i) {
- const Temp &temp = _defUses.temp(int(i));
- if (temp.kind == Temp::Invalid)
- continue;
- const DiscoveredType &tempType = _tempTypes[i];
- if (tempType.type == UnknownType)
- continue;
- propagator.run(temp, tempType);
- }
-
- _worklist = 0;
- }
-
-private:
- bool run(Stmt *s) {
- TypingResult ty;
- std::swap(_ty, ty);
- std::swap(_currentStmt, s);
- visit(_currentStmt);
- std::swap(_currentStmt, s);
- std::swap(_ty, ty);
- return ty.fullyTyped;
- }
-
- TypingResult run(Expr *e) {
- TypingResult ty;
- std::swap(_ty, ty);
- visit(e);
- std::swap(_ty, ty);
-
- if (ty.type != UnknownType)
- setType(e, ty.type);
- return ty;
- }
-
- void setType(Expr *e, DiscoveredType ty) {
- if (Temp *t = e->asTemp()) {
- if (DebugTypeInference)
- qDebug() << "Setting type for temp" << t->index
- << " to " << typeName(Type(ty.type)) << "(" << ty.type << ")"
- << endl;
-
- DiscoveredType &it = _tempTypes[t->index];
- if (it != ty) {
- it = ty;
-
- if (DebugTypeInference) {
- for (Stmt *s : _defUses.uses(*t)) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Pushing back dependent stmt: ";
- IRPrinter(&qout).print(s);
- qout.flush();
- qDebug("%s", buf.data().constData());
- }
- }
-
- for (Stmt *s : qAsConst(_defUses.uses(*t))) {
- if (s != _currentStmt) {
- *_worklist += s;
- }
- }
- }
- } else {
- e->type = (Type) ty.type;
- }
- }
-
-private:
- void visit(Expr *e)
- {
- if (auto c = e->asConst()) {
- visitConst(c);
- } else if (auto s = e->asString()) {
- visitString(s);
- } else if (auto r = e->asRegExp()) {
- visitRegExp(r);
- } else if (auto n = e->asName()) {
- visitName(n);
- } else if (auto t = e->asTemp()) {
- visitTemp(t);
- } else if (auto a = e->asArgLocal()) {
- visitArgLocal(a);
- } else if (auto c = e->asClosure()) {
- visitClosure(c);
- } else if (auto c = e->asConvert()) {
- visitConvert(c);
- } else if (auto u = e->asUnop()) {
- visitUnop(u);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitConst(Const *c) {
- if (c->type & NumberType) {
- if (canConvertToSignedInteger(c->value))
- _ty = TypingResult(SInt32Type);
- else if (canConvertToUnsignedInteger(c->value))
- _ty = TypingResult(UInt32Type);
- else
- _ty = TypingResult(c->type);
- } else
- _ty = TypingResult(c->type);
- }
- void visitString(IR::String *) { _ty = TypingResult(StringType); }
- void visitRegExp(IR::RegExp *) { _ty = TypingResult(VarType); }
- void visitName(Name *) { _ty = TypingResult(VarType); }
- void visitTemp(Temp *e) {
- if (e->memberResolver && e->memberResolver->isValid())
- _ty = TypingResult(e->memberResolver);
- else
- _ty = TypingResult(_tempTypes[e->index]);
- setType(e, _ty.type);
- }
- void visitArgLocal(ArgLocal *e) {
- _ty = TypingResult(VarType);
- setType(e, _ty.type);
- }
-
- void visitClosure(Closure *) { _ty = TypingResult(VarType); }
- void visitConvert(Convert *e) {
- _ty = TypingResult(e->type);
- }
-
- void visitUnop(Unop *e) {
- _ty = run(e->expr);
- switch (e->op) {
- case OpUPlus: _ty.type = DoubleType; return;
- case OpUMinus: _ty.type = DoubleType; return;
- case OpCompl: _ty.type = SInt32Type; return;
- case OpNot: _ty.type = BoolType; return;
-
- case OpPreIncrement:
- case OpPreDecrement:
- Q_ASSERT(!"Inplace operators should have been removed!");
- Q_UNREACHABLE();
- default:
- Q_UNIMPLEMENTED();
- Q_UNREACHABLE();
- }
- }
-
- void visitBinop(Binop *e) {
- TypingResult leftTy = run(e->left);
- TypingResult rightTy = run(e->right);
- _ty.fullyTyped = leftTy.fullyTyped && rightTy.fullyTyped;
-
- switch (e->op) {
- case OpAdd:
- if (leftTy.type.test(VarType) || leftTy.type.test(QObjectType) || rightTy.type.test(VarType) || rightTy.type.test(QObjectType))
- _ty.type = VarType;
- else if (leftTy.type.test(StringType) || rightTy.type.test(StringType))
- _ty.type = StringType;
- else if (leftTy.type != UnknownType && rightTy.type != UnknownType)
- _ty.type = DoubleType;
- else
- _ty.type = UnknownType;
- break;
- case OpSub:
- _ty.type = DoubleType;
- break;
-
- case OpMul:
- case OpDiv:
- case OpMod:
- _ty.type = DoubleType;
- break;
-
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- case OpLShift:
- case OpRShift:
- _ty.type = SInt32Type;
- break;
- case OpURShift:
- _ty.type = UInt32Type;
- break;
-
- case OpGt:
- case OpLt:
- case OpGe:
- case OpLe:
- case OpEqual:
- case OpNotEqual:
- case OpStrictEqual:
- case OpStrictNotEqual:
- case OpAnd:
- case OpOr:
- case OpInstanceof:
- case OpIn:
- _ty.type = BoolType;
- break;
-
- default:
- Q_UNIMPLEMENTED();
- Q_UNREACHABLE();
- }
- }
-
- void visitCall(Call *e) {
- _ty = run(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- _ty.fullyTyped &= run(it->expr).fullyTyped;
- _ty.type = VarType;
- }
- void visitNew(New *e) {
- _ty = run(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- _ty.fullyTyped &= run(it->expr).fullyTyped;
- _ty.type = VarType;
- }
- void visitSubscript(Subscript *e) {
- _ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped;
- _ty.type = VarType;
- }
-
- void visitMember(Member *e) {
- _ty = run(e->base);
-
- if (_ty.fullyTyped && _ty.type.memberResolver && _ty.type.memberResolver->isValid()) {
- MemberExpressionResolver *resolver = _ty.type.memberResolver;
- _ty.type = resolver->resolveMember(qmlEngine, resolver, e);
- } else
- _ty.type = VarType;
- }
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto j = s->asJump()) {
- visitJump(j);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitExp(Exp *s) { _ty = run(s->expr); }
- void visitMove(Move *s) {
- if (Temp *t = s->target->asTemp()) {
- if (Name *n = s->source->asName()) {
- if (n->builtin == Name::builtin_qml_context) {
- _ty = TypingResult(t->memberResolver);
- setType(n, _ty.type);
- setType(t, _ty.type);
- return;
- }
- }
- TypingResult sourceTy = run(s->source);
- setType(t, sourceTy.type);
- _ty = sourceTy;
- return;
- }
-
- TypingResult sourceTy = run(s->source);
- _ty = run(s->target);
- _ty.fullyTyped &= sourceTy.fullyTyped;
- }
-
- void visitJump(Jump *) { _ty = TypingResult(MissingType); }
- void visitCJump(CJump *s) { _ty = run(s->cond); }
- void visitRet(Ret *s) { _ty = run(s->expr); }
- void visitPhi(Phi *s) {
- _ty = run(s->incoming[0]);
- for (int i = 1, ei = s->incoming.size(); i != ei; ++i) {
- TypingResult ty = run(s->incoming[i]);
- if (!ty.fullyTyped && _ty.fullyTyped) {
- // When one of the temps not fully typed, we already know that we cannot completely type this node.
- // So, pick the type we calculated upto this point, and wait until the unknown one will be typed.
- // At that point, this statement will be re-scheduled, and then we can fully type this node.
- _ty.fullyTyped = false;
- break;
- }
- _ty.type.type |= ty.type.type;
- _ty.fullyTyped &= ty.fullyTyped;
- if (_ty.type.test(QObjectType) && _ty.type.memberResolver)
- _ty.type.memberResolver->clear(); // ### TODO: find common ancestor meta-object
- }
-
- switch (_ty.type.type) {
- case UnknownType:
- case UndefinedType:
- case NullType:
- case BoolType:
- case SInt32Type:
- case UInt32Type:
- case DoubleType:
- case StringType:
- case QObjectType:
- case VarType:
- // The type is not a combination of two or more types, so we're done.
- break;
-
- default:
- // There are multiple types involved, so:
- if (_ty.type.isNumber())
- // The type is any combination of double/int32/uint32, but nothing else. So we can
- // type it as double.
- _ty.type = DoubleType;
- else
- // There just is no single type that can hold this combination, so:
- _ty.type = VarType;
- }
-
- setType(s->targetTemp, _ty.type);
- }
-};
-
-class ReverseInference
-{
- const DefUses &_defUses;
-
-public:
- ReverseInference(const DefUses &defUses)
- : _defUses(defUses)
- {}
-
- void run(IR::Function *f)
- {
- Q_UNUSED(f);
-
- QVector<UntypedTemp> knownOk;
- QVector<UntypedTemp> candidates = _defUses.defsUntyped();
- while (!candidates.isEmpty()) {
- UntypedTemp temp = candidates.last();
- candidates.removeLast();
-
- if (knownOk.contains(temp))
- continue;
-
- if (!isUsedAsInt32(temp, knownOk))
- continue;
-
- Stmt *s = _defUses.defStmt(temp.temp);
- Move *m = s->asMove();
- if (!m)
- continue;
- Temp *target = m->target->asTemp();
- if (!target || temp != UntypedTemp(*target) || target->type == SInt32Type)
- continue;
- if (Temp *t = m->source->asTemp()) {
- candidates.append(*t);
- } else if (m->source->asConvert()) {
- break;
- } else if (Binop *b = m->source->asBinop()) {
- bool iterateOnOperands = true;
-
- switch (b->op) {
- case OpSub:
- case OpMul:
- case OpAdd:
- if (b->left->type == SInt32Type && b->right->type == SInt32Type) {
- iterateOnOperands = false;
- break;
- } else {
- continue;
- }
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- case OpLShift:
- case OpRShift:
- case OpURShift:
- break;
- default:
- continue;
- }
-
- if (iterateOnOperands) {
- if (Temp *lt = b->left->asTemp())
- candidates.append(*lt);
- if (Temp *rt = b->right->asTemp())
- candidates.append(*rt);
- }
- } else if (Unop *u = m->source->asUnop()) {
- if (u->op == OpCompl || u->op == OpUPlus) {
- if (Temp *t = u->expr->asTemp())
- candidates.append(*t);
- }
- } else {
- continue;
- }
-
- knownOk.append(temp);
- }
-
- PropagateTempTypes propagator(_defUses);
- for (const UntypedTemp &t : qAsConst(knownOk)) {
- propagator.run(t, SInt32Type);
- if (Stmt *defStmt = _defUses.defStmt(t.temp)) {
- if (Move *m = defStmt->asMove()) {
- if (Convert *c = m->source->asConvert()) {
- c->type = SInt32Type;
- } else if (Unop *u = m->source->asUnop()) {
- if (u->op != OpUMinus)
- u->type = SInt32Type;
- } else if (Binop *b = m->source->asBinop()) {
- b->type = SInt32Type;
- }
- }
- }
- }
- }
-
-private:
- bool isUsedAsInt32(const UntypedTemp &t, const QVector<UntypedTemp> &knownOk) const
- {
- const QVector<Stmt *> &uses = _defUses.uses(t.temp);
- if (uses.isEmpty())
- return false;
-
- for (Stmt *use : uses) {
- if (Move *m = use->asMove()) {
- Temp *targetTemp = m->target->asTemp();
-
- if (m->source->asTemp()) {
- if (!targetTemp || !knownOk.contains(*targetTemp))
- return false;
- } else if (m->source->asConvert()) {
- continue;
- } else if (Binop *b = m->source->asBinop()) {
- switch (b->op) {
- case OpAdd:
- case OpSub:
- case OpMul:
- if (!targetTemp || !knownOk.contains(*targetTemp))
- return false;
- Q_FALLTHROUGH();
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- case OpRShift:
- case OpLShift:
- case OpURShift:
- continue;
- default:
- return false;
- }
- } else if (Unop *u = m->source->asUnop()) {
- if (u->op == OpUPlus) {
- if (!targetTemp || !knownOk.contains(*targetTemp))
- return false;
- } else if (u->op != OpCompl) {
- return false;
- }
- } else {
- return false;
- }
- } else
- return false;
- }
-
- return true;
- }
-};
-
-void convertConst(Const *c, Type targetType)
-{
- switch (targetType) {
- case DoubleType:
- break;
- case SInt32Type:
- c->value = QV4::Primitive::toInt32(c->value);
- break;
- case UInt32Type:
- c->value = QV4::Primitive::toUInt32(c->value);
- break;
- case BoolType:
- c->value = !(c->value == 0 || std::isnan(c->value));
- break;
- case NullType:
- case UndefinedType:
- c->value = qt_qnan();
- c->type = targetType;
- break;
- default:
- Q_UNIMPLEMENTED();
- Q_ASSERT(!"Unimplemented!");
- break;
- }
- c->type = targetType;
-}
-
-class TypePropagation
-{
- DefUses &_defUses;
- Type _ty;
- IR::Function *_f;
-
- bool run(Expr *&e, Type requestedType = UnknownType, bool insertConversion = true) {
- qSwap(_ty, requestedType);
- visit(e);
- qSwap(_ty, requestedType);
-
- if (requestedType != UnknownType) {
- if (e->type != requestedType) {
- if (requestedType & NumberType || requestedType == BoolType) {
- if (insertConversion)
- addConversion(e, requestedType);
- return true;
- }
- }
- }
-
- return false;
- }
-
- struct Conversion {
- Expr **expr;
- Type targetType;
- Stmt *stmt;
-
- Conversion(Expr **expr = 0, Type targetType = UnknownType, Stmt *stmt = 0)
- : expr(expr)
- , targetType(targetType)
- , stmt(stmt)
- {}
- };
-
- Stmt *_currStmt;
- QVector<Conversion> _conversions;
-
- void addConversion(Expr *&expr, Type targetType) {
- _conversions.append(Conversion(&expr, targetType, _currStmt));
- }
-
-public:
- TypePropagation(DefUses &defUses) : _defUses(defUses), _ty(UnknownType) {}
-
- void run(IR::Function *f, StatementWorklist &worklist) {
- _f = f;
- for (BasicBlock *bb : f->basicBlocks()) {
- if (bb->isRemoved())
- continue;
- _conversions.clear();
-
- for (Stmt *s : bb->statements()) {
- _currStmt = s;
- visit(s);
- }
-
- for (const Conversion &conversion : qAsConst(_conversions)) {
- IR::Move *move = conversion.stmt->asMove();
-
- // Note: isel only supports move into member when source is a temp, so convert
- // is not a supported source.
- if (move && move->source->asTemp() && !move->target->asMember()) {
- *conversion.expr = bb->CONVERT(*conversion.expr, conversion.targetType);
- } else if (Const *c = (*conversion.expr)->asConst()) {
- convertConst(c, conversion.targetType);
- } else if (ArgLocal *al = (*conversion.expr)->asArgLocal()) {
- Temp *target = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer));
- target->type = conversion.targetType;
- Expr *convert = bb->CONVERT(al, conversion.targetType);
- Move *convCall = f->NewStmt<Move>();
- worklist.registerNewStatement(convCall);
- convCall->init(target, convert);
- _defUses.addDef(target, convCall, bb);
-
- Temp *source = bb->TEMP(target->index);
- source->type = conversion.targetType;
- _defUses.addUse(*source, conversion.stmt);
-
- if (conversion.stmt->asPhi()) {
- // Only temps can be used as arguments to phi nodes, so this is a sanity check...:
- Q_UNREACHABLE();
- } else {
- bb->insertStatementBefore(conversion.stmt, convCall);
- }
-
- *conversion.expr = source;
- } else if (Temp *t = (*conversion.expr)->asTemp()) {
- Temp *target = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer));
- target->type = conversion.targetType;
- Expr *convert = bb->CONVERT(t, conversion.targetType);
- Move *convCall = f->NewStmt<Move>();
- worklist.registerNewStatement(convCall);
- convCall->init(target, convert);
- _defUses.addDef(target, convCall, bb);
- _defUses.addUse(*t, convCall);
-
- Temp *source = bb->TEMP(target->index);
- source->type = conversion.targetType;
- _defUses.removeUse(conversion.stmt, *t);
- _defUses.addUse(*source, conversion.stmt);
-
- if (Phi *phi = conversion.stmt->asPhi()) {
- int idx = phi->incoming.indexOf(t);
- Q_ASSERT(idx != -1);
- bb->in[idx]->insertStatementBeforeTerminator(convCall);
- } else {
- bb->insertStatementBefore(conversion.stmt, convCall);
- }
-
- *conversion.expr = source;
- } else if (Unop *u = (*conversion.expr)->asUnop()) {
- // convert:
- // int32{%2} = double{-double{%1}};
- // to:
- // double{%3} = double{-double{%1}};
- // int32{%2} = int32{convert(double{%3})};
- Temp *tmp = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer));
- tmp->type = u->type;
- Move *extraMove = f->NewStmt<Move>();
- worklist.registerNewStatement(extraMove);
- extraMove->init(tmp, u);
- _defUses.addDef(tmp, extraMove, bb);
-
- if (Temp *unopOperand = u->expr->asTemp()) {
- _defUses.addUse(*unopOperand, extraMove);
- _defUses.removeUse(move, *unopOperand);
- }
-
- bb->insertStatementBefore(conversion.stmt, extraMove);
-
- *conversion.expr = bb->CONVERT(CloneExpr::cloneTemp(tmp, f), conversion.targetType);
- _defUses.addUse(*tmp, move);
- } else {
- Q_UNREACHABLE();
- }
- }
- }
- }
-
-private:
- void visit(Expr *e)
- {
- if (auto c = e->asConst()) {
- visitConst(c);
- } else if (auto c = e->asConvert()) {
- run(c->expr, c->type);
- } else if (auto u = e->asUnop()) {
- run(u->expr, u->type);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- }
- }
-
- void visitConst(Const *c) {
- if (_ty & NumberType && c->type & NumberType) {
- if (_ty == SInt32Type)
- c->value = QV4::Primitive::toInt32(c->value);
- else if (_ty == UInt32Type)
- c->value = QV4::Primitive::toUInt32(c->value);
- c->type = _ty;
- }
- }
-
- void visitBinop(Binop *e) {
- // FIXME: This routine needs more tuning!
- switch (e->op) {
- case OpAdd:
- case OpSub:
- case OpMul:
- case OpDiv:
- case OpMod:
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- run(e->left, e->type);
- run(e->right, e->type);
- break;
-
- case OpLShift:
- case OpRShift:
- case OpURShift:
- run(e->left, SInt32Type);
- run(e->right, SInt32Type);
- break;
-
- case OpGt:
- case OpLt:
- case OpGe:
- case OpLe:
- case OpEqual:
- case OpNotEqual:
- if (e->left->type == DoubleType) {
- run(e->right, DoubleType);
- } else if (e->right->type == DoubleType) {
- run(e->left, DoubleType);
- } else {
- run(e->left, e->left->type);
- run(e->right, e->right->type);
- }
- break;
-
- case OpStrictEqual:
- case OpStrictNotEqual:
- case OpInstanceof:
- case OpIn:
- run(e->left, e->left->type);
- run(e->right, e->right->type);
- break;
-
- default:
- Q_UNIMPLEMENTED();
- Q_UNREACHABLE();
- }
- }
- void visitCall(Call *e) {
- run(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- run(it->expr);
- }
- void visitNew(New *e) {
- run(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- run(it->expr);
- }
- void visitSubscript(Subscript *e) { run(e->base); run(e->index); }
- void visitMember(Member *e) { run(e->base); }
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- }
- }
-
- void visitExp(Exp *s) { run(s->expr); }
- void visitMove(Move *s) {
- if (s->source->asConvert())
- return; // this statement got inserted for a phi-node type conversion
-
- run(s->target);
-
- if (Unop *u = s->source->asUnop()) {
- if (u->op == OpUPlus) {
- if (run(u->expr, s->target->type, false)) {
- Convert *convert = _f->New<Convert>();
- convert->init(u->expr, s->target->type);
- s->source = convert;
- } else {
- s->source = u->expr;
- }
-
- return;
- }
- }
-
- const Member *targetMember = s->target->asMember();
- const bool inhibitConversion = targetMember && targetMember->inhibitTypeConversionOnWrite;
-
- run(s->source, s->target->type, !inhibitConversion);
- }
- void visitCJump(CJump *s) {
- run(s->cond, BoolType);
- }
- void visitRet(Ret *s) { run(s->expr); }
- void visitPhi(Phi *s) {
- Type ty = s->targetTemp->type;
- for (int i = 0, ei = s->incoming.size(); i != ei; ++i)
- run(s->incoming[i], ty);
- }
-};
-
-void splitCriticalEdges(IR::Function *f, DominatorTree &df, StatementWorklist &worklist, DefUses &defUses)
-{
- const QVector<BasicBlock *> copy = f->basicBlocks();
- for (BasicBlock *toBB : copy) {
- if (toBB->isRemoved())
- continue;
- if (toBB->in.size() < 2)
- continue;
-
- for (int inIdx = 0, eInIdx = toBB->in.size(); inIdx != eInIdx; ++inIdx) {
- BasicBlock *fromBB = toBB->in[inIdx];
- if (fromBB->out.size() < 2)
- continue;
-
- // We found a critical edge.
- // create the basic block:
- BasicBlock *newBB = f->newBasicBlock(toBB->catchBlock);
- Jump *s = f->NewStmt<Jump>();
- worklist.registerNewStatement(s);
- defUses.registerNewStatement(s);
- s->init(toBB);
- newBB->appendStatement(s);
-
- // rewire the old outgoing edge
- int outIdx = fromBB->out.indexOf(toBB);
- fromBB->out[outIdx] = newBB;
- newBB->in.append(fromBB);
-
- // rewire the old incoming edge
- toBB->in[inIdx] = newBB;
- newBB->out.append(toBB);
-
- // add newBB to the correct loop group
- if (toBB->isGroupStart()) {
- if (fromBB == toBB) {
- // special case: the loop header points back to itself (so it's a small loop).
- newBB->setContainingGroup(toBB);
- } else {
- BasicBlock *container;
- for (container = fromBB->containingGroup(); container; container = container->containingGroup())
- if (container == toBB)
- break;
- if (container == toBB) // if we were already inside the toBB loop
- newBB->setContainingGroup(toBB);
- else
- newBB->setContainingGroup(toBB->containingGroup());
- }
- } else {
- newBB->setContainingGroup(toBB->containingGroup());
- }
-
- // patch the terminator
- Stmt *terminator = fromBB->terminator();
- if (Jump *j = terminator->asJump()) {
- Q_ASSERT(outIdx == 0);
- j->target = newBB;
- } else if (CJump *j = terminator->asCJump()) {
- if (outIdx == 0)
- j->iftrue = newBB;
- else if (outIdx == 1)
- j->iffalse = newBB;
- else
- Q_ASSERT(!"Invalid out edge index for CJUMP!");
- } else if (terminator->asRet()) {
- Q_ASSERT(!"A block with a RET at the end cannot have outgoing edges.");
- } else {
- Q_ASSERT(!"Unknown terminator!");
- }
-
-// qDebug() << "splitting edge" << fromBB->index() << "->" << toBB->index()
-// << "by inserting block" << newBB->index();
-
- // Set the immediate dominator of the new block to inBB
- df.setImmediateDominator(newBB, fromBB);
-
- bool toNeedsNewIdom = true;
- for (BasicBlock *bb : toBB->in) {
- if (bb != newBB && bb != toBB && !df.dominates(toBB, bb)) {
- toNeedsNewIdom = false;
- break;
- }
- }
- if (toNeedsNewIdom)
- df.setImmediateDominator(toBB, newBB);
- }
- }
-}
-
-// Detect all (sub-)loops in a function.
-//
-// Doing loop detection on the CFG is better than relying on the statement information in
-// order to mark loops. Although JavaScript only has natural loops, it can still be the case
-// that something is not a loop even though a loop-like-statement is in the source. For
-// example:
-// while (true) {
-// if (i > 0)
-// break;
-// else
-// break;
-// }
-//
-// Algorithm:
-// - do a DFS on the dominator tree, where for each node:
-// - collect all back-edges
-// - if there are back-edges, the node is a loop-header for a new loop, so:
-// - walk the CFG is reverse-direction, and for every node:
-// - if the node already belongs to a loop, we've found a nested loop:
-// - get the loop-header for the (outermost) nested loop
-// - add that loop-header to the current loop
-// - continue by walking all incoming edges that do not yet belong to the current loop
-// - if the node does not belong to a loop yet, add it to the current loop, and
-// go on with all incoming edges
-//
-// Loop-header detection by checking for back-edges is very straight forward: a back-edge is
-// an incoming edge where the other node is dominated by the current node. Meaning: all
-// execution paths that reach that other node have to go through the current node, that other
-// node ends with a (conditional) jump back to the loop header.
-//
-// The exact order of the DFS on the dominator tree is not important. The only property has to
-// be that a node is only visited when all the nodes it dominates have been visited before.
-// The reason for the DFS is that for nested loops, the inner loop's loop-header is dominated
-// by the outer loop's header. So, by visiting depth-first, sub-loops are identified before
-// their containing loops, which makes nested-loop identification free. An added benefit is
-// that the nodes for those sub-loops are only processed once.
-//
-// Note: independent loops that share the same header are merged together. For example, in
-// the code snippet below, there are 2 back-edges into the loop-header, but only one single
-// loop will be detected.
-// while (a) {
-// if (b)
-// continue;
-// else
-// continue;
-// }
-class LoopDetection
-{
- enum { DebugLoopDetection = 0 };
-
- Q_DISABLE_COPY(LoopDetection)
-
-public:
- struct LoopInfo
- {
- BasicBlock *loopHeader;
- QVector<BasicBlock *> loopBody;
- QVector<LoopInfo *> nestedLoops;
- LoopInfo *parentLoop;
-
- LoopInfo(BasicBlock *loopHeader = 0)
- : loopHeader(loopHeader)
- , parentLoop(0)
- {}
-
- bool isValid() const
- { return loopHeader != 0; }
-
- void addNestedLoop(LoopInfo *nested)
- {
- Q_ASSERT(nested);
- Q_ASSERT(!nestedLoops.contains(nested));
- Q_ASSERT(nested->parentLoop == 0);
- nested->parentLoop = this;
- nestedLoops.append(nested);
- }
- };
-
-public:
- LoopDetection(const DominatorTree &dt)
- : dt(dt)
- {}
-
- ~LoopDetection()
- {
- qDeleteAll(loopInfos);
- }
-
- void run(IR::Function *function)
- {
- std::vector<BasicBlock *> backedges;
- backedges.reserve(4);
-
- const auto order = dt.calculateDFNodeIterOrder();
- for (BasicBlock *bb : order) {
- Q_ASSERT(!bb->isRemoved());
-
- backedges.clear();
-
- for (BasicBlock *in : bb->in)
- if (bb == in || dt.dominates(bb, in))
- backedges.push_back(in);
-
- if (!backedges.empty()) {
- subLoop(bb, backedges);
- }
- }
-
- createLoopInfos(function);
- dumpLoopInfo();
- }
-
- void dumpLoopInfo() const
- {
- if (!DebugLoopDetection)
- return;
-
- qDebug() << "Found" << loopInfos.size() << "loops";
- for (const LoopInfo *info : loopInfos) {
- qDebug() << "Loop header:" << info->loopHeader->index()
- << "for loop" << quint64(info);
- for (BasicBlock *bb : info->loopBody)
- qDebug() << " " << bb->index();
- for (LoopInfo *nested : info->nestedLoops)
- qDebug() << " sub loop:" << quint64(nested);
- qDebug() << " parent loop:" << quint64(info->parentLoop);
- }
- }
-
- QVector<LoopInfo *> allLoops() const
- { return loopInfos; }
-
- // returns all loop headers for loops that have no nested loops.
- QVector<LoopInfo *> innermostLoops() const
- {
- QVector<LoopInfo *> inner(loopInfos);
-
- for (int i = 0; i < inner.size(); ) {
- if (inner.at(i)->nestedLoops.isEmpty())
- ++i;
- else
- inner.remove(i);
- }
-
- return inner;
- }
-
-private:
- void subLoop(BasicBlock *loopHead, const std::vector<BasicBlock *> &backedges)
- {
- loopHead->markAsGroupStart();
- LoopInfo *info = new LoopInfo;
- info->loopHeader = loopHead;
- loopInfos.append(info);
-
- std::vector<BasicBlock *> worklist;
- worklist.reserve(backedges.size() + 8);
- worklist.insert(worklist.end(), backedges.begin(), backedges.end());
- while (!worklist.empty()) {
- BasicBlock *predIt = worklist.back();
- worklist.pop_back();
-
- BasicBlock *subloop = predIt->containingGroup();
- if (subloop) {
- // This is a discovered block. Find its outermost discovered loop.
- while (BasicBlock *parentLoop = subloop->containingGroup())
- subloop = parentLoop;
-
- // If it is already discovered to be a subloop of this loop, continue.
- if (subloop == loopHead)
- continue;
-
- // Yay, it's a subloop of this loop.
- subloop->setContainingGroup(loopHead);
- predIt = subloop;
-
- // Add all predecessors of the subloop header to the worklist, as long as
- // those predecessors are not in the current subloop. It might be the case
- // that they are in other loops, which we will then add as a subloop to the
- // current loop.
- for (BasicBlock *predIn : predIt->in)
- if (predIn->containingGroup() != subloop)
- worklist.push_back(predIn);
- } else {
- if (predIt == loopHead)
- continue;
-
- // This is an undiscovered block. Map it to the current loop.
- predIt->setContainingGroup(loopHead);
-
- // Add all incoming edges to the worklist.
- for (BasicBlock *bb : predIt->in)
- worklist.push_back(bb);
- }
- }
- }
-
-private:
- const DominatorTree &dt;
- QVector<LoopInfo *> loopInfos;
-
- void createLoopInfos(IR::Function *function)
- {
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
- if (BasicBlock *loopHeader = bb->containingGroup())
- findLoop(loopHeader)->loopBody.append(bb);
- }
-
- for (int i = 0, size = loopInfos.size(); i < size; ++i) {
- if (BasicBlock *containingLoopHeader = loopInfos.at(i)->loopHeader->containingGroup())
- findLoop(containingLoopHeader)->addNestedLoop(loopInfos.at(i));
- }
- }
-
- LoopInfo *findLoop(BasicBlock *loopHeader)
- {
- for (LoopInfo *info : qAsConst(loopInfos)) {
- if (info->loopHeader == loopHeader)
- return info;
- }
-
- Q_UNREACHABLE();
- return nullptr;
- }
-};
-
-// High-level algorithm:
-// 0. start with the first node (the start node) of a function
-// 1. emit the node
-// 2. add all outgoing edges that are not yet emitted to the postponed stack
-// 3. When the postponed stack is empty, pop a stack from the loop stack. If that is empty too,
-// we're done.
-// 4. pop a node from the postponed stack, and check if it can be scheduled:
-// a. if all incoming edges are scheduled, go to 4.
-// b. if an incoming edge is unscheduled, but it's a back-edge (an edge in a loop that jumps
-// back to the start of the loop), ignore it
-// c. if there is any unscheduled edge that is not a back-edge, ignore this node, and go to 4.
-// 5. if this node is the start of a loop, push the postponed stack on the loop stack.
-// 6. go back to 1.
-//
-// The postponing action in step 2 will put the node into its containing group. The case where this
-// is important is when a (labeled) continue or a (labeled) break statement occur in a loop: the
-// outgoing edge points to a node that is not part of the current loop (and possibly not of the
-// parent loop).
-//
-// Linear scan register allocation benefits greatly from short life-time intervals with few holes
-// (see for example section 4 (Lifetime Analysis) of [Wimmer1]). This algorithm makes sure that the
-// blocks of a group are scheduled together, with no non-loop blocks in between. This applies
-// recursively for nested loops. It also schedules groups of if-then-else-endif blocks together for
-// the same reason.
-class BlockScheduler
-{
- enum { DebugBlockScheduler = 0 };
-
- IR::Function *function;
- const DominatorTree &dominatorTree;
-
- struct WorkForGroup
- {
- BasicBlock *group;
- QStack<BasicBlock *> postponed;
-
- WorkForGroup(BasicBlock *group = 0): group(group) {}
- };
- WorkForGroup currentGroup;
- QStack<WorkForGroup> postponedGroups;
- QVector<BasicBlock *> sequence;
- ProcessedBlocks emitted;
- QHash<BasicBlock *, BasicBlock *> loopsStartEnd;
-
- bool checkCandidate(BasicBlock *candidate)
- {
- Q_ASSERT(candidate->containingGroup() == currentGroup.group);
-
- for (BasicBlock *in : candidate->in) {
- if (emitted.alreadyProcessed(in))
- continue;
-
- if (dominatorTree.dominates(candidate, in))
- // this is a loop, where there in -> candidate edge is the jump back to the top of the loop.
- continue;
-
- if (in == candidate)
- // this is a very tight loop, e.g.:
- // L1: ...
- // goto L1
- // This can happen when, for example, the basic-block merging gets rid of the empty
- // body block. In this case, we can safely schedule this block (if all other
- // incoming edges are either loop-back edges, or have been scheduled already).
- continue;
-
- return false; // an incoming edge that is not yet emitted, and is not a back-edge
- }
-
- if (candidate->isGroupStart()) {
- // postpone everything, and schedule the loop first.
- postponedGroups.push(currentGroup);
- currentGroup = WorkForGroup(candidate);
- }
-
- return true;
- }
-
- BasicBlock *pickNext()
- {
- while (true) {
- while (currentGroup.postponed.isEmpty()) {
- if (postponedGroups.isEmpty())
- return 0;
- if (currentGroup.group) // record the first and the last node of a group
- loopsStartEnd.insert(currentGroup.group, sequence.last());
- currentGroup = postponedGroups.pop();
- }
-
- BasicBlock *next = currentGroup.postponed.pop();
- if (checkCandidate(next))
- return next;
- }
-
- Q_UNREACHABLE();
- return 0;
- }
-
- void emitBlock(BasicBlock *bb)
- {
- Q_ASSERT(!bb->isRemoved());
- if (emitted.alreadyProcessed(bb))
- return;
-
- sequence.append(bb);
- emitted.markAsProcessed(bb);
- }
-
- void schedule(BasicBlock *functionEntryPoint)
- {
- BasicBlock *next = functionEntryPoint;
-
- while (next) {
- emitBlock(next);
- for (int i = next->out.size(); i != 0; ) {
- // postpone all outgoing edges, if they were not already processed
- --i;
- BasicBlock *out = next->out[i];
- if (!emitted.alreadyProcessed(out))
- postpone(out);
- }
- next = pickNext();
- }
- }
-
- void postpone(BasicBlock *bb)
- {
- if (currentGroup.group == bb->containingGroup()) {
- currentGroup.postponed.append(bb);
- return;
- }
-
- for (int i = postponedGroups.size(); i != 0; ) {
- --i;
- WorkForGroup &g = postponedGroups[i];
- if (g.group == bb->containingGroup()) {
- g.postponed.append(bb);
- return;
- }
- }
-
- Q_UNREACHABLE();
- }
-
- void dumpLoopStartsEnds() const
- {
- qDebug() << "Found" << loopsStartEnd.size() << "loops:";
- for (auto key : loopsStartEnd.keys())
- qDebug("Loop starting at L%d ends at L%d.", key->index(),
- loopsStartEnd.value(key)->index());
- }
-
-public:
- BlockScheduler(IR::Function *function, const DominatorTree &dominatorTree)
- : function(function)
- , dominatorTree(dominatorTree)
- , sequence(0)
- , emitted(function)
- {}
-
- QHash<BasicBlock *, BasicBlock *> go()
- {
- showMeTheCode(function, "Before block scheduling");
- if (DebugBlockScheduler)
- dominatorTree.dumpImmediateDominators();
-
- schedule(function->basicBlock(0));
-
- Q_ASSERT(function->liveBasicBlocksCount() == sequence.size());
- function->setScheduledBlocks(sequence);
- if (DebugBlockScheduler)
- dumpLoopStartsEnds();
- return loopsStartEnd;
- }
-};
-
-#ifndef QT_NO_DEBUG
-void checkCriticalEdges(const QVector<BasicBlock *> &basicBlocks) {
- for (BasicBlock *bb : basicBlocks) {
- if (bb && bb->out.size() > 1) {
- for (BasicBlock *bb2 : bb->out) {
- if (bb2 && bb2->in.size() > 1) {
- qDebug() << "found critical edge between block"
- << bb->index() << "and block" << bb2->index();
- Q_ASSERT(false);
- }
- }
- }
- }
-}
-#endif
-
-static void cleanupBasicBlocks(IR::Function *function)
-{
- showMeTheCode(function, "Before basic block cleanup");
-
- // Algorithm: this is the iterative version of a depth-first search for all blocks that are
- // reachable through outgoing edges, starting with the start block and all exception handler
- // blocks.
- QBitArray reachableBlocks(function->basicBlockCount());
- QVarLengthArray<BasicBlock *, 16> postponed;
- for (int i = 0, ei = function->basicBlockCount(); i != ei; ++i) {
- BasicBlock *bb = function->basicBlock(i);
- if (i == 0 || bb->isExceptionHandler())
- postponed.append(bb);
- }
-
- while (!postponed.isEmpty()) {
- BasicBlock *bb = postponed.back();
- postponed.pop_back();
- if (bb->isRemoved()) // this block was removed before, we don't need to clean it up.
- continue;
-
- reachableBlocks.setBit(bb->index());
-
- for (BasicBlock *outBB : bb->out) {
- if (!reachableBlocks.at(outBB->index()))
- postponed.append(outBB);
- }
- }
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved()) // the block has already been removed, so ignore it
- continue;
- if (reachableBlocks.at(bb->index())) // the block is reachable, so ignore it
- continue;
-
- for (BasicBlock *outBB : bb->out) {
- if (outBB->isRemoved() || !reachableBlocks.at(outBB->index()))
- continue; // We do not need to unlink from blocks that are scheduled to be removed.
-
- int idx = outBB->in.indexOf(bb);
- if (idx != -1) {
- outBB->in.remove(idx);
- for (Stmt *s : outBB->statements()) {
- if (Phi *phi = s->asPhi())
- phi->incoming.remove(idx);
- else
- break;
- }
- }
- }
-
- function->removeBasicBlock(bb);
- }
-
- showMeTheCode(function, "After basic block cleanup");
-}
-
-inline Const *isConstPhi(Phi *phi)
-{
- if (Const *c = phi->incoming[0]->asConst()) {
- for (int i = 1, ei = phi->incoming.size(); i != ei; ++i) {
- if (Const *cc = phi->incoming[i]->asConst()) {
- if (c->value != cc->value)
- return 0;
- if (!(c->type == cc->type || (c->type & NumberType && cc->type & NumberType)))
- return 0;
- if (int(c->value) == 0 && int(cc->value) == 0)
- if (isNegative(c->value) != isNegative(cc->value))
- return 0;
- } else {
- return 0;
- }
- }
- return c;
- }
- return 0;
-}
-
-static Expr *clone(Expr *e, IR::Function *function) {
- if (Temp *t = e->asTemp()) {
- return CloneExpr::cloneTemp(t, function);
- } else if (Const *c = e->asConst()) {
- return CloneExpr::cloneConst(c, function);
- } else if (Name *n = e->asName()) {
- return CloneExpr::cloneName(n, function);
- } else {
- Q_UNREACHABLE();
- return e;
- }
-}
-
-class ExprReplacer
-{
- DefUses &_defUses;
- IR::Function* _function;
- Temp *_toReplace;
- Expr *_replacement;
-
-public:
- ExprReplacer(DefUses &defUses, IR::Function *function)
- : _defUses(defUses)
- , _function(function)
- , _toReplace(0)
- , _replacement(0)
- {}
-
- bool operator()(Temp *toReplace, Expr *replacement, StatementWorklist &W, QVector<Stmt *> *newUses = 0)
- {
- Q_ASSERT(replacement->asTemp() || replacement->asConst() || replacement->asName());
-
- qSwap(_toReplace, toReplace);
- qSwap(_replacement, replacement);
-
- const QVector<Stmt *> &uses = _defUses.uses(*_toReplace);
-
- // Prevent the following:
- // L3:
- // %1 = phi L1: %2, L2: %3
- // %4 = phi L1: %5, L2: %6
- // %6 = %1
- // From turning into:
- // L3:
- // %1 = phi L1: %2, L2: %3
- // %4 = phi L1: %5, L2: %1
- //
- // Because both phi nodes are "executed in parallel", we cannot replace %6 by %1 in the
- // second phi node. So, if the defining statement for a temp is a phi node, and one of the
- // uses of the to-be-replaced statement is a phi node in the same block as the defining
- // statement, bail out.
- if (Temp *r = _replacement->asTemp()) {
- if (_defUses.defStmt(*r)->asPhi()) {
- BasicBlock *replacementDefBlock = _defUses.defStmtBlock(*r);
- for (Stmt *use : uses) {
- if (Phi *usePhi = use->asPhi()) {
- if (_defUses.defStmtBlock(*usePhi->targetTemp) == replacementDefBlock)
- return false;
- }
- }
- }
- }
-
-// qout << "Replacing ";toReplace->dump(qout);qout<<" by ";replacement->dump(qout);qout<<endl;
-
- if (newUses)
- newUses->reserve(uses.size());
-
-// qout << " " << uses.size() << " uses:"<<endl;
- for (Stmt *use : uses) {
-// qout<<" ";use->dump(qout);qout<<"\n";
- visit(use);
-// qout<<" -> ";use->dump(qout);qout<<"\n";
- W += use;
- if (newUses)
- newUses->push_back(use);
- }
-
- qSwap(_replacement, replacement);
- qSwap(_toReplace, toReplace);
- return true;
- }
-
-private:
- void visit(Expr *e)
- {
- if (auto c = e->asConst()) {
- visitConst(c);
- } else if (auto s = e->asString()) {
- visitString(s);
- } else if (auto r = e->asRegExp()) {
- visitRegExp(r);
- } else if (auto n = e->asName()) {
- visitName(n);
- } else if (auto t = e->asTemp()) {
- visitTemp(t);
- } else if (auto a = e->asArgLocal()) {
- visitArgLocal(a);
- } else if (auto c = e->asClosure()) {
- visitClosure(c);
- } else if (auto c = e->asConvert()) {
- visitConvert(c);
- } else if (auto u = e->asUnop()) {
- visitUnop(u);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitConst(Const *) {}
- void visitString(IR::String *) {}
- void visitRegExp(IR::RegExp *) {}
- void visitName(Name *) {}
- void visitTemp(Temp *) {}
- void visitArgLocal(ArgLocal *) {}
- void visitClosure(Closure *) {}
- void visitConvert(Convert *e) { check(e->expr); }
- void visitUnop(Unop *e) { check(e->expr); }
- void visitBinop(Binop *e) { check(e->left); check(e->right); }
- void visitCall(Call *e) {
- check(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- check(it->expr);
- }
- void visitNew(New *e) {
- check(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- check(it->expr);
- }
- void visitSubscript(Subscript *e) { check(e->base); check(e->index); }
- void visitMember(Member *e) { check(e->base); }
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto j = s->asJump()) {
- visitJump(j);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitExp(Exp *s) { check(s->expr); }
- void visitMove(Move *s) { check(s->target); check(s->source); }
- void visitJump(Jump *) {}
- void visitCJump(CJump *s) { check(s->cond); }
- void visitRet(Ret *s) { check(s->expr); }
- void visitPhi(Phi *s) {
- for (int i = 0, ei = s->incoming.size(); i != ei; ++i)
- check(s->incoming[i]);
- }
-
-private:
- void check(Expr *&e) {
- if (equals(e, _toReplace)) {
- e = clone(_replacement, _function);
- } else {
- visit(e);
- }
- }
-
- // This only calculates equality for everything needed by constant propagation
- bool equals(Expr *e1, Expr *e2) const {
- if (e1 == e2)
- return true;
-
- if (Const *c1 = e1->asConst()) {
- if (Const *c2 = e2->asConst())
- return c1->value == c2->value && (c1->type == c2->type || (c1->type & NumberType && c2->type & NumberType));
- } else if (Temp *t1 = e1->asTemp()) {
- if (Temp *t2 = e2->asTemp())
- return *t1 == *t2;
- } else if (Name *n1 = e1->asName()) {
- if (Name *n2 = e2->asName()) {
- if (n1->id) {
- if (n2->id)
- return *n1->id == *n2->id;
- } else {
- return n1->builtin == n2->builtin;
- }
- }
- }
-
- if (e1->type == IR::NullType && e2->type == IR::NullType)
- return true;
- if (e1->type == IR::UndefinedType && e2->type == IR::UndefinedType)
- return true;
-
- return false;
- }
-};
-
-namespace {
-/// This function removes the basic-block from the function's list, unlinks any uses and/or defs,
-/// and removes unreachable staements from the worklist, so that optimiseSSA won't consider them
-/// anymore.
-void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUses,
- StatementWorklist &W, DominatorTree &dt)
-{
- enum { DebugUnlinking = 0 };
-
- struct Util {
- static void removeIncomingEdge(BasicBlock *from, BasicBlock *to, DefUses &defUses, StatementWorklist &W)
- {
- int idx = to->in.indexOf(from);
- if (idx == -1)
- return;
-
- to->in.remove(idx);
- for (Stmt *outStmt : to->statements()) {
- if (!outStmt)
- continue;
- if (Phi *phi = outStmt->asPhi()) {
- if (Temp *t = phi->incoming[idx]->asTemp()) {
- defUses.removeUse(phi, *t);
- W += defUses.defStmt(*t);
- }
- phi->incoming.remove(idx);
- W += phi;
- } else {
- break;
- }
- }
- }
-
- static bool isReachable(BasicBlock *bb, const DominatorTree &dt)
- {
- for (BasicBlock *in : bb->in) {
- if (in->isRemoved())
- continue;
- if (dt.dominates(bb, in)) // a back-edge, not interesting
- continue;
- return true;
- }
-
- return false;
- }
- };
-
- Q_ASSERT(!from->isRemoved());
- Q_ASSERT(!to->isRemoved());
-
- // don't purge blocks that are entry points for catch statements. They might not be directly
- // connected, but are required anyway
- if (to->isExceptionHandler())
- return;
-
- if (DebugUnlinking)
- qDebug("Unlinking L%d -> L%d...", from->index(), to->index());
-
- // First, unlink the edge
- from->out.removeOne(to);
- Util::removeIncomingEdge(from, to, defUses, W);
-
- BasicBlockSet siblings;
- siblings.init(func);
-
- // Check if the target is still reachable...
- if (Util::isReachable(to, dt)) { // yes, recalculate the immediate dominator, and we're done.
- if (DebugUnlinking)
- qDebug(".. L%d is still reachable, recalulate idom.", to->index());
- dt.collectSiblings(to, siblings);
- } else {
- if (DebugUnlinking)
- qDebug(".. L%d is unreachable, purging it:", to->index());
- // The target is unreachable, so purge it:
- QVector<BasicBlock *> toPurge;
- toPurge.reserve(8);
- toPurge.append(to);
- while (!toPurge.isEmpty()) {
- BasicBlock *bb = toPurge.first();
- toPurge.removeFirst();
- if (DebugUnlinking)
- qDebug("... purging L%d", bb->index());
-
- if (bb->isRemoved())
- continue;
-
- // unlink all incoming edges
- for (BasicBlock *in : bb->in) {
- int idx = in->out.indexOf(bb);
- if (idx != -1)
- in->out.remove(idx);
- }
-
- // unlink all outgoing edges, including "arguments" to phi statements
- for (BasicBlock *out : bb->out) {
- if (out->isRemoved())
- continue;
-
- Util::removeIncomingEdge(bb, out, defUses, W);
-
- if (Util::isReachable(out, dt)) {
- dt.collectSiblings(out, siblings);
- } else {
- // if a successor has no incoming edges after unlinking the current basic block,
- // then it is unreachable, and can be purged too
- toPurge.append(out);
- }
- }
-
- // unlink all defs/uses from the statements in the basic block
- for (Stmt *s : bb->statements()) {
- if (!s)
- continue;
-
- W += defUses.removeDefUses(s);
- W -= s;
- }
-
- siblings.remove(bb);
- dt.setImmediateDominator(bb, 0);
- func->removeBasicBlock(bb);
- }
- }
-
- dt.recalculateIDoms(siblings);
- if (DebugUnlinking)
- qDebug("Unlinking done.");
-}
-
-bool tryOptimizingComparison(Expr *&expr)
-{
- Binop *b = expr->asBinop();
- if (!b)
- return false;
- Const *leftConst = b->left->asConst();
- if (!leftConst || leftConst->type == StringType || leftConst->type == VarType || leftConst->type == QObjectType)
- return false;
- Const *rightConst = b->right->asConst();
- if (!rightConst || rightConst->type == StringType || rightConst->type == VarType || rightConst->type == QObjectType)
- return false;
-
- QV4::Primitive l = convertToValue(leftConst);
- QV4::Primitive r = convertToValue(rightConst);
-
- switch (b->op) {
- case OpGt:
- leftConst->value = Runtime::method_compareGreaterThan(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpLt:
- leftConst->value = Runtime::method_compareLessThan(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpGe:
- leftConst->value = Runtime::method_compareGreaterEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpLe:
- leftConst->value = Runtime::method_compareLessEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpStrictEqual:
- leftConst->value = Runtime::method_compareStrictEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpEqual:
- leftConst->value = Runtime::method_compareEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpStrictNotEqual:
- leftConst->value = Runtime::method_compareStrictNotEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpNotEqual:
- leftConst->value = Runtime::method_compareNotEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- default:
- break;
- }
-
- return false;
-}
-
-void cfg2dot(IR::Function *f, const QVector<LoopDetection::LoopInfo *> &loops = QVector<LoopDetection::LoopInfo *>())
-{
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR");
- if (!showCode)
- return;
-
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
-
- struct Util {
- QTextStream &qout;
- Util(QTextStream &qout): qout(qout) {}
- void genLoop(const LoopDetection::LoopInfo *loop)
- {
- qout << " subgraph \"cluster" << quint64(loop) << "\" {\n";
- qout << " L" << loop->loopHeader->index() << ";\n";
- for (BasicBlock *bb : loop->loopBody)
- qout << " L" << bb->index() << ";\n";
- for (LoopDetection::LoopInfo *nested : loop->nestedLoops)
- genLoop(nested);
- qout << " }\n";
- }
- };
-
- QString name;
- if (f->name) name = *f->name;
- else name = QStringLiteral("%1").arg((unsigned long long)f);
- qout << "digraph \"" << name << "\" { ordering=out;\n";
-
- for (LoopDetection::LoopInfo *l : loops) {
- if (l->parentLoop == 0)
- Util(qout).genLoop(l);
- }
-
- for (BasicBlock *bb : f->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- int idx = bb->index();
- qout << " L" << idx << " [label=\"L" << idx << "\"";
- if (idx == 0 || bb->terminator()->asRet())
- qout << ", shape=doublecircle";
- else
- qout << ", shape=circle";
- qout << "];\n";
- for (BasicBlock *out : bb->out)
- qout << " L" << idx << " -> L" << out->index() << "\n";
- }
-
- qout << "}\n";
- buf.close();
- qDebug("%s", buf.data().constData());
-}
-
-} // anonymous namespace
-
-void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df)
-{
- IR::Function *function = W.function();
- ExprReplacer replaceUses(defUses, function);
-
- Stmt *s = 0;
- while ((s = W.takeNext(s))) {
-
- if (Phi *phi = s->asPhi()) {
- // dead code elimination:
- if (defUses.useCount(*phi->targetTemp) == 0) {
- W += defUses.removeDefUses(phi);
- W.remove(s);
- continue;
- }
-
- // constant propagation:
- if (Const *c = isConstPhi(phi)) {
- replaceUses(phi->targetTemp, c, W);
- defUses.removeDef(*phi->targetTemp);
- W.remove(s);
- continue;
- }
-
- // copy propagation:
- if (phi->incoming.size() == 1) {
- Temp *t = phi->targetTemp;
- Expr *e = phi->incoming.first();
-
- QVector<Stmt *> newT2Uses;
- replaceUses(t, e, W, &newT2Uses);
- if (Temp *t2 = e->asTemp()) {
- defUses.removeUse(s, *t2);
- defUses.addUses(*t2, newT2Uses);
- W += defUses.defStmt(*t2);
- }
- defUses.removeDef(*t);
- W.remove(s);
- continue;
- }
- } else if (Move *m = s->asMove()) {
- if (Convert *convert = m->source->asConvert()) {
- if (Const *sourceConst = convert->expr->asConst()) {
- convertConst(sourceConst, convert->type);
- m->source = sourceConst;
- W += m;
- continue;
- } else if (Temp *sourceTemp = convert->expr->asTemp()) {
- if (sourceTemp->type == convert->type) {
- m->source = sourceTemp;
- W += m;
- continue;
- }
- }
- }
-
- if (Temp *targetTemp = m->target->asTemp()) {
- // dead code elimination:
- if (defUses.useCount(*targetTemp) == 0) {
- EliminateDeadCode(defUses, W).run(m->source, s);
- if (!m->source)
- W.remove(s);
- continue;
- }
-
- // constant propagation:
- if (Const *sourceConst = m->source->asConst()) {
- Q_ASSERT(sourceConst->type != UnknownType);
- replaceUses(targetTemp, sourceConst, W);
- defUses.removeDef(*targetTemp);
- W.remove(s);
- continue;
- }
- if (Member *member = m->source->asMember()) {
- if (member->kind == Member::MemberOfEnum) {
- Const *c = function->New<Const>();
- const int enumValue = member->enumValue;
- c->init(SInt32Type, enumValue);
- replaceUses(targetTemp, c, W);
- defUses.removeDef(*targetTemp);
- W.remove(s);
- defUses.removeUse(s, *member->base->asTemp());
- continue;
- } else if (member->kind != IR::Member::MemberOfIdObjectsArray && member->attachedPropertiesId != 0 && member->property && member->base->asTemp()) {
- // Attached properties have no dependency on their base. Isel doesn't
- // need it and we can eliminate the temp used to initialize it.
- defUses.removeUse(s, *member->base->asTemp());
- Const *c = function->New<Const>();
- c->init(SInt32Type, 0);
- member->base = c;
- continue;
- }
- }
-
- // copy propagation:
- if (Temp *sourceTemp = m->source->asTemp()) {
- QVector<Stmt *> newT2Uses;
- if (replaceUses(targetTemp, sourceTemp, W, &newT2Uses)) {
- defUses.removeUse(s, *sourceTemp);
- defUses.addUses(*sourceTemp, newT2Uses);
- defUses.removeDef(*targetTemp);
- W.remove(s);
- }
- continue;
- }
-
- if (Unop *unop = m->source->asUnop()) {
- // Constant unary expression evaluation:
- if (Const *constOperand = unop->expr->asConst()) {
- if (constOperand->type & NumberType || constOperand->type == BoolType) {
- // TODO: implement unop propagation for other constant types
- bool doneSomething = false;
- switch (unop->op) {
- case OpNot:
- constOperand->value = !constOperand->value;
- constOperand->type = BoolType;
- doneSomething = true;
- break;
- case OpUMinus:
- if (int(constOperand->value) == 0 && int(constOperand->value) == constOperand->value) {
- if (isNegative(constOperand->value))
- constOperand->value = 0;
- else
- constOperand->value = -1 / Q_INFINITY;
- constOperand->type = DoubleType;
- doneSomething = true;
- break;
- }
-
- constOperand->value = -constOperand->value;
- doneSomething = true;
- break;
- case OpUPlus:
- if (unop->type != UnknownType)
- constOperand->type = unop->type;
- doneSomething = true;
- break;
- case OpCompl:
- constOperand->value = ~QV4::Primitive::toInt32(constOperand->value);
- constOperand->type = SInt32Type;
- doneSomething = true;
- break;
- case OpPreIncrement:
- constOperand->value = constOperand->value + 1;
- doneSomething = true;
- break;
- case OpPreDecrement:
- constOperand->value = constOperand->value - 1;
- doneSomething = true;
- break;
- default:
- break;
- };
-
- if (doneSomething) {
- m->source = constOperand;
- W += m;
- }
- }
- }
- // TODO: if the result of a unary not operation is only used in a cjump,
- // then inline it.
-
- continue;
- }
-
- if (Binop *binop = m->source->asBinop()) {
- Const *leftConst = binop->left->asConst();
- Const *rightConst = binop->right->asConst();
-
- { // Typical casts to int32:
- Expr *casted = 0;
- switch (binop->op) {
- case OpBitAnd:
- if (leftConst && !rightConst && QV4::Primitive::toUInt32(leftConst->value) == 0xffffffff)
- casted = binop->right;
- else if (!leftConst && rightConst && QV4::Primitive::toUInt32(rightConst->value) == 0xffffffff)
- casted = binop->left;
- break;
- case OpBitOr:
- if (leftConst && !rightConst && QV4::Primitive::toInt32(leftConst->value) == 0)
- casted = binop->right;
- else if (!leftConst && rightConst && QV4::Primitive::toUInt32(rightConst->value) == 0)
- casted = binop->left;
- break;
- default:
- break;
- }
- if (casted && casted->type == SInt32Type) {
- m->source = casted;
- W += m;
- continue;
- }
- }
- if (rightConst) {
- switch (binop->op) {
- case OpLShift:
- case OpRShift:
- if (double v = QV4::Primitive::toInt32(rightConst->value) & 0x1f) {
- // mask right hand side of shift operations
- rightConst->value = v;
- rightConst->type = SInt32Type;
- } else {
- // shifting a value over 0 bits is a move:
- if (rightConst->value == 0) {
- m->source = binop->left;
- W += m;
- }
- }
-
- break;
- default:
- break;
- }
- }
-
- // TODO: More constant binary expression evaluation
- // TODO: If the result of the move is only used in one single cjump, then
- // inline the binop into the cjump.
- if (!leftConst || leftConst->type == StringType || leftConst->type == VarType || leftConst->type == QObjectType)
- continue;
- if (!rightConst || rightConst->type == StringType || rightConst->type == VarType || rightConst->type == QObjectType)
- continue;
-
- QV4::Primitive lc = convertToValue(leftConst);
- QV4::Primitive rc = convertToValue(rightConst);
- double l = lc.toNumber();
- double r = rc.toNumber();
-
- switch (binop->op) {
- case OpMul:
- leftConst->value = l * r;
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- case OpAdd:
- leftConst->value = l + r;
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- case OpSub:
- leftConst->value = l - r;
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- case OpDiv:
- leftConst->value = l / r;
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- case OpMod:
- leftConst->value = std::fmod(l, r);
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- default:
- if (tryOptimizingComparison(m->source))
- W += m;
- break;
- }
-
- continue;
- }
- } // TODO: var{#0} = double{%10} where %10 is defined once and used once. E.g.: function(t){t = t % 2; return t; }
-
- } else if (CJump *cjump = s->asCJump()) {
- if (Const *constantCondition = cjump->cond->asConst()) {
- // Note: this assumes that there are no critical edges! Meaning, we can safely purge
- // any basic blocks that are found to be unreachable.
- Jump *jump = function->NewStmt<Jump>();
- W.registerNewStatement(jump);
- if (convertToValue(constantCondition).toBoolean()) {
- jump->target = cjump->iftrue;
- unlink(cjump->parent, cjump->iffalse, function, defUses, W, df);
- } else {
- jump->target = cjump->iffalse;
- unlink(cjump->parent, cjump->iftrue, function, defUses, W, df);
- }
- W.replace(s, jump);
-
- continue;
- } else if (cjump->cond->asBinop()) {
- if (tryOptimizingComparison(cjump->cond))
- W += cjump;
- continue;
- }
- // TODO: Constant unary expression evaluation
- // TODO: if the expression is an unary not operation, lift the expression, and switch
- // the then/else blocks.
- }
- }
-
- W.applyToFunction();
-}
-
-//### TODO: use DefUses from the optimizer, because it already has all this information
-class InputOutputCollector
-{
- void setOutput(Temp *out)
- {
- Q_ASSERT(!output);
- output = out;
- }
-
-public:
- std::vector<Temp *> inputs;
- Temp *output;
-
- InputOutputCollector()
- { inputs.reserve(4); }
-
- void collect(Stmt *s) {
- inputs.resize(0);
- output = 0;
- visit(s);
- }
-
-private:
- void visit(Expr *e)
- {
- if (auto t = e->asTemp()) {
- inputs.push_back(t);
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-
- void visit(Stmt *s)
- {
- if (auto m = s->asMove()) {
- visit(m->source);
- if (Temp *t = m->target->asTemp()) {
- setOutput(t);
- } else {
- visit(m->target);
- }
- } else if (s->asPhi()) {
- // Handled separately
- } else {
- STMT_VISIT_ALL_KINDS(s);
- }
- }
-};
-
-/*
- * The algorithm is described in:
- *
- * Linear Scan Register Allocation on SSA Form
- * Christian Wimmer & Michael Franz, CGO'10, April 24-28, 2010
- *
- * See LifeTimeIntervals::renumber for details on the numbering.
- */
-class LifeRanges {
- class LiveRegs
- {
- typedef std::vector<int> Storage;
- Storage regs;
-
- public:
- void insert(int r)
- {
- if (find(r) == end())
- regs.push_back(r);
- }
-
- void unite(const LiveRegs &other)
- {
- if (other.empty())
- return;
- if (empty()) {
- regs = other.regs;
- return;
- }
- for (int r : other.regs)
- insert(r);
- }
-
- typedef Storage::iterator iterator;
- iterator find(int r)
- { return std::find(regs.begin(), regs.end(), r); }
-
- iterator begin()
- { return regs.begin(); }
-
- iterator end()
- { return regs.end(); }
-
- void erase(iterator it)
- { regs.erase(it); }
-
- void remove(int r)
- {
- iterator it = find(r);
- if (it != end())
- erase(it);
- }
-
- bool empty() const
- { return regs.empty(); }
-
- int size() const
- { return int(regs.size()); }
-
- int at(int idx) const
- { return regs.at(idx); }
- };
-
- std::vector<LiveRegs> _liveIn;
- std::vector<LifeTimeInterval *> _intervals;
- LifeTimeIntervals::Ptr _sortedIntervals;
-
- LifeTimeInterval &interval(const Temp *temp)
- {
- LifeTimeInterval *lti = _intervals[temp->index];
- Q_ASSERT(lti);
- return *lti;
- }
-
- void ensureInterval(const IR::Temp &temp)
- {
- Q_ASSERT(!temp.isInvalid());
- LifeTimeInterval *&lti = _intervals[temp.index];
- if (lti)
- return;
- lti = new LifeTimeInterval;
- lti->setTemp(temp);
- }
-
- int defPosition(IR::Stmt *s) const
- {
- return usePosition(s) + 1;
- }
-
- int usePosition(IR::Stmt *s) const
- {
- return _sortedIntervals->positionForStatement(s);
- }
-
- int start(IR::BasicBlock *bb) const
- {
- return _sortedIntervals->startPosition(bb);
- }
-
- int end(IR::BasicBlock *bb) const
- {
- return _sortedIntervals->endPosition(bb);
- }
-
-public:
- LifeRanges(IR::Function *function, const QHash<BasicBlock *, BasicBlock *> &startEndLoops)
- : _intervals(function->tempCount)
- {
- _sortedIntervals = LifeTimeIntervals::create(function);
- _liveIn.resize(function->basicBlockCount());
-
- for (int i = function->basicBlockCount() - 1; i >= 0; --i) {
- BasicBlock *bb = function->basicBlock(i);
- buildIntervals(bb, startEndLoops.value(bb, 0));
- }
-
- _intervals.clear();
- }
-
- LifeTimeIntervals::Ptr intervals() const { return _sortedIntervals; }
-
- void dump() const
- {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
-
- qout << "Life ranges:" << endl;
- qout << "Intervals:" << endl;
- const auto intervals = _sortedIntervals->intervals();
- for (const LifeTimeInterval *range : intervals) {
- range->dump(qout);
- qout << endl;
- }
-
- IRPrinter printer(&qout);
- for (size_t i = 0, ei = _liveIn.size(); i != ei; ++i) {
- qout << "L" << i <<" live-in: ";
- auto live = _liveIn.at(i);
- if (live.empty())
- qout << "(none)";
- std::sort(live.begin(), live.end());
- for (int i = 0; i < live.size(); ++i) {
- if (i > 0) qout << ", ";
- qout << '%' << live.at(i);
- }
- qout << endl;
- }
- buf.close();
- qDebug("%s", buf.data().constData());
- }
-
-private:
- void buildIntervals(BasicBlock *bb, BasicBlock *loopEnd)
- {
- LiveRegs live;
- for (BasicBlock *successor : bb->out) {
- live.unite(_liveIn[successor->index()]);
- const int bbIndex = successor->in.indexOf(bb);
- Q_ASSERT(bbIndex >= 0);
-
- for (Stmt *s : successor->statements()) {
- if (Phi *phi = s->asPhi()) {
- if (Temp *t = phi->incoming.at(bbIndex)->asTemp()) {
- ensureInterval(*t);
- live.insert(t->index);
- }
- } else {
- break;
- }
- }
- }
-
- const QVector<Stmt *> &statements = bb->statements();
-
- for (int reg : live)
- _intervals[reg]->addRange(start(bb), end(bb));
-
- InputOutputCollector collector;
- for (int i = statements.size() - 1; i >= 0; --i) {
- Stmt *s = statements.at(i);
- if (Phi *phi = s->asPhi()) {
- ensureInterval(*phi->targetTemp);
- LiveRegs::iterator it = live.find(phi->targetTemp->index);
- if (it == live.end()) {
- // a phi node target that is only defined, but never used
- interval(phi->targetTemp).setFrom(start(bb));
- } else {
- live.erase(it);
- }
- _sortedIntervals->add(&interval(phi->targetTemp));
- continue;
- }
- collector.collect(s);
- //### TODO: use DefUses from the optimizer, because it already has all this information
- if (Temp *opd = collector.output) {
- ensureInterval(*opd);
- LifeTimeInterval &lti = interval(opd);
- lti.setFrom(defPosition(s));
- live.remove(lti.temp().index);
- _sortedIntervals->add(&lti);
- }
- //### TODO: use DefUses from the optimizer, because it already has all this information
- for (size_t i = 0, ei = collector.inputs.size(); i != ei; ++i) {
- Temp *opd = collector.inputs[i];
- ensureInterval(*opd);
- interval(opd).addRange(start(bb), usePosition(s));
- live.insert(opd->index);
- }
- }
-
- if (loopEnd) { // Meaning: bb is a loop header, because loopEnd is set to non-null.
- for (int reg : live)
- _intervals[reg]->addRange(start(bb), usePosition(loopEnd->terminator()));
- }
-
- _liveIn[bb->index()] = std::move(live);
- }
-};
-
-void removeUnreachleBlocks(IR::Function *function)
-{
- QVector<BasicBlock *> newSchedule;
- newSchedule.reserve(function->basicBlockCount());
- for (BasicBlock *bb : function->basicBlocks())
- if (!bb->isRemoved())
- newSchedule.append(bb);
- function->setScheduledBlocks(newSchedule);
-}
-
-class ConvertArgLocals
-{
-public:
- ConvertArgLocals(IR::Function *function)
- : function(function)
- , convertArgs(!function->usesArgumentsObject)
- {
- tempForFormal.resize(function->formals.size(), -1);
- tempForLocal.resize(function->locals.size(), -1);
- }
-
- void toTemps()
- {
- if (function->variablesCanEscape())
- return;
-
- QVector<Stmt *> extraMoves;
- if (convertArgs) {
- const int formalCount = function->formals.size();
- extraMoves.reserve(formalCount + function->basicBlock(0)->statementCount());
- extraMoves.resize(formalCount);
-
- for (int i = 0; i != formalCount; ++i) {
- const int newTemp = function->tempCount++;
- tempForFormal[i] = newTemp;
-
- ArgLocal *source = function->New<ArgLocal>();
- source->init(ArgLocal::Formal, i, 0);
-
- Temp *target = function->New<Temp>();
- target->init(Temp::VirtualRegister, newTemp);
-
- Move *m = function->NewStmt<Move>();
- m->init(target, source);
- extraMoves[i] = m;
- }
- }
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (!bb->isRemoved()) {
- for (Stmt *s : bb->statements()) {
- visit(s);
- }
- }
- }
-
- if (convertArgs && function->formals.size() > 0)
- function->basicBlock(0)->prependStatements(extraMoves);
-
- function->locals.clear();
- }
-
-private:
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- check(e->expr);
- } else if (auto m = s->asMove()) {
- check(m->target); check(m->source);
- } else if (auto c = s->asCJump()) {
- check(c->cond);
- } else if (auto r = s->asRet()) {
- check(r->expr);
- }
- }
-
- void visit(Expr *e)
- {
- if (auto c = e->asConvert()) {
- check(c->expr);
- } else if (auto u = e->asUnop()) {
- check(u->expr);
- } else if (auto b = e->asBinop()) {
- check(b->left); check(b->right);
- } else if (auto c = e->asCall()) {
- check(c->base);
- for (ExprList *it = c->args; it; it = it->next) {
- check(it->expr);
- }
- } else if (auto n = e->asNew()) {
- check(n->base);
- for (ExprList *it = n->args; it; it = it->next) {
- check(it->expr);
- }
- } else if (auto s = e->asSubscript()) {
- check(s->base); check(s->index);
- } else if (auto m = e->asMember()) {
- check(m->base);
- }
- }
-
- void check(Expr *&e) {
- if (ArgLocal *al = e->asArgLocal()) {
- if (al->kind == ArgLocal::Local) {
- Temp *t = function->New<Temp>();
- t->init(Temp::VirtualRegister, fetchTempForLocal(al->index));
- e = t;
- } else if (convertArgs && al->kind == ArgLocal::Formal) {
- Temp *t = function->New<Temp>();
- t->init(Temp::VirtualRegister, fetchTempForFormal(al->index));
- e = t;
- }
- } else {
- visit(e);
- }
- }
-
- int fetchTempForLocal(int local)
- {
- int &ref = tempForLocal[local];
- if (ref == -1)
- ref = function->tempCount++;
- return ref;
- }
-
- int fetchTempForFormal(int formal)
- {
- return tempForFormal[formal];
- }
-
- IR::Function *function;
- bool convertArgs;
- std::vector<int> tempForFormal;
- std::vector<int> tempForLocal;
-};
-
-class CloneBasicBlock: protected CloneExpr
-{
-public:
- BasicBlock *operator()(IR::BasicBlock *originalBlock)
- {
- block = new BasicBlock(originalBlock->function, 0);
-
- for (Stmt *s : originalBlock->statements()) {
- visit(s);
- clonedStmt->location = s->location;
- }
-
- return block;
- }
-
-private:
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- clonedStmt = block->EXP(clone(e->expr));
- } else if (auto m = s->asMove()) {
- clonedStmt = block->MOVE(clone(m->target), clone(m->source));
- } else if (auto j = s->asJump()) {
- clonedStmt = block->JUMP(j->target);
- } else if (auto c = s->asCJump()) {
- clonedStmt = block->CJUMP(clone(c->cond), c->iftrue, c->iffalse);
- } else if (auto r = s->asRet()) {
- clonedStmt = block->RET(clone(r->expr));
- } else if (auto p = s->asPhi()) {
- Phi *phi = block->function->NewStmt<Phi>();
- clonedStmt = phi;
-
- phi->targetTemp = clone(p->targetTemp);
- for (Expr *in : p->incoming)
- phi->incoming.append(clone(in));
- block->appendStatement(phi);
- } else {
- Q_UNREACHABLE();
- }
- }
-
-private:
- IR::Stmt *clonedStmt;
-};
-
-static void verifyCFG(IR::Function *function)
-{
- if (!DoVerification)
- return;
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved()) {
- Q_ASSERT(bb->in.isEmpty());
- Q_ASSERT(bb->out.isEmpty());
- continue;
- }
-
- Q_ASSERT(function->basicBlock(bb->index()) == bb);
-
- // Check the terminators:
- Stmt *terminator = bb->terminator();
- if (terminator == nullptr) {
- Stmt *last = bb->statements().last();
- Call *call = last->asExp()->expr->asCall();
- Name *baseName = call->base->asName();
- Q_ASSERT(baseName->builtin == Name::builtin_rethrow);
- Q_UNUSED(baseName);
- } else if (Jump *jump = terminator->asJump()) {
- Q_UNUSED(jump);
- Q_ASSERT(jump->target);
- Q_ASSERT(!jump->target->isRemoved());
- Q_ASSERT(bb->out.size() == 1);
- Q_ASSERT(bb->out.first() == jump->target);
- } else if (CJump *cjump = terminator->asCJump()) {
- Q_UNUSED(cjump);
- Q_ASSERT(bb->out.size() == 2);
- Q_ASSERT(cjump->iftrue);
- Q_ASSERT(!cjump->iftrue->isRemoved());
- Q_ASSERT(cjump->iftrue == bb->out[0]);
- Q_ASSERT(cjump->iffalse);
- Q_ASSERT(!cjump->iffalse->isRemoved());
- Q_ASSERT(cjump->iffalse == bb->out[1]);
- } else if (terminator->asRet()) {
- Q_ASSERT(bb->out.size() == 0);
- } else {
- Q_UNREACHABLE();
- }
-
- // Check the outgoing edges:
- for (BasicBlock *out : bb->out) {
- Q_UNUSED(out);
- Q_ASSERT(!out->isRemoved());
- Q_ASSERT(out->in.contains(bb));
- }
-
- // Check the incoming edges:
- for (BasicBlock *in : bb->in) {
- Q_UNUSED(in);
- Q_ASSERT(!in->isRemoved());
- Q_ASSERT(in->out.contains(bb));
- }
- }
-}
-
-static void verifyImmediateDominators(const DominatorTree &dt, IR::Function *function)
-{
- if (!DoVerification)
- return;
-
- cfg2dot(function);
- dt.dumpImmediateDominators();
- DominatorTree referenceTree(function);
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- BasicBlock *idom = dt.immediateDominator(bb);
- BasicBlock *referenceIdom = referenceTree.immediateDominator(bb);
- Q_UNUSED(idom);
- Q_UNUSED(referenceIdom);
- Q_ASSERT(idom == referenceIdom);
- }
-}
-
-static void verifyNoPointerSharing(IR::Function *function)
-{
- if (!DoVerification)
- return;
-
- class {
- public:
- void operator()(IR::Function *f)
- {
- for (BasicBlock *bb : f->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- for (Stmt *s : bb->statements()) {
- visit(s);
- }
- }
- }
-
- private:
- void visit(Stmt *s)
- {
- check(s);
- STMT_VISIT_ALL_KINDS(s);
- }
-
- void visit(Expr *e)
- {
- check(e);
- EXPR_VISIT_ALL_KINDS(e);
- }
-
- private:
- void check(Stmt *s)
- {
- Q_ASSERT(!stmts.contains(s));
- stmts.insert(s);
- }
-
- void check(Expr *e)
- {
- Q_ASSERT(!exprs.contains(e));
- exprs.insert(e);
- }
-
- QSet<Stmt *> stmts;
- QSet<Expr *> exprs;
- } V;
- V(function);
-}
-
-// Loop-peeling is done by unfolding the loop once. The "original" loop basic blocks stay where they
-// are, and a copy of the loop is placed after it. Special care is taken while copying the loop body:
-// by having the copies of the basic-blocks point to the same nodes as the "original" basic blocks,
-// updating the immediate dominators is easy: if the edge of a copied basic-block B points to a
-// block C that has also been copied, set the immediate dominator of B to the corresponding
-// immediate dominator of C. Finally, for any node outside the loop that gets a new edge attached,
-// the immediate dominator has to be re-calculated.
-class LoopPeeling
-{
- DominatorTree &dt;
-
-public:
- LoopPeeling(DominatorTree &dt)
- : dt(dt)
- {}
-
- void run(const QVector<LoopDetection::LoopInfo *> &loops)
- {
- for (LoopDetection::LoopInfo *loopInfo : loops)
- peelLoop(loopInfo);
- }
-
-private:
- // All copies have their outgoing edges pointing to the same successor block as the originals.
- // For each copied block, check where the outgoing edges point to. If it's a block inside the
- // (original) loop, rewire it to the corresponding copy. Otherwise, which is when it points
- // out of the loop, leave it alone.
- // As an extra, collect all edges that point out of the copied loop, because the targets need
- // to have their immediate dominator rechecked.
- void rewire(BasicBlock *newLoopBlock, const QVector<BasicBlock *> &from, const QVector<BasicBlock *> &to, QVector<BasicBlock *> &loopExits)
- {
- for (int i = 0, ei = newLoopBlock->out.size(); i != ei; ++i) {
- BasicBlock *&out = newLoopBlock->out[i];
- const int idx = from.indexOf(out);
- if (idx == -1) {
- if (!loopExits.contains(out))
- loopExits.append(out);
- } else {
- out->in.removeOne(newLoopBlock);
- BasicBlock *newTo = to.at(idx);
- newTo->in.append(newLoopBlock);
- out = newTo;
-
- Stmt *terminator = newLoopBlock->terminator();
- if (Jump *jump = terminator->asJump()) {
- Q_ASSERT(i == 0);
- jump->target = newTo;
- } else if (CJump *cjump = terminator->asCJump()) {
- Q_ASSERT(i == 0 || i == 1);
- if (i == 0)
- cjump->iftrue = newTo;
- else
- cjump->iffalse = newTo;
- }
- }
- }
- }
-
- void peelLoop(LoopDetection::LoopInfo *loop)
- {
- IR::Function *f = loop->loopHeader->function;
- CloneBasicBlock clone;
-
- LoopDetection::LoopInfo unpeeled(*loop);
- unpeeled.loopHeader = clone(unpeeled.loopHeader);
- unpeeled.loopHeader->setContainingGroup(loop->loopHeader->containingGroup());
- unpeeled.loopHeader->markAsGroupStart(true);
- f->addBasicBlock(unpeeled.loopHeader);
- for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) {
- BasicBlock *&bodyBlock = unpeeled.loopBody[i];
- bodyBlock = clone(bodyBlock);
- bodyBlock->setContainingGroup(unpeeled.loopHeader);
- Q_ASSERT(bodyBlock->statementCount() == loop->loopBody[i]->statementCount());
- }
-
- // The cloned blocks will have no incoming edges, but they do have outgoing ones (copying
- // the terminators will automatically insert that edge). The blocks where the originals
- // pointed to will have an extra incoming edge from the copied blocks.
-
- BasicBlock::IncomingEdges inCopy = loop->loopHeader->in;
- for (BasicBlock *in : inCopy) {
- if (loop->loopHeader != in // this can happen for really tight loops (where there are no body blocks). This is a back-edge in that case.
- && unpeeled.loopHeader != in && !unpeeled.loopBody.contains(in) // if the edge is not coming from within the copied set, leave it alone
- && !dt.dominates(loop->loopHeader, in)) // an edge coming from within the loop (so a back-edge): this is handled when rewiring all outgoing edges
- continue;
-
- unpeeled.loopHeader->in.append(in);
- loop->loopHeader->in.removeOne(in);
-
- Stmt *terminator = in->terminator();
- if (Jump *jump = terminator->asJump()) {
- jump->target = unpeeled.loopHeader;
- in->out[0] = unpeeled.loopHeader;
- } else if (CJump *cjump = terminator->asCJump()) {
- if (cjump->iftrue == loop->loopHeader) {
- cjump->iftrue = unpeeled.loopHeader;
- Q_ASSERT(in->out[0] == loop->loopHeader);
- in->out[0] = unpeeled.loopHeader;
- } else if (cjump->iffalse == loop->loopHeader) {
- cjump->iffalse = unpeeled.loopHeader;
- Q_ASSERT(in->out[1] == loop->loopHeader);
- in->out[1] = unpeeled.loopHeader;
- } else {
- Q_UNREACHABLE();
- }
- }
- }
-
- QVector<BasicBlock *> loopExits;
- loopExits.reserve(8);
- loopExits.append(unpeeled.loopHeader);
-
- rewire(unpeeled.loopHeader, loop->loopBody, unpeeled.loopBody, loopExits);
- for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) {
- BasicBlock *bodyBlock = unpeeled.loopBody.at(i);
- rewire(bodyBlock, loop->loopBody, unpeeled.loopBody, loopExits);
- f->addBasicBlock(bodyBlock);
- }
-
- // The original loop is now peeled off, and won't jump back to the loop header. Meaning, it
- // is not a loop anymore, so unmark it.
- loop->loopHeader->markAsGroupStart(false);
- for (BasicBlock *bb : qAsConst(loop->loopBody))
- bb->setContainingGroup(loop->loopHeader->containingGroup());
-
- // Set the immediate dominator of the new loop header to the old one. The real immediate
- // dominator will be calculated later.
- dt.setImmediateDominator(unpeeled.loopHeader, loop->loopHeader);
- // calculate the idoms in a separate loop, because addBasicBlock in the previous loop will
- // set the block index, which in turn is used by the dominator tree.
- for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) {
- BasicBlock *bodyBlock = unpeeled.loopBody.at(i);
- BasicBlock *idom = dt.immediateDominator(loop->loopBody.at(i));
- const int idx = loop->loopBody.indexOf(idom);
- if (idom == loop->loopHeader)
- idom = unpeeled.loopHeader;
- else if (idx != -1)
- idom = unpeeled.loopBody.at(idx);
- Q_ASSERT(idom);
- dt.setImmediateDominator(bodyBlock, idom);
- }
-
- BasicBlockSet siblings(f);
- for (BasicBlock *bb : qAsConst(loopExits))
- dt.collectSiblings(bb, siblings);
-
- siblings.insert(unpeeled.loopHeader);
- dt.recalculateIDoms(siblings, loop->loopHeader);
- dt.dumpImmediateDominators();
- verifyImmediateDominators(dt, f);
- }
-};
-
-class RemoveLineNumbers: private SideEffectsChecker
-{
-public:
- static void run(IR::Function *function)
- {
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- for (Stmt *s : bb->statements()) {
- if (!hasSideEffects(s)) {
- s->location = QQmlJS::AST::SourceLocation();
- }
- }
- }
- }
-
-private:
- ~RemoveLineNumbers() {}
-
- static bool hasSideEffects(Stmt *stmt)
- {
- RemoveLineNumbers checker;
- if (auto e = stmt->asExp()) {
- checker.visit(e->expr);
- } else if (auto m = stmt->asMove()) {
- checker.visit(m->source);
- if (!checker.seenSideEffects()) {
- checker.visit(m->target);
- }
- } else if (auto c = stmt->asCJump()) {
- checker.visit(c->cond);
- } else if (auto r = stmt->asRet()) {
- checker.visit(r->expr);
- }
- return checker.seenSideEffects();
- }
-
- void visitTemp(Temp *) Q_DECL_OVERRIDE Q_DECL_FINAL {}
-};
-
-void mergeBasicBlocks(IR::Function *function, DefUses *du, DominatorTree *dt)
-{
- enum { DebugBlockMerging = 0 };
-
- if (function->hasTry)
- return;
-
- showMeTheCode(function, "Before basic block merging");
-
- // Now merge a basic block with its successor when there is one outgoing edge, and the
- // successor has one incoming edge.
- for (int i = 0, ei = function->basicBlockCount(); i != ei; ++i) {
- BasicBlock *bb = function->basicBlock(i);
-
- bb->nextLocation = QQmlJS::AST::SourceLocation(); // make sure appendStatement doesn't mess with the line info
-
- if (bb->isRemoved()) continue; // the block has been removed, so ignore it
- if (bb->out.size() != 1) continue; // more than one outgoing edge
- BasicBlock *successor = bb->out.first();
- if (successor->in.size() != 1) continue; // more than one incoming edge
-
- // Loop header? No efficient way to update the other blocks that refer to this as containing group,
- // so don't do merging yet.
- if (successor->isGroupStart()) continue;
-
- // Ok, we can merge the two basic blocks.
- if (DebugBlockMerging) {
- qDebug("Merging L%d into L%d", successor->index(), bb->index());
- }
- Q_ASSERT(bb->terminator()->asJump());
- bb->removeStatement(bb->statementCount() - 1); // remove the terminator, and replace it with:
- for (Stmt *s : successor->statements()) {
- bb->appendStatement(s); // add all statements from the successor to the current basic block
- if (auto cjump = s->asCJump())
- cjump->parent = bb;
- }
- bb->out = successor->out; // set the outgoing edges to the successor's so they're now in sync with our new terminator
- for (auto newSuccessor : bb->out) {
- for (auto &backlink : newSuccessor->in) {
- if (backlink == successor) {
- backlink = bb; // for all successors of our successor: set the incoming edges to come from bb, because we'll now jump there.
- }
- }
- }
- if (du) {
- // all statements in successor have moved to bb, so make sure that the containing blocks
- // stored in DefUses get updated (meaning: point to bb)
- du->replaceBasicBlock(successor, bb);
- }
- if (dt) {
- // update the immediate dominators to: any block that was dominated by the successor
- // will now need to point to bb's immediate dominator. The reason is that bb itself
- // won't be anyones immediate dominator, because it had just one outgoing edge.
- dt->mergeIntoPredecessor(successor);
- }
- function->removeBasicBlock(successor);
- --i; // re-run on the current basic-block, so any chain gets collapsed.
- }
-
- showMeTheCode(function, "After basic block merging");
- verifyCFG(function);
-}
-
-} // anonymous namespace
-
-void LifeTimeInterval::setFrom(int from) {
- Q_ASSERT(from > 0);
-
- if (_ranges.isEmpty()) { // this is the case where there is no use, only a define
- _ranges.prepend(LifeTimeIntervalRange(from, from));
- if (_end == InvalidPosition)
- _end = from;
- } else {
- _ranges.first().start = from;
- }
-}
-
-void LifeTimeInterval::addRange(int from, int to) {
- Q_ASSERT(from > 0);
- Q_ASSERT(to > 0);
- Q_ASSERT(to >= from);
-
- if (_ranges.isEmpty()) {
- _ranges.prepend(LifeTimeIntervalRange(from, to));
- _end = to;
- return;
- }
-
- LifeTimeIntervalRange *p = &_ranges.first();
- if (to + 1 >= p->start && p->end + 1 >= from) {
- p->start = qMin(p->start, from);
- p->end = qMax(p->end, to);
- while (_ranges.count() > 1) {
- LifeTimeIntervalRange *p1 = p + 1;
- if (p->end + 1 < p1->start || p1->end + 1 < p->start)
- break;
- p1->start = qMin(p->start, p1->start);
- p1->end = qMax(p->end, p1->end);
- _ranges.remove(0);
- p = &_ranges.first();
- }
- } else {
- if (to < p->start) {
- _ranges.prepend(LifeTimeIntervalRange(from, to));
- } else {
- Q_ASSERT(from > _ranges.last().end);
- _ranges.push_back(LifeTimeIntervalRange(from, to));
- }
- }
-
- _end = _ranges.last().end;
-}
-
-LifeTimeInterval LifeTimeInterval::split(int atPosition, int newStart)
-{
- Q_ASSERT(atPosition < newStart || newStart == InvalidPosition);
- Q_ASSERT(atPosition <= _end);
- Q_ASSERT(newStart <= _end || newStart == InvalidPosition);
-
- if (_ranges.isEmpty() || atPosition < _ranges.first().start)
- return LifeTimeInterval();
-
- LifeTimeInterval newInterval = *this;
- newInterval.setSplitFromInterval(true);
-
- // search where to split the interval
- for (int i = 0, ei = _ranges.size(); i < ei; ++i) {
- if (_ranges.at(i).start <= atPosition) {
- if (_ranges.at(i).end >= atPosition) {
- // split happens in the middle of a range. Keep this range in both the old and the
- // new interval, and correct the end/start later
- _ranges.resize(i + 1);
- newInterval._ranges.remove(0, i);
- break;
- }
- } else {
- // split happens between two ranges.
- _ranges.resize(i);
- newInterval._ranges.remove(0, i);
- break;
- }
- }
-
- if (newInterval._ranges.first().end == atPosition)
- newInterval._ranges.remove(0);
-
- if (newStart == InvalidPosition) {
- // the temp stays inactive for the rest of its lifetime
- newInterval = LifeTimeInterval();
- } else {
- // find the first range where the temp will get active again:
- while (!newInterval._ranges.isEmpty()) {
- const LifeTimeIntervalRange &range = newInterval._ranges.first();
- if (range.start > newStart) {
- // The split position is before the start of the range. Either we managed to skip
- // over the correct range, or we got an invalid split request. Either way, this
- // Should Never Happen <TM>.
- Q_ASSERT(range.start > newStart);
- return LifeTimeInterval();
- } else if (range.start <= newStart && range.end >= newStart) {
- // yay, we found the range that should be the new first range in the new interval!
- break;
- } else {
- // the temp stays inactive for this interval, so remove it.
- newInterval._ranges.remove(0);
- }
- }
- Q_ASSERT(!newInterval._ranges.isEmpty());
- newInterval._ranges.first().start = newStart;
- _end = newStart;
- }
-
- // if we're in the middle of a range, set the end to the split position
- if (_ranges.last().end > atPosition)
- _ranges.last().end = atPosition;
-
- validate();
- newInterval.validate();
-
- return newInterval;
-}
-
-void LifeTimeInterval::dump(QTextStream &out) const {
- IRPrinter(&out).print(const_cast<Temp *>(&_temp));
- out << ": ends at " << _end << " with ranges ";
- if (_ranges.isEmpty())
- out << "(none)";
- for (int i = 0; i < _ranges.size(); ++i) {
- if (i > 0) out << ", ";
- out << _ranges[i].start << " - " << _ranges[i].end;
- }
- if (_reg != InvalidRegister)
- out << " (register " << _reg << ")";
-}
-
-
-bool LifeTimeInterval::lessThanForTemp(const LifeTimeInterval *r1, const LifeTimeInterval *r2)
-{
- return r1->temp() < r2->temp();
-}
-
-LifeTimeIntervals::LifeTimeIntervals(IR::Function *function)
- : _basicBlockPosition(function->basicBlockCount())
- , _positionForStatement(function->statementCount(), IR::Stmt::InvalidId)
- , _lastPosition(0)
-{
- _intervals.reserve(function->tempCount + 32); // we reserve a bit more space for intervals, because the register allocator will add intervals with fixed ranges for each register.
- renumber(function);
-}
-
-// Renumbering works as follows:
-// - phi statements are not numbered
-// - statement numbers start at 0 (zero) and increment get an even number (lastPosition + 2)
-// - basic blocks start at firstStatementNumber - 1, or rephrased: lastPosition + 1
-// - basic blocks end at the number of the last statement
-// And during life-time calculation the next rule is used:
-// - any temporary starts its life-time at definingStatementPosition + 1
-//
-// This numbering simulates half-open intervals. For example:
-// 0: %1 = 1
-// 2: %2 = 2
-// 4: %3 = %1 + %2
-// 6: print(%3)
-// Here the half-open life-time intervals would be:
-// %1: (0-4]
-// %2: (2-4]
-// %3: (4-6]
-// Instead, we use the even statement positions for uses of temporaries, and the odd positions for
-// their definitions:
-// %1: [1-4]
-// %2: [3-4]
-// %3: [5-6]
-// This has the nice advantage that placing %3 (for example) is really easy: the start will
-// never overlap with the end of the uses of the operands used in the defining statement.
-//
-// The reason to start a basic-block at firstStatementPosition - 1 is to have correct start
-// positions for target temporaries of phi-nodes. Those temporaries will now start before the
-// first statement. This also means that any moves that get generated when transforming out of SSA
-// form, will not interfere with (read: overlap) any defining statements in the preceding
-// basic-block.
-void LifeTimeIntervals::renumber(IR::Function *function)
-{
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- _basicBlockPosition[bb->index()].start = _lastPosition + 1;
-
- for (Stmt *s : bb->statements()) {
- if (s->asPhi())
- continue;
-
- _lastPosition += 2;
- _positionForStatement[s->id()] = _lastPosition;
- }
-
- _basicBlockPosition[bb->index()].end = _lastPosition;
- }
-}
-
-LifeTimeIntervals::~LifeTimeIntervals()
-{
- qDeleteAll(_intervals);
-}
-
-Optimizer::Optimizer(IR::Function *function)
- : function(function)
- , inSSA(false)
-{}
-
-void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool peelLoops)
-{
- showMeTheCode(function, "Before running the optimizer");
-
- cleanupBasicBlocks(function);
-
- function->removeSharedExpressions();
- int statementCount = 0;
- for (BasicBlock *bb : function->basicBlocks())
- if (!bb->isRemoved())
- statementCount += bb->statementCount();
-// showMeTheCode(function);
-
- static bool doSSA = qEnvironmentVariableIsEmpty("QV4_NO_SSA");
-
- if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA && statementCount <= 300) {
-// qout << "SSA for " << (function->name ? qPrintable(*function->name) : "<anonymous>") << endl;
-
- mergeBasicBlocks(function, nullptr, nullptr);
-
- ConvertArgLocals(function).toTemps();
- showMeTheCode(function, "After converting arguments to locals");
-
- // Calculate the dominator tree:
- DominatorTree df(function);
-
- {
- // This is in a separate scope, because loop-peeling doesn't (yet) update the LoopInfo
- // calculated by the LoopDetection. So by putting it in a separate scope, it is not
- // available after peeling.
-
- LoopDetection loopDetection(df);
- loopDetection.run(function);
- showMeTheCode(function, "After loop detection");
-// cfg2dot(function, loopDetection.allLoops());
-
- // ### disable loop peeling for now. It doesn't give any measurable performance
- // improvements at this time, but significantly increases the size of the
- // JIT generated code
- Q_UNUSED(peelLoops);
- if (0 && peelLoops) {
- QVector<LoopDetection::LoopInfo *> innerLoops = loopDetection.innermostLoops();
- LoopPeeling(df).run(innerLoops);
-
-// cfg2dot(function, loopDetection.allLoops());
- showMeTheCode(function, "After loop peeling");
- if (!innerLoops.isEmpty())
- verifyImmediateDominators(df, function);
- }
- }
-
- verifyCFG(function);
- verifyNoPointerSharing(function);
-
- df.computeDF();
-
- verifyCFG(function);
- verifyImmediateDominators(df, function);
-
- DefUses defUses(function);
-
-// qout << "Converting to SSA..." << endl;
- convertToSSA(function, df, defUses);
-// showMeTheCode(function);
-// defUses.dump();
-
-// qout << "Cleaning up phi nodes..." << endl;
- cleanupPhis(defUses);
- showMeTheCode(function, "After cleaning up phi-nodes");
-
- StatementWorklist worklist(function);
-
- if (doTypeInference) {
-// qout << "Running type inference..." << endl;
- TypeInference(qmlEngine, defUses).run(worklist);
- showMeTheCode(function, "After type inference");
-
-// qout << "Doing reverse inference..." << endl;
- ReverseInference(defUses).run(function);
-// showMeTheCode(function);
-
-// qout << "Doing type propagation..." << endl;
- TypePropagation(defUses).run(function, worklist);
-// showMeTheCode(function);
- verifyNoPointerSharing(function);
- }
-
- static const bool doOpt = qEnvironmentVariableIsEmpty("QV4_NO_OPT");
- if (doOpt) {
-// qout << "Running SSA optimization..." << endl;
- worklist.reset();
- optimizeSSA(worklist, defUses, df);
- showMeTheCode(function, "After optimization");
-
- verifyImmediateDominators(df, function);
- verifyCFG(function);
- }
-
- verifyNoPointerSharing(function);
- mergeBasicBlocks(function, &defUses, &df);
-
- verifyImmediateDominators(df, function);
- verifyCFG(function);
-
- // Basic-block cycles that are unreachable (i.e. for loops in a then-part where the
- // condition is calculated to be always false) are not yet removed. This will choke the
- // block scheduling, so remove those now.
-// qout << "Cleaning up unreachable basic blocks..." << endl;
- cleanupBasicBlocks(function);
-// showMeTheCode(function);
-
- verifyImmediateDominators(df, function);
- verifyCFG(function);
-
- // Transform the CFG into edge-split SSA.
- showMeTheCode(function, "Before edge splitting");
- splitCriticalEdges(function, df, worklist, defUses);
- showMeTheCode(function, "After edge splitting");
-
- verifyImmediateDominators(df, function);
- verifyCFG(function);
-
-// qout << "Doing block scheduling..." << endl;
-// df.dumpImmediateDominators();
- startEndLoops = BlockScheduler(function, df).go();
- showMeTheCode(function, "After basic block scheduling");
-// cfg2dot(function);
-
-#ifndef QT_NO_DEBUG
- checkCriticalEdges(function->basicBlocks());
-#endif
-
- if (!function->module->debugMode) {
- RemoveLineNumbers::run(function);
- showMeTheCode(function, "After line number removal");
- }
-
-// qout << "Finished SSA." << endl;
- inSSA = true;
- } else {
- removeUnreachleBlocks(function);
- inSSA = false;
- }
-}
-
-void Optimizer::convertOutOfSSA() {
- if (!inSSA)
- return;
-
- // There should be no critical edges at this point.
-
- for (BasicBlock *bb : function->basicBlocks()) {
- MoveMapping moves;
-
- for (BasicBlock *successor : bb->out) {
- const int inIdx = successor->in.indexOf(bb);
- Q_ASSERT(inIdx >= 0);
- for (Stmt *s : successor->statements()) {
- if (Phi *phi = s->asPhi()) {
- moves.add(clone(phi->incoming[inIdx], function),
- clone(phi->targetTemp, function)->asTemp());
- } else {
- break;
- }
- }
- }
-
- if (DebugMoveMapping) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- os << "Move mapping for function ";
- if (function->name)
- os << *function->name;
- else
- os << (void *) function;
- os << " on basic-block L" << bb->index() << ":" << endl;
- moves.dump();
- buf.close();
- qDebug("%s", buf.data().constData());
- }
-
- moves.order();
-
- moves.insertMoves(bb, function, true);
- }
-
- for (BasicBlock *bb : function->basicBlocks()) {
- while (!bb->isEmpty()) {
- if (bb->statements().first()->asPhi()) {
- bb->removeStatement(0);
- } else {
- break;
- }
- }
- }
-}
-
-LifeTimeIntervals::Ptr Optimizer::lifeTimeIntervals() const
-{
- Q_ASSERT(isInSSA());
-
- LifeRanges lifeRanges(function, startEndLoops);
-// lifeRanges.dump();
-// showMeTheCode(function);
- return lifeRanges.intervals();
-}
-
-static int countPhis(BasicBlock *bb)
-{
- int count = 0;
- for (Stmt *s : bb->statements()) {
- if (s->isa<Phi>())
- ++count;
- else
- break;
- }
-
- return count;
-}
-
-// Basic blocks can have only 1 terminator. This function returns a bit vector, where a 1 on a
-// certain index indicates that the terminator (jump) at the end of the basic block with that index
-// can be omitted.
-BitVector Optimizer::calculateOptionalJumps()
-{
- const int maxSize = function->basicBlockCount();
- BitVector optional(maxSize, false);
- if (maxSize < 2)
- return optional;
-
- BitVector reachableWithoutJump(maxSize, false);
-
- for (int i = maxSize - 1; i >= 0; --i) {
- BasicBlock *bb = function->basicBlock(i);
- if (bb->isRemoved())
- continue;
-
- if (Jump *jump = bb->statements().last()->asJump()) {
- if (reachableWithoutJump.at(jump->target->index())) {
- if (bb->statements().size() - countPhis(bb)> 1)
- reachableWithoutJump.clear();
- optional.setBit(bb->index());
- reachableWithoutJump.setBit(bb->index());
- continue;
- }
- }
-
- reachableWithoutJump.clear();
- reachableWithoutJump.setBit(bb->index());
- }
-
- return optional;
-}
-
-void Optimizer::showMeTheCode(IR::Function *function, const char *marker)
-{
- ::showMeTheCode(function, marker);
-}
-
-static inline bool overlappingStorage(const Temp &t1, const Temp &t2)
-{
- // This is the same as the operator==, but for one detail: memory locations are not sensitive
- // to types, and neither are general-purpose registers.
-
- if (t1.index != t2.index)
- return false; // different position, where-ever that may (physically) be.
- if (t1.kind != t2.kind)
- return false; // formal/local/(physical-)register/stack do never overlap
- if (t1.kind != Temp::PhysicalRegister) // Other than registers, ...
- return t1.kind == t2.kind; // ... everything else overlaps: any memory location can hold everything.
-
- // So now the index is the same, and we know that both stored in a register. If both are
- // floating-point registers, they are the same. Or, if both are non-floating-point registers,
- // generally called general-purpose registers, they are also the same.
- return (t1.type == DoubleType && t2.type == DoubleType)
- || (t1.type != DoubleType && t2.type != DoubleType);
-}
-
-MoveMapping::Moves MoveMapping::sourceUsages(Expr *e, const Moves &moves)
-{
- Moves usages;
-
- if (Temp *t = e->asTemp()) {
- for (int i = 0, ei = moves.size(); i != ei; ++i) {
- const Move &move = moves[i];
- if (Temp *from = move.from->asTemp())
- if (overlappingStorage(*from, *t))
- usages.append(move);
- }
- }
-
- return usages;
-}
-
-void MoveMapping::add(Expr *from, Temp *to) {
- if (Temp *t = from->asTemp()) {
- if (overlappingStorage(*t, *to)) {
- // assignments like fp1 = fp1 or var{&1} = double{&1} can safely be skipped.
- if (DebugMoveMapping) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- IRPrinter printer(&os);
- os << "Skipping ";
- printer.print(to);
- os << " <- ";
- printer.print(from);
- buf.close();
- qDebug("%s", buf.data().constData());
- }
- return;
- }
- }
-
- Move m(from, to);
- if (_moves.contains(m))
- return;
- _moves.append(m);
-}
-
-// Order the moves that are generated when resolving edges during register allocation (see [Wimmer1]
-// section 6 for details). Now these moves form one or more graphs, so we have to output them in
-// such an order that values don't get overwritten:
-// r1 <- r0
-// r2 <- r1
-// That input has to be ordered as follows in order to prevent the value in r1 from being lost:
-// r2 <- r1
-// r1 <- r0
-//
-// So, the algorithm is to output the leaves first, and take them out of the input. This will result
-// in some moves to become leaves (in the above example: when leaf r2 <- r1 is generated and taken
-// away, the r1 <- r0 is now a leaf), so we can output those and take those out, and repeat until
-// there are no more leafs.
-//
-// The tricky part is that there might be cycles:
-// r4 <- r5
-// r5 <- r4
-// These have to be turned into a "register swap":
-// r4 <=> r5
-//
-// So after running the above algorithm where we progressively remove the leaves, we are left with
-// zero or more cycles. To resolve those, we break one of the edges of the cycle, and for all other
-// edges we generate swaps. Note that the swaps will always occur as the last couple of moves,
-// because otherwise they might clobber sources for moves:
-// r4 <=> r5
-// r6 <- r5
-// Here, the value of r5 is already overwritten with the one in r4, so the correct order is:
-// r6 <- r5
-// r4 <=> r5
-void MoveMapping::order()
-{
- QList<Move> output;
- output.reserve(_moves.size());
-
- while (!_moves.isEmpty()) {
- // Take out all leaf edges, because we can output them without any problems.
- int nextLeaf = findLeaf();
- if (nextLeaf == -1)
- break; // No more leafs left, we're done here.
- output.append(_moves.takeAt(nextLeaf));
- // Now there might be new leaf edges: any move that had the input of the previously found
- // leaf as an output, so loop around.
- }
-
- while (!_moves.isEmpty()) {
- // We're now left with one or more cycles.
- // Step one: break the/a cycle.
- _moves.removeFirst();
- // Step two: find the other edges of the cycle, starting with the one of that is now a leaf.
- while (!_moves.isEmpty()) {
- int nextLeaf = findLeaf();
- if (nextLeaf == -1)
- break; // We're done with this cycle.
- Move m = _moves.takeAt(nextLeaf);
- // Step three: get the edges from the cycle and turn it into a swap
- m.needsSwap = true;
- output.append(m);
- // Because we took out a leaf, find the next one.
- }
- // We're done with the cycle, let's see if there are more.
- }
-
- _moves = output;
-}
-
-int MoveMapping::findLeaf() const
-{
- for (int i = 0, e = _moves.size(); i != e; ++i) {
- // Take an edge from the list...
- const Temp *target = _moves.at(i).to;
- // ... and see if its target is used as a source...
- bool targetUsedAsSource = false;
- for (int j = 0; j != e; ++j) {
- if (i == j)
- continue;
-
- Expr *source = _moves.at(j).from;
- if (const Temp *sourceTemp = source->asTemp()) {
- if (overlappingStorage(*target, *sourceTemp)) {
- targetUsedAsSource = true;
- break;
- }
- }
- }
- // ... if not, we have a leaf edge ...
- if (!targetUsedAsSource)
- return i;
- // .. otherwise we try the next one.
- }
-
- return -1; // No leaf found
-}
-
-QList<IR::Move *> MoveMapping::insertMoves(BasicBlock *bb, IR::Function *function, bool atEnd) const
-{
- QList<IR::Move *> newMoves;
- newMoves.reserve(_moves.size());
-
- int insertionPoint = atEnd ? bb->statements().size() - 1 : 0;
- for (const Move &m : _moves) {
- IR::Move *move = function->NewStmt<IR::Move>();
- move->init(clone(m.to, function), clone(m.from, function));
- move->swap = m.needsSwap;
- bb->insertStatementBefore(insertionPoint++, move);
- newMoves.append(move);
- }
-
- return newMoves;
-}
-
-void MoveMapping::dump() const
-{
- if (DebugMoveMapping) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- IRPrinter printer(&os);
- os << "Move mapping has " << _moves.size() << " moves..." << endl;
- for (const Move &m : _moves) {
- os << "\t";
- printer.print(m.to);
- if (m.needsSwap)
- os << " <-> ";
- else
- os << " <-- ";
- printer.print(m.from);
- os << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-}
-
-// References:
-// [Wimmer1] C. Wimmer and M. Franz. Linear Scan Register Allocation on SSA Form. In Proceedings of
-// CGO'10, ACM Press, 2010
-// [Wimmer2] C. Wimmer and H. Mossenbock. Optimized Interval Splitting in a Linear Scan Register
-// Allocator. In Proceedings of the ACM/USENIX International Conference on Virtual
-// Execution Environments, pages 132-141. ACM Press, 2005.
-// [Briggs] P. Briggs, K.D. Cooper, T.J. Harvey, and L.T. Simpson. Practical Improvements to the
-// Construction and Destruction of Static Single Assignment Form.
-// [Appel] A.W. Appel. Modern Compiler Implementation in Java. Second edition, Cambridge
-// University Press.
-// [Ramalingam] G. Ramalingam and T. Reps. An Incremental Algorithm for Maintaining the Dominator
-// Tree of a Reducible Flowgraph.
diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h
deleted file mode 100644
index b9d8ae0a6c..0000000000
--- a/src/qml/compiler/qv4ssa_p.h
+++ /dev/null
@@ -1,472 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4SSA_P_H
-#define QV4SSA_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qv4jsir_p.h"
-#include "qv4isel_util_p.h"
-#include <private/qv4util_p.h>
-#include <QtCore/QSharedPointer>
-
-QT_BEGIN_NAMESPACE
-class QTextStream;
-class QQmlEnginePrivate;
-
-namespace QV4 {
-namespace IR {
-
-struct LifeTimeIntervalRange {
- int start;
- int end;
-
- LifeTimeIntervalRange(int start = -1, int end = -1)
- : start(start)
- , end(end)
- {}
-
- bool covers(int position) const { return start <= position && position <= end; }
-};
-} // IR namespace
-} // QV4 namespace
-
-Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeIntervalRange, Q_PRIMITIVE_TYPE);
-
-namespace QV4 {
-namespace IR {
-
-class Q_AUTOTEST_EXPORT LifeTimeInterval {
-public:
- typedef QVarLengthArray<LifeTimeIntervalRange, 4> Ranges;
-
-private:
- Temp _temp;
- Ranges _ranges;
- int _end;
- int _reg;
- unsigned _isFixedInterval : 1;
- unsigned _isSplitFromInterval : 1;
-
-public:
- enum { InvalidPosition = -1 };
- enum { InvalidRegister = -1 };
-
- explicit LifeTimeInterval(int rangeCapacity = 4)
- : _end(InvalidPosition)
- , _reg(InvalidRegister)
- , _isFixedInterval(0)
- , _isSplitFromInterval(0)
- { _ranges.reserve(rangeCapacity); }
-
- bool isValid() const { return _end != InvalidRegister; }
-
- void setTemp(const Temp &temp) { this->_temp = temp; }
- Temp temp() const { return _temp; }
- bool isFP() const { return _temp.type == IR::DoubleType; }
-
- void setFrom(int from);
- void addRange(int from, int to);
- const Ranges &ranges() const { return _ranges; }
-
- int start() const { return _ranges.first().start; }
- int end() const { return _end; }
- bool covers(int position) const
- {
- for (int i = 0, ei = _ranges.size(); i != ei; ++i) {
- if (_ranges.at(i).covers(position))
- return true;
- }
- return false;
- }
-
- int reg() const { return _reg; }
- void setReg(int reg) { Q_ASSERT(!_isFixedInterval); _reg = reg; }
-
- bool isFixedInterval() const { return _isFixedInterval; }
- void setFixedInterval(bool isFixedInterval) { _isFixedInterval = isFixedInterval; }
-
- LifeTimeInterval split(int atPosition, int newStart);
- bool isSplitFromInterval() const { return _isSplitFromInterval; }
- void setSplitFromInterval(bool isSplitFromInterval) { _isSplitFromInterval = isSplitFromInterval; }
-
- void dump(QTextStream &out) const;
- static bool lessThan(const LifeTimeInterval *r1, const LifeTimeInterval *r2);
- static bool lessThanForTemp(const LifeTimeInterval *r1, const LifeTimeInterval *r2);
-
- void validate() const {
-#if !defined(QT_NO_DEBUG)
- // Validate the new range
- if (_end != InvalidPosition) {
- Q_ASSERT(!_ranges.isEmpty());
- for (const LifeTimeIntervalRange &range : qAsConst(_ranges)) {
- Q_ASSERT(range.start >= 0);
- Q_ASSERT(range.end >= 0);
- Q_ASSERT(range.start <= range.end);
- }
- }
-#endif
- }
-};
-
-inline bool LifeTimeInterval::lessThan(const LifeTimeInterval *r1, const LifeTimeInterval *r2)
-{
- if (r1->_ranges.first().start == r2->_ranges.first().start) {
- if (r1->isSplitFromInterval() == r2->isSplitFromInterval())
- return r1->_ranges.last().end < r2->_ranges.last().end;
- else
- return r1->isSplitFromInterval();
- } else
- return r1->_ranges.first().start < r2->_ranges.first().start;
-}
-
-class LifeTimeIntervals
-{
- Q_DISABLE_COPY(LifeTimeIntervals)
-
- LifeTimeIntervals(IR::Function *function);
- void renumber(IR::Function *function);
-
-public:
- typedef QSharedPointer<LifeTimeIntervals> Ptr;
- static Ptr create(IR::Function *function)
- { return Ptr(new LifeTimeIntervals(function)); }
-
- ~LifeTimeIntervals();
-
- // takes ownership of the pointer
- void add(LifeTimeInterval *interval)
- { _intervals.append(interval); }
-
- // After calling Optimizer::lifeTimeIntervals() the result will have all intervals in descending order of start position.
- QVector<LifeTimeInterval *> intervals() const
- { return _intervals; }
-
- int size() const
- { return _intervals.size(); }
-
- int positionForStatement(Stmt *stmt) const
- {
- Q_ASSERT(stmt->id() >= 0);
- if (static_cast<unsigned>(stmt->id()) < _positionForStatement.size())
- return _positionForStatement[stmt->id()];
-
- return Stmt::InvalidId;
- }
-
- int startPosition(BasicBlock *bb) const
- {
- Q_ASSERT(bb->index() >= 0);
- Q_ASSERT(static_cast<unsigned>(bb->index()) < _basicBlockPosition.size());
-
- return _basicBlockPosition.at(bb->index()).start;
- }
-
- int endPosition(BasicBlock *bb) const
- {
- Q_ASSERT(bb->index() >= 0);
- Q_ASSERT(static_cast<unsigned>(bb->index()) < _basicBlockPosition.size());
-
- return _basicBlockPosition.at(bb->index()).end;
- }
-
- int lastPosition() const
- {
- return _lastPosition;
- }
-
-private:
- struct BasicBlockPositions {
- int start;
- int end;
-
- BasicBlockPositions()
- : start(IR::Stmt::InvalidId)
- , end(IR::Stmt::InvalidId)
- {}
- };
-
- std::vector<BasicBlockPositions> _basicBlockPosition;
- std::vector<int> _positionForStatement;
- QVector<LifeTimeInterval *> _intervals;
- int _lastPosition;
-};
-
-class Q_QML_PRIVATE_EXPORT Optimizer
-{
- Q_DISABLE_COPY(Optimizer)
-
-public:
- Optimizer(Function *function);
-
- void run(QQmlEnginePrivate *qmlEngine, bool doTypeInference = true, bool peelLoops = true);
- void convertOutOfSSA();
-
- bool isInSSA() const
- { return inSSA; }
-
- QHash<BasicBlock *, BasicBlock *> loopStartEndBlocks() const { return startEndLoops; }
-
- LifeTimeIntervals::Ptr lifeTimeIntervals() const;
-
- BitVector calculateOptionalJumps();
-
- static void showMeTheCode(Function *function, const char *marker);
-
-private:
- Function *function;
- bool inSSA;
- QHash<BasicBlock *, BasicBlock *> startEndLoops;
-};
-
-class Q_QML_AUTOTEST_EXPORT MoveMapping
-{
-#ifdef V4_AUTOTEST
-public:
-#endif
- struct Move {
- Expr *from;
- Temp *to;
- bool needsSwap;
-
- Move(Expr *from, Temp *to, bool needsSwap = false)
- : from(from), to(to), needsSwap(needsSwap)
- {}
-
- bool operator==(const Move &other) const
- { return from == other.from && to == other.to; }
- };
- typedef QList<Move> Moves;
-
- Moves _moves;
-
- static Moves sourceUsages(Expr *e, const Moves &moves);
-
-public:
- void add(Expr *from, Temp *to);
- void order();
- QList<IR::Move *> insertMoves(BasicBlock *bb, Function *function, bool atEnd) const;
-
- void dump() const;
-
-private:
- int findLeaf() const;
-};
-
-/*
- * stack slot allocation:
- *
- * foreach bb do
- * foreach stmt do
- * if the current statement is not a phi-node:
- * purge ranges that end before the current statement
- * check for life ranges to activate, and if they don't have a stackslot associated then allocate one
- * renumber temps to stack
- * for phi nodes: check if all temps (src+dst) are assigned stack slots and marked as allocated
- * if it's a jump:
- * foreach phi node in the successor:
- * allocate slots for each temp (both sources and targets) if they don't have one allocated already
- * insert moves before the jump
- */
-class AllocateStackSlots: protected ConvertTemps
-{
- IR::LifeTimeIntervals::Ptr _intervals;
- QVector<IR::LifeTimeInterval *> _unhandled;
- QVector<IR::LifeTimeInterval *> _live;
- QBitArray _slotIsInUse;
- IR::Function *_function;
-
- int defPosition(IR::Stmt *s) const
- {
- return usePosition(s) + 1;
- }
-
- int usePosition(IR::Stmt *s) const
- {
- return _intervals->positionForStatement(s);
- }
-
-public:
- AllocateStackSlots(const IR::LifeTimeIntervals::Ptr &intervals)
- : _intervals(intervals)
- , _slotIsInUse(intervals->size(), false)
- , _function(0)
- {
- _live.reserve(8);
- _unhandled = _intervals->intervals();
- }
-
- void forFunction(IR::Function *function)
- {
- IR::Optimizer::showMeTheCode(function, "Before stack slot allocation");
- _function = function;
- toStackSlots(function);
- }
-
-protected:
- int allocateFreeSlot() override
- {
- for (int i = 0, ei = _slotIsInUse.size(); i != ei; ++i) {
- if (!_slotIsInUse[i]) {
- if (_nextUnusedStackSlot <= i) {
- Q_ASSERT(_nextUnusedStackSlot == i);
- _nextUnusedStackSlot = i + 1;
- }
- _slotIsInUse[i] = true;
- return i;
- }
- }
-
- Q_UNREACHABLE();
- return -1;
- }
-
- void process(IR::Stmt *s) override
- {
-// qDebug("L%d statement %d:", _currentBasicBlock->index, s->id);
-
- if (IR::Phi *phi = s->asPhi()) {
- visitPhi(phi);
- } else {
- // purge ranges no longer alive:
- for (int i = 0; i < _live.size(); ) {
- const IR::LifeTimeInterval *lti = _live.at(i);
- if (lti->end() < usePosition(s)) {
-// qDebug() << "\t - moving temp" << lti->temp().index << "to handled, freeing slot" << _stackSlotForTemp[lti->temp().index];
- _live.remove(i);
- Q_ASSERT(_slotIsInUse[_stackSlotForTemp[lti->temp().index]]);
- _slotIsInUse[_stackSlotForTemp[lti->temp().index]] = false;
- continue;
- } else {
- ++i;
- }
- }
-
- // active new ranges:
- while (!_unhandled.isEmpty()) {
- IR::LifeTimeInterval *lti = _unhandled.last();
- if (lti->start() > defPosition(s))
- break; // we're done
- Q_ASSERT(!_stackSlotForTemp.contains(lti->temp().index));
- _stackSlotForTemp[lti->temp().index] = allocateFreeSlot();
-// qDebug() << "\t - activating temp" << lti->temp().index << "on slot" << _stackSlotForTemp[lti->temp().index];
- _live.append(lti);
- _unhandled.removeLast();
- }
-
- visit(s);
- }
-
- if (IR::Jump *jump = s->asJump()) {
- IR::MoveMapping moves;
- for (IR::Stmt *succStmt : jump->target->statements()) {
- if (IR::Phi *phi = succStmt->asPhi()) {
- forceActivation(*phi->targetTemp);
- for (int i = 0, ei = phi->incoming.size(); i != ei; ++i) {
- IR::Expr *e = phi->incoming[i];
- if (IR::Temp *t = e->asTemp()) {
- forceActivation(*t);
- }
- if (jump->target->in[i] == _currentBasicBlock)
- moves.add(phi->incoming[i], phi->targetTemp);
- }
- } else {
- break;
- }
- }
- moves.order();
- const QList<IR::Move *> newMoves = moves.insertMoves(_currentBasicBlock, _function, true);
- for (IR::Move *move : newMoves)
- visit(move);
- }
- }
-
- void forceActivation(const IR::Temp &t)
- {
- if (_stackSlotForTemp.contains(t.index))
- return;
-
- int i = _unhandled.size() - 1;
- for (; i >= 0; --i) {
- IR::LifeTimeInterval *lti = _unhandled[i];
- if (lti->temp() == t) {
- _live.append(lti);
- _unhandled.remove(i);
- break;
- }
- }
- Q_ASSERT(i >= 0); // check that we always found the entry
-
- _stackSlotForTemp[t.index] = allocateFreeSlot();
-// qDebug() << "\t - force activating temp" << t.index << "on slot" << _stackSlotForTemp[t.index];
- }
-
- void visitPhi(IR::Phi *phi) override
- {
- Q_UNUSED(phi);
-#if !defined(QT_NO_DEBUG)
- Q_ASSERT(_stackSlotForTemp.contains(phi->targetTemp->index));
- Q_ASSERT(_slotIsInUse[_stackSlotForTemp[phi->targetTemp->index]]);
- for (IR::Expr *e : phi->incoming) {
- if (IR::Temp *t = e->asTemp())
- Q_ASSERT(_stackSlotForTemp.contains(t->index));
- }
-#endif // defined(QT_NO_DEBUG)
- }
-};
-
-} // IR namespace
-} // QV4 namespace
-
-
-Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeInterval, Q_MOVABLE_TYPE);
-
-QT_END_NAMESPACE
-
-#endif // QV4SSA_P_H