diff options
author | Lars Knoll <lars.knoll@qt.io> | 2017-06-22 10:01:17 +0200 |
---|---|---|
committer | Erik Verbruggen <erik.verbruggen@qt.io> | 2017-06-30 11:58:44 +0000 |
commit | 29e41a9ee61a05274f77f89e9ffd8875f90d3308 (patch) | |
tree | 7de81ef4ad3bb0b2b0cd3313ee4dd03a76e9c681 | |
parent | 3a9f4d3ae701c7119016a0bf8b4e65ceb17864b0 (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>
44 files changed, 41 insertions, 20534 deletions
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index 14954bf380..f3617ed6ff 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -43,7 +43,6 @@ #include "qqmldebugpacket.h" #include <private/qv4engine_p.h> -#include <private/qv4isel_moth_p.h> #include <private/qv4function_p.h> #include <private/qqmldebugconnector_p.h> #include <private/qv8engine_p.h> diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp index 41ce9ac6dc..5e4a8e3eb1 100644 --- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp @@ -50,7 +50,6 @@ #include <private/qv4objectiterator_p.h> #include <private/qv4identifier_p.h> #include <private/qv4runtime_p.h> -#include <private/qv4isel_moth_p.h> #include <private/qqmldebugserviceinterfaces_p.h> #include <QtQml/qjsengine.h> 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 *<i = _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 <i = interval(opd); - lti.setFrom(defPosition(s)); - live.remove(lti.temp().index); - _sortedIntervals->add(<i); - } - //### 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 diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri deleted file mode 100644 index 7ea4e951d5..0000000000 --- a/src/qml/jit/jit.pri +++ /dev/null @@ -1,22 +0,0 @@ -include(../../3rdparty/masm/masm-defs.pri) - -INCLUDEPATH += $$PWD -INCLUDEPATH += $$OUT_PWD - -HEADERS += \ - $$PWD/qv4assembler_p.h \ - $$PWD/qv4regalloc_p.h \ - $$PWD/qv4targetplatform_p.h \ - $$PWD/qv4isel_masm_p.h \ - $$PWD/qv4binop_p.h \ - $$PWD/qv4unop_p.h \ - $$PWD/qv4registerinfo_p.h - -SOURCES += \ - $$PWD/qv4assembler.cpp \ - $$PWD/qv4regalloc.cpp \ - $$PWD/qv4isel_masm.cpp \ - $$PWD/qv4binop.cpp \ - $$PWD/qv4unop.cpp \ - -include(../../3rdparty/masm/masm.pri) diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp deleted file mode 100644 index d062f3bbb2..0000000000 --- a/src/qml/jit/qv4assembler.cpp +++ /dev/null @@ -1,726 +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_masm_p.h" -#include "qv4runtime_p.h" -#include "qv4ssa_p.h" -#include "qv4regalloc_p.h" -#include "qv4assembler_p.h" - -#include <assembler/LinkBuffer.h> -#include <WTFStubs.h> - -#if !defined(V4_BOOTSTRAP) -#include "qv4function_p.h" -#endif - -#include <iostream> -#include <QBuffer> -#include <QCoreApplication> - -#if ENABLE(ASSEMBLER) - -#if USE(UDIS86) -# include <udis86.h> -#endif - -using namespace QV4; -using namespace QV4::JIT; - -CompilationUnit::~CompilationUnit() -{ -} - -#if !defined(V4_BOOTSTRAP) - -void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) -{ - runtimeFunctions.resize(data->functionTableSize); - runtimeFunctions.fill(0); - for (int i = 0 ;i < runtimeFunctions.size(); ++i) { - const CompiledData::Function *compiledFunction = data->functionAt(i); - - QV4::Function *runtimeFunction = new QV4::Function(engine, this, compiledFunction, - (ReturnedValue (*)(QV4::ExecutionEngine *, const uchar *)) codeRefs[i].code().executableAddress()); - runtimeFunctions[i] = runtimeFunction; - } -} - -bool CompilationUnit::memoryMapCode(QString *errorString) -{ - Q_UNUSED(errorString); - codeRefs.resize(data->functionTableSize); - - const char *basePtr = reinterpret_cast<const char *>(data); - - for (uint i = 0; i < data->functionTableSize; ++i) { - const CompiledData::Function *compiledFunction = data->functionAt(i); - void *codePtr = const_cast<void *>(reinterpret_cast<const void *>(basePtr + compiledFunction->codeOffset)); - JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr)); - JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize); - codeRefs[i] = codeRef; - - static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM"); - if (showCode) { - WTF::dataLogF("Mapped JIT code for %s\n", qPrintable(stringAt(compiledFunction->nameIndex))); - disassemble(codeRef.code(), compiledFunction->codeSize, " ", WTF::dataFile()); - } - } - - return true; -} - -#endif // !defined(V4_BOOTSTRAP) - -void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) -{ - const int codeAlignment = 16; - quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize); - Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); - for (int i = 0; i < codeRefs.size(); ++i) { - CompiledData::Function *compiledFunction = const_cast<CompiledData::Function *>(unit->functionAt(i)); - compiledFunction->codeOffset = offset; - compiledFunction->codeSize = codeRefs.at(i).size(); - offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize); - } -} - -bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) -{ - Q_ASSERT(device->pos() == unit->unitSize); - Q_ASSERT(device->atEnd()); - Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); - - QByteArray padding; - - for (int i = 0; i < codeRefs.size(); ++i) { - const CompiledData::Function *compiledFunction = unit->functionAt(i); - - if (device->pos() > qint64(compiledFunction->codeOffset)) { - *errorString = QStringLiteral("Invalid state of cache file to write."); - return false; - } - - const quint64 paddingSize = compiledFunction->codeOffset - device->pos(); - padding.fill(0, paddingSize); - qint64 written = device->write(padding); - if (written != padding.size()) { - *errorString = device->errorString(); - return false; - } - - const void *undecoratedCodePtr = codeRefs.at(i).code().dataLocation(); - written = device->write(reinterpret_cast<const char *>(undecoratedCodePtr), compiledFunction->codeSize); - if (written != qint64(compiledFunction->codeSize)) { - *errorString = device->errorString(); - return false; - } - } - return true; -} - -template <typename TargetConfiguration> -Assembler<TargetConfiguration>::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) - : _function(function) - , _nextBlock(0) - , _executableAllocator(executableAllocator) - , _jsGenerator(jsGenerator) -{ - _addrs.resize(_function->basicBlockCount()); - _patches.resize(_function->basicBlockCount()); - _labelPatches.resize(_function->basicBlockCount()); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::registerBlock(IR::BasicBlock* block, IR::BasicBlock *nextBlock) -{ - _addrs[block->index()] = label(); - catchBlock = block->catchBlock; - _nextBlock = nextBlock; -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) -{ - Q_UNUSED(current); - - if (target != _nextBlock) - _patches[target->index()].push_back(jump()); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) -{ - _patches[targetBlock->index()].push_back(targetJump); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, Label target) -{ - DataLabelPatch p; - p.dataLabel = patch; - p.target = target; - _dataLabelPatches.push_back(p); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, IR::BasicBlock *target) -{ - _labelPatches[target->index()].push_back(patch); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) -{ - generateCJumpOnCompare(RelationalCondition::NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond, - RegisterID left, - TrustedImm32 right, - IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - if (trueBlock == _nextBlock) { - Jump target = branch32(invert(cond), left, right); - addPatch(falseBlock, target); - } else { - Jump target = branch32(cond, left, right); - addPatch(trueBlock, target); - jumpToBlock(currentBlock, falseBlock); - } -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond, - RegisterID left, - RegisterID right, - IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - if (trueBlock == _nextBlock) { - Jump target = branch32(invert(cond), left, right); - addPatch(falseBlock, target); - } else { - Jump target = branch32(cond, left, right); - addPatch(trueBlock, target); - jumpToBlock(currentBlock, falseBlock); - } -} - -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Pointer -Assembler<TargetConfiguration>::loadAddressForWriting(RegisterID tmp, IR::Expr *e, WriteBarrier::Type *barrier) -{ - if (barrier) - *barrier = WriteBarrier::NoBarrier; - IR::Temp *t = e->asTemp(); - if (t) - return loadTempAddress(t); - else - return loadArgLocalAddressForWriting(tmp, e->asArgLocal(), barrier); -} - -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadTempAddress(IR::Temp *t) -{ - if (t->kind == IR::Temp::StackSlot) - return stackSlotPointer(t); - else - Q_UNREACHABLE(); -} - -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Pointer -Assembler<TargetConfiguration>::loadArgLocalAddressForWriting(RegisterID baseReg, IR::ArgLocal *al, WriteBarrier::Type *barrier) -{ - if (barrier) - *barrier = _function->argLocalRequiresWriteBarrier(al) ? WriteBarrier::Barrier : WriteBarrier::NoBarrier; - - int32_t offset = 0; - int scope = al->scope; - loadPtr(Address(EngineRegister, targetStructureOffset(offsetof(EngineBase, current))), baseReg); - - const qint32 outerOffset = targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, outer)); - const qint32 localsOffset = targetStructureOffset(Heap::CallContextData::baseOffset + offsetof(Heap::CallContextData, function)) - + 8 // locals is always 8 bytes away from function, regardless of pointer size. - + offsetof(ValueArray<0>, values); - - while (scope) { - loadPtr(Address(baseReg, outerOffset), baseReg); - --scope; - } - switch (al->kind) { - case IR::ArgLocal::Formal: - case IR::ArgLocal::ScopedFormal: { - if (barrier && *barrier == WriteBarrier::Barrier) { - // if we need a barrier, the baseReg has to point to the ExecutionContext - // callData comes directly after locals, calculate the offset using that - offset = localsOffset + _function->localsCountForScope(al) * sizeof(Value); - offset += sizeof(CallData) + (al->index - 1) * sizeof(Value); - } else { - const qint32 callDataOffset = targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, callData)); - loadPtr(Address(baseReg, callDataOffset), baseReg); - offset = sizeof(CallData) + (al->index - 1) * sizeof(Value); - } - } break; - case IR::ArgLocal::Local: - case IR::ArgLocal::ScopedLocal: { - offset = localsOffset + al->index * sizeof(Value); - } break; - default: - Q_UNREACHABLE(); - } - return Pointer(baseReg, offset); -} - -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadStringAddress(RegisterID reg, const QString &string) -{ - loadPtr(Address(Assembler::EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), Assembler::ScratchRegister); - loadPtr(Address(Assembler::ScratchRegister, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, compilationUnit))), Assembler::ScratchRegister); - loadPtr(Address(Assembler::ScratchRegister, offsetof(CompiledData::CompilationUnitBase, runtimeStrings)), reg); - const int id = _jsGenerator->registerString(string); - return Pointer(reg, id * RegisterSize); -} - -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(IR::Const *c, RegisterID baseReg) -{ - return loadConstant(convertToValue<TargetPrimitive>(c), baseReg); -} - -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(const TargetPrimitive &v, RegisterID baseReg) -{ - loadPtr(Address(Assembler::EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), baseReg); - loadPtr(Address(baseReg, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, constantTable))), baseReg); - const int index = _jsGenerator->registerConstant(v.rawValue()); - return Address(baseReg, index * sizeof(QV4::Value)); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::loadStringRef(RegisterID reg, const QString &string) -{ - const int id = _jsGenerator->registerString(string); - move(TrustedImm32(id), reg); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::storeValue(TargetPrimitive value, IR::Expr *destination) -{ - WriteBarrier::Type barrier; - Address addr = loadAddressForWriting(ScratchRegister, destination, &barrier); - storeValue(value, addr, barrier); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::enterStandardStackFrame(const RegisterInformation ®ularRegistersToSave, - const RegisterInformation &fpRegistersToSave) -{ - platformEnterStandardStackFrame(this); - - move(StackPointerRegister, JITTargetPlatform::FramePointerRegister); - - const int frameSize = _stackLayout->calculateStackFrameSize(); - subPtr(TrustedImm32(frameSize), StackPointerRegister); - - Address slotAddr(JITTargetPlatform::FramePointerRegister, 0); - for (int i = 0, ei = fpRegistersToSave.size(); i < ei; ++i) { - Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); - slotAddr.offset -= sizeof(double); - TargetConfiguration::MacroAssembler::storeDouble(fpRegistersToSave.at(i).reg<FPRegisterID>(), slotAddr); - } - for (int i = 0, ei = regularRegistersToSave.size(); i < ei; ++i) { - Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); - slotAddr.offset -= RegisterSize; - storePtr(regularRegistersToSave.at(i).reg<RegisterID>(), slotAddr); - } - - platformFinishEnteringStandardStackFrame(this); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::leaveStandardStackFrame(const RegisterInformation ®ularRegistersToSave, - const RegisterInformation &fpRegistersToSave) -{ - Address slotAddr(JITTargetPlatform::FramePointerRegister, -regularRegistersToSave.size() * RegisterSize - fpRegistersToSave.size() * sizeof(double)); - - // restore the callee saved registers - for (int i = regularRegistersToSave.size() - 1; i >= 0; --i) { - Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); - loadPtr(slotAddr, regularRegistersToSave.at(i).reg<RegisterID>()); - slotAddr.offset += RegisterSize; - } - for (int i = fpRegistersToSave.size() - 1; i >= 0; --i) { - Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); - TargetConfiguration::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>()); - slotAddr.offset += sizeof(double); - } - - Q_ASSERT(slotAddr.offset == 0); - - const int frameSize = _stackLayout->calculateStackFrameSize(); - platformLeaveStandardStackFrame(this, frameSize); -} - - - - -// Try to load the source expression into the destination FP register. This assumes that two -// general purpose (integer) registers are available: the ScratchRegister and the -// ReturnValueRegister. It returns a Jump if no conversion can be performed. -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::genTryDoubleConversion(IR::Expr *src, FPRegisterID dest) -{ - switch (src->type) { - case IR::DoubleType: - moveDouble(toDoubleRegister(src, dest), dest); - return Assembler::Jump(); - case IR::SInt32Type: - convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister), - dest); - return Assembler::Jump(); - case IR::UInt32Type: - convertUInt32ToDouble(toUInt32Register(src, Assembler::ScratchRegister), - dest, Assembler::ReturnValueRegister); - return Assembler::Jump(); - case IR::NullType: - case IR::UndefinedType: - case IR::BoolType: - // TODO? - case IR::StringType: - return jump(); - default: - break; - } - - Q_ASSERT(src->asTemp() || src->asArgLocal()); - - // It's not a number type, so it cannot be in a register. - Q_ASSERT(src->asArgLocal() || src->asTemp()->kind != IR::Temp::PhysicalRegister || src->type == IR::BoolType); - - Assembler::Pointer tagAddr = loadAddressForReading(Assembler::ScratchRegister, src); - tagAddr.offset += 4; - load32(tagAddr, Assembler::ScratchRegister); - - // check if it's an int32: - Assembler::Jump isNoInt = branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(quint32(ValueTypeInternal::Integer))); - convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister), dest); - Assembler::Jump intDone = jump(); - - // not an int, check if it's a double: - isNoInt.link(this); - Assembler::Jump isNoDbl = RegisterSizeDependentOps::checkIfTagRegisterIsDouble(this, ScratchRegister); - toDoubleRegister(src, dest); - intDone.link(this); - - return isNoDbl; -} - -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchDouble(bool invertCondition, IR::AluOp op, - IR::Expr *left, IR::Expr *right) -{ - DoubleCondition cond; - switch (op) { - case IR::OpGt: cond = Assembler::DoubleGreaterThan; break; - case IR::OpLt: cond = Assembler::DoubleLessThan; break; - case IR::OpGe: cond = Assembler::DoubleGreaterThanOrEqual; break; - case IR::OpLe: cond = Assembler::DoubleLessThanOrEqual; break; - case IR::OpEqual: - case IR::OpStrictEqual: cond = Assembler::DoubleEqual; break; - case IR::OpNotEqual: - case IR::OpStrictNotEqual: cond = Assembler::DoubleNotEqualOrUnordered; break; // No, the inversion of DoubleEqual is NOT DoubleNotEqual. - default: - Q_UNREACHABLE(); - } - if (invertCondition) - cond = TargetConfiguration::MacroAssembler::invert(cond); - - return TargetConfiguration::MacroAssembler::branchDouble(cond, toDoubleRegister(left, FPGpr0), toDoubleRegister(right, JITTargetPlatform::FPGpr1)); -} - -template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right) -{ - Assembler::RelationalCondition cond; - switch (op) { - case IR::OpGt: cond = Assembler::GreaterThan; break; - case IR::OpLt: cond = Assembler::LessThan; break; - case IR::OpGe: cond = Assembler::GreaterThanOrEqual; break; - case IR::OpLe: cond = Assembler::LessThanOrEqual; break; - case IR::OpEqual: - case IR::OpStrictEqual: cond = Assembler::Equal; break; - case IR::OpNotEqual: - case IR::OpStrictNotEqual: cond = Assembler::NotEqual; break; - default: - Q_UNREACHABLE(); - } - if (invertCondition) - cond = TargetConfiguration::MacroAssembler::invert(cond); - - return TargetConfiguration::MacroAssembler::branch32(cond, - toInt32Register(left, Assembler::ScratchRegister), - toInt32Register(right, Assembler::ReturnValueRegister)); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave) -{ - _stackLayout.reset(new StackLayout(_function, maxArgCountForBuiltins, regularRegistersToSave, fpRegistersToSave)); -} - -template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::returnFromFunction(IR::Ret *s, RegisterInformation regularRegistersToSave, RegisterInformation fpRegistersToSave) -{ - if (!s) { - // this only happens if the method doesn't have a return statement and can - // only exit through an exception - } else if (IR::Temp *t = s->expr->asTemp()) { - RegisterSizeDependentOps::setFunctionReturnValueFromTemp(this, t); - } else if (IR::Const *c = s->expr->asConst()) { - auto retVal = convertToValue<TargetPrimitive>(c); - RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); - } else { - Q_UNREACHABLE(); - Q_UNUSED(s); - } - - Label leaveStackFrame = label(); - - const int locals = stackLayout().calculateJSStackFrameSize(); - subPtr(TrustedImm32(sizeof(QV4::Value)*locals), JITTargetPlatform::LocalsRegister); - storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop)))); - - leaveStandardStackFrame(regularRegistersToSave, fpRegistersToSave); - ret(); - - exceptionReturnLabel = label(); - auto retVal = TargetPrimitive::undefinedValue(); - RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); - jump(leaveStackFrame); -} - -namespace { -class QIODevicePrintStream: public FilePrintStream -{ - Q_DISABLE_COPY(QIODevicePrintStream) - -public: - explicit QIODevicePrintStream(QIODevice *dest) - : FilePrintStream(0) - , dest(dest) - , buf(4096, '0') - { - Q_ASSERT(dest); - } - - ~QIODevicePrintStream() - {} - - void vprintf(const char* format, va_list argList) override WTF_ATTRIBUTE_PRINTF(2, 0) - { - const int written = qvsnprintf(buf.data(), buf.size(), format, argList); - if (written > 0) - dest->write(buf.constData(), written); - memset(buf.data(), 0, qMin(written, buf.size())); - } - - void flush() override - {} - -private: - QIODevice *dest; - QByteArray buf; -}; -} // anonymous namespace - -static void printDisassembledOutputWithCalls(QByteArray processedOutput, const QHash<void*, const char*>& functions) -{ - for (QHash<void*, const char*>::ConstIterator it = functions.begin(), end = functions.end(); - it != end; ++it) { - const QByteArray ptrString = "0x" + QByteArray::number(quintptr(it.key()), 16); - int idx = processedOutput.indexOf(ptrString); - if (idx < 0) - continue; - idx = processedOutput.lastIndexOf('\n', idx); - if (idx < 0) - continue; - processedOutput = processedOutput.insert(idx, QByteArrayLiteral(" ; call ") + it.value()); - } - - qDebug("%s", processedOutput.constData()); -} - -#if defined(Q_OS_LINUX) -static FILE *pmap; - -static void qt_closePmap() -{ - if (pmap) { - fclose(pmap); - pmap = 0; - } -} - -#endif - -template <typename TargetConfiguration> -JSC::MacroAssemblerCodeRef Assembler<TargetConfiguration>::link(int *codeSize) -{ - Label endOfCode = label(); - - { - for (size_t i = 0, ei = _patches.size(); i != ei; ++i) { - Label target = _addrs.at(i); - Q_ASSERT(target.isSet()); - for (Jump jump : qAsConst(_patches.at(i))) - jump.linkTo(target, this); - } - } - - JSC::JSGlobalData dummy(_executableAllocator); - JSC::LinkBuffer<typename TargetConfiguration::MacroAssembler> linkBuffer(dummy, this, 0); - - for (const DataLabelPatch &p : qAsConst(_dataLabelPatches)) - linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); - - // link exception handlers - for (Jump jump : qAsConst(exceptionPropagationJumps)) - linkBuffer.link(jump, linkBuffer.locationOf(exceptionReturnLabel)); - - { - for (size_t i = 0, ei = _labelPatches.size(); i != ei; ++i) { - Label target = _addrs.at(i); - Q_ASSERT(target.isSet()); - for (DataLabelPtr label : _labelPatches.at(i)) - linkBuffer.patch(label, linkBuffer.locationOf(target)); - } - } - - *codeSize = linkBuffer.offsetOf(endOfCode); - - QByteArray name; - - JSC::MacroAssemblerCodeRef codeRef; - - static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM"); - if (showCode) { - QHash<void*, const char*> functions; -#ifndef QT_NO_DEBUG - for (CallInfo call : qAsConst(_callInfos)) - functions[linkBuffer.locationOf(call.label).dataLocation()] = call.functionName; -#endif - - QBuffer buf; - buf.open(QIODevice::WriteOnly); - WTF::setDataFile(new QIODevicePrintStream(&buf)); - - name = _function->name->toUtf8(); - if (name.isEmpty()) - name = "IR::Function(0x" + QByteArray::number(quintptr(_function), 16) + ')'; - codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); - - WTF::setDataFile(stderr); - printDisassembledOutputWithCalls(buf.data(), functions); - } else { - codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); - } - -#if defined(Q_OS_LINUX) - // This implements writing of JIT'd addresses so that perf can find the - // symbol names. - // - // Perf expects the mapping to be in a certain place and have certain - // content, for more information, see: - // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt - static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP"); - static bool profileInitialized = false; - if (doProfile && !profileInitialized) { - profileInitialized = true; - - char pname[PATH_MAX]; - snprintf(pname, PATH_MAX - 1, "/tmp/perf-%lu.map", - (unsigned long)QCoreApplication::applicationPid()); - - pmap = fopen(pname, "w"); - if (!pmap) - qWarning("QV4: Can't write %s, call stacks will not contain JavaScript function names", pname); - - // make sure we clean up nicely - std::atexit(qt_closePmap); - } - - if (pmap) { - // this may have been pre-populated, if QV4_SHOW_ASM was on - if (name.isEmpty()) { - name = _function->name->toUtf8(); - if (name.isEmpty()) - name = "IR::Function(0x" + QByteArray::number(quintptr(_function), 16) + ')'; - } - - fprintf(pmap, "%llx %x %.*s\n", - (long long unsigned int)codeRef.code().executableAddress(), - *codeSize, - name.length(), - name.constData()); - fflush(pmap); - } -#endif - - return codeRef; -} - -template class QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>; -#if defined(V4_BOOTSTRAP) -#if !CPU(ARM_THUMB2) -template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>; -#endif -#if !CPU(ARM64) -template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>; -#endif -#endif - -#endif diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h deleted file mode 100644 index 9e38696d7a..0000000000 --- a/src/qml/jit/qv4assembler_p.h +++ /dev/null @@ -1,1851 +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 QV4ASSEMBLER_P_H -#define QV4ASSEMBLER_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/qv4jsir_p.h" -#include "private/qv4isel_p.h" -#include "private/qv4isel_util_p.h" -#include "private/qv4value_p.h" -#include "private/qv4context_p.h" -#include "private/qv4engine_p.h" -#include "private/qv4writebarrier_p.h" -#include "qv4targetplatform_p.h" - -#include <config.h> -#include <wtf/Vector.h> - -#include <climits> - -#if ENABLE(ASSEMBLER) - -#include <assembler/MacroAssembler.h> -#include <assembler/MacroAssemblerCodeRef.h> - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace JIT { - -struct CompilationUnit : public QV4::CompiledData::CompilationUnit -{ - virtual ~CompilationUnit(); - -#if !defined(V4_BOOTSTRAP) - void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; - bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; -#endif - void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; - bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; - - // Coderef + execution engine - - QVector<JSC::MacroAssemblerCodeRef> codeRefs; -}; - -template <typename PlatformAssembler, TargetOperatingSystemSpecialization Specialization> -struct AssemblerTargetConfiguration -{ - typedef JSC::MacroAssembler<PlatformAssembler> MacroAssembler; - typedef TargetPlatform<PlatformAssembler, Specialization> Platform; - // More things coming here in the future, such as Target OS -}; - -#if CPU(ARM_THUMB2) -typedef JSC::MacroAssemblerARMv7 DefaultPlatformMacroAssembler; -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#elif CPU(ARM64) -typedef JSC::MacroAssemblerARM64 DefaultPlatformMacroAssembler; -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#elif CPU(ARM_TRADITIONAL) -typedef JSC::MacroAssemblerARM DefaultPlatformMacroAssembler; -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#elif CPU(MIPS) -typedef JSC::MacroAssemblerMIPS DefaultPlatformMacroAssembler; -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#elif CPU(X86) -typedef JSC::MacroAssemblerX86 DefaultPlatformMacroAssembler; -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#elif CPU(X86_64) -typedef JSC::MacroAssemblerX86_64 DefaultPlatformMacroAssembler; - -#if OS(WINDOWS) -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, WindowsSpecialization> DefaultAssemblerTargetConfiguration; -#else -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#endif - -#elif CPU(SH4) -typedef JSC::MacroAssemblerSH4 DefaultPlatformMacroAssembler; -typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; -#endif - -#define isel_stringIfyx(s) #s -#define isel_stringIfy(s) isel_stringIfyx(s) - -#define generateRuntimeCall(as, t, function, ...) \ - as->generateFunctionCallImp(Runtime::Method_##function##_NeedsExceptionCheck, t, "Runtime::" isel_stringIfy(function), typename JITAssembler::RuntimeCall(QV4::Runtime::function), __VA_ARGS__) - - -template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform, int RegisterSize> -struct RegisterSizeDependentAssembler -{ -}; - -template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform> -struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatform, 4> -{ - using RegisterID = typename JITAssembler::RegisterID; - using FPRegisterID = typename JITAssembler::FPRegisterID; - using RelationalCondition = typename JITAssembler::RelationalCondition; - using ResultCondition = typename JITAssembler::ResultCondition; - using Address = typename JITAssembler::Address; - using Pointer = typename JITAssembler::Pointer; - using TrustedImm32 = typename JITAssembler::TrustedImm32; - using TrustedImm64 = typename JITAssembler::TrustedImm64; - using Jump = typename JITAssembler::Jump; - using Label = typename JITAssembler::Label; - using ValueTypeInternal = Value::ValueTypeInternal_32; - using TargetPrimitive = TargetPrimitive32; - - static void emitSetGrayBit(JITAssembler *as, RegisterID base) - { - bool returnValueUsed = (base == TargetPlatform::ReturnValueRegister); - - as->push(TargetPlatform::EngineRegister); // free up one register for work - - RegisterID grayBitmap = returnValueUsed ? TargetPlatform::ScratchRegister : TargetPlatform::ReturnValueRegister; - as->move(base, grayBitmap); - Q_ASSERT(base != grayBitmap); - as->urshift32(TrustedImm32(Chunk::ChunkShift), grayBitmap); - as->lshift32(TrustedImm32(Chunk::ChunkShift), grayBitmap); - Q_STATIC_ASSERT(offsetof(Chunk, grayBitmap) == 0); - - RegisterID index = base; - as->move(base, index); - as->sub32(grayBitmap, index); - as->urshift32(TrustedImm32(Chunk::SlotSizeShift), index); - RegisterID grayIndex = TargetPlatform::EngineRegister; - as->move(index, grayIndex); - as->urshift32(TrustedImm32(Chunk::BitShift), grayIndex); - as->lshift32(TrustedImm32(2), grayIndex); // 4 bytes per quintptr - as->add32(grayIndex, grayBitmap); - as->and32(TrustedImm32(Chunk::Bits - 1), index); - - RegisterID bit = TargetPlatform::EngineRegister; - as->move(TrustedImm32(1), bit); - as->lshift32(index, bit); - - as->load32(Pointer(grayBitmap, 0), index); - as->or32(bit, index); - as->store32(index, Pointer(grayBitmap, 0)); - - as->pop(TargetPlatform::EngineRegister); - } - -#if WRITEBARRIER(none) - static Q_ALWAYS_INLINE void emitWriteBarrier(JITAssembler *, Address) {} -#endif - - static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest) - { - as->MacroAssembler::loadDouble(addr, dest); - } - - static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr, WriteBarrier::Type barrier) - { - as->MacroAssembler::storeDouble(source, addr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, addr); - } - - static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target) - { - WriteBarrier::Type barrier; - Pointer ptr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); - as->storeDouble(source, ptr, barrier); - } - - static void storeValue(JITAssembler *as, TargetPrimitive value, Address destination, WriteBarrier::Type barrier) - { - as->store32(TrustedImm32(value.value()), destination); - destination.offset += 4; - as->store32(TrustedImm32(value.tag()), destination); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, destination); - } - - template <typename Source, typename Destination> - static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination, WriteBarrier::Type barrier) - { - as->loadDouble(source, TargetPlatform::FPGpr0); - // We need to pass NoBarrier to storeDouble and call emitWriteBarrier ourselves, as the - // code in storeDouble assumes the type we're storing is actually a double, something - // that isn't always the case here. - as->storeDouble(TargetPlatform::FPGpr0, destination, WriteBarrier::NoBarrier); - if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, destination); - } - - static void loadDoubleConstant(JITAssembler *as, IR::Const *c, FPRegisterID target) - { - as->MacroAssembler::loadDouble(as->loadConstant(c, TargetPlatform::ScratchRegister), target); - } - - static void storeReturnValue(JITAssembler *as, FPRegisterID dest) - { - as->moveIntsToDouble(TargetPlatform::LowReturnValueRegister, TargetPlatform::HighReturnValueRegister, dest, TargetPlatform::FPGpr0); - } - - static void storeReturnValue(JITAssembler *as, const Pointer &dest, WriteBarrier::Type barrier) - { - Address destination = dest; - as->store32(TargetPlatform::LowReturnValueRegister, destination); - destination.offset += 4; - as->store32(TargetPlatform::HighReturnValueRegister, destination); - if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, dest); - } - - static void setFunctionReturnValueFromTemp(JITAssembler *as, IR::Temp *t) - { - const auto lowReg = TargetPlatform::LowReturnValueRegister; - const auto highReg = TargetPlatform::HighReturnValueRegister; - - if (t->kind == IR::Temp::PhysicalRegister) { - switch (t->type) { - case IR::DoubleType: - as->moveDoubleToInts((FPRegisterID) t->index, lowReg, highReg); - break; - case IR::UInt32Type: { - RegisterID srcReg = (RegisterID) t->index; - Jump intRange = as->branch32(JITAssembler::GreaterThanOrEqual, srcReg, TrustedImm32(0)); - as->convertUInt32ToDouble(srcReg, TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister); - as->moveDoubleToInts(TargetPlatform::FPGpr0, lowReg, highReg); - Jump done = as->jump(); - intRange.link(as); - as->move(srcReg, lowReg); - as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Integer)), highReg); - done.link(as); - } break; - case IR::SInt32Type: - as->move((RegisterID) t->index, lowReg); - as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Integer)), highReg); - break; - case IR::BoolType: - as->move((RegisterID) t->index, lowReg); - as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Boolean)), highReg); - break; - default: - Q_UNREACHABLE(); - } - } else { - Pointer addr = as->loadAddressForReading(TargetPlatform::ScratchRegister, t); - as->load32(addr, lowReg); - addr.offset += 4; - as->load32(addr, highReg); - } - } - - static void setFunctionReturnValueFromConst(JITAssembler *as, TargetPrimitive retVal) - { - as->move(TrustedImm32(retVal.value()), TargetPlatform::LowReturnValueRegister); - as->move(TrustedImm32(retVal.tag()), TargetPlatform::HighReturnValueRegister); - } - - static void loadArgumentInRegister(JITAssembler *as, IR::Temp* temp, RegisterID dest, int argumentNumber) - { - Q_UNUSED(as); - Q_UNUSED(temp); - Q_UNUSED(dest); - Q_UNUSED(argumentNumber); - } - - static void loadArgumentInRegister(JITAssembler *as, IR::ArgLocal* al, RegisterID dest, int argumentNumber) - { - Q_UNUSED(as); - Q_UNUSED(al); - Q_UNUSED(dest); - Q_UNUSED(argumentNumber); - } - - static void loadArgumentInRegister(JITAssembler *as, IR::Const* c, RegisterID dest, int argumentNumber) - { - Q_UNUSED(as); - Q_UNUSED(c); - Q_UNUSED(dest); - Q_UNUSED(argumentNumber); - } - - static void loadArgumentInRegister(JITAssembler *as, IR::Expr* expr, RegisterID dest, int argumentNumber) - { - Q_UNUSED(as); - Q_UNUSED(expr); - Q_UNUSED(dest); - Q_UNUSED(argumentNumber); - } - - static void zeroRegister(JITAssembler *as, RegisterID reg) - { - as->move(TrustedImm32(0), reg); - } - - static void zeroStackSlot(JITAssembler *as, int slot) - { - as->poke(TrustedImm32(0), slot); - } - - static void generateCJumpOnUndefined(JITAssembler *as, - RelationalCondition cond, IR::Expr *right, - RegisterID scratchRegister, RegisterID tagRegister, - IR::BasicBlock *nextBlock, IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) - { - Pointer tagAddr = as->loadAddressForReading(scratchRegister, right); - as->load32(tagAddr, tagRegister); - Jump j = as->branch32(JITAssembler::invert(cond), tagRegister, TrustedImm32(0)); - as->addPatch(falseBlock, j); - - tagAddr.offset += 4; - as->load32(tagAddr, tagRegister); - const TrustedImm32 tag(QV4::Value::Managed_Type_Internal); - Q_ASSERT(nextBlock == as->nextBlock()); - Q_UNUSED(nextBlock); - as->generateCJumpOnCompare(cond, tagRegister, tag, currentBlock, trueBlock, falseBlock); - } - - static void convertVarToSInt32(JITAssembler *as, IR::Expr *source, IR::Expr *target) - { - Q_ASSERT(source->type == IR::VarType); - // load the tag: - Pointer addr = as->loadAddressForReading(TargetPlatform::ScratchRegister, source); - Pointer tagAddr = addr; - tagAddr.offset += 4; - as->load32(tagAddr, TargetPlatform::ReturnValueRegister); - - // check if it's an int32: - Jump fallback = as->branch32(RelationalCondition::NotEqual, TargetPlatform::ReturnValueRegister, - TrustedImm32(quint32(Value::ValueTypeInternal_32::Integer))); - IR::Temp *targetTemp = target->asTemp(); - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - as->load32(addr, TargetPlatform::ReturnValueRegister); - WriteBarrier::Type barrier; - Pointer targetAddr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); - as->store32(TargetPlatform::ReturnValueRegister, targetAddr); - targetAddr.offset += 4; - as->store32(TrustedImm32(quint32(Value::ValueTypeInternal_32::Integer)), targetAddr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, targetAddr); - } else { - as->load32(addr, (RegisterID) targetTemp->index); - } - Jump intDone = as->jump(); - - // not an int: - fallback.link(as); - generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt, - as->loadAddressForReading(TargetPlatform::ScratchRegister, source)); - as->storeInt32(TargetPlatform::ReturnValueRegister, target); - - intDone.link(as); - } - - static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr, WriteBarrier::Type barrier) - { - as->store32(registerWithPtr, destAddr); - destAddr.offset += 4; - as->store32(TrustedImm32(QV4::Value::Managed_Type_Internal_32), destAddr); - if (WriteBarrier::isRequired<WriteBarrier::Object>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, destAddr); - } - - static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister) - { - as->and32(TrustedImm32(Value::NotDouble_Mask), tagOrValueRegister); - return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister, - TrustedImm32(Value::NotDouble_Mask)); - } - - static void initializeLocalVariables(JITAssembler *as, int localsCount) - { - as->move(TrustedImm32(0), TargetPlatform::ReturnValueRegister); - as->move(TrustedImm32(localsCount), TargetPlatform::ScratchRegister); - Label loop = as->label(); - as->store32(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister)); - as->add32(TrustedImm32(4), TargetPlatform::LocalsRegister); - as->store32(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister)); - as->add32(TrustedImm32(4), TargetPlatform::LocalsRegister); - Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister); - jump.linkTo(loop, as); - } - - static Jump checkIfTagRegisterIsDouble(JITAssembler *as, RegisterID tagRegister) - { - as->and32(TrustedImm32(Value::NotDouble_Mask), tagRegister); - Jump isNoDbl = as->branch32(RelationalCondition::Equal, tagRegister, TrustedImm32(Value::NotDouble_Mask)); - return isNoDbl; - } -}; - -template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform> -struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatform, 8> -{ - using RegisterID = typename JITAssembler::RegisterID; - using FPRegisterID = typename JITAssembler::FPRegisterID; - using Address = typename JITAssembler::Address; - using TrustedImm32 = typename JITAssembler::TrustedImm32; - using TrustedImm64 = typename JITAssembler::TrustedImm64; - using Pointer = typename JITAssembler::Pointer; - using RelationalCondition = typename JITAssembler::RelationalCondition; - using ResultCondition = typename JITAssembler::ResultCondition; - using BranchTruncateType = typename JITAssembler::BranchTruncateType; - using Jump = typename JITAssembler::Jump; - using Label = typename JITAssembler::Label; - using ValueTypeInternal = Value::ValueTypeInternal_64; - using TargetPrimitive = TargetPrimitive64; - - static void emitSetGrayBit(JITAssembler *as, RegisterID base) - { - bool returnValueUsed = (base == TargetPlatform::ReturnValueRegister); - - as->push(TargetPlatform::EngineRegister); // free up one register for work - - RegisterID grayBitmap = returnValueUsed ? TargetPlatform::ScratchRegister : TargetPlatform::ReturnValueRegister; - as->move(base, grayBitmap); - Q_ASSERT(base != grayBitmap); - as->urshift64(TrustedImm32(Chunk::ChunkShift), grayBitmap); - as->lshift64(TrustedImm32(Chunk::ChunkShift), grayBitmap); - Q_STATIC_ASSERT(offsetof(Chunk, grayBitmap) == 0); - - RegisterID index = base; - as->move(base, index); - as->sub64(grayBitmap, index); - as->urshift64(TrustedImm32(Chunk::SlotSizeShift), index); - RegisterID grayIndex = TargetPlatform::EngineRegister; - as->move(index, grayIndex); - as->urshift64(TrustedImm32(Chunk::BitShift), grayIndex); - as->lshift64(TrustedImm32(3), grayIndex); // 8 bytes per quintptr - as->add64(grayIndex, grayBitmap); - as->and64(TrustedImm32(Chunk::Bits - 1), index); - - RegisterID bit = TargetPlatform::EngineRegister; - as->move(TrustedImm32(1), bit); - as->lshift64(index, bit); - - as->load64(Pointer(grayBitmap, 0), index); - as->or64(bit, index); - as->store64(index, Pointer(grayBitmap, 0)); - - as->pop(TargetPlatform::EngineRegister); - } - -#if WRITEBARRIER(none) - static Q_ALWAYS_INLINE void emitWriteBarrier(JITAssembler *, Address) {} -#endif - - static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest) - { - as->load64(addr, TargetPlatform::ReturnValueRegister); - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest); - } - - static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr, WriteBarrier::Type barrier) - { - as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister); - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - as->store64(TargetPlatform::ReturnValueRegister, addr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, addr); - } - - static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target) - { - as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister); - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - WriteBarrier::Type barrier; - Pointer ptr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); - as->store64(TargetPlatform::ReturnValueRegister, ptr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, ptr); - } - - static void storeReturnValue(JITAssembler *as, FPRegisterID dest) - { - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest); - } - - static void storeReturnValue(JITAssembler *as, const Pointer &dest, WriteBarrier::Type barrier) - { - as->store64(TargetPlatform::ReturnValueRegister, dest); - if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, dest); - } - - static void setFunctionReturnValueFromTemp(JITAssembler *as, IR::Temp *t) - { - if (t->kind == IR::Temp::PhysicalRegister) { - if (t->type == IR::DoubleType) { - as->moveDoubleTo64((FPRegisterID) t->index, - TargetPlatform::ReturnValueRegister); - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - } else if (t->type == IR::UInt32Type) { - RegisterID srcReg = (RegisterID) t->index; - Jump intRange = as->branch32(RelationalCondition::GreaterThanOrEqual, srcReg, TrustedImm32(0)); - as->convertUInt32ToDouble(srcReg, TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister); - as->moveDoubleTo64(TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister); - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - Jump done = as->jump(); - intRange.link(as); - as->zeroExtend32ToPtr(srcReg, TargetPlatform::ReturnValueRegister); - quint64 tag = quint64(QV4::Value::ValueTypeInternal_64::Integer); - as->or64(TrustedImm64(tag << 32), - TargetPlatform::ReturnValueRegister); - done.link(as); - } else { - as->zeroExtend32ToPtr((RegisterID) t->index, TargetPlatform::ReturnValueRegister); - quint64 tag; - switch (t->type) { - case IR::SInt32Type: - tag = quint64(QV4::Value::ValueTypeInternal_64::Integer); - break; - case IR::BoolType: - tag = quint64(QV4::Value::ValueTypeInternal_64::Boolean); - break; - default: - tag = 31337; // bogus value - Q_UNREACHABLE(); - } - as->or64(TrustedImm64(tag << 32), - TargetPlatform::ReturnValueRegister); - } - } else { - as->copyValue(TargetPlatform::ReturnValueRegister, t, WriteBarrier::NoBarrier); - } - } - - static void setFunctionReturnValueFromConst(JITAssembler *as, TargetPrimitive retVal) - { - as->move(TrustedImm64(retVal.rawValue()), TargetPlatform::ReturnValueRegister); - } - - static void storeValue(JITAssembler *as, TargetPrimitive value, Address destination, WriteBarrier::Type barrier) - { - as->store64(TrustedImm64(value.rawValue()), destination); - if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, destination); - } - - template <typename Source, typename Destination> - static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination, WriteBarrier::Type barrier) - { - // Use ReturnValueRegister as "scratch" register because loadArgument - // and storeArgument are functions that may need a scratch register themselves. - loadArgumentInRegister(as, source, TargetPlatform::ReturnValueRegister, 0); - as->storeReturnValue(destination, barrier); - } - - static void loadDoubleConstant(JITAssembler *as, IR::Const *c, FPRegisterID target) - { - Q_STATIC_ASSERT(sizeof(int64_t) == sizeof(double)); - int64_t i; - memcpy(&i, &c->value, sizeof(double)); - as->move(TrustedImm64(i), TargetPlatform::ReturnValueRegister); - as->move64ToDouble(TargetPlatform::ReturnValueRegister, target); - } - - static void loadArgumentInRegister(JITAssembler *as, Address addressOfValue, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - as->load64(addressOfValue, dest); - } - - static void loadArgumentInRegister(JITAssembler *as, IR::Temp* temp, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - if (temp) { - Pointer addr = as->loadTempAddress(temp); - as->load64(addr, dest); - } else { - auto undefined = TargetPrimitive::undefinedValue(); - as->move(TrustedImm64(undefined.rawValue()), dest); - } - } - - static void loadArgumentInRegister(JITAssembler *as, IR::ArgLocal* al, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - if (al) { - Pointer addr = as->loadArgLocalAddressForReading(dest, al); - as->load64(addr, dest); - } else { - auto undefined = TargetPrimitive::undefinedValue(); - as->move(TrustedImm64(undefined.rawValue()), dest); - } - } - - static void loadArgumentInRegister(JITAssembler *as, IR::Const* c, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - auto v = convertToValue<TargetPrimitive64>(c); - as->move(TrustedImm64(v.rawValue()), dest); - } - - static void loadArgumentInRegister(JITAssembler *as, IR::Expr* expr, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - if (!expr) { - auto undefined = TargetPrimitive::undefinedValue(); - as->move(TrustedImm64(undefined.rawValue()), dest); - } else if (IR::Temp *t = expr->asTemp()){ - loadArgumentInRegister(as, t, dest, argumentNumber); - } else if (IR::ArgLocal *al = expr->asArgLocal()) { - loadArgumentInRegister(as, al, dest, argumentNumber); - } else if (IR::Const *c = expr->asConst()) { - loadArgumentInRegister(as, c, dest, argumentNumber); - } else { - Q_ASSERT(!"unimplemented expression type in loadArgument"); - } - } - - static void zeroRegister(JITAssembler *as, RegisterID reg) - { - as->move(TrustedImm64(0), reg); - } - - static void zeroStackSlot(JITAssembler *as, int slot) - { - as->store64(TrustedImm64(0), as->addressForPoke(slot)); - } - - static void generateCJumpOnCompare(JITAssembler *as, - RelationalCondition cond, - RegisterID left, - TrustedImm64 right, - IR::BasicBlock *nextBlock, - IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) - { - if (trueBlock == nextBlock) { - Jump target = as->branch64(as->invert(cond), left, right); - as->addPatch(falseBlock, target); - } else { - Jump target = as->branch64(cond, left, right); - as->addPatch(trueBlock, target); - as->jumpToBlock(currentBlock, falseBlock); - } - } - - static void generateCJumpOnUndefined(JITAssembler *as, - RelationalCondition cond, IR::Expr *right, - RegisterID scratchRegister, RegisterID tagRegister, - IR::BasicBlock *nextBlock, IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) - { - Pointer addr = as->loadAddressForReading(scratchRegister, right); - as->load64(addr, tagRegister); - const TrustedImm64 tag(0); - generateCJumpOnCompare(as, cond, tagRegister, tag, nextBlock, currentBlock, trueBlock, falseBlock); - } - - static void convertVarToSInt32(JITAssembler *as, IR::Expr *source, IR::Expr *target) - { - Q_ASSERT(source->type == IR::VarType); - Pointer addr = as->loadAddressForReading(TargetPlatform::ScratchRegister, source); - as->load64(addr, TargetPlatform::ScratchRegister); - as->move(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); - - // check if it's integer convertible - as->urshift64(TrustedImm32(QV4::Value::IsIntegerConvertible_Shift), TargetPlatform::ScratchRegister); - Jump isIntConvertible = as->branch32(RelationalCondition::Equal, TargetPlatform::ScratchRegister, TrustedImm32(3)); - - // nope, not integer convertible, so check for a double: - as->urshift64(TrustedImm32( - QV4::Value::IsDoubleTag_Shift - QV4::Value::IsIntegerConvertible_Shift), - TargetPlatform::ScratchRegister); - Jump fallback = as->branch32(RelationalCondition::GreaterThan, TargetPlatform::ScratchRegister, TrustedImm32(0)); - - // it's a double - as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - as->move64ToDouble(TargetPlatform::ReturnValueRegister, TargetPlatform::FPGpr0); - Jump success = - as->branchTruncateDoubleToInt32(TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister, - BranchTruncateType::BranchIfTruncateSuccessful); - - // not an int: - fallback.link(as); - generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt, - as->loadAddressForReading(TargetPlatform::ScratchRegister, source)); - - - isIntConvertible.link(as); - success.link(as); - IR::Temp *targetTemp = target->asTemp(); - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - WriteBarrier::Type barrier; - Pointer targetAddr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); - as->store32(TargetPlatform::ReturnValueRegister, targetAddr); - targetAddr.offset += 4; - as->store32(TrustedImm32(quint32(Value::ValueTypeInternal_64::Integer)), targetAddr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, targetAddr); - } else { - as->storeInt32(TargetPlatform::ReturnValueRegister, target); - } - } - - static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr, WriteBarrier::Type barrier) - { - as->store64(registerWithPtr, destAddr); - if (WriteBarrier::isRequired<WriteBarrier::Object>() && barrier == WriteBarrier::Barrier) - emitWriteBarrier(as, destAddr); - } - - static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister) - { - as->rshift32(TrustedImm32(Value::IsDoubleTag_Shift), tagOrValueRegister); - return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister, - TrustedImm32(0)); - } - - static void initializeLocalVariables(JITAssembler *as, int localsCount) - { - as->move(TrustedImm64(0), TargetPlatform::ReturnValueRegister); - as->move(TrustedImm32(localsCount), TargetPlatform::ScratchRegister); - Label loop = as->label(); - as->store64(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister)); - as->add64(TrustedImm32(8), TargetPlatform::LocalsRegister); - Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister); - jump.linkTo(loop, as); - } - - static Jump checkIfTagRegisterIsDouble(JITAssembler *as, RegisterID tagRegister) - { - as->rshift32(TrustedImm32(Value::IsDoubleTag_Shift), tagRegister); - Jump isNoDbl = as->branch32(RelationalCondition::Equal, tagRegister, TrustedImm32(0)); - return isNoDbl; - } -}; - -template <typename TargetConfiguration> -class Assembler : public TargetConfiguration::MacroAssembler, public TargetConfiguration::Platform -{ - Q_DISABLE_COPY(Assembler) - -public: - Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator); - - using MacroAssembler = typename TargetConfiguration::MacroAssembler; - using RegisterID = typename MacroAssembler::RegisterID; - using FPRegisterID = typename MacroAssembler::FPRegisterID; - using Address = typename MacroAssembler::Address; - using Label = typename MacroAssembler::Label; - using Jump = typename MacroAssembler::Jump; - using DataLabelPtr = typename MacroAssembler::DataLabelPtr; - using TrustedImm32 = typename MacroAssembler::TrustedImm32; - using TrustedImm64 = typename MacroAssembler::TrustedImm64; - using TrustedImmPtr = typename MacroAssembler::TrustedImmPtr; - using RelationalCondition = typename MacroAssembler::RelationalCondition; - using typename MacroAssembler::DoubleCondition; - using MacroAssembler::label; - using MacroAssembler::move; - using MacroAssembler::jump; - using MacroAssembler::add32; - using MacroAssembler::and32; - using MacroAssembler::store32; - using MacroAssembler::loadPtr; - using MacroAssembler::load32; - using MacroAssembler::branch32; - using MacroAssembler::subDouble; - using MacroAssembler::subPtr; - using MacroAssembler::addPtr; - using MacroAssembler::call; - using MacroAssembler::poke; - using MacroAssembler::branchTruncateDoubleToUint32; - using MacroAssembler::or32; - using MacroAssembler::moveDouble; - using MacroAssembler::convertUInt32ToDouble; - using MacroAssembler::invert; - using MacroAssembler::convertInt32ToDouble; - using MacroAssembler::rshift32; - using MacroAssembler::storePtr; - using MacroAssembler::ret; - - using JITTargetPlatform = typename TargetConfiguration::Platform; - using JITTargetPlatform::RegisterArgumentCount; - using JITTargetPlatform::StackSpaceAllocatedUponFunctionEntry; - using JITTargetPlatform::RegisterSize; - using JITTargetPlatform::StackAlignment; - using JITTargetPlatform::ReturnValueRegister; - using JITTargetPlatform::StackPointerRegister; - using JITTargetPlatform::ScratchRegister; - using JITTargetPlatform::EngineRegister; - using JITTargetPlatform::StackShadowSpace; - using JITTargetPlatform::registerForArgument; - using JITTargetPlatform::FPGpr0; - using JITTargetPlatform::platformEnterStandardStackFrame; - using JITTargetPlatform::platformFinishEnteringStandardStackFrame; - using JITTargetPlatform::platformLeaveStandardStackFrame; - - static qint32 targetStructureOffset(qint32 hostOffset) - { - Q_ASSERT(hostOffset % QT_POINTER_SIZE == 0); - return (hostOffset * RegisterSize) / QT_POINTER_SIZE; - } - - struct LookupCall { - Address addr; - uint getterSetterOffset; - - LookupCall(const Address &addr, uint getterSetterOffset) - : addr(addr) - , getterSetterOffset(getterSetterOffset) - {} - }; - - struct RuntimeCall { - Address addr; - - inline RuntimeCall(Runtime::RuntimeMethods method = Runtime::InvalidRuntimeMethod); - bool isValid() const { return addr.offset >= 0; } - }; - - // Explicit type to allow distinguishing between - // pushing an address itself or the value it points - // to onto the stack when calling functions. - struct Pointer : public Address - { - explicit Pointer(const Address& addr) - : Address(addr) - {} - explicit Pointer(RegisterID reg, int32_t offset) - : Address(reg, offset) - {} - }; - - using RegisterSizeDependentOps = RegisterSizeDependentAssembler<Assembler<TargetConfiguration>, MacroAssembler, JITTargetPlatform, RegisterSize>; - using ValueTypeInternal = typename RegisterSizeDependentOps::ValueTypeInternal; - using TargetPrimitive = typename RegisterSizeDependentOps::TargetPrimitive; - - // V4 uses two stacks: one stack with QV4::Value items, which is checked by the garbage - // collector, and one stack used by the native C/C++/ABI code. This C++ stack is not scanned - // by the garbage collector, so if any JS object needs to be retained, it should be put on the - // JS stack. - // - // The "saved reg arg X" are on the C++ stack is used to store values in registers that need to - // be passed by reference to native functions. It is fine to use the C++ stack, because only - // non-object values can be stored in registers. - // - // Stack layout for the C++ stack: - // return address - // old FP <- FP - // callee saved reg n - // ... - // callee saved reg 0 - // saved reg arg n - // ... - // saved reg arg 0 <- SP - // - // Stack layout for the JS stack: - // function call argument n <- LocalsRegister - // ... - // function call argument 0 - // local 0 - // ... - // local n - class StackLayout - { - public: - StackLayout(IR::Function *function, int maxArgCountForBuiltins, int normalRegistersToSave, int fpRegistersToSave) - : normalRegistersToSave(normalRegistersToSave) - , fpRegistersToSave(fpRegistersToSave) - , maxOutgoingArgumentCount(function->maxNumberOfArguments) - , localCount(function->tempCount) - , savedRegCount(maxArgCountForBuiltins) - { -#if 0 // debug code - qDebug("calleeSavedRegCount.....: %d",calleeSavedRegCount); - qDebug("maxOutgoingArgumentCount: %d",maxOutgoingArgumentCount); - qDebug("localCount..............: %d",localCount); - qDebug("savedConstCount.........: %d",savedRegCount); - for (int i = 0; i < maxOutgoingArgumentCount; ++i) - qDebug("argumentAddressForCall(%d) = 0x%x / -0x%x", i, - argumentAddressForCall(i).offset, -argumentAddressForCall(i).offset); - for (int i = 0; i < localCount; ++i) - qDebug("local(%d) = 0x%x / -0x%x", i, stackSlotPointer(i).offset, - -stackSlotPointer(i).offset); - qDebug("savedReg(0) = 0x%x / -0x%x", savedRegPointer(0).offset, -savedRegPointer(0).offset); - qDebug("savedReg(1) = 0x%x / -0x%x", savedRegPointer(1).offset, -savedRegPointer(1).offset); - qDebug("savedReg(2) = 0x%x / -0x%x", savedRegPointer(2).offset, -savedRegPointer(2).offset); - qDebug("savedReg(3) = 0x%x / -0x%x", savedRegPointer(3).offset, -savedRegPointer(3).offset); - qDebug("savedReg(4) = 0x%x / -0x%x", savedRegPointer(4).offset, -savedRegPointer(4).offset); - qDebug("savedReg(5) = 0x%x / -0x%x", savedRegPointer(5).offset, -savedRegPointer(5).offset); - - qDebug("callDataAddress(0) = 0x%x", callDataAddress(0).offset); -#endif - } - - int calculateStackFrameSize() const - { - // sp was aligned before executing the call instruction. So, calculate all contents - // that were saved after that aligned stack...: - const int stackSpaceAllocatedOtherwise = StackSpaceAllocatedUponFunctionEntry - + RegisterSize; // saved FramePointerRegister - - // ... then calculate the stuff we want to store ...: - int frameSize = RegisterSize * normalRegistersToSave + sizeof(double) * fpRegistersToSave; - frameSize += savedRegCount * sizeof(QV4::Value); // (these get written out as Values, not as native registers) - - Q_ASSERT(frameSize + stackSpaceAllocatedOtherwise < INT_MAX); - // .. then align that chunk ..: - frameSize = static_cast<int>(WTF::roundUpToMultipleOf(StackAlignment, frameSize + stackSpaceAllocatedOtherwise)); - // ... which now holds our frame size + the extra stuff that was pushed due to the call. - // So subtract that extra stuff, and we have our frame size: - frameSize -= stackSpaceAllocatedOtherwise; - - return frameSize; - } - - /// \return the stack frame size in number of Value items. - int calculateJSStackFrameSize() const - { - return (localCount + sizeof(QV4::CallData)/sizeof(QV4::Value) - 1 + maxOutgoingArgumentCount) + 1; - } - - Address stackSlotPointer(int idx) const - { - Q_ASSERT(idx >= 0); - Q_ASSERT(idx < localCount); - - Pointer addr = callDataAddress(0); - addr.offset -= sizeof(QV4::Value) * (idx + 1); - return addr; - } - - // Some run-time functions take (Value* args, int argc). This function is for populating - // the args. - Pointer argumentAddressForCall(int argument) const - { - Q_ASSERT(argument >= 0); - Q_ASSERT(argument < maxOutgoingArgumentCount); - - const int index = maxOutgoingArgumentCount - argument; - return Pointer(Assembler::LocalsRegister, sizeof(QV4::Value) * (-index)); - } - - Pointer callDataAddress(int offset = 0) const { - return Pointer(Assembler::LocalsRegister, offset - (sizeof(QV4::CallData) + sizeof(QV4::Value) * (maxOutgoingArgumentCount - 1))); - } - - Address savedRegPointer(int offset) const - { - Q_ASSERT(offset >= 0); - Q_ASSERT(offset < savedRegCount); - - // Get the address of the bottom-most element of our frame: - Address ptr(Assembler::FramePointerRegister, -calculateStackFrameSize()); - // This now is the element with offset 0. So: - ptr.offset += offset * sizeof(QV4::Value); - // and we're done! - return ptr; - } - - private: - int normalRegistersToSave; - int fpRegistersToSave; - - /// arg count for calls to JS functions - int maxOutgoingArgumentCount; - - /// the number of spill slots needed by this function - int localCount; - - /// used by built-ins to save arguments (e.g. constants) to the stack when they need to be - /// passed by reference. - int savedRegCount; - }; - - struct VoidType { VoidType() {} }; - static const VoidType Void; - - typedef JSC::FunctionPtr FunctionPtr; - -#ifndef QT_NO_DEBUG - struct CallInfo { - Label label; - const char* functionName; - }; -#endif - struct PointerToValue { - PointerToValue(IR::Expr *value) - : value(value) - {} - IR::Expr *value; - }; - struct StringToIndex { - explicit StringToIndex(const QString &string) : string(string) {} - QString string; - }; - struct Reference { - Reference(IR::Expr *value) : value(value) { - Q_ASSERT(value->asTemp() || value->asArgLocal()); - } - IR::Expr *value; - }; - - void callAbsolute(const char* /*functionName*/, const LookupCall &lookupCall) - { - call(lookupCall.addr); - } - - void callAbsolute(const char *functionName, const RuntimeCall &runtimeCall) - { - call(runtimeCall.addr); -#ifndef QT_NO_DEBUG - // the code below is to get proper function names in the disassembly - CallInfo info; - info.functionName = functionName; - info.label = label(); - _callInfos.append(info); -#else - Q_UNUSED(functionName) -#endif - } - - void registerBlock(IR::BasicBlock*, IR::BasicBlock *nextBlock); - IR::BasicBlock *nextBlock() const { return _nextBlock; } - void jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target); - void addPatch(IR::BasicBlock* targetBlock, Jump targetJump); - void addPatch(DataLabelPtr patch, Label target); - void addPatch(DataLabelPtr patch, IR::BasicBlock *target); - void generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm32 right, - IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock); - void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right, - IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock); - void generateCJumpOnUndefined(RelationalCondition cond, IR::Expr *right, - RegisterID scratchRegister, RegisterID tagRegister, - IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) - { - RegisterSizeDependentOps::generateCJumpOnUndefined(this, cond, right, scratchRegister, tagRegister, - _nextBlock, currentBlock, trueBlock, falseBlock); - } - - Jump generateIsDoubleCheck(RegisterID tagOrValueRegister) - { - return RegisterSizeDependentOps::generateIsDoubleCheck(this, tagOrValueRegister); - } - - Jump genTryDoubleConversion(IR::Expr *src, FPRegisterID dest); - Jump branchDouble(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); - Jump branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); - - Pointer loadAddressForWriting(RegisterID tmp, IR::Expr *t, WriteBarrier::Type *barrier); - Pointer loadAddressForReading(RegisterID tmp, IR::Expr *t) { - return loadAddressForWriting(tmp, t, 0); - } - - Pointer loadTempAddress(IR::Temp *t); - Pointer loadArgLocalAddressForWriting(RegisterID baseReg, IR::ArgLocal *al, WriteBarrier::Type *barrier); - Pointer loadArgLocalAddressForReading(RegisterID baseReg, IR::ArgLocal *al) { - return loadArgLocalAddressForWriting(baseReg, al, 0); - } - Pointer loadStringAddress(RegisterID reg, const QString &string); - Address loadConstant(IR::Const *c, RegisterID baseReg); - Address loadConstant(const TargetPrimitive &v, RegisterID baseReg); - void loadStringRef(RegisterID reg, const QString &string); - Pointer stackSlotPointer(IR::Temp *t) const - { - Q_ASSERT(t->kind == IR::Temp::StackSlot); - - return Pointer(_stackLayout->stackSlotPointer(t->index)); - } - - template <int argumentNumber> - void saveOutRegister(PointerToValue arg) - { - if (!arg.value) - return; - if (IR::Temp *t = arg.value->asTemp()) { - if (t->kind == IR::Temp::PhysicalRegister) { - Pointer addr(_stackLayout->savedRegPointer(argumentNumber)); - switch (t->type) { - case IR::BoolType: - storeBool((RegisterID) t->index, addr, WriteBarrier::NoBarrier); - break; - case IR::SInt32Type: - storeInt32((RegisterID) t->index, addr, WriteBarrier::NoBarrier); - break; - case IR::UInt32Type: - storeUInt32((RegisterID) t->index, addr, WriteBarrier::NoBarrier); - break; - case IR::DoubleType: - storeDouble((FPRegisterID) t->index, addr, WriteBarrier::NoBarrier); - break; - default: - Q_UNIMPLEMENTED(); - } - } - } - } - - template <int, typename ArgType> - void saveOutRegister(ArgType) - {} - - void loadArgumentInRegister(RegisterID source, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - move(source, dest); - } - - void loadArgumentInRegister(const Pointer& ptr, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - addPtr(TrustedImm32(ptr.offset), ptr.base, dest); - } - - void loadArgumentInRegister(PointerToValue temp, RegisterID dest, int argumentNumber) - { - if (!temp.value) { - RegisterSizeDependentOps::zeroRegister(this, dest); - } else { - Pointer addr = toAddress(dest, temp.value, argumentNumber, 0); - loadArgumentInRegister(addr, dest, argumentNumber); - } - } - void loadArgumentInRegister(StringToIndex temp, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - loadStringRef(dest, temp.string); - } - - void loadArgumentInRegister(Reference temp, RegisterID dest, int argumentNumber) - { - Q_ASSERT(temp.value); - Pointer addr = loadAddressForReading(dest, temp.value); - loadArgumentInRegister(addr, dest, argumentNumber); - } - - void loadArgumentInRegister(IR::Temp* temp, RegisterID dest, int argumentNumber) - { - RegisterSizeDependentOps::loadArgumentInRegister(this, temp, dest, argumentNumber); - } - - void loadArgumentInRegister(IR::ArgLocal* al, RegisterID dest, int argumentNumber) - { - RegisterSizeDependentOps::loadArgumentInRegister(this, al, dest, argumentNumber); - } - - void loadArgumentInRegister(IR::Const* c, RegisterID dest, int argumentNumber) - { - RegisterSizeDependentOps::loadArgumentInRegister(this, c, dest, argumentNumber); - } - - void loadArgumentInRegister(IR::Expr* expr, RegisterID dest, int argumentNumber) - { - RegisterSizeDependentOps::loadArgumentInRegister(this, expr, dest, argumentNumber); - } - - void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - RegisterSizeDependentOps::zeroRegister(this, dest); - if (imm32.m_value) - move(imm32, dest); - } - - void storeReturnValue(RegisterID dest, WriteBarrier::Type barrier = WriteBarrier::NoBarrier) - { - Q_UNUSED(barrier); - Q_ASSERT(barrier == WriteBarrier::NoBarrier); - move(ReturnValueRegister, dest); - } - - void storeUInt32ReturnValue(RegisterID dest) - { - subPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); - Pointer tmp(StackPointerRegister, 0); - storeReturnValue(tmp, WriteBarrier::NoBarrier); - toUInt32Register(tmp, dest); - addPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); - } - - void storeReturnValue(FPRegisterID dest) - { - RegisterSizeDependentOps::storeReturnValue(this, dest); - } - - void storeReturnValue(const Pointer &dest, WriteBarrier::Type barrier) - { - RegisterSizeDependentOps::storeReturnValue(this, dest, barrier); - } - - void storeReturnValue(IR::Expr *target) - { - if (!target) - return; - - IR::Temp *temp = target->asTemp(); - if (temp && temp->kind == IR::Temp::PhysicalRegister) { - if (temp->type == IR::DoubleType) - storeReturnValue((FPRegisterID) temp->index); - else if (temp->type == IR::UInt32Type) - storeUInt32ReturnValue((RegisterID) temp->index); - else - storeReturnValue((RegisterID) temp->index); - return; - } else { - WriteBarrier::Type barrier; - Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier); - storeReturnValue(addr, barrier); - } - } - - void storeReturnValue(VoidType) - { - } - - template <int StackSlot> - void loadArgumentOnStack(RegisterID reg, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - poke(reg, StackSlot); - } - - template <int StackSlot> - void loadArgumentOnStack(TrustedImm32 value, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - poke(value, StackSlot); - } - - template <int StackSlot> - void loadArgumentOnStack(const Pointer& ptr, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister); - poke(ScratchRegister, StackSlot); - } - - template <int StackSlot> - void loadArgumentOnStack(PointerToValue temp, int argumentNumber) - { - if (temp.value) { - Pointer ptr = toAddress(ScratchRegister, temp.value, argumentNumber, 0); - loadArgumentOnStack<StackSlot>(ptr, argumentNumber); - } else { - RegisterSizeDependentOps::zeroStackSlot(this, StackSlot); - } - } - - template <int StackSlot> - void loadArgumentOnStack(StringToIndex temp, int argumentNumber) - { - Q_UNUSED(argumentNumber); - loadStringRef(ScratchRegister, temp.string); - poke(ScratchRegister, StackSlot); - } - - template <int StackSlot> - void loadArgumentOnStack(Reference temp, int argumentNumber) - { - Q_ASSERT (temp.value); - - Pointer ptr = loadAddressForReading(ScratchRegister, temp.value); - loadArgumentOnStack<StackSlot>(ptr, argumentNumber); - } - - void loadDouble(IR::Expr *source, FPRegisterID dest) - { - IR::Temp *sourceTemp = source->asTemp(); - if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) { - moveDouble((FPRegisterID) sourceTemp->index, dest); - return; - } - Pointer ptr = loadAddressForReading(ScratchRegister, source); - loadDouble(ptr, dest); - } - - void storeDouble(FPRegisterID source, IR::Expr* target) - { - IR::Temp *targetTemp = target->asTemp(); - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - moveDouble(source, (FPRegisterID) targetTemp->index); - return; - } - RegisterSizeDependentOps::storeDouble(this, source, target); - } - - void loadDouble(Address addr, FPRegisterID dest) - { - RegisterSizeDependentOps::loadDouble(this, addr, dest); - } - - void storeDouble(FPRegisterID source, Address addr, WriteBarrier::Type barrier) - { - RegisterSizeDependentOps::storeDouble(this, source, addr, barrier); - } - - template <typename Result, typename Source> - void copyValue(Result result, Source source, WriteBarrier::Type barrier); - template <typename Result> - void copyValue(Result result, IR::Expr* source, WriteBarrier::Type barrier); - - // The scratch register is used to calculate the temp address for the source. - void memcopyValue(Pointer target, IR::Expr *source, RegisterID scratchRegister, WriteBarrier::Type barrier) - { - Q_ASSERT(!source->asTemp() || source->asTemp()->kind != IR::Temp::PhysicalRegister); - Q_ASSERT(target.base != scratchRegister); - loadRawValue(loadAddressForReading(scratchRegister, source), FPGpr0); - storeRawValue(FPGpr0, target, barrier); - } - - // The scratch register is used to calculate the temp address for the source. - void memcopyValue(IR::Expr *target, Pointer source, FPRegisterID fpScratchRegister, RegisterID scratchRegister) - { - loadRawValue(source, fpScratchRegister); - WriteBarrier::Type barrier; - Pointer dest = loadAddressForWriting(scratchRegister, target, &barrier); - storeRawValue(fpScratchRegister, dest, barrier); - } - - void loadRawValue(Pointer source, FPRegisterID dest) - { - TargetConfiguration::MacroAssembler::loadDouble(source, dest); - } - - void storeRawValue(FPRegisterID source, Pointer dest, WriteBarrier::Type barrier) - { - TargetConfiguration::MacroAssembler::storeDouble(source, dest); - if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) - RegisterSizeDependentOps::emitWriteBarrier(this, dest); - } - - void storeValue(TargetPrimitive value, Address destination, WriteBarrier::Type barrier) - { - RegisterSizeDependentOps::storeValue(this, value, destination, barrier); - } - - void storeValue(TargetPrimitive value, IR::Expr* temp); - - void emitWriteBarrier(Address addr, WriteBarrier::Type barrier) { - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - RegisterSizeDependentOps::emitWriteBarrier(this, addr); - } - - void enterStandardStackFrame(const RegisterInformation ®ularRegistersToSave, - const RegisterInformation &fpRegistersToSave); - void leaveStandardStackFrame(const RegisterInformation ®ularRegistersToSave, - const RegisterInformation &fpRegistersToSave); - - void checkException() { - this->load8(Address(EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, hasException))), ScratchRegister); - Jump exceptionThrown = branch32(RelationalCondition::NotEqual, ScratchRegister, TrustedImm32(0)); - if (catchBlock) - addPatch(catchBlock, exceptionThrown); - else - exceptionPropagationJumps.append(exceptionThrown); - } - void jumpToExceptionHandler() { - Jump exceptionThrown = jump(); - if (catchBlock) - addPatch(catchBlock, exceptionThrown); - else - exceptionPropagationJumps.append(exceptionThrown); - } - - template <int argumentNumber, typename T> - void loadArgumentOnStackOrRegister(const T &value) - { - if (argumentNumber < RegisterArgumentCount) - loadArgumentInRegister(value, registerForArgument(argumentNumber), argumentNumber); - else - loadArgumentOnStack<argumentNumber - RegisterArgumentCount + (StackShadowSpace / RegisterSize)>(value, argumentNumber); - } - - template <int argumentNumber> - void loadArgumentOnStackOrRegister(const VoidType &value) - { - Q_UNUSED(value); - } - - template <bool selectFirst, int First, int Second> - struct Select - { - enum { Chosen = First }; - }; - - template <int First, int Second> - struct Select<false, First, Second> - { - enum { Chosen = Second }; - }; - - template <int ArgumentIndex, typename Parameter> - struct SizeOnStack - { - enum { Size = Select<ArgumentIndex >= RegisterArgumentCount, RegisterSize, 0>::Chosen }; - }; - - template <int ArgumentIndex> - struct SizeOnStack<ArgumentIndex, VoidType> - { - enum { Size = 0 }; - }; - - template <typename T> bool prepareCall(T &) - { return true; } - - bool prepareCall(LookupCall &lookupCall) - { - // IMPORTANT! See generateLookupCall in qv4isel_masm_p.h for details! - - // load the table from the context - loadPtr(Address(EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), ScratchRegister); - loadPtr(Address(ScratchRegister, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, lookups))), - lookupCall.addr.base); - // pre-calculate the indirect address for the lookupCall table: - if (lookupCall.addr.offset) - addPtr(TrustedImm32(lookupCall.addr.offset), lookupCall.addr.base); - // store it as the first argument - loadArgumentOnStackOrRegister<0>(lookupCall.addr.base); - // set the destination addresses offset to the getterSetterOffset. The base is the lookupCall table's address - lookupCall.addr.offset = lookupCall.getterSetterOffset; - return false; - } - - template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> - void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) - { - int stackSpaceNeeded = SizeOnStack<0, Arg1>::Size - + SizeOnStack<1, Arg2>::Size - + SizeOnStack<2, Arg3>::Size - + SizeOnStack<3, Arg4>::Size - + SizeOnStack<4, Arg5>::Size - + SizeOnStack<5, Arg6>::Size - + StackShadowSpace; - - if (stackSpaceNeeded) { - Q_ASSERT(stackSpaceNeeded < (INT_MAX - StackAlignment)); - stackSpaceNeeded = static_cast<int>(WTF::roundUpToMultipleOf(StackAlignment, stackSpaceNeeded)); - subPtr(TrustedImm32(stackSpaceNeeded), StackPointerRegister); - } - - // First save any arguments that reside in registers, because they could be overwritten - // if that register is also used to pass arguments. - saveOutRegister<5>(arg6); - saveOutRegister<4>(arg5); - saveOutRegister<3>(arg4); - saveOutRegister<2>(arg3); - saveOutRegister<1>(arg2); - saveOutRegister<0>(arg1); - - loadArgumentOnStackOrRegister<5>(arg6); - loadArgumentOnStackOrRegister<4>(arg5); - loadArgumentOnStackOrRegister<3>(arg4); - loadArgumentOnStackOrRegister<2>(arg3); - loadArgumentOnStackOrRegister<1>(arg2); - - if (prepareCall(function)) - loadArgumentOnStackOrRegister<0>(arg1); - - if (JITTargetPlatform::gotRegister != -1) - load32(Address(JITTargetPlatform::FramePointerRegister, JITTargetPlatform::savedGOTRegisterSlotOnStack()), static_cast<RegisterID>(JITTargetPlatform::gotRegister)); // restore the GOT ptr - - callAbsolute(functionName, function); - - if (stackSpaceNeeded) - addPtr(TrustedImm32(stackSpaceNeeded), StackPointerRegister); - - if (needsExceptionCheck) { - checkException(); - } - - storeReturnValue(r); - - } - - template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> - void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) - { - generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); - } - - template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4> - void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, arg4, VoidType(), VoidType()); - } - - template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3> - void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3) - { - generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType(), VoidType()); - } - - template <typename ArgRet, typename Callable, typename Arg1, typename Arg2> - void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2) - { - generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType(), VoidType()); - } - - template <typename ArgRet, typename Callable, typename Arg1> - void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1) - { - generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType(), VoidType()); - } - - Pointer toAddress(RegisterID tmpReg, IR::Expr *e, int offset, WriteBarrier::Type *barrier) - { - if (barrier) - *barrier = WriteBarrier::NoBarrier; - if (IR::Const *c = e->asConst()) { - Address addr = _stackLayout->savedRegPointer(offset); - Address tagAddr = addr; - tagAddr.offset += 4; - - auto v = convertToValue<TargetPrimitive>(c); - store32(TrustedImm32(v.value()), addr); - store32(TrustedImm32(v.tag()), tagAddr); - return Pointer(addr); - } - - if (IR::Temp *t = e->asTemp()) - if (t->kind == IR::Temp::PhysicalRegister) - return Pointer(_stackLayout->savedRegPointer(offset)); - - return loadAddressForWriting(tmpReg, e, barrier); - } - - void storeBool(RegisterID reg, Pointer addr, WriteBarrier::Type barrier) - { - store32(reg, addr); - addr.offset += 4; - store32(TrustedImm32(TargetPrimitive::fromBoolean(0).tag()), addr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - RegisterSizeDependentOps::emitWriteBarrier(this, addr); - } - - void storeBool(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - void storeBool(RegisterID reg, IR::Expr *target) - { - if (IR::Temp *targetTemp = target->asTemp()) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { - move(reg, (RegisterID) targetTemp->index); - return; - } - } - - WriteBarrier::Type barrier; - Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier); - storeBool(reg, addr, barrier); - } - - void storeBool(bool value, IR::Expr *target) { - TrustedImm32 trustedValue(value ? 1 : 0); - - if (IR::Temp *targetTemp = target->asTemp()) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { - move(trustedValue, (RegisterID) targetTemp->index); - return; - } - } - - move(trustedValue, ScratchRegister); - storeBool(ScratchRegister, target); - } - - void storeInt32(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - void storeInt32(RegisterID reg, Pointer addr, WriteBarrier::Type barrier) - { - store32(reg, addr); - addr.offset += 4; - store32(TrustedImm32(TargetPrimitive::fromInt32(0).tag()), addr); - if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) - RegisterSizeDependentOps::emitWriteBarrier(this, addr); - } - - void storeInt32(RegisterID reg, IR::Expr *target) - { - IR::Temp *targetTemp = target->asTemp(); - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - move(reg, (RegisterID) targetTemp->index); - } else { - WriteBarrier::Type barrier; - Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier); - storeInt32(reg, addr, barrier); - } - } - - void storeUInt32(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - void storeUInt32(RegisterID reg, Pointer addr, WriteBarrier::Type barrier) - { - // The UInt32 representation in QV4::Value is really convoluted. See also toUInt32Register. - Jump intRange = branch32(RelationalCondition::GreaterThanOrEqual, reg, TrustedImm32(0)); - convertUInt32ToDouble(reg, FPGpr0, ReturnValueRegister); - storeDouble(FPGpr0, addr, barrier); - Jump done = jump(); - intRange.link(this); - storeInt32(reg, addr, barrier); - done.link(this); - } - - void storeUInt32(RegisterID reg, IR::Expr *target) - { - IR::Temp *targetTemp = target->asTemp(); - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - move(reg, (RegisterID) targetTemp->index); - } else { - WriteBarrier::Type barrier; - Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier); - storeUInt32(reg, addr, barrier); - } - } - - FPRegisterID toDoubleRegister(IR::Expr *e, FPRegisterID target = FPGpr0) - { - if (IR::Const *c = e->asConst()) { - RegisterSizeDependentOps::loadDoubleConstant(this, c, target); - return target; - } - - if (IR::Temp *t = e->asTemp()) - if (t->kind == IR::Temp::PhysicalRegister) - return (FPRegisterID) t->index; - - loadDouble(e, target); - return target; - } - - RegisterID toBoolRegister(IR::Expr *e, RegisterID scratchReg) - { - return toInt32Register(e, scratchReg); - } - - RegisterID toInt32Register(IR::Expr *e, RegisterID scratchReg) - { - if (IR::Const *c = e->asConst()) { - move(TrustedImm32(convertToValue<Primitive>(c).int_32()), scratchReg); - return scratchReg; - } - - if (IR::Temp *t = e->asTemp()) - if (t->kind == IR::Temp::PhysicalRegister) - return (RegisterID) t->index; - - return toInt32Register(loadAddressForReading(scratchReg, e), scratchReg); - } - - RegisterID toInt32Register(Pointer addr, RegisterID scratchReg) - { - load32(addr, scratchReg); - return scratchReg; - } - - RegisterID toUInt32Register(IR::Expr *e, RegisterID scratchReg) - { - if (IR::Const *c = e->asConst()) { - move(TrustedImm32(unsigned(c->value)), scratchReg); - return scratchReg; - } - - if (IR::Temp *t = e->asTemp()) - if (t->kind == IR::Temp::PhysicalRegister) - return (RegisterID) t->index; - - return toUInt32Register(loadAddressForReading(scratchReg, e), scratchReg); - } - - RegisterID toUInt32Register(Pointer addr, RegisterID scratchReg) - { - Q_ASSERT(addr.base != scratchReg); - - // The UInt32 representation in QV4::Value is really convoluted. See also storeUInt32. - Pointer tagAddr = addr; - tagAddr.offset += 4; - load32(tagAddr, scratchReg); - Jump inIntRange = branch32(RelationalCondition::Equal, scratchReg, TrustedImm32(quint32(ValueTypeInternal::Integer))); - - // it's not in signed int range, so load it as a double, and truncate it down - loadDouble(addr, FPGpr0); - Address inversionAddress = loadConstant(TargetPrimitive::fromDouble(double(INT_MAX) + 1), scratchReg); - subDouble(inversionAddress, FPGpr0); - Jump canNeverHappen = branchTruncateDoubleToUint32(FPGpr0, scratchReg); - canNeverHappen.link(this); - or32(TrustedImm32(1 << 31), scratchReg); - Jump done = jump(); - - inIntRange.link(this); - load32(addr, scratchReg); - - done.link(this); - return scratchReg; - } - - void returnFromFunction(IR::Ret *s, RegisterInformation regularRegistersToSave, RegisterInformation fpRegistersToSave); - - JSC::MacroAssemblerCodeRef link(int *codeSize); - - void setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave); - const StackLayout &stackLayout() const { return *_stackLayout.data(); } - void initializeLocalVariables() - { - const int locals = _stackLayout->calculateJSStackFrameSize(); - if (locals <= 0) - return; - loadPtr(Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop))), JITTargetPlatform::LocalsRegister); - RegisterSizeDependentOps::initializeLocalVariables(this, locals); - storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop)))); - } - - Label exceptionReturnLabel; - IR::BasicBlock * catchBlock; - QVector<Jump> exceptionPropagationJumps; -private: - QScopedPointer<const StackLayout> _stackLayout; - IR::Function *_function; - std::vector<Label> _addrs; - std::vector<std::vector<Jump>> _patches; -#ifndef QT_NO_DEBUG - QVector<CallInfo> _callInfos; -#endif - - struct DataLabelPatch { - DataLabelPtr dataLabel; - Label target; - }; - std::vector<DataLabelPatch> _dataLabelPatches; - - std::vector<std::vector<DataLabelPtr>> _labelPatches; - IR::BasicBlock *_nextBlock; - - QV4::ExecutableAllocator *_executableAllocator; - QV4::Compiler::JSUnitGenerator *_jsGenerator; -}; - -template <typename TargetConfiguration> -const typename Assembler<TargetConfiguration>::VoidType Assembler<TargetConfiguration>::Void; - -template <typename TargetConfiguration> -template <typename Result, typename Source> -void Assembler<TargetConfiguration>::copyValue(Result result, Source source, WriteBarrier::Type barrier) -{ - RegisterSizeDependentOps::copyValueViaRegisters(this, source, result, barrier); -} - -template <typename TargetConfiguration> -template <typename Result> -void Assembler<TargetConfiguration>::copyValue(Result result, IR::Expr* source, WriteBarrier::Type barrier) -{ - if (source->type == IR::BoolType) { - RegisterID reg = toInt32Register(source, ScratchRegister); - storeBool(reg, result, barrier); - } else if (source->type == IR::SInt32Type) { - RegisterID reg = toInt32Register(source, ScratchRegister); - storeInt32(reg, result, barrier); - } else if (source->type == IR::UInt32Type) { - RegisterID reg = toUInt32Register(source, ScratchRegister); - storeUInt32(reg, result, barrier); - } else if (source->type == IR::DoubleType) { - storeDouble(toDoubleRegister(source), result, barrier); - } else if (source->asTemp() || source->asArgLocal()) { - RegisterSizeDependentOps::copyValueViaRegisters(this, source, result, barrier); - } else if (IR::Const *c = source->asConst()) { - auto v = convertToValue<TargetPrimitive>(c); - storeValue(v, result, barrier); - } else { - Q_UNREACHABLE(); - } -} - -template <typename TargetConfiguration> -inline Assembler<TargetConfiguration>::RuntimeCall::RuntimeCall(Runtime::RuntimeMethods method) - : addr(Assembler::EngineRegister, - method == Runtime::InvalidRuntimeMethod ? -1 : (Assembler<TargetConfiguration>::targetStructureOffset(offsetof(EngineBase, runtime) + Runtime::runtimeMethodOffset(method)))) -{ -} - -} // end of namespace JIT -} // end of namespace QV4 - -QT_END_NAMESPACE - -#endif // ENABLE(ASSEMBLER) - -#endif // QV4ISEL_MASM_P_H diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp deleted file mode 100644 index a1c65f644c..0000000000 --- a/src/qml/jit/qv4binop.cpp +++ /dev/null @@ -1,665 +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 <qv4binop_p.h> -#include <qv4assembler_p.h> - -#if ENABLE(ASSEMBLER) - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace JIT { - -template <typename JITAssembler> -struct ArchitectureSpecificBinaryOperation -{ - using FPRegisterID = typename JITAssembler::FPRegisterID; - - static bool doubleAdd(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - Q_UNUSED(as); - Q_UNUSED(lhs); - Q_UNUSED(rhs); - Q_UNUSED(targetReg); - return false; - } - static bool doubleMul(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - Q_UNUSED(as); - Q_UNUSED(lhs); - Q_UNUSED(rhs); - Q_UNUSED(targetReg); - return false; - } - static bool doubleSub(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - Q_UNUSED(as); - Q_UNUSED(lhs); - Q_UNUSED(rhs); - Q_UNUSED(targetReg); - return false; - } - static bool doubleDiv(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - Q_UNUSED(as); - Q_UNUSED(lhs); - Q_UNUSED(rhs); - Q_UNUSED(targetReg); - return false; - } -}; - -#if CPU(X86) -template <> -struct ArchitectureSpecificBinaryOperation<Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>>> -{ - using JITAssembler = Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>>; - using FPRegisterID = JITAssembler::FPRegisterID; - using Address = JITAssembler::Address; - - static bool doubleAdd(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); - as->addDouble(addr, targetReg); - return true; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X + [temp-memory-address] -> Y = X; Y += [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->addDouble(as->loadTempAddress(t), targetReg); - return true; - } - } - return false; - } - static bool doubleMul(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); - as->mulDouble(addr, targetReg); - return true; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X * [temp-memory-address] -> Y = X; Y *= [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->mulDouble(as->loadTempAddress(t), targetReg); - return true; - } - } - return false; - } - static bool doubleSub(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); - as->subDouble(addr, targetReg); - return true; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X - [temp-memory-address] -> Y = X; Y -= [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->subDouble(as->loadTempAddress(t), targetReg); - return true; - } - } - return false; - } - static bool doubleDiv(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) - { - if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); - as->divDouble(addr, targetReg); - return true; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X / [temp-memory-address] -> Y = X; Y /= [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->divDouble(as->loadTempAddress(t), targetReg); - return true; - } - } - return false; - } -}; -#endif - -#define OP(op) \ - { "Runtime::" isel_stringIfy(op), QV4::Runtime::op, QV4::Runtime::InvalidRuntimeMethod, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } -#define OPCONTEXT(op) \ - { "Runtime::" isel_stringIfy(op), QV4::Runtime::InvalidRuntimeMethod, QV4::Runtime::op, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } - -#define INLINE_OP(op, memOp, immOp) \ - { "Runtime::" isel_stringIfy(op), QV4::Runtime::op, QV4::Runtime::InvalidRuntimeMethod, memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck } -#define INLINE_OPCONTEXT(op, memOp, immOp) \ - { "Runtime::" isel_stringIfy(op), QV4::Runtime::InvalidRuntimeMethod, QV4::Runtime::op, memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck } - -#define NULL_OP \ - { 0, QV4::Runtime::InvalidRuntimeMethod, QV4::Runtime::InvalidRuntimeMethod, 0, 0, false } - -template <typename JITAssembler> -const typename Binop<JITAssembler>::OpInfo Binop<JITAssembler>::operations[IR::LastAluOp + 1] = { - NULL_OP, // OpInvalid - NULL_OP, // OpIfTrue - NULL_OP, // OpNot - NULL_OP, // OpUMinus - NULL_OP, // OpUPlus - NULL_OP, // OpCompl - NULL_OP, // OpIncrement - NULL_OP, // OpDecrement - - INLINE_OP(bitAnd, &Binop<JITAssembler>::inline_and32, &Binop<JITAssembler>::inline_and32), // OpBitAnd - INLINE_OP(bitOr, &Binop<JITAssembler>::inline_or32, &Binop<JITAssembler>::inline_or32), // OpBitOr - INLINE_OP(bitXor, &Binop<JITAssembler>::inline_xor32, &Binop<JITAssembler>::inline_xor32), // OpBitXor - - INLINE_OPCONTEXT(add, &Binop<JITAssembler>::inline_add32, &Binop<JITAssembler>::inline_add32), // OpAdd - INLINE_OP(sub, &Binop<JITAssembler>::inline_sub32, &Binop<JITAssembler>::inline_sub32), // OpSub - INLINE_OP(mul, &Binop<JITAssembler>::inline_mul32, &Binop<JITAssembler>::inline_mul32), // OpMul - - OP(div), // OpDiv - OP(mod), // OpMod - - INLINE_OP(shl, &Binop<JITAssembler>::inline_shl32, &Binop<JITAssembler>::inline_shl32), // OpLShift - INLINE_OP(shr, &Binop<JITAssembler>::inline_shr32, &Binop<JITAssembler>::inline_shr32), // OpRShift - INLINE_OP(ushr, &Binop<JITAssembler>::inline_ushr32, &Binop<JITAssembler>::inline_ushr32), // OpURShift - - OP(greaterThan), // OpGt - OP(lessThan), // OpLt - OP(greaterEqual), // OpGe - OP(lessEqual), // OpLe - OP(equal), // OpEqual - OP(notEqual), // OpNotEqual - OP(strictEqual), // OpStrictEqual - OP(strictNotEqual), // OpStrictNotEqual - - OPCONTEXT(instanceof), // OpInstanceof - OPCONTEXT(in), // OpIn - - NULL_OP, // OpAnd - NULL_OP // OpOr -}; - - - -template <typename JITAssembler> -void Binop<JITAssembler>::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) -{ - if (op != IR::OpMod - && lhs->type == IR::DoubleType && rhs->type == IR::DoubleType) { - doubleBinop(lhs, rhs, target); - return; - } - if (lhs->type == IR::SInt32Type && rhs->type == IR::SInt32Type) { - if (int32Binop(lhs, rhs, target)) - return; - } - - Jump done; - if (lhs->type != IR::StringType && rhs->type != IR::StringType) - done = genInlineBinop(lhs, rhs, target); - - // TODO: inline var===null and var!==null - Binop::OpInfo info = Binop::operation(op); - - if (op == IR::OpAdd && - (lhs->type == IR::StringType || rhs->type == IR::StringType)) { - const Binop::OpInfo stringAdd = OPCONTEXT(addString); - info = stringAdd; - } - - typename JITAssembler::RuntimeCall fallBack(info.fallbackImplementation); - typename JITAssembler::RuntimeCall context(info.contextImplementation); - if (fallBack.isValid()) { - as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, fallBack, - PointerToValue(lhs), - PointerToValue(rhs)); - } else if (context.isValid()) { - as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, context, - JITAssembler::EngineRegister, - PointerToValue(lhs), - PointerToValue(rhs)); - } else { - Q_ASSERT(!"unreachable"); - } - - if (done.isSet()) - done.link(as); - -} - -template <typename JITAssembler> -void Binop<JITAssembler>::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) -{ - IR::Temp *targetTemp = target->asTemp(); - FPRegisterID targetReg; - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - targetReg = (FPRegisterID) targetTemp->index; - else - targetReg = JITAssembler::FPGpr0; - - switch (op) { - case IR::OpAdd: - if (lhs->asConst()) - std::swap(lhs, rhs); // Y = constant + X -> Y = X + constant - - if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleAdd(as, lhs, rhs, targetReg)) - break; - - as->addDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); - break; - - case IR::OpMul: - if (lhs->asConst()) - std::swap(lhs, rhs); // Y = constant * X -> Y = X * constant - - if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleMul(as, lhs, rhs, targetReg)) - break; - - as->mulDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); - break; - - case IR::OpSub: - if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleSub(as, lhs, rhs, targetReg)) - break; - - if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister - && targetTemp - && targetTemp->kind == IR::Temp::PhysicalRegister - && targetTemp->index == rhs->asTemp()->index) { // Y = X - Y -> Tmp = Y; Y = X - Tmp - as->moveDouble(as->toDoubleRegister(rhs, JITAssembler::FPGpr1), JITAssembler::FPGpr1); - as->subDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), JITAssembler::FPGpr1, targetReg); - break; - } - - as->subDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); - break; - - case IR::OpDiv: - if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleDiv(as, lhs, rhs, targetReg)) - break; - - if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister - && targetTemp - && targetTemp->kind == IR::Temp::PhysicalRegister - && targetTemp->index == rhs->asTemp()->index) { // Y = X / Y -> Tmp = Y; Y = X / Tmp - as->moveDouble(as->toDoubleRegister(rhs, JITAssembler::FPGpr1), JITAssembler::FPGpr1); - as->divDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), JITAssembler::FPGpr1, targetReg); - break; - } - - as->divDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); - break; - - default: { - Jump trueCase = as->branchDouble(false, op, lhs, rhs); - as->storeBool(false, target); - Jump done = as->jump(); - trueCase.link(as); - as->storeBool(true, target); - done.link(as); - } return; - } - - if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - as->storeDouble(targetReg, target); -} - -template <typename JITAssembler> -bool Binop<JITAssembler>::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) -{ - Q_ASSERT(leftSource->type == IR::SInt32Type); - Q_ASSERT(rightSource->type == IR::SInt32Type); - - switch (op) { - case IR::OpBitAnd: - case IR::OpBitOr: - case IR::OpBitXor: - case IR::OpAdd: - case IR::OpMul: - if (leftSource->asConst()) // X = Const op Y -> X = Y op Const - std::swap(leftSource, rightSource); - else if (IR::Temp *t = leftSource->asTemp()) { - if (t->kind != IR::Temp::PhysicalRegister) // X = [address] op Y -> X = Y op [address] - std::swap(leftSource, rightSource); - } - break; - - case IR::OpLShift: - case IR::OpRShift: - case IR::OpURShift: - case IR::OpSub: - // handled by this method, but we can't flip operands. - break; - - default: - return false; // not handled by this method, stop here. - } - - bool inplaceOpWithAddress = false; - - IR::Temp *targetTemp = target->asTemp(); - RegisterID targetReg = JITAssembler::ReturnValueRegister; - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - IR::Temp *rhs = rightSource->asTemp(); - if (!rhs || rhs->kind != IR::Temp::PhysicalRegister || rhs->index != targetTemp->index) { - // We try to load leftSource into the target's register, but we can't do that if - // the target register is the same as rightSource. - targetReg = (RegisterID) targetTemp->index; - } else if (rhs && rhs->kind == IR::Temp::PhysicalRegister && targetTemp->index == rhs->index) { - // However, if the target register is the same as the rightSource register, we can flip - // the operands for certain operations. - switch (op) { - case IR::OpBitAnd: - case IR::OpBitOr: - case IR::OpBitXor: - case IR::OpAdd: - case IR::OpMul: - // X = Y op X -> X = X op Y (or rephrased: X op= Y (so an in-place operation)) - std::swap(leftSource, rightSource); - targetReg = (RegisterID) targetTemp->index; - break; - - case IR::OpLShift: - case IR::OpRShift: - case IR::OpURShift: - case IR::OpSub: - break; - - default: - Q_UNREACHABLE(); - return false; - } - } - - // determine if we have X op= [address] - if (IR::Temp *lhs = leftSource->asTemp()) { - if (lhs->kind == IR::Temp::PhysicalRegister && lhs->index == targetTemp->index) { - if (IR::Temp *rhs = rightSource->asTemp()) { - if (rhs->kind != IR::Temp::PhysicalRegister) { - switch (op) { - case IR::OpBitAnd: - case IR::OpBitOr: - case IR::OpBitXor: - case IR::OpAdd: - case IR::OpMul: - inplaceOpWithAddress = true; - break; - default: - break; - } - } - } - } - } - } - - // Special cases: - switch (op) { - case IR::OpSub: - if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister - && targetTemp - && targetTemp->kind == IR::Temp::PhysicalRegister - && targetTemp->index == rightSource->asTemp()->index) { - // X = Y - X -> Tmp = X; X = Y; X -= Tmp - targetReg = (RegisterID) targetTemp->index; - as->move(targetReg, JITAssembler::ScratchRegister); - as->move(as->toInt32Register(leftSource, targetReg), targetReg); - as->sub32(JITAssembler::ScratchRegister, targetReg); - } else { - as->move(as->toInt32Register(leftSource, targetReg), targetReg); - as->sub32(as->toInt32Register(rightSource, JITAssembler::ScratchRegister), targetReg); - } - as->storeInt32(targetReg, target); - return true; - - case IR::OpLShift: - case IR::OpRShift: - case IR::OpURShift: - if (IR::Const *c = rightSource->asConst()) { - if ((QV4::Primitive::toUInt32(c->value) & 0x1f) == 0) { - RegisterID r = as->toInt32Register(leftSource, targetReg); - as->storeInt32(r, target); - return true; - } - } - break; - - default: - break; - } - - RegisterID l = as->toInt32Register(leftSource, targetReg); - if (IR::Const *c = rightSource->asConst()) { // All cases of Y = X op Const - TrustedImm32 r(int(c->value)); - TrustedImm32 ur(QV4::Primitive::toUInt32(c->value) & 0x1f); - - switch (op) { - case IR::OpBitAnd: as->and32(r, l, targetReg); break; - case IR::OpBitOr: as->or32 (r, l, targetReg); break; - case IR::OpBitXor: as->xor32(r, l, targetReg); break; - case IR::OpAdd: as->add32(r, l, targetReg); break; - case IR::OpMul: as->mul32(r, l, targetReg); break; - - case IR::OpLShift: as->lshift32(l, ur, targetReg); break; - case IR::OpRShift: as->rshift32(l, ur, targetReg); break; - case IR::OpURShift: as->urshift32(l, ur, targetReg); - as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! - return true; - - case IR::OpSub: // already handled before - default: // not handled by this method: - Q_UNREACHABLE(); - return false; - } - } else if (inplaceOpWithAddress) { // All cases of X = X op [address-of-Y] - Pointer rhsAddr = as->loadAddressForReading(JITAssembler::ScratchRegister, rightSource); - switch (op) { - case IR::OpBitAnd: as->and32(rhsAddr, targetReg); break; - case IR::OpBitOr: as->or32 (rhsAddr, targetReg); break; - case IR::OpBitXor: as->xor32(rhsAddr, targetReg); break; - case IR::OpAdd: as->add32(rhsAddr, targetReg); break; - case IR::OpMul: as->mul32(rhsAddr, targetReg); break; - break; - - default: // not handled by this method: - Q_UNREACHABLE(); - return false; - } - } else { // All cases of Z = X op Y - RegisterID r = as->toInt32Register(rightSource, JITAssembler::ScratchRegister); - switch (op) { - case IR::OpBitAnd: as->and32(l, r, targetReg); break; - case IR::OpBitOr: as->or32 (l, r, targetReg); break; - case IR::OpBitXor: as->xor32(l, r, targetReg); break; - case IR::OpAdd: as->add32(l, r, targetReg); break; - case IR::OpMul: as->mul32(l, r, targetReg); break; - -#if CPU(X86) || CPU(X86_64) - // Intel does the & 0x1f on the CPU, so: - case IR::OpLShift: as->lshift32(l, r, targetReg); break; - case IR::OpRShift: as->rshift32(l, r, targetReg); break; - case IR::OpURShift: as->urshift32(l, r, targetReg); - as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! - return true; -#else - // Not all CPUs accept shifts over more than 31 bits, and some CPUs (like ARM) will do - // surprising stuff when shifting over 0 bits. -#define CHECK_RHS(op) { \ - as->and32(TrustedImm32(0x1f), r, JITAssembler::ScratchRegister); \ - Jump notZero = as->branch32(RelationalCondition::NotEqual, JITAssembler::ScratchRegister, TrustedImm32(0)); \ - as->move(l, targetReg); \ - Jump done = as->jump(); \ - notZero.link(as); \ - op; \ - done.link(as); \ -} - case IR::OpLShift: CHECK_RHS(as->lshift32(l, JITAssembler::ScratchRegister, targetReg)); break; - case IR::OpRShift: CHECK_RHS(as->rshift32(l, JITAssembler::ScratchRegister, targetReg)); break; - case IR::OpURShift: - CHECK_RHS(as->urshift32(l, JITAssembler::ScratchRegister, targetReg)); - as->storeUInt32(targetReg, target); - // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! - return true; -#undef CHECK_RHS -#endif - - case IR::OpSub: // already handled before - default: // not handled by this method: - Q_UNREACHABLE(); - return false; - } - } - - as->storeInt32(targetReg, target); - return true; -} - -template <typename JITAssembler> -inline typename JITAssembler::FPRegisterID getFreeFPReg(IR::Expr *shouldNotOverlap, unsigned hint) -{ - if (IR::Temp *t = shouldNotOverlap->asTemp()) - if (t->type == IR::DoubleType) - if (t->kind == IR::Temp::PhysicalRegister) - if (t->index == hint) - return typename JITAssembler::FPRegisterID(hint + 1); - return typename JITAssembler::FPRegisterID(hint); -} - -template <typename JITAssembler> -typename JITAssembler::Jump Binop<JITAssembler>::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) -{ - Jump done; - - // Try preventing a call for a few common binary operations. This is used in two cases: - // - no register allocation was performed (not available for the platform, or the IR was - // not transformed into SSA) - // - type inference found that either or both operands can be of non-number type, and the - // register allocator will have prepared for a call (meaning: all registers that do not - // hold operands are spilled to the stack, which makes them available here) - // Note: FPGPr0 can still not be used, because uint32->double conversion uses it as a scratch - // register. - switch (op) { - case IR::OpAdd: { - FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); - FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); - Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); - Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); - - as->addDouble(rReg, lReg); - as->storeDouble(lReg, target); - done = as->jump(); - - if (leftIsNoDbl.isSet()) - leftIsNoDbl.link(as); - if (rightIsNoDbl.isSet()) - rightIsNoDbl.link(as); - } break; - case IR::OpMul: { - FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); - FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); - Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); - Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); - - as->mulDouble(rReg, lReg); - as->storeDouble(lReg, target); - done = as->jump(); - - if (leftIsNoDbl.isSet()) - leftIsNoDbl.link(as); - if (rightIsNoDbl.isSet()) - rightIsNoDbl.link(as); - } break; - case IR::OpSub: { - FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); - FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); - Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); - Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); - - as->subDouble(rReg, lReg); - as->storeDouble(lReg, target); - done = as->jump(); - - if (leftIsNoDbl.isSet()) - leftIsNoDbl.link(as); - if (rightIsNoDbl.isSet()) - rightIsNoDbl.link(as); - } break; - case IR::OpDiv: { - FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); - FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); - Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); - Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); - - as->divDouble(rReg, lReg); - as->storeDouble(lReg, target); - done = as->jump(); - - if (leftIsNoDbl.isSet()) - leftIsNoDbl.link(as); - if (rightIsNoDbl.isSet()) - rightIsNoDbl.link(as); - } break; - default: - break; - } - - return done; -} - -template struct QV4::JIT::Binop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>; -#if defined(V4_BOOTSTRAP) -#if !CPU(ARM_THUMB2) -template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>; -#endif -#if !CPU(ARM64) -template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>>; -#endif -#endif - -} // end of namespace JIT -} // end of namespace QV4 - -QT_END_NAMESPACE - - -#endif diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h deleted file mode 100644 index 1b1ab7f24d..0000000000 --- a/src/qml/jit/qv4binop_p.h +++ /dev/null @@ -1,256 +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 QV4BINOP_P_H -#define QV4BINOP_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_masm_p.h> -#include <qv4assembler_p.h> - -QT_BEGIN_NAMESPACE - -#if ENABLE(ASSEMBLER) - -namespace QV4 { -namespace JIT { - -template <typename JITAssembler> -struct Binop { - Binop(JITAssembler *assembler, IR::AluOp operation) - : as(assembler) - , op(operation) - {} - - using Jump = typename JITAssembler::Jump; - using Address = typename JITAssembler::Address; - using RegisterID = typename JITAssembler::RegisterID; - using FPRegisterID = typename JITAssembler::FPRegisterID; - using TrustedImm32 = typename JITAssembler::TrustedImm32; - using ResultCondition = typename JITAssembler::ResultCondition; - using RelationalCondition = typename JITAssembler::RelationalCondition; - using Pointer = typename JITAssembler::Pointer; - using PointerToValue = typename JITAssembler::PointerToValue; - - void generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target); - void doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target); - bool int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target); - Jump genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target); - - typedef Jump (Binop::*MemRegOp)(Address, RegisterID); - typedef Jump (Binop::*ImmRegOp)(TrustedImm32, RegisterID); - - struct OpInfo { - const char *name; - Runtime::RuntimeMethods fallbackImplementation; - Runtime::RuntimeMethods contextImplementation; - MemRegOp inlineMemRegOp; - ImmRegOp inlineImmRegOp; - bool needsExceptionCheck; - }; - - static const OpInfo operations[IR::LastAluOp + 1]; - static const OpInfo &operation(IR::AluOp operation) - { return operations[operation]; } - - Jump inline_add32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - return as->branchAdd32(ResultCondition::Overflow, addr, reg); -#else - as->load32(addr, JITAssembler::ScratchRegister); - return as->branchAdd32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); -#endif - } - - Jump inline_add32(TrustedImm32 imm, RegisterID reg) - { - return as->branchAdd32(ResultCondition::Overflow, imm, reg); - } - - Jump inline_sub32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - return as->branchSub32(ResultCondition::Overflow, addr, reg); -#else - as->load32(addr, JITAssembler::ScratchRegister); - return as->branchSub32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); -#endif - } - - Jump inline_sub32(TrustedImm32 imm, RegisterID reg) - { - return as->branchSub32(ResultCondition::Overflow, imm, reg); - } - - Jump inline_mul32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - return as->branchMul32(JITAssembler::Overflow, addr, reg); -#else - as->load32(addr, JITAssembler::ScratchRegister); - return as->branchMul32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); -#endif - } - - Jump inline_mul32(TrustedImm32 imm, RegisterID reg) - { - return as->branchMul32(ResultCondition::Overflow, imm, reg, reg); - } - - Jump inline_shl32(Address addr, RegisterID reg) - { - as->load32(addr, JITAssembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); - as->lshift32(JITAssembler::ScratchRegister, reg); - return Jump(); - } - - Jump inline_shl32(TrustedImm32 imm, RegisterID reg) - { - imm.m_value &= 0x1f; - as->lshift32(imm, reg); - return Jump(); - } - - Jump inline_shr32(Address addr, RegisterID reg) - { - as->load32(addr, JITAssembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); - as->rshift32(JITAssembler::ScratchRegister, reg); - return Jump(); - } - - Jump inline_shr32(TrustedImm32 imm, RegisterID reg) - { - imm.m_value &= 0x1f; - as->rshift32(imm, reg); - return Jump(); - } - - Jump inline_ushr32(Address addr, RegisterID reg) - { - as->load32(addr, JITAssembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); - as->urshift32(JITAssembler::ScratchRegister, reg); - return as->branchTest32(ResultCondition::Signed, reg, reg); - } - - Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) - { - imm.m_value &= 0x1f; - as->urshift32(imm, reg); - return as->branchTest32(ResultCondition::Signed, reg, reg); - } - - Jump inline_and32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - as->and32(addr, reg); -#else - as->load32(addr, JITAssembler::ScratchRegister); - as->and32(JITAssembler::ScratchRegister, reg); -#endif - return Jump(); - } - - Jump inline_and32(TrustedImm32 imm, RegisterID reg) - { - as->and32(imm, reg); - return Jump(); - } - - Jump inline_or32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - as->or32(addr, reg); -#else - as->load32(addr, JITAssembler::ScratchRegister); - as->or32(JITAssembler::ScratchRegister, reg); -#endif - return Jump(); - } - - Jump inline_or32(TrustedImm32 imm, RegisterID reg) - { - as->or32(imm, reg); - return Jump(); - } - - Jump inline_xor32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - as->xor32(addr, reg); -#else - as->load32(addr, JITAssembler::ScratchRegister); - as->xor32(JITAssembler::ScratchRegister, reg); -#endif - return Jump(); - } - - Jump inline_xor32(TrustedImm32 imm, RegisterID reg) - { - as->xor32(imm, reg); - return Jump(); - } - - - - JITAssembler *as; - IR::AluOp op; -}; - -} -} - -#endif - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp deleted file mode 100644 index 4a84d1866f..0000000000 --- a/src/qml/jit/qv4isel_masm.cpp +++ /dev/null @@ -1,1688 +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_masm_p.h" -#include "qv4runtime_p.h" -#include "qv4lookup_p.h" -#include "qv4ssa_p.h" -#include "qv4regalloc_p.h" -#include "qv4assembler_p.h" -#include "qv4unop_p.h" -#include "qv4binop_p.h" - -#include <QtCore/QBuffer> -#include <QtCore/QCoreApplication> - -#include <assembler/LinkBuffer.h> -#include <WTFStubs.h> - -#include <iostream> - -#if ENABLE(ASSEMBLER) - -#if USE(UDIS86) -# include <udis86.h> -#endif - -using namespace QV4; -using namespace QV4::JIT; - - -template <typename JITAssembler> -InstructionSelection<JITAssembler>::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) - : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory) - , _block(0) - , _as(0) - , compilationUnit(new CompilationUnit) - , qmlEngine(qmlEngine) -{ - compilationUnit->codeRefs.resize(module->functions.size()); - module->unitFlags |= QV4::CompiledData::Unit::ContainsMachineCode; -} - -template <typename JITAssembler> -InstructionSelection<JITAssembler>::~InstructionSelection() -{ - delete _as; -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::run(int functionIndex) -{ - IR::Function *function = irModule->functions[functionIndex]; - qSwap(_function, function); - - IR::Optimizer opt(_function); - opt.run(qmlEngine); - - static const bool withRegisterAllocator = qEnvironmentVariableIsEmpty("QV4_NO_REGALLOC"); - if (JITTargetPlatform::RegAllocIsSupported && opt.isInSSA() && withRegisterAllocator) { - RegisterAllocator regalloc(JITTargetPlatform::getRegisterInfo()); - regalloc.run(_function, opt); - calculateRegistersToSave(regalloc.usedRegisters()); - } else { - if (opt.isInSSA()) - // No register allocator available for this platform, or env. var was set, so: - opt.convertOutOfSSA(); - ConvertTemps().toStackSlots(_function); - IR::Optimizer::showMeTheCode(_function, "After stack slot allocation"); - calculateRegistersToSave(JITTargetPlatform::getRegisterInfo()); // FIXME: this saves all registers. We can probably do with a subset: those that are not used by the register allocator. - } - BitVector removableJumps = opt.calculateOptionalJumps(); - qSwap(_removableJumps, removableJumps); - - JITAssembler* oldAssembler = _as; - _as = new JITAssembler(jsGenerator, _function, executableAllocator); - _as->setStackLayout(6, // 6 == max argc for calls to built-ins with an argument array - regularRegistersToSave.size(), - fpRegistersToSave.size()); - _as->enterStandardStackFrame(regularRegistersToSave, fpRegistersToSave); - - if (JITTargetPlatform::RegisterArgumentCount > 0) - _as->move(_as->registerForArgument(0), JITTargetPlatform::EngineRegister); - else - _as->loadPtr(addressForArgument(0), JITTargetPlatform::EngineRegister); - - _as->initializeLocalVariables(); - - int lastLine = 0; - for (int i = 0, ei = _function->basicBlockCount(); i != ei; ++i) { - IR::BasicBlock *nextBlock = (i < ei - 1) ? _function->basicBlock(i + 1) : 0; - _block = _function->basicBlock(i); - if (_block->isRemoved()) - continue; - _as->registerBlock(_block, nextBlock); - - for (IR::Stmt *s : _block->statements()) { - if (s->location.isValid()) { - if (int(s->location.startLine) != lastLine) { - _as->loadPtr(Address(JITTargetPlatform::EngineRegister, JITAssembler::targetStructureOffset(offsetof(QV4::EngineBase, current))), JITTargetPlatform::ScratchRegister); - Address lineAddr(JITTargetPlatform::ScratchRegister, JITAssembler::targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, lineNumber))); - _as->store32(TrustedImm32(s->location.startLine), lineAddr); - lastLine = s->location.startLine; - } - } - visit(s); - } - } - - if (!_as->exceptionReturnLabel.isSet()) - visitRet(0); - - int dummySize; - JSC::MacroAssemblerCodeRef codeRef =_as->link(&dummySize); - compilationUnit->codeRefs[functionIndex] = codeRef; - - qSwap(_function, function); - delete _as; - _as = oldAssembler; - qSwap(_removableJumps, removableJumps); -} - -template <typename JITAssembler> -QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection<JITAssembler>::backendCompileStep() -{ - QQmlRefPointer<QV4::CompiledData::CompilationUnit> result; - result.adopt(compilationUnit.take()); - return result; -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) -{ - prepareCallData(args, 0); - - if (useFastLookups && func->global) { - uint index = registerGlobalGetterLookup(*func->id); - generateRuntimeCall(_as, result, callGlobalLookup, - JITTargetPlatform::EngineRegister, - TrustedImm32(index), - baseAddressForCallData()); - } else { - generateRuntimeCall(_as, result, callActivationProperty, - JITTargetPlatform::EngineRegister, - StringToIndex(*func->id), - baseAddressForCallData()); - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinTypeofQmlContextProperty(IR::Expr *base, - IR::Member::MemberKind kind, - int propertyIndex, IR::Expr *result) -{ - if (kind == IR::Member::MemberOfQmlScopeObject) { - generateRuntimeCall(_as, result, typeofScopeObjectProperty, JITTargetPlatform::EngineRegister, - PointerToValue(base), - TrustedImm32(propertyIndex)); - } else if (kind == IR::Member::MemberOfQmlContextObject) { - generateRuntimeCall(_as, result, typeofContextObjectProperty, - JITTargetPlatform::EngineRegister, PointerToValue(base), - TrustedImm32(propertyIndex)); - } else { - Q_UNREACHABLE(); - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinTypeofMember(IR::Expr *base, const QString &name, - IR::Expr *result) -{ - generateRuntimeCall(_as, result, typeofMember, JITTargetPlatform::EngineRegister, - PointerToValue(base), StringToIndex(name)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, - IR::Expr *result) -{ - generateRuntimeCall(_as, result, typeofElement, - JITTargetPlatform::EngineRegister, - PointerToValue(base), PointerToValue(index)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinTypeofName(const QString &name, IR::Expr *result) -{ - generateRuntimeCall(_as, result, typeofName, JITTargetPlatform::EngineRegister, - StringToIndex(name)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) -{ - generateRuntimeCall(_as, result, typeofValue, JITTargetPlatform::EngineRegister, - PointerToValue(value)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) -{ - generateRuntimeCall(_as, result, deleteMember, JITTargetPlatform::EngineRegister, - Reference(base), StringToIndex(name)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, - IR::Expr *result) -{ - generateRuntimeCall(_as, result, deleteElement, JITTargetPlatform::EngineRegister, - Reference(base), PointerToValue(index)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDeleteName(const QString &name, IR::Expr *result) -{ - generateRuntimeCall(_as, result, deleteName, JITTargetPlatform::EngineRegister, - StringToIndex(name)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDeleteValue(IR::Expr *result) -{ - _as->storeValue(JITAssembler::TargetPrimitive::fromBoolean(false), result); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinThrow(IR::Expr *arg) -{ - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, throwException, JITTargetPlatform::EngineRegister, - PointerToValue(arg)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinReThrow() -{ - _as->jumpToExceptionHandler(); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinUnwindException(IR::Expr *result) -{ - generateRuntimeCall(_as, result, unwindException, JITTargetPlatform::EngineRegister); - -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinPushCatchScope(const QString &exceptionName) -{ - generateRuntimeCall(_as, JITAssembler::Void, pushCatchScope, JITTargetPlatform::EngineRegister, StringToIndex(exceptionName)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) -{ - Q_ASSERT(arg); - Q_ASSERT(result); - - generateRuntimeCall(_as, result, foreachIterator, JITTargetPlatform::EngineRegister, PointerToValue(arg)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) -{ - Q_ASSERT(arg); - Q_ASSERT(result); - - generateRuntimeCall(_as, result, foreachNextPropertyName, Reference(arg)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinPushWithScope(IR::Expr *arg) -{ - Q_ASSERT(arg); - - generateRuntimeCall(_as, JITAssembler::Void, pushWithScope, Reference(arg), JITTargetPlatform::EngineRegister); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinPopScope() -{ - generateRuntimeCall(_as, JITAssembler::Void, popScope, JITTargetPlatform::EngineRegister); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDeclareVar(bool deletable, const QString &name) -{ - generateRuntimeCall(_as, JITAssembler::Void, declareVar, JITTargetPlatform::EngineRegister, - TrustedImm32(deletable), StringToIndex(name)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) -{ - Q_ASSERT(result); - - int length = prepareVariableArguments(args); - generateRuntimeCall(_as, result, arrayLiteral, JITTargetPlatform::EngineRegister, - baseAddressForCallArguments(), TrustedImm32(length)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) -{ - Q_ASSERT(result); - - QVector<Compiler::JSUnitGenerator::MemberInfo> members; - int argc = 0; - - 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; - - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); - - if (!isData) { - it = it->next; - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); - } - } - - const int classId = registerJSClass(members); - - it = arrayEntries; - uint arrayValueCount = 0; - while (it) { - uint index = it->expr->asConst()->value; - it = it->next; - - bool isData = it->expr->asConst()->value; - it = it->next; - - if (!isData) { - it = it->next; // getter - it = it->next; // setter - continue; - } - - ++arrayValueCount; - - // Index - _as->storeValue(JITAssembler::TargetPrimitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++), WriteBarrier::NoBarrier); - - // Value - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); - it = it->next; - } - - it = arrayEntries; - uint arrayGetterSetterCount = 0; - while (it) { - uint index = it->expr->asConst()->value; - it = it->next; - - bool isData = it->expr->asConst()->value; - it = it->next; - - if (isData) { - it = it->next; // value - continue; - } - - ++arrayGetterSetterCount; - - // Index - _as->storeValue(JITAssembler::TargetPrimitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++), WriteBarrier::NoBarrier); - - // Getter - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); - it = it->next; - - // Setter - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); - it = it->next; - } - - generateRuntimeCall(_as, result, objectLiteral, JITTargetPlatform::EngineRegister, - baseAddressForCallArguments(), TrustedImm32(classId), - TrustedImm32(arrayValueCount), TrustedImm32(arrayGetterSetterCount | (needSparseArray << 30))); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinSetupArgumentObject(IR::Expr *result) -{ - generateRuntimeCall(_as, result, setupArgumentsObject, JITTargetPlatform::EngineRegister); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callBuiltinConvertThisToObject() -{ - generateRuntimeCall(_as, JITAssembler::Void, convertThisToObject, JITTargetPlatform::EngineRegister); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) -{ - Q_ASSERT(value); - - prepareCallData(args, 0); - if (value->asConst()) - generateRuntimeCall(_as, result, callValue, JITTargetPlatform::EngineRegister, - PointerToValue(value), - baseAddressForCallData()); - else - generateRuntimeCall(_as, result, callValue, JITTargetPlatform::EngineRegister, - Reference(value), - baseAddressForCallData()); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadThisObject(IR::Expr *temp) -{ - WriteBarrier::Type barrier; - Pointer addr = _as->loadAddressForWriting(JITTargetPlatform::ScratchRegister, temp, &barrier); - _as->loadPtr(Address(JITTargetPlatform::EngineRegister, JITAssembler::targetStructureOffset(offsetof(QV4::EngineBase, current))), JITTargetPlatform::ReturnValueRegister); - _as->loadPtr(Address(JITTargetPlatform::ReturnValueRegister,JITAssembler::targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, callData))), JITTargetPlatform::ReturnValueRegister); - _as->copyValue(addr, Address(JITTargetPlatform::ReturnValueRegister, offsetof(CallData, thisObject)), barrier); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadQmlContext(IR::Expr *temp) -{ - generateRuntimeCall(_as, temp, getQmlContext, JITTargetPlatform::EngineRegister); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadQmlImportedScripts(IR::Expr *temp) -{ - generateRuntimeCall(_as, temp, getQmlImportedScripts, JITTargetPlatform::EngineRegister); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadQmlSingleton(const QString &name, IR::Expr *temp) -{ - generateRuntimeCall(_as, temp, getQmlSingleton, JITTargetPlatform::EngineRegister, StringToIndex(name)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadConst(IR::Const *sourceConst, IR::Expr *target) -{ - if (IR::Temp *targetTemp = target->asTemp()) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { - if (targetTemp->type == IR::DoubleType) { - Q_ASSERT(sourceConst->type == IR::DoubleType); - _as->toDoubleRegister(sourceConst, (FPRegisterID) targetTemp->index); - } else if (targetTemp->type == IR::SInt32Type) { - Q_ASSERT(sourceConst->type == IR::SInt32Type); - _as->toInt32Register(sourceConst, (RegisterID) targetTemp->index); - } else if (targetTemp->type == IR::UInt32Type) { - Q_ASSERT(sourceConst->type == IR::UInt32Type); - _as->toUInt32Register(sourceConst, (RegisterID) targetTemp->index); - } else if (targetTemp->type == IR::BoolType) { - Q_ASSERT(sourceConst->type == IR::BoolType); - _as->move(TrustedImm32(convertToValue<Primitive>(sourceConst).int_32()), - (RegisterID) targetTemp->index); - } else { - Q_UNREACHABLE(); - } - return; - } - } - - _as->storeValue(convertToValue<typename JITAssembler::TargetPrimitive>(sourceConst), target); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadString(const QString &str, IR::Expr *target) -{ - Pointer srcAddr = _as->loadStringAddress(JITTargetPlatform::ReturnValueRegister, str); - _as->loadPtr(srcAddr, JITTargetPlatform::ReturnValueRegister); - WriteBarrier::Type barrier; - Pointer destAddr = _as->loadAddressForWriting(JITTargetPlatform::ScratchRegister, target, &barrier); - JITAssembler::RegisterSizeDependentOps::loadManagedPointer(_as, JITTargetPlatform::ReturnValueRegister, destAddr, barrier); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) -{ - int id = registerRegExp(sourceRegexp); - generateRuntimeCall(_as, target, regexpLiteral, JITTargetPlatform::EngineRegister, TrustedImm32(id)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::getActivationProperty(const IR::Name *name, IR::Expr *target) -{ - if (useFastLookups && name->global) { - uint index = registerGlobalGetterLookup(*name->id); - generateLookupCall(target, index, offsetof(QV4::Lookup, globalGetter), JITTargetPlatform::EngineRegister, JITAssembler::Void); - return; - } - generateRuntimeCall(_as, target, getActivationProperty, JITTargetPlatform::EngineRegister, StringToIndex(*name->id)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::setActivationProperty(IR::Expr *source, const QString &targetName) -{ - // ### should use a lookup call here - generateRuntimeCall(_as, JITAssembler::Void, setActivationProperty, - JITTargetPlatform::EngineRegister, StringToIndex(targetName), PointerToValue(source)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::initClosure(IR::Closure *closure, IR::Expr *target) -{ - int id = closure->value; - generateRuntimeCall(_as, target, closure, JITTargetPlatform::EngineRegister, TrustedImm32(id)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::getProperty(IR::Expr *base, const QString &name, IR::Expr *target) -{ - if (useFastLookups) { - uint index = registerGetterLookup(name); - generateLookupCall(target, index, offsetof(QV4::Lookup, getter), JITTargetPlatform::EngineRegister, PointerToValue(base), JITAssembler::Void); - } else { - generateRuntimeCall(_as, target, getProperty, JITTargetPlatform::EngineRegister, - PointerToValue(base), StringToIndex(name)); - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) -{ - if (kind == IR::Member::MemberOfQmlScopeObject) - generateRuntimeCall(_as, target, getQmlScopeObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index), TrustedImm32(captureRequired)); - else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(_as, target, getQmlContextObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index), TrustedImm32(captureRequired)); - else if (kind == IR::Member::MemberOfIdObjectsArray) - generateRuntimeCall(_as, target, getQmlIdObject, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index)); - else - Q_ASSERT(false); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) -{ - if (attachedPropertiesId != 0) - generateRuntimeCall(_as, target, getQmlAttachedProperty, JITTargetPlatform::EngineRegister, TrustedImm32(attachedPropertiesId), TrustedImm32(propertyIndex)); - else if (isSingleton) - generateRuntimeCall(_as, target, getQmlSingletonQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(propertyIndex), - TrustedImm32(captureRequired)); - else - generateRuntimeCall(_as, target, getQmlQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(propertyIndex), - TrustedImm32(captureRequired)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::setProperty(IR::Expr *source, IR::Expr *targetBase, - const QString &targetName) -{ - if (useFastLookups) { - uint index = registerSetterLookup(targetName); - generateLookupCall(JITAssembler::Void, index, offsetof(QV4::Lookup, setter), - JITTargetPlatform::EngineRegister, - PointerToValue(targetBase), - PointerToValue(source)); - } else { - generateRuntimeCall(_as, JITAssembler::Void, setProperty, JITTargetPlatform::EngineRegister, - PointerToValue(targetBase), StringToIndex(targetName), - PointerToValue(source)); - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) -{ - if (kind == IR::Member::MemberOfQmlScopeObject) - generateRuntimeCall(_as, JITAssembler::Void, setQmlScopeObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), - TrustedImm32(propertyIndex), PointerToValue(source)); - else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(_as, JITAssembler::Void, setQmlContextObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), - TrustedImm32(propertyIndex), PointerToValue(source)); - else - Q_ASSERT(false); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) -{ - generateRuntimeCall(_as, JITAssembler::Void, setQmlQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), - TrustedImm32(propertyIndex), PointerToValue(source)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) -{ - if (0 && useFastLookups) { - uint lookup = registerIndexedGetterLookup(); - generateLookupCall(target, lookup, offsetof(QV4::Lookup, indexedGetter), - JITTargetPlatform::EngineRegister, - PointerToValue(base), - PointerToValue(index)); - return; - } - - generateRuntimeCall(_as, target, getElement, JITTargetPlatform::EngineRegister, - PointerToValue(base), PointerToValue(index)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) -{ - if (0 && useFastLookups) { - uint lookup = registerIndexedSetterLookup(); - generateLookupCall(JITAssembler::Void, lookup, offsetof(QV4::Lookup, indexedSetter), - JITTargetPlatform::EngineRegister, - PointerToValue(targetBase), PointerToValue(targetIndex), - PointerToValue(source)); - return; - } - generateRuntimeCall(_as, JITAssembler::Void, setElement, JITTargetPlatform::EngineRegister, - PointerToValue(targetBase), PointerToValue(targetIndex), - PointerToValue(source)); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::copyValue(IR::Expr *source, IR::Expr *target) -{ - IR::Temp *sourceTemp = source->asTemp(); - IR::Temp *targetTemp = target->asTemp(); - - if (sourceTemp && targetTemp && *sourceTemp == *targetTemp) - return; - if (IR::ArgLocal *sal = source->asArgLocal()) - if (IR::ArgLocal *tal = target->asArgLocal()) - if (*sal == *tal) - return; - - if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) { - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - if (sourceTemp->type == IR::DoubleType) - _as->moveDouble((FPRegisterID) sourceTemp->index, - (FPRegisterID) targetTemp->index); - else - _as->move((RegisterID) sourceTemp->index, - (RegisterID) targetTemp->index); - return; - } else { - switch (sourceTemp->type) { - case IR::DoubleType: - _as->storeDouble((FPRegisterID) sourceTemp->index, target); - break; - case IR::SInt32Type: - _as->storeInt32((RegisterID) sourceTemp->index, target); - break; - case IR::UInt32Type: - _as->storeUInt32((RegisterID) sourceTemp->index, target); - break; - case IR::BoolType: - _as->storeBool((RegisterID) sourceTemp->index, target); - break; - default: - Q_ASSERT(!"Unreachable"); - break; - } - return; - } - } else if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - switch (targetTemp->type) { - case IR::DoubleType: - Q_ASSERT(source->type == IR::DoubleType); - _as->toDoubleRegister(source, (FPRegisterID) targetTemp->index); - return; - case IR::BoolType: - Q_ASSERT(source->type == IR::BoolType); - _as->toInt32Register(source, (RegisterID) targetTemp->index); - return; - case IR::SInt32Type: - Q_ASSERT(source->type == IR::SInt32Type); - _as->toInt32Register(source, (RegisterID) targetTemp->index); - return; - case IR::UInt32Type: - Q_ASSERT(source->type == IR::UInt32Type); - _as->toUInt32Register(source, (RegisterID) targetTemp->index); - return; - default: - Q_ASSERT(!"Unreachable"); - break; - } - } - - WriteBarrier::Type barrier; - Pointer addr = _as->loadAddressForWriting(JITTargetPlatform::ReturnValueRegister, target, &barrier); - // The target is not a physical register, nor is the source. So we can do a memory-to-memory copy: - _as->memcopyValue(addr, source, JITTargetPlatform::ScratchRegister, barrier); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::swapValues(IR::Expr *source, IR::Expr *target) -{ - IR::Temp *sourceTemp = source->asTemp(); - IR::Temp *targetTemp = target->asTemp(); - - if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) { - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { - Q_ASSERT(sourceTemp->type == targetTemp->type); - - if (sourceTemp->type == IR::DoubleType) { - _as->moveDouble((FPRegisterID) targetTemp->index, JITTargetPlatform::FPGpr0); - _as->moveDouble((FPRegisterID) sourceTemp->index, - (FPRegisterID) targetTemp->index); - _as->moveDouble(JITTargetPlatform::FPGpr0, (FPRegisterID) sourceTemp->index); - } else { - _as->swap((RegisterID) sourceTemp->index, - (RegisterID) targetTemp->index); - } - return; - } - } else if (!sourceTemp || sourceTemp->kind == IR::Temp::StackSlot) { - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - // Note: a swap for two stack-slots can involve different types. - WriteBarrier::Type barrierForSource, barrierForTarget; - Pointer sAddr = _as->loadAddressForWriting(JITTargetPlatform::ScratchRegister, source, &barrierForSource); - Pointer tAddr = _as->loadAddressForWriting(JITTargetPlatform::ReturnValueRegister, target, &barrierForTarget); - _as->loadRawValue(sAddr, JITTargetPlatform::FPGpr0); - _as->loadRawValue(tAddr, JITTargetPlatform::FPGpr1); - _as->storeRawValue(JITTargetPlatform::FPGpr1, sAddr, barrierForSource); - _as->storeRawValue(JITTargetPlatform::FPGpr0, tAddr, barrierForTarget); - return; - } - } - - IR::Expr *memExpr = !sourceTemp || sourceTemp->kind == IR::Temp::StackSlot ? source : target; - IR::Temp *regTemp = sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister ? sourceTemp - : targetTemp; - Q_ASSERT(memExpr); - Q_ASSERT(regTemp); - - WriteBarrier::Type barrier; - Pointer addr = _as->loadAddressForWriting(JITTargetPlatform::ReturnValueRegister, memExpr, &barrier); - if (regTemp->type == IR::DoubleType) { - _as->loadDouble(addr, JITTargetPlatform::FPGpr0); - _as->storeDouble((FPRegisterID) regTemp->index, addr, barrier); - _as->moveDouble(JITTargetPlatform::FPGpr0, (FPRegisterID) regTemp->index); - } else if (regTemp->type == IR::UInt32Type) { - _as->toUInt32Register(addr, JITTargetPlatform::ScratchRegister); - _as->storeUInt32((RegisterID) regTemp->index, addr, barrier); - _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index); - } else { - _as->load32(addr, JITTargetPlatform::ScratchRegister); - _as->store32((RegisterID) regTemp->index, addr); - if (regTemp->type != memExpr->type) { - addr.offset += 4; - quint32 tag; - switch (regTemp->type) { - case IR::BoolType: - tag = quint32(JITAssembler::ValueTypeInternal::Boolean); - break; - case IR::SInt32Type: - tag = quint32(JITAssembler::ValueTypeInternal::Integer); - break; - default: - tag = 31337; // bogus value - Q_UNREACHABLE(); - } - _as->store32(TrustedImm32(tag), addr); - _as->emitWriteBarrier(addr, barrier); - } - _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index); - } -} - -#define setOp(op, opName, operation) \ - do { \ - op = typename JITAssembler::RuntimeCall(QV4::Runtime::operation); opName = "Runtime::" isel_stringIfy(operation); \ - needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ - } while (0) -#define setOpContext(op, opName, operation) \ - do { \ - opContext = typename JITAssembler::RuntimeCall(QV4::Runtime::operation); opName = "Runtime::" isel_stringIfy(operation); \ - needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ - } while (0) - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) -{ - QV4::JIT::Unop<JITAssembler> unop(_as, oper); - unop.generate(source, target); -} - - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) -{ - QV4::JIT::Binop<JITAssembler> binop(_as, oper); - binop.generate(leftSource, rightSource, target); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) -{ - prepareCallData(args, base); - - if (kind == IR::Member::MemberOfQmlScopeObject) - generateRuntimeCall(_as, result, callQmlScopeObjectProperty, - JITTargetPlatform::EngineRegister, - TrustedImm32(propertyIndex), - baseAddressForCallData()); - else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(_as, result, callQmlContextObjectProperty, - JITTargetPlatform::EngineRegister, - TrustedImm32(propertyIndex), - baseAddressForCallData()); - else - Q_ASSERT(false); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, - IR::Expr *result) -{ - Q_ASSERT(base != 0); - - prepareCallData(args, base); - - if (useFastLookups) { - uint index = registerGetterLookup(name); - generateRuntimeCall(_as, result, callPropertyLookup, - JITTargetPlatform::EngineRegister, - TrustedImm32(index), - baseAddressForCallData()); - } else { - generateRuntimeCall(_as, result, callProperty, JITTargetPlatform::EngineRegister, - StringToIndex(name), - baseAddressForCallData()); - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, - IR::Expr *result) -{ - Q_ASSERT(base != 0); - - prepareCallData(args, base); - generateRuntimeCall(_as, result, callElement, JITTargetPlatform::EngineRegister, - PointerToValue(index), - baseAddressForCallData()); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::convertType(IR::Expr *source, IR::Expr *target) -{ - switch (target->type) { - case IR::DoubleType: - convertTypeToDouble(source, target); - break; - case IR::BoolType: - convertTypeToBool(source, target); - break; - case IR::SInt32Type: - convertTypeToSInt32(source, target); - break; - case IR::UInt32Type: - convertTypeToUInt32(source, target); - break; - default: - convertTypeSlowPath(source, target); - break; - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::convertTypeSlowPath(IR::Expr *source, IR::Expr *target) -{ - Q_ASSERT(target->type != IR::BoolType); - - if (target->type & IR::NumberType) - unop(IR::OpUPlus, source, target); - else - copyValue(source, target); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::convertTypeToDouble(IR::Expr *source, IR::Expr *target) -{ - switch (source->type) { - case IR::SInt32Type: - case IR::BoolType: - case IR::NullType: - convertIntToDouble(source, target); - break; - case IR::UInt32Type: - convertUIntToDouble(source, target); - break; - case IR::UndefinedType: - _as->loadDouble(_as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source), JITTargetPlatform::FPGpr0); - _as->storeDouble(JITTargetPlatform::FPGpr0, target); - break; - case IR::StringType: - case IR::VarType: { - // load the tag: - Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); - tagAddr.offset += 4; - _as->load32(tagAddr, JITTargetPlatform::ScratchRegister); - - // check if it's an int32: - Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, - TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer))); - convertIntToDouble(source, target); - Jump intDone = _as->jump(); - - // not an int, check if it's NOT a double: - isNoInt.link(_as); - Jump isDbl = _as->generateIsDoubleCheck(JITTargetPlatform::ScratchRegister); - - generateRuntimeCall(_as, target, toDouble, PointerToValue(source)); - Jump noDoubleDone = _as->jump(); - - // it is a double: - isDbl.link(_as); - Pointer addr2 = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); - IR::Temp *targetTemp = target->asTemp(); - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - _as->memcopyValue(target, addr2, JITTargetPlatform::FPGpr0, JITTargetPlatform::ReturnValueRegister); - } else { - _as->loadDouble(addr2, (FPRegisterID) targetTemp->index); - } - - noDoubleDone.link(_as); - intDone.link(_as); - } break; - default: - convertTypeSlowPath(source, target); - break; - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::convertTypeToBool(IR::Expr *source, IR::Expr *target) -{ - IR::Temp *sourceTemp = source->asTemp(); - switch (source->type) { - case IR::SInt32Type: - case IR::UInt32Type: - convertIntToBool(source, target); - break; - case IR::DoubleType: { - // The source is in a register if the register allocator is used. If the register - // allocator was not used, then that means that we can use any register for to - // load the double into. - FPRegisterID reg; - if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) - reg = (FPRegisterID) sourceTemp->index; - else - reg = _as->toDoubleRegister(source, (FPRegisterID) 1); - Jump nonZero = _as->branchDoubleNonZero(reg, JITTargetPlatform::FPGpr0); - - // it's 0, so false: - _as->storeBool(false, target); - Jump done = _as->jump(); - - // it's non-zero, so true: - nonZero.link(_as); - _as->storeBool(true, target); - - // done: - done.link(_as); - } break; - case IR::UndefinedType: - case IR::NullType: - _as->storeBool(false, target); - break; - case IR::StringType: - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, - PointerToValue(source)); - _as->storeBool(JITTargetPlatform::ReturnValueRegister, target); - Q_FALLTHROUGH(); - case IR::VarType: - default: - Pointer addr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); - Pointer tagAddr = addr; - tagAddr.offset += 4; - _as->load32(tagAddr, JITTargetPlatform::ReturnValueRegister); - - // checkif it's a bool: - Jump notBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, - TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean))); - _as->load32(addr, JITTargetPlatform::ReturnValueRegister); - Jump boolDone = _as->jump(); - // check if it's an int32: - notBool.link(_as); - Jump fallback = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, - TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer))); - _as->load32(addr, JITTargetPlatform::ReturnValueRegister); - Jump isZero = _as->branch32(RelationalCondition::Equal, JITTargetPlatform::ReturnValueRegister, - TrustedImm32(0)); - _as->move(TrustedImm32(1), JITTargetPlatform::ReturnValueRegister); - Jump intDone = _as->jump(); - - // not an int: - fallback.link(_as); - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, - PointerToValue(source)); - - isZero.link(_as); - intDone.link(_as); - boolDone.link(_as); - _as->storeBool(JITTargetPlatform::ReturnValueRegister, target); - - break; - } -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::convertTypeToSInt32(IR::Expr *source, IR::Expr *target) -{ - switch (source->type) { - case IR::VarType: { - JITAssembler::RegisterSizeDependentOps::convertVarToSInt32(_as, source, target); - } break; - case IR::DoubleType: { - Jump success = - _as->branchTruncateDoubleToInt32(_as->toDoubleRegister(source), - JITTargetPlatform::ReturnValueRegister, - BranchTruncateType::BranchIfTruncateSuccessful); - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, doubleToInt, - PointerToValue(source)); - success.link(_as); - _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); - } break; - case IR::UInt32Type: - _as->storeInt32(_as->toUInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); - break; - case IR::NullType: - case IR::UndefinedType: - _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister); - _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); - break; - case IR::BoolType: - _as->storeInt32(_as->toInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); - break; - case IR::StringType: - default: - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toInt, - _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source)); - _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); - break; - } // switch (source->type) -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::convertTypeToUInt32(IR::Expr *source, IR::Expr *target) -{ - switch (source->type) { - case IR::VarType: { - // load the tag: - Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); - tagAddr.offset += 4; - _as->load32(tagAddr, JITTargetPlatform::ScratchRegister); - - // check if it's an int32: - Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, - TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer))); - Pointer addr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); - _as->storeUInt32(_as->toInt32Register(addr, JITTargetPlatform::ScratchRegister), target); - Jump intDone = _as->jump(); - - // not an int: - isNoInt.link(_as); - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt, - _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source)); - _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); - - intDone.link(_as); - } break; - case IR::DoubleType: { - FPRegisterID reg = _as->toDoubleRegister(source); - Jump success = - _as->branchTruncateDoubleToUint32(reg, JITTargetPlatform::ReturnValueRegister, - BranchTruncateType::BranchIfTruncateSuccessful); - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, doubleToUInt, - PointerToValue(source)); - success.link(_as); - _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); - } break; - case IR::NullType: - case IR::UndefinedType: - _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister); - _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); - break; - case IR::StringType: - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt, - PointerToValue(source)); - _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); - break; - case IR::SInt32Type: - case IR::BoolType: - _as->storeUInt32(_as->toInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); - break; - default: - break; - } // switch (source->type) -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) -{ - Q_ASSERT(func != 0); - prepareCallData(args, 0); - - if (useFastLookups && func->global) { - uint index = registerGlobalGetterLookup(*func->id); - generateRuntimeCall(_as, result, constructGlobalLookup, - JITTargetPlatform::EngineRegister, - TrustedImm32(index), baseAddressForCallData()); - return; - } - - generateRuntimeCall(_as, result, constructActivationProperty, - JITTargetPlatform::EngineRegister, - StringToIndex(*func->id), - baseAddressForCallData()); -} - - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) -{ - prepareCallData(args, base); - if (useFastLookups) { - uint index = registerGetterLookup(name); - generateRuntimeCall(_as, result, constructPropertyLookup, - JITTargetPlatform::EngineRegister, - TrustedImm32(index), - baseAddressForCallData()); - return; - } - - generateRuntimeCall(_as, result, constructProperty, JITTargetPlatform::EngineRegister, - StringToIndex(name), - baseAddressForCallData()); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) -{ - Q_ASSERT(value != 0); - - prepareCallData(args, 0); - generateRuntimeCall(_as, result, constructValue, - JITTargetPlatform::EngineRegister, - Reference(value), - baseAddressForCallData()); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::visitJump(IR::Jump *s) -{ - if (!_removableJumps.at(_block->index())) - _as->jumpToBlock(_block, s->target); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::visitCJump(IR::CJump *s) -{ - IR::Temp *t = s->cond->asTemp(); - if (t || s->cond->asArgLocal()) { - RegisterID reg; - if (t && t->kind == IR::Temp::PhysicalRegister) { - Q_ASSERT(t->type == IR::BoolType); - reg = (RegisterID) t->index; - } else if (t && t->kind == IR::Temp::StackSlot && t->type == IR::BoolType) { - reg = JITTargetPlatform::ReturnValueRegister; - _as->toInt32Register(t, reg); - } else { - Address temp = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, s->cond); - Address tag = temp; - tag.offset += QV4::Value::tagOffset(); - Jump booleanConversion = _as->branch32(RelationalCondition::NotEqual, tag, - TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean))); - - Address data = temp; - data.offset += QV4::Value::valueOffset(); - _as->load32(data, JITTargetPlatform::ReturnValueRegister); - Jump testBoolean = _as->jump(); - - booleanConversion.link(_as); - reg = JITTargetPlatform::ReturnValueRegister; - generateRuntimeCall(_as, reg, toBoolean, Reference(s->cond)); - - testBoolean.link(_as); - } - - _as->generateCJumpOnNonZero(reg, _block, s->iftrue, s->iffalse); - return; - } else if (IR::Const *c = s->cond->asConst()) { - // TODO: SSA optimization for constant condition evaluation should remove this. - // See also visitCJump() in RegAllocInfo. - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, - PointerToValue(c)); - _as->generateCJumpOnNonZero(JITTargetPlatform::ReturnValueRegister, _block, s->iftrue, s->iffalse); - return; - } else if (IR::Binop *b = s->cond->asBinop()) { - if (b->left->type == IR::DoubleType && b->right->type == IR::DoubleType - && visitCJumpDouble(b->op, b->left, b->right, s->iftrue, s->iffalse)) - return; - - if (b->left->type == IR::SInt32Type && b->right->type == IR::SInt32Type - && visitCJumpSInt32(b->op, b->left, b->right, s->iftrue, s->iffalse)) - return; - - if (b->op == IR::OpStrictEqual || b->op == IR::OpStrictNotEqual) { - visitCJumpStrict(b, s->iftrue, s->iffalse); - return; - } - if (b->op == IR::OpEqual || b->op == IR::OpNotEqual) { - visitCJumpEqual(b, s->iftrue, s->iffalse); - return; - } - - typename JITAssembler::RuntimeCall op; - typename JITAssembler::RuntimeCall opContext; - const char *opName = 0; - bool needsExceptionCheck; - switch (b->op) { - default: Q_UNREACHABLE(); Q_ASSERT(!"todo"); break; - case IR::OpGt: setOp(op, opName, compareGreaterThan); break; - case IR::OpLt: setOp(op, opName, compareLessThan); break; - case IR::OpGe: setOp(op, opName, compareGreaterEqual); break; - case IR::OpLe: setOp(op, opName, compareLessEqual); break; - case IR::OpEqual: setOp(op, opName, compareEqual); break; - case IR::OpNotEqual: setOp(op, opName, compareNotEqual); break; - case IR::OpStrictEqual: setOp(op, opName, compareStrictEqual); break; - case IR::OpStrictNotEqual: setOp(op, opName, compareStrictNotEqual); break; - case IR::OpInstanceof: setOpContext(op, opName, compareInstanceof); break; - case IR::OpIn: setOpContext(op, opName, compareIn); break; - } // switch - - // TODO: in SSA optimization, do constant expression evaluation. - // The case here is, for example: - // if (true === true) ..... - // Of course, after folding the CJUMP to a JUMP, dead-code (dead-basic-block) - // elimination (which isn't there either) would remove the whole else block. - if (opContext.isValid()) - _as->generateFunctionCallImp(needsExceptionCheck, - JITTargetPlatform::ReturnValueRegister, opName, opContext, - JITTargetPlatform::EngineRegister, - PointerToValue(b->left), - PointerToValue(b->right)); - else - _as->generateFunctionCallImp(needsExceptionCheck, - JITTargetPlatform::ReturnValueRegister, opName, op, - PointerToValue(b->left), - PointerToValue(b->right)); - - _as->generateCJumpOnNonZero(JITTargetPlatform::ReturnValueRegister, _block, s->iftrue, s->iffalse); - return; - } - Q_UNREACHABLE(); -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::visitRet(IR::Ret *s) -{ - _as->returnFromFunction(s, regularRegistersToSave, fpRegistersToSave); -} - -template <typename JITAssembler> -int InstructionSelection<JITAssembler>::prepareVariableArguments(IR::ExprList* args) -{ - int argc = 0; - for (IR::ExprList *it = args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = args; it; it = it->next, ++i) { - IR::Expr *arg = it->expr; - Q_ASSERT(arg != 0); - Pointer dst(_as->stackLayout().argumentAddressForCall(i)); - if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister) - _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister, WriteBarrier::NoBarrier); - else - _as->copyValue(dst, arg, WriteBarrier::NoBarrier); - } - - return argc; -} - -template <typename JITAssembler> -int InstructionSelection<JITAssembler>::prepareCallData(IR::ExprList* args, IR::Expr *thisObject) -{ - int argc = 0; - for (IR::ExprList *it = args; it; it = it->next) { - ++argc; - } - - Pointer p = _as->stackLayout().callDataAddress(offsetof(CallData, tag)); - _as->store32(TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer)), p); - p = _as->stackLayout().callDataAddress(offsetof(CallData, argc)); - _as->store32(TrustedImm32(argc), p); - p = _as->stackLayout().callDataAddress(offsetof(CallData, thisObject)); - if (!thisObject) - _as->storeValue(JITAssembler::TargetPrimitive::undefinedValue(), p, WriteBarrier::NoBarrier); - else - _as->copyValue(p, thisObject, WriteBarrier::NoBarrier); - - int i = 0; - for (IR::ExprList *it = args; it; it = it->next, ++i) { - IR::Expr *arg = it->expr; - Q_ASSERT(arg != 0); - Pointer dst(_as->stackLayout().argumentAddressForCall(i)); - if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister) - _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister, WriteBarrier::NoBarrier); - else - _as->copyValue(dst, arg, WriteBarrier::NoBarrier); - } - return argc; -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::calculateRegistersToSave(const RegisterInformation &used) -{ - regularRegistersToSave.clear(); - fpRegistersToSave.clear(); - - for (const RegisterInfo &ri : JITTargetPlatform::getRegisterInfo()) { - if (JITTargetPlatform::gotRegister != -1 && ri.isRegularRegister() && ri.reg<RegisterID>() == JITTargetPlatform::gotRegister) { - regularRegistersToSave.append(ri); - continue; - } - if (ri.isCallerSaved()) - continue; - if (ri.isRegularRegister()) { - if (ri.isPredefined() || used.contains(ri)) - regularRegistersToSave.append(ri); - } else { - Q_ASSERT(ri.isFloatingPoint()); - if (ri.isPredefined() || used.contains(ri)) - fpRegistersToSave.append(ri); - } - } -} - -QT_BEGIN_NAMESPACE -namespace QV4 { -bool operator==(const Primitive &v1, const Primitive &v2) -{ - return v1.rawValue() == v2.rawValue(); -} -} // QV4 namespace -QT_END_NAMESPACE - -template <typename JITAssembler> -bool InstructionSelection<JITAssembler>::visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right, - IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) -{ - if (_as->nextBlock() == iftrue) { - Jump target = _as->branchDouble(true, op, left, right); - _as->addPatch(iffalse, target); - } else { - Jump target = _as->branchDouble(false, op, left, right); - _as->addPatch(iftrue, target); - _as->jumpToBlock(_block, iffalse); - } - return true; -} - -template <typename JITAssembler> -bool InstructionSelection<JITAssembler>::visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right, - IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) -{ - if (_as->nextBlock() == iftrue) { - Jump target = _as->branchInt32(true, op, left, right); - _as->addPatch(iffalse, target); - } else { - Jump target = _as->branchInt32(false, op, left, right); - _as->addPatch(iftrue, target); - _as->jumpToBlock(_block, iffalse); - } - return true; -} - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - Q_ASSERT(binop->op == IR::OpStrictEqual || binop->op == IR::OpStrictNotEqual); - - if (visitCJumpStrictNull(binop, trueBlock, falseBlock)) - return; - if (visitCJumpStrictUndefined(binop, trueBlock, falseBlock)) - return; - if (visitCJumpStrictBool(binop, trueBlock, falseBlock)) - return; - - IR::Expr *left = binop->left; - IR::Expr *right = binop->right; - - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, compareStrictEqual, - PointerToValue(left), PointerToValue(right)); - _as->generateCJumpOnCompare(binop->op == IR::OpStrictEqual ? RelationalCondition::NotEqual : RelationalCondition::Equal, - JITTargetPlatform::ReturnValueRegister, TrustedImm32(0), - _block, trueBlock, falseBlock); -} - -// Only load the non-null temp. -template <typename JITAssembler> -bool InstructionSelection<JITAssembler>::visitCJumpStrictNull(IR::Binop *binop, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - IR::Expr *varSrc = 0; - if (binop->left->type == IR::VarType && binop->right->type == IR::NullType) - varSrc = binop->left; - else if (binop->left->type == IR::NullType && binop->right->type == IR::VarType) - varSrc = binop->right; - if (!varSrc) - return false; - - if (varSrc->asTemp() && varSrc->asTemp()->kind == IR::Temp::PhysicalRegister) { - _as->jumpToBlock(_block, falseBlock); - return true; - } - - if (IR::Const *c = varSrc->asConst()) { - if (c->type == IR::NullType) - _as->jumpToBlock(_block, trueBlock); - else - _as->jumpToBlock(_block, falseBlock); - return true; - } - - Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, varSrc); - tagAddr.offset += 4; - const RegisterID tagReg = JITTargetPlatform::ScratchRegister; - _as->load32(tagAddr, tagReg); - - RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal - : RelationalCondition::NotEqual; - const TrustedImm32 tag{quint32(JITAssembler::ValueTypeInternal::Null)}; - _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); - return true; -} - -template <typename JITAssembler> -bool InstructionSelection<JITAssembler>::visitCJumpStrictUndefined(IR::Binop *binop, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - IR::Expr *varSrc = 0; - if (binop->left->type == IR::VarType && binop->right->type == IR::UndefinedType) - varSrc = binop->left; - else if (binop->left->type == IR::UndefinedType && binop->right->type == IR::VarType) - varSrc = binop->right; - if (!varSrc) - return false; - - if (varSrc->asTemp() && varSrc->asTemp()->kind == IR::Temp::PhysicalRegister) { - _as->jumpToBlock(_block, falseBlock); - return true; - } - - if (IR::Const *c = varSrc->asConst()) { - if (c->type == IR::UndefinedType) - _as->jumpToBlock(_block, trueBlock); - else - _as->jumpToBlock(_block, falseBlock); - return true; - } - - RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal - : RelationalCondition::NotEqual; - const RegisterID tagReg = JITTargetPlatform::ReturnValueRegister; - _as->generateCJumpOnUndefined(cond, varSrc, JITTargetPlatform::ScratchRegister, tagReg, _block, trueBlock, falseBlock); - return true; -} - -template <typename JITAssembler> -bool InstructionSelection<JITAssembler>::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - IR::Expr *boolSrc = 0, *otherSrc = 0; - if (binop->left->type == IR::BoolType) { - boolSrc = binop->left; - otherSrc = binop->right; - } else if (binop->right->type == IR::BoolType) { - boolSrc = binop->right; - otherSrc = binop->left; - } else { - // neither operands are statically typed as bool, so bail out. - return false; - } - if (otherSrc->type == IR::UnknownType) { - // Ok, we really need to call into the runtime. - // (This case doesn't happen when the optimizer ran, because everything will be typed (yes, - // possibly as "var" meaning anything), but it does happen for $0===true, which is generated - // for things where the optimizer didn't run (like functions with a try block).) - return false; - } - - RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal - : RelationalCondition::NotEqual; - - if (otherSrc->type == IR::BoolType) { // both are boolean - RegisterID one = _as->toBoolRegister(boolSrc, JITTargetPlatform::ReturnValueRegister); - RegisterID two = _as->toBoolRegister(otherSrc, JITTargetPlatform::ScratchRegister); - _as->generateCJumpOnCompare(cond, one, two, _block, trueBlock, falseBlock); - return true; - } - - if (otherSrc->type != IR::VarType) { - _as->jumpToBlock(_block, falseBlock); - return true; - } - - Pointer otherAddr = _as->loadAddressForReading(JITTargetPlatform::ReturnValueRegister, otherSrc); - otherAddr.offset += 4; // tag address - - // check if the tag of the var operand is indicates 'boolean' - _as->load32(otherAddr, JITTargetPlatform::ScratchRegister); - Jump noBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, - TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean))); - if (binop->op == IR::OpStrictEqual) - _as->addPatch(falseBlock, noBool); - else - _as->addPatch(trueBlock, noBool); - - // ok, both are boolean, so let's load them and compare them. - otherAddr.offset -= 4; // int_32 address - _as->load32(otherAddr, JITTargetPlatform::ReturnValueRegister); - RegisterID boolReg = _as->toBoolRegister(boolSrc, JITTargetPlatform::ScratchRegister); - _as->generateCJumpOnCompare(cond, boolReg, JITTargetPlatform::ReturnValueRegister, _block, trueBlock, - falseBlock); - return true; -} - -template <typename JITAssembler> -bool InstructionSelection<JITAssembler>::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - Q_ASSERT(nullOrUndef == IR::NullType || nullOrUndef == IR::UndefinedType); - - IR::Expr *varSrc = 0; - if (binop->left->type == IR::VarType && binop->right->type == nullOrUndef) - varSrc = binop->left; - else if (binop->left->type == nullOrUndef && binop->right->type == IR::VarType) - varSrc = binop->right; - if (!varSrc) - return false; - - if (varSrc->asTemp() && varSrc->asTemp()->kind == IR::Temp::PhysicalRegister) { - _as->jumpToBlock(_block, falseBlock); - return true; - } - - if (IR::Const *c = varSrc->asConst()) { - if (c->type == nullOrUndef) - _as->jumpToBlock(_block, trueBlock); - else - _as->jumpToBlock(_block, falseBlock); - return true; - } - - Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, varSrc); - tagAddr.offset += 4; - const RegisterID tagReg = JITTargetPlatform::ReturnValueRegister; - _as->load32(tagAddr, tagReg); - - if (binop->op == IR::OpNotEqual) - qSwap(trueBlock, falseBlock); - Jump isNull = _as->branch32(RelationalCondition::Equal, tagReg, TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Null))); - Jump isNotUndefinedTag = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(int(QV4::Value::Managed_Type_Internal))); - tagAddr.offset -= 4; - _as->load32(tagAddr, tagReg); - Jump isNotUndefinedValue = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(0)); - _as->addPatch(trueBlock, isNull); - _as->addPatch(falseBlock, isNotUndefinedTag); - _as->addPatch(falseBlock, isNotUndefinedValue); - _as->jumpToBlock(_block, trueBlock); - - return true; -} - - -template <typename JITAssembler> -void InstructionSelection<JITAssembler>::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - Q_ASSERT(binop->op == IR::OpEqual || binop->op == IR::OpNotEqual); - - if (visitCJumpNullUndefined(IR::NullType, binop, trueBlock, falseBlock)) - return; - - IR::Expr *left = binop->left; - IR::Expr *right = binop->right; - - generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, compareEqual, - PointerToValue(left), PointerToValue(right)); - _as->generateCJumpOnCompare(binop->op == IR::OpEqual ? RelationalCondition::NotEqual : RelationalCondition::Equal, - JITTargetPlatform::ReturnValueRegister, TrustedImm32(0), - _block, trueBlock, falseBlock); -} - -template <typename JITAssembler> -QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory<JITAssembler>::createUnitForLoading() -{ - QQmlRefPointer<CompiledData::CompilationUnit> result; - result.adopt(new JIT::CompilationUnit); - return result; -} - -#endif // ENABLE(ASSEMBLER) - -QT_BEGIN_NAMESPACE -namespace QV4 { namespace JIT { -#if ENABLE(ASSEMBLER) -template class Q_QML_EXPORT InstructionSelection<>; -template class Q_QML_EXPORT ISelFactory<>; -#endif - -#if defined(V4_BOOTSTRAP) - -Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &architecture) -{ - Q_UNUSED(architecture) -#if ENABLE(ASSEMBLER) - using ARMv7CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>; - using ARM64CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>; - - if (architecture == QLatin1String("arm")) - return new ISelFactory<ARMv7CrossAssembler>; - else if (architecture == QLatin1String("arm64")) - return new ISelFactory<ARM64CrossAssembler>; - - QString hostArch; -#if CPU(ARM_THUMB2) - hostArch = QStringLiteral("arm"); -#elif CPU(MIPS) - hostArch = QStringLiteral("mips"); -#elif CPU(X86) - hostArch = QStringLiteral("i386"); -#elif CPU(X86_64) - hostArch = QStringLiteral("x86_64"); -#endif - if (!hostArch.isEmpty() && architecture == hostArch) - return new ISelFactory<>; -#endif // ENABLE(ASSEMBLER) - - return nullptr; -} - -#endif -} } -QT_END_NAMESPACE - diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h deleted file mode 100644 index 7019a117a2..0000000000 --- a/src/qml/jit/qv4isel_masm_p.h +++ /dev/null @@ -1,319 +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_MASM_P_H -#define QV4ISEL_MASM_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/qv4jsir_p.h" -#include "private/qv4isel_p.h" -#include "private/qv4isel_util_p.h" -#include "private/qv4util_p.h" -#include "private/qv4value_p.h" -#include "private/qv4lookup_p.h" - -#include <QtCore/QHash> -#include <QtCore/QStack> -#include <config.h> -#include <wtf/Vector.h> - -#include "qv4assembler_p.h" - -#if ENABLE(ASSEMBLER) - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace JIT { - -template <typename JITAssembler = Assembler<DefaultAssemblerTargetConfiguration>> -class Q_QML_EXPORT InstructionSelection: - protected 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<QV4::CompiledData::CompilationUnit> backendCompileStep() 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 loadThisObject(IR::Expr *temp) override; - void loadQmlContext(IR::Expr *target) override; - void loadQmlImportedScripts(IR::Expr *target) override; - void loadQmlSingleton(const QString &name, IR::Expr *target) override; - void loadConst(IR::Const *sourceConst, IR::Expr *target) 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 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 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 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 *sourceTemp, IR::Expr *target) override; - void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) override; - - using Address = typename JITAssembler::Address; - using Pointer = typename JITAssembler::Pointer; - using PointerToValue = typename JITAssembler::PointerToValue; - using RegisterID = typename JITAssembler::RegisterID; - using FPRegisterID = typename JITAssembler::FPRegisterID; - using ResultCondition = typename JITAssembler::ResultCondition; - using TrustedImm32 = typename JITAssembler::TrustedImm32; - using TrustedImm64 = typename JITAssembler::TrustedImm64; - using Label = typename JITAssembler::Label; - using Jump = typename JITAssembler::Jump; - using StringToIndex = typename JITAssembler::StringToIndex; - using Reference = typename JITAssembler::Reference; - using RelationalCondition = typename JITAssembler::RelationalCondition; - using BranchTruncateType = typename JITAssembler::BranchTruncateType; - using RuntimeCall = typename JITAssembler::RuntimeCall; - - using JITTargetPlatform = typename JITAssembler::JITTargetPlatform; - - Address addressForArgument(int index) const - { - // FramePointerRegister points to its old value on the stack, and above - // it we have the return address, hence the need to step over two - // values before reaching the first argument. - return Address(JITTargetPlatform::FramePointerRegister, (index + 2) * JITTargetPlatform::RegisterSize); - } - - Pointer baseAddressForCallArguments() - { - return _as->stackLayout().argumentAddressForCall(0); - } - - Pointer baseAddressForCallData() - { - return _as->stackLayout().callDataAddress(); - } - - 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 visitJump(IR::Jump *) override; - void visitCJump(IR::CJump *) override; - void visitRet(IR::Ret *) override; - - bool visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right, - IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); - bool visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right, - IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); - void visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - bool visitCJumpStrictNull(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - bool visitCJumpStrictUndefined(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - bool visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - bool visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, - IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - void visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - -private: - void convertTypeSlowPath(IR::Expr *source, IR::Expr *target); - void convertTypeToDouble(IR::Expr *source, IR::Expr *target); - void convertTypeToBool(IR::Expr *source, IR::Expr *target); - void convertTypeToSInt32(IR::Expr *source, IR::Expr *target); - void convertTypeToUInt32(IR::Expr *source, IR::Expr *target); - - void convertIntToDouble(IR::Expr *source, IR::Expr *target) - { - if (IR::Temp *targetTemp = target->asTemp()) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { - if (IR::Temp *sourceTemp = source->asTemp()) { - if (sourceTemp->kind == IR::Temp::PhysicalRegister) { - _as->convertInt32ToDouble((RegisterID) sourceTemp->index, - (FPRegisterID) targetTemp->index); - } else { - _as->convertInt32ToDouble(_as->loadAddressForReading(JITTargetPlatform::ReturnValueRegister, sourceTemp), - (FPRegisterID) targetTemp->index); - } - } else { - _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister), - (FPRegisterID) targetTemp->index); - } - - return; - } - } - - _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister), - JITTargetPlatform::FPGpr0); - _as->storeDouble(JITTargetPlatform::FPGpr0, target); - } - - void convertUIntToDouble(IR::Expr *source, IR::Expr *target) - { - RegisterID tmpReg = JITTargetPlatform::ScratchRegister; - RegisterID reg = _as->toInt32Register(source, tmpReg); - - if (IR::Temp *targetTemp = target->asTemp()) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { - _as->convertUInt32ToDouble(reg, (FPRegisterID) targetTemp->index, tmpReg); - return; - } - } - - _as->convertUInt32ToDouble(_as->toUInt32Register(source, tmpReg), - JITTargetPlatform::FPGpr0, tmpReg); - _as->storeDouble(JITTargetPlatform::FPGpr0, target); - } - - void convertIntToBool(IR::Expr *source, IR::Expr *target) - { - RegisterID reg = JITTargetPlatform::ScratchRegister; - - if (IR::Temp *targetTemp = target->asTemp()) - if (targetTemp->kind == IR::Temp::PhysicalRegister) - reg = (RegisterID) targetTemp->index; - _as->move(_as->toInt32Register(source, reg), reg); - _as->compare32(RelationalCondition::NotEqual, reg, TrustedImm32(0), reg); - _as->storeBool(reg, target); - } - - int prepareVariableArguments(IR::ExprList* args); - int prepareCallData(IR::ExprList* args, IR::Expr *thisObject); - - void calculateRegistersToSave(const RegisterInformation &used); - - template <typename Retval, typename Arg1, typename Arg2, typename Arg3, typename Arg4> - void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - // Note: using the return value register is intentional: for ABIs where the first parameter - // goes into the same register as the return value (currently only ARM), the prepareCall - // will combine loading the looupAddr into the register and calculating the indirect call - // address. - Pointer lookupAddr(JITTargetPlatform::ReturnValueRegister, index * sizeof(QV4::Lookup)); - - _as->generateFunctionCallImp(true, retval, "lookup getter/setter", - typename JITAssembler::LookupCall(lookupAddr, getterSetterOffset), lookupAddr, - arg1, arg2, arg3, arg4); - } - - template <typename Retval, typename Arg1, typename Arg2> - void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2) - { - generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, typename JITAssembler::VoidType()); - } - - template <typename Retval, typename Arg1, typename Arg2, typename Arg3> - void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3) - { - generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, arg3, typename JITAssembler::VoidType()); - } - - IR::BasicBlock *_block; - BitVector _removableJumps; - JITAssembler* _as; - - QScopedPointer<CompilationUnit> compilationUnit; - QQmlEnginePrivate *qmlEngine; - RegisterInformation regularRegistersToSave; - RegisterInformation fpRegistersToSave; -}; - -template <typename JITAssembler = Assembler<DefaultAssemblerTargetConfiguration>> -class Q_QML_EXPORT ISelFactory: public EvalISelFactory -{ -public: - ISelFactory() : EvalISelFactory(QStringLiteral("jit")) {} - 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<JITAssembler>(qmlEngine, execAllocator, module, jsGenerator, this); } - bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL - { return true; } - QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE Q_DECL_FINAL; -}; - -} // end of namespace JIT -} // end of namespace QV4 - -QT_END_NAMESPACE - -#endif // ENABLE(ASSEMBLER) - -#endif // QV4ISEL_MASM_P_H diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp deleted file mode 100644 index d418b050c4..0000000000 --- a/src/qml/jit/qv4regalloc.cpp +++ /dev/null @@ -1,1971 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the V4VM 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/QBuffer> -#include <QtCore/QDebug> -#include "qv4regalloc_p.h" -#include "qv4alloca_p.h" -#include <private/qv4value_p.h> - -#include <algorithm> -#if defined(Q_CC_MINGW) -# include <malloc.h> -#endif - -namespace { -enum { DebugRegAlloc = 0 }; - -struct Use { - enum RegisterFlag { MustHaveRegister = 0, CouldHaveRegister = 1 }; - unsigned flag : 1; - unsigned pos : 31; - - Use(): flag(MustHaveRegister), pos(0) {} - Use(int position, RegisterFlag flag): flag(flag), pos(position) - { Q_ASSERT(position >= 0); } - - bool mustHaveRegister() const { return flag == MustHaveRegister; } -}; -} - -QT_BEGIN_NAMESPACE - -Q_DECLARE_TYPEINFO(Use, Q_MOVABLE_TYPE); - -using namespace QV4::IR; - -namespace QV4 { -namespace JIT { - -namespace { -class IRPrinterWithPositions: public IRPrinter -{ - LifeTimeIntervals::Ptr intervals; - const int positionSize; - -public: - IRPrinterWithPositions(QTextStream *out, const LifeTimeIntervals::Ptr &intervals) - : IRPrinter(out) - , intervals(intervals) - , positionSize(QString::number(intervals->lastPosition()).size()) - {} - -protected: - void addStmtNr(Stmt *s) Q_DECL_OVERRIDE Q_DECL_FINAL - { - addJustifiedNr(intervals->positionForStatement(s)); - } -}; - -class IRPrinterWithRegisters: public IRPrinterWithPositions -{ - const RegisterInformation &_registerInformation; - QHash<int, const RegisterInfo *> _infoForRegularRegister; - QHash<int, const RegisterInfo *> _infoForFPRegister; - -public: - IRPrinterWithRegisters(QTextStream *out, const LifeTimeIntervals::Ptr &intervals, - const RegisterInformation ®isterInformation) - : IRPrinterWithPositions(out, intervals) - , _registerInformation(registerInformation) - { - for (int i = 0, ei = _registerInformation.size(); i != ei; ++i) - if (_registerInformation.at(i).isRegularRegister()) - _infoForRegularRegister.insert(_registerInformation.at(i).reg<int>(), - &_registerInformation.at(i)); - else - _infoForFPRegister.insert(_registerInformation.at(i).reg<int>(), - &_registerInformation.at(i)); - } - -protected: - void visitTemp(Temp *e) Q_DECL_OVERRIDE Q_DECL_FINAL - { - switch (e->kind) { - case Temp::PhysicalRegister: { - const RegisterInfo *ri = e->type == DoubleType ? _infoForFPRegister.value(e->index, 0) - : _infoForRegularRegister.value(e->index, 0); - if (ri) { - *out << ri->prettyName(); - break; - } - Q_FALLTHROUGH(); - } - default: - IRPrinterWithPositions::visitTemp(e); - } - } -}; -} - -class RegAllocInfo: public IRDecoder -{ -public: - typedef QVarLengthArray<Temp, 4> Hints; - -private: - struct Def { - unsigned valid : 1; - unsigned canHaveReg : 1; - unsigned isPhiTarget : 1; - - Def(): valid(0), canHaveReg(0), isPhiTarget(0) {} - Def(bool canHaveReg, bool isPhiTarget) - : valid(1), canHaveReg(canHaveReg), isPhiTarget(isPhiTarget) - { - } - - bool isValid() const { return valid != 0; } - }; - - IR::LifeTimeIntervals::Ptr _lifeTimeIntervals; - BasicBlock *_currentBB; - Stmt *_currentStmt; - std::vector<Def> _defs; - std::vector<std::vector<Use> > _uses; - std::vector<int> _calls; - std::vector<Hints> _hints; - - int usePosition(Stmt *s) const - { - int usePos = _lifeTimeIntervals->positionForStatement(s); - if (usePos == Stmt::InvalidId) // phi-node operand, so: - usePos = _lifeTimeIntervals->startPosition(_currentBB); - return usePos; - } - -public: - RegAllocInfo(): _currentBB(0), _currentStmt(0) {} - - void collect(IR::Function *function, const IR::LifeTimeIntervals::Ptr &lifeTimeIntervals) - { - _lifeTimeIntervals = lifeTimeIntervals; - _defs.resize(function->tempCount); - _uses.resize(function->tempCount); - _calls.reserve(function->statementCount() / 3); - _hints.resize(function->tempCount); - - for (BasicBlock *bb : function->basicBlocks()) { - _currentBB = bb; - for (Stmt *s : bb->statements()) { - _currentStmt = s; - visit(s); - } - } - } - - const std::vector<Use> &uses(const Temp &t) const - { - return _uses.at(t.index); - } - - bool canHaveRegister(const Temp &t) const { - Q_ASSERT(_defs[t.index].isValid()); - return _defs[t.index].canHaveReg; - } - bool isPhiTarget(const Temp &t) const { - Q_ASSERT(_defs[t.index].isValid()); - return _defs[t.index].isPhiTarget; - } - - const std::vector<int> &calls() const { return _calls; } - const Hints &hints(const Temp &t) const { return _hints[t.index]; } - void addHint(const Temp &t, int physicalRegister) - { addHint(t, Temp::PhysicalRegister, physicalRegister); } - - void addHint(const Temp &t, Temp::Kind kind, int hintedIndex) - { - Hints &hints = _hints[t.index]; - for (Hints::iterator i = hints.begin(), ei = hints.end(); i != ei; ++i) - if (i->index == hintedIndex) - return; - - Temp hint; - hint.init(kind, hintedIndex); - hints.append(hint); - } - - void dump() const - { - if (!DebugRegAlloc) - return; - - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinterWithPositions printer(&qout, _lifeTimeIntervals); - - qout << "RegAllocInfo:" << endl << "Defs/uses:" << endl; - for (unsigned t = 0; t < _defs.size(); ++t) { - const std::vector<Use> &uses = _uses[t]; - if (uses.empty()) - continue; - qout << "%" << t <<": " - << " (" - << (_defs[t].canHaveReg ? "can" : "can NOT") - << " have a register, and " - << (_defs[t].isPhiTarget ? "is" : "is NOT") - << " defined by a phi node), uses at: "; - for (unsigned i = 0; i < uses.size(); ++i) { - if (i > 0) qout << ", "; - qout << uses[i].pos; - if (uses[i].mustHaveRegister()) qout << "(R)"; else qout << "(S)"; - } - qout << endl; - } - - qout << "Calls at: "; - for (unsigned i = 0; i < _calls.size(); ++i) { - if (i > 0) qout << ", "; - qout << _calls[i]; - } - qout << endl; - - qout << "Hints:" << endl; - for (unsigned t = 0; t < _hints.size(); ++t) { - if (_uses[t].empty()) - continue; - qout << "\t%" << t << ": "; - const Hints &hints = _hints[t]; - for (int i = 0; i < hints.size(); ++i) { - if (i > 0) qout << ", "; - printer.print(hints[i]); - } - qout << endl; - } - qDebug("%s", buf.data().constData()); - } - -protected: // IRDecoder - void callBuiltinInvalid(IR::Name *, IR::ExprList *, IR::Expr *) override {} - void callBuiltinTypeofQmlContextProperty(IR::Expr *, IR::Member::MemberKind, int, IR::Expr *) override {} - void callBuiltinTypeofMember(IR::Expr *, const QString &, IR::Expr *) override {} - void callBuiltinTypeofSubscript(IR::Expr *, IR::Expr *, IR::Expr *) override {} - void callBuiltinTypeofName(const QString &, IR::Expr *) override {} - void callBuiltinTypeofValue(IR::Expr *, IR::Expr *) override {} - void callBuiltinDeleteMember(IR::Expr *, const QString &, IR::Expr *) override {} - void callBuiltinDeleteSubscript(IR::Expr *, IR::Expr *, IR::Expr *) override {} - void callBuiltinDeleteName(const QString &, IR::Expr *) override {} - void callBuiltinDeleteValue(IR::Expr *) override {} - void callBuiltinThrow(IR::Expr *) override {} - void callBuiltinReThrow() override {} - void callBuiltinUnwindException(IR::Expr *) override {} - void callBuiltinPushCatchScope(const QString &) override {}; - void callBuiltinForeachIteratorObject(IR::Expr *, IR::Expr *) override {} - virtual void callBuiltinForeachNextProperty(IR::Temp *, IR::Temp *) {} - void callBuiltinForeachNextPropertyname(IR::Expr *, IR::Expr *) override {} - void callBuiltinPushWithScope(IR::Expr *) override {} - void callBuiltinPopScope() override {} - void callBuiltinDeclareVar(bool , const QString &) override {} - void callBuiltinDefineArray(IR::Expr *, IR::ExprList *) override {} - void callBuiltinDefineObjectLiteral(IR::Expr *, int, IR::ExprList *, IR::ExprList *, bool) override {} - void callBuiltinSetupArgumentObject(IR::Expr *) override {} - void callBuiltinConvertThisToObject() override {} - - void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override - { - addDef(result); - if (IR::Temp *tempValue = value->asTemp()) - addUses(tempValue, Use::CouldHaveRegister); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int propertyIndex, - IR::ExprList *args, IR::Expr *result) override - { - Q_UNUSED(propertyIndex) - - addDef(result); - addUses(base->asTemp(), Use::CouldHaveRegister); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, - IR::Expr *result) override - { - Q_UNUSED(name) - - addDef(result); - addUses(base->asTemp(), Use::CouldHaveRegister); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, - IR::Expr *result) override - { - addDef(result); - addUses(base->asTemp(), Use::CouldHaveRegister); - addUses(index->asTemp(), Use::CouldHaveRegister); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void convertType(IR::Expr *source, IR::Expr *target) override - { - addDef(target); - - bool needsCall = true; - Use::RegisterFlag sourceReg = Use::CouldHaveRegister; - - switch (target->type) { - case DoubleType: - switch (source->type) { - case UInt32Type: - case SInt32Type: - case NullType: - case UndefinedType: - case BoolType: - needsCall = false; - break; - default: - break; - } - break; - case BoolType: - switch (source->type) { - case UInt32Type: - sourceReg = Use::MustHaveRegister; - needsCall = false; - break; - case DoubleType: - case UndefinedType: - case NullType: - case SInt32Type: - needsCall = false; - break; - default: - break; - } - break; - case SInt32Type: - switch (source->type) { - case UInt32Type: - case NullType: - case UndefinedType: - case BoolType: - needsCall = false; - default: - break; - } - break; - case UInt32Type: - switch (source->type) { - case SInt32Type: - case NullType: - case UndefinedType: - case BoolType: - needsCall = false; - default: - break; - } - break; - default: - break; - } - - Temp *sourceTemp = source->asTemp(); - if (sourceTemp) - addUses(sourceTemp, sourceReg); - - if (needsCall) - addCall(); - else if (target->asTemp()) - addHint(target->asTemp(), sourceTemp); - } - - void constructActivationProperty(IR::Name *, IR::ExprList *args, IR::Expr *result) override - { - addDef(result); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void constructProperty(IR::Expr *base, const QString &, IR::ExprList *args, IR::Expr *result) override - { - addDef(result); - addUses(base, Use::CouldHaveRegister); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override - { - addDef(result); - addUses(value, Use::CouldHaveRegister); - addUses(args, Use::CouldHaveRegister); - addCall(); - } - - void loadThisObject(IR::Expr *temp) override - { - addDef(temp); - } - - void loadQmlContext(IR::Expr *temp) override - { - addDef(temp); - addCall(); - } - - void loadQmlImportedScripts(IR::Expr *temp) override - { - addDef(temp); - addCall(); - } - - void loadQmlSingleton(const QString &/*name*/, Expr *temp) override - { - Q_UNUSED(temp); - - addDef(temp); - addCall(); - } - - void loadConst(IR::Const *sourceConst, Expr *targetTemp) override - { - Q_UNUSED(sourceConst); - - addDef(targetTemp); - } - - void loadString(const QString &str, Expr *targetTemp) override - { - Q_UNUSED(str); - - addDef(targetTemp); - } - - void loadRegexp(IR::RegExp *sourceRegexp, Expr *targetTemp) override - { - Q_UNUSED(sourceRegexp); - - addDef(targetTemp); - addCall(); - } - - void getActivationProperty(const IR::Name *, Expr *temp) override - { - addDef(temp); - addCall(); - } - - void setActivationProperty(IR::Expr *source, const QString &) override - { - addUses(source->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void initClosure(IR::Closure *closure, Expr *target) override - { - Q_UNUSED(closure); - - addDef(target); - addCall(); - } - - void getProperty(IR::Expr *base, const QString &, Expr *target) override - { - addDef(target); - addUses(base->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &) override - { - addUses(source->asTemp(), Use::CouldHaveRegister); - addUses(targetBase->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, - IR::Member::MemberKind /*kind*/, int /*propertyIndex*/) override - { - addUses(source->asTemp(), Use::CouldHaveRegister); - addUses(targetBase->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int /*propertyIndex*/) override - { - addUses(source->asTemp(), Use::CouldHaveRegister); - addUses(targetBase->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int /*index*/, - bool /*captureRequired*/, IR::Expr *target) override - { - addDef(target); - addUses(base->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void getQObjectProperty(IR::Expr *base, int /*propertyIndex*/, bool /*captureRequired*/, - bool /*isSingleton*/, int /*attachedPropertiesId*/, IR::Expr *target) override - { - addDef(target); - addUses(base->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void getElement(IR::Expr *base, IR::Expr *index, Expr *target) override - { - addDef(target); - addUses(base->asTemp(), Use::CouldHaveRegister); - addUses(index->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) override - { - addUses(source->asTemp(), Use::CouldHaveRegister); - addUses(targetBase->asTemp(), Use::CouldHaveRegister); - addUses(targetIndex->asTemp(), Use::CouldHaveRegister); - addCall(); - } - - void copyValue(Expr *source, Expr *target) override - { - addDef(target); - Temp *sourceTemp = source->asTemp(); - if (!sourceTemp) - return; - addUses(sourceTemp, Use::CouldHaveRegister); - Temp *targetTemp = target->asTemp(); - if (targetTemp) - addHint(targetTemp, sourceTemp); - } - - void swapValues(Expr *, Expr *) override - { - // Inserted by the register allocator, so it cannot occur here. - Q_UNREACHABLE(); - } - - void unop(AluOp oper, Expr *source, Expr *target) override - { - addDef(target); - - bool needsCall = true; - if (oper == OpNot && source->type == IR::BoolType && target->type == IR::BoolType) - needsCall = false; - -#if 0 // TODO: change masm to generate code - switch (oper) { - case OpIfTrue: - case OpNot: - case OpUMinus: - case OpUPlus: - case OpCompl: - needsCall = sourceTemp->type & ~NumberType && sourceTemp->type != BoolType; - break; - - case OpIncrement: - case OpDecrement: - default: - Q_UNREACHABLE(); - } -#endif - - IR::Temp *sourceTemp = source->asTemp(); - if (needsCall) { - if (sourceTemp) - addUses(sourceTemp, Use::CouldHaveRegister); - addCall(); - } else { - if (sourceTemp) - addUses(sourceTemp, Use::MustHaveRegister); - } - } - - void binop(AluOp oper, Expr *leftSource, Expr *rightSource, Expr *target) override - { - bool needsCall = true; - - if (oper == OpStrictEqual || oper == OpStrictNotEqual) { - bool noCall = leftSource->type == NullType || rightSource->type == NullType - || leftSource->type == UndefinedType || rightSource->type == UndefinedType - || leftSource->type == BoolType || rightSource->type == BoolType; - needsCall = !noCall; - } else if (leftSource->type == DoubleType && rightSource->type == DoubleType) { - if (oper == OpMul || oper == OpAdd || oper == OpDiv || oper == OpSub - || (oper >= OpGt && oper <= OpStrictNotEqual)) { - needsCall = false; - } - } else if (oper == OpBitAnd || oper == OpBitOr || oper == OpBitXor || oper == OpLShift || oper == OpRShift || oper == OpURShift) { - needsCall = false; - } else if (oper == OpAdd || oper == OpMul || oper == OpSub - || (oper >= OpGt && oper <= OpStrictNotEqual)) { - if (leftSource->type == SInt32Type && rightSource->type == SInt32Type) - needsCall = false; - } - - addDef(target); - - if (needsCall) { - addUses(leftSource->asTemp(), Use::CouldHaveRegister); - addUses(rightSource->asTemp(), Use::CouldHaveRegister); - addCall(); - } else { - addUses(leftSource->asTemp(), Use::MustHaveRegister); - addHint(target, leftSource->asTemp()); - addHint(target, rightSource->asTemp()); - -#if CPU(X86) || CPU(X86_64) - switch (oper) { - // The rhs operand can be a memory address - case OpAdd: - case OpSub: - case OpMul: - case OpDiv: -#if CPU(X86_64) - if (leftSource->type == DoubleType || rightSource->type == DoubleType) { - // well, on 64bit the doubles are mangled, so they must first be loaded in a register and demangled, so...: - addUses(rightSource->asTemp(), Use::MustHaveRegister); - break; - } - Q_FALLTHROUGH(); -#endif - case OpBitAnd: - case OpBitOr: - case OpBitXor: - addUses(rightSource->asTemp(), Use::CouldHaveRegister); - break; - - default: - addUses(rightSource->asTemp(), Use::MustHaveRegister); - break; - } -#else - addUses(rightSource->asTemp(), Use::MustHaveRegister); -#endif - } - } - - void visitJump(IR::Jump *) override {} - void visitCJump(IR::CJump *s) override - { - if (Temp *t = s->cond->asTemp()) { -#if 0 // TODO: change masm to generate code - addUses(t, Use::MustHaveRegister); -#else - addUses(t, Use::CouldHaveRegister); - addCall(); -#endif - } else if (Binop *b = s->cond->asBinop()) { - binop(b->op, b->left, b->right, 0); - } else if (s->cond->asConst()) { - // TODO: SSA optimization for constant condition evaluation should remove this. - // See also visitCJump() in masm. - addCall(); - } else { - Q_UNREACHABLE(); - } - } - - void visitRet(IR::Ret *s) override - { addUses(s->expr->asTemp(), Use::CouldHaveRegister); } - - void visitPhi(IR::Phi *s) override - { - addDef(s->targetTemp, true); - for (int i = 0, ei = s->incoming.size(); i < ei; ++i) { - Expr *e = s->incoming.at(i); - if (Temp *t = e->asTemp()) { - // The actual use of an incoming value in a phi node is right before the terminator - // of the other side of the incoming edge. - const int usePos = _lifeTimeIntervals->positionForStatement(_currentBB->in.at(i)->terminator()) - 1; - addUses(t, Use::CouldHaveRegister, usePos); - addHint(s->targetTemp, t); - addHint(t, s->targetTemp); - } - } - } - -protected: - void callBuiltin(IR::Call *c, IR::Expr *result) override - { - addDef(result); - addUses(c->base, Use::CouldHaveRegister); - addUses(c->args, Use::CouldHaveRegister); - addCall(); - } - -private: - void addDef(Expr *e, bool isPhiTarget = false) - { - if (!e) - return; - Temp *t = e->asTemp(); - if (!t) - return; - if (!t || t->kind != Temp::VirtualRegister) - return; - Q_ASSERT(!_defs[t->index].isValid()); - bool canHaveReg = true; - switch (t->type) { - case QObjectType: - case VarType: - case StringType: - case UndefinedType: - case NullType: - canHaveReg = false; - break; - default: - break; - } - - _defs[t->index] = Def(canHaveReg, isPhiTarget); - } - - void addUses(Expr *e, Use::RegisterFlag flag) - { - const int usePos = usePosition(_currentStmt); - addUses(e, flag, usePos); - } - - void addUses(Expr *e, Use::RegisterFlag flag, int usePos) - { - Q_ASSERT(usePos > 0); - if (!e) - return; - Temp *t = e->asTemp(); - if (!t) - return; - if (t && t->kind == Temp::VirtualRegister) - _uses[t->index].push_back(Use(usePos, flag)); - } - - void addUses(ExprList *l, Use::RegisterFlag flag) - { - for (ExprList *it = l; it; it = it->next) - addUses(it->expr, flag); - } - - void addCall() - { - _calls.push_back(usePosition(_currentStmt)); - } - - void addHint(Expr *hinted, Temp *hint1, Temp *hint2 = 0) - { - if (hinted) - if (Temp *hintedTemp = hinted->asTemp()) - addHint(hintedTemp, hint1, hint2); - } - - void addHint(Temp *hinted, Temp *hint1, Temp *hint2 = 0) - { - if (!hinted || hinted->kind != Temp::VirtualRegister) - return; - if (hint1 && hint1->kind == Temp::VirtualRegister && hinted->type == hint1->type) - addHint(*hinted, Temp::VirtualRegister, hint1->index); - if (hint2 && hint2->kind == Temp::VirtualRegister && hinted->type == hint2->type) - addHint(*hinted, Temp::VirtualRegister, hint2->index); - } -}; - -} // JIT namespace -} // QV4 namespace -QT_END_NAMESPACE - -QT_USE_NAMESPACE - -using namespace QT_PREPEND_NAMESPACE(QV4::JIT); -using namespace QT_PREPEND_NAMESPACE(QV4::IR); -using namespace QT_PREPEND_NAMESPACE(QV4); - -namespace { -class ResolutionPhase -{ - Q_DISABLE_COPY(ResolutionPhase) - - LifeTimeIntervals::Ptr _intervals; - QVector<LifeTimeInterval *> _unprocessedReverseOrder; - IR::Function *_function; - const std::vector<int> &_assignedSpillSlots; - std::vector<const LifeTimeInterval *> _liveIntervals; - const QVector<const RegisterInfo *> &_intRegs; - const QVector<const RegisterInfo *> &_fpRegs; - - Stmt *_currentStmt; - std::vector<Move *> _loads; - std::vector<Move *> _stores; - - std::vector<std::vector<const LifeTimeInterval *> > _liveAtStart; - std::vector<std::vector<const LifeTimeInterval *> > _liveAtEnd; - -public: - ResolutionPhase(QVector<LifeTimeInterval *> &&unprocessedReversedOrder, - const LifeTimeIntervals::Ptr &intervals, - IR::Function *function, - const std::vector<int> &assignedSpillSlots, - const QVector<const RegisterInfo *> &intRegs, - const QVector<const RegisterInfo *> &fpRegs) - : _intervals(intervals) - , _unprocessedReverseOrder(unprocessedReversedOrder) - , _function(function) - , _assignedSpillSlots(assignedSpillSlots) - , _intRegs(intRegs) - , _fpRegs(fpRegs) - , _currentStmt(0) - { - _liveAtStart.resize(function->basicBlockCount()); - _liveAtEnd.resize(function->basicBlockCount()); - } - - void run() { - renumber(); - if (DebugRegAlloc) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinterWithPositions(&qout, _intervals).print(_function); - qDebug("%s", buf.data().constData()); - } - resolve(); - } - -private: - int defPosition(Stmt *s) const - { - return usePosition(s) + 1; - } - - int usePosition(Stmt *s) const - { - return _intervals->positionForStatement(s); - } - - void renumber() - { - QVector<Stmt *> newStatements; - - for (BasicBlock *bb : _function->basicBlocks()) { - _currentStmt = 0; - - QVector<Stmt *> statements = bb->statements(); - newStatements.reserve(bb->statements().size() + 7); - newStatements.erase(newStatements.begin(), newStatements.end()); - - cleanOldIntervals(_intervals->startPosition(bb)); - addNewIntervals(_intervals->startPosition(bb)); - _liveAtStart[bb->index()] = _liveIntervals; - - for (int i = 0, ei = statements.size(); i != ei; ++i) { - _currentStmt = statements.at(i); - _loads.clear(); - _stores.clear(); - if (_currentStmt->asTerminator()) - addNewIntervals(usePosition(_currentStmt)); - else - addNewIntervals(defPosition(_currentStmt)); - visit(_currentStmt); - for (Move *load : _loads) - newStatements.append(load); - if (_currentStmt->asPhi()) - newStatements.prepend(_currentStmt); - else - newStatements.append(_currentStmt); - for (Move *store : _stores) - newStatements.append(store); - } - - cleanOldIntervals(_intervals->endPosition(bb)); - _liveAtEnd[bb->index()] = _liveIntervals; - - if (DebugRegAlloc) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream os(&buf); - os << "Intervals live at the start of L" << bb->index() << ":" << endl; - if (_liveAtStart[bb->index()].empty()) - os << "\t(none)" << endl; - for (const LifeTimeInterval *i : _liveAtStart.at(bb->index())) { - os << "\t"; - i->dump(os); - os << endl; - } - os << "Intervals live at the end of L" << bb->index() << ":" << endl; - if (_liveAtEnd[bb->index()].empty()) - os << "\t(none)" << endl; - for (const LifeTimeInterval *i : _liveAtEnd.at(bb->index())) { - os << "\t"; - i->dump(os); - os << endl; - } - qDebug("%s", buf.data().constData()); - } - - bb->setStatements(newStatements); - } - - } - - const LifeTimeInterval *findLiveInterval(Temp *t) const - { - for (const LifeTimeInterval *lti : _liveIntervals) { - if (lti->temp() == *t) - return lti; - } - - return nullptr; - } - - void maybeGenerateSpill(Temp *t) - { - const LifeTimeInterval *i = findLiveInterval(t); - if (i->reg() == LifeTimeInterval::InvalidRegister) - return; - - const RegisterInfo *pReg = platformRegister(*i); - Q_ASSERT(pReg); - int spillSlot = _assignedSpillSlots[i->temp().index]; - if (spillSlot != RegisterAllocator::InvalidSpillSlot) - _stores.push_back(generateSpill(spillSlot, i->temp().type, pReg->reg<int>())); - } - - void addNewIntervals(int position) - { - if (position == Stmt::InvalidId) - return; - - while (!_unprocessedReverseOrder.isEmpty()) { - const LifeTimeInterval *i = _unprocessedReverseOrder.constLast(); - if (i->start() > position) - break; - - Q_ASSERT(!i->isFixedInterval()); - auto it = _liveIntervals.begin(); - for (; it != _liveIntervals.end(); ++it) { - if ((*it)->temp() == i->temp()) { - *it = i; - break; - } - } - if (it == _liveIntervals.end()) - _liveIntervals.push_back(i); -// qDebug() << "-- Activating interval for temp" << i->temp().index; - - _unprocessedReverseOrder.removeLast(); - } - } - - void cleanOldIntervals(int position) - { - for (size_t it = 0; it != _liveIntervals.size(); ) { - const LifeTimeInterval *lti = _liveIntervals.at(it); - if (lti->end() < position || lti->isFixedInterval()) - _liveIntervals.erase(_liveIntervals.begin() + it); - else - ++it; - } - } - - void resolve() - { - for (BasicBlock *bb : _function->basicBlocks()) { - for (BasicBlock *bbOut : bb->out) - resolveEdge(bb, bbOut); - } - } - - Phi *findDefPhi(const Temp &t, BasicBlock *bb) const - { - for (Stmt *s : bb->statements()) { - Phi *phi = s->asPhi(); - if (!phi) - return 0; - - if (*phi->targetTemp == t) - return phi; - } - - Q_UNREACHABLE(); - } - - void resolveEdge(BasicBlock *predecessor, BasicBlock *successor) - { - if (DebugRegAlloc) { - qDebug() << "Resolving edge" << predecessor->index() << "->" << successor->index(); - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinterWithPositions printer(&qout, _intervals); - printer.print(predecessor); - printer.print(successor); - qDebug("%s", buf.data().constData()); - } - - MoveMapping mapping; - - const int predecessorEnd = _intervals->endPosition(predecessor); - Q_ASSERT(predecessorEnd > 0); - - int successorStart = _intervals->startPosition(successor); - Q_ASSERT(successorStart > 0); - - for (const LifeTimeInterval *it : _liveAtStart.at(successor->index())) { - bool isPhiTarget = false; - Expr *moveFrom = 0; - - if (it->start() == successorStart) { - if (Phi *phi = findDefPhi(it->temp(), successor)) { - isPhiTarget = true; - Expr *opd = phi->incoming[successor->in.indexOf(predecessor)]; - if (opd->asConst()) { - moveFrom = opd; - } else { - Temp *t = opd->asTemp(); - Q_ASSERT(t); - - for (const LifeTimeInterval *it2 : _liveAtEnd.at(predecessor->index())) { - if (it2->temp() == *t - && it2->reg() != LifeTimeInterval::InvalidRegister - && it2->covers(predecessorEnd)) { - moveFrom = createPhysicalRegister(it2, t->type); - break; - } - } - if (!moveFrom) - moveFrom = createTemp(Temp::StackSlot, - _assignedSpillSlots[t->index], - t->type); - } - } - } else { - for (const LifeTimeInterval *predIt : _liveAtEnd.at(predecessor->index())) { - if (predIt->temp() == it->temp()) { - if (predIt->reg() != LifeTimeInterval::InvalidRegister - && predIt->covers(predecessorEnd)) { - moveFrom = createPhysicalRegister(predIt, predIt->temp().type); - } else { - int spillSlot = _assignedSpillSlots[predIt->temp().index]; - if (spillSlot != -1) - moveFrom = createTemp(Temp::StackSlot, spillSlot, predIt->temp().type); - } - break; - } - } - } - if (!moveFrom) { -#if !defined(QT_NO_DEBUG) && 0 - bool lifeTimeHole = false; - if (it->ranges().first().start <= successorStart && it->ranges().last().end >= successorStart) - lifeTimeHole = !it->covers(successorStart); - - Q_ASSERT(!_info->isPhiTarget(it->temp()) || it->isSplitFromInterval() || lifeTimeHole); - if (_info->def(it->temp()) != successorStart && !it->isSplitFromInterval()) { - const int successorEnd = successor->terminator()->id(); - const int idx = successor->in.indexOf(predecessor); - for (const Use &use : _info->uses(it->temp)) { - if (use.pos == static_cast<unsigned>(successorStart)) { - // only check the current edge, not all other possible ones. This is - // important for phi nodes: they have uses that are only valid when - // coming in over a specific edge. - for (Stmt *s : successor->statements()) { - if (Phi *phi = s->asPhi()) { - Q_ASSERT(it->temp().index != phi->targetTemp->index); - Q_ASSERT(phi->d->incoming[idx]->asTemp() == 0 - || it->temp().index != phi->d->incoming[idx]->asTemp()->index); - } else { - // TODO: check that the first non-phi statement does not use - // the temp. - break; - } - } - } else { - Q_ASSERT(use.pos < static_cast<unsigned>(successorStart) || - use.pos > static_cast<unsigned>(successorEnd)); - } - } - } -#endif - - continue; - } - - Temp *moveTo; - if (it->reg() == LifeTimeInterval::InvalidRegister || !it->covers(successorStart)) { - if (!isPhiTarget) // if it->temp() is a phi target, skip it. - continue; - const int spillSlot = _assignedSpillSlots[it->temp().index]; - if (spillSlot == RegisterAllocator::InvalidSpillSlot) - continue; // it has a life-time hole here. - moveTo = createTemp(Temp::StackSlot, spillSlot, it->temp().type); - } else { - moveTo = createPhysicalRegister(it, it->temp().type); - } - - // add move to mapping - mapping.add(moveFrom, moveTo); - } - - if (DebugRegAlloc) - mapping.dump(); - mapping.order(); - if (DebugRegAlloc) - mapping.dump(); - - bool insertIntoPredecessor = successor->in.size() > 1; - mapping.insertMoves(insertIntoPredecessor ? predecessor : successor, _function, - insertIntoPredecessor); - - if (DebugRegAlloc) { - qDebug() << ".. done, result:"; - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinterWithPositions printer(&qout, _intervals); - printer.print(predecessor); - printer.print(successor); - qDebug("%s", buf.data().constData()); - } - } - - Temp *createTemp(Temp::Kind kind, int index, Type type) const - { - Q_ASSERT(index >= 0); - Temp *t = _function->New<Temp>(); - t->init(kind, index); - t->type = type; - return t; - } - - Temp *createPhysicalRegister(const LifeTimeInterval *i, Type type) const - { - const RegisterInfo *ri = platformRegister(*i); - Q_ASSERT(ri); - return createTemp(Temp::PhysicalRegister, ri->reg<int>(), type); - } - - const RegisterInfo *platformRegister(const LifeTimeInterval &i) const - { - if (i.isFP()) - return _fpRegs.value(i.reg(), 0); - else - return _intRegs.value(i.reg(), 0); - } - - Move *generateSpill(int spillSlot, Type type, int pReg) const - { - Q_ASSERT(spillSlot >= 0); - - Move *store = _function->NewStmt<Move>(); - store->init(createTemp(Temp::StackSlot, spillSlot, type), - createTemp(Temp::PhysicalRegister, pReg, type)); - return store; - } - - Move *generateUnspill(const Temp &t, int pReg) const - { - Q_ASSERT(pReg >= 0); - int spillSlot = _assignedSpillSlots[t.index]; - Q_ASSERT(spillSlot != -1); - Move *load = _function->NewStmt<Move>(); - load->init(createTemp(Temp::PhysicalRegister, pReg, t.type), - createTemp(Temp::StackSlot, spillSlot, t.type)); - return load; - } - -private: - void visit(Expr *e) - { - switch (e->exprKind) { - case Expr::TempExpr: - visitTemp(e->asTemp()); - break; - default: - EXPR_VISIT_ALL_KINDS(e); - break; - } - } - - void visitTemp(Temp *t) - { - if (t->kind != Temp::VirtualRegister) - return; - - const LifeTimeInterval *i = findLiveInterval(t); - Q_ASSERT(i->isValid()); - - if (_currentStmt != 0 && i->start() == usePosition(_currentStmt)) { - Q_ASSERT(i->isSplitFromInterval()); - const RegisterInfo *pReg = platformRegister(*i); - Q_ASSERT(pReg); - _loads.push_back(generateUnspill(i->temp(), pReg->reg<int>())); - } - - if (i->reg() != LifeTimeInterval::InvalidRegister && - (i->covers(defPosition(_currentStmt)) || - i->covers(usePosition(_currentStmt)))) { - const RegisterInfo *pReg = platformRegister(*i); - Q_ASSERT(pReg); - t->kind = Temp::PhysicalRegister; - t->index = pReg->reg<unsigned>(); - } else { - int stackSlot = _assignedSpillSlots[t->index]; - Q_ASSERT(stackSlot >= 0); - t->kind = Temp::StackSlot; - t->index = stackSlot; - } - } - - void visit(Stmt *s) - { - switch (s->stmtKind) { - case Stmt::MoveStmt: { - auto m = s->asMove(); - if (Temp *t = m->target->asTemp()) - maybeGenerateSpill(t); - - visit(m->source); - visit(m->target); - } break; - case Stmt::PhiStmt: { - auto p = s->asPhi(); - maybeGenerateSpill(p->targetTemp); - } break; - default: - STMT_VISIT_ALL_KINDS(s); - break; - } - } -}; -} // anonymous namespace - -RegisterAllocator::RegisterAllocator(const QV4::JIT::RegisterInformation ®isterInformation) - : _registerInformation(registerInformation) -{ - for (int i = 0, ei = registerInformation.size(); i != ei; ++i) { - const RegisterInfo ®Info = registerInformation.at(i); - if (regInfo.useForRegAlloc()) { - if (regInfo.isRegularRegister()) - _normalRegisters.append(®Info); - else - _fpRegisters.append(®Info); - } - } - Q_ASSERT(_normalRegisters.size() >= 2); - Q_ASSERT(_fpRegisters.size() >= 2); - _active.reserve((_normalRegisters.size() + _fpRegisters.size()) * 2); - _inactive.reserve(_active.size()); - - _regularRegsInUse.resize(_normalRegisters.size()); - _fpRegsInUse.resize(_fpRegisters.size()); -} - -RegisterAllocator::~RegisterAllocator() -{ -} - -void RegisterAllocator::run(IR::Function *function, const Optimizer &opt) -{ - _lastAssignedRegister.assign(function->tempCount, LifeTimeInterval::InvalidRegister); - _assignedSpillSlots.assign(function->tempCount, InvalidSpillSlot); - _activeSpillSlots.resize(function->tempCount); - - if (DebugRegAlloc) - qDebug() << "*** Running regalloc for function" << (function->name ? qPrintable(*function->name) : "NO NAME") << "***"; - - _lifeTimeIntervals = opt.lifeTimeIntervals(); - - _unhandled = _lifeTimeIntervals->intervals(); - _handled.reserve(_unhandled.size()); - - _info.reset(new RegAllocInfo); - _info->collect(function, _lifeTimeIntervals); - - if (DebugRegAlloc) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout << "Ranges:" << endl; - QVector<LifeTimeInterval *> intervals = _unhandled; - std::reverse(intervals.begin(), intervals.end()); - for (const LifeTimeInterval *r : qAsConst(intervals)) { - r->dump(qout); - qout << endl; - } - qDebug("%s", buf.data().constData()); - _info->dump(); - - qDebug() << "*** Before register allocation:"; - buf.setData(QByteArray()); - IRPrinterWithPositions(&qout, _lifeTimeIntervals).print(function); - qDebug("%s", buf.data().constData()); - } - prepareRanges(); - - linearScan(); - - if (DebugRegAlloc) - dump(function); - - // sort the ranges in reverse order, so the ResolutionPhase can take from the end (and thereby - // prevent the copy overhead that taking from the beginning would give). - std::sort(_handled.begin(), _handled.end(), - [](const LifeTimeInterval *r1, const LifeTimeInterval *r2) -> bool { - return LifeTimeInterval::lessThan(r2, r1); - }); - ResolutionPhase(std::move(_handled), _lifeTimeIntervals, function, _assignedSpillSlots, _normalRegisters, _fpRegisters).run(); - - function->tempCount = *std::max_element(_assignedSpillSlots.begin(), _assignedSpillSlots.end()) + 1; - - if (DebugRegAlloc) - qDebug() << "*** Finished regalloc , result:"; - - static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR"); - if (showCode) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinterWithRegisters(&qout, _lifeTimeIntervals, _registerInformation).print(function); - qDebug("%s", buf.data().constData()); - } -} - -RegisterInformation RegisterAllocator::usedRegisters() const -{ - RegisterInformation regInfo; - - for (int i = 0, ei = _normalRegisters.size(); i != ei; ++i) { - if (_regularRegsInUse.testBit(i)) - regInfo.append(*_normalRegisters.at(i)); - } - - for (int i = 0, ei = _fpRegisters.size(); i != ei; ++i) { - if (_fpRegsInUse.testBit(i)) - regInfo.append(*_fpRegisters.at(i)); - } - - return regInfo; -} - -void RegisterAllocator::markInUse(int reg, bool isFPReg) -{ - if (isFPReg) - _fpRegsInUse.setBit(reg); - else - _regularRegsInUse.setBit(reg); -} - -static inline LifeTimeInterval createFixedInterval(int rangeCount) -{ - LifeTimeInterval i(rangeCount); - i.setReg(0); - - Temp t; - t.init(Temp::PhysicalRegister, 0); - t.type = IR::SInt32Type; - i.setTemp(t); - - return i; -} - -LifeTimeInterval *RegisterAllocator::cloneFixedInterval(int reg, bool isFP, const LifeTimeInterval &original) -{ - LifeTimeInterval *lti = new LifeTimeInterval(original); - _lifeTimeIntervals->add(lti); - lti->setReg(reg); - lti->setFixedInterval(true); - - Temp t; - t.init(Temp::PhysicalRegister, reg); - t.type = isFP ? IR::DoubleType : IR::SInt32Type; - lti->setTemp(t); - - return lti; -} - -// Creates the intervals with fixed ranges. See [Wimmer2]. Note that this only applies to callee- -// saved registers. -void RegisterAllocator::prepareRanges() -{ - LifeTimeInterval ltiWithCalls = createFixedInterval(int(_info->calls().size())); - for (int callPosition : _info->calls()) - ltiWithCalls.addRange(callPosition, callPosition); - - const int regCount = _normalRegisters.size(); - _fixedRegisterRanges.resize(regCount); - for (int reg = 0; reg < regCount; ++reg) { - if (_normalRegisters.at(reg)->isCallerSaved()) { - LifeTimeInterval *lti = cloneFixedInterval(reg, false, ltiWithCalls); - if (lti->isValid()) { - _fixedRegisterRanges[reg] = lti; - _active.append(lti); - } - } - } - - const int fpRegCount = _fpRegisters.size(); - _fixedFPRegisterRanges.resize(fpRegCount); - for (int fpReg = 0; fpReg < fpRegCount; ++fpReg) { - if (_fpRegisters.at(fpReg)->isCallerSaved()) { - LifeTimeInterval *lti = cloneFixedInterval(fpReg, true, ltiWithCalls); - if (lti->isValid()) { - _fixedFPRegisterRanges[fpReg] = lti; - _active.append(lti); - } - } - } -} - -void RegisterAllocator::linearScan() -{ - while (!_unhandled.isEmpty()) { - LifeTimeInterval *current = _unhandled.back(); - _unhandled.pop_back(); - const int position = current->start(); - - // check for intervals in active that are handled or inactive - for (int i = 0; i < _active.size(); ) { - LifeTimeInterval *it = _active.at(i); - if (it->end() < position) { - if (!it->isFixedInterval()) - _handled += it; - _active.remove(i); - } else if (!it->covers(position)) { - _inactive += it; - _active.remove(i); - } else { - ++i; - } - } - - // check for intervals in inactive that are handled or active - for (int i = 0; i < _inactive.size(); ) { - LifeTimeInterval *it = _inactive.at(i); - if (it->end() < position) { - if (!it->isFixedInterval()) - _handled += it; - _inactive.remove(i); - } else if (it->covers(position)) { - if (it->reg() != LifeTimeInterval::InvalidRegister) { - _active += it; - _inactive.remove(i); - } else { - // although this interval is now active, it has no register allocated (always - // spilled), so leave it in inactive. - ++i; - } - } else { - ++i; - } - } - - Q_ASSERT(!current->isFixedInterval()); - -#ifdef DEBUG_REGALLOC - qDebug() << "** Position" << position; -#endif // DEBUG_REGALLOC - - if (_info->canHaveRegister(current->temp())) { - tryAllocateFreeReg(*current); - if (current->reg() == LifeTimeInterval::InvalidRegister) - allocateBlockedReg(*current); - if (current->reg() != LifeTimeInterval::InvalidRegister) - _active += current; - } else { - assignSpillSlot(current->temp(), current->start(), current->end()); - _inactive += current; - if (DebugRegAlloc) - qDebug() << "*** allocating stack slot" << _assignedSpillSlots[current->temp().index] - << "for %" << current->temp().index << "as it cannot be loaded in a register"; - } - } - - for (LifeTimeInterval *r : qAsConst(_active)) - if (!r->isFixedInterval()) - _handled.append(r); - _active.clear(); - for (LifeTimeInterval *r : qAsConst(_inactive)) - if (!r->isFixedInterval()) - _handled.append(r); - _inactive.clear(); -} - -static inline int indexOfRangeCoveringPosition(const LifeTimeInterval::Ranges &ranges, int position) -{ - for (int i = 0, ei = ranges.size(); i != ei; ++i) { - if (position <= ranges[i].end) - return i; - } - return -1; -} - -static inline int intersectionPosition(const LifeTimeIntervalRange &one, const LifeTimeIntervalRange &two) -{ - if (one.covers(two.start)) - return two.start; - if (two.covers(one.start)) - return one.start; - return -1; -} - -static inline bool isFP(const Temp &t) -{ return t.type == DoubleType; } - -static inline bool candidateIsBetterFit(int bestSizeSoFar, int idealSize, int candidateSize) -{ - // If the candidateSize is larger than the current we take it only if the current size does not - // yet fit for the whole interval. - if (bestSizeSoFar < candidateSize && bestSizeSoFar < idealSize) - return true; - - // If the candidateSize is smaller we only take it if it still fits the whole interval. - if (bestSizeSoFar > candidateSize && candidateSize >= idealSize) - return true; - - // Other wise: no luck. - return false; -} - -// Out of all available registers (with their next-uses), choose the one that fits the requested -// duration best. This can return a register that is not free for the whole interval, but that's -// fine: we just have to split the current interval. -static void longestAvailableReg(int *nextUses, int nextUseCount, int ®, int &freeUntilPos_reg, int lastUse) -{ - reg = LifeTimeInterval::InvalidRegister; - freeUntilPos_reg = 0; - - for (int candidate = 0, candidateEnd = nextUseCount; candidate != candidateEnd; ++candidate) { - int fp = nextUses[candidate]; - if (candidateIsBetterFit(freeUntilPos_reg, lastUse, fp)) { - reg = candidate; - freeUntilPos_reg = fp; - } - } -} - -#define CALLOC_ON_STACK(ty, ptr, sz, val) \ - Q_ASSERT(sz > 0); \ - Q_ALLOCA_VAR(ty, ptr, sizeof(ty) * (sz)); \ - for (ty *it = ptr, *eit = ptr + (sz); it != eit; ++it) \ - *it = val; - -// Try to allocate a register that's currently free. -void RegisterAllocator::tryAllocateFreeReg(LifeTimeInterval ¤t) -{ - Q_ASSERT(!current.isFixedInterval()); - Q_ASSERT(current.reg() == LifeTimeInterval::InvalidRegister); - - const bool needsFPReg = isFP(current.temp()); - const int freeUntilPosCount = needsFPReg ? _fpRegisters.size() : _normalRegisters.size(); - CALLOC_ON_STACK(int, freeUntilPos, freeUntilPosCount, INT_MAX); - - for (Intervals::const_iterator i = _active.constBegin(), ei = _active.constEnd(); i != ei; ++i) { - const LifeTimeInterval *it = *i; - if (it->isFP() == needsFPReg) - freeUntilPos[it->reg()] = 0; // mark register as unavailable - } - - for (Intervals::const_iterator i = _inactive.constBegin(), ei = _inactive.constEnd(); i != ei; ++i) { - const LifeTimeInterval *it = *i; - if (it->isFP() != needsFPReg) - continue; // different register type, so not applicable. - if (it->reg() == LifeTimeInterval::InvalidRegister) - continue; // this range does not block a register from being used, as it has no register assigned - - if (current.isSplitFromInterval() || it->isFixedInterval()) { - const int intersectionPos = nextIntersection(current, *it); - if (intersectionPos != -1) - freeUntilPos[it->reg()] = qMin(freeUntilPos[it->reg()], intersectionPos); - } - } - - int reg = LifeTimeInterval::InvalidRegister; - int freeUntilPos_reg = 0; - - const RegAllocInfo::Hints &hints = _info->hints(current.temp()); - for (RegAllocInfo::Hints::const_iterator i = hints.begin(), ei = hints.end(); i != ei; ++i) { - const Temp &hint = *i; - int candidate; - if (hint.kind == Temp::PhysicalRegister) - candidate = hint.index; - else - candidate = _lastAssignedRegister[hint.index]; - - const int end = current.end(); - if (candidate == LifeTimeInterval::InvalidRegister) - continue; // the candidate has no register assigned, so it cannot be (re-)used - if (current.isFP() != isFP(hint)) - continue; // different register type, so not applicable. - - const int fp = freeUntilPos[candidate]; - if (candidateIsBetterFit(freeUntilPos_reg, end, fp)) { - reg = candidate; - freeUntilPos_reg = fp; - } - } - - // None of the hinted registers could fit the interval, so try all registers next. - if (reg == LifeTimeInterval::InvalidRegister) - longestAvailableReg(freeUntilPos, freeUntilPosCount, reg, freeUntilPos_reg, current.end()); - - if (freeUntilPos_reg == 0) { - // no register available without spilling - if (DebugRegAlloc) - qDebug("*** no register available for %u", current.temp().index); - return; - } else if (current.end() < freeUntilPos_reg) { - // register available for the whole interval - if (DebugRegAlloc) - qDebug() << "*** allocating register" << reg << "for the whole interval of %" << current.temp().index; - current.setReg(reg); - _lastAssignedRegister[current.temp().index] = reg; - markInUse(reg, needsFPReg); - } else { - // register available for the first part of the interval - - // TODO: this is slightly inefficient in the following case: - // %1 = something - // some_call(%1) - // %2 = %1 + 1 - // Now %1 will get a register assigned, and will be spilled to the stack immediately. It - // would be better to check if there are actually uses in the range before the split. - - current.setReg(reg); - _lastAssignedRegister[current.temp().index] = reg; - if (DebugRegAlloc) - qDebug() << "*** allocating register" << reg << "for the first part of interval of %" << current.temp().index; - split(current, freeUntilPos_reg, true); - markInUse(reg, needsFPReg); - } -} - -// This gets called when all registers are currently in use. -void RegisterAllocator::allocateBlockedReg(LifeTimeInterval ¤t) -{ - Q_ASSERT(!current.isFixedInterval()); - Q_ASSERT(current.reg() == LifeTimeInterval::InvalidRegister); - const int position = current.start(); - - const bool isPhiTarget = _info->isPhiTarget(current.temp()); - if (isPhiTarget && !current.isSplitFromInterval()) { - // Special case: storing to a phi-node's target will result in a single move. So, if we - // would spill another interval to the stack (that's 1 store), and then do the move for the - // phi target (at least 1 move or a load), that would result in 2 instructions. Instead, we - // force the phi-node's target to go to the stack immediately, which is always a single - // store. - split(current, position + 1, true); - _inactive.append(¤t); - return; - } - - const bool needsFPReg = isFP(current.temp()); - const int nextUsePosCount = needsFPReg ? _fpRegisters.size() : _normalRegisters.size(); - CALLOC_ON_STACK(int, nextUsePos, nextUsePosCount, INT_MAX); - QVector<LifeTimeInterval *> nextUseRangeForReg(nextUsePosCount, 0); - - for (Intervals::const_iterator i = _active.constBegin(), ei = _active.constEnd(); i != ei; ++i) { - LifeTimeInterval &it = **i; - if (it.isFP() != needsFPReg) - continue; // different register type, so not applicable. - - const int nu = it.isFixedInterval() ? 0 : nextUse(it.temp(), current.start()); - if (nu == position) { - nextUsePos[it.reg()] = 0; - } else if (nu != -1 && nu < nextUsePos[it.reg()]) { - nextUsePos[it.reg()] = nu; - nextUseRangeForReg[it.reg()] = ⁢ - } else if (nu == -1 && nextUsePos[it.reg()] == INT_MAX) { - // in a loop, the range can be active, but the result might only be used before the - // current position (e.g. the induction variable being used in the phi node in the loop - // header). So, we can use this register, but we need to remember to split the interval - // in order to have the edge-resolving generate a load at the edge going back to the - // loop header. - nextUseRangeForReg[it.reg()] = ⁢ - } - } - - for (Intervals::const_iterator i = _inactive.constBegin(), ei = _inactive.constEnd(); i != ei; ++i) { - LifeTimeInterval &it = **i; - if (it.isFP() != needsFPReg) - continue; // different register type, so not applicable. - if (it.reg() == LifeTimeInterval::InvalidRegister) - continue; // this range does not block a register from being used, as it has no register assigned - - if (current.isSplitFromInterval() || it.isFixedInterval()) { - if (nextIntersection(current, it) != -1) { - const int nu = nextUse(it.temp(), current.start()); - if (nu != -1 && nu < nextUsePos[it.reg()]) { - nextUsePos[it.reg()] = nu; - nextUseRangeForReg[it.reg()] = ⁢ - } - } - } - } - - int reg, nextUsePos_reg; - longestAvailableReg(nextUsePos, nextUsePosCount, reg, nextUsePos_reg, current.end()); - - Q_ASSERT(current.start() <= nextUsePos_reg); - - // spill interval that currently block reg - if (DebugRegAlloc) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream out(&buf); - out << "*** spilling intervals that block reg " <<reg<< " for interval "; - current.dump(out); - qDebug("%s", buf.data().constData()); - } - current.setReg(reg); - _lastAssignedRegister[current.temp().index] = reg; - LifeTimeInterval *nextUse = nextUseRangeForReg[reg]; - Q_ASSERT(nextUse); - Q_ASSERT(!nextUse->isFixedInterval()); - - split(*nextUse, position, /*skipOptionalRegisterUses =*/ true); - - // We might have chosen a register that is used by a range that has a hole in its life time. - // If that's the case, check if the current interval completely fits in the hole. Or rephrased: - // check if the current interval will use the register after that hole ends (so that range, and - // if so, split that interval so that it gets a new register assigned when it needs one. - splitInactiveAtEndOfLifetimeHole(reg, needsFPReg, position); - - // make sure that current does not intersect with the fixed interval for reg - const LifeTimeInterval *fixedRegRange = needsFPReg ? _fixedFPRegisterRanges.at(reg) - : _fixedRegisterRanges.at(reg); - if (fixedRegRange) { - int ni = nextIntersection(current, *fixedRegRange); - if (ni != -1) { - if (DebugRegAlloc) { - qDebug("***-- current range intersects with a fixed reg use at %d, so splitting it.", ni); - } - // current does overlap with a fixed interval, so split current before that intersection. - split(current, ni, true); - } - } -} - -int RegisterAllocator::nextIntersection(const LifeTimeInterval ¤t, - const LifeTimeInterval &another) const -{ - const LifeTimeInterval::Ranges ¤tRanges = current.ranges(); - int currentIt = 0; - - const LifeTimeInterval::Ranges &anotherRanges = another.ranges(); - const int anotherItStart = indexOfRangeCoveringPosition(anotherRanges, current.start()); - if (anotherItStart == -1) - return -1; - - for (int currentEnd = currentRanges.size(); currentIt < currentEnd; ++currentIt) { - const LifeTimeIntervalRange currentRange = currentRanges.at(currentIt); - for (int anotherIt = anotherItStart, anotherEnd = anotherRanges.size(); anotherIt < anotherEnd; ++anotherIt) { - const LifeTimeIntervalRange anotherRange = anotherRanges.at(anotherIt); - if (anotherRange.start > currentRange.end) - break; - int intersectPos = intersectionPosition(currentRange, anotherRange); - if (intersectPos != -1) - return intersectPos; - } - } - - return -1; -} - -/// Find the first use after the start position for the given temp. -/// -/// This is only called when all registers are in use, and when one of them has to be spilled to the -/// stack. So, uses where a register is optional can be ignored. -int RegisterAllocator::nextUse(const Temp &t, int startPosition) const -{ - typedef std::vector<Use>::const_iterator ConstIt; - - const std::vector<Use> &usePositions = _info->uses(t); - const ConstIt cend = usePositions.end(); - for (ConstIt it = usePositions.begin(); it != cend; ++it) { - if (it->mustHaveRegister()) { - const int usePos = it->pos; - if (usePos >= startPosition) - return usePos; - } - } - - return -1; -} - -static inline void insertReverseSorted(QVector<LifeTimeInterval *> &intervals, LifeTimeInterval *newInterval) -{ - newInterval->validate(); - for (int i = intervals.size(); i > 0;) { - if (LifeTimeInterval::lessThan(newInterval, intervals.at(--i))) { - intervals.insert(i + 1, newInterval); - return; - } - } - intervals.insert(0, newInterval); -} - -void RegisterAllocator::split(LifeTimeInterval ¤t, int beforePosition, - bool skipOptionalRegisterUses) -{ // TODO: check if we can always skip the optional register uses - Q_ASSERT(!current.isFixedInterval()); - - if (DebugRegAlloc) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream out(&buf); - out << "***** split request for range "; - current.dump(out); - out << " before position " << beforePosition - << " and skipOptionalRegisterUses = " << skipOptionalRegisterUses << endl; - qDebug("%s", buf.data().constData()); - } - - assignSpillSlot(current.temp(), current.start(), current.end()); - - const int firstPosition = current.start(); - Q_ASSERT(beforePosition > firstPosition && "split before start"); - - int lastUse = firstPosition; - int nextUse = -1; - const std::vector<Use> &usePositions = _info->uses(current.temp()); - for (size_t i = 0, ei = usePositions.size(); i != ei; ++i) { - const Use &usePosition = usePositions.at(i); - const int usePos = usePosition.pos; - if (lastUse < usePos && usePos < beforePosition) { - lastUse = usePos; - } else if (usePos >= beforePosition) { - if (!skipOptionalRegisterUses || usePosition.mustHaveRegister()) { - nextUse = usePos; - break; - } - } - } - Q_ASSERT(lastUse != -1); - Q_ASSERT(lastUse < beforePosition); - - LifeTimeInterval newInterval = current.split(lastUse, nextUse); - if (DebugRegAlloc) { - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream out(&buf); - out << "***** last use = " << lastUse << ", nextUse = " << nextUse << endl; - out << "***** new interval: "; - newInterval.dump(out); - out << endl; - out << "***** preceding interval: "; - current.dump(out); - out << endl; - qDebug("%s", buf.data().constData()); - } - if (newInterval.isValid()) { - if (current.reg() != LifeTimeInterval::InvalidRegister) - _info->addHint(current.temp(), current.reg()); - newInterval.setReg(LifeTimeInterval::InvalidRegister); - LifeTimeInterval *newIntervalPtr = new LifeTimeInterval(newInterval); - _lifeTimeIntervals->add(newIntervalPtr); - insertReverseSorted(_unhandled, newIntervalPtr); - } -} - -void RegisterAllocator::splitInactiveAtEndOfLifetimeHole(int reg, bool isFPReg, int position) -{ - for (int i = 0, ei = _inactive.size(); i != ei; ++i) { - LifeTimeInterval &interval = *_inactive[i]; - if (interval.isFixedInterval()) - continue; - if (isFPReg == interval.isFP() && interval.reg() == reg) { - LifeTimeInterval::Ranges ranges = interval.ranges(); - int endOfLifetimeHole = -1; - for (int j = 0, ej = ranges.size(); j != ej; ++j) { - if (position < ranges[j].start) - endOfLifetimeHole = ranges[j].start; - } - if (endOfLifetimeHole != -1) - split(interval, endOfLifetimeHole); - } - } -} - -void RegisterAllocator::assignSpillSlot(const Temp &t, int startPos, int endPos) -{ - if (_assignedSpillSlots[t.index] != InvalidSpillSlot) - return; - - for (int i = 0, ei = _activeSpillSlots.size(); i != ei; ++i) { - if (_activeSpillSlots.at(i) < startPos) { - _activeSpillSlots[i] = endPos; - _assignedSpillSlots[t.index] = i; - return; - } - } - - Q_UNREACHABLE(); -} - -void RegisterAllocator::dump(IR::Function *function) const -{ - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - IRPrinterWithPositions printer(&qout, _lifeTimeIntervals); - - qout << "Ranges:" << endl; - QVector<LifeTimeInterval *> handled = _handled; - std::sort(handled.begin(), handled.end(), LifeTimeInterval::lessThanForTemp); - for (const LifeTimeInterval *r : qAsConst(handled)) { - r->dump(qout); - qout << endl; - } - - qout << "Spill slots:" << endl; - for (unsigned i = 0; i < _assignedSpillSlots.size(); ++i) - if (_assignedSpillSlots[i] != InvalidSpillSlot) - qout << "\t%" << i << " -> " << _assignedSpillSlots[i] << endl; - - printer.print(function); - 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. -// [Traub] Omri Traub, Glenn Holloway, and Michael D. Smith. Quality and Speed in Linear-scan -// Register Allocation. In Proceedings of the ACM SIGPLAN 1998 Conference on Programming -// Language Design and Implementation, pages 142-151, June 1998. diff --git a/src/qml/jit/qv4regalloc_p.h b/src/qml/jit/qv4regalloc_p.h deleted file mode 100644 index 4ee440b73e..0000000000 --- a/src/qml/jit/qv4regalloc_p.h +++ /dev/null @@ -1,140 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the V4VM 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 QV4REGALLOC_P_H -#define QV4REGALLOC_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 "qv4global_p.h" -#include "qv4isel_p.h" -#include "qv4ssa_p.h" -#include "qv4registerinfo_p.h" - -#include <config.h> - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace JIT { - -class RegAllocInfo; - -// This class implements a linear-scan register allocator, with a couple of tweaks: -// - Second-chance allocation is used when an interval becomes active after a spill, which results -// in fewer differences between edges, and hence fewer moves before jumps. -// - Use positions are flagged with either "must have" register or "could have" register. This is -// used to decide whether a register is really needed when a temporary is used after a spill -// occurred. -// - Fixed intervals are used to denotate IR positions where certain registers are needed in order -// to implement the operation, and cannot be used by a temporary on that position. An example is -// caller saved registers, where the call will use/clobber those registers. -// - Hints are used to indicate which registers could be used to generate more compact code. An -// example is an addition, where one (or both) operands' life-time ends at that instruction. In -// this case, re-using an operand register for the result will result in an in-place add. -// - SSA form properties are used: -// - to simplify life-times (two temporaries will never interfere as long as their intervals -// are not split), resulting in a slightly faster algorithm; -// - when a temporary needs to be spilled, it is done directly after calculating it, so that -// 1 store is generated even if multiple spills/splits happen. -// - phi-node elimination (SSA form deconstruction) is done when resolving differences between -// CFG edges -class RegisterAllocator -{ - typedef IR::LifeTimeInterval LifeTimeInterval; - - const RegisterInformation &_registerInformation; - QVector<const RegisterInfo *> _normalRegisters; - QVector<const RegisterInfo *> _fpRegisters; - QScopedPointer<RegAllocInfo> _info; - - QVector<LifeTimeInterval *> _fixedRegisterRanges, _fixedFPRegisterRanges; - - IR::LifeTimeIntervals::Ptr _lifeTimeIntervals; - typedef QVector<LifeTimeInterval *> Intervals; - Intervals _unhandled, _active, _inactive, _handled; - - std::vector<int> _lastAssignedRegister; - std::vector<int> _assignedSpillSlots; - QVector<int> _activeSpillSlots; - - QBitArray _regularRegsInUse, _fpRegsInUse; - - Q_DISABLE_COPY(RegisterAllocator) - -public: - enum { InvalidSpillSlot = -1 }; - - RegisterAllocator(const RegisterInformation ®isterInformation); - ~RegisterAllocator(); - - void run(IR::Function *function, const IR::Optimizer &opt); - RegisterInformation usedRegisters() const; - -private: - void markInUse(int reg, bool isFPReg); - LifeTimeInterval *cloneFixedInterval(int reg, bool isFP, const LifeTimeInterval &original); - void prepareRanges(); - void linearScan(); - void tryAllocateFreeReg(LifeTimeInterval ¤t); - void allocateBlockedReg(LifeTimeInterval ¤t); - int nextIntersection(const LifeTimeInterval ¤t, const LifeTimeInterval &another) const; - int nextUse(const IR::Temp &t, int startPosition) const; - void split(LifeTimeInterval ¤t, int beforePosition, bool skipOptionalRegisterUses =false); - void splitInactiveAtEndOfLifetimeHole(int reg, bool isFPReg, int position); - void assignSpillSlot(const IR::Temp &t, int startPos, int endPos); - void resolve(IR::Function *function, const IR::Optimizer &opt); - - void dump(IR::Function *function) const; -}; - -} // end of namespace JIT -} // end of namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4REGALLOC_P_H diff --git a/src/qml/jit/qv4registerinfo_p.h b/src/qml/jit/qv4registerinfo_p.h deleted file mode 100644 index 214206db91..0000000000 --- a/src/qml/jit/qv4registerinfo_p.h +++ /dev/null @@ -1,112 +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 QV4REGISTERINFO_P_H -#define QV4REGISTERINFO_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 <QtCore/QString> - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace JIT { - -class RegisterInfo -{ -public: - enum { InvalidRegister = (1 << 29) - 1 }; - enum SavedBy { CallerSaved, CalleeSaved }; - enum RegisterType { RegularRegister, FloatingPointRegister }; - enum Usage { Predefined, RegAlloc }; - -public: - RegisterInfo() - : _savedBy(CallerSaved) - , _usage(Predefined) - , _type(RegularRegister) - , _reg(InvalidRegister) - {} - - RegisterInfo(int reg, const QString &prettyName, RegisterType type, SavedBy savedBy, Usage usage) - : _prettyName(prettyName) - , _savedBy(savedBy) - , _usage(usage) - , _type(type) - , _reg(reg) - {} - - bool operator==(const RegisterInfo &other) const - { return _type == other._type && _reg == other._reg; } - - bool isValid() const { return _reg != InvalidRegister; } - template <typename T> T reg() const { return static_cast<T>(_reg); } - QString prettyName() const { return _prettyName; } - bool isCallerSaved() const { return _savedBy == CallerSaved; } - bool isCalleeSaved() const { return _savedBy == CalleeSaved; } - bool isFloatingPoint() const { return _type == FloatingPointRegister; } - bool isRegularRegister() const { return _type == RegularRegister; } - bool useForRegAlloc() const { return _usage == RegAlloc; } - bool isPredefined() const { return _usage == Predefined; } - -private: - QString _prettyName; - unsigned _savedBy : 1; - unsigned _usage : 1; - unsigned _type : 1; - unsigned _reg : 29; -}; -typedef QVector<RegisterInfo> RegisterInformation; - -} // JIT namespace -} // QV4 namespace - -QT_END_NAMESPACE - -#endif // QV4REGISTERINFO_P_H diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h deleted file mode 100644 index 6d788f4a93..0000000000 --- a/src/qml/jit/qv4targetplatform_p.h +++ /dev/null @@ -1,707 +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 QV4TARGETPLATFORM_P_H -#define QV4TARGETPLATFORM_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 <config.h> - -#if ENABLE(ASSEMBLER) - -#include <private/qv4value_p.h> -#include "qv4registerinfo_p.h" -#include <assembler/MacroAssembler.h> - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace JIT { - -enum TargetOperatingSystemSpecialization { - NoOperatingSystemSpecialization, - WindowsSpecialization -}; - -// The TargetPlatform class describes how the stack and the registers work on a CPU+ABI combination. -// -// All combinations have a separate definition, guarded by #ifdefs. The exceptions are: -// - Linux/x86 and win32, which are the same, except that linux non-PIC/PIE code does not need to -// restore ebx (which holds the GOT ptr) before a call -// - All (supported) ARM platforms, where the only variety is the platform specific usage of r9, -// and the frame-pointer in Thumb/Thumb2 v.s. ARM mode. -// -// Specific handling of ebx when it holds the GOT: -// In this case we can use it, but it needs to be restored when doing a call. So, the handling is as -// follows: it is marked as caller saved, meaning the value in it won't survive a call. When -// calculating the list of callee saved registers in getCalleeSavedRegisters (which is used to -// generate push/pop instructions in the prelude/postlude), we add ebx too. Then when synthesizing -// a call, we add a load it right before emitting the call instruction. -// -// NOTE: When adding new architecture, do not forget to whitelist it in qv4global_p.h! -template <typename PlatformAssembler, TargetOperatingSystemSpecialization specialization = NoOperatingSystemSpecialization> -class TargetPlatform -{ -}; - -#if CPU(X86) && (OS(LINUX) || OS(WINDOWS) || OS(QNX) || OS(FREEBSD) || defined(Q_OS_IOS)) -template <> -class TargetPlatform<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization> -{ -public: - using PlatformAssembler = JSC::MacroAssemblerX86; - using RegisterID = PlatformAssembler::RegisterID; - using FPRegisterID = PlatformAssembler::FPRegisterID; - using TrustedImm32 = PlatformAssembler::TrustedImm32; - - enum { RegAllocIsSupported = 1 }; - - static const RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const RegisterID LocalsRegister = JSC::X86Registers::edi; - static const RegisterID EngineRegister = JSC::X86Registers::esi; - static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID ScratchRegister = JSC::X86Registers::ecx; - static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - static const RegisterID LowReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID HighReturnValueRegister = JSC::X86Registers::edx; - - static RegisterInformation getRegisterInfo() - { - typedef RegisterInfo RI; - static RegisterInformation info = RegisterInformation() - << RI(JSC::X86Registers::edx, QStringLiteral("edx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::ebx, QStringLiteral("ebx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::edi, QStringLiteral("edi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::esi, QStringLiteral("esi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - ; - return info; - } - -# define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 - static const int RegisterSize = 4; - - static const int RegisterArgumentCount = 0; - static RegisterID registerForArgument(int) { Q_UNREACHABLE(); } - - static const int StackAlignment = 16; - static const int StackShadowSpace = 0; - static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. - static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } - static void platformFinishEnteringStandardStackFrame(PlatformAssembler *) {} - static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) - { - if (frameSize > 0) - as->add32(TrustedImm32(frameSize), StackPointerRegister); - as->pop(FramePointerRegister); - } - -#if OS(WINDOWS) || OS(QNX) || \ - ((OS(LINUX) || OS(FREEBSD)) && (defined(__PIC__) || defined(__PIE__))) - - static const int gotRegister = JSC::X86Registers::ebx; - static int savedGOTRegisterSlotOnStack() { - static int ebxIdx = -1; - if (ebxIdx == -1) { - int calleeSaves = 0; - const auto infos = getRegisterInfo(); - for (const RegisterInfo &info : infos) { - if (info.reg<JSC::X86Registers::RegisterID>() == JSC::X86Registers::ebx) { - ebxIdx = calleeSaves; - break; - } else if (info.isCalleeSaved()) { - ++calleeSaves; - } - } - Q_ASSERT(ebxIdx >= 0); - ebxIdx += 1; - } - return ebxIdx * -int(sizeof(void*)); - } -#else - static const int gotRegister = -1; - static int savedGOTRegisterSlotOnStack() { return -1; } -#endif -}; -#endif // x86 - -#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X) || OS(FREEBSD) || OS(QNX) || defined(Q_OS_IOS)) -template <> -class TargetPlatform<JSC::MacroAssemblerX86_64, NoOperatingSystemSpecialization> -{ -public: - using PlatformAssembler = JSC::MacroAssemblerX86_64; - using RegisterID = PlatformAssembler::RegisterID; - using FPRegisterID = PlatformAssembler::FPRegisterID; - using TrustedImm32 = PlatformAssembler::TrustedImm32; - - enum { RegAllocIsSupported = 1 }; - - static const RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const RegisterID LocalsRegister = JSC::X86Registers::r12; - static const RegisterID EngineRegister = JSC::X86Registers::r14; - static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID ScratchRegister = JSC::X86Registers::r10; - static const RegisterID DoubleMaskRegister = JSC::X86Registers::r13; - static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - - static RegisterInformation getRegisterInfo() - { - typedef RegisterInfo RI; - static RegisterInformation info = RegisterInformation() - << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - // r11 is used as scratch register by the macro assembler - << RI(JSC::X86Registers::r12, QStringLiteral("r12"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - ; - return info; - } - -#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 - static const int RegisterSize = 8; - - static const int RegisterArgumentCount = 6; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::X86Registers::edi, - JSC::X86Registers::esi, - JSC::X86Registers::edx, - JSC::X86Registers::ecx, - JSC::X86Registers::r8, - JSC::X86Registers::r9 - }; - Q_ASSERT(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - }; - - static const int StackAlignment = 16; - static const int StackShadowSpace = 0; - static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. - static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } - static void platformFinishEnteringStandardStackFrame(PlatformAssembler *as) - { - as->move(PlatformAssembler::TrustedImm64(QV4::Value::NaNEncodeMask), DoubleMaskRegister); - } - static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) - { - if (frameSize > 0) - as->add64(TrustedImm32(frameSize), StackPointerRegister); - as->pop(FramePointerRegister); - } - - static const int gotRegister = -1; - static int savedGOTRegisterSlotOnStack() { return -1; } -}; -#endif // Linux/MacOS on x86_64 - -#if CPU(X86_64) && OS(WINDOWS) -template <> -class TargetPlatform<JSC::MacroAssemblerX86_64, WindowsSpecialization> -{ -public: - using PlatformAssembler = JSC::MacroAssemblerX86_64; - using RegisterID = PlatformAssembler::RegisterID; - using FPRegisterID = PlatformAssembler::FPRegisterID; - using TrustedImm32 = PlatformAssembler::TrustedImm32; - - enum { RegAllocIsSupported = 1 }; - - static const RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const RegisterID LocalsRegister = JSC::X86Registers::r12; - static const RegisterID EngineRegister = JSC::X86Registers::r14; - static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID ScratchRegister = JSC::X86Registers::r10; - static const RegisterID DoubleMaskRegister = JSC::X86Registers::r13; - static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - - static RegisterInformation getRegisterInfo() - { - typedef RegisterInfo RI; - static RegisterInformation info = RegisterInformation() - << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - // r11 is used as scratch register by the macro assembler - << RI(JSC::X86Registers::r12, QStringLiteral("r12"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - ; - return info; - } - -#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 - static const int RegisterSize = 8; - - static const int RegisterArgumentCount = 4; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::X86Registers::ecx, - JSC::X86Registers::edx, - JSC::X86Registers::r8, - JSC::X86Registers::r9 - }; - Q_ASSERT(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - } - - static const int StackAlignment = 16; - static const int StackShadowSpace = 32; - static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. - static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } - static void platformFinishEnteringStandardStackFrame(PlatformAssembler *as) - { - as->move(PlatformAssembler::TrustedImm64(QV4::Value::NaNEncodeMask), DoubleMaskRegister); - } - static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) - { - if (frameSize > 0) - as->add64(TrustedImm32(frameSize), StackPointerRegister); - as->pop(FramePointerRegister); - } - - static const int gotRegister = -1; - static int savedGOTRegisterSlotOnStack() { return -1; } -}; -#endif // Windows on x86_64 - -#if CPU(ARM) || defined(V4_BOOTSTRAP) -template <> -class TargetPlatform<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization> -{ -public: - using PlatformAssembler = JSC::MacroAssemblerARMv7; - using RegisterID = PlatformAssembler::RegisterID; - using FPRegisterID = PlatformAssembler::FPRegisterID; - using TrustedImm32 = PlatformAssembler::TrustedImm32; - - enum { RegAllocIsSupported = 1 }; - - // The AAPCS specifies that the platform ABI has to define the usage of r9. Known are: - // - The GNU/Linux ABI defines it as an additional callee-saved variable register (v6). - // - iOS (for which we cannot JIT, but still...) defines it as having a special use, so we do - // not touch it, nor use it. - // - Any other platform has not been verified, so we conservatively assume we cannot use it. -#if OS(LINUX) -#define CAN_USE_R9 -#endif - - // There are two designated frame-pointer registers on ARM, depending on which instruction set - // is used for the subroutine: r7 for Thumb or Thumb2, and r11 for ARM. We assign the constants - // accordingly, and assign the locals-register to the "other" register. -#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP) - static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7; - static const RegisterID LocalsRegister = JSC::ARMRegisters::r11; -#else // Thumbs down - static const RegisterID FramePointerRegister = JSC::ARMRegisters::r11; - static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; -#endif - static const RegisterID StackPointerRegister = JSC::ARMRegisters::r13; - static const RegisterID ScratchRegister = JSC::ARMRegisters::r5; - static const RegisterID EngineRegister = JSC::ARMRegisters::r10; - static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; - static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; - static const FPRegisterID FPGpr1 = JSC::ARMRegisters::d1; - static const RegisterID LowReturnValueRegister = JSC::ARMRegisters::r0; - static const RegisterID HighReturnValueRegister = JSC::ARMRegisters::r1; - - static RegisterInformation getRegisterInfo() - { - typedef RegisterInfo RI; - static RegisterInformation info = RegisterInformation() - << RI(JSC::ARMRegisters::r0, QStringLiteral("r0"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) - << RI(JSC::ARMRegisters::r1, QStringLiteral("r1"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::r2, QStringLiteral("r2"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::r3, QStringLiteral("r3"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::r4, QStringLiteral("r4"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::r5, QStringLiteral("r5"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::ARMRegisters::r6, QStringLiteral("r6"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) -#if !CPU(ARM_THUMB2) && !defined(V4_BOOTSTRAP) - << RI(JSC::ARMRegisters::r7, QStringLiteral("r7"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) -#endif - << RI(JSC::ARMRegisters::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) -#ifdef CAN_USE_R9 - << RI(JSC::ARMRegisters::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) -#endif - << RI(JSC::ARMRegisters::r10, QStringLiteral("r10"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) -#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP) - << RI(JSC::ARMRegisters::r11, QStringLiteral("r11"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) -#endif - << RI(JSC::ARMRegisters::d2, QStringLiteral("d2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d3, QStringLiteral("d3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d4, QStringLiteral("d4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d5, QStringLiteral("d5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d6, QStringLiteral("d6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d8, QStringLiteral("d8"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d9, QStringLiteral("d9"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d10, QStringLiteral("d10"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d11, QStringLiteral("d11"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d12, QStringLiteral("d12"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d13, QStringLiteral("d13"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d14, QStringLiteral("d14"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARMRegisters::d15, QStringLiteral("d15"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - ; - return info; - } - -#undef HAVE_ALU_OPS_WITH_MEM_OPERAND - static const int RegisterSize = 4; - - static const int RegisterArgumentCount = 4; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::ARMRegisters::r0, - JSC::ARMRegisters::r1, - JSC::ARMRegisters::r2, - JSC::ARMRegisters::r3 - }; - - Q_ASSERT(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - }; - - static const int StackAlignment = 8; // Per AAPCS - static const int StackShadowSpace = 0; - static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - - static void platformEnterStandardStackFrame(PlatformAssembler *as) - { - as->push(JSC::ARMRegisters::lr); - as->push(FramePointerRegister); - } - - static void platformFinishEnteringStandardStackFrame(PlatformAssembler *) {} - - static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) - { - if (frameSize > 0) { - // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't - // work well for large immediates. - as->move(TrustedImm32(frameSize), JSC::ARMRegisters::r3); - as->add32(JSC::ARMRegisters::r3, StackPointerRegister); - } - as->pop(FramePointerRegister); - as->pop(JSC::ARMRegisters::lr); - } - - static const int gotRegister = -1; - static int savedGOTRegisterSlotOnStack() { return -1; } -}; -#endif // ARM (32 bit) - -#if CPU(ARM64) || defined(V4_BOOTSTRAP) -template <> -class TargetPlatform<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization> -{ -public: - using PlatformAssembler = JSC::MacroAssemblerARM64; - using RegisterID = PlatformAssembler::RegisterID; - using FPRegisterID = PlatformAssembler::FPRegisterID; - using TrustedImm32 = PlatformAssembler::TrustedImm32; - - enum { RegAllocIsSupported = 1 }; - - static const RegisterID FramePointerRegister = JSC::ARM64Registers::fp; - static const RegisterID LocalsRegister = JSC::ARM64Registers::x28; - static const RegisterID StackPointerRegister = JSC::ARM64Registers::sp; - static const RegisterID ScratchRegister = JSC::ARM64Registers::x9; - static const RegisterID EngineRegister = JSC::ARM64Registers::x27; - static const RegisterID ReturnValueRegister = JSC::ARM64Registers::x0; - static const RegisterID DoubleMaskRegister = JSC::ARM64Registers::x26; - static const FPRegisterID FPGpr0 = JSC::ARM64Registers::q0; - static const FPRegisterID FPGpr1 = JSC::ARM64Registers::q1; - - static RegisterInformation getRegisterInfo() - { - typedef RegisterInfo RI; - static RegisterInformation info = RegisterInformation() - << RI(JSC::ARM64Registers::x0, QStringLiteral("x0"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) - << RI(JSC::ARM64Registers::x1, QStringLiteral("x1"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x2, QStringLiteral("x2"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x3, QStringLiteral("x3"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x4, QStringLiteral("x4"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x5, QStringLiteral("x5"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x6, QStringLiteral("x6"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x7, QStringLiteral("x7"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x8, QStringLiteral("x8"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) - << RI(JSC::ARM64Registers::x9, QStringLiteral("x9"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::ARM64Registers::x10, QStringLiteral("x10"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x11, QStringLiteral("x11"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x12, QStringLiteral("x12"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x13, QStringLiteral("x13"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x14, QStringLiteral("x14"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x15, QStringLiteral("x15"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x19, QStringLiteral("x19"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x20, QStringLiteral("x20"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x21, QStringLiteral("x21"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x22, QStringLiteral("x22"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x23, QStringLiteral("x23"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x24, QStringLiteral("x24"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x25, QStringLiteral("x25"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::x26, QStringLiteral("x26"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::ARM64Registers::x27, QStringLiteral("x27"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::ARM64Registers::x28, QStringLiteral("x28"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - - << RI(JSC::ARM64Registers::q2, QStringLiteral("q2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q3, QStringLiteral("q3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q4, QStringLiteral("q4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q5, QStringLiteral("q5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q6, QStringLiteral("q6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q8, QStringLiteral("q8"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q9, QStringLiteral("q9"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q10, QStringLiteral("q10"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q11, QStringLiteral("q11"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q12, QStringLiteral("q12"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q13, QStringLiteral("q13"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q14, QStringLiteral("q14"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q15, QStringLiteral("q15"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q16, QStringLiteral("q16"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q17, QStringLiteral("q17"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q18, QStringLiteral("q18"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q19, QStringLiteral("q19"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q20, QStringLiteral("q20"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q21, QStringLiteral("q21"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q22, QStringLiteral("q22"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q23, QStringLiteral("q23"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q24, QStringLiteral("q24"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q25, QStringLiteral("q25"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q26, QStringLiteral("q26"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q27, QStringLiteral("q27"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q28, QStringLiteral("q28"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q29, QStringLiteral("q29"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q30, QStringLiteral("q30"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::ARM64Registers::q31, QStringLiteral("q31"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - ; - return info; - } - -#undef HAVE_ALU_OPS_WITH_MEM_OPERAND - static const int RegisterSize = 8; - - static const int RegisterArgumentCount = 8; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::ARM64Registers::x0, - JSC::ARM64Registers::x1, - JSC::ARM64Registers::x2, - JSC::ARM64Registers::x3, - JSC::ARM64Registers::x4, - JSC::ARM64Registers::x5, - JSC::ARM64Registers::x6, - JSC::ARM64Registers::x7 - }; - - Q_ASSERT(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - }; - - static const int StackAlignment = 16; - static const int StackShadowSpace = 0; - static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - - static void platformEnterStandardStackFrame(PlatformAssembler *as) - { - as->pushPair(FramePointerRegister, JSC::ARM64Registers::lr); - } - - static void platformFinishEnteringStandardStackFrame(PlatformAssembler *as) - { - as->move(PlatformAssembler::TrustedImm64(QV4::Value::NaNEncodeMask), DoubleMaskRegister); - } - - static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) - { - if (frameSize > 0) - as->add64(TrustedImm32(frameSize), StackPointerRegister); - as->popPair(FramePointerRegister, JSC::ARM64Registers::lr); - } - - static const int gotRegister = -1; - static int savedGOTRegisterSlotOnStack() { return -1; } -}; -#endif // ARM64 - -#if defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) -template <> -class TargetPlatform<JSC::MacroAssemblerMIPS, NoOperatingSystemSpecialization> -{ -public: - using PlatformAssembler = JSC::MacroAssemblerMIPS; - using RegisterID = PlatformAssembler::RegisterID; - using FPRegisterID = PlatformAssembler::FPRegisterID; - using TrustedImm32 = PlatformAssembler::TrustedImm32; - enum { RegAllocIsSupported = 1 }; - - static const RegisterID FramePointerRegister = JSC::MIPSRegisters::fp; - static const RegisterID StackPointerRegister = JSC::MIPSRegisters::sp; - static const RegisterID LocalsRegister = JSC::MIPSRegisters::s0; - static const RegisterID EngineRegister = JSC::MIPSRegisters::s1; - static const RegisterID ReturnValueRegister = JSC::MIPSRegisters::v0; - static const RegisterID ScratchRegister = JSC::MIPSRegisters::s2; - static const FPRegisterID FPGpr0 = JSC::MIPSRegisters::f0; - static const FPRegisterID FPGpr1 = JSC::MIPSRegisters::f2; - static const RegisterID LowReturnValueRegister = JSC::MIPSRegisters::v0; - static const RegisterID HighReturnValueRegister = JSC::MIPSRegisters::v1; - - static RegisterInformation getRegisterInfo() - { - typedef RegisterInfo RI; - static RegisterInformation info = RegisterInformation() - // Note: t0, t1, t2, t3 and f16 are already used by MacroAssemblerMIPS. - << RI(JSC::MIPSRegisters::t4, QStringLiteral("t4"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::t5, QStringLiteral("t5"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::t6, QStringLiteral("t6"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::t7, QStringLiteral("t7"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::t8, QStringLiteral("t8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::s0, QStringLiteral("s0"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::MIPSRegisters::s1, QStringLiteral("s1"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::MIPSRegisters::s2, QStringLiteral("s2"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::MIPSRegisters::s3, QStringLiteral("s3"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f4, QStringLiteral("f4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f6, QStringLiteral("f6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f8, QStringLiteral("f8"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f10, QStringLiteral("f10"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f18, QStringLiteral("f18"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f20, QStringLiteral("f20"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f22, QStringLiteral("f22"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f24, QStringLiteral("f24"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f26, QStringLiteral("f26"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - << RI(JSC::MIPSRegisters::f28, QStringLiteral("f28"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) - ; - return info; - } - -#undef HAVE_ALU_OPS_WITH_MEM_OPERAND - static const int RegisterSize = 4; - - static const int RegisterArgumentCount = 4; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::MIPSRegisters::a0, - JSC::MIPSRegisters::a1, - JSC::MIPSRegisters::a2, - JSC::MIPSRegisters::a3 - }; - - Q_ASSERT(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - }; - - static const int StackAlignment = 8; - static const int StackShadowSpace = 4 * RegisterSize; // Stack space for 4 argument registers. - static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - - static void platformEnterStandardStackFrame(PlatformAssembler *as) - { - as->push(JSC::MIPSRegisters::ra); - as->push(FramePointerRegister); - } - - static void platformFinishEnteringStandardStackFrame(PlatformAssembler *) {} - - static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) - { - if (frameSize > 0) - as->add32(TrustedImm32(frameSize), StackPointerRegister); - as->pop(FramePointerRegister); - as->pop(JSC::MIPSRegisters::ra); - } - - - static const int gotRegister = -1; - static int savedGOTRegisterSlotOnStack() { return -1; } -}; -#endif // Linux on MIPS (32 bit) - -} // JIT namespace -} // QV4 namespace - -QT_END_NAMESPACE - -#endif // ENABLE(ASSEMBLER) - -#endif // QV4TARGETPLATFORM_P_H diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp deleted file mode 100644 index 7ffa1c7151..0000000000 --- a/src/qml/jit/qv4unop.cpp +++ /dev/null @@ -1,166 +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 <qv4unop_p.h> -#include <qv4assembler_p.h> - -#if ENABLE(ASSEMBLER) - -using namespace QV4; -using namespace JIT; - -#define stringIfyx(s) #s -#define stringIfy(s) stringIfyx(s) -#define setOp(operation) \ - do { \ - call = typename JITAssembler::RuntimeCall(QV4::Runtime::operation); name = "Runtime::" stringIfy(operation); \ - needsExceptionCheck = Runtime::Method_##operation##_NeedsExceptionCheck; \ - } while (0) - -template <typename JITAssembler> -void Unop<JITAssembler>::generate(IR::Expr *source, IR::Expr *target) -{ - bool needsExceptionCheck; - typename JITAssembler::RuntimeCall call; - const char *name = 0; - switch (op) { - case IR::OpNot: - generateNot(source, target); - return; - case IR::OpUMinus: - generateUMinus(source, target); - return; - case IR::OpUPlus: setOp(uPlus); break; - case IR::OpCompl: - generateCompl(source, target); - return; - case IR::OpPreIncrement: setOp(preIncrement); break; - case IR::OpPreDecrement: setOp(preDecrement); break; - default: - Q_UNREACHABLE(); - } // switch - - Q_ASSERT(call.isValid()); - _as->generateFunctionCallImp(needsExceptionCheck, target, name, call, PointerToValue(source)); -} - -template <typename JITAssembler> -void Unop<JITAssembler>::generateUMinus(IR::Expr *source, IR::Expr *target) -{ - IR::Temp *targetTemp = target->asTemp(); - - if (IR::Const *c = source->asConst()) { - if (c->value == 0 && source->type == IR::SInt32Type) { - // special case: minus integer 0 is 0, which is not what JS expects it to be, so always - // do a runtime call - generateRuntimeCall(_as, target, uMinus, PointerToValue(source)); - return; - } - } - if (source->type == IR::SInt32Type) { - typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (typename JITAssembler::RegisterID) targetTemp->index; - typename JITAssembler::RegisterID sReg = _as->toInt32Register(source, tReg); - _as->move(sReg, tReg); - _as->neg32(tReg); - if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - _as->storeInt32(tReg, target); - return; - } - - generateRuntimeCall(_as, target, uMinus, PointerToValue(source)); -} - -template <typename JITAssembler> -void Unop<JITAssembler>::generateNot(IR::Expr *source, IR::Expr *target) -{ - IR::Temp *targetTemp = target->asTemp(); - if (source->type == IR::BoolType) { - typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (typename JITAssembler::RegisterID) targetTemp->index; - _as->xor32(TrustedImm32(0x1), _as->toInt32Register(source, tReg), tReg); - if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - _as->storeBool(tReg, target); - return; - } else if (source->type == IR::SInt32Type) { - typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (typename JITAssembler::RegisterID) targetTemp->index; - _as->compare32(RelationalCondition::Equal, - _as->toInt32Register(source, JITAssembler::ScratchRegister), TrustedImm32(0), - tReg); - if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - _as->storeBool(tReg, target); - return; - } else if (source->type == IR::DoubleType) { - // ### - } - // ## generic implementation testing for int/bool - - generateRuntimeCall(_as, target, uNot, PointerToValue(source)); -} - -template <typename JITAssembler> -void Unop<JITAssembler>::generateCompl(IR::Expr *source, IR::Expr *target) -{ - IR::Temp *targetTemp = target->asTemp(); - if (source->type == IR::SInt32Type) { - typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; - if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (typename JITAssembler::RegisterID) targetTemp->index; - _as->xor32(TrustedImm32(0xffffffff), _as->toInt32Register(source, tReg), tReg); - if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - _as->storeInt32(tReg, target); - return; - } - generateRuntimeCall(_as, target, complement, PointerToValue(source)); -} - -template struct QV4::JIT::Unop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>; -#if defined(V4_BOOTSTRAP) -#if !CPU(ARM_THUMB2) -template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>; -#endif -#if !CPU(ARM64) -template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>>; -#endif -#endif - -#endif diff --git a/src/qml/jit/qv4unop_p.h b/src/qml/jit/qv4unop_p.h deleted file mode 100644 index fb68f80eec..0000000000 --- a/src/qml/jit/qv4unop_p.h +++ /dev/null @@ -1,92 +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 QV4UNOP_P_H -#define QV4UNOP_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_masm_p.h> - -QT_BEGIN_NAMESPACE - -#if ENABLE(ASSEMBLER) - -namespace QV4 { -namespace JIT { - -template <typename JITAssembler> -struct Unop { - Unop(JITAssembler *assembler, IR::AluOp operation) - : _as(assembler) - , op(operation) - {} - - using RelationalCondition = typename JITAssembler::RelationalCondition; - using PointerToValue = typename JITAssembler::PointerToValue; - using RuntimeCall = typename JITAssembler::RuntimeCall; - using TrustedImm32 = typename JITAssembler::TrustedImm32; - - void generate(IR::Expr *source, IR::Expr *target); - - void generateUMinus(IR::Expr *source, IR::Expr *target); - void generateNot(IR::Expr *source, IR::Expr *target); - void generateCompl(IR::Expr *source, IR::Expr *target); - - JITAssembler *_as; - IR::AluOp op; -}; - -} -} - -#endif - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 90e705e42f..0ee7445477 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -666,12 +666,12 @@ Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t) Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) { - bool global = (flags & IR::RegExp::RegExp_Global); + bool global = (flags & QV4::CompiledData::RegExp::RegExp_Global); bool ignoreCase = false; bool multiline = false; - if (flags & IR::RegExp::RegExp_IgnoreCase) + if (flags & QV4::CompiledData::RegExp::RegExp_IgnoreCase) ignoreCase = true; - if (flags & IR::RegExp::RegExp_Multiline) + if (flags & QV4::CompiledData::RegExp::RegExp_Multiline) multiline = true; Scope scope(this); diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index b3bd28e18b..d483db7a8e 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -51,7 +51,6 @@ #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> -#include <qv4jsir_p.h> #include <qv4codegen_p.h> #ifndef Q_OS_WIN diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 0916e8e110..c0f05d7a3d 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -52,7 +52,6 @@ #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> -#include <qv4jsir_p.h> #include <qv4codegen_p.h> #include "private/qlocale_tools_p.h" #include "private/qtools_p.h" diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 3b1e680831..d9869920d9 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -48,8 +48,6 @@ #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> -#include <qv4jsir_p.h> -#include <qv4codegen_p.h> #include "private/qlocale_tools_p.h" #include <QtCore/QDebug> diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 6356ad45f3..a02d5926a7 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -105,7 +105,7 @@ struct RegExpObject: Object { V4_INTERNALCLASS(RegExpObject) V4_PROTOTYPE(regExpPrototype) - // needs to be compatible with the flags in qv4jsir_p.h + // needs to be compatible with the flags in qv4compileddata_p.h enum Flags { RegExp_Global = 0x01, RegExp_IgnoreCase = 0x02, diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 13f84ccd0e..68ed07b214 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -52,7 +52,6 @@ #include <private/qqmljsast_p.h> #include <private/qqmlengine_p.h> #include <private/qv4profiling_p.h> -#include <qv4jsir_p.h> #include <qv4codegen_p.h> #include <QtCore/QDebug> diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 81f5c3566c..ab74fd22d3 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -53,8 +53,6 @@ #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> -#include <qv4jsir_p.h> -#include <qv4codegen_p.h> #include <cassert> diff --git a/src/qmldevtools/qmldevtools.pro b/src/qmldevtools/qmldevtools.pro index b3cc01d2a9..5ee97776b8 100644 --- a/src/qmldevtools/qmldevtools.pro +++ b/src/qmldevtools/qmldevtools.pro @@ -16,16 +16,5 @@ include(../qml/parser/parser.pri) include(../qml/jsruntime/jsruntime.pri) include(../qml/compiler/compiler.pri) include(../qml/memory/memory.pri) -include(../qml/jit/jit.pri) - -HEADERS += \ - $$PWD/../qml/compiler/qv4isel_p.h \ - $$PWD/../qml/compiler/qv4isel_moth_p.h \ - $$PWD/../qml/compiler/qv4ssa_p.h - -SOURCES += \ - $$PWD/../qml/compiler/qv4isel_p.cpp \ - $$PWD/../qml/compiler/qv4isel_moth.cpp \ - $$PWD/../qml/compiler/qv4ssa.cpp load(qt_module) diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index a379badd34..1aae02b167 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -38,7 +38,6 @@ #include <private/qv4debugging_p.h> #include <private/qv8engine_p.h> #include <private/qv4objectiterator_p.h> -#include <private/qv4isel_moth_p.h> #include <private/qv4string_p.h> #include <private/qqmlbuiltinfunctions_p.h> #include <private/qqmldebugservice_p.h> diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 722286a38f..015d06ab81 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -30,8 +30,6 @@ #include <private/qv4compileddata_p.h> #include <private/qv4compiler_p.h> -#include <private/qv4jsir_p.h> -#include <private/qv4isel_p.h> #include <private/qv8engine_p.h> #include <private/qv4engine_p.h> #include <private/qv4codegen_p.h> diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index 6bc199ee6b..26297d3d9a 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -35,17 +35,7 @@ #include <QHashFunctions> #include <private/qqmlirbuilder_p.h> -#include <private/qv4isel_moth_p.h> #include <private/qqmljsparser_p.h> -#include <private/qv4jssimplifier_p.h> - -QT_BEGIN_NAMESPACE - -namespace QV4 { namespace JIT { -Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &architecture); -} } - -QT_END_NAMESPACE struct Error { @@ -114,7 +104,7 @@ static void annotateListElements(QmlIR::Document *document) } } -static bool compileQmlFile(const QString &inputFileName, const QString &outputFileName, QV4::EvalISelFactory *iselFactory, const QString &targetABI, Error *error) +static bool compileQmlFile(const QString &inputFileName, const QString &outputFileName, const QString &targetABI, Error *error) { QmlIR::Document irDocument(/*debugMode*/false); irDocument.jsModule.targetABI = targetABI; @@ -149,7 +139,7 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi annotateListElements(&irDocument); { - QmlIR::JSCodeGen v4CodeGen(/*empty input file name*/QString(), irDocument.code, + QmlIR::JSCodeGen v4CodeGen(irDocument.code, &irDocument.jsGenerator, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable); @@ -178,16 +168,14 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi QmlIR::QmlUnitGenerator generator; - { - QQmlJavaScriptBindingExpressionSimplificationPass pass(irDocument.objects, &irDocument.jsModule, &irDocument.jsGenerator); - pass.reduceTranslationBindings(); - } +// { +// QQmlJavaScriptBindingExpressionSimplificationPass pass(irDocument.objects, &irDocument.jsModule, &irDocument.jsGenerator); +// pass.reduceTranslationBindings(); +// } - QV4::ExecutableAllocator allocator; - QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, &allocator, &irDocument.jsModule, &irDocument.jsGenerator)); // Disable lookups in non-standalone (aka QML) mode - isel->setUseFastLookups(false); - irDocument.javaScriptCompilationUnit = isel->compile(/*generate unit*/false); + v4CodeGen.setUseFastLookups(false); + irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(/*generate unit*/false); QV4::CompiledData::Unit *unit = generator.generate(irDocument); unit->flags |= QV4::CompiledData::Unit::StaticData; unit->flags |= QV4::CompiledData::Unit::PendingTypeCompilation; @@ -201,7 +189,7 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi return true; } -static bool compileJSFile(const QString &inputFileName, const QString &outputFileName, QV4::EvalISelFactory *iselFactory, const QString &targetABI, Error *error) +static bool compileJSFile(const QString &inputFileName, const QString &outputFileName, const QString &targetABI, Error *error) { QmlIR::Document irDocument(/*debugMode*/false); irDocument.jsModule.targetABI = targetABI; @@ -256,11 +244,12 @@ static bool compileJSFile(const QString &inputFileName, const QString &outputFil } { - QmlIR::JSCodeGen v4CodeGen(inputFileName, irDocument.code, &irDocument.jsGenerator, + irDocument.jsModule.fileName = inputFileName; + QmlIR::JSCodeGen v4CodeGen(irDocument.code, &irDocument.jsGenerator, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable); - v4CodeGen.generateFromProgram(/*empty input file name*/QString(), sourceCode, program, &irDocument.jsModule, QQmlJS::Codegen::GlobalCode); + v4CodeGen.generateFromProgram(inputFileName, sourceCode, program, &irDocument.jsModule, QQmlJS::GlobalCode); QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors(); if (!jsErrors.isEmpty()) { for (const QQmlJS::DiagnosticMessage &e: qAsConst(jsErrors)) { @@ -274,11 +263,9 @@ static bool compileJSFile(const QString &inputFileName, const QString &outputFil QmlIR::QmlUnitGenerator generator; - QV4::ExecutableAllocator allocator; - QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, &allocator, &irDocument.jsModule, &irDocument.jsGenerator)); // Disable lookups in non-standalone (aka QML) mode - isel->setUseFastLookups(false); - irDocument.javaScriptCompilationUnit = isel->compile(/*generate unit*/false); + v4CodeGen.setUseFastLookups(false); + irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(/*generate unit*/false); QV4::CompiledData::Unit *unit = generator.generate(irDocument); unit->flags |= QV4::CompiledData::Unit::StaticData; irDocument.javaScriptCompilationUnit->data = unit; @@ -330,17 +317,12 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - QScopedPointer<QV4::EvalISelFactory> isel; - const QString targetArchitecture = parser.value(targetArchitectureOption); - - isel.reset(QV4::JIT::createISelForArchitecture(targetArchitecture)); - - if (parser.isSet(checkIfSupportedOption)) { - if (isel.isNull()) - return EXIT_FAILURE; - else - return EXIT_SUCCESS; - } +// if (parser.isSet(checkIfSupportedOption)) { +// if (isel.isNull()) +// return EXIT_FAILURE; +// else +// return EXIT_SUCCESS; +// } const QStringList sources = parser.positionalArguments(); if (sources.isEmpty()){ @@ -351,9 +333,6 @@ int main(int argc, char **argv) } const QString inputFile = sources.first(); - if (!isel) - isel.reset(new QV4::Moth::ISelFactory); - Error error; QString outputFileName = inputFile + QLatin1Char('c'); @@ -363,12 +342,12 @@ int main(int argc, char **argv) const QString targetABI = parser.value(targetABIOption); if (inputFile.endsWith(QLatin1String(".qml"))) { - if (!compileQmlFile(inputFile, outputFileName, isel.data(), targetABI, &error)) { + if (!compileQmlFile(inputFile, outputFileName, targetABI, &error)) { error.augment(QLatin1String("Error compiling qml file: ")).print(); return EXIT_FAILURE; } } else if (inputFile.endsWith(QLatin1String(".js"))) { - if (!compileJSFile(inputFile, outputFileName, isel.data(), targetABI, &error)) { + if (!compileJSFile(inputFile, outputFileName, targetABI, &error)) { error.augment(QLatin1String("Error compiling qml file: ")).print(); return EXIT_FAILURE; } diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp index 0fd897225c..e73dcd4dc6 100644 --- a/tools/qmljs/qmljs.cpp +++ b/tools/qmljs/qmljs.cpp @@ -33,7 +33,6 @@ #include "private/qv4globalobject_p.h" #include "private/qv4codegen_p.h" #include "private/qv4objectproto_p.h" -#include "private/qv4isel_p.h" #include "private/qv4mm_p.h" #include "private/qv4context_p.h" #include "private/qv4script_p.h" |