diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2017-11-21 12:29:40 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2017-11-21 12:29:40 +0100 |
commit | d373d5e7d70e968cfba8957596ed6fe4f46990c8 (patch) | |
tree | c52bf2b0fbbfdb13d644b4050aa7a931ef4b7109 /src/qml/compiler | |
parent | 9880acb424fd814501ba5fc4ae1caa989e23fafa (diff) | |
parent | 9af8a47746b69b6040fc149c1d24602a1e25b08f (diff) |
Merge remote-tracking branch 'origin/wip/new-backend' into dev
Conflicts:
src/qml/compiler/qv4isel_moth.cpp
src/qml/compiler/qv4jsir_p.h
src/qml/jsruntime/qv4engine_p.h
src/qml/jsruntime/qv4vme_moth.cpp
tests/auto/qml/qml.pro
Change-Id: Ia7b6ec24c7fcbcbb1786d9e798d2df294020ae37
Diffstat (limited to 'src/qml/compiler')
29 files changed, 6007 insertions, 15058 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index d9b985e33b..0d63d3b76f 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -2,26 +2,24 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD HEADERS += \ + $$PWD/qv4bytecodegenerator_p.h \ $$PWD/qv4compileddata_p.h \ $$PWD/qv4compiler_p.h \ + $$PWD/qv4compilercontext_p.h \ + $$PWD/qv4compilercontrolflow_p.h \ + $$PWD/qv4compilerscanfunctions_p.h \ $$PWD/qv4codegen_p.h \ - $$PWD/qv4isel_p.h \ - $$PWD/qv4jsir_p.h \ - $$PWD/qv4isel_util_p.h \ - $$PWD/qv4ssa_p.h \ $$PWD/qqmlirbuilder_p.h \ - $$PWD/qqmltypecompiler_p.h \ - $$PWD/qv4jssimplifier_p.h + $$PWD/qqmltypecompiler_p.h SOURCES += \ + $$PWD/qv4bytecodegenerator.cpp \ $$PWD/qv4compileddata.cpp \ $$PWD/qv4compiler.cpp \ + $$PWD/qv4compilercontext.cpp \ + $$PWD/qv4compilerscanfunctions.cpp \ $$PWD/qv4codegen.cpp \ - $$PWD/qv4isel_p.cpp \ - $$PWD/qv4jsir.cpp \ - $$PWD/qv4ssa.cpp \ - $$PWD/qqmlirbuilder.cpp \ - $$PWD/qv4jssimplifier.cpp + $$PWD/qqmlirbuilder.cpp !qmldevtools_build { @@ -46,11 +44,9 @@ qtConfig(private_tests):qtConfig(dlopen): QMAKE_USE_PRIVATE += libdl qmldevtools_build|qtConfig(qml-interpreter) { HEADERS += \ - $$PWD/qv4instr_moth_p.h \ - $$PWD/qv4isel_moth_p.h + $$PWD/qv4instr_moth_p.h SOURCES += \ - $$PWD/qv4instr_moth.cpp \ - $$PWD/qv4isel_moth.cpp + $$PWD/qv4instr_moth.cpp } gcc { diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 02ff8947c4..128eb2c720 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -43,6 +43,7 @@ #include <private/qv4compileddata_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljslexer_p.h> +#include <private/qv4compilerscanfunctions_p.h> #include <QCoreApplication> #include <QCryptographicHash> #include <cmath> @@ -61,7 +62,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 @@ -1067,15 +1068,18 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST } else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) { binding->type = QV4::CompiledData::Binding::Type_Number; binding->setNumberValueInternal(lit->value); + } else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) { + if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base)) { + tryGeneratingTranslationBinding(base->name, call->arguments, binding); + // If it wasn't a translation binding, a normal script binding will be generated + // below. + } } else if (QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(expr)) { binding->flags |= QV4::CompiledData::Binding::IsFunctionExpression; - } else { - - if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) { - if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) { - binding->type = QV4::CompiledData::Binding::Type_Number; - binding->setNumberValueInternal(-lit->value); - } + } else if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) { + if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) { + binding->type = QV4::CompiledData::Binding::Type_Number; + binding->setNumberValueInternal(-lit->value); } } } @@ -1097,6 +1101,121 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST } } +void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::ArgumentList *args, QV4::CompiledData::Binding *binding) +{ + if (base == QLatin1String("qsTr")) { + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + translationData.commentIndex = 0; // empty string + + if (!args || !args->expression) + return; // no arguments, stop + + QStringRef translation; + if (QQmlJS::AST::StringLiteral *arg1 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) { + translation = arg1->value; + } else { + return; // first argument is not a string, stop + } + + args = args->next; + + if (args) { + QQmlJS::AST::StringLiteral *arg2 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression); + if (!arg2) + return; // second argument is not a string, stop + translationData.commentIndex = jsGenerator->registerString(arg2->value.toString()); + + args = args->next; + if (args) { + if (QQmlJS::AST::NumericLiteral *arg3 = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(args->expression)) { + translationData.number = int(arg3->value); + args = args->next; + } else { + return; // third argument is not a translation number, stop + } + } + } + + if (args) + return; // too many arguments, stop + + binding->type = QV4::CompiledData::Binding::Type_Translation; + binding->stringIndex = jsGenerator->registerString(translation.toString()); + binding->value.translationData = translationData; + } else if (base == QLatin1String("qsTrId")) { + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + translationData.commentIndex = 0; // empty string, but unused + + if (!args || !args->expression) + return; // no arguments, stop + + QStringRef id; + if (QQmlJS::AST::StringLiteral *arg1 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) { + id = arg1->value; + } else { + return; // first argument is not a string, stop + } + + args = args->next; + + if (args) { + if (QQmlJS::AST::NumericLiteral *arg3 = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(args->expression)) { + translationData.number = int(arg3->value); + args = args->next; + } else { + return; // third argument is not a translation number, stop + } + } + + if (args) + return; // too many arguments, stop + + binding->type = QV4::CompiledData::Binding::Type_TranslationById; + binding->stringIndex = jsGenerator->registerString(id.toString()); + binding->value.translationData = translationData; + } else if (base == QLatin1String("QT_TR_NOOP") || base == QLatin1String("QT_TRID_NOOP")) { + if (!args || !args->expression) + return; // no arguments, stop + + QStringRef str; + if (QQmlJS::AST::StringLiteral *arg1 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) { + str = arg1->value; + } else { + return; // first argument is not a string, stop + } + + args = args->next; + if (args) + return; // too many arguments, stop + + binding->type = QV4::CompiledData::Binding::Type_String; + binding->stringIndex = jsGenerator->registerString(str.toString()); + } else if (base == QLatin1String("QT_TRANSLATE_NOOP")) { + if (!args || !args->expression) + return; // no arguments, stop + + args = args->next; + if (!args || !args->expression) + return; // no second arguments, stop + + QStringRef str; + if (QQmlJS::AST::StringLiteral *arg2 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) { + str = arg2->value; + } else { + return; // first argument is not a string, stop + } + + args = args->next; + if (args) + return; // too many arguments, stop + + binding->type = QV4::CompiledData::Binding::Type_String; + binding->stringIndex = jsGenerator->registerString(str.toString()); + } +} + void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value) { const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken; @@ -1649,9 +1768,10 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding return bindingPtr; } -JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR::Module *jsModule, QQmlJS::Engine *jsEngine, +JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, + QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool) - : QQmlJS::Codegen(/*strict mode*/false) + : QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false) , sourceCode(sourceCode) , jsEngine(jsEngine) , qmlRoot(qmlRoot) @@ -1660,11 +1780,10 @@ JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR , _disableAcceleratedLookups(false) , _contextObject(0) , _scopeObject(0) - , _qmlContextTemp(-1) - , _importedScriptsTemp(-1) + , _qmlContextSlot(-1) + , _importedScriptsSlot(-1) { _module = jsModule; - _module->setFileName(fileName); _fileNameIsUrl = true; } @@ -1684,8 +1803,8 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil { QVector<int> runtimeFunctionIndices(functions.size()); - ScanFunctions scan(this, sourceCode, GlobalCode); - scan.enterEnvironment(0, QmlBinding); + QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::GlobalCode); + scan.enterEnvironment(0, QV4::Compiler::QmlBinding); scan.enterQmlScope(qmlRoot, QStringLiteral("context scope")); for (const CompiledFunctionOrExpression &f : functions) { Q_ASSERT(f.node != qmlRoot); @@ -1694,7 +1813,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil if (function) scan.enterQmlFunction(function); else - scan.enterEnvironment(f.node, QmlBinding); + scan.enterEnvironment(f.node, QV4::Compiler::QmlBinding); scan(function ? function->body : f.node); scan.leaveEnvironment(); @@ -1702,8 +1821,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil scan.leaveEnvironment(); scan.leaveEnvironment(); - _variableEnvironment = 0; - _function = _module->functions.at(defineFunction(QStringLiteral("context scope"), qmlRoot, 0, 0)); + _context = 0; for (int i = 0; i < functions.count(); ++i) { const CompiledFunctionOrExpression &qmlFunction = functions.at(i); @@ -1745,35 +1863,36 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil runtimeFunctionIndices[i] = idx; } - qDeleteAll(_envMap); - _envMap.clear(); return runtimeFunctionIndices; } +int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::SourceElements *body) +{ + int qmlContextTemp = -1; + int importedScriptsTemp = -1; + qSwap(_qmlContextSlot, qmlContextTemp); + qSwap(_importedScriptsSlot, importedScriptsTemp); + + int result = Codegen::defineFunction(name, ast, formals, body); + + qSwap(_importedScriptsSlot, importedScriptsTemp); + qSwap(_qmlContextSlot, qmlContextTemp); + + return result; +} + #ifndef V4_BOOTSTRAP -QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name, bool *propertyExistsButForceNameLookup) +QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name) { - if (propertyExistsButForceNameLookup) - *propertyExistsButForceNameLookup = false; QQmlPropertyData *pd = cache->property(name, /*object*/0, /*context*/0); // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time - if (pd && pd->isFunction()) { - if (propertyExistsButForceNameLookup) - *propertyExistsButForceNameLookup = true; - pd = 0; - } + if (!pd || pd->isFunction()) + return 0; - if (pd && !cache->isAllowedInRevision(pd)) - pd = 0; + if (!cache->isAllowedInRevision(pd)) + return 0; - // Return a copy allocated from our memory pool. Property data pointers can change - // otherwise when the QQmlPropertyCache changes later in the QML type compilation process. - if (pd) { - QQmlPropertyData *copy = pd; - pd = _function->New<QQmlPropertyData>(); - *pd = *copy; - } return pd; } @@ -1784,7 +1903,9 @@ enum MetaObjectResolverFlags { ResolveTypeInformationOnly = 0x8 }; +#if 0 static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject); + static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index); static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, @@ -2018,34 +2139,40 @@ static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, resolver->qmlType = qmlType; resolver->flags = index; } +#endif #endif // V4_BOOTSTRAP void JSCodeGen::beginFunctionBodyHook() { - _qmlContextTemp = _block->newTemp(); - _importedScriptsTemp = _block->newTemp(); + _qmlContextSlot = bytecodeGenerator->newRegister(); + _importedScriptsSlot = bytecodeGenerator->newRegister(); #ifndef V4_BOOTSTRAP - QV4::IR::Temp *temp = _block->TEMP(_qmlContextTemp); + Instruction::LoadQmlContext load; + load.result = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot(); + bytecodeGenerator->addInstruction(load); + +#if 0 temp->type = QV4::IR::QObjectType; temp->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); initMetaObjectResolver(temp->memberResolver, _scopeObject); auto name = _block->NAME(QV4::IR::Name::builtin_qml_context, 0, 0); name->type = temp->type; - move(temp, name); +#endif - move(_block->TEMP(_importedScriptsTemp), _block->NAME(QV4::IR::Name::builtin_qml_imported_scripts_object, 0, 0)); + Instruction::LoadQmlImportedScripts loadScripts; + loadScripts.result = Reference::fromStackSlot(this, _importedScriptsSlot).stackSlot(); + bytecodeGenerator->addInstruction(loadScripts); #endif } -QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col) +QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name) { - Q_UNUSED(line) - Q_UNUSED(col) #ifndef V4_BOOTSTRAP if (_disableAcceleratedLookups) - return 0; + return Reference(); + // Implement QML lookup semantics in the current file context. // // Note: We do not check if properties of the qml scope object or context object @@ -2060,20 +2187,16 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int // Look for IDs first. for (const IdMapping &mapping : qAsConst(_idObjects)) { if (name == mapping.name) { - if (_function->isQmlBinding) - _function->idObjectDependencies.insert(mapping.idIndex); - - QV4::IR::Expr *s = _block->MEMBER(_block->TEMP(_qmlContextTemp), _function->newString(name), 0, QV4::IR::Member::MemberOfIdObjectsArray, mapping.idIndex); - QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); - _block->MOVE(result, s); - result = _block->TEMP(result->index); - if (mapping.type) { - result->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - result->memberResolver->owner = _function; - initMetaObjectResolver(result->memberResolver, mapping.type); - result->memberResolver->flags |= AllPropertiesAreFinal; - } - result->isReadOnly = true; // don't allow use as lvalue + if (_context->compilationMode == QV4::Compiler::QmlBinding) + _context->idObjectDependencies.insert(mapping.idIndex); + + Instruction::LoadIdObject load; + load.base = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot(); + load.index = mapping.idIndex; + + Reference result = Reference::fromAccumulator(this); + bytecodeGenerator->addInstruction(load); + result.isReadonly = true; return result; } } @@ -2082,69 +2205,47 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int QQmlTypeNameCache::Result r = imports->query(name); if (r.isValid()) { if (r.scriptIndex != -1) { - return _block->SUBSCRIPT(_block->TEMP(_importedScriptsTemp), - _block->CONST(QV4::IR::SInt32Type, r.scriptIndex)); + Reference imports = Reference::fromStackSlot(this, _importedScriptsSlot); + return Reference::fromSubscript(imports, Reference::fromConst(this, QV4::Encode(r.scriptIndex))); } else if (r.type.isValid()) { - QV4::IR::Name *typeName = _block->NAME(name, line, col); - // Make sure the run-time loads this through the more efficient singleton getter. - typeName->qmlSingleton = r.type.isCompositeSingleton(); - typeName->freeOfSideEffects = true; - QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); - _block->MOVE(result, typeName); - - result = _block->TEMP(result->index); - result->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - result->memberResolver->owner = _function; - initQmlTypeResolver(result->memberResolver, r.type); - return result; + if (r.type.isCompositeSingleton()) { + Instruction::LoadQmlSingleton load; + load.name = registerString(name); + bytecodeGenerator->addInstruction(load); + return Reference::fromAccumulator(this); + } + return Reference::fromName(this, name); } else { Q_ASSERT(r.importNamespace); - QV4::IR::Name *namespaceName = _block->NAME(name, line, col); - namespaceName->freeOfSideEffects = true; - QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); - result->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - result->memberResolver->owner = _function; - initImportNamespaceResolver(result->memberResolver, imports, r.importNamespace); - - _block->MOVE(result, namespaceName); - return _block->TEMP(result->index); + return Reference::fromName(this, name); } } } if (_scopeObject) { - bool propertyExistsButForceNameLookup = false; - QQmlPropertyData *pd = lookupQmlCompliantProperty(_scopeObject, name, &propertyExistsButForceNameLookup); - if (propertyExistsButForceNameLookup) - return 0; - if (pd) { - QV4::IR::Temp *base = _block->TEMP(_qmlContextTemp); - base->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - base->memberResolver->owner = _function; - initMetaObjectResolver(base->memberResolver, _scopeObject); - return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlScopeObject); - } + QQmlPropertyData *data = lookupQmlCompliantProperty(_scopeObject, name); + if (!data) + return Reference::fromName(this, name); + Reference base = Reference::fromStackSlot(this, _qmlContextSlot); + bool captureRequired = !data->isConstant() && !data->isQmlBinding(); + return Reference::fromQmlScopeObject(base, data->coreIndex(), data->notifyIndex(), + captureRequired); } if (_contextObject) { - bool propertyExistsButForceNameLookup = false; - QQmlPropertyData *pd = lookupQmlCompliantProperty(_contextObject, name, &propertyExistsButForceNameLookup); - if (propertyExistsButForceNameLookup) - return 0; - if (pd) { - QV4::IR::Temp *base = _block->TEMP(_qmlContextTemp); - base->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - base->memberResolver->owner = _function; - initMetaObjectResolver(base->memberResolver, _contextObject); - return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlContextObject); - } + QQmlPropertyData *data = lookupQmlCompliantProperty(_contextObject, name); + if (!data) + return Reference::fromName(this, name); + Reference base = Reference::fromStackSlot(this, _qmlContextSlot); + bool captureRequired = !data->isConstant() && !data->isQmlBinding(); + return Reference::fromQmlContextObject(base, data->coreIndex(), data->notifyIndex(), + captureRequired); } - #else Q_UNUSED(name) #endif // V4_BOOTSTRAP // fall back to name lookup at run-time. - return 0; + return Reference(); } #ifndef V4_BOOTSTRAP diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index a5b4815745..406c939998 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -453,7 +453,7 @@ struct Q_QML_PRIVATE_EXPORT Document Document(bool debugMode); QString code; QQmlJS::Engine jsParserEngine; - QV4::IR::Module jsModule; + QV4::Compiler::Module jsModule; QList<const QV4::CompiledData::Import *> imports; QList<Pragma*> pragmas; QQmlJS::AST::UiProgram *program; @@ -527,6 +527,7 @@ public: const QQmlJS::AST::SourceLocation &last) const; void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement); + void tryGeneratingTranslationBinding(const QStringRef &base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding); void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value); void appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false); @@ -606,9 +607,9 @@ struct Q_QML_EXPORT PropertyResolver }; #endif -struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QQmlJS::Codegen +struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen { - JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR::Module *jsModule, + JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool); @@ -626,12 +627,17 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QQmlJS::Codegen // Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions); + int defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body) override; + protected: void beginFunctionBodyHook() override; - QV4::IR::Expr *fallbackNameLookup(const QString &name, int line, int col) override; + Reference fallbackNameLookup(const QString &name) override; private: - QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name, bool *propertyExistsButForceNameLookup = 0); + // returns nullptr if lookup needs to happen by name + QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name); QString sourceCode; QQmlJS::Engine *jsEngine; // needed for memory pool @@ -643,8 +649,8 @@ private: ObjectIdMapping _idObjects; QQmlPropertyCache *_contextObject; QQmlPropertyCache *_scopeObject; - int _qmlContextTemp; - int _importedScriptsTemp; + int _qmlContextSlot; + int _importedScriptsSlot; }; struct Q_QML_PRIVATE_EXPORT IRLoader { diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 46f0c46b99..97ca597953 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -44,10 +44,9 @@ #include <private/qqmlcustomparser_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlcomponent_p.h> -#include <private/qv4ssa_p.h> #include "qqmlpropertycachecreator_p.h" -#include "qv4jssimplifier_p.h" +//#include "qv4jssimplifier_p.h" #define COMPILE_EXCEPTION(token, desc) \ { \ @@ -140,19 +139,15 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() sss.scan(); } - QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, typeNameCache, &document->jsGenerator.stringTable); + document->jsModule.fileName = typeData->finalUrlString(); + QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine, document->program, typeNameCache, &document->jsGenerator.stringTable); + v4CodeGenerator.setUseFastLookups(false); + // ### v4CodeGenerator.setUseTypeInference(true); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) return nullptr; - QQmlJavaScriptBindingExpressionSimplificationPass pass(document->objects, &document->jsModule, &document->jsGenerator); - pass.reduceTranslationBindings(); - - QV4::ExecutionEngine *v4 = engine->v4engine(); - QScopedPointer<QV4::EvalInstructionSelection> isel(v4->iselFactory->create(engine, v4->executableAllocator, &document->jsModule, &document->jsGenerator)); - isel->setUseFastLookups(false); - isel->setUseTypeInference(true); - document->javaScriptCompilationUnit = isel->compile(/*generated unit data*/false); + document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false); } // Generate QML compiled type data structures @@ -208,11 +203,6 @@ int QQmlTypeCompiler::registerString(const QString &str) return document->jsGenerator.registerString(str); } -QV4::IR::Module *QQmlTypeCompiler::jsIRModule() const -{ - return &document->jsModule; -} - const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const { return document->javaScriptCompilationUnit->data; @@ -501,7 +491,9 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio QQmlJS::AST::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements); functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body); - functionDeclaration->functionToken = foe->node->firstSourceLocation(); + functionDeclaration->lbraceToken = functionDeclaration->functionToken + = foe->node->firstSourceLocation(); + functionDeclaration->rbraceToken = foe->node->lastSourceLocation(); } foe->node = functionDeclaration; binding->propertyNameIndex = compiler->registerString(propertyName); diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 4878a90641..d905b956c7 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -111,8 +111,6 @@ public: int registerString(const QString &str); - QV4::IR::Module *jsIRModule() const; - const QV4::CompiledData::Unit *qmlUnit() const; QUrl url() const { return typeData->finalUrl(); } diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp new file mode 100644 index 0000000000..05bbf25292 --- /dev/null +++ b/src/qml/compiler/qv4bytecodegenerator.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 <private/qv4bytecodegenerator_p.h> +#include <private/qv4compilercontext_p.h> +#include <private/qqmljsastfwd_p.h> +#include <private/qv4compileddata_p.h> + +QT_USE_NAMESPACE +using namespace QV4; +using namespace Moth; + +void BytecodeGenerator::setLocation(const QQmlJS::AST::SourceLocation &loc) +{ + currentLine = static_cast<int>(loc.startLine); +} + +int BytecodeGenerator::newRegister() +{ + int t = currentReg++; + if (regCount < currentReg) + regCount = currentReg; + return t; +} + +int BytecodeGenerator::newRegisterArray(int n) +{ + int t = currentReg; + currentReg += n; + if (regCount < currentReg) + regCount = currentReg; + return t; +} + +void BytecodeGenerator::packInstruction(I &i) +{ + uchar type = *reinterpret_cast<uchar *>(i.packed); + Q_ASSERT(type >= MOTH_NUM_INSTRUCTIONS()); + if (type >= MOTH_NUM_INSTRUCTIONS()) + type -= MOTH_NUM_INSTRUCTIONS(); + int instructionsAsInts[sizeof(Instr)/sizeof(int)]; + int nMembers = Moth::InstrInfo::argumentCount[static_cast<int>(i.type)]; + memcpy(instructionsAsInts, i.packed + 1, nMembers*sizeof(int)); + enum { + Normal, + Wide + } width = Normal; + for (int n = 0; n < nMembers; ++n) { + if (width == Normal && (static_cast<char>(instructionsAsInts[n]) != instructionsAsInts[n])) + width = Wide; + } + char *code = i.packed; + switch (width) { + case Normal: + *reinterpret_cast<uchar *>(code) = type; + ++code; + for (int n = 0; n < nMembers; ++n) { + char v = static_cast<char>(instructionsAsInts[n]); + memcpy(code, &v, 1); + code += 1; + } + i.size = code - i.packed; + if (i.offsetForJump != -1) + i.offsetForJump = i.size - 1; + break; + case Wide: + // nothing to do + break; + } +} + +void BytecodeGenerator::adjustJumpOffsets() +{ + for (int index = 0; index < instructions.size(); ++index) { + auto &i = instructions[index]; + if (i.offsetForJump == -1) // no jump + continue; + Q_ASSERT(i.linkedLabel != -1 && labels.at(i.linkedLabel) != -1); + const auto &linkedInstruction = instructions.at(labels.at(i.linkedLabel)); + char *c = i.packed + i.offsetForJump; + int jumpOffset = linkedInstruction.position - (i.position + i.size); +// qDebug() << "adjusting jump offset for instruction" << index << i.position << i.size << "offsetForJump" << i.offsetForJump << "target" +// << labels.at(i.linkedLabel) << linkedInstruction.position << "jumpOffset" << jumpOffset; + uchar type = *reinterpret_cast<const uchar *>(i.packed); + if (type >= MOTH_NUM_INSTRUCTIONS()) { + Q_ASSERT(i.offsetForJump == i.size - 4); + memcpy(c, &jumpOffset, sizeof(int)); + } else { + Q_ASSERT(i.offsetForJump == i.size - 1); + char o = jumpOffset; + Q_ASSERT(o == jumpOffset); + *c = o; + } + } +} + +void BytecodeGenerator::compressInstructions() +{ + // first round: compress all non jump instructions + int position = 0; + for (auto &i : instructions) { + i.position = position; + if (i.offsetForJump == -1) + packInstruction(i); + position += i.size; + } + + adjustJumpOffsets(); + + // compress all jumps + position = 0; + for (auto &i : instructions) { + i.position = position; + if (i.offsetForJump != -1) + packInstruction(i); + position += i.size; + } + + // adjust once again, as the packing above could have changed offsets + adjustJumpOffsets(); +} + +void BytecodeGenerator::finalize(Compiler::Context *context) +{ + compressInstructions(); + + // collect content and line numbers + QByteArray code; + QVector<CompiledData::CodeOffsetToLine> lineNumbers; + currentLine = -1; + Q_UNUSED(startLine); + for (const auto &i : qAsConst(instructions)) { + if (i.line != currentLine) { + currentLine = i.line; + CompiledData::CodeOffsetToLine entry; + entry.codeOffset = code.size(); + entry.line = currentLine; + lineNumbers.append(entry); + } + code.append(i.packed, i.size); + } + + context->code = code; + context->lineNumberMapping = lineNumbers; +} + +int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) { + if (debugMode && type != Instr::Type::Debug) { +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug() + if (instructions.isEmpty() || currentLine != instructions.constLast().line) { + addInstruction(Instruction::Debug()); + } else if (type == Instr::Type::Ret) { + currentLine = -currentLine; + addInstruction(Instruction::Debug()); + currentLine = -currentLine; + } +QT_WARNING_POP + } + + const int pos = instructions.size(); + + int s = Moth::InstrInfo::argumentCount[static_cast<int>(type)]*sizeof(int); + if (offsetOfOffset != -1) + offsetOfOffset += 1; + I instr{type, static_cast<short>(s + 1), 0, currentLine, offsetOfOffset, -1, "\0\0" }; + char *code = instr.packed; + *reinterpret_cast<uchar *>(code) = static_cast<uchar>(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type)); + ++code; + Q_ASSERT(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type) < 256); + memcpy(code, &i, s); + instructions.append(instr); + + return pos; +} diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h new file mode 100644 index 0000000000..3d516da922 --- /dev/null +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QV4BYTECODEGENERATOR_P_H +#define QV4BYTECODEGENERATOR_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/qv4instr_moth_p.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace AST { +class SourceLocation; +} +} + +namespace QV4 { +namespace Moth { + +class BytecodeGenerator { +public: + BytecodeGenerator(int line, bool debug) + : startLine(line), debugMode(debug) {} + + struct Label { + enum LinkMode { + LinkNow, + LinkLater + }; + Label() = default; + Label(BytecodeGenerator *generator, LinkMode mode = LinkNow) + : generator(generator), + index(generator->labels.size()) { + generator->labels.append(mode == LinkNow ? generator->instructions.size() : -1); + } + static Label returnLabel() { + Label l; + l.index = INT_MAX; + return l; + } + bool isReturn() const { + return index == INT_MAX; + } + + void link() { + Q_ASSERT(index >= 0); + Q_ASSERT(generator->labels[index] == -1); + generator->labels[index] = generator->instructions.size(); + } + + BytecodeGenerator *generator = 0; + int index = -1; + }; + + struct Jump { + Jump(BytecodeGenerator *generator, int instruction) + : generator(generator), + index(instruction) + {} + ~Jump() { + Q_ASSERT(generator->instructions[index].linkedLabel != -1); + } + + + BytecodeGenerator *generator; + int index; + + void link() { + link(generator->label()); + } + void link(Label l) { + Q_ASSERT(l.index >= 0); + Q_ASSERT(generator->instructions[index].linkedLabel == -1); + generator->instructions[index].linkedLabel = l.index; + } + }; + + struct ExceptionHandler : public Label { + ExceptionHandler(BytecodeGenerator *generator) + : Label(generator, LinkLater) + { + } + ~ExceptionHandler() + { + Q_ASSERT(generator->currentExceptionHandler != this); + } + }; + + Label label() { + return Label(this, Label::LinkNow); + } + + Label newLabel() { + return Label(this, Label::LinkLater); + } + + ExceptionHandler newExceptionHandler() { + return ExceptionHandler(this); + } + + template<int InstrT> + void addInstruction(const InstrData<InstrT> &data) + { + Instr genericInstr; + InstrMeta<InstrT>::setData(genericInstr, data); + addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr); + } + + Q_REQUIRED_RESULT Jump jump() + { + Instruction::Jump data; + return addJumpInstruction(data); + } + + Q_REQUIRED_RESULT Jump jumpTrue() + { + Instruction::JumpTrue data; + return addJumpInstruction(data); + } + + Q_REQUIRED_RESULT Jump jumpFalse() + { + Instruction::JumpFalse data; + return addJumpInstruction(data); + } + + void jumpStrictEqual(const StackSlot &lhs, const Label &target) + { + Instruction::CmpStrictEqual cmp; + cmp.lhs = lhs; + addInstruction(cmp); + addJumpInstruction(Instruction::JumpTrue()).link(target); + } + + void jumpStrictNotEqual(const StackSlot &lhs, const Label &target) + { + Instruction::CmpStrictNotEqual cmp; + cmp.lhs = lhs; + addInstruction(cmp); + addJumpInstruction(Instruction::JumpTrue()).link(target); + } + + Q_REQUIRED_RESULT Jump jumpStrictEqualStackSlotInt(const StackSlot &lhs, int rhs) + { + Instruction::JumpStrictEqualStackSlotInt data; + data.lhs = lhs; + data.rhs = rhs; + return addJumpInstruction(data); + } + + Q_REQUIRED_RESULT Jump jumpStrictNotEqualStackSlotInt(const StackSlot &lhs, int rhs) + { + Instruction::JumpStrictNotEqualStackSlotInt data; + data.lhs = lhs; + data.rhs = rhs; + return addJumpInstruction(data); + } + + void setExceptionHandler(ExceptionHandler *handler) + { + currentExceptionHandler = handler; + Instruction::SetExceptionHandler data; + data.offset = 0; + if (!handler) + addInstruction(data); + else + addJumpInstruction(data).link(*handler); + } + + void setLocation(const QQmlJS::AST::SourceLocation &loc); + + ExceptionHandler *exceptionHandler() const { + return currentExceptionHandler; + } + + int newRegister(); + int newRegisterArray(int n); + int registerCount() const { return regCount; } + + void finalize(Compiler::Context *context); + + template<int InstrT> + Jump addJumpInstruction(const InstrData<InstrT> &data) + { + Instr genericInstr; + InstrMeta<InstrT>::setData(genericInstr, data); + return Jump(this, addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr, offsetof(InstrData<InstrT>, offset))); + } + + void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel) + { + if (jumpOnFalse) + addJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); + else + addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); + } + +private: + friend struct Jump; + friend struct Label; + friend struct ExceptionHandler; + + int addInstructionHelper(Moth::Instr::Type type, const Instr &i, int offsetOfOffset = -1); + + struct I { + Moth::Instr::Type type; + short size; + uint position; + int line; + int offsetForJump; + int linkedLabel; + char packed[sizeof(Instr) + 2]; // 2 for instruction and prefix + }; + + void compressInstructions(); + void packInstruction(I &i); + void adjustJumpOffsets(); + + QVector<I> instructions; + QVector<int> labels; + ExceptionHandler *currentExceptionHandler; + int regCount = 0; +public: + int currentReg = 0; +private: + int startLine = 0; + int currentLine = 0; + bool debugMode = false; +}; + +} +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 3fff29bce0..b9d223bab0 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -39,7 +39,6 @@ #include "qv4codegen_p.h" #include "qv4util_p.h" -#include "qv4engine_p.h" #include <QtCore/QCoreApplication> #include <QtCore/QStringList> @@ -51,10 +50,10 @@ #include <private/qqmljsast_p.h> #include <private/qv4string_p.h> #include <private/qv4value_p.h> - -#ifndef V4_BOOTSTRAP -#include <qv4context_p.h> -#endif +#include <private/qv4compilercontext_p.h> +#include <private/qv4compilercontrolflow_p.h> +#include <private/qv4bytecodegenerator_p.h> +#include <private/qv4compilerscanfunctions_p.h> #include <cmath> #include <iostream> @@ -63,37 +62,13 @@ #undef CONST #endif +QT_USE_NAMESPACE using namespace QV4; -using namespace QQmlJS; -using namespace AST; - -static inline void setLocation(IR::Stmt *s, const SourceLocation &loc) -{ - if (s && loc.isValid()) - s->location = loc; -} - -static bool cjumpCanHandle(IR::AluOp op) -{ - switch (op) { - case IR::OpIn: - case IR::OpInstanceof: - case IR::OpEqual: - case IR::OpNotEqual: - case IR::OpGe: - case IR::OpGt: - case IR::OpLe: - case IR::OpLt: - case IR::OpStrictEqual: - case IR::OpStrictNotEqual: - return true; - default: - return false; - } -} +using namespace QV4::Compiler; +using namespace QQmlJS::AST; -static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body, - const SourceLocation &fallback) +static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGenerator, + const Statement *body, const SourceLocation &fallback) { switch (body->kind) { // Statements where we might never execute the last line. @@ -105,672 +80,170 @@ static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body, case Statement::Kind_LocalForEachStatement: case Statement::Kind_LocalForStatement: case Statement::Kind_WhileStatement: - setLocation(s, fallback); + bytecodeGenerator->setLocation(fallback); break; default: - setLocation(s, body->lastSourceLocation()); + bytecodeGenerator->setLocation(body->lastSourceLocation()); break; } } -static inline bool isSimpleExpr(IR::Expr *e) -{ - switch (e->exprKind) { - case IR::Expr::TempExpr: - case IR::Expr::ArgLocalExpr: - case IR::Expr::ConstExpr: - return true; - default: - return false; - } -} - -Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) - : _cg(cg) - , _sourceCode(sourceCode) - , _variableEnvironment(0) - , _allowFuncDecls(true) - , defaultProgramMode(defaultProgramMode) -{ -} - -void Codegen::ScanFunctions::operator()(Node *node) -{ - if (node) - node->accept(this); -} - -void Codegen::ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) -{ - Environment *e = _cg->newEnvironment(node, _variableEnvironment, compilationMode); - if (!e->isStrict) - e->isStrict = _cg->_strictMode; - _envStack.append(e); - _variableEnvironment = e; -} - -void Codegen::ScanFunctions::leaveEnvironment() -{ - _envStack.pop(); - _variableEnvironment = _envStack.isEmpty() ? 0 : _envStack.top(); -} - -void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) -{ - for (SourceElements *it = ast; it; it = it->next) { - if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) { - if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) { - if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) { - // Use the source code, because the StringLiteral's - // value might have escape sequences in it, which is not - // allowed. - if (strLit->literalToken.length < 2) - continue; - QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); - if (str == QLatin1String("use strict")) { - _variableEnvironment->isStrict = true; - } else { - // TODO: give a warning. - } - continue; - } - } - } - - break; - } -} - -void Codegen::ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc) -{ - if (_variableEnvironment->isStrict) { - if (name == QLatin1String("implements") - || name == QLatin1String("interface") - || name == QLatin1String("let") - || name == QLatin1String("package") - || name == QLatin1String("private") - || name == QLatin1String("protected") - || name == QLatin1String("public") - || name == QLatin1String("static") - || name == QLatin1String("yield")) { - _cg->throwSyntaxError(loc, QStringLiteral("Unexpected strict mode reserved word")); - } - } -} -void Codegen::ScanFunctions::checkForArguments(AST::FormalParameterList *parameters) -{ - while (parameters) { - if (parameters->name == QLatin1String("arguments")) - _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - parameters = parameters->next; - } -} - -bool Codegen::ScanFunctions::visit(Program *ast) -{ - enterEnvironment(ast, defaultProgramMode); - checkDirectivePrologue(ast->elements); - return true; -} - -void Codegen::ScanFunctions::endVisit(Program *) -{ - leaveEnvironment(); -} - -bool Codegen::ScanFunctions::visit(CallExpression *ast) -{ - if (! _variableEnvironment->hasDirectEval) { - if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { - if (id->name == QLatin1String("eval")) { - if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown) - _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed; - _variableEnvironment->hasDirectEval = true; - } - } - } - int argc = 0; - for (ArgumentList *it = ast->arguments; it; it = it->next) - ++argc; - _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); - return true; -} - -bool Codegen::ScanFunctions::visit(NewMemberExpression *ast) -{ - int argc = 0; - for (ArgumentList *it = ast->arguments; it; it = it->next) - ++argc; - _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); - return true; -} - -bool Codegen::ScanFunctions::visit(ArrayLiteral *ast) -{ - int index = 0; - for (ElementList *it = ast->elements; it; it = it->next) { - for (Elision *elision = it->elision; elision; elision = elision->next) - ++index; - ++index; - } - if (ast->elision) { - for (Elision *elision = ast->elision->next; elision; elision = elision->next) - ++index; - } - _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, index); - return true; -} - -bool Codegen::ScanFunctions::visit(VariableDeclaration *ast) -{ - if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); - checkName(ast->name, ast->identifierToken); - if (ast->name == QLatin1String("arguments")) - _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) { - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); - return false; - } - QString name = ast->name.toString(); - const Environment::Member *m = 0; - if (_variableEnvironment->memberInfo(name, &m)) { - if (m->isLexicallyScoped() || ast->isLexicallyScoped()) { - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); - return false; - } - } - _variableEnvironment->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration, ast->scope); - return true; -} - -bool Codegen::ScanFunctions::visit(IdentifierExpression *ast) -{ - checkName(ast->name, ast->identifierToken); - if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) - _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed; - return true; -} - -bool Codegen::ScanFunctions::visit(ExpressionStatement *ast) -{ - if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) { - if (!_allowFuncDecls) - _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration")); - - enterFunction(expr, /*enterName*/ true); - Node::accept(expr->formals, this); - Node::accept(expr->body, this); - leaveEnvironment(); - return false; - } else { - SourceLocation firstToken = ast->firstSourceLocation(); - if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QLatin1String("function")) { - _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token")); - } - } - return true; -} - -bool Codegen::ScanFunctions::visit(FunctionExpression *ast) -{ - enterFunction(ast, /*enterName*/ false); - return true; -} - -void Codegen::ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName, bool isExpression) -{ - if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); - enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); -} - -void Codegen::ScanFunctions::endVisit(FunctionExpression *) -{ - leaveEnvironment(); -} - -bool Codegen::ScanFunctions::visit(ObjectLiteral *ast) -{ - int argc = 0; - for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { - QString key = it->assignment->name->asString(); - if (QV4::String::toArrayIndex(key) != UINT_MAX) - ++argc; - ++argc; - if (AST::cast<AST::PropertyGetterSetter *>(it->assignment)) - ++argc; - } - _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); - Node::accept(ast->properties, this); - return false; -} - -bool Codegen::ScanFunctions::visit(PropertyGetterSetter *ast) -{ - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); - enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false); - return true; -} - -void Codegen::ScanFunctions::endVisit(PropertyGetterSetter *) -{ - leaveEnvironment(); -} - -bool Codegen::ScanFunctions::visit(FunctionDeclaration *ast) -{ - enterFunction(ast, /*enterName*/ true, /*isExpression */false); - return true; -} - -void Codegen::ScanFunctions::endVisit(FunctionDeclaration *) -{ - leaveEnvironment(); -} - -bool Codegen::ScanFunctions::visit(WithStatement *ast) -{ - if (_variableEnvironment->isStrict) { - _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); - return false; - } - - return true; -} - -bool Codegen::ScanFunctions::visit(DoWhileStatement *ast) { - { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); - Node::accept(ast->statement, this); - } - Node::accept(ast->expression, this); - return false; -} - -bool Codegen::ScanFunctions::visit(ForStatement *ast) { - Node::accept(ast->initialiser, this); - Node::accept(ast->condition, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); - Node::accept(ast->statement, this); - - return false; -} - -bool Codegen::ScanFunctions::visit(LocalForStatement *ast) { - Node::accept(ast->declarations, this); - Node::accept(ast->condition, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); - Node::accept(ast->statement, this); - - return false; -} - -bool Codegen::ScanFunctions::visit(ForEachStatement *ast) { - Node::accept(ast->initialiser, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); - Node::accept(ast->statement, this); - - return false; -} - -bool Codegen::ScanFunctions::visit(LocalForEachStatement *ast) { - Node::accept(ast->declaration, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); - Node::accept(ast->statement, this); - - return false; -} - -bool Codegen::ScanFunctions::visit(ThisExpression *) -{ - _variableEnvironment->usesThis = true; - return false; -} - -bool Codegen::ScanFunctions::visit(Block *ast) { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _variableEnvironment->isStrict ? false : _allowFuncDecls); - Node::accept(ast->statements, this); - return false; -} - -void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) -{ - bool wasStrict = false; - if (_variableEnvironment) { - _variableEnvironment->hasNestedFunctions = true; - // The identifier of a function expression cannot be referenced from the enclosing environment. - if (expr) - _variableEnvironment->enter(name, Environment::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr); - if (name == QLatin1String("arguments")) - _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - wasStrict = _variableEnvironment->isStrict; - } - - enterEnvironment(ast, FunctionCode); - checkForArguments(formals); - - _variableEnvironment->isNamedFunctionExpression = isExpression && !name.isEmpty(); - _variableEnvironment->formals = formals; - - if (body) - checkDirectivePrologue(body->elements); - - if (wasStrict || _variableEnvironment->isStrict) { - QStringList args; - for (FormalParameterList *it = formals; it; it = it->next) { - QString arg = it->name.toString(); - if (args.contains(arg)) { - _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); - return; - } - if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) { - _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg)); - return; - } - args += arg; - } - } -} - - -Codegen::Codegen(bool strict) +Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) : _module(0) - , _function(0) - , _block(0) - , _exitBlock(0) , _returnAddress(0) - , _variableEnvironment(0) - , _loop(0) + , _context(0) , _labelledStatement(0) - , _scopeAndFinally(0) + , jsUnitGenerator(jsUnitGenerator) , _strictMode(strict) , _fileNameIsUrl(false) , hasError(false) { + jsUnitGenerator->codeGeneratorName = QStringLiteral("moth"); } void Codegen::generateFromProgram(const QString &fileName, const QString &sourceCode, Program *node, - QV4::IR::Module *module, - CompilationMode mode, - const QStringList &inheritedLocals) + Module *module, + CompilationMode mode) { Q_ASSERT(node); _module = module; - _variableEnvironment = 0; + _context = 0; - _module->setFileName(fileName); + _module->fileName = fileName; ScanFunctions scan(this, sourceCode, mode); scan(node); - defineFunction(QStringLiteral("%entry"), node, 0, node->elements, inheritedLocals); - qDeleteAll(_envMap); - _envMap.clear(); -} - -void Codegen::generateFromFunctionExpression(const QString &fileName, - const QString &sourceCode, - AST::FunctionExpression *ast, - QV4::IR::Module *module) -{ - _module = module; - _module->setFileName(fileName); - _variableEnvironment = 0; - - ScanFunctions scan(this, sourceCode, GlobalCode); - // fake a global environment - scan.enterEnvironment(0, FunctionCode); - scan(ast); - scan.leaveEnvironment(); - - defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); - - qDeleteAll(_envMap); - _envMap.clear(); -} - - -void Codegen::enterEnvironment(Node *node) -{ - _variableEnvironment = _envMap.value(node); - Q_ASSERT(_variableEnvironment); -} - -void Codegen::leaveEnvironment() -{ - Q_ASSERT(_variableEnvironment); - _variableEnvironment = _variableEnvironment->parent; -} - -void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock) -{ - _loop = new Loop(node, breakBlock, continueBlock, _loop); - _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement - _loop->scopeAndFinally = _scopeAndFinally; - _labelledStatement = 0; -} - -void Codegen::leaveLoop() -{ - Loop *current = _loop; - _loop = _loop->parent; - delete current; -} - -IR::Expr *Codegen::member(IR::Expr *base, const QString *name) -{ - if (hasError) - return 0; - - if (base->asTemp() || base->asArgLocal()) - return _block->MEMBER(base, name); - else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), base); - return _block->MEMBER(_block->TEMP(t), name); - } + defineFunction(QStringLiteral("%entry"), node, 0, node->elements); } -IR::Expr *Codegen::argument(IR::Expr *expr) +void Codegen::enterContext(Node *node) { - if (expr && !expr->asTemp()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - return expr; + _context = _module->contextMap.value(node); + Q_ASSERT(_context); } -// keeps references alive, converts other expressions to temps -IR::Expr *Codegen::reference(IR::Expr *expr) +int Codegen::leaveContext() { - if (hasError) - return 0; - - if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - return expr; + Q_ASSERT(_context); + Q_ASSERT(!_context->controlFlow); + int functionIndex = _context->functionIndex; + _context = _context->parent; + return functionIndex; } -IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr, const SourceLocation &loc) +Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) { if (hasError) - return 0; - - Q_ASSERT(op != IR::OpIncrement); - Q_ASSERT(op != IR::OpDecrement); + return _expr.result(); - if (IR::Const *c = expr->asConst()) { - if (c->type == IR::NumberType) { +#ifndef V4_BOOTSTRAP + if (expr.isConst()) { + auto v = Value::fromReturnedValue(expr.constant); + if (v.isNumber()) { switch (op) { - case IR::OpNot: - return _block->CONST(IR::BoolType, !c->value); - case IR::OpUMinus: - return _block->CONST(IR::NumberType, -c->value); - case IR::OpUPlus: + case Not: + return Reference::fromConst(this, Encode(!v.toBoolean())); + case UMinus: + return Reference::fromConst(this, Runtime::method_uMinus(v)); + case UPlus: return expr; - case IR::OpCompl: - return _block->CONST(IR::NumberType, ~QV4::Primitive::toInt32(c->value)); - case IR::OpIncrement: - return _block->CONST(IR::NumberType, c->value + 1); - case IR::OpDecrement: - return _block->CONST(IR::NumberType, c->value - 1); + case Compl: + return Reference::fromConst(this, Encode((int)~v.toInt32())); default: break; } } } +#endif // V4_BOOTSTRAP - TempScope scope(_function); - if (isSimpleExpr(expr)) - return _block->UNOP(op, expr); - - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), expr), loc); - expr = _block->TEMP(t); - return _block->UNOP(op, expr); -} - -IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AST::SourceLocation &loc) -{ - if (hasError) - return 0; - - TempScope scope(_function); - - if (IR::Const *c1 = left->asConst()) { - if (IR::Const *c2 = right->asConst()) { - if ((c1->type & IR::NumberType) && (c2->type & IR::NumberType)) { - switch (op) { - case IR::OpAdd: return _block->CONST(IR::NumberType, c1->value + c2->value); - case IR::OpAnd: return _block->CONST(IR::BoolType, c1->value ? c2->value : 0); - case IR::OpBitAnd: return _block->CONST(IR::NumberType, int(c1->value) & int(c2->value)); - case IR::OpBitOr: return _block->CONST(IR::NumberType, int(c1->value) | int(c2->value)); - case IR::OpBitXor: return _block->CONST(IR::NumberType, int(c1->value) ^ int(c2->value)); - case IR::OpDiv: return _block->CONST(IR::NumberType, c1->value / c2->value); - case IR::OpEqual: return _block->CONST(IR::BoolType, c1->value == c2->value); - case IR::OpNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value); - case IR::OpStrictEqual: return _block->CONST(IR::BoolType, c1->value == c2->value); - case IR::OpStrictNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value); - case IR::OpGe: return _block->CONST(IR::BoolType, c1->value >= c2->value); - case IR::OpGt: return _block->CONST(IR::BoolType, c1->value > c2->value); - case IR::OpLe: return _block->CONST(IR::BoolType, c1->value <= c2->value); - case IR::OpLt: return _block->CONST(IR::BoolType, c1->value < c2->value); - case IR::OpLShift: return _block->CONST(IR::NumberType, QV4::Primitive::toInt32(c1->value) << (QV4::Primitive::toUInt32(c2->value) & 0x1f)); - case IR::OpMod: return _block->CONST(IR::NumberType, std::fmod(c1->value, c2->value)); - case IR::OpMul: return _block->CONST(IR::NumberType, c1->value * c2->value); - case IR::OpOr: return _block->CONST(IR::NumberType, c1->value ? c1->value : c2->value); - case IR::OpRShift: return _block->CONST(IR::NumberType, QV4::Primitive::toInt32(c1->value) >> (QV4::Primitive::toUInt32(c2->value) & 0x1f)); - case IR::OpSub: return _block->CONST(IR::NumberType, c1->value - c2->value); - case IR::OpURShift: return _block->CONST(IR::NumberType,QV4::Primitive::toUInt32(c1->value) >> (QV4::Primitive::toUInt32(c2->value) & 0x1f)); - - case IR::OpInstanceof: - case IR::OpIn: - break; - - case IR::OpIfTrue: // unary ops - case IR::OpNot: - case IR::OpUMinus: - case IR::OpUPlus: - case IR::OpCompl: - case IR::OpIncrement: - case IR::OpDecrement: - case IR::OpInvalid: - break; - } - } + switch (op) { + case UMinus: { + expr.loadInAccumulator(); + Instruction::UMinus uminus; + bytecodeGenerator->addInstruction(uminus); + return Reference::fromAccumulator(this); + } + case UPlus: { + expr.loadInAccumulator(); + Instruction::UPlus uplus; + bytecodeGenerator->addInstruction(uplus); + return Reference::fromAccumulator(this); + } + case Not: { + expr.loadInAccumulator(); + Instruction::UNot unot; + bytecodeGenerator->addInstruction(unot); + return Reference::fromAccumulator(this); + } + case Compl: { + expr.loadInAccumulator(); + Instruction::UCompl ucompl; + bytecodeGenerator->addInstruction(ucompl); + return Reference::fromAccumulator(this); + } + case PostIncrement: + if (!_expr.accept(nx) || requiresReturnValue) { + Reference e = expr.asLValue(); + e.loadInAccumulator(); + Instruction::UPlus uplus; + bytecodeGenerator->addInstruction(uplus); + Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); + Instruction::Increment inc; + bytecodeGenerator->addInstruction(inc); + e.storeConsumeAccumulator(); + return originalValue; + } else { + // intentionally fall-through: the result is never used, so it's equivalent to + // "expr += 1", which is what a pre-increment does as well. } - } else if (op == IR::OpAdd) { - if (IR::String *s1 = left->asString()) { - if (IR::String *s2 = right->asString()) { - return _block->STRING(_function->newString(*s1->value + *s2->value)); - } + case PreIncrement: { + Reference e = expr.asLValue(); + e.loadInAccumulator(); + Instruction::Increment inc; + bytecodeGenerator->addInstruction(inc); + if (_expr.accept(nx)) + return e.storeConsumeAccumulator(); + else + return e.storeRetainAccumulator(); + } + case PostDecrement: + if (!_expr.accept(nx) || requiresReturnValue) { + Reference e = expr.asLValue(); + e.loadInAccumulator(); + Instruction::UPlus uplus; + bytecodeGenerator->addInstruction(uplus); + Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); + Instruction::Decrement dec; + bytecodeGenerator->addInstruction(dec); + e.storeConsumeAccumulator(); + return originalValue; + } else { + // intentionally fall-through: the result is never used, so it's equivalent to + // "expr -= 1", which is what a pre-decrement does as well. } + case PreDecrement: { + Reference e = expr.asLValue(); + e.loadInAccumulator(); + Instruction::Decrement dec; + bytecodeGenerator->addInstruction(dec); + if (_expr.accept(nx)) + return e.storeConsumeAccumulator(); + else + return e.storeRetainAccumulator(); } - - if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), left), loc); - left = _block->TEMP(t); - } - - if (!right->asTemp() && !right->asArgLocal() && !right->asConst()) { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), right), loc); - right = _block->TEMP(t); - } - - Q_ASSERT(left->asTemp() || left->asArgLocal() || left->asConst()); - Q_ASSERT(right->asTemp() || right->asArgLocal() || right->asConst()); - - return _block->BINOP(op, left, right); -} - -IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) -{ - if (hasError) - return 0; - base = reference(base); - return _block->CALL(base, args); -} - -IR::Stmt *Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) -{ - if (hasError) - return 0; - - Q_ASSERT(target->isLValue()); - - if (op != IR::OpInvalid) { - return move(target, binop(op, target, source)); - } - - TempScope scope(_function); - - if (!source->asTemp() && !source->asConst() && !target->asTemp() && !source->asArgLocal() && !target->asArgLocal()) { - unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), source); - source = _block->TEMP(t); - } - if (source->asConst() && !target->asTemp() && !target->asArgLocal()) { - unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), source); - source = _block->TEMP(t); } - return _block->MOVE(target, source); + Q_UNREACHABLE(); } -IR::Stmt *Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) +void Codegen::addCJump() { - if (hasError) - return 0; - - TempScope scope(_function); - - if (! (cond->asTemp() || (cond->asBinop() && cjumpCanHandle(cond->asBinop()->op)) )) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), cond); - cond = _block->TEMP(t); - } - return _block->CJUMP(cond, iftrue, iffalse); + bytecodeGenerator->addCJumpInstruction(_expr.trueBlockFollowsCondition(), + _expr.iftrue(), _expr.iffalse()); } void Codegen::accept(Node *node) @@ -784,52 +257,71 @@ void Codegen::accept(Node *node) void Codegen::statement(Statement *ast) { - TempScope scope(_function); + RegisterScope scope(this); + + bytecodeGenerator->setLocation(ast->firstSourceLocation()); - _block->nextLocation = ast->firstSourceLocation(); + VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); + qSwap(_volataleMemoryLocations, vLocs); accept(ast); + qSwap(_volataleMemoryLocations, vLocs); } void Codegen::statement(ExpressionNode *ast) { - TempScope scope(_function); + RegisterScope scope(this); if (! ast) { return; } else { Result r(nx); qSwap(_expr, r); + VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); + qSwap(_volataleMemoryLocations, vLocs); + accept(ast); + + qSwap(_volataleMemoryLocations, vLocs); + qSwap(_expr, r); + if (hasError) return; - qSwap(_expr, r); - if (r.format == ex) { - if (r->asCall()) { - _block->EXP(*r); // the nest nx representation for calls is EXP(CALL(c..)) - } else if (r->asTemp() || r->asArgLocal()) { - // there is nothing to do - } else { - unsigned t = _block->newTemp(); - move(_block->TEMP(t), *r); - } - } + if (r.result().loadTriggersSideEffect()) + r.result().loadInAccumulator(); // triggers side effects } } -void Codegen::condition(ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) +void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, + const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition) { - if (ast) { - Result r(iftrue, iffalse); - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - if (r.format == ex) { - setLocation(cjump(*r, r.iftrue, r.iffalse), ast->firstSourceLocation()); - } + if (hasError) + return; + + if (!ast) + return; + + Result r(iftrue, iffalse, trueBlockFollowsCondition); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + + if (hasError) + return; + + if (r.format() == ex) { + Q_ASSERT(iftrue == r.iftrue()); + Q_ASSERT(iffalse == r.iffalse()); + Q_ASSERT(r.result().isValid()); + bytecodeGenerator->setLocation(ast->firstSourceLocation()); + r.result().loadInAccumulator(); + if (r.trueBlockFollowsCondition()) + bytecodeGenerator->jumpFalse().link(*r.iffalse()); + else + bytecodeGenerator->jumpTrue().link(*r.iftrue()); } } -Codegen::Result Codegen::expression(ExpressionNode *ast) +Codegen::Reference Codegen::expression(ExpressionNode *ast) { Result r; if (ast) { @@ -837,7 +329,7 @@ Codegen::Result Codegen::expression(ExpressionNode *ast) accept(ast); qSwap(_expr, r); } - return r; + return r.result(); } Codegen::Result Codegen::sourceElement(SourceElement *ast) @@ -851,17 +343,6 @@ Codegen::Result Codegen::sourceElement(SourceElement *ast) return r; } -Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast) -{ - UiMember m; - if (ast) { - qSwap(_uiMember, m); - accept(ast); - qSwap(_uiMember, m); - } - return m; -} - void Codegen::functionBody(FunctionBody *ast) { if (ast) @@ -877,36 +358,58 @@ void Codegen::program(Program *ast) void Codegen::sourceElements(SourceElements *ast) { + bool _requiresReturnValue = false; + qSwap(_requiresReturnValue, requiresReturnValue); for (SourceElements *it = ast; it; it = it->next) { + if (!it->next) + qSwap(_requiresReturnValue, requiresReturnValue); sourceElement(it->element); if (hasError) return; + if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(it->element)) { + if (AST::cast<ThrowStatement *>(sse->statement) || + AST::cast<ReturnStatement *>(sse->statement)) + return; + } + } +} + +void Codegen::statementList(StatementList *ast) +{ + bool _requiresReturnValue = requiresReturnValue; + requiresReturnValue = false; + for (StatementList *it = ast; it; it = it->next) { + if (!it->next || + it->next->statement->kind == Statement::Kind_BreakStatement || + it->next->statement->kind == Statement::Kind_ContinueStatement || + it->next->statement->kind == Statement::Kind_ReturnStatement) + requiresReturnValue = _requiresReturnValue; + statement(it->statement); + requiresReturnValue = false; + if (it->statement->kind == Statement::Kind_ThrowStatement || + it->statement->kind == Statement::Kind_BreakStatement || + it->statement->kind == Statement::Kind_ContinueStatement || + it->statement->kind == Statement::Kind_ReturnStatement) + // any code after those statements is unreachable + break; } + requiresReturnValue = _requiresReturnValue; } void Codegen::variableDeclaration(VariableDeclaration *ast) { - IR::Expr *initializer = 0; + RegisterScope scope(this); + if (!ast->expression) return; - Result expr = expression(ast->expression); + Reference rhs = expression(ast->expression); if (hasError) return; - Q_ASSERT(expr.code); - initializer = *expr; - - IR::Expr *lhs = identifier(ast->name.toString(), ast->identifierToken.startLine, - ast->identifierToken.startColumn); - - if (lhs->asArgLocal()) { - move(lhs, initializer); - } else { - TempScope scope(_function); - int initialized = _block->newTemp(); - move(_block->TEMP(initialized), initializer); - move(lhs, _block->TEMP(initialized)); - } + Reference lhs = referenceForName(ast->name.toString(), true); + //### if lhs is a temp, this won't generate a temp-to-temp move. Same for when rhs is a const + rhs.loadInAccumulator(); + lhs.storeConsumeAccumulator(); } void Codegen::variableDeclarationList(VariableDeclarationList *ast) @@ -919,175 +422,175 @@ void Codegen::variableDeclarationList(VariableDeclarationList *ast) bool Codegen::visit(ArgumentList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(CaseBlock *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(CaseClause *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(CaseClauses *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(Catch *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(DefaultClause *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(ElementList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(Elision *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(Finally *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(FormalParameterList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(FunctionBody *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(Program *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(PropertyAssignmentList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(PropertyNameAndValue *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(PropertyGetterSetter *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(SourceElements *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(StatementList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiArrayMemberList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiImport *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiHeaderItemList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiPragma *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiObjectInitializer *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiObjectMemberList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiParameterList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiProgram *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiQualifiedId *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(UiQualifiedPragmaId *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(VariableDeclaration *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } bool Codegen::visit(VariableDeclarationList *) { - Q_ASSERT(!"unreachable"); + Q_UNREACHABLE(); return false; } @@ -1106,57 +609,47 @@ bool Codegen::visit(ArrayLiteral *ast) if (hasError) return false; - const unsigned t = _block->newTemp(); + RegisterScope scope(this); - TempScope scope(_function); + int argc = 0; + int args = -1; + auto push = [this, &argc, &args](AST::ExpressionNode *arg) { + int temp = bytecodeGenerator->newRegister(); + if (args == -1) + args = temp; + if (!arg) { + auto c = Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()); + (void) c.storeOnStack(temp); + } else { + RegisterScope scope(this); + (void) expression(arg).storeOnStack(temp); + } + ++argc; + }; - IR::ExprList *args = 0; - IR::ExprList *current = 0; for (ElementList *it = ast->elements; it; it = it->next) { - for (Elision *elision = it->elision; elision; elision = elision->next) { - IR::ExprList *arg = _function->New<IR::ExprList>(); - if (!current) { - args = arg; - } else { - current->next = arg; - } - current = arg; - current->expr = _block->CONST(IR::MissingType, 0); - } - Result expr = expression(it->expression); - if (hasError) - return false; - IR::ExprList *arg = _function->New<IR::ExprList>(); - if (!current) { - args = arg; - } else { - current->next = arg; - } - current = arg; + for (Elision *elision = it->elision; elision; elision = elision->next) + push(0); - IR::Expr *exp = *expr; - if (exp->asTemp() || expr->asArgLocal() || exp->asConst()) { - current->expr = exp; - } else { - unsigned value = _block->newTemp(); - move(_block->TEMP(value), exp); - current->expr = _block->TEMP(value); - } + push(it->expression); + if (hasError) + return false; } - for (Elision *elision = ast->elision; elision; elision = elision->next) { - IR::ExprList *arg = _function->New<IR::ExprList>(); - if (!current) { - args = arg; - } else { - current->next = arg; - } - current = arg; - current->expr = _block->CONST(IR::MissingType, 0); + for (Elision *elision = ast->elision; elision; elision = elision->next) + push(0); + + if (args == -1) { + Q_ASSERT(argc == 0); + args = 0; } - move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_array, 0, 0), args)); - _expr.code = _block->TEMP(t); + Instruction::DefineArray call; + call.argc = argc; + call.args = Moth::StackSlot::createRegister(args); + bytecodeGenerator->addInstruction(call); + _expr.setResult(Reference::fromAccumulator(this)); + return false; } @@ -1165,43 +658,41 @@ bool Codegen::visit(ArrayMemberExpression *ast) if (hasError) return false; - IR::Expr *base = *expression(ast->base); + Reference base = expression(ast->base); if (hasError) return false; - if (!isSimpleExpr(base)) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), base); - base = _block->TEMP(t); - } - - IR::Expr *index = *expression(ast->expression); - if (hasError) + base = base.storeOnStack(); + if (AST::StringLiteral *str = AST::cast<AST::StringLiteral *>(ast->expression)) { + QString s = str->value.toString(); + uint arrayIndex = QV4::String::toArrayIndex(s); + if (arrayIndex == UINT_MAX) { + _expr.setResult(Reference::fromMember(base, str->value.toString())); + return false; + } + Reference index = Reference::fromConst(this, QV4::Encode(arrayIndex)); + _expr.setResult(Reference::fromSubscript(base, index)); return false; - if (!isSimpleExpr(index)) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), index); - index = _block->TEMP(t); } - - _expr.code = _block->SUBSCRIPT(base, index); + Reference index = expression(ast->expression); + _expr.setResult(Reference::fromSubscript(base, index)); return false; } -static IR::AluOp baseOp(int op) +static QSOperator::Op baseOp(int op) { switch ((QSOperator::Op) op) { - case QSOperator::InplaceAnd: return IR::OpBitAnd; - case QSOperator::InplaceSub: return IR::OpSub; - case QSOperator::InplaceDiv: return IR::OpDiv; - case QSOperator::InplaceAdd: return IR::OpAdd; - case QSOperator::InplaceLeftShift: return IR::OpLShift; - case QSOperator::InplaceMod: return IR::OpMod; - case QSOperator::InplaceMul: return IR::OpMul; - case QSOperator::InplaceOr: return IR::OpBitOr; - case QSOperator::InplaceRightShift: return IR::OpRShift; - case QSOperator::InplaceURightShift: return IR::OpURShift; - case QSOperator::InplaceXor: return IR::OpBitXor; - default: return IR::OpInvalid; + case QSOperator::InplaceAnd: return QSOperator::BitAnd; + case QSOperator::InplaceSub: return QSOperator::Sub; + case QSOperator::InplaceDiv: return QSOperator::Div; + case QSOperator::InplaceAdd: return QSOperator::Add; + case QSOperator::InplaceLeftShift: return QSOperator::LShift; + case QSOperator::InplaceMod: return QSOperator::Mod; + case QSOperator::InplaceMul: return QSOperator::Mul; + case QSOperator::InplaceOr: return QSOperator::BitOr; + case QSOperator::InplaceRightShift: return QSOperator::RShift; + case QSOperator::InplaceURightShift: return QSOperator::URShift; + case QSOperator::InplaceXor: return QSOperator::BitXor; + default: return QSOperator::Invalid; } } @@ -1212,89 +703,89 @@ bool Codegen::visit(BinaryExpression *ast) if (ast->op == QSOperator::And) { if (_expr.accept(cx)) { - IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); - condition(ast->left, iftrue, _expr.iffalse); - _block = iftrue; - condition(ast->right, _expr.iftrue, _expr.iffalse); + auto iftrue = bytecodeGenerator->newLabel(); + condition(ast->left, &iftrue, _expr.iffalse(), true); + iftrue.link(); + condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition()); } else { - IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); - - const unsigned r = _block->newTemp(); + auto iftrue = bytecodeGenerator->newLabel(); + auto endif = bytecodeGenerator->newLabel(); - Result lhs = expression(ast->left); + Reference left = expression(ast->left); if (hasError) return false; - move(_block->TEMP(r), *lhs); - setLocation(cjump(_block->TEMP(r), iftrue, endif), ast->operatorToken); - _block = iftrue; - Result rhs = expression(ast->right); + left.loadInAccumulator(); + + bytecodeGenerator->setLocation(ast->operatorToken); + bytecodeGenerator->jumpFalse().link(endif); + iftrue.link(); + + Reference right = expression(ast->right); if (hasError) return false; - move(_block->TEMP(r), *rhs); - _block->JUMP(endif); + right.loadInAccumulator(); - _expr.code = _block->TEMP(r); - _block = endif; + endif.link(); + + _expr.setResult(Reference::fromAccumulator(this)); } return false; } else if (ast->op == QSOperator::Or) { if (_expr.accept(cx)) { - IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); - condition(ast->left, _expr.iftrue, iffalse); - _block = iffalse; - condition(ast->right, _expr.iftrue, _expr.iffalse); + auto iffalse = bytecodeGenerator->newLabel(); + condition(ast->left, _expr.iftrue(), &iffalse, false); + iffalse.link(); + condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition()); } else { - IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); + auto iffalse = bytecodeGenerator->newLabel(); + auto endif = bytecodeGenerator->newLabel(); - const unsigned r = _block->newTemp(); - Result lhs = expression(ast->left); + Reference left = expression(ast->left); if (hasError) return false; - move(_block->TEMP(r), *lhs); - setLocation(cjump(_block->TEMP(r), endif, iffalse), ast->operatorToken); - _block = iffalse; - Result rhs = expression(ast->right); + left.loadInAccumulator(); + + bytecodeGenerator->setLocation(ast->operatorToken); + bytecodeGenerator->jumpTrue().link(endif); + iffalse.link(); + + Reference right = expression(ast->right); if (hasError) return false; - move(_block->TEMP(r), *rhs); - _block->JUMP(endif); + right.loadInAccumulator(); + + endif.link(); - _block = endif; - _expr.code = _block->TEMP(r); + _expr.setResult(Reference::fromAccumulator(this)); } return false; } - IR::Expr* left = *expression(ast->left); + Reference left = expression(ast->left); if (hasError) return false; switch (ast->op) { case QSOperator::Or: case QSOperator::And: + Q_UNREACHABLE(); // handled separately above break; case QSOperator::Assign: { + if (!left.isLValue()) { + throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue")); + return false; + } + left = left.asLValue(); if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation())) return false; - Result right = expression(ast->right); + expression(ast->right).loadInAccumulator(); if (hasError) return false; - if (!left->isLValue()) { - throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue")); - return false; - } - - if (_expr.accept(nx)) { - move(left, *right); - } else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), *right); - move(left, _block->TEMP(t)); - _expr.code = _block->TEMP(t); - } + if (_expr.accept(nx)) + _expr.setResult(left.storeConsumeAccumulator()); + else + _expr.setResult(left.storeRetainAccumulator()); break; } @@ -1311,23 +802,36 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::InplaceXor: { if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation())) return false; - Result right = expression(ast->right); - if (hasError) - return false; - if (!left->isLValue()) { + + if (!left.isLValue()) { throwSyntaxError(ast->operatorToken, QStringLiteral("left-hand side of inplace operator is not an lvalue")); return false; } + left = left.asLValue(); + + Reference tempLeft = left.storeOnStack(); + Reference right = expression(ast->right); + + if (hasError) + return false; + + binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator(); + _expr.setResult(left.storeRetainAccumulator()); - TempScope scope(_function); - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), *right); - move(left, _block->TEMP(t), baseOp(ast->op)); - if (!_expr.accept(nx)) - _expr.code = left; break; } + case QSOperator::BitAnd: + case QSOperator::BitOr: + case QSOperator::BitXor: + if (left.isConst()) { + Reference right = expression(ast->right); + if (hasError) + return false; + _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left)); + break; + } + // intentional fall-through! case QSOperator::In: case QSOperator::InstanceOf: case QSOperator::Equal: @@ -1337,55 +841,408 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::Le: case QSOperator::Lt: case QSOperator::StrictEqual: - case QSOperator::StrictNotEqual: { - TempScope scope(_function); - if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), left), ast->operatorToken); - left = _block->TEMP(t); + case QSOperator::StrictNotEqual: + case QSOperator::Add: + case QSOperator::Div: + case QSOperator::Mod: + case QSOperator::Mul: + case QSOperator::Sub: + case QSOperator::LShift: + case QSOperator::RShift: + case QSOperator::URShift: { + Reference right; + if (AST::NumericLiteral *rhs = AST::cast<AST::NumericLiteral *>(ast->right)) { + visit(rhs); + right = _expr.result(); + } else { + left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it + right = expression(ast->right); } - - Result right = expression(ast->right); if (hasError) return false; - if (_expr.accept(cx)) { - setLocation(cjump(binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken), _expr.iftrue, _expr.iffalse), ast->operatorToken); + _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right)); + + break; + } + + } // switch + + return false; +} + +Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Reference &right) +{ + switch (oper) { + case QSOperator::Add: { + //### Todo: when we add type hints, we can generate an Increment when both the lhs is a number and the rhs == 1 + left = left.storeOnStack(); + right.loadInAccumulator(); + Instruction::Add add; + add.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(add); + break; + } + case QSOperator::Sub: { + if (right.isConst() && right.constant == Encode(int(1))) { + left.loadInAccumulator(); + bytecodeGenerator->addInstruction(Instruction::Decrement()); } else { - _expr.code = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); + left = left.storeOnStack(); + right.loadInAccumulator(); + Instruction::Sub sub; + sub.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(sub); } break; } - - case QSOperator::Add: + case QSOperator::Mul: { + left = left.storeOnStack(); + right.loadInAccumulator(); + Instruction::Mul mul; + mul.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(mul); + break; + } + case QSOperator::Div: { + left = left.storeOnStack(); + right.loadInAccumulator(); + Instruction::Div div; + div.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(div); + break; + } + case QSOperator::Mod: { + left = left.storeOnStack(); + right.loadInAccumulator(); + Instruction::Mod mod; + mod.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(mod); + break; + } case QSOperator::BitAnd: + if (right.isConst()) { + int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32(); + if (left.isConst()) { + int result = Primitive::fromReturnedValue(left.constant).toInt32() & rightAsInt; + return Reference::fromConst(this, Encode(result)); + } + left.loadInAccumulator(); + Instruction::BitAndConst bitAnd; + bitAnd.rhs = rightAsInt; + bytecodeGenerator->addInstruction(bitAnd); + } else { + right.loadInAccumulator(); + Instruction::BitAnd bitAnd; + bitAnd.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(bitAnd); + } + break; case QSOperator::BitOr: + if (right.isConst()) { + int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32(); + if (left.isConst()) { + int result = Primitive::fromReturnedValue(left.constant).toInt32() | rightAsInt; + return Reference::fromConst(this, Encode(result)); + } + left.loadInAccumulator(); + Instruction::BitOrConst bitOr; + bitOr.rhs = rightAsInt; + bytecodeGenerator->addInstruction(bitOr); + } else { + right.loadInAccumulator(); + Instruction::BitOr bitOr; + bitOr.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(bitOr); + } + break; case QSOperator::BitXor: - case QSOperator::Div: - case QSOperator::LShift: - case QSOperator::Mod: - case QSOperator::Mul: + if (right.isConst()) { + int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32(); + if (left.isConst()) { + int result = Primitive::fromReturnedValue(left.constant).toInt32() ^ rightAsInt; + return Reference::fromConst(this, Encode(result)); + } + left.loadInAccumulator(); + Instruction::BitXorConst bitXor; + bitXor.rhs = rightAsInt; + bytecodeGenerator->addInstruction(bitXor); + } else { + right.loadInAccumulator(); + Instruction::BitXor bitXor; + bitXor.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(bitXor); + } + break; + case QSOperator::URShift: + if (right.isConst()) { + left.loadInAccumulator(); + Instruction::UShrConst ushr; + ushr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f; + bytecodeGenerator->addInstruction(ushr); + } else { + right.loadInAccumulator(); + Instruction::UShr ushr; + ushr.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(ushr); + } + break; case QSOperator::RShift: - case QSOperator::Sub: - case QSOperator::URShift: { - TempScope scope(_function); - if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), left), ast->operatorToken); - left = _block->TEMP(t); + if (right.isConst()) { + left.loadInAccumulator(); + Instruction::ShrConst shr; + shr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f; + bytecodeGenerator->addInstruction(shr); + } else { + right.loadInAccumulator(); + Instruction::Shr shr; + shr.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(shr); + } + break; + case QSOperator::LShift: + if (right.isConst()) { + left.loadInAccumulator(); + Instruction::ShlConst shl; + shl.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f; + bytecodeGenerator->addInstruction(shl); + } else { + right.loadInAccumulator(); + Instruction::Shl shl; + shl.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(shl); } + break; + case QSOperator::InstanceOf: { + Instruction::CmpInstanceOf binop; + left = left.storeOnStack(); + right.loadInAccumulator(); + binop.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(binop); + break; + } + case QSOperator::In: { + Instruction::CmpIn binop; + left = left.storeOnStack(); + right.loadInAccumulator(); + binop.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(binop); + break; + } + case QSOperator::StrictEqual: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); - Result right = expression(ast->right); - if (hasError) - return false; + Instruction::CmpStrictEqual cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + } + case QSOperator::StrictNotEqual: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); + + Instruction::CmpStrictNotEqual cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + } + case QSOperator::Equal: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); - _expr.code = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); + Instruction::CmpEq cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); break; } + case QSOperator::NotEqual: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); - } // switch + Instruction::CmpNe cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + } + case QSOperator::Gt: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); - return false; + Instruction::CmpGt cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + } + case QSOperator::Ge: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); + + Instruction::CmpGe cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + } + case QSOperator::Lt: { + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); + + Instruction::CmpLt cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + } + case QSOperator::Le: + if (_expr.accept(cx)) + return jumpBinop(oper, left, right); + + Instruction::CmpLe cmp; + left = left.storeOnStack(); + right.loadInAccumulator(); + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + break; + default: + Q_UNREACHABLE(); + } + + return Reference::fromAccumulator(this); +} + +static QSOperator::Op operatorForSwappedOperands(QSOperator::Op oper) +{ + switch (oper) { + case QSOperator::StrictEqual: return QSOperator::StrictEqual; + case QSOperator::StrictNotEqual: return QSOperator::StrictNotEqual; + case QSOperator::Equal: return QSOperator::Equal; + case QSOperator::NotEqual: return QSOperator::NotEqual; + case QSOperator::Gt: return QSOperator::Le; + case QSOperator::Ge: return QSOperator::Lt; + case QSOperator::Lt: return QSOperator::Ge; + case QSOperator::Le: return QSOperator::Gt; + default: Q_UNIMPLEMENTED(); return QSOperator::Invalid; + } +} + +Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right) +{ + if (left.isConst()) { + oper = operatorForSwappedOperands(oper); + qSwap(left, right); + } + + if (right.isConst() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) { + Value c = Primitive::fromReturnedValue(right.constant); + if (c.isNull() || c.isUndefined()) { + left.loadInAccumulator(); + if (oper == QSOperator::Equal) { + Instruction::CmpEqNull cmp; + bytecodeGenerator->addInstruction(cmp); + addCJump(); + return Reference(); + } else if (oper == QSOperator::NotEqual) { + Instruction::CmpNeNull cmp; + bytecodeGenerator->addInstruction(cmp); + addCJump(); + return Reference(); + } + } else if (c.isInt32()) { + left.loadInAccumulator(); + if (oper == QSOperator::Equal) { + Instruction::CmpEqInt cmp; + cmp.lhs = c.int_32(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + return Reference(); + } else if (oper == QSOperator::NotEqual) { + Instruction::CmpNeInt cmp; + cmp.lhs = c.int_32(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + return Reference(); + } + + } + } + + left = left.storeOnStack(); + right.loadInAccumulator(); + + switch (oper) { + case QSOperator::StrictEqual: { + Instruction::CmpStrictEqual cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::StrictNotEqual: { + Instruction::CmpStrictNotEqual cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::Equal: { + Instruction::CmpEq cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::NotEqual: { + Instruction::CmpNe cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::Gt: { + Instruction::CmpGt cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::Ge: { + Instruction::CmpGe cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::Lt: { + Instruction::CmpLt cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + case QSOperator::Le: { + Instruction::CmpLe cmp; + cmp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + break; + } + default: + Q_UNREACHABLE(); + } + return Reference(); } bool Codegen::visit(CallExpression *ast) @@ -1393,54 +1250,138 @@ bool Codegen::visit(CallExpression *ast) if (hasError) return false; - Result base = expression(ast->base); - IR::ExprList *args = 0, **args_it = &args; - for (ArgumentList *it = ast->arguments; it; it = it->next) { - Result arg = expression(it->expression); - if (hasError) - return false; - IR::Expr *actual = argument(*arg); - *args_it = _function->New<IR::ExprList>(); - (*args_it)->init(actual); - args_it = &(*args_it)->next; + RegisterScope scope(this); + + Reference base = expression(ast->base); + if (hasError) + return false; + switch (base.type) { + case Reference::Member: + case Reference::Subscript: + base = base.asLValue(); + break; + case Reference::Name: + break; + default: + base = base.storeOnStack(); + break; } + + auto calldata = pushArgs(ast->arguments); if (hasError) return false; - _expr.code = call(*base, args); + + //### Do we really need all these call instructions? can's we load the callee in a temp? + if (base.type == Reference::Member) { + if (useFastLookups) { + Instruction::CallPropertyLookup call; + call.base = base.propertyBase.stackSlot(); + call.lookupIndex = registerGetterLookup(base.propertyNameIndex); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } else { + Instruction::CallProperty call; + call.base = base.propertyBase.stackSlot(); + call.name = base.propertyNameIndex; + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } + } else if (base.type == Reference::Subscript) { + Instruction::CallElement call; + call.base = base.elementBase; + call.index = base.elementSubscript.stackSlot(); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } else if (base.type == Reference::Name) { + if (base.name == QStringLiteral("eval")) { + Instruction::CallPossiblyDirectEval call; + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } else if (useFastLookups && base.global) { + Instruction::CallGlobalLookup call; + call.index = registerGlobalGetterLookup(base.nameAsIndex()); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } else { + Instruction::CallName call; + call.name = base.nameAsIndex(); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } + } else { + base.loadInAccumulator(); + + Instruction::CallValue call; + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } + + _expr.setResult(Reference::fromAccumulator(this)); return false; } +Codegen::Arguments Codegen::pushArgs(ArgumentList *args) +{ + int argc = 0; + for (ArgumentList *it = args; it; it = it->next) + ++argc; + + if (!argc) + return { 0, 0 }; + + int calldata = bytecodeGenerator->newRegisterArray(argc); + + argc = 0; + for (ArgumentList *it = args; it; it = it->next) { + RegisterScope scope(this); + Reference e = expression(it->expression); + if (hasError) + break; + if (!argc && !it->next) { + // avoid copy for functions taking a single argument + if (e.isStackSlot()) + return { 1, e.stackSlot() }; + } + (void) e.storeOnStack(calldata + argc); + ++argc; + } + + return { argc, calldata }; +} + bool Codegen::visit(ConditionalExpression *ast) { if (hasError) return true; - IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); + RegisterScope scope(this); - const unsigned t = _block->newTemp(); - TempScope scope(_function); + BytecodeGenerator::Label iftrue = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label iffalse = bytecodeGenerator->newLabel(); + condition(ast->expression, &iftrue, &iffalse, true); - condition(ast->expression, iftrue, iffalse); - - _block = iftrue; - Result ok = expression(ast->ok); + iftrue.link(); + Reference ok = expression(ast->ok); if (hasError) return false; - move(_block->TEMP(t), *ok); - _block->JUMP(endif); + ok.loadInAccumulator(); + BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump(); - _block = iffalse; - Result ko = expression(ast->ko); + iffalse.link(); + Reference ko = expression(ast->ko); if (hasError) return false; - move(_block->TEMP(t), *ko); - _block->JUMP(endif); - - _block = endif; + ko.loadInAccumulator(); - _expr.code = _block->TEMP(t); + jump_endif.link(); + _expr.setResult(Reference::fromAccumulator(this)); return false; } @@ -1450,48 +1391,59 @@ bool Codegen::visit(DeleteExpression *ast) if (hasError) return false; - IR::Expr* expr = *expression(ast->expression); + Reference expr = expression(ast->expression); if (hasError) return false; - // Temporaries cannot be deleted - IR::ArgLocal *al = expr->asArgLocal(); - if (al && al->index < static_cast<unsigned>(_variableEnvironment->members.size())) { + + switch (expr.type) { + case Reference::StackSlot: + if (!expr.stackSlotIsLocalOrArgument) + break; + // fall through + case Reference::ScopedLocal: // Trying to delete a function argument might throw. - if (_function->isStrict) { + if (_context->isStrict) { throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode.")); return false; } - _expr.code = _block->CONST(IR::BoolType, 0); + _expr.setResult(Reference::fromConst(this, QV4::Encode(false))); return false; - } - if (_function->isStrict && expr->asName()) { - throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode.")); + case Reference::Name: { + if (_context->isStrict) { + throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode.")); + return false; + } + Instruction::DeleteName del; + del.name = expr.nameAsIndex(); + bytecodeGenerator->addInstruction(del); + _expr.setResult(Reference::fromAccumulator(this)); return false; } - - // [[11.4.1]] Return true if it's not a reference - if (expr->asConst() || expr->asString()) { - _expr.code = _block->CONST(IR::BoolType, 1); + case Reference::Member: { + //### maybe add a variant where the base can be in the accumulator? + expr = expr.asLValue(); + Instruction::DeleteMember del; + del.base = expr.propertyBase.stackSlot(); + del.member = expr.propertyNameIndex; + bytecodeGenerator->addInstruction(del); + _expr.setResult(Reference::fromAccumulator(this)); return false; } - - // Return values from calls are also not a reference, but we have to - // perform the call to allow for side effects. - if (expr->asCall()) { - _block->EXP(expr); - _expr.code = _block->CONST(IR::BoolType, 1); + case Reference::Subscript: { + //### maybe add a variant where the index can be in the accumulator? + expr = expr.asLValue(); + Instruction::DeleteSubscript del; + del.base = expr.elementBase; + del.index = expr.elementSubscript.stackSlot(); + bytecodeGenerator->addInstruction(del); + _expr.setResult(Reference::fromAccumulator(this)); return false; } - if (expr->asTemp() || - (expr->asArgLocal() && - expr->asArgLocal()->index >= static_cast<unsigned>(_variableEnvironment->members.size()))) { - _expr.code = _block->CONST(IR::BoolType, 1); - return false; + default: + break; } - - IR::ExprList *args = _function->New<IR::ExprList>(); - args->init(reference(expr)); - _expr.code = call(_block->NAME(IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); + // [[11.4.1]] Return true if it's not a reference + _expr.setResult(Reference::fromConst(this, QV4::Encode(true))); return false; } @@ -1500,11 +1452,7 @@ bool Codegen::visit(FalseLiteral *) if (hasError) return false; - if (_expr.accept(cx)) { - _block->JUMP(_expr.iffalse); - } else { - _expr.code = _block->CONST(IR::BoolType, 0); - } + _expr.setResult(Reference::fromConst(this, QV4::Encode(false))); return false; } @@ -1513,9 +1461,10 @@ bool Codegen::visit(FieldMemberExpression *ast) if (hasError) return false; - Result base = expression(ast->base); - if (!hasError) - _expr.code = member(*base, _function->newString(ast->name.toString())); + Reference base = expression(ast->base); + if (hasError) + return false; + _expr.setResult(Reference::fromMember(base, ast->name.toString())); return false; } @@ -1524,64 +1473,109 @@ bool Codegen::visit(FunctionExpression *ast) if (hasError) return false; - TempScope scope(_function); + RegisterScope scope(this); int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); - _expr.code = _block->CLOSURE(function); + loadClosure(function); + _expr.setResult(Reference::fromAccumulator(this)); return false; } -IR::Expr *Codegen::identifier(const QString &name, int line, int col) +Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs) { - if (hasError) - return 0; - - uint scope = 0; - Environment *e = _variableEnvironment; - IR::Function *f = _function; + int scope = 0; + Context *c = _context; - while (f && e->parent) { - if (f->insideWithOrCatch || (f->isNamedExpression && QStringRef(f->name) == name)) - return _block->NAME(name, line, col); - - int index = e->findMember(name); - Q_ASSERT (index < e->members.size()); - if (index != -1) { - IR::ArgLocal *al = _block->LOCAL(index, scope); - if (name == QLatin1String("arguments") || name == QLatin1String("eval")) - al->isArgumentsOrEval = true; - return al; + // skip the innermost context if it's simple (as the runtime won't + // create a context for it + if (c->canUseSimpleCall()) { + Context::Member m = c->findMember(name); + if (m.type != Context::UndefinedMember) { + Q_ASSERT((!m.canEscape)); + Reference r = Reference::fromStackSlot(this, m.index, true /*isLocal*/); + if (name == QLatin1String("arguments") || name == QLatin1String("eval")) { + r.isArgOrEval = true; + if (isLhs && c->isStrict) + // ### add correct source location + throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode")); + } + return r; + } + const int argIdx = c->findArgument(name); + if (argIdx != -1) { + Q_ASSERT(!c->argumentsCanEscape && (c->usesArgumentsObject != Context::ArgumentsObjectUsed || c->isStrict)); + return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name)); + } + c = c->parent; + } + + while (c->parent) { + if (c->forceLookupByName()) + goto loadByName; + + Context::Member m = c->findMember(name); + if (m.type != Context::UndefinedMember) { + Reference r = m.canEscape ? Reference::fromScopedLocal(this, m.index, scope) + : Reference::fromStackSlot(this, m.index, true /*isLocal*/); + if (name == QLatin1String("arguments") || name == QLatin1String("eval")) { + r.isArgOrEval = true; + if (isLhs && c->isStrict) + // ### add correct source location + throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode")); + } + return r; + } + const int argIdx = c->findArgument(name); + if (argIdx != -1) { + if (c->argumentsCanEscape || c->usesArgumentsObject == Context::ArgumentsObjectUsed) { + int idx = argIdx + c->locals.size(); + return Reference::fromScopedLocal(this, idx, scope); + } else { + Q_ASSERT(scope == 0); + return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name)); + } } - const int argIdx = f->indexOfArgument(QStringRef(&name)); - if (argIdx != -1) - return _block->ARG(argIdx, scope); - if (!f->isStrict && f->hasDirectEval) - return _block->NAME(name, line, col); + if (!c->isStrict && c->hasDirectEval) + goto loadByName; ++scope; - e = e->parent; - f = f->outer; + c = c->parent; } - // This hook allows implementing QML lookup semantics - if (IR::Expr *fallback = fallbackNameLookup(name, line, col)) - return fallback; + { + // This hook allows implementing QML lookup semantics + Reference fallback = fallbackNameLookup(name); + if (fallback.type != Reference::Invalid) + return fallback; + } - if (!e->parent && (!f || !f->insideWithOrCatch) && _variableEnvironment->compilationMode != EvalCode && e->compilationMode != QmlBinding) - return _block->GLOBALNAME(name, line, col); + if (!c->parent && !c->forceLookupByName() && _context->compilationMode != EvalCode && c->compilationMode != QmlBinding) { + Reference r = Reference::fromName(this, name); + r.global = true; + return r; + } // global context or with. Lookup by name - return _block->NAME(name, line, col); + loadByName: + return Reference::fromName(this, name); +} +void Codegen::loadClosure(int closureId) +{ + if (closureId >= 0) { + Instruction::LoadClosure load; + load.value = closureId; + bytecodeGenerator->addInstruction(load); + } else { + Reference::fromConst(this, Encode::undefined()).loadInAccumulator(); + } } -IR::Expr *Codegen::fallbackNameLookup(const QString &name, int line, int col) +Codegen::Reference Codegen::fallbackNameLookup(const QString &name) { Q_UNUSED(name) - Q_UNUSED(line) - Q_UNUSED(col) - return 0; + return Reference(); } bool Codegen::visit(IdentifierExpression *ast) @@ -1589,7 +1583,7 @@ bool Codegen::visit(IdentifierExpression *ast) if (hasError) return false; - _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); + _expr.setResult(referenceForName(ast->name.toString(), false)); return false; } @@ -1606,18 +1600,21 @@ bool Codegen::visit(NewExpression *ast) { if (hasError) return false; - TempScope scope(_function); - Result base = expression(ast->expression); + RegisterScope scope(this); + + Reference base = expression(ast->expression); if (hasError) return false; - IR::Expr *expr = *base; - if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - _expr.code = _block->NEW(expr, 0); + //### Maybe create a ConstructA that takes an accumulator? + base = base.storeOnStack(); + + Instruction::Construct create; + create.func = base.stackSlot(); + create.argc = 0; + create.argv = 0; + bytecodeGenerator->addInstruction(create); + _expr.setResult(Reference::fromAccumulator(this)); return false; } @@ -1626,32 +1623,23 @@ bool Codegen::visit(NewMemberExpression *ast) if (hasError) return false; - const unsigned t = _block->newTemp(); + RegisterScope scope(this); - TempScope scope(_function); + Reference base = expression(ast->base); + if (hasError) + return false; + base = base.storeOnStack(); - Result base = expression(ast->base); + auto calldata = pushArgs(ast->arguments); if (hasError) return false; - IR::Expr *expr = *base; - if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - IR::ExprList *args = 0, **args_it = &args; - for (ArgumentList *it = ast->arguments; it; it = it->next) { - Result arg = expression(it->expression); - if (hasError) - return false; - IR::Expr *actual = argument(*arg); - *args_it = _function->New<IR::ExprList>(); - (*args_it)->init(actual); - args_it = &(*args_it)->next; - } - move(_block->TEMP(t), _block->NEW(expr, args)); - _expr.code = _block->TEMP(t); + Instruction::Construct create; + create.func = base.stackSlot(); + create.argc = calldata.argc; + create.argv = calldata.argv; + bytecodeGenerator->addInstruction(create); + _expr.setResult(Reference::fromAccumulator(this)); return false; } @@ -1660,14 +1648,7 @@ bool Codegen::visit(NotExpression *ast) if (hasError) return false; - const unsigned r = _block->newTemp(); - TempScope scope(_function); - - Result expr = expression(ast->expression); - if (hasError) - return false; - setLocation(move(_block->TEMP(r), unop(IR::OpNot, *expr, ast->notToken)), ast->notToken); - _expr.code = _block->TEMP(r); + _expr.setResult(unop(Not, expression(ast->expression))); return false; } @@ -1676,8 +1657,10 @@ bool Codegen::visit(NullExpression *) if (hasError) return false; - if (_expr.accept(cx)) _block->JUMP(_expr.iffalse); - else _expr.code = _block->CONST(IR::NullType, 0); + if (_expr.accept(cx)) + bytecodeGenerator->jump().link(*_expr.iffalse()); + else + _expr.setResult(Reference::fromConst(this, Encode::null())); return false; } @@ -1687,30 +1670,10 @@ bool Codegen::visit(NumericLiteral *ast) if (hasError) return false; - if (_expr.accept(cx)) { - if (ast->value) _block->JUMP(_expr.iftrue); - else _block->JUMP(_expr.iffalse); - } else { - _expr.code = _block->CONST(IR::NumberType, ast->value); - } + _expr.setResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value))); return false; } -struct ObjectPropertyValue { - ObjectPropertyValue() - : value(0) - , getter(-1) - , setter(-1) - {} - - IR::Expr *value; - int getter; // index in _module->functions or -1 if not set - int setter; - - bool hasGetter() const { return getter >= 0; } - bool hasSetter() const { return setter >= 0; } -}; - bool Codegen::visit(ObjectLiteral *ast) { if (hasError) @@ -1718,33 +1681,27 @@ bool Codegen::visit(ObjectLiteral *ast) QMap<QString, ObjectPropertyValue> valueMap; - const unsigned t = _block->newTemp(); - TempScope scope(_function); + RegisterScope scope(this); for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { QString name = it->assignment->name->asString(); if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) { - Result value = expression(nv->value); + Reference value = expression(nv->value); if (hasError) return false; + ObjectPropertyValue &v = valueMap[name]; - if (v.hasGetter() || v.hasSetter() || (_function->isStrict && v.value)) { + if (v.hasGetter() || v.hasSetter() || (_context->isStrict && v.rvalue.isValid())) { throwSyntaxError(nv->lastSourceLocation(), QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name)); return false; } - if (IR::Const *c = (*value)->asConst()) { - valueMap[name].value = c; - } else { - unsigned t = _block->newTemp(); - move(_block->TEMP(t), *value); - valueMap[name].value = _block->TEMP(t); - } + v.rvalue = value.storeOnStack(); } else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) { const int function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); ObjectPropertyValue &v = valueMap[name]; - if (v.value || + if (v.rvalue.isValid() || (gs->type == PropertyGetterSetter::Getter && v.hasGetter()) || (gs->type == PropertyGetterSetter::Setter && v.hasSetter())) { throwSyntaxError(gs->lastSourceLocation(), @@ -1760,96 +1717,90 @@ bool Codegen::visit(ObjectLiteral *ast) } } - // The linked-list arguments to builtin_define_object_literal - // begin with a CONST counting the number of key/value pairs, followed by the - // key value pairs, followed by the array entries. - IR::ExprList *args = _function->New<IR::ExprList>(); - - IR::Const *entryCountParam = _function->New<IR::Const>(); - entryCountParam->init(IR::SInt32Type, 0); - args->expr = entryCountParam; - args->next = 0; - - IR::ExprList *keyValueEntries = 0; - IR::ExprList *currentKeyValueEntry = 0; - int keyValueEntryCount = 0; - IR::ExprList *arrayEntries = 0; + QVector<QString> nonArrayKey, arrayKeyWithValue, arrayKeyWithGetterSetter; + bool needSparseArray = false; // set to true if any array index is bigger than 16 - IR::ExprList *currentArrayEntry = 0; - - for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(); it != valueMap.end(); ) { - IR::ExprList **currentPtr = 0; - uint keyAsIndex = QV4::String::toArrayIndex(it.key()); - if (keyAsIndex != UINT_MAX) { - if (!arrayEntries) { - arrayEntries = _function->New<IR::ExprList>(); - currentArrayEntry = arrayEntries; - } else { - currentArrayEntry->next = _function->New<IR::ExprList>(); - currentArrayEntry = currentArrayEntry->next; - } - currentPtr = ¤tArrayEntry; - IR::Const *idx = _function->New<IR::Const>(); - idx->init(IR::UInt32Type, keyAsIndex); - (*currentPtr)->expr = idx; + for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(), eit = valueMap.end(); + it != eit; ++it) { + QString name = it.key(); + uint keyAsIndex = QV4::String::toArrayIndex(name); + if (keyAsIndex != std::numeric_limits<uint>::max()) { + it->keyAsIndex = keyAsIndex; + if (keyAsIndex > 16) + needSparseArray = true; + if (it->hasSetter() || it->hasGetter()) + arrayKeyWithGetterSetter.append(name); + else + arrayKeyWithValue.append(name); } else { - if (!keyValueEntries) { - keyValueEntries = _function->New<IR::ExprList>(); - currentKeyValueEntry = keyValueEntries; - } else { - currentKeyValueEntry->next = _function->New<IR::ExprList>(); - currentKeyValueEntry = currentKeyValueEntry->next; - } - currentPtr = ¤tKeyValueEntry; - (*currentPtr)->expr = _block->NAME(it.key(), 0, 0); - keyValueEntryCount++; + nonArrayKey.append(name); } + } - IR::ExprList *¤t = *currentPtr; - if (it->value) { - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = _block->CONST(IR::BoolType, true); - - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = it->value; + int args = -1; + auto push = [this, &args](const Reference &arg) { + int temp = bytecodeGenerator->newRegister(); + if (args == -1) + args = temp; + (void) arg.storeOnStack(temp); + }; + + QVector<QV4::Compiler::JSUnitGenerator::MemberInfo> members; + + Reference acc = Reference::fromAccumulator(this); + // generate the key/value pairs + for (const QString &key : qAsConst(nonArrayKey)) { + const ObjectPropertyValue &prop = valueMap[key]; + + if (prop.hasGetter() || prop.hasSetter()) { + Q_ASSERT(!prop.rvalue.isValid()); + loadClosure(prop.getter); + push(acc); + loadClosure(prop.setter); + push(acc); + members.append({ key, true }); } else { - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = _block->CONST(IR::BoolType, false); - - unsigned getter = _block->newTemp(); - unsigned setter = _block->newTemp(); - move(_block->TEMP(getter), it->hasGetter() ? _block->CLOSURE(it->getter) : _block->CONST(IR::UndefinedType, 0)); - move(_block->TEMP(setter), it->hasSetter() ? _block->CLOSURE(it->setter) : _block->CONST(IR::UndefinedType, 0)); - - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = _block->TEMP(getter); - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = _block->TEMP(setter); + Q_ASSERT(prop.rvalue.isValid()); + push(prop.rvalue); + members.append({ key, false }); } - - it = valueMap.erase(it); } - entryCountParam->value = keyValueEntryCount; + // generate array entries with values + for (const QString &key : qAsConst(arrayKeyWithValue)) { + const ObjectPropertyValue &prop = valueMap[key]; + Q_ASSERT(!prop.hasGetter() && !prop.hasSetter()); + push(Reference::fromConst(this, Encode(prop.keyAsIndex))); + push(prop.rvalue); + } - if (keyValueEntries) - args->next = keyValueEntries; - if (arrayEntries) { - if (currentKeyValueEntry) - currentKeyValueEntry->next = arrayEntries; - else - args->next = arrayEntries; + // generate array entries with both a value and a setter + for (const QString &key : qAsConst(arrayKeyWithGetterSetter)) { + const ObjectPropertyValue &prop = valueMap[key]; + Q_ASSERT(!prop.rvalue.isValid()); + push(Reference::fromConst(this, Encode(prop.keyAsIndex))); + loadClosure(prop.getter); + push(acc); + loadClosure(prop.setter); + push(acc); } - move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_object_literal, - ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args)); + int classId = jsUnitGenerator->registerJSClass(members); + + uint arrayGetterSetterCountAndFlags = arrayKeyWithGetterSetter.size(); + arrayGetterSetterCountAndFlags |= needSparseArray << 30; - _expr.code = _block->TEMP(t); + if (args == -1) + args = 0; + + Instruction::DefineObjectLiteral call; + call.internalClassId = classId; + call.arrayValueCount = arrayKeyWithValue.size(); + call.arrayGetterSetterCountAndFlags = arrayGetterSetterCountAndFlags; + call.args = Moth::StackSlot::createRegister(args); + bytecodeGenerator->addInstruction(call); + + _expr.setResult(Reference::fromAccumulator(this)); return false; } @@ -1858,26 +1809,17 @@ bool Codegen::visit(PostDecrementExpression *ast) if (hasError) return false; - Result expr = expression(ast->base); + Reference expr = expression(ast->base); if (hasError) return false; - if (!expr->isLValue()) { + if (!expr.isLValue()) { throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation")); return false; } - if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken)) + if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken)) return false; - const unsigned oldValue = _block->newTemp(); - setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->decrementToken)), ast->decrementToken); - - TempScope scope(_function); - const unsigned newValue = _block->newTemp(); - setLocation(move(_block->TEMP(newValue), binop(IR::OpSub, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->decrementToken)), ast->decrementToken); - setLocation(move(*expr, _block->TEMP(newValue)), ast->decrementToken); - - if (!_expr.accept(nx)) - _expr.code = _block->TEMP(oldValue); + _expr.setResult(unop(PostDecrement, expr)); return false; } @@ -1887,54 +1829,35 @@ bool Codegen::visit(PostIncrementExpression *ast) if (hasError) return false; - Result expr = expression(ast->base); + Reference expr = expression(ast->base); if (hasError) return false; - if (!expr->isLValue()) { + if (!expr.isLValue()) { throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation")); return false; } - if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken)) + if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken)) return false; - const unsigned oldValue = _block->newTemp(); - setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->incrementToken)), ast->incrementToken); - - TempScope scope(_function); - const unsigned newValue = _block->newTemp(); - setLocation(move(_block->TEMP(newValue), binop(IR::OpAdd, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->incrementToken)), ast->incrementToken); - setLocation(move(*expr, _block->TEMP(newValue)), ast->incrementToken); - - if (!_expr.accept(nx)) - _expr.code = _block->TEMP(oldValue); - + _expr.setResult(unop(PostIncrement, expr)); return false; } bool Codegen::visit(PreDecrementExpression *ast) -{ - if (hasError) +{ if (hasError) return false; - Result expr = expression(ast->expression); + Reference expr = expression(ast->expression); if (hasError) return false; - if (!expr->isLValue()) { + if (!expr.isLValue()) { throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference.")); return false; } - if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken)) + if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken)) return false; - IR::Expr *op = binop(IR::OpSub, *expr, _block->CONST(IR::NumberType, 1), ast->decrementToken); - if (_expr.accept(nx)) { - setLocation(move(*expr, op), ast->decrementToken); - } else { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), op), ast->decrementToken); - setLocation(move(*expr, _block->TEMP(t)), ast->decrementToken); - _expr.code = _block->TEMP(t); - } + _expr.setResult(unop(PreDecrement, expr)); return false; } @@ -1943,25 +1866,17 @@ bool Codegen::visit(PreIncrementExpression *ast) if (hasError) return false; - Result expr = expression(ast->expression); + Reference expr = expression(ast->expression); if (hasError) return false; - if (!expr->isLValue()) { + if (!expr.isLValue()) { throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference.")); return false; } - if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken)) + if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken)) return false; - IR::Expr *op = binop(IR::OpAdd, unop(IR::OpUPlus, *expr, ast->incrementToken), _block->CONST(IR::NumberType, 1), ast->incrementToken); - if (_expr.accept(nx)) { - setLocation(move(*expr, op), ast->incrementToken); - } else { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), op), ast->incrementToken); - setLocation(move(*expr, _block->TEMP(t)), ast->incrementToken); - _expr.code = _block->TEMP(t); - } + _expr.setResult(unop(PreIncrement, expr)); return false; } @@ -1970,7 +1885,13 @@ bool Codegen::visit(RegExpLiteral *ast) if (hasError) return false; - _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags); + auto r = Reference::fromAccumulator(this); + r.isReadonly = true; + _expr.setResult(r); + + Instruction::LoadRegExp instr; + instr.regExpId = jsUnitGenerator->registerRegExp(ast); + bytecodeGenerator->addInstruction(instr); return false; } @@ -1979,16 +1900,22 @@ bool Codegen::visit(StringLiteral *ast) if (hasError) return false; - _expr.code = _block->STRING(_function->newString(ast->value.toString())); + auto r = Reference::fromAccumulator(this); + r.isReadonly = true; + _expr.setResult(r); + + Instruction::LoadRuntimeString instr; + instr.stringId = registerString(ast->value.toString()); + bytecodeGenerator->addInstruction(instr); return false; } -bool Codegen::visit(ThisExpression *ast) +bool Codegen::visit(ThisExpression *) { if (hasError) return false; - _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn); + _expr.setResult(Reference::fromThis(this)); return false; } @@ -1997,14 +1924,7 @@ bool Codegen::visit(TildeExpression *ast) if (hasError) return false; - const unsigned t = _block->newTemp(); - TempScope scope(_function); - - Result expr = expression(ast->expression); - if (hasError) - return false; - setLocation(move(_block->TEMP(t), unop(IR::OpCompl, *expr, ast->tildeToken)), ast->tildeToken); - _expr.code = _block->TEMP(t); + _expr.setResult(unop(Compl, expression(ast->expression))); return false; } @@ -2013,11 +1933,7 @@ bool Codegen::visit(TrueLiteral *) if (hasError) return false; - if (_expr.accept(cx)) { - _block->JUMP(_expr.iftrue); - } else { - _expr.code = _block->CONST(IR::BoolType, 1); - } + _expr.setResult(Reference::fromConst(this, QV4::Encode(true))); return false; } @@ -2026,14 +1942,24 @@ bool Codegen::visit(TypeOfExpression *ast) if (hasError) return false; - TempScope scope(_function); + RegisterScope scope(this); - Result expr = expression(ast->expression); + Reference expr = expression(ast->expression); if (hasError) return false; - IR::ExprList *args = _function->New<IR::ExprList>(); - args->init(reference(*expr)); - _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); + + if (expr.type == Reference::Name) { + // special handling as typeof doesn't throw here + Instruction::TypeofName instr; + instr.name = expr.nameAsIndex(); + bytecodeGenerator->addInstruction(instr); + } else { + expr.loadInAccumulator(); + Instruction::TypeofValue instr; + bytecodeGenerator->addInstruction(instr); + } + _expr.setResult(Reference::fromAccumulator(this)); + return false; } @@ -2042,12 +1968,7 @@ bool Codegen::visit(UnaryMinusExpression *ast) if (hasError) return false; - Result expr = expression(ast->expression); - if (hasError) - return false; - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), unop(IR::OpUMinus, *expr, ast->minusToken)), ast->minusToken); - _expr.code = _block->TEMP(t); + _expr.setResult(unop(UMinus, expression(ast->expression))); return false; } @@ -2056,12 +1977,7 @@ bool Codegen::visit(UnaryPlusExpression *ast) if (hasError) return false; - Result expr = expression(ast->expression); - if (hasError) - return false; - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), unop(IR::OpUPlus, *expr, ast->plusToken)), ast->plusToken); - _expr.code = _block->TEMP(t); + _expr.setResult(unop(UPlus, expression(ast->expression))); return false; } @@ -2070,10 +1986,10 @@ bool Codegen::visit(VoidExpression *ast) if (hasError) return false; - TempScope scope(_function); + RegisterScope scope(this); statement(ast->expression); - _expr.code = _block->CONST(IR::UndefinedType, 0); + _expr.setResult(Reference::fromConst(this, Encode::undefined())); return false; } @@ -2082,146 +1998,190 @@ bool Codegen::visit(FunctionDeclaration * ast) if (hasError) return false; - TempScope scope(_function); + RegisterScope scope(this); - if (_variableEnvironment->compilationMode == QmlBinding) - move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0)); + if (_context->compilationMode == QmlBinding) + Reference::fromName(this, ast->name.toString()).loadInAccumulator(); _expr.accept(nx); return false; } +static bool endsWithReturn(Node *node) +{ + if (!node) + return false; + if (AST::cast<ReturnStatement *>(node)) + return true; + if (AST::cast<ThrowStatement *>(node)) + return true; + if (Program *p = AST::cast<Program *>(node)) + return endsWithReturn(p->elements); + if (SourceElements *se = AST::cast<SourceElements *>(node)) { + while (se->next) + se = se->next; + return endsWithReturn(se->element); + } + if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(node)) + return endsWithReturn(sse->statement); + if (StatementList *sl = AST::cast<StatementList *>(node)) { + while (sl->next) + sl = sl->next; + return endsWithReturn(sl->statement); + } + if (Block *b = AST::cast<Block *>(node)) + return endsWithReturn(b->statements); + if (IfStatement *is = AST::cast<IfStatement *>(node)) + return is->ko && endsWithReturn(is->ok) && endsWithReturn(is->ko); + return false; +} + int Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, - AST::SourceElements *body, - const QStringList &inheritedLocals) -{ - Loop *loop = 0; - qSwap(_loop, loop); - QStack<IR::BasicBlock *> exceptionHandlers; - qSwap(_exceptionHandlers, exceptionHandlers); - - ScopeAndFinally *scopeAndFinally = 0; - - enterEnvironment(ast); - IR::Function *function = _module->newFunction(name, _function); - int functionIndex = _module->functions.count() - 1; - - IR::BasicBlock *entryBlock = function->newBasicBlock(0); - IR::BasicBlock *exitBlock = function->newBasicBlock(0, IR::Function::DontInsertBlock); - function->hasDirectEval = _variableEnvironment->hasDirectEval || _variableEnvironment->compilationMode == EvalCode - || _module->debugMode; // Conditional breakpoints are like eval in the function - function->usesArgumentsObject = _variableEnvironment->parent && (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUsed); - function->usesThis = _variableEnvironment->usesThis; - function->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); - function->isStrict = _variableEnvironment->isStrict; - function->isNamedExpression = _variableEnvironment->isNamedFunctionExpression; - function->isQmlBinding = _variableEnvironment->compilationMode == QmlBinding; - - AST::SourceLocation loc = ast->firstSourceLocation(); - function->line = loc.startLine; - function->column = loc.startColumn; - - if (function->usesArgumentsObject) - _variableEnvironment->enter(QStringLiteral("arguments"), Environment::VariableDeclaration, AST::VariableDeclaration::FunctionScope); + AST::SourceElements *body) +{ + Q_UNUSED(formals); + + enterContext(ast); + + if (_context->functionIndex >= 0) + // already defined + return leaveContext(); + + _context->name = name; + _module->functions.append(_context); + _context->functionIndex = _module->functions.count() - 1; + + _context->hasDirectEval |= (_context->compilationMode == EvalCode || _context->compilationMode == GlobalCode || _module->debugMode); // Conditional breakpoints are like eval in the function + + BytecodeGenerator bytecode(_context->line, _module->debugMode); + BytecodeGenerator *savedBytecodeGenerator; + savedBytecodeGenerator = bytecodeGenerator; + bytecodeGenerator = &bytecode; + bytecodeGenerator->setLocation(ast->firstSourceLocation()); + + // reserve the js stack frame (Context & js Function & accumulator) + bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size()); + + int returnAddress = -1; + bool _requiresReturnValue = (_context->compilationMode == QmlBinding || _context->compilationMode == EvalCode || _context->compilationMode == GlobalCode); + qSwap(requiresReturnValue, _requiresReturnValue); + if (requiresReturnValue) + returnAddress = bytecodeGenerator->newRegister(); + if (!_context->parent || _context->usesArgumentsObject == Context::ArgumentsObjectUnknown) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) + _context->addLocalVar(QStringLiteral("arguments"), Context::VariableDeclaration, AST::VariableDeclaration::FunctionScope); + + bool allVarsEscape = _context->hasWith || _context->hasTry || _context->hasDirectEval; + if (_context->compilationMode == QmlBinding // we don't really need this for bindings, but we do for signal handlers, and we don't know if the code is a signal handler or not. + || (!_context->canUseSimpleCall() && _context->compilationMode != GlobalCode && + (_context->compilationMode != EvalCode || _context->isStrict))) { + Instruction::CreateCallContext createContext; + bytecodeGenerator->addInstruction(createContext); + } // variables in global code are properties of the global context object, not locals as with other functions. - if (_variableEnvironment->compilationMode == FunctionCode || _variableEnvironment->compilationMode == QmlBinding) { - unsigned t = 0; - for (Environment::MemberMap::iterator it = _variableEnvironment->members.begin(), end = _variableEnvironment->members.end(); it != end; ++it) { + if (_context->compilationMode == FunctionCode || _context->compilationMode == QmlBinding) { + for (Context::MemberMap::iterator it = _context->members.begin(), end = _context->members.end(); it != end; ++it) { const QString &local = it.key(); - function->LOCAL(local); - (*it).index = t; - entryBlock->MOVE(entryBlock->LOCAL(t, 0), entryBlock->CONST(IR::UndefinedType, 0)); - ++t; - } - } else { - if (!_variableEnvironment->isStrict) { - for (const QString &inheritedLocal : qAsConst(inheritedLocals)) { - function->LOCAL(inheritedLocal); - unsigned tempIndex = entryBlock->newTemp(); - Environment::Member member = { Environment::UndefinedMember, - static_cast<int>(tempIndex), 0, - AST::VariableDeclaration::VariableScope::FunctionScope }; - _variableEnvironment->members.insert(inheritedLocal, member); + if (allVarsEscape) + it->canEscape = true; + if (it->canEscape) { + it->index = _context->locals.size(); + _context->locals.append(local); + if (it->type == Context::ThisFunctionName) { + // move the name from the stack to the call context + Instruction::LoadReg load; + load.reg = CallData::Function; + bytecodeGenerator->addInstruction(load); + Instruction::StoreLocal store; + store.index = it->index; + bytecodeGenerator->addInstruction(store); + } + } else { + if (it->type == Context::ThisFunctionName) + it->index = CallData::Function; + else + it->index = bytecodeGenerator->newRegister(); } } - - IR::ExprList *args = 0; - for (Environment::MemberMap::const_iterator it = _variableEnvironment->members.constBegin(), cend = _variableEnvironment->members.constEnd(); it != cend; ++it) { + } else { + for (Context::MemberMap::const_iterator it = _context->members.constBegin(), cend = _context->members.constEnd(); it != cend; ++it) { const QString &local = it.key(); - IR::ExprList *next = function->New<IR::ExprList>(); - next->expr = entryBlock->NAME(local, 0, 0); - next->next = args; - args = next; - } - if (args) { - IR::ExprList *next = function->New<IR::ExprList>(); - next->expr = entryBlock->CONST(IR::BoolType, false); // ### Investigate removal of bool deletable - next->next = args; - args = next; - entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(IR::Name::builtin_declare_vars, 0, 0), args)); + Instruction::DeclareVar declareVar; + declareVar.isDeletable = false; + declareVar.varName = registerString(local); + bytecodeGenerator->addInstruction(declareVar); } } - unsigned returnAddress = entryBlock->newTemp(); - - entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0)); - setLocation(exitBlock->RET(exitBlock->TEMP(returnAddress)), ast->lastSourceLocation()); - - qSwap(_function, function); - qSwap(_block, entryBlock); - qSwap(_exitBlock, exitBlock); qSwap(_returnAddress, returnAddress); - qSwap(_scopeAndFinally, scopeAndFinally); - - for (FormalParameterList *it = formals; it; it = it->next) { - _function->RECEIVE(it->name.toString()); - } - - for (const Environment::Member &member : qAsConst(_variableEnvironment->members)) { + for (const Context::Member &member : qAsConst(_context->members)) { if (member.function) { const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body ? member.function->body->elements : 0); - if (! _variableEnvironment->parent) { - move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), - _block->CLOSURE(function)); + loadClosure(function); + if (! _context->parent) { + Reference::fromName(this, member.function->name.toString()).storeConsumeAccumulator(); } else { Q_ASSERT(member.index >= 0); - move(_block->LOCAL(member.index, 0), _block->CLOSURE(function)); + Reference local = member.canEscape ? Reference::fromScopedLocal(this, member.index, 0) + : Reference::fromStackSlot(this, member.index, true); + local.storeConsumeAccumulator(); } } } - if (_function->usesArgumentsObject) { - move(identifier(QStringLiteral("arguments"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), - _block->CALL(_block->NAME(IR::Name::builtin_setup_argument_object, - ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0)); + if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) { + if (_context->isStrict) { + Instruction::CreateUnmappedArgumentsObject setup; + bytecodeGenerator->addInstruction(setup); + } else { + Instruction::CreateMappedArgumentsObject setup; + bytecodeGenerator->addInstruction(setup); + } + referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator(); } - if (_function->usesThis && !_function->isStrict) { + if (_context->usesThis && !_context->isStrict) { // make sure we convert this to an object - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_convert_this_to_object, - ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0)); + Instruction::ConvertThisToObject convert; + bytecodeGenerator->addInstruction(convert); } beginFunctionBodyHook(); sourceElements(body); - _function->addBasicBlock(_exitBlock); + if (hasError || !endsWithReturn(body)) { + bytecodeGenerator->setLocation(ast->lastSourceLocation()); + if (requiresReturnValue) { + if (_returnAddress >= 0) { + Instruction::LoadReg load; + load.reg = Moth::StackSlot::createRegister(_returnAddress); + bytecodeGenerator->addInstruction(load); + } + } else { + Reference::fromConst(this, Encode::undefined()).loadInAccumulator(); + } + bytecodeGenerator->addInstruction(Instruction::Ret()); + } - _block->JUMP(_exitBlock); + bytecodeGenerator->finalize(_context); + _context->registerCount = bytecodeGenerator->registerCount(); + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); + if (showCode) { + qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict + << "register count" << _context->registerCount; + QV4::Moth::dumpBytecode(_context->code, _context->locals.size(), _context->arguments.size(), + _context->line, _context->lineNumberMapping); + qDebug(); + } - qSwap(_function, function); - qSwap(_block, entryBlock); - qSwap(_exitBlock, exitBlock); qSwap(_returnAddress, returnAddress); - qSwap(_scopeAndFinally, scopeAndFinally); - qSwap(_exceptionHandlers, exceptionHandlers); - qSwap(_loop, loop); - - leaveEnvironment(); + qSwap(requiresReturnValue, _requiresReturnValue); + bytecodeGenerator = savedBytecodeGenerator; - return functionIndex; + return leaveContext(); } bool Codegen::visit(FunctionSourceElement *ast) @@ -2247,11 +2207,9 @@ bool Codegen::visit(Block *ast) if (hasError) return false; - TempScope scope(_function); + RegisterScope scope(this); - for (StatementList *it = ast->statements; it; it = it->next) { - statement(it->statement); - } + statementList(ast->statements); return false; } @@ -2260,27 +2218,22 @@ bool Codegen::visit(BreakStatement *ast) if (hasError) return false; - TempScope scope(_function); - - if (!_loop) { + if (!_context->controlFlow) { throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop")); return false; } - Loop *loop = 0; - if (ast->label.isEmpty()) - loop = _loop; - else { - for (loop = _loop; loop; loop = loop->parent) { - if (loop->labelledStatement && loop->labelledStatement->label == ast->label) - break; - } - if (!loop) { + + ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Break, ast->label.toString()); + if (h.type == ControlFlow::Invalid) { + if (ast->label.isEmpty()) + throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop")); + else throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString())); - return false; - } + return false; } - unwindException(loop->scopeAndFinally); - _block->JUMP(loop->breakBlock); + + _context->controlFlow->jumpToHandler(h); + return false; } @@ -2289,33 +2242,24 @@ bool Codegen::visit(ContinueStatement *ast) if (hasError) return false; - TempScope scope(_function); + RegisterScope scope(this); - Loop *loop = 0; - if (ast->label.isEmpty()) { - for (loop = _loop; loop; loop = loop->parent) { - if (loop->continueBlock) - break; - } - } else { - for (loop = _loop; loop; loop = loop->parent) { - if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { - if (!loop->continueBlock) - loop = 0; - break; - } - } - if (!loop) { - throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString())); - return false; - } + if (!_context->controlFlow) { + throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Continue outside of loop")); + return false; } - if (!loop) { - throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("continue outside of loop")); + + ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Continue, ast->label.toString()); + if (h.type == ControlFlow::Invalid) { + if (ast->label.isEmpty()) + throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString())); + else + throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("continue outside of loop")); return false; } - unwindException(loop->scopeAndFinally); - _block->JUMP(loop->continueBlock); + + _context->controlFlow->jumpToHandler(h); + return false; } @@ -2330,26 +2274,27 @@ bool Codegen::visit(DoWhileStatement *ast) if (hasError) return true; - TempScope scope(_function); + RegisterScope scope(this); - IR::BasicBlock *loopbody = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *loopcond = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *loopend = _function->newBasicBlock(exceptionHandler()); + BytecodeGenerator::Label body = bytecodeGenerator->label(); + BytecodeGenerator::Label cond = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); - enterLoop(ast, loopend, loopcond); + ControlFlowLoop flow(this, &end, &cond); - _block->JUMP(loopbody); - - _block = loopbody; statement(ast->statement); - setJumpOutLocation(_block->JUMP(loopcond), ast->statement, ast->semicolonToken); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->semicolonToken); - _block = loopcond; - condition(ast->expression, loopbody, loopend); + cond.link(); - _block = loopend; + if (!AST::cast<FalseLiteral *>(ast->expression)) { + if (AST::cast<TrueLiteral *>(ast->expression)) + bytecodeGenerator->jump().link(body); + else + condition(ast->expression, &body, &end, false); + } - leaveLoop(); + end.link(); return false; } @@ -2367,12 +2312,13 @@ bool Codegen::visit(ExpressionStatement *ast) if (hasError) return true; - TempScope scope(_function); + RegisterScope scope(this); - if (_variableEnvironment->compilationMode == EvalCode || _variableEnvironment->compilationMode == QmlBinding) { - Result e = expression(ast->expression); - if (*e) - move(_block->TEMP(_returnAddress), *e); + if (requiresReturnValue) { + Reference e = expression(ast->expression); + if (hasError) + return false; + (void) e.storeOnStack(_returnAddress); } else { statement(ast->expression); } @@ -2384,46 +2330,44 @@ bool Codegen::visit(ForEachStatement *ast) if (hasError) return true; - TempScope scope(_function); - - IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler()); + RegisterScope scope(this); - int objectToIterateOn = _block->newTemp(); - Result expr = expression(ast->expression); + Reference obj = Reference::fromStackSlot(this); + Reference expr = expression(ast->expression); if (hasError) - return false; - move(_block->TEMP(objectToIterateOn), *expr); - IR::ExprList *args = _function->New<IR::ExprList>(); - args->init(_block->TEMP(objectToIterateOn)); + return true; - int iterator = _block->newTemp(); - move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + expr.loadInAccumulator(); + Instruction::ForeachIteratorObject iteratorObjInstr; + bytecodeGenerator->addInstruction(iteratorObjInstr); + obj.storeConsumeAccumulator(); - enterLoop(ast, foreachend, foreachin); - _block->JUMP(foreachin); + BytecodeGenerator::Label in = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); + + bytecodeGenerator->jump().link(in); + + ControlFlowLoop flow(this, &end, &in); + + BytecodeGenerator::Label body = bytecodeGenerator->label(); - _block = foreachbody; - int temp = _block->newTemp(); - Result init = expression(ast->initialiser); - if (hasError) - return false; - move(*init, _block->TEMP(temp)); statement(ast->statement); - setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); + + in.link(); + + Reference lhs = expression(ast->initialiser).asLValue(); - _block = foreachin; + obj.loadInAccumulator(); + Instruction::ForeachNextPropertyName nextPropInstr; + bytecodeGenerator->addInstruction(nextPropInstr); + lhs = lhs.storeRetainAccumulator().storeOnStack(); - args = _function->New<IR::ExprList>(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); - int null = _block->newTemp(); - move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); - setLocation(cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend), ast->forToken); - _block = foreachend; + Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator(); + bytecodeGenerator->jumpStrictNotEqual(lhs.stackSlot(), body); + + end.link(); - leaveLoop(); return false; } @@ -2432,35 +2376,28 @@ bool Codegen::visit(ForStatement *ast) if (hasError) return true; - TempScope scope(_function); - - IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *forend = _function->newBasicBlock(exceptionHandler()); + RegisterScope scope(this); statement(ast->initialiser); - _block->JUMP(forcond); - enterLoop(ast, forend, forstep); + BytecodeGenerator::Label cond = bytecodeGenerator->label(); + BytecodeGenerator::Label body = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label step = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); - _block = forcond; - if (ast->condition) - condition(ast->condition, forbody, forend); - else - _block->JUMP(forbody); + ControlFlowLoop flow(this, &end, &step); + + condition(ast->condition, &body, &end, true); - _block = forbody; + body.link(); statement(ast->statement); - setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); - _block = forstep; + step.link(); statement(ast->expression); - _block->JUMP(forcond); + bytecodeGenerator->jump().link(cond); - _block = forend; - - leaveLoop(); + end.link(); return false; } @@ -2470,26 +2407,28 @@ bool Codegen::visit(IfStatement *ast) if (hasError) return true; - TempScope scope(_function); - - IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(exceptionHandler()) : 0; - IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); + RegisterScope scope(this); - condition(ast->expression, iftrue, ast->ko ? iffalse : endif); + BytecodeGenerator::Label trueLabel = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label falseLabel = bytecodeGenerator->newLabel(); + condition(ast->expression, &trueLabel, &falseLabel, true); - _block = iftrue; + trueLabel.link(); statement(ast->ok); - setJumpOutLocation(_block->JUMP(endif), ast->ok, ast->ifToken); - if (ast->ko) { - _block = iffalse; - statement(ast->ko); - setJumpOutLocation(_block->JUMP(endif), ast->ko, ast->elseToken); + if (endsWithReturn(ast)) { + falseLabel.link(); + statement(ast->ko); + } else { + BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump(); + falseLabel.link(); + statement(ast->ko); + jump_endif.link(); + } + } else { + falseLabel.link(); } - _block = endif; - return false; } @@ -2498,12 +2437,12 @@ bool Codegen::visit(LabelledStatement *ast) if (hasError) return true; - TempScope scope(_function); + RegisterScope scope(this); // check that no outer loop contains the label - Loop *l = _loop; + ControlFlow *l = _context->controlFlow; while (l) { - if (l->labelledStatement && l->labelledStatement->label == ast->label) { + if (l->label() == ast->label) { QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString()); throwSyntaxError(ast->firstSourceLocation(), error); return false; @@ -2521,12 +2460,10 @@ bool Codegen::visit(LabelledStatement *ast) AST::cast<AST::LocalForEachStatement *>(ast->statement)) { statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. } else { - IR::BasicBlock *breakBlock = _function->newBasicBlock(exceptionHandler()); - enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0); + BytecodeGenerator::Label breakLabel = bytecodeGenerator->newLabel(); + ControlFlowLoop flow(this, &breakLabel); statement(ast->statement); - _block->JUMP(breakBlock); - _block = breakBlock; - leaveLoop(); + breakLabel.link(); } return false; @@ -2537,40 +2474,44 @@ bool Codegen::visit(LocalForEachStatement *ast) if (hasError) return true; - TempScope scope(_function); + RegisterScope scope(this); - IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler()); + Reference obj = Reference::fromStackSlot(this); + Reference expr = expression(ast->expression); + if (hasError) + return true; variableDeclaration(ast->declaration); - int iterator = _block->newTemp(); - move(_block->TEMP(iterator), *expression(ast->expression)); - IR::ExprList *args = _function->New<IR::ExprList>(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + expr.loadInAccumulator(); + Instruction::ForeachIteratorObject iteratorObjInstr; + bytecodeGenerator->addInstruction(iteratorObjInstr); + obj.storeConsumeAccumulator(); + + BytecodeGenerator::Label in = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); - _block->JUMP(foreachin); - enterLoop(ast, foreachend, foreachin); + bytecodeGenerator->jump().link(in); + ControlFlowLoop flow(this, &end, &in); - _block = foreachbody; - int temp = _block->newTemp(); - move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); + BytecodeGenerator::Label body = bytecodeGenerator->label(); + + Reference it = referenceForName(ast->declaration->name.toString(), true).asLValue(); statement(ast->statement); - setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); + + in.link(); - _block = foreachin; + obj.loadInAccumulator(); + Instruction::ForeachNextPropertyName nextPropInstr; + bytecodeGenerator->addInstruction(nextPropInstr); + auto lhs = it.storeRetainAccumulator().storeOnStack(); - args = _function->New<IR::ExprList>(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); - int null = _block->newTemp(); - move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); - setLocation(cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend), ast->forToken); - _block = foreachend; + Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator(); + bytecodeGenerator->jumpStrictNotEqual(lhs.stackSlot(), body); + + end.link(); - leaveLoop(); return false; } @@ -2579,35 +2520,27 @@ bool Codegen::visit(LocalForStatement *ast) if (hasError) return true; - TempScope scope(_function); - - IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *forend = _function->newBasicBlock(exceptionHandler()); + RegisterScope scope(this); variableDeclarationList(ast->declarations); - _block->JUMP(forcond); - enterLoop(ast, forend, forstep); + BytecodeGenerator::Label cond = bytecodeGenerator->label(); + BytecodeGenerator::Label body = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label step = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); - _block = forcond; - if (ast->condition) - condition(ast->condition, forbody, forend); - else - _block->JUMP(forbody); + ControlFlowLoop flow(this, &end, &step); + + condition(ast->condition, &body, &end, true); - _block = forbody; + body.link(); statement(ast->statement); - setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); - _block = forstep; + step.link(); statement(ast->expression); - _block->JUMP(forcond); - - _block = forend; - - leaveLoop(); + bytecodeGenerator->jump().link(cond); + end.link(); return false; } @@ -2617,24 +2550,30 @@ bool Codegen::visit(ReturnStatement *ast) if (hasError) return true; - if (_variableEnvironment->compilationMode != FunctionCode && _variableEnvironment->compilationMode != QmlBinding) { + if (_context->compilationMode != FunctionCode && _context->compilationMode != QmlBinding) { throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function")); return false; } + Reference expr; if (ast->expression) { - Result expr = expression(ast->expression); - move(_block->TEMP(_returnAddress), *expr); + expr = expression(ast->expression); + if (hasError) + return false; + } else { + expr = Reference::fromConst(this, Encode::undefined()); } - // Since we're leaving, don't let any finally statements we emit as part of the unwinding - // jump to exception handlers at run-time if they throw. - IR::BasicBlock *unwindBlock = _function->newBasicBlock(/*no exception handler*/nullptr); - _block->JUMP(unwindBlock); - _block = unwindBlock; - - unwindException(0); - - _block->JUMP(_exitBlock); + if (_context->controlFlow && _context->controlFlow->returnRequiresUnwind()) { + if (_returnAddress >= 0) + (void) expr.storeOnStack(_returnAddress); + else + expr.loadInAccumulator(); + ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Return); + _context->controlFlow->jumpToHandler(h); + } else { + expr.loadInAccumulator(); + bytecodeGenerator->addInstruction(Instruction::Ret()); + } return false; } @@ -2643,245 +2582,151 @@ bool Codegen::visit(SwitchStatement *ast) if (hasError) return true; - TempScope scope(_function); - - IR::BasicBlock *switchend = _function->newBasicBlock(exceptionHandler()); + RegisterScope scope(this); if (ast->block) { - int lhs = _block->newTemp(); - move(_block->TEMP(lhs), *expression(ast->expression)); - IR::BasicBlock *switchcond = _function->newBasicBlock(exceptionHandler()); - _block->JUMP(switchcond); - IR::BasicBlock *previousBlock = 0; - - QHash<Node *, IR::BasicBlock *> blockMap; - - enterLoop(ast, switchend, 0); + BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel(); + Reference lhs = expression(ast->expression); + if (hasError) + return false; + lhs = lhs.storeOnStack(); + + // set up labels for all clauses + QHash<Node *, BytecodeGenerator::Label> blockMap; + for (CaseClauses *it = ast->block->clauses; it; it = it->next) + blockMap[it->clause] = bytecodeGenerator->newLabel(); + if (ast->block->defaultClause) + blockMap[ast->block->defaultClause] = bytecodeGenerator->newLabel(); + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) + blockMap[it->clause] = bytecodeGenerator->newLabel(); + + // do the switch conditions for (CaseClauses *it = ast->block->clauses; it; it = it->next) { CaseClause *clause = it->clause; - - _block = _function->newBasicBlock(exceptionHandler()); - blockMap[clause] = _block; - - if (previousBlock && !previousBlock->isTerminated()) - previousBlock->JUMP(_block); - - for (StatementList *it2 = clause->statements; it2; it2 = it2->next) - statement(it2->statement); - - previousBlock = _block; - } - - if (ast->block->defaultClause) { - _block = _function->newBasicBlock(exceptionHandler()); - blockMap[ast->block->defaultClause] = _block; - - if (previousBlock && !previousBlock->isTerminated()) - previousBlock->JUMP(_block); - - for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next) - statement(it2->statement); - - previousBlock = _block; + Reference rhs = expression(clause->expression); + if (hasError) + return false; + rhs.loadInAccumulator(); + bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause)); } for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { CaseClause *clause = it->clause; + Reference rhs = expression(clause->expression); + if (hasError) + return false; + rhs.loadInAccumulator(); + bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause)); + } - _block = _function->newBasicBlock(exceptionHandler()); - blockMap[clause] = _block; + if (DefaultClause *defaultClause = ast->block->defaultClause) + bytecodeGenerator->jump().link(blockMap.value(defaultClause)); + else + bytecodeGenerator->jump().link(switchEnd); - if (previousBlock && !previousBlock->isTerminated()) - previousBlock->JUMP(_block); + ControlFlowLoop flow(this, &switchEnd); - for (StatementList *it2 = clause->statements; it2; it2 = it2->next) - statement(it2->statement); + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + blockMap[clause].link(); - previousBlock = _block; + statementList(clause->statements); } - leaveLoop(); - - _block->JUMP(switchend); + if (ast->block->defaultClause) { + DefaultClause *clause = ast->block->defaultClause; + blockMap[clause].link(); - _block = switchcond; - for (CaseClauses *it = ast->block->clauses; it; it = it->next) { - CaseClause *clause = it->clause; - Result rhs = expression(clause->expression); - IR::BasicBlock *iftrue = blockMap[clause]; - IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); - setLocation(cjump(binop(IR::OpStrictEqual, _block->TEMP(lhs), *rhs), iftrue, iffalse), clause->caseToken); - _block = iffalse; + statementList(clause->statements); } for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { CaseClause *clause = it->clause; - Result rhs = expression(clause->expression); - IR::BasicBlock *iftrue = blockMap[clause]; - IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); - setLocation(cjump(binop(IR::OpStrictEqual, _block->TEMP(lhs), *rhs), iftrue, iffalse), clause->caseToken); - _block = iffalse; - } + blockMap[clause].link(); - if (DefaultClause *defaultClause = ast->block->defaultClause) { - setLocation(_block->JUMP(blockMap[ast->block->defaultClause]), defaultClause->defaultToken); + statementList(clause->statements); } - } - _block->JUMP(switchend); + switchEnd.link(); + + } - _block = switchend; return false; } bool Codegen::visit(ThrowStatement *ast) { if (hasError) - return true; + return false; + + RegisterScope scope(this); - TempScope scope(_function); + Reference expr = expression(ast->expression); + if (hasError) + return false; - Result expr = expression(ast->expression); - move(_block->TEMP(_returnAddress), *expr); - IR::ExprList *throwArgs = _function->New<IR::ExprList>(); - throwArgs->expr = _block->TEMP(_returnAddress); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + if (_context->controlFlow) { + _context->controlFlow->handleThrow(expr); + } else { + expr.loadInAccumulator(); + Instruction::ThrowException instr; + bytecodeGenerator->addInstruction(instr); + } return false; } -bool Codegen::visit(TryStatement *ast) +void Codegen::handleTryCatch(TryStatement *ast) { - if (hasError) - return true; - - TempScope scope(_function); - - _function->hasTry = true; - - if (_function->isStrict && ast->catchExpression && - (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) { + Q_ASSERT(ast); + if (_context->isStrict && + (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) { throwSyntaxError(ast->catchExpression->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode")); - return false; + return; } - IR::BasicBlock *surroundingExceptionHandler = exceptionHandler(); - - // We always need a finally body to clean up the exception handler - // exceptions thrown in finally get caught by the surrounding catch block - IR::BasicBlock *finallyBody = 0; - IR::BasicBlock *catchBody = 0; - IR::BasicBlock *catchExceptionHandler = 0; - IR::BasicBlock *end = _function->newBasicBlock(surroundingExceptionHandler, IR::Function::DontInsertBlock); - - if (ast->finallyExpression) - finallyBody = _function->newBasicBlock(surroundingExceptionHandler, IR::Function::DontInsertBlock); - - if (ast->catchExpression) { - // exception handler for the catch body - catchExceptionHandler = _function->newBasicBlock(0, IR::Function::DontInsertBlock); - pushExceptionHandler(catchExceptionHandler); - catchBody = _function->newBasicBlock(catchExceptionHandler, IR::Function::DontInsertBlock); - popExceptionHandler(); - pushExceptionHandler(catchBody); - } else { - Q_ASSERT(finallyBody); - pushExceptionHandler(finallyBody); + RegisterScope scope(this); + BytecodeGenerator::Label noException = bytecodeGenerator->newLabel(); + { + ControlFlowCatch catchFlow(this, ast->catchExpression); + RegisterScope scope(this); + statement(ast->statement); + bytecodeGenerator->jump().link(noException); } + noException.link(); +} - IR::BasicBlock *tryBody = _function->newBasicBlock(exceptionHandler()); - _block->JUMP(tryBody); - - ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression); - _scopeAndFinally = &tcf; - - _block = tryBody; - statement(ast->statement); - _block->JUMP(finallyBody ? finallyBody : end); - - popExceptionHandler(); +void Codegen::handleTryFinally(TryStatement *ast) +{ + RegisterScope scope(this); + ControlFlowFinally finally(this, ast->finallyExpression); if (ast->catchExpression) { - pushExceptionHandler(catchExceptionHandler); - _function->addBasicBlock(catchBody); - _block = catchBody; - - ++_function->insideWithOrCatch; - IR::ExprList *catchArgs = _function->New<IR::ExprList>(); - catchArgs->init(_block->STRING(_function->newString(ast->catchExpression->name.toString()))); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_catch_scope, 0, 0), catchArgs)); - { - ScopeAndFinally scope(_scopeAndFinally, ScopeAndFinally::CatchScope); - _scopeAndFinally = &scope; - statement(ast->catchExpression->statement); - _scopeAndFinally = scope.parent; - } - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); - --_function->insideWithOrCatch; - _block->JUMP(finallyBody ? finallyBody : end); - popExceptionHandler(); - - _function->addBasicBlock(catchExceptionHandler); - catchExceptionHandler->EXP(catchExceptionHandler->CALL(catchExceptionHandler->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); - if (finallyBody || surroundingExceptionHandler) - catchExceptionHandler->JUMP(finallyBody ? finallyBody : surroundingExceptionHandler); - else - catchExceptionHandler->EXP(catchExceptionHandler->CALL(catchExceptionHandler->NAME(IR::Name::builtin_rethrow, 0, 0), 0)); + handleTryCatch(ast); + } else { + RegisterScope scope(this); + statement(ast->statement); } +} - _scopeAndFinally = tcf.parent; - - if (finallyBody) { - _function->addBasicBlock(finallyBody); - _block = finallyBody; - - TempScope scope(_function); +bool Codegen::visit(TryStatement *ast) +{ + if (hasError) + return true; - int hasException = _block->newTemp(); - move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_unwind_exception, /*line*/0, /*column*/0), 0)); + Q_ASSERT(_context->hasTry); - if (ast->finallyExpression && ast->finallyExpression->statement) - statement(ast->finallyExpression->statement); + RegisterScope scope(this); - IR::ExprList *arg = _function->New<IR::ExprList>(); - arg->expr = _block->TEMP(hasException); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), arg)); - _block->JUMP(end); + if (ast->finallyExpression && ast->finallyExpression->statement) { + handleTryFinally(ast); + } else { + handleTryCatch(ast); } - _function->addBasicBlock(end); - _block = end; - return false; } -void Codegen::unwindException(Codegen::ScopeAndFinally *outest) -{ - int savedDepthForWidthOrCatch = _function->insideWithOrCatch; - ScopeAndFinally *scopeAndFinally = _scopeAndFinally; - qSwap(_scopeAndFinally, scopeAndFinally); - while (_scopeAndFinally != outest) { - switch (_scopeAndFinally->type) { - case ScopeAndFinally::WithScope: - // fall through - case ScopeAndFinally::CatchScope: - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0))); - _scopeAndFinally = _scopeAndFinally->parent; - --_function->insideWithOrCatch; - break; - case ScopeAndFinally::TryScope: { - ScopeAndFinally *tc = _scopeAndFinally; - _scopeAndFinally = tc->parent; - if (tc->finally && tc->finally->statement) - statement(tc->finally->statement); - break; - } - } - } - qSwap(_scopeAndFinally, scopeAndFinally); - _function->insideWithOrCatch = savedDepthForWidthOrCatch; -} - bool Codegen::visit(VariableStatement *ast) { if (hasError) @@ -2896,23 +2741,25 @@ bool Codegen::visit(WhileStatement *ast) if (hasError) return true; - IR::BasicBlock *whilecond = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *whilebody = _function->newBasicBlock(exceptionHandler()); - IR::BasicBlock *whileend = _function->newBasicBlock(exceptionHandler()); + if (AST::cast<FalseLiteral *>(ast->expression)) + return false; - enterLoop(ast, whileend, whilecond); + RegisterScope scope(this); - _block->JUMP(whilecond); - _block = whilecond; - condition(ast->expression, whilebody, whileend); + BytecodeGenerator::Label start = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label cond = bytecodeGenerator->label(); + ControlFlowLoop flow(this, &end, &cond); - _block = whilebody; - statement(ast->statement); - setJumpOutLocation(_block->JUMP(whilecond), ast->statement, ast->whileToken); + if (!AST::cast<TrueLiteral *>(ast->expression)) + condition(ast->expression, &start, &end, true); - _block = whileend; - leaveLoop(); + start.link(); + statement(ast->statement); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->whileToken); + bytecodeGenerator->jump().link(cond); + end.link(); return false; } @@ -2921,103 +2768,75 @@ bool Codegen::visit(WithStatement *ast) if (hasError) return true; - TempScope scope(_function); + RegisterScope scope(this); - _function->hasWith = true; + _context->hasWith = true; - const int withObject = _block->newTemp(); - Result src = expression(ast->expression); + Reference src = expression(ast->expression); if (hasError) return false; - _block->MOVE(_block->TEMP(withObject), *src); - - // need an exception handler for with to cleanup the with scope - IR::BasicBlock *withExceptionHandler = _function->newBasicBlock(exceptionHandler()); - withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); - if (!exceptionHandler()) - withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(IR::Name::builtin_rethrow, 0, 0), 0)); - else - withExceptionHandler->JUMP(exceptionHandler()); - - pushExceptionHandler(withExceptionHandler); - - IR::BasicBlock *withBlock = _function->newBasicBlock(exceptionHandler()); + src = src.storeOnStack(); // trigger load before we setup the exception handler, so exceptions here go to the right place + src.loadInAccumulator(); - _block->JUMP(withBlock); - _block = withBlock; - IR::ExprList *args = _function->New<IR::ExprList>(); - args->init(_block->TEMP(withObject)); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with_scope, 0, 0), args)); + ControlFlowWith flow(this); - ++_function->insideWithOrCatch; - { - ScopeAndFinally scope(_scopeAndFinally); - _scopeAndFinally = &scope; - statement(ast->statement); - _scopeAndFinally = scope.parent; - } - --_function->insideWithOrCatch; - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); - popExceptionHandler(); - - IR::BasicBlock *next = _function->newBasicBlock(exceptionHandler()); - _block->JUMP(next); - _block = next; + statement(ast->statement); return false; } bool Codegen::visit(UiArrayBinding *) { - Q_ASSERT(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } bool Codegen::visit(UiObjectBinding *) { - Q_ASSERT(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } bool Codegen::visit(UiObjectDefinition *) { - Q_ASSERT(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } bool Codegen::visit(UiPublicMember *) { - Q_ASSERT(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } bool Codegen::visit(UiScriptBinding *) { - Q_ASSERT(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } bool Codegen::visit(UiSourceElement *) { - Q_ASSERT(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } -bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr *expr, const SourceLocation& loc) +bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const SourceLocation& loc) { - if (!_variableEnvironment->isStrict) - return false; - if (IR::Name *n = expr->asName()) { - if (*n->id != QLatin1String("eval") && *n->id != QLatin1String("arguments")) - return false; - } else if (IR::ArgLocal *al = expr->asArgLocal()) { - if (!al->isArgumentsOrEval) - return false; - } else { + if (!_context->isStrict) return false; + bool isArgOrEval = false; + if (r.type == Reference::Name) { + QString str = jsUnitGenerator->stringForIndex(r.nameAsIndex()); + if (str == QLatin1String("eval") || str == QLatin1String("arguments")) { + isArgOrEval = true; + } + } else if (r.type == Reference::ScopedLocal || r.isRegister()) { + isArgOrEval = r.isArgOrEval; } - throwSyntaxError(loc, QStringLiteral("Variable name may not be eval or arguments in strict mode")); - return true; + if (isArgOrEval) + throwSyntaxError(loc, QStringLiteral("Variable name may not be eval or arguments in strict mode")); + return isArgOrEval; } void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) @@ -3049,6 +2868,116 @@ QList<QQmlJS::DiagnosticMessage> Codegen::errors() const return _errors; } +QQmlRefPointer<CompiledData::CompilationUnit> Codegen::generateCompilationUnit(bool generateUnitData) +{ + CompiledData::CompilationUnit *compilationUnit = new CompiledData::CompilationUnit; + if (generateUnitData) + compilationUnit->data = jsUnitGenerator->generateUnit(); + + QQmlRefPointer<CompiledData::CompilationUnit> unit; + unit.adopt(compilationUnit); + return unit; +} + +QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading() +{ + QQmlRefPointer<CompiledData::CompilationUnit> result; + result.adopt(new CompiledData::CompilationUnit); + return result; +} + +class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor +{ + VolatileMemoryLocations locs; + +public: + Codegen::VolatileMemoryLocations scan(AST::Node *s) + { + s->accept(this); + return locs; + } + + bool visit(ArrayMemberExpression *) Q_DECL_OVERRIDE + { + locs.setAllVolatile(); + return false; + } + + bool visit(FieldMemberExpression *) Q_DECL_OVERRIDE + { + locs.setAllVolatile(); + return false; + } + + bool visit(PostIncrementExpression *e) Q_DECL_OVERRIDE + { + collectIdentifiers(locs.specificLocations, e->base); + return false; + } + + bool visit(PostDecrementExpression *e) Q_DECL_OVERRIDE + { + collectIdentifiers(locs.specificLocations, e->base); + return false; + } + + bool visit(PreIncrementExpression *e) Q_DECL_OVERRIDE + { + collectIdentifiers(locs.specificLocations, e->expression); + return false; + } + + bool visit(PreDecrementExpression *e) Q_DECL_OVERRIDE + { + collectIdentifiers(locs.specificLocations, e->expression); + return false; + } + + bool visit(BinaryExpression *e) Q_DECL_OVERRIDE + { + switch (e->op) { + case QSOperator::InplaceAnd: + case QSOperator::InplaceSub: + case QSOperator::InplaceDiv: + case QSOperator::InplaceAdd: + case QSOperator::InplaceLeftShift: + case QSOperator::InplaceMod: + case QSOperator::InplaceMul: + case QSOperator::InplaceOr: + case QSOperator::InplaceRightShift: + case QSOperator::InplaceURightShift: + case QSOperator::InplaceXor: + collectIdentifiers(locs.specificLocations, e); + return false; + + default: + return true; + } + } + +private: + void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) const { + class Collector: public QQmlJS::AST::Visitor { + QVector<QStringView> &ids; + public: + Collector(QVector<QStringView> &ids): ids(ids) {} + virtual bool visit(IdentifierExpression *ie) { + ids.append(ie->name); + return false; + } + }; + Collector collector(ids); + node->accept(&collector); + } +}; + +Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast) const +{ + VolatileMemoryLocationScanner scanner; + return scanner.scan(ast); +} + + #ifndef V4_BOOTSTRAP QList<QQmlError> Codegen::qmlErrors() const @@ -3074,20 +3003,458 @@ QList<QQmlError> Codegen::qmlErrors() const return qmlErrors; } -void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) +#endif // V4_BOOTSTRAP + +bool Codegen::RValue::operator==(const RValue &other) const { - if (hasError) - return; - hasError = true; - engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn); + switch (type) { + case Accumulator: + return other.isAccumulator(); + case StackSlot: + return other.isStackSlot() && theStackSlot == other.theStackSlot; + case Const: + return other.isConst() && constant == other.constant; + default: + return false; + } } -void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail) +Codegen::RValue Codegen::RValue::storeOnStack() const { - if (hasError) + switch (type) { + case Accumulator: + return RValue::fromStackSlot(codegen, Reference::fromAccumulator(codegen).storeOnStack().stackSlot()); + case StackSlot: + return *this; + case Const: + return RValue::fromStackSlot(codegen, Reference::storeConstOnStack(codegen, constant).stackSlot()); + default: + Q_UNREACHABLE(); + } +} + +Codegen::Reference::Reference(const Codegen::Reference &other) +{ + *this = other; +} + +Codegen::Reference &Codegen::Reference::operator =(const Reference &other) +{ + type = other.type; + + switch (type) { + case Invalid: + case Accumulator: + break; + case StackSlot: + theStackSlot = other.theStackSlot; + break; + case ScopedLocal: + index = other.index; + scope = other.scope; + break; + case Name: + name = other.name; + break; + case Member: + propertyBase = other.propertyBase; + propertyNameIndex = other.propertyNameIndex; + break; + case Subscript: + elementBase = other.elementBase; + elementSubscript = other.elementSubscript; + break; + case Const: + constant = other.constant; + break; + case QmlScopeObject: + case QmlContextObject: + qmlBase = other.qmlBase; + qmlCoreIndex = other.qmlCoreIndex; + qmlNotifyIndex = other.qmlNotifyIndex; + captureRequired = other.captureRequired; + break; + } + + // keep loaded reference + isArgOrEval = other.isArgOrEval; + codegen = other.codegen; + isReadonly = other.isReadonly; + stackSlotIsLocalOrArgument = other.stackSlotIsLocalOrArgument; + isVolatile = other.isVolatile; + global = other.global; + return *this; +} + +bool Codegen::Reference::operator==(const Codegen::Reference &other) const +{ + if (type != other.type) + return false; + switch (type) { + case Invalid: + case Accumulator: + break; + case StackSlot: + return theStackSlot == other.theStackSlot; + case ScopedLocal: + return index == other.index && scope == other.scope; + case Name: + return nameAsIndex() == other.nameAsIndex(); + case Member: + return propertyBase == other.propertyBase && propertyNameIndex == other.propertyNameIndex; + case Subscript: + return elementBase == other.elementBase && elementSubscript == other.elementSubscript; + case Const: + return constant == other.constant; + case QmlScopeObject: + case QmlContextObject: + return qmlCoreIndex == other.qmlCoreIndex && qmlNotifyIndex == other.qmlNotifyIndex + && captureRequired == other.captureRequired; + } + return true; +} + +Codegen::RValue Codegen::Reference::asRValue() const +{ + switch (type) { + case Invalid: + Q_UNREACHABLE(); + case Accumulator: + return RValue::fromAccumulator(codegen); + case StackSlot: + return RValue::fromStackSlot(codegen, stackSlot()); + case Const: + return RValue::fromConst(codegen, constant); + default: + loadInAccumulator(); + return RValue::fromAccumulator(codegen); + } +} + +Codegen::Reference Codegen::Reference::asLValue() const +{ + switch (type) { + case Invalid: + case Accumulator: + Q_UNREACHABLE(); + case Member: + if (!propertyBase.isStackSlot()) { + Reference r = *this; + r.propertyBase = propertyBase.storeOnStack(); + return r; + } + return *this; + case Subscript: + if (!elementSubscript.isStackSlot()) { + Reference r = *this; + r.elementSubscript = elementSubscript.storeOnStack(); + return r; + } + return *this; + default: + return *this; + } +} + +Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const +{ + storeAccumulator(); // it doesn't matter what happens here, just do it. + return Reference(); +} + +Codegen::Reference Codegen::Reference::storeOnStack() const +{ return doStoreOnStack(-1); } + +void Codegen::Reference::storeOnStack(int slotIndex) const +{ doStoreOnStack(slotIndex); } + +Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const +{ + if (isStackSlot() && slotIndex == -1 && !(stackSlotIsLocalOrArgument && isVolatile)) + return *this; + + if (isStackSlot()) { // temp-to-temp move + Reference dest = Reference::fromStackSlot(codegen, slotIndex); + Instruction::MoveReg move; + move.srcReg = stackSlot(); + move.destReg = dest.stackSlot(); + codegen->bytecodeGenerator->addInstruction(move); + return dest; + } + + Reference slot = Reference::fromStackSlot(codegen, slotIndex); + if (isConst()) { + Instruction::MoveConst move; + move.constIndex = codegen->registerConstant(constant); + move.destTemp = slot.stackSlot(); + codegen->bytecodeGenerator->addInstruction(move); + } else { + loadInAccumulator(); + slot.storeConsumeAccumulator(); + } + return slot; +} + +Codegen::Reference Codegen::Reference::storeRetainAccumulator() const +{ + if (storeWipesAccumulator()) { + // a store will + auto tmp = Reference::fromStackSlot(codegen); + tmp.storeAccumulator(); // this is safe, and won't destory the accumulator + storeAccumulator(); + return tmp; + } else { + // ok, this is safe, just do the store. + storeAccumulator(); + return *this; + } +} + +bool Codegen::Reference::storeWipesAccumulator() const +{ + switch (type) { + default: + case Invalid: + case Const: + case Accumulator: + Q_UNREACHABLE(); + return false; + case StackSlot: + case ScopedLocal: + return false; + case Name: + case Member: + case Subscript: + case QmlScopeObject: + case QmlContextObject: + return true; + } +} + +void Codegen::Reference::storeAccumulator() const +{ + switch (type) { + case StackSlot: { + Instruction::StoreReg store; + store.reg = theStackSlot; + codegen->bytecodeGenerator->addInstruction(store); return; - hasError = true; - engine->throwReferenceError(detail, _module->fileName, loc.startLine, loc.startColumn); + } + case ScopedLocal: { + if (scope == 0) { + Instruction::StoreLocal store; + store.index = index; + codegen->bytecodeGenerator->addInstruction(store); + } else { + Instruction::StoreScopedLocal store; + store.index = index; + store.scope = scope; + codegen->bytecodeGenerator->addInstruction(store); + } + return; + } + case Name: { + Context *c = codegen->currentContext(); + if (c->isStrict) { + Instruction::StoreNameStrict store; + store.name = nameAsIndex(); + codegen->bytecodeGenerator->addInstruction(store); + } else { + Instruction::StoreNameSloppy store; + store.name = nameAsIndex(); + codegen->bytecodeGenerator->addInstruction(store); + } + } return; + case Member: + if (codegen->useFastLookups) { + Instruction::SetLookup store; + store.base = propertyBase.stackSlot(); + store.index = codegen->registerSetterLookup(propertyNameIndex); + codegen->bytecodeGenerator->addInstruction(store); + } else { + Instruction::StoreProperty store; + store.base = propertyBase.stackSlot(); + store.name = propertyNameIndex; + codegen->bytecodeGenerator->addInstruction(store); + } + return; + case Subscript: { + Instruction::StoreElement store; + store.base = elementBase; + store.index = elementSubscript.stackSlot(); + codegen->bytecodeGenerator->addInstruction(store); + } return; + case QmlScopeObject: { + Instruction::StoreScopeObjectProperty store; + store.base = qmlBase; + store.propertyIndex = qmlCoreIndex; + codegen->bytecodeGenerator->addInstruction(store); + } return; + case QmlContextObject: { + Instruction::StoreContextObjectProperty store; + store.base = qmlBase; + store.propertyIndex = qmlCoreIndex; + codegen->bytecodeGenerator->addInstruction(store); + } return; + case Invalid: + case Accumulator: + case Const: + break; + } + + Q_ASSERT(false); + Q_UNREACHABLE(); } -#endif // V4_BOOTSTRAP +void Codegen::Reference::loadInAccumulator() const +{ + switch (type) { + case Accumulator: + return; + case Const: { +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs. + if (constant == Encode::null()) { + Instruction::LoadNull load; + codegen->bytecodeGenerator->addInstruction(load); + } else if (constant == Encode(true)) { + Instruction::LoadTrue load; + codegen->bytecodeGenerator->addInstruction(load); + } else if (constant == Encode(false)) { + Instruction::LoadFalse load; + codegen->bytecodeGenerator->addInstruction(load); + } else if (constant == Encode::undefined()) { + Instruction::LoadUndefined load; + codegen->bytecodeGenerator->addInstruction(load); + } else { + Value p = Primitive::fromReturnedValue(constant); + if (p.isNumber()) { + double d = p.asDouble(); + int i = static_cast<int>(d); + if (d == i && (d != 0 || !std::signbit(d))) { + if (!i) { + Instruction::LoadZero load; + codegen->bytecodeGenerator->addInstruction(load); + return; + } + Instruction::LoadInt load; + load.value = Primitive::fromReturnedValue(constant).toInt32(); + codegen->bytecodeGenerator->addInstruction(load); + return; + } + } + Instruction::LoadConst load; + load.index = codegen->registerConstant(constant); + codegen->bytecodeGenerator->addInstruction(load); + } +QT_WARNING_POP + } return; + case StackSlot: { + Instruction::LoadReg load; + load.reg = stackSlot(); + codegen->bytecodeGenerator->addInstruction(load); + } return; + case ScopedLocal: { + if (!scope) { + Instruction::LoadLocal load; + load.index = index; + codegen->bytecodeGenerator->addInstruction(load); + } else { + Instruction::LoadScopedLocal load; + load.index = index; + load.scope = scope; + codegen->bytecodeGenerator->addInstruction(load); + } + return; + } + case Name: + if (global) { + // these value properties of the global object are immutable, we we can directly convert them + // to their numeric value here + if (name == QStringLiteral("undefined")) { + Reference::fromConst(codegen, Encode::undefined()).loadInAccumulator(); + return; + } else if (name == QStringLiteral("Infinity")) { + Reference::fromConst(codegen, Encode(qInf())).loadInAccumulator(); + return; + } else if (name == QStringLiteral("Nan")) { + Reference::fromConst(codegen, Encode(qQNaN())).loadInAccumulator(); + return; + } + } + if (codegen->useFastLookups && global) { + Instruction::LoadGlobalLookup load; + load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); + codegen->bytecodeGenerator->addInstruction(load); + } else { + Instruction::LoadName load; + load.name = nameAsIndex(); + codegen->bytecodeGenerator->addInstruction(load); + } + return; + case Member: + if (codegen->useFastLookups) { + if (propertyBase.isAccumulator()) { + Instruction::GetLookupA load; + load.index = codegen->registerGetterLookup(propertyNameIndex); + codegen->bytecodeGenerator->addInstruction(load); + } else { + Instruction::GetLookup load; + load.base = propertyBase.storeOnStack().stackSlot(); + load.index = codegen->registerGetterLookup(propertyNameIndex); + codegen->bytecodeGenerator->addInstruction(load); + } + } else { + if (propertyBase.isAccumulator()) { + Instruction::LoadPropertyA load; + load.name = propertyNameIndex; + codegen->bytecodeGenerator->addInstruction(load); + } else { + Instruction::LoadProperty load; + load.base = propertyBase.storeOnStack().stackSlot(); + load.name = propertyNameIndex; + codegen->bytecodeGenerator->addInstruction(load); + } + } + return; + case Subscript: { + if (elementSubscript.isAccumulator()) { + Instruction::LoadElementA load; + load.base = elementBase; + codegen->bytecodeGenerator->addInstruction(load); + } else if (elementSubscript.isConst()) { + Reference::fromConst(codegen, elementSubscript.constantValue()).loadInAccumulator(); + Instruction::LoadElementA load; + load.base = elementBase; + codegen->bytecodeGenerator->addInstruction(load); + } else { + Instruction::LoadElement load; + load.base = elementBase; + load.index = elementSubscript.storeOnStack().stackSlot(); + codegen->bytecodeGenerator->addInstruction(load); + } + } return; + case QmlScopeObject: { + Instruction::LoadScopeObjectProperty load; + load.base = qmlBase; + load.propertyIndex = qmlCoreIndex; + load.captureRequired = captureRequired; + codegen->bytecodeGenerator->addInstruction(load); + if (!captureRequired) + codegen->_context->scopeObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex); + } return; + case QmlContextObject: { + Instruction::LoadContextObjectProperty load; + load.base = qmlBase; + load.propertyIndex = qmlCoreIndex; + load.captureRequired = captureRequired; + codegen->bytecodeGenerator->addInstruction(load); + if (!captureRequired) + codegen->_context->contextObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex); + } return; + case Invalid: + break; + } + Q_ASSERT(false); + Q_UNREACHABLE(); +} diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 31e9cc0f46..dba9388292 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -51,311 +51,465 @@ // #include "private/qv4global_p.h" -#include "qv4jsir_p.h" #include <private/qqmljsastvisitor_p.h> #include <private/qqmljsast_p.h> #include <private/qqmljsengine_p.h> +#include <private/qv4instr_moth_p.h> +#include <private/qv4compiler_p.h> +#include <private/qv4compilercontext_p.h> +#include <private/qqmlrefcount_p.h> #include <QtCore/QStringList> +#include <QtCore/QDateTime> #include <QStack> #ifndef V4_BOOTSTRAP #include <qqmlerror.h> #endif #include <private/qv4util_p.h> +#include <private/qv4bytecodegenerator_p.h> QT_BEGIN_NAMESPACE -namespace QQmlJS { -namespace AST { -class UiParameterList; +using namespace QQmlJS; + +namespace QV4 { + +namespace Moth { +struct Instruction; } +namespace CompiledData { +struct CompilationUnit; +} + +namespace Compiler { + +struct ControlFlow; +struct ControlFlowCatch; +struct ControlFlowFinally; -class Q_QML_PRIVATE_EXPORT Codegen: protected AST::Visitor +class Q_QML_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor { +protected: + using BytecodeGenerator = QV4::Moth::BytecodeGenerator; + using Instruction = QV4::Moth::Instruction; public: - Codegen(bool strict); - - enum CompilationMode { - GlobalCode, - EvalCode, - FunctionCode, - QmlBinding // This is almost the same as EvalCode, except: - // * function declarations are moved to the return address when encountered - // * return statements are allowed everywhere (like in FunctionCode) - // * variable declarations are treated as true locals (like in FunctionCode) - }; + Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict); + void generateFromProgram(const QString &fileName, const QString &sourceCode, AST::Program *ast, - QV4::IR::Module *module, - CompilationMode mode = GlobalCode, - const QStringList &inheritedLocals = QStringList()); - void generateFromFunctionExpression(const QString &fileName, - const QString &sourceCode, - AST::FunctionExpression *ast, - QV4::IR::Module *module); + Module *module, + CompilationMode mode = GlobalCode); -protected: - enum Format { ex, cx, nx }; - struct Result { - QV4::IR::Expr *code; - QV4::IR::BasicBlock *iftrue; - QV4::IR::BasicBlock *iffalse; - Format format; - Format requested; +public: + class VolatileMemoryLocationScanner; + class VolatileMemoryLocations { + friend VolatileMemoryLocationScanner; + bool allVolatile = false; + QVector<QStringView> specificLocations; + public: + bool isVolatile(const QStringView &name) { + if (allVolatile) + return true; + return specificLocations.contains(name); + } - explicit Result(Format requested = ex) - : code(0) - , iftrue(0) - , iffalse(0) - , format(ex) - , requested(requested) {} - - explicit Result(QV4::IR::BasicBlock *iftrue, QV4::IR::BasicBlock *iffalse) - : code(0) - , iftrue(iftrue) - , iffalse(iffalse) - , format(ex) - , requested(cx) {} - - inline QV4::IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; } - inline QV4::IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; } + void add(const QStringRef &name) { if (!allVolatile) specificLocations.append(name); } + void setAllVolatile() { allVolatile = true; } + }; + class RValue { + Codegen *codegen; + enum Type { + Invalid, + Accumulator, + StackSlot, + Const + } type; + union { + Moth::StackSlot theStackSlot; + QV4::ReturnedValue constant; + }; - bool accept(Format f) - { - if (requested == f) { - format = f; + public: + static RValue fromStackSlot(Codegen *codegen, Moth::StackSlot stackSlot) { + RValue r; + r.codegen = codegen; + r.type = StackSlot; + r.theStackSlot = stackSlot; + return r; + } + static RValue fromAccumulator(Codegen *codegen) { + RValue r; + r.codegen = codegen; + r.type = Accumulator; + return r; + } + static RValue fromConst(Codegen *codegen, QV4::ReturnedValue value) { + RValue r; + r.codegen = codegen; + r.type = Const; + r.constant = value; + return r; + } + + bool operator==(const RValue &other) const; + + bool isValid() const { return type != Invalid; } + bool isAccumulator() const { return type == Accumulator; } + bool isStackSlot() const { return type == StackSlot; } + bool isConst() const { return type == Const; } + + Moth::StackSlot stackSlot() const { + Q_ASSERT(isStackSlot()); + return theStackSlot; + } + + QV4::ReturnedValue constantValue() const { + Q_ASSERT(isConst()); + return constant; + } + + Q_REQUIRED_RESULT RValue storeOnStack() const; + }; + struct Reference { + enum Type { + Invalid, + Accumulator, + StackSlot, + ScopedLocal, + Name, + Member, + Subscript, + QmlScopeObject, + QmlContextObject, + LastLValue = QmlContextObject, + Const + } type = Invalid; + + bool isLValue() const { return !isReadonly; } + + Reference(Codegen *cg, Type type = Invalid) : type(type), codegen(cg) {} + Reference() + : type(Invalid) + , codegen(nullptr) + {} + Reference(const Reference &other); + + Reference &operator =(const Reference &other); + + bool operator==(const Reference &other) const; + + bool isValid() const { return type != Invalid; } + bool loadTriggersSideEffect() const { + switch (type) { + case Name: + case Member: + case Subscript: return true; + default: + return false; } - return false; } - }; + bool isConst() const { return type == Const; } + bool isAccumulator() const { return type == Accumulator; } + bool isStackSlot() const { return type == StackSlot; } + bool isRegister() const { + return isStackSlot(); + } - struct Environment { - Environment *parent; + static Reference fromAccumulator(Codegen *cg) { + return Reference(cg, Accumulator); + } + static Reference fromStackSlot(Codegen *cg, int tempIndex = -1, bool isLocal = false) { + Reference r(cg, StackSlot); + if (tempIndex == -1) + tempIndex = cg->bytecodeGenerator->newRegister(); + r.theStackSlot = Moth::StackSlot::createRegister(tempIndex); + r.stackSlotIsLocalOrArgument = isLocal; + return r; + } + static Reference fromArgument(Codegen *cg, int index, bool isVolatile) { + Reference r(cg, StackSlot); + r.theStackSlot = Moth::StackSlot::createRegister(index + sizeof(CallData)/sizeof(Value) - 1); + r.stackSlotIsLocalOrArgument = true; + r.isVolatile = isVolatile; + return r; + } + static Reference fromScopedLocal(Codegen *cg, int index, int scope) { + Reference r(cg, ScopedLocal); + r.index = index; + r.scope = scope; + return r; + } + static Reference fromName(Codegen *cg, const QString &name) { + Reference r(cg, Name); + r.name = name; + return r; + } + static Reference fromMember(const Reference &baseRef, const QString &name) { + Reference r(baseRef.codegen, Member); + r.propertyBase = baseRef.asRValue(); + r.propertyNameIndex = r.codegen->registerString(name); + return r; + } + static Reference fromSubscript(const Reference &baseRef, const Reference &subscript) { + Q_ASSERT(baseRef.isStackSlot()); + Reference r(baseRef.codegen, Subscript); + r.elementBase = baseRef.stackSlot(); + r.elementSubscript = subscript.asRValue(); + return r; + } + static Reference fromConst(Codegen *cg, QV4::ReturnedValue constant) { + Reference r(cg, Const); + r.constant = constant; + r.isReadonly = true; + return r; + } + static Reference fromQmlScopeObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, bool captureRequired) { + Reference r(base.codegen, QmlScopeObject); + r.qmlBase = base.storeOnStack().stackSlot(); + r.qmlCoreIndex = coreIndex; + r.qmlNotifyIndex = notifyIndex; + r.captureRequired = captureRequired; + return r; + } + static Reference fromQmlContextObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, bool captureRequired) { + Reference r(base.codegen, QmlContextObject); + r.qmlBase = base.storeOnStack().stackSlot(); + r.qmlCoreIndex = coreIndex; + r.qmlNotifyIndex = notifyIndex; + r.captureRequired = captureRequired; + return r; + } + static Reference fromThis(Codegen *cg) { + Reference r = fromStackSlot(cg, CallData::This); + r.isReadonly = true; + return r; + } - enum MemberType { - UndefinedMember, - VariableDefinition, - VariableDeclaration, - FunctionDefinition - }; + RValue asRValue() const; + Reference asLValue() const; - struct Member { - MemberType type; - int index; - AST::FunctionExpression *function; - AST::VariableDeclaration::VariableScope scope; + Q_REQUIRED_RESULT static Reference storeConstOnStack(Codegen *cg, QV4::ReturnedValue constant) + { return Reference::fromConst(cg, constant).storeOnStack(); } - bool isLexicallyScoped() const { return this->scope != AST::VariableDeclaration::FunctionScope; } - }; - typedef QMap<QString, Member> MemberMap; - - MemberMap members; - AST::FormalParameterList *formals; - int maxNumberOfArguments; - bool hasDirectEval; - bool hasNestedFunctions; - bool isStrict; - bool isNamedFunctionExpression; - bool usesThis; - enum UsesArgumentsObject { - ArgumentsObjectUnknown, - ArgumentsObjectNotUsed, - ArgumentsObjectUsed + static void storeConstOnStack(Codegen *cg, QV4::ReturnedValue constant, int stackSlot) + { Reference::fromConst(cg, constant).storeOnStack(stackSlot); } + + Q_REQUIRED_RESULT Reference storeOnStack() const; + void storeOnStack(int tempIndex) const; + Q_REQUIRED_RESULT Reference storeRetainAccumulator() const; + Reference storeConsumeAccumulator() const; + + bool storeWipesAccumulator() const; + void loadInAccumulator() const; + + int nameAsIndex() const { + Q_ASSERT(type == Name); + return codegen->registerString(name); + } + + Moth::StackSlot stackSlot() const { + if (Q_UNLIKELY(!isStackSlot())) + Q_UNREACHABLE(); + return theStackSlot; + } + + union { + Moth::StackSlot theStackSlot; + QV4::ReturnedValue constant; + struct { // Scoped arguments/Local + int index; + int scope; + }; + struct { + RValue propertyBase; + int propertyNameIndex; + }; + struct { + Moth::StackSlot elementBase; + RValue elementSubscript; + }; + struct { // QML scope/context object case + Moth::StackSlot qmlBase; + qint16 qmlCoreIndex; + qint16 qmlNotifyIndex; + bool captureRequired; + }; }; + QString name; + mutable bool isArgOrEval = false; + bool isReadonly = false; + bool stackSlotIsLocalOrArgument = false; + bool isVolatile = false; + bool global = false; + Codegen *codegen; + + private: + void storeAccumulator() const; + Reference doStoreOnStack(int tempIndex) const; + }; - UsesArgumentsObject usesArgumentsObject; - - CompilationMode compilationMode; - - Environment(Environment *parent, CompilationMode mode) - : parent(parent) - , formals(0) - , maxNumberOfArguments(0) - , hasDirectEval(false) - , hasNestedFunctions(false) - , isStrict(false) - , isNamedFunctionExpression(false) - , usesThis(false) - , usesArgumentsObject(ArgumentsObjectUnknown) - , compilationMode(mode) - { - if (parent && parent->isStrict) - isStrict = true; + struct RegisterScope { + RegisterScope(Codegen *cg) + : generator(cg->bytecodeGenerator), + regCountForScope(generator->currentReg) {} + ~RegisterScope() { + generator->currentReg = regCountForScope; } + BytecodeGenerator *generator; + int regCountForScope; + }; + + struct ObjectPropertyValue { + ObjectPropertyValue() + : getter(-1) + , setter(-1) + , keyAsIndex(UINT_MAX) + {} + + Reference rvalue; + int getter; // index in _module->functions or -1 if not set + int setter; + uint keyAsIndex; + + bool hasGetter() const { return getter >= 0; } + bool hasSetter() const { return setter >= 0; } + }; +protected: - int findMember(const QString &name) const + enum Format { ex, cx, nx }; + class Result { + Reference _result; + + const BytecodeGenerator::Label *_iftrue; + const BytecodeGenerator::Label *_iffalse; + Format _format; + Format _requested; + bool _trueBlockFollowsCondition = false; + + public: + explicit Result(const Reference &lrvalue) + : _result(lrvalue) + , _iftrue(nullptr) + , _iffalse(nullptr) + , _format(ex) + , _requested(ex) { - MemberMap::const_iterator it = members.find(name); - if (it == members.end()) - return -1; - Q_ASSERT((*it).index != -1 || !parent); - return (*it).index; } - bool memberInfo(const QString &name, const Member **m) const + explicit Result(Format requested = ex) + : _iftrue(0) + , _iffalse(0) + , _format(ex) + , _requested(requested) {} + + explicit Result(const BytecodeGenerator::Label *iftrue, + const BytecodeGenerator::Label *iffalse, + bool trueBlockFollowsCondition) + : _iftrue(iftrue) + , _iffalse(iffalse) + , _format(ex) + , _requested(cx) + , _trueBlockFollowsCondition(trueBlockFollowsCondition) { - Q_ASSERT(m); - MemberMap::const_iterator it = members.find(name); - if (it == members.end()) { - *m = 0; - return false; - } - *m = &(*it); - return true; + Q_ASSERT(iftrue); + Q_ASSERT(iffalse); } - bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) - { - Environment *it = this; - *distance = 0; - for (; it; it = it->parent, ++(*distance)) { - int idx = it->findMember(name); - if (idx != -1) { - *scope = it; - *index = idx; - return true; - } - } - return false; + const BytecodeGenerator::Label *iftrue() const { + Q_ASSERT(_requested == cx); + return _iftrue; + } + + const BytecodeGenerator::Label *iffalse() const { + Q_ASSERT(_requested == cx); + return _iffalse; + } + + Format format() const { + return _format; } - void enter(const QString &name, MemberType type, AST::VariableDeclaration::VariableScope scope, AST::FunctionExpression *function = 0) + bool accept(Format f) { - if (! name.isEmpty()) { - if (type != FunctionDefinition) { - for (AST::FormalParameterList *it = formals; it; it = it->next) - if (it->name == name) - return; - } - MemberMap::iterator it = members.find(name); - if (it == members.end()) { - Member m; - m.index = -1; - m.type = type; - m.function = function; - m.scope = scope; - members.insert(name, m); - } else { - Q_ASSERT(scope == (*it).scope); - if ((*it).type <= type) { - (*it).type = type; - (*it).function = function; - } - } + if (_requested == f) { + _format = f; + return true; } + return false; } - }; - struct TempScope { - TempScope(QV4::IR::Function *f) - : function(f), - tempCountForScope(f->currentTemp) {} - ~TempScope() { - function->currentTemp = tempCountForScope; + bool trueBlockFollowsCondition() const { + return _trueBlockFollowsCondition; } - QV4::IR::Function *function; - int tempCountForScope; - }; - Environment *newEnvironment(AST::Node *node, Environment *parent, CompilationMode compilationMode) - { - Environment *env = new Environment(parent, compilationMode); - _envMap.insert(node, env); - return env; - } + const Reference &result() const { + return _result; + } - struct UiMember { + void setResult(const Reference &result) { + _result = result; + } }; - struct ScopeAndFinally { - enum ScopeType { - WithScope, - TryScope, - CatchScope - }; + void enterContext(AST::Node *node); + int leaveContext(); - ScopeAndFinally *parent; - AST::Finally *finally; - ScopeType type; + void leaveLoop(); - ScopeAndFinally(ScopeAndFinally *parent, ScopeType t = WithScope) : parent(parent), finally(0), type(t) {} - ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally) - : parent(parent), finally(finally), type(TryScope) - {} + enum UnaryOperation { + UPlus, + UMinus, + PreIncrement, + PreDecrement, + PostIncrement, + PostDecrement, + Not, + Compl }; - struct Loop { - AST::LabelledStatement *labelledStatement; - AST::Statement *node; - QV4::IR::BasicBlock *breakBlock; - QV4::IR::BasicBlock *continueBlock; - Loop *parent; - ScopeAndFinally *scopeAndFinally; - - Loop(AST::Statement *node, QV4::IR::BasicBlock *breakBlock, QV4::IR::BasicBlock *continueBlock, Loop *parent) - : labelledStatement(0), node(node), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {} - }; + Reference unop(UnaryOperation op, const Reference &expr); - void enterEnvironment(AST::Node *node); - void leaveEnvironment(); + void addCJump(); - void enterLoop(AST::Statement *node, QV4::IR::BasicBlock *breakBlock, QV4::IR::BasicBlock *continueBlock); - void leaveLoop(); - QV4::IR::BasicBlock *exceptionHandler() const - { - if (_exceptionHandlers.isEmpty()) - return 0; - return _exceptionHandlers.top(); - } - void pushExceptionHandler(QV4::IR::BasicBlock *handler) - { - handler->setExceptionHandler(true); - _exceptionHandlers.push(handler); + int registerString(const QString &name) { + return jsUnitGenerator->registerString(name); } - void popExceptionHandler() - { - Q_ASSERT(!_exceptionHandlers.isEmpty()); - _exceptionHandlers.pop(); - } - - QV4::IR::Expr *member(QV4::IR::Expr *base, const QString *name); - QV4::IR::Expr *argument(QV4::IR::Expr *expr); - QV4::IR::Expr *reference(QV4::IR::Expr *expr); - QV4::IR::Expr *unop(QV4::IR::AluOp op, QV4::IR::Expr *expr, const AST::SourceLocation &loc = AST::SourceLocation()); - QV4::IR::Expr *binop(QV4::IR::AluOp op, QV4::IR::Expr *left, QV4::IR::Expr *right, const AST::SourceLocation &loc = AST::SourceLocation()); - QV4::IR::Expr *call(QV4::IR::Expr *base, QV4::IR::ExprList *args); - QV4::IR::Stmt *move(QV4::IR::Expr *target, QV4::IR::Expr *source, QV4::IR::AluOp op = QV4::IR::OpInvalid); - QV4::IR::Stmt *cjump(QV4::IR::Expr *cond, QV4::IR::BasicBlock *iftrue, QV4::IR::BasicBlock *iffalse); + int registerConstant(QV4::ReturnedValue v) { return jsUnitGenerator->registerConstant(v); } + int registerGetterLookup(int nameIndex) { return jsUnitGenerator->registerGetterLookup(nameIndex); } + int registerSetterLookup(int nameIndex) { return jsUnitGenerator->registerSetterLookup(nameIndex); } + int registerGlobalGetterLookup(int nameIndex) { return jsUnitGenerator->registerGlobalGetterLookup(nameIndex); } // Returns index in _module->functions - int defineFunction(const QString &name, AST::Node *ast, - AST::FormalParameterList *formals, - AST::SourceElements *body, - const QStringList &inheritedLocals = QStringList()); - - void unwindException(ScopeAndFinally *outest); + virtual int defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body); void statement(AST::Statement *ast); void statement(AST::ExpressionNode *ast); - void condition(AST::ExpressionNode *ast, QV4::IR::BasicBlock *iftrue, QV4::IR::BasicBlock *iffalse); - Result expression(AST::ExpressionNode *ast); + void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, + const BytecodeGenerator::Label *iffalse, + bool trueBlockFollowsCondition); + Reference expression(AST::ExpressionNode *ast); Result sourceElement(AST::SourceElement *ast); - UiMember uiObjectMember(AST::UiObjectMember *ast); void accept(AST::Node *node); void functionBody(AST::FunctionBody *ast); void program(AST::Program *ast); void sourceElements(AST::SourceElements *ast); + void statementList(AST::StatementList *ast); void variableDeclaration(AST::VariableDeclaration *ast); void variableDeclarationList(AST::VariableDeclarationList *ast); - QV4::IR::Expr *identifier(const QString &name, int line = 0, int col = 0); + Reference referenceForName(const QString &name, bool lhs); + + void loadClosure(int index); + // Hook provided to implement QML lookup semantics - virtual QV4::IR::Expr *fallbackNameLookup(const QString &name, int line, int col); + virtual Reference fallbackNameLookup(const QString &name); virtual void beginFunctionBodyHook() {} // nodes @@ -457,7 +611,7 @@ protected: bool visit(AST::UiScriptBinding *ast) override; bool visit(AST::UiSourceElement *ast) override; - bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(QV4::IR::Expr* expr, const AST::SourceLocation &loc); + bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const AST::SourceLocation &loc); virtual void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); virtual void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); @@ -467,117 +621,47 @@ public: QList<QQmlError> qmlErrors() const; #endif + Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right); + Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right); + struct Arguments { int argc; int argv; }; + Arguments pushArgs(AST::ArgumentList *args); + + void setUseFastLookups(bool b) { useFastLookups = b; } + + void handleTryCatch(AST::TryStatement *ast); + void handleTryFinally(AST::TryStatement *ast); + + QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(bool generateUnitData = true); + static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading(); + + Context *currentContext() const { return _context; } + protected: + friend class ScanFunctions; + friend struct ControlFlow; + friend struct ControlFlowCatch; + friend struct ControlFlowFinally; Result _expr; - QString _property; - UiMember _uiMember; - QV4::IR::Module *_module; - QV4::IR::Function *_function; - QV4::IR::BasicBlock *_block; - QV4::IR::BasicBlock *_exitBlock; - unsigned _returnAddress; - Environment *_variableEnvironment; - Loop *_loop; + VolatileMemoryLocations _volataleMemoryLocations; + Module *_module; + int _returnAddress; + Context *_context; AST::LabelledStatement *_labelledStatement; - ScopeAndFinally *_scopeAndFinally; - QHash<AST::Node *, Environment *> _envMap; - QHash<AST::FunctionExpression *, int> _functionMap; - QStack<QV4::IR::BasicBlock *> _exceptionHandlers; + QV4::Compiler::JSUnitGenerator *jsUnitGenerator; + BytecodeGenerator *bytecodeGenerator = 0; bool _strictMode; + bool useFastLookups = true; + bool requiresReturnValue = false; bool _fileNameIsUrl; bool hasError; QList<QQmlJS::DiagnosticMessage> _errors; - class ScanFunctions: protected Visitor - { - typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment; - public: - ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode); - void operator()(AST::Node *node); - - void enterEnvironment(AST::Node *node, CompilationMode compilationMode); - void leaveEnvironment(); - - void enterQmlScope(AST::Node *ast, const QString &name) - { enterFunction(ast, name, /*formals*/0, /*body*/0, /*expr*/0, /*isExpression*/false); } - - void enterQmlFunction(AST::FunctionDeclaration *ast) - { enterFunction(ast, false, false); } - - protected: - using Visitor::visit; - using Visitor::endVisit; - - void checkDirectivePrologue(AST::SourceElements *ast); - - void checkName(const QStringRef &name, const AST::SourceLocation &loc); - void checkForArguments(AST::FormalParameterList *parameters); - - bool visit(AST::Program *ast) override; - void endVisit(AST::Program *) override; - - bool visit(AST::CallExpression *ast) override; - bool visit(AST::NewMemberExpression *ast) override; - bool visit(AST::ArrayLiteral *ast) override; - bool visit(AST::VariableDeclaration *ast) override; - bool visit(AST::IdentifierExpression *ast) override; - bool visit(AST::ExpressionStatement *ast) override; - bool visit(AST::FunctionExpression *ast) override; - - void enterFunction(AST::FunctionExpression *ast, bool enterName, bool isExpression = true); - - void endVisit(AST::FunctionExpression *) override; - - bool visit(AST::ObjectLiteral *ast) override; - - bool visit(AST::PropertyGetterSetter *ast) override; - void endVisit(AST::PropertyGetterSetter *) override; - - bool visit(AST::FunctionDeclaration *ast) override; - void endVisit(AST::FunctionDeclaration *) override; - - bool visit(AST::WithStatement *ast) override; - - bool visit(AST::DoWhileStatement *ast) override; - bool visit(AST::ForStatement *ast) override; - bool visit(AST::LocalForStatement *ast) override; - bool visit(AST::ForEachStatement *ast) override; - bool visit(AST::LocalForEachStatement *ast) override; - bool visit(AST::ThisExpression *ast) override; - - bool visit(AST::Block *ast) override; - - protected: - void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr, bool isExpression); - - // fields: - Codegen *_cg; - const QString _sourceCode; - Environment *_variableEnvironment; - QStack<Environment *> _envStack; - - bool _allowFuncDecls; - CompilationMode defaultProgramMode; - }; - -}; - -#ifndef V4_BOOTSTRAP -class RuntimeCodegen : public Codegen -{ -public: - RuntimeCodegen(QV4::ExecutionEngine *engine, bool strict) - : Codegen(strict) - , engine(engine) - {} - - void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override; - void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override; private: - QV4::ExecutionEngine *engine; + VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const; }; -#endif // V4_BOOTSTRAP + +} } diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 292f5cde45..3a1fd7fce5 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> @@ -50,6 +49,7 @@ #include <private/qqmlpropertycache_p.h> #include <private/qqmltypeloader_p.h> #include <private/qqmlengine_p.h> +#include <private/qv4vme_moth_p.h> #include "qv4compilationunitmapper_p.h" #include <QQmlPropertyMap> #include <QDateTime> @@ -94,23 +94,10 @@ static QString cacheFilePath(const QUrl &url) } #endif -#ifndef V4_BOOTSTRAP CompilationUnit::CompilationUnit() - : data(0) - , engine(0) - , qmlEngine(0) - , runtimeLookups(0) - , runtimeRegularExpressions(0) - , runtimeClasses(0) - , constants(nullptr) - , totalBindingsCount(0) - , totalParserStatusCount(0) - , totalObjectCount(0) - , metaTypeId(-1) - , listMetaTypeId(-1) - , isRegisteredWithEngine(false) {} +#ifndef V4_BOOTSTRAP CompilationUnit::~CompilationUnit() { unlink(); @@ -163,10 +150,6 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) l->setter = QV4::Lookup::setterGeneric; else if (type == CompiledData::Lookup::Type_GlobalGetter) l->globalGetter = QV4::Lookup::globalGetterGeneric; - else if (type == CompiledData::Lookup::Type_IndexedGetter) - l->indexedGetter = QV4::Lookup::indexedGetterGeneric; - else if (type == CompiledData::Lookup::Type_IndexedSetter) - l->indexedSetter = QV4::Lookup::indexedSetterGeneric; for (int j = 0; j < QV4::Lookup::Size; ++j) l->classList[j] = 0; @@ -201,6 +184,19 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) linkBackendToEngine(engine); + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); + if (showCode) { + qDebug() << "=== Constant table"; + Moth::dumpConstantTable(constants, data->constantTableSize); + qDebug() << "=== String table"; + for (uint i = 0; i < data->stringTableSize; ++i) + qDebug() << " " << i << ":" << runtimeStrings[i]->toQString(); + qDebug() << "=== Closure table"; + for (uint i = 0; i < data->functionTableSize; ++i) + qDebug() << " " << i << ":" << runtimeFunctions[i]->name()->toQString(); + qDebug() << "root function at index " << (data->indexOfRootFunction != -1 ? data->indexOfRootFunction : 0); + } + if (data->indexOfRootFunction != -1) return runtimeFunctions[data->indexOfRootFunction]; else @@ -260,14 +256,6 @@ void CompilationUnit::markObjects(QV4::MarkStack *markStack) } } -void CompilationUnit::destroy() -{ - if (qmlEngine) - QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this); - else - delete this; -} - IdentifierHash<int> CompilationUnit::namedObjectsPerComponent(int componentObjectIndex) { auto it = namedObjectsPerComponentCache.find(componentObjectIndex); @@ -349,7 +337,7 @@ bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHashe sizeof(data->dependencyMD5Checksum)) == 0; } -bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, EvalISelFactory *iselFactory, QString *errorString) +bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString) { if (!QQmlFile::isLocalFile(url)) { *errorString = QStringLiteral("File has to be a local file."); @@ -382,26 +370,26 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeS { const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex); - const QString expectedCodeGenerator = iselFactory->codeGeneratorName; + const QString expectedCodeGenerator = QStringLiteral("moth"); // ### if (foundCodeGenerator != expectedCodeGenerator) { *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator); return false; } } - if (!memoryMapCode(errorString)) - return false; - dataPtrChange.commit(); free(const_cast<Unit*>(oldDataPtr)); backingFile.reset(cacheFile.take()); return true; } -bool CompilationUnit::memoryMapCode(QString *errorString) +void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) { - *errorString = QStringLiteral("Missing code mapping backend"); - return false; + runtimeFunctions.resize(data->functionTableSize); + for (int i = 0 ;i < runtimeFunctions.size(); ++i) { + const QV4::CompiledData::Function *compiledFunction = data->functionAt(i); + runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction, &Moth::VME::exec); + } } #endif // V4_BOOTSTRAP @@ -443,17 +431,12 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) memcpy(&unitPtr, &dataPtr, sizeof(unitPtr)); unitPtr->flags |= Unit::StaticData; - prepareCodeOffsetsForDiskStorage(unitPtr); - qint64 headerWritten = cacheFile.write(modifiedUnit); if (headerWritten != modifiedUnit.size()) { *errorString = cacheFile.errorString(); return false; } - if (!saveCodeToDisk(&cacheFile, unitPtr, errorString)) - return false; - if (!cacheFile.commit()) { *errorString = cacheFile.errorString(); return false; @@ -467,19 +450,6 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) #endif // QT_CONFIG(temporaryfile) } -void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) -{ - Q_UNUSED(unit); -} - -bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QString *errorString) -{ - Q_UNUSED(device); - Q_UNUSED(unit); - *errorString = QStringLiteral("Saving code to disk is not supported in this configuration"); - return false; -} - Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) { if (!irDocument->javaScriptCompilationUnit->data) @@ -774,6 +744,17 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *e #endif +void CompilationUnit::destroy() +{ +#if !defined(V4_BOOTSTRAP) + if (qmlEngine) + QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this); + else +#endif + delete this; +} + + void Unit::generateChecksum() { #ifndef V4_BOOTSTRAP diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index a83dea1a0a..55c5f1f29f 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -72,7 +72,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x13 +#define QV4_DATA_STRUCTURE_VERSION 0x15 class QIODevice; class QQmlPropertyCache; @@ -87,9 +87,6 @@ struct Document; } namespace QV4 { -namespace IR { -struct Function; -} struct Function; class EvalISelFactory; @@ -155,9 +152,7 @@ struct Lookup enum Type : unsigned int { Type_Getter = 0x0, Type_Setter = 0x1, - Type_GlobalGetter = 2, - Type_IndexedGetter = 3, - Type_IndexedSetter = 4 + Type_GlobalGetter = 2 }; union { @@ -202,6 +197,11 @@ struct String }; static_assert(sizeof(String) == 4, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +struct CodeOffsetToLine { + quint32_le codeOffset; + quint32_le line; +}; + // Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties // for unaligned access. The ordering of the fields is also from largest to smallest. struct Function @@ -210,22 +210,23 @@ struct Function IsStrict = 0x1, HasDirectEval = 0x2, UsesArgumentsObject = 0x4, - IsNamedExpression = 0x8, - HasCatchOrWith = 0x10, - CanUseSimpleCall = 0x20 +// Unused = 0x8, + HasCatchOrWith = 0x10 }; - // Absolute offset into file where the code for this function is located. Only used when the function - // is serialized. - quint64_le codeOffset; - quint64_le codeSize; + // Absolute offset into file where the code for this function is located. + quint32_le codeOffset; + quint32_le codeSize; quint32_le nameIndex; quint32_le nFormals; quint32_le formalsOffset; quint32_le nLocals; quint32_le localsOffset; + quint32_le nLineNumbers; + quint32_le lineNumberOffset; quint32_le nInnerFunctions; + quint32_le nRegisters; Location location; // Qml Extensions Begin @@ -249,6 +250,7 @@ struct Function const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); } const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); } + const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset); } const quint32_le *qmlIdObjectDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset); } const quint32_le *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset); } const quint32_le *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset); } @@ -258,13 +260,23 @@ struct Function const quint32_le *formalsEnd() const { return formalsTable() + nFormals; } // --- + const uchar *code() const { return reinterpret_cast<const uchar *>(this) + codeOffset; } + inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; } - static int calculateSize(int nFormals, int nLocals, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies) { - return (sizeof(Function) + (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + 2 * nPropertyDependencies) * sizeof(quint32) + 7) & ~0x7; + static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies, int codeSize) { + int trailingData = (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + + 2 * nPropertyDependencies)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine); + size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize); + Q_ASSERT(size < INT_MAX); + return int(size); + } + + static size_t align(size_t a) { + return (a + 7) & ~size_t(7); } }; -static_assert(sizeof(Function) == 72, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Function) == 76, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); // Qml data structures @@ -860,40 +872,59 @@ typedef QVector<QQmlPropertyData*> BindingPropertyData; struct Q_QML_PRIVATE_EXPORT CompilationUnitBase { - QV4::Heap::String **runtimeStrings = 0; // Array + // pointers either to data->constants() or little-endian memory copy. + QV4::Heap::String **runtimeStrings = nullptr; // Array + const Value* constants = nullptr; + QV4::Value *runtimeRegularExpressions = nullptr; }; Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0); +Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **)); +Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const Value *)); -struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public QQmlRefCount +struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase { +public: + CompilationUnit(); #ifdef V4_BOOTSTRAP - CompilationUnit() - : data(0) - {} - virtual ~CompilationUnit() {} + ~CompilationUnit() {} #else - CompilationUnit(); - virtual ~CompilationUnit(); + ~CompilationUnit(); #endif - const Unit *data; + void addref() + { + Q_ASSERT(refCount.load() > 0); + refCount.ref(); + } + + void release() + { + Q_ASSERT(refCount.load() > 0); + if (!refCount.deref()) + destroy(); + } + int count() const + { + return refCount.load(); + } + + const Unit *data = nullptr; // Called only when building QML, when we build the header for JS first and append QML data - virtual QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument); + QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument); #ifndef V4_BOOTSTRAP QIntrusiveListNode nextCompilationUnit; - ExecutionEngine *engine; - QQmlEnginePrivate *qmlEngine; // only used in QML environment for composite types, not in plain QJSEngine case. + ExecutionEngine *engine = nullptr; + QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case. QString fileName() const { return data->stringAt(data->sourceFileIndex); } QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; } - QV4::Lookup *runtimeLookups; - QV4::Value *runtimeRegularExpressions; - QV4::InternalClass **runtimeClasses; + QV4::Lookup *runtimeLookups = nullptr; + QV4::InternalClass **runtimeClasses = nullptr; QVector<QV4::Function *> runtimeFunctions; mutable QQmlNullableValue<QUrl> m_url; @@ -913,23 +944,20 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public QHash<int, IdentifierHash<int>> namedObjectsPerComponentCache; IdentifierHash<int> namedObjectsPerComponent(int componentObjectIndex); - // pointers either to data->constants() or little-endian memory copy. - const Value* constants; - void finalizeCompositeType(QQmlEnginePrivate *qmlEngine); - int totalBindingsCount; // Number of bindings used in this type - int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses - int totalObjectCount; // Number of objects explicitly instantiated + int totalBindingsCount = 0; // Number of bindings used in this type + int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses + int totalObjectCount = 0; // Number of objects explicitly instantiated QVector<QQmlScriptData *> dependentScripts; ResolvedTypeReferenceMap resolvedTypes; bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const; - int metaTypeId; - int listMetaTypeId; - bool isRegisteredWithEngine; + int metaTypeId = -1; + int listMetaTypeId = -1; + bool isRegisteredWithEngine = false; QScopedPointer<CompilationUnitMapper> backingFile; @@ -960,25 +988,23 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public void markObjects(MarkStack *markStack); - void destroy() override; - - bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, EvalISelFactory *iselFactory, QString *errorString); + bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString); protected: - virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0; - virtual bool memoryMapCode(QString *errorString); + void linkBackendToEngine(QV4::ExecutionEngine *engine); #endif // V4_BOOTSTRAP +private: + void destroy(); + + QAtomicInt refCount = 1; + public: #if defined(V4_BOOTSTRAP) bool saveToDisk(const QString &outputFileName, QString *errorString); #else bool saveToDisk(const QUrl &unitUrl, QString *errorString); #endif - -protected: - virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit); - virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString); }; #ifndef V4_BOOTSTRAP diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index f02ee728c9..96c9307513 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -39,10 +39,12 @@ #include <qv4compiler_p.h> #include <qv4compileddata_p.h> -#include <qv4isel_p.h> +#include <qv4codegen_p.h> #include <private/qv4string_p.h> #include <private/qv4value_p.h> #include <private/qv4alloca_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsast_p.h> #include <wtf/MathExtras.h> #include <QCryptographicHash> @@ -98,70 +100,66 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) } } -QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::IR::Module *module) - : irModule(module) +QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::Compiler::Module *module) + : module(module) { // Make sure the empty string always gets index 0 registerString(QString()); } -uint QV4::Compiler::JSUnitGenerator::registerIndexedGetterLookup() +int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name) { - CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_IndexedGetter; - l.nameIndex = 0; - lookups << l; - return lookups.size() - 1; + return registerGetterLookup(registerString(name)); } -uint QV4::Compiler::JSUnitGenerator::registerIndexedSetterLookup() +int QV4::Compiler::JSUnitGenerator::registerGetterLookup(int nameIndex) { CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_IndexedSetter; - l.nameIndex = 0; + l.type_and_flags = CompiledData::Lookup::Type_Getter; + l.nameIndex = nameIndex; lookups << l; return lookups.size() - 1; } -uint QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name) +int QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name) { - CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_Getter; - l.nameIndex = registerString(name); - lookups << l; - return lookups.size() - 1; + return registerSetterLookup(registerString(name)); } - -uint QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name) +int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex) { CompiledData::Lookup l; l.type_and_flags = CompiledData::Lookup::Type_Setter; - l.nameIndex = registerString(name); + l.nameIndex = nameIndex; lookups << l; return lookups.size() - 1; } -uint QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name) +int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name) +{ + return registerGlobalGetterLookup(registerString(name)); +} + +int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex) { CompiledData::Lookup l; l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter; - l.nameIndex = registerString(name); + l.nameIndex = nameIndex; lookups << l; return lookups.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerRegExp(QV4::IR::RegExp *regexp) +int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *regexp) { CompiledData::RegExp re; - re.stringIndex = registerString(*regexp->value); + re.stringIndex = registerString(regexp->pattern.toString()); re.flags = 0; - if (regexp->flags & QV4::IR::RegExp::RegExp_Global) + if (regexp->flags & QQmlJS::Lexer::RegExp_Global) re.flags |= CompiledData::RegExp::RegExp_Global; - if (regexp->flags & QV4::IR::RegExp::RegExp_IgnoreCase) + if (regexp->flags & QQmlJS::Lexer::RegExp_IgnoreCase) re.flags |= CompiledData::RegExp::RegExp_IgnoreCase; - if (regexp->flags & QV4::IR::RegExp::RegExp_Multiline) + if (regexp->flags & QQmlJS::Lexer::RegExp_Multiline) re.flags |= CompiledData::RegExp::RegExp_Multiline; regexps.append(re); @@ -177,50 +175,62 @@ int QV4::Compiler::JSUnitGenerator::registerConstant(QV4::ReturnedValue v) return constants.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, IR::ExprList *args) +QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx) +{ + return constants.at(idx); +} + +int QV4::Compiler::JSUnitGenerator::registerJSClass(const QVector<MemberInfo> &members) { // ### re-use existing class definitions. - const int size = CompiledData::JSClass::calculateSize(count); + const int size = CompiledData::JSClass::calculateSize(members.size()); jsClassOffsets.append(jsClassData.size()); const int oldSize = jsClassData.size(); jsClassData.resize(jsClassData.size() + size); memset(jsClassData.data() + oldSize, 0, size); CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize); - jsClass->nMembers = count; + jsClass->nMembers = members.size(); CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1); - IR::ExprList *it = args; - for (int i = 0; i < count; ++i, it = it->next, ++member) { - QV4::IR::Name *name = it->expr->asName(); - it = it->next; + for (const MemberInfo &memberInfo : members) { + member->nameOffset = registerString(memberInfo.name); + member->isAccessor = memberInfo.isAccessor; + ++member; + } - const bool isData = it->expr->asConst()->value; - it = it->next; + return jsClassOffsets.size() - 1; +} - member->nameOffset = registerString(*name->id); - member->isAccessor = !isData; +int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, CompiledData::JSClassMember *members) +{ + const int size = CompiledData::JSClass::calculateSize(count); + jsClassOffsets.append(jsClassData.size()); + const int oldSize = jsClassData.size(); + jsClassData.resize(jsClassData.size() + size); + memset(jsClassData.data() + oldSize, 0, size); - if (!isData) - it = it->next; - } + CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize); + jsClass->nMembers = count; + CompiledData::JSClassMember *jsClassMembers = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1); + memcpy(jsClassMembers, members, sizeof(CompiledData::JSClassMember)*count); return jsClassOffsets.size() - 1; } QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option) { - registerString(irModule->fileName); - for (QV4::IR::Function *f : qAsConst(irModule->functions)) { - registerString(*f->name); - for (int i = 0; i < f->formals.size(); ++i) - registerString(*f->formals.at(i)); + registerString(module->fileName); + for (Context *f : qAsConst(module->functions)) { + registerString(f->name); + for (int i = 0; i < f->arguments.size(); ++i) + registerString(f->arguments.at(i)); for (int i = 0; i < f->locals.size(); ++i) - registerString(*f->locals.at(i)); + registerString(f->locals.at(i)); } - Q_ALLOCA_VAR(quint32_le, functionOffsets, irModule->functions.size() * sizeof(quint32_le)); + Q_ALLOCA_VAR(quint32_le, functionOffsets, module->functions.size() * sizeof(quint32_le)); uint jsClassDataOffset = 0; char *dataPtr; @@ -235,9 +245,9 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(quint32_le)); - for (int i = 0; i < irModule->functions.size(); ++i) { - QV4::IR::Function *function = irModule->functions.at(i); - if (function == irModule->rootFunction) + for (int i = 0; i < module->functions.size(); ++i) { + Context *function = module->functions.at(i); + if (function == module->rootContext) unit->indexOfRootFunction = i; writeFunction(dataPtr + functionOffsets[i], function); @@ -277,14 +287,14 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO return unit; } -void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *irFunction) const +void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Context *irFunction) const { QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f; quint32 currentOffset = sizeof(QV4::CompiledData::Function); currentOffset = (currentOffset + 7) & ~quint32(0x7); - function->nameIndex = getStringId(*irFunction->name); + function->nameIndex = getStringId(irFunction->name); function->flags = 0; if (irFunction->hasDirectEval) function->flags |= CompiledData::Function::HasDirectEval; @@ -292,13 +302,9 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i function->flags |= CompiledData::Function::UsesArgumentsObject; if (irFunction->isStrict) function->flags |= CompiledData::Function::IsStrict; - if (irFunction->isNamedExpression) - function->flags |= CompiledData::Function::IsNamedExpression; if (irFunction->hasTry || irFunction->hasWith) function->flags |= CompiledData::Function::HasCatchOrWith; - if (irFunction->canUseSimpleCall()) - function->flags |= CompiledData::Function::CanUseSimpleCall; - function->nFormals = irFunction->formals.size(); + function->nFormals = irFunction->arguments.size(); function->formalsOffset = currentOffset; currentOffset += function->nFormals * sizeof(quint32); @@ -306,7 +312,13 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i function->localsOffset = currentOffset; currentOffset += function->nLocals * sizeof(quint32); - function->nInnerFunctions = irFunction->nestedFunctions.size(); + function->nLineNumbers = irFunction->lineNumberMapping.size(); + function->lineNumberOffset = currentOffset; + currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine); + + function->nInnerFunctions = irFunction->nestedContexts.size(); + + function->nRegisters = irFunction->registerCount; function->nDependingIdObjects = 0; function->nDependingContextProperties = 0; @@ -333,18 +345,21 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i function->location.line = irFunction->line; function->location.column = irFunction->column; - function->codeOffset = 0; - function->codeSize = 0; + function->codeOffset = currentOffset; + function->codeSize = irFunction->code.size(); // write formals quint32_le *formals = (quint32_le *)(f + function->formalsOffset); - for (int i = 0; i < irFunction->formals.size(); ++i) - formals[i] = getStringId(*irFunction->formals.at(i)); + for (int i = 0; i < irFunction->arguments.size(); ++i) + formals[i] = getStringId(irFunction->arguments.at(i)); // write locals quint32_le *locals = (quint32_le *)(f + function->localsOffset); for (int i = 0; i < irFunction->locals.size(); ++i) - locals[i] = getStringId(*irFunction->locals.at(i)); + locals[i] = getStringId(irFunction->locals.at(i)); + + // write line numbers + memcpy(f + function->lineNumberOffset, irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine)); // write QML dependencies quint32_le *writtenDeps = (quint32_le *)(f + function->dependingIdObjectsOffset); @@ -364,6 +379,9 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i *writtenDeps++ = property.key(); // property index *writtenDeps++ = property.value(); // notify index } + + // write byte code + memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size()); } QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset) @@ -372,17 +390,17 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp memset(&unit, 0, sizeof(unit)); memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic)); unit.flags = QV4::CompiledData::Unit::IsJavascript; - unit.flags |= irModule->unitFlags; + unit.flags |= module->unitFlags; unit.version = QV4_DATA_STRUCTURE_VERSION; unit.qtVersion = QT_VERSION; memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum)); - unit.architectureIndex = registerString(irModule->targetABI.isEmpty() ? QSysInfo::buildAbi() : irModule->targetABI); + unit.architectureIndex = registerString(module->targetABI.isEmpty() ? QSysInfo::buildAbi() : module->targetABI); unit.codeGeneratorIndex = registerString(codeGeneratorName); memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum)); quint32 nextOffset = sizeof(CompiledData::Unit); - unit.functionTableSize = irModule->functions.size(); + unit.functionTableSize = module->functions.size(); unit.offsetToFunctionTable = nextOffset; nextOffset += unit.functionTableSize * sizeof(uint); @@ -410,13 +428,14 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp nextOffset = (nextOffset + 7) & ~quint32(0x7); - for (int i = 0; i < irModule->functions.size(); ++i) { - QV4::IR::Function *f = irModule->functions.at(i); + for (int i = 0; i < module->functions.size(); ++i) { + Context *f = module->functions.at(i); functionOffsets[i] = nextOffset; const int qmlIdDepsCount = f->idObjectDependencies.count(); const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count(); - nextOffset += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), qmlIdDepsCount, qmlPropertyDepsCount); + nextOffset += QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(), + qmlIdDepsCount, qmlPropertyDepsCount, f->code.size()); } if (option == GenerateWithStringTable) { @@ -428,8 +447,8 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.offsetToStringTable = 0; } unit.indexOfRootFunction = -1; - unit.sourceFileIndex = getStringId(irModule->fileName); - unit.sourceTimeStamp = irModule->sourceTimeStamp.isValid() ? irModule->sourceTimeStamp.toMSecsSinceEpoch() : 0; + unit.sourceFileIndex = getStringId(module->fileName); + unit.sourceTimeStamp = module->sourceTimeStamp.isValid() ? module->sourceTimeStamp.toMSecsSinceEpoch() : 0; unit.nImports = 0; unit.offsetToImports = 0; unit.nObjects = 0; diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 9c51a44bad..360af6540f 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -51,8 +51,11 @@ // #include <QtCore/qstring.h> -#include "qv4jsir_p.h" -#include <private/qendian_p.h> +#include <QtCore/qhash.h> +#include <QtCore/qstringlist.h> +#include <private/qv4global_p.h> +#include <private/qqmljsastfwd_p.h> +#include <private/qv4compileddata_p.h> QT_BEGIN_NAMESPACE @@ -90,23 +93,31 @@ private: }; struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { - JSUnitGenerator(IR::Module *module); + struct MemberInfo { + QString name; + bool isAccessor; + }; + + JSUnitGenerator(Module *module); int registerString(const QString &str) { return stringTable.registerString(str); } int getStringId(const QString &string) const { return stringTable.getStringId(string); } QString stringForIndex(int index) const { return stringTable.stringForIndex(index); } - uint registerGetterLookup(const QString &name); - uint registerSetterLookup(const QString &name); - uint registerGlobalGetterLookup(const QString &name); - uint registerIndexedGetterLookup(); - uint registerIndexedSetterLookup(); + int registerGetterLookup(const QString &name); + int registerGetterLookup(int nameIndex); + int registerSetterLookup(const QString &name); + int registerSetterLookup(int nameIndex); + int registerGlobalGetterLookup(const QString &name); + int registerGlobalGetterLookup(int nameIndex); - int registerRegExp(IR::RegExp *regexp); + int registerRegExp(QQmlJS::AST::RegExpLiteral *regexp); int registerConstant(ReturnedValue v); + ReturnedValue constant(int idx); - int registerJSClass(int count, IR::ExprList *args); + int registerJSClass(const QVector<MemberInfo> &members); + int registerJSClass(int count, CompiledData::JSClassMember *members); enum GeneratorOption { GenerateWithStringTable, @@ -115,14 +126,14 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable); // Returns bytes written - void writeFunction(char *f, IR::Function *irFunction) const; + void writeFunction(char *f, Context *irFunction) const; StringTableGenerator stringTable; QString codeGeneratorName; private: CompiledData::Unit generateHeader(GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset); - IR::Module *irModule; + Module *module; QList<CompiledData::Lookup> lookups; QVector<CompiledData::RegExp> regexps; diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp new file mode 100644 index 0000000000..3293dc53c8 --- /dev/null +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qv4compilercontext_p.h" +#include "qv4compilercontrolflow_p.h" + +QT_USE_NAMESPACE +using namespace QV4; +using namespace QV4::Compiler; +using namespace QQmlJS::AST; + +QT_BEGIN_NAMESPACE + +Context *Module::newContext(Node *node, Context *parent, CompilationMode compilationMode) +{ + Context *c = new Context(parent, compilationMode); + if (node) { + SourceLocation loc = node->firstSourceLocation(); + c->line = loc.startLine; + c->column = loc.startColumn; + } + + contextMap.insert(node, c); + + if (!parent) + rootContext = c; + else { + parent->nestedContexts.append(c); + c->isStrict = parent->isStrict; + } + + return c; +} + +bool Context::forceLookupByName() +{ + ControlFlow *flow = controlFlow; + while (flow) { + if (flow->needsLookupByName) + return true; + flow = flow->parent; + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h new file mode 100644 index 0000000000..3db30ea23d --- /dev/null +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QV4COMPILERCONTEXT_P_H +#define QV4COMPILERCONTEXT_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/qqmljsast_p.h> +#include <private/qv4compileddata_p.h> +#include <QtCore/QStringList> +#include <QtCore/QDateTime> +#include <QtCore/QStack> +#include <QtCore/QHash> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Compiler { + +struct ControlFlow; + +enum CompilationMode { + GlobalCode, + EvalCode, + FunctionCode, + QmlBinding // This is almost the same as EvalCode, except: + // * function declarations are moved to the return address when encountered + // * return statements are allowed everywhere (like in FunctionCode) + // * variable declarations are treated as true locals (like in FunctionCode) +}; + +struct Context; + +struct Module { + Module(bool debugMode) + : debugMode(debugMode) + {} + ~Module() { + qDeleteAll(contextMap); + } + + Context *newContext(QQmlJS::AST::Node *node, Context *parent, CompilationMode compilationMode); + + QHash<QQmlJS::AST::Node *, Context *> contextMap; + QList<Context *> functions; + Context *rootContext; + QString fileName; + QDateTime sourceTimeStamp; + uint unitFlags = 0; // flags merged into CompiledData::Unit::flags + bool debugMode = false; + QString targetABI; // ### seems unused currently +}; + + +struct Context { + Context *parent; + QString name; + int line = 0; + int column = 0; + int registerCount = 0; + int functionIndex = -1; + + enum MemberType { + UndefinedMember, + ThisFunctionName, + VariableDefinition, + VariableDeclaration, + FunctionDefinition + }; + + struct Member { + MemberType type = UndefinedMember; + int index = -1; + QQmlJS::AST::VariableDeclaration::VariableScope scope = QQmlJS::AST::VariableDeclaration::FunctionScope; + mutable bool canEscape = false; + QQmlJS::AST::FunctionExpression *function = 0; + + bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableDeclaration::FunctionScope; } + }; + typedef QMap<QString, Member> MemberMap; + + MemberMap members; + QSet<QString> usedVariables; + QQmlJS::AST::FormalParameterList *formals = 0; + QStringList arguments; + QStringList locals; + QVector<Context *> nestedContexts; + + ControlFlow *controlFlow = 0; + QByteArray code; + QVector<CompiledData::CodeOffsetToLine> lineNumberMapping; + + int maxNumberOfArguments = 0; + bool hasDirectEval = false; + bool hasNestedFunctions = false; + bool isStrict = false; + bool usesThis = false; + bool hasTry = false; + bool hasWith = false; + mutable bool argumentsCanEscape = false; + + enum UsesArgumentsObject { + ArgumentsObjectUnknown, + ArgumentsObjectNotUsed, + ArgumentsObjectUsed + }; + + UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown; + + CompilationMode compilationMode; + + 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)); + } + }; + + // Qml extension: + SmallSet<int> idObjectDependencies; + PropertyDependencyMap contextObjectPropertyDependencies; + PropertyDependencyMap scopeObjectPropertyDependencies; + + Context(Context *parent, CompilationMode mode) + : parent(parent) + , compilationMode(mode) + { + if (parent && parent->isStrict) + isStrict = true; + } + + bool forceLookupByName(); + + + bool canUseSimpleCall() const { + return nestedContexts.isEmpty() && + locals.isEmpty() && + !hasTry && !hasWith && + (usesArgumentsObject == ArgumentsObjectNotUsed || isStrict) && !hasDirectEval; + } + + int findArgument(const QString &name) + { + // search backwards to handle duplicate argument names correctly + for (int i = arguments.size() - 1; i >= 0; --i) { + if (arguments.at(i) == name) + return i; + } + return -1; + } + + Member findMember(const QString &name) const + { + MemberMap::const_iterator it = members.find(name); + if (it == members.end()) + return Member(); + Q_ASSERT(it->index != -1 || !parent); + return (*it); + } + + bool memberInfo(const QString &name, const Member **m) const + { + Q_ASSERT(m); + MemberMap::const_iterator it = members.find(name); + if (it == members.end()) { + *m = 0; + return false; + } + *m = &(*it); + return true; + } + + void addUsedVariable(const QString &name) { + usedVariables.insert(name); + } + + void addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = 0) + { + if (! name.isEmpty()) { + if (type != FunctionDefinition) { + for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next) + if (it->name == name) + return; + } + MemberMap::iterator it = members.find(name); + if (it == members.end()) { + Member m; + m.type = type; + m.function = function; + m.scope = scope; + members.insert(name, m); + } else { + Q_ASSERT(scope == (*it).scope); + if ((*it).type <= type) { + (*it).type = type; + (*it).function = function; + } + } + } + } +}; + + +} } // namespace QV4::Compiler + +QT_END_NAMESPACE + +#endif // QV4CODEGEN_P_H diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h new file mode 100644 index 0000000000..13716fe29f --- /dev/null +++ b/src/qml/compiler/qv4compilercontrolflow_p.h @@ -0,0 +1,467 @@ +/**************************************************************************** +** +** 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 QV4COMPILERCONTROLFLOW_P_H +#define QV4COMPILERCONTROLFLOW_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/qv4codegen_p.h> +#include <private/qqmljsast_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Compiler { + +struct ControlFlow { + using Reference = Codegen::Reference; + using BytecodeGenerator = Moth::BytecodeGenerator; + using Instruction = Moth::Instruction; + + enum Type { + Loop, + With, + Finally, + Catch + }; + + enum HandlerType { + Invalid, + Break, + Continue, + Return, + Throw + }; + + struct Handler { + HandlerType type; + QString label; + BytecodeGenerator::Label linkLabel; + int tempIndex; + int value; + }; + + Codegen *cg; + ControlFlow *parent; + Type type; + bool needsLookupByName = false; + + ControlFlow(Codegen *cg, Type type) + : cg(cg), parent(cg->_context->controlFlow), type(type) + { + cg->_context->controlFlow = this; + } + + virtual ~ControlFlow() { + cg->_context->controlFlow = parent; + } + + void emitReturnStatement() const { + if (cg->_returnAddress >= 0) { + Instruction::LoadReg load; + load.reg = Moth::StackSlot::createRegister(cg->_returnAddress); + generator()->addInstruction(load); + } + Instruction::Ret ret; + cg->bytecodeGenerator->addInstruction(ret); + } + + void jumpToHandler(const Handler &h) { + if (h.linkLabel.isReturn()) { + emitReturnStatement(); + } else { + if (h.tempIndex >= 0) + Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex); + cg->bytecodeGenerator->jump().link(h.linkLabel); + } + } + + bool returnRequiresUnwind() const { + const ControlFlow *f = this; + while (f) { + if (f->type == Finally) + return true; + f = f->parent; + } + return false; + } + + virtual QString label() const { return QString(); } + + bool isSimple() const { + return type == Loop; + } + + Handler getParentHandler(HandlerType type, const QString &label = QString()) { + if (parent) + return parent->getHandler(type, label); + switch (type) { + case Break: + case Continue: + return { Invalid, QString(), {}, -1, 0 }; + case Return: + case Throw: + return { type, QString(), BytecodeGenerator::Label::returnLabel(), -1, 0 }; + case Invalid: + break; + } + Q_ASSERT(false); + Q_UNREACHABLE(); + } + + virtual Handler getHandler(HandlerType type, const QString &label = QString()) = 0; + + BytecodeGenerator::ExceptionHandler *parentExceptionHandler() { + return parent ? parent->exceptionHandler() : 0; + } + + virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { + return parentExceptionHandler(); + } + + + virtual void handleThrow(const Reference &expr) { + Reference e = expr; + Handler h = getHandler(ControlFlow::Throw); + if (h.tempIndex >= 0) { + e = e.storeOnStack(); + Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex); + } + e.loadInAccumulator(); + Instruction::ThrowException instr; + generator()->addInstruction(instr); + } + +protected: + QString loopLabel() const { + QString label; + if (cg->_labelledStatement) { + label = cg->_labelledStatement->label.toString(); + cg->_labelledStatement = 0; + } + return label; + } + BytecodeGenerator *generator() const { + return cg->bytecodeGenerator; + } +}; + +struct ControlFlowLoop : public ControlFlow +{ + QString loopLabel; + BytecodeGenerator::Label *breakLabel = 0; + BytecodeGenerator::Label *continueLabel = 0; + + ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = 0) + : ControlFlow(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel) + { + } + + virtual QString label() const { return loopLabel; } + + virtual Handler getHandler(HandlerType type, const QString &label = QString()) { + switch (type) { + case Break: + if (breakLabel && (label.isEmpty() || label == loopLabel)) + return { type, loopLabel, *breakLabel, -1, 0 }; + break; + case Continue: + if (continueLabel && (label.isEmpty() || label == loopLabel)) + return { type, loopLabel, *continueLabel, -1, 0 }; + break; + case Return: + case Throw: + break; + case Invalid: + Q_ASSERT(false); + Q_UNREACHABLE(); + } + return getParentHandler(type, label); + } + +}; + +struct ControlFlowUnwind : public ControlFlow +{ + BytecodeGenerator::ExceptionHandler unwindLabel; + int controlFlowTemp; + QVector<Handler> handlers; + + ControlFlowUnwind(Codegen *cg, Type type) + : ControlFlow(cg, type), unwindLabel(generator()->newExceptionHandler()) + { + Q_ASSERT(type != Loop); + controlFlowTemp = static_cast<int>(generator()->newRegister()); + Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp); + // we'll need at least a handler for throw + getHandler(Throw); + } + + void emitUnwindHandler() + { + Q_ASSERT(!isSimple()); + + Reference temp = Reference::fromStackSlot(cg, controlFlowTemp); + for (const auto &h : qAsConst(handlers)) { + Handler parentHandler = getParentHandler(h.type, h.label); + + if (h.type == Throw || parentHandler.tempIndex >= 0) { + BytecodeGenerator::Label skip = generator()->newLabel(); + generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip); + if (h.type == Throw) + emitForThrowHandling(); + jumpToHandler(parentHandler); + skip.link(); + } else { + if (parentHandler.linkLabel.isReturn()) { + BytecodeGenerator::Label skip = generator()->newLabel(); + generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip); + emitReturnStatement(); + skip.link(); + } else { + generator()->jumpStrictEqualStackSlotInt(temp.stackSlot(), h.value).link(parentHandler.linkLabel); + } + } + } + } + + virtual Handler getHandler(HandlerType type, const QString &label = QString()) { + for (const auto &h : qAsConst(handlers)) { + if (h.type == type && h.label == label) + return h; + } + Handler h = { + type, + label, + unwindLabel, + controlFlowTemp, + handlers.size() + }; + handlers.append(h); + return h; + } + + virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { + return &unwindLabel; + } + + virtual void emitForThrowHandling() { } +}; + +struct ControlFlowWith : public ControlFlowUnwind +{ + ControlFlowWith(Codegen *cg) + : ControlFlowUnwind(cg, With) + { + needsLookupByName = true; + + savedContextRegister = Moth::StackSlot::createRegister(generator()->newRegister()); + + // assumes the with object is in the accumulator + Instruction::PushWithContext pushScope; + pushScope.reg = savedContextRegister; + generator()->addInstruction(pushScope); + generator()->setExceptionHandler(&unwindLabel); + } + + virtual ~ControlFlowWith() { + // emit code for unwinding + unwindLabel.link(); + + generator()->setExceptionHandler(parentExceptionHandler()); + Instruction::PopContext pop; + pop.reg = savedContextRegister; + generator()->addInstruction(pop); + + emitUnwindHandler(); + } + Moth::StackSlot savedContextRegister; +}; + +struct ControlFlowCatch : public ControlFlowUnwind +{ + AST::Catch *catchExpression; + bool insideCatch = false; + BytecodeGenerator::ExceptionHandler exceptionLabel; + BytecodeGenerator::ExceptionHandler catchUnwindLabel; + + ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression) + : ControlFlowUnwind(cg, Catch), catchExpression(catchExpression), + exceptionLabel(generator()->newExceptionHandler()), + catchUnwindLabel(generator()->newExceptionHandler()) + { + generator()->setExceptionHandler(&exceptionLabel); + } + + virtual Handler getHandler(HandlerType type, const QString &label = QString()) { + Handler h = getParentHandler(type, label); + if (h.type == Invalid) + return h; + h = ControlFlowUnwind::getHandler(type, label); + if (insideCatch) + // if we're inside the catch block, we need to jump to the pop scope + // instruction at the end of the catch block, not the unwind handler + h.linkLabel = catchUnwindLabel; + else if (type == Throw) + // if we're inside the try block, we need to jump to the catch block, + // not the unwind handler + h.linkLabel = exceptionLabel; + return h; + } + + virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { + return insideCatch ? &catchUnwindLabel : &exceptionLabel; + } + + ~ControlFlowCatch() { + // emit code for unwinding + + needsLookupByName = true; + insideCatch = true; + + Codegen::RegisterScope scope(cg); + + // exceptions inside the try block go here + exceptionLabel.link(); + Moth::StackSlot savedContextReg = Moth::StackSlot::createRegister(generator()->newRegister()); + Instruction::PushCatchContext pushCatch; + pushCatch.name = cg->registerString(catchExpression->name.toString()); + pushCatch.reg = savedContextReg; + generator()->addInstruction(pushCatch); + // clear the unwind temp for exceptions, we want to resume normal code flow afterwards + Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp); + generator()->setExceptionHandler(&catchUnwindLabel); + + cg->statement(catchExpression->statement); + + insideCatch = false; + needsLookupByName = false; + + // exceptions inside catch and break/return statements go here + catchUnwindLabel.link(); + Instruction::PopContext pop; + pop.reg = savedContextReg; + generator()->addInstruction(pop); + + // break/continue/return statements in try go here + unwindLabel.link(); + generator()->setExceptionHandler(parentExceptionHandler()); + + emitUnwindHandler(); + } +}; + +struct ControlFlowFinally : public ControlFlowUnwind +{ + AST::Finally *finally; + bool insideFinally = false; + int exceptionTemp = -1; + + ControlFlowFinally(Codegen *cg, AST::Finally *finally) + : ControlFlowUnwind(cg, Finally), finally(finally) + { + Q_ASSERT(finally != 0); + generator()->setExceptionHandler(&unwindLabel); + } + + virtual Handler getHandler(HandlerType type, const QString &label = QString()) { + // if we're inside the finally block, any exceptions etc. should + // go directly to the parent handler + if (insideFinally) + return getParentHandler(type, label); + return ControlFlowUnwind::getHandler(type, label); + } + + virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { + return insideFinally ? parentExceptionHandler() : ControlFlowUnwind::exceptionHandler(); + } + + ~ControlFlowFinally() { + // emit code for unwinding + unwindLabel.link(); + + Codegen::RegisterScope scope(cg); + + Moth::StackSlot retVal = Moth::StackSlot::createRegister(generator()->newRegister()); + Instruction::StoreReg storeRetVal; + storeRetVal.reg = retVal; + generator()->addInstruction(storeRetVal); + + insideFinally = true; + exceptionTemp = generator()->newRegister(); + Instruction::GetException instr; + generator()->addInstruction(instr); + Reference::fromStackSlot(cg, exceptionTemp).storeConsumeAccumulator(); + + generator()->setExceptionHandler(parentExceptionHandler()); + cg->statement(finally->statement); + insideFinally = false; + + Instruction::LoadReg loadRetVal; + loadRetVal.reg = retVal; + generator()->addInstruction(loadRetVal); + + emitUnwindHandler(); + } + + virtual void emitForThrowHandling() { + // reset the exception flag, that got cleared before executing the statements in finally + Reference::fromStackSlot(cg, exceptionTemp).loadInAccumulator(); + Instruction::SetException setException; + Q_ASSERT(exceptionTemp != -1); + generator()->addInstruction(setException); + } +}; + +} } // QV4::Compiler namespace + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp new file mode 100644 index 0000000000..6a9b064bd8 --- /dev/null +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -0,0 +1,475 @@ +/**************************************************************************** +** +** 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 "qv4compilerscanfunctions_p.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QStringList> +#include <QtCore/QSet> +#include <QtCore/QBuffer> +#include <QtCore/QBitArray> +#include <QtCore/QLinkedList> +#include <QtCore/QStack> +#include <private/qqmljsast_p.h> +#include <private/qv4compilercontext_p.h> +#include <private/qv4codegen_p.h> +#include <private/qv4string_p.h> + +QT_USE_NAMESPACE +using namespace QV4; +using namespace QV4::Compiler; +using namespace QQmlJS::AST; + +ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) + : _cg(cg) + , _sourceCode(sourceCode) + , _context(0) + , _allowFuncDecls(true) + , defaultProgramMode(defaultProgramMode) +{ +} + +void ScanFunctions::operator()(Node *node) +{ + if (node) + node->accept(this); + + calcEscapingVariables(); +} + +void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) +{ + Context *e = _cg->_module->newContext(node, _context, compilationMode); + if (!e->isStrict) + e->isStrict = _cg->_strictMode; + _contextStack.append(e); + _context = e; +} + +void ScanFunctions::leaveEnvironment() +{ + _contextStack.pop(); + _context = _contextStack.isEmpty() ? 0 : _contextStack.top(); +} + +void ScanFunctions::checkDirectivePrologue(SourceElements *ast) +{ + for (SourceElements *it = ast; it; it = it->next) { + if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) { + if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) { + if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) { + // Use the source code, because the StringLiteral's + // value might have escape sequences in it, which is not + // allowed. + if (strLit->literalToken.length < 2) + continue; + QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); + if (str == QLatin1String("use strict")) { + _context->isStrict = true; + } else { + // TODO: give a warning. + } + continue; + } + } + } + + break; + } +} + +void ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc) +{ + if (_context->isStrict) { + if (name == QLatin1String("implements") + || name == QLatin1String("interface") + || name == QLatin1String("let") + || name == QLatin1String("package") + || name == QLatin1String("private") + || name == QLatin1String("protected") + || name == QLatin1String("public") + || name == QLatin1String("static") + || name == QLatin1String("yield")) { + _cg->throwSyntaxError(loc, QStringLiteral("Unexpected strict mode reserved word")); + } + } +} +void ScanFunctions::checkForArguments(AST::FormalParameterList *parameters) +{ + while (parameters) { + if (parameters->name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + parameters = parameters->next; + } +} + +bool ScanFunctions::visit(Program *ast) +{ + enterEnvironment(ast, defaultProgramMode); + checkDirectivePrologue(ast->elements); + return true; +} + +void ScanFunctions::endVisit(Program *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(CallExpression *ast) +{ + if (! _context->hasDirectEval) { + if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { + if (id->name == QLatin1String("eval")) { + if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown) + _context->usesArgumentsObject = Context::ArgumentsObjectUsed; + _context->hasDirectEval = true; + } + } + } + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); + return true; +} + +bool ScanFunctions::visit(NewMemberExpression *ast) +{ + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); + return true; +} + +bool ScanFunctions::visit(ArrayLiteral *ast) +{ + int index = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) + ++index; + ++index; + } + if (ast->elision) { + for (Elision *elision = ast->elision->next; elision; elision = elision->next) + ++index; + } + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, index); + return true; +} + +bool ScanFunctions::visit(VariableDeclaration *ast) +{ + if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); + checkName(ast->name, ast->identifierToken); + if (ast->name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); + return false; + } + QString name = ast->name.toString(); + const Context::Member *m = 0; + if (_context->memberInfo(name, &m)) { + if (m->isLexicallyScoped() || ast->isLexicallyScoped()) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); + return false; + } + } + _context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope); + return true; +} + +bool ScanFunctions::visit(IdentifierExpression *ast) +{ + checkName(ast->name, ast->identifierToken); + if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectUsed; + _context->addUsedVariable(ast->name.toString()); + return true; +} + +bool ScanFunctions::visit(ExpressionStatement *ast) +{ + if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) { + if (!_allowFuncDecls) + _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration")); + + enterFunction(expr, /*enterName*/ true); + Node::accept(expr->formals, this); + Node::accept(expr->body, this); + leaveEnvironment(); + return false; + } else { + SourceLocation firstToken = ast->firstSourceLocation(); + if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QLatin1String("function")) { + _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token")); + } + } + return true; +} + +bool ScanFunctions::visit(FunctionExpression *ast) +{ + enterFunction(ast, /*enterName*/ false); + return true; +} + +void ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName) +{ + if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); + enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0); +} + +void ScanFunctions::endVisit(FunctionExpression *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(ObjectLiteral *ast) +{ + int argc = 0; + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { + QString key = it->assignment->name->asString(); + if (QV4::String::toArrayIndex(key) != UINT_MAX) + ++argc; + ++argc; + if (AST::cast<AST::PropertyGetterSetter *>(it->assignment)) + ++argc; + } + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + Node::accept(ast->properties, this); + return false; +} + +bool ScanFunctions::visit(PropertyGetterSetter *ast) +{ + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0); + return true; +} + +void ScanFunctions::endVisit(PropertyGetterSetter *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(FunctionDeclaration *ast) +{ + enterFunction(ast, /*enterName*/ true); + return true; +} + +void ScanFunctions::endVisit(FunctionDeclaration *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(TryStatement *) +{ + // ### should limit to catch(), as try{} finally{} should be ok without + _context->hasTry = true; + return true; +} + +bool ScanFunctions::visit(WithStatement *ast) +{ + if (_context->isStrict) { + _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); + return false; + } + + _context->hasWith = true; + return true; +} + +bool ScanFunctions::visit(DoWhileStatement *ast) { + { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + } + Node::accept(ast->expression, this); + return false; +} + +bool ScanFunctions::visit(ForStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + + return false; +} + +bool ScanFunctions::visit(LocalForStatement *ast) { + Node::accept(ast->declarations, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + + return false; +} + +bool ScanFunctions::visit(ForEachStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + + return false; +} + +bool ScanFunctions::visit(LocalForEachStatement *ast) { + Node::accept(ast->declaration, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + + return false; +} + +bool ScanFunctions::visit(ThisExpression *) +{ + _context->usesThis = true; + return false; +} + +bool ScanFunctions::visit(Block *ast) { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls); + Node::accept(ast->statements, this); + return false; +} + +void ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr) +{ + if (_context) { + _context->hasNestedFunctions = true; + // The identifier of a function expression cannot be referenced from the enclosing environment. + if (expr) + _context->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr); + if (name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + } + + enterEnvironment(ast, FunctionCode); + checkForArguments(formals); + + if (!name.isEmpty()) + _context->addLocalVar(name, Context::ThisFunctionName, QQmlJS::AST::VariableDeclaration::FunctionScope); + _context->formals = formals; + + if (body && !_context->isStrict) + checkDirectivePrologue(body->elements); + + for (FormalParameterList *it = formals; it; it = it->next) { + QString arg = it->name.toString(); + int duplicateIndex = _context->arguments.indexOf(arg); + if (duplicateIndex != -1) { + if (_context->isStrict) { + _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); + return; + } else { + // change the name of the earlier argument to enforce the specified lookup semantics + QString modified = arg; + while (_context->arguments.contains(modified)) + modified += QString(0xfffe); + _context->arguments[duplicateIndex] = modified; + } + } + if (_context->isStrict) { + if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) { + _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg)); + return; + } + } + _context->arguments += arg; + } +} + +void ScanFunctions::calcEscapingVariables() +{ + Module *m = _cg->_module; + + for (Context *inner : m->contextMap) { + for (const QString &var : qAsConst(inner->usedVariables)) { + Context *c = inner; + while (c) { + Context::MemberMap::const_iterator it = c->members.find(var); + if (it != c->members.end()) { + if (c != inner) + it->canEscape = true; + break; + } + if (c->findArgument(var) != -1) { + if (c != inner) + c->argumentsCanEscape = true; + break; + } + c = c->parent; + } + } + Context *c = inner->parent; + while (c) { + c->hasDirectEval |= inner->hasDirectEval; + c = c->parent; + } + } + + static const bool showEscapingVars = qEnvironmentVariableIsSet("QV4_SHOW_ESCAPING_VARS"); + if (showEscapingVars) { + qDebug() << "==== escaping variables ===="; + for (Context *c : m->contextMap) { + qDebug() << "Context" << c->name << ":"; + qDebug() << " Arguments escape" << c->argumentsCanEscape; + for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) { + qDebug() << " " << it.key() << it.value().canEscape; + } + } + } +} diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h new file mode 100644 index 0000000000..0b898e587d --- /dev/null +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** 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 QV4COMPILERSCANFUNCTIONS_P_H +#define QV4COMPILERSCANFUNCTIONS_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/qqmljsastvisitor_p.h> +#include <private/qqmljsast_p.h> +#include <private/qqmljsengine_p.h> +#include <private/qv4compilercontext_p.h> +#include <private/qv4util_p.h> +#include <QtCore/QStringList> +#include <QStack> + +QT_BEGIN_NAMESPACE + +using namespace QQmlJS; + +namespace QV4 { + +namespace Moth { +struct Instruction; +} + +namespace CompiledData { +struct CompilationUnit; +} + +namespace Compiler { + +class Codegen; + +class ScanFunctions: protected QQmlJS::AST::Visitor +{ + typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment; +public: + ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode); + void operator()(AST::Node *node); + + void enterEnvironment(AST::Node *node, CompilationMode compilationMode); + void leaveEnvironment(); + + void enterQmlScope(AST::Node *ast, const QString &name) + { enterFunction(ast, name, /*formals*/0, /*body*/0, /*expr*/0); } + + void enterQmlFunction(AST::FunctionDeclaration *ast) + { enterFunction(ast, false); } + +protected: + using Visitor::visit; + using Visitor::endVisit; + + void checkDirectivePrologue(AST::SourceElements *ast); + + void checkName(const QStringRef &name, const AST::SourceLocation &loc); + void checkForArguments(AST::FormalParameterList *parameters); + + bool visit(AST::Program *ast) override; + void endVisit(AST::Program *) override; + + bool visit(AST::CallExpression *ast) override; + bool visit(AST::NewMemberExpression *ast) override; + bool visit(AST::ArrayLiteral *ast) override; + bool visit(AST::VariableDeclaration *ast) override; + bool visit(AST::IdentifierExpression *ast) override; + bool visit(AST::ExpressionStatement *ast) override; + bool visit(AST::FunctionExpression *ast) override; + + void enterFunction(AST::FunctionExpression *ast, bool enterName); + + void endVisit(AST::FunctionExpression *) override; + + bool visit(AST::ObjectLiteral *ast) override; + + bool visit(AST::PropertyGetterSetter *ast) override; + void endVisit(AST::PropertyGetterSetter *) override; + + bool visit(AST::FunctionDeclaration *ast) override; + void endVisit(AST::FunctionDeclaration *) override; + + bool visit(AST::TryStatement *ast) override; + bool visit(AST::WithStatement *ast) override; + + bool visit(AST::DoWhileStatement *ast) override; + bool visit(AST::ForStatement *ast) override; + bool visit(AST::LocalForStatement *ast) override; + bool visit(AST::ForEachStatement *ast) override; + bool visit(AST::LocalForEachStatement *ast) override; + bool visit(AST::ThisExpression *ast) override; + + bool visit(AST::Block *ast) override; + +protected: + void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr); + + void calcEscapingVariables(); +// fields: + Codegen *_cg; + const QString _sourceCode; + Context *_context; + QStack<Context *> _contextStack; + + bool _allowFuncDecls; + CompilationMode defaultProgramMode; +}; + +} + +} + +QT_END_NAMESPACE + +#endif // QV4CODEGEN_P_H diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index cf8cf623bc..9cfde99e6b 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -38,17 +38,603 @@ ****************************************************************************/ #include "qv4instr_moth_p.h" +#include <private/qv4compileddata_p.h> using namespace QV4; using namespace QV4::Moth; -int Instr::size(Type type) +int InstrInfo::size(Instr::Type type) { -#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size; +#define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: return InstrMeta<int(Instr::Type::I)>::Size; switch (type) { FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) - default: return 0; } #undef MOTH_RETURN_INSTR_SIZE + Q_UNREACHABLE(); } +static QByteArray alignedNumber(int n) { + QByteArray number = QByteArray::number(n); + while (number.size() < 8) + number.prepend(' '); + return number; +} + +static QByteArray alignedLineNumber(int line) { + if (line > 0) + return alignedNumber(static_cast<int>(line)); + return QByteArray(" "); +} + +static QByteArray rawBytes(const char *data, int n) +{ + QByteArray ba; + while (n) { + uint num = *reinterpret_cast<const uchar *>(data); + if (num < 16) + ba += '0'; + ba += QByteArray::number(num, 16) + " "; + ++data; + --n; + } + while (ba.size() < 25) + ba += ' '; + return ba; +} + +static QString toString(QV4::ReturnedValue v) +{ +#ifdef V4_BOOTSTRAP + return QStringLiteral("string-const(%1)").arg(v); +#else // !V4_BOOTSTRAP + Value val = Value::fromReturnedValue(v); + QString result; + if (val.isInt32()) + result = QLatin1String("int "); + else if (val.isDouble()) + result = QLatin1String("double "); + if (val.isEmpty()) + result += QLatin1String("empty"); + else + result += val.toQStringNoThrow(); + return result; +#endif // V4_BOOTSTRAP +} + +#define ABSOLUTE_OFFSET() \ + (code - start + offset) + +#define MOTH_BEGIN_INSTR(instr) \ + { \ + INSTR_##instr(MOTH_DECODE_WITH_BASE) \ + QDebug d = qDebug(); \ + d.noquote(); \ + d.nospace(); \ + d << alignedLineNumber(line) << alignedNumber(codeOffset).constData() << ": " \ + << rawBytes(base_ptr, int(code - base_ptr)) << #instr << " "; + +#define MOTH_END_INSTR(instr) \ + continue; \ + } + +QT_BEGIN_NAMESPACE +namespace QV4 { +namespace Moth { + +const int InstrInfo::argumentCount[] = { + FOR_EACH_MOTH_INSTR(MOTH_COLLECT_NARGS) +}; + + +void dumpConstantTable(const Value *constants, uint count) +{ + QDebug d = qDebug(); + d.nospace(); + for (uint i = 0; i < count; ++i) + d << alignedNumber(int(i)).constData() << ": " + << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n"; +} + +QString dumpRegister(int reg, int nFormals) +{ + Q_STATIC_ASSERT(offsetof(CallData, function) == 0); + Q_STATIC_ASSERT(offsetof(CallData, context) == sizeof(Value)); + Q_STATIC_ASSERT(offsetof(CallData, accumulator) == 2*sizeof(Value)); + Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 3*sizeof(Value)); + if (reg == CallData::Function) + return QStringLiteral("(function)"); + else if (reg == CallData::Context) + return QStringLiteral("(context)"); + else if (reg == CallData::Accumulator) + return QStringLiteral("(accumulator)"); + else if (reg == CallData::This) + return QStringLiteral("(this)"); + else if (reg == CallData::Argc) + return QStringLiteral("(argc)"); + reg -= 4; + if (reg <= nFormals) + return QStringLiteral("a%1").arg(reg); + reg -= nFormals; + return QStringLiteral("r%1").arg(reg); + +} + +QString dumpArguments(int argc, int argv, int nFormals) +{ + if (!argc) + return QStringLiteral("()"); + return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")"); +} + + +void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*startLine*/, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping) +{ + MOTH_JUMP_TABLE; + + auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) { + return entry.codeOffset < offset; + }; + + int lastLine = -1; + const char *start = code; + const char *end = code + len; + while (code < end) { + const CompiledData::CodeOffsetToLine *codeToLine = std::lower_bound(lineNumberMapping.constBegin(), lineNumberMapping.constEnd(), static_cast<uint>(code - start) + 1, findLine) - 1; + int line = int(codeToLine->line); + if (line != lastLine) + lastLine = line; + else + line = -1; + + int codeOffset = int(code - start); + + MOTH_DISPATCH() + + MOTH_BEGIN_INSTR(LoadReg) + d << dumpRegister(reg, nFormals); + MOTH_END_INSTR(LoadReg) + + MOTH_BEGIN_INSTR(StoreReg) + d << dumpRegister(reg, nFormals); + MOTH_END_INSTR(StoreReg) + + MOTH_BEGIN_INSTR(MoveReg) + d << dumpRegister(destReg, nFormals) << ", " << dumpRegister(srcReg, nFormals); + MOTH_END_INSTR(MoveReg) + + MOTH_BEGIN_INSTR(LoadConst) + d << "C" << index; + MOTH_END_INSTR(LoadConst) + + MOTH_BEGIN_INSTR(LoadNull) + MOTH_END_INSTR(LoadNull) + + MOTH_BEGIN_INSTR(LoadZero) + MOTH_END_INSTR(LoadZero) + + MOTH_BEGIN_INSTR(LoadTrue) + MOTH_END_INSTR(LoadTrue) + + MOTH_BEGIN_INSTR(LoadFalse) + MOTH_END_INSTR(LoadFalse) + + MOTH_BEGIN_INSTR(LoadUndefined) + MOTH_END_INSTR(LoadUndefined) + + MOTH_BEGIN_INSTR(LoadInt) + d << value; + MOTH_END_INSTR(LoadInt) + + MOTH_BEGIN_INSTR(MoveConst) + d << dumpRegister(destTemp, nFormals) << ", C" << constIndex; + MOTH_END_INSTR(MoveConst) + + MOTH_BEGIN_INSTR(LoadLocal) + if (index < nLocals) + d << "l" << index; + else + d << "a" << (index - nLocals); + MOTH_END_INSTR(LoadLocal) + + MOTH_BEGIN_INSTR(StoreLocal) + if (index < nLocals) + d << "l" << index; + else + d << "a" << (index - nLocals); + MOTH_END_INSTR(StoreLocal) + + MOTH_BEGIN_INSTR(LoadScopedLocal) + if (index < nLocals) + d << "l" << index << "@" << scope; + else + d << "a" << (index - nLocals) << "@" << scope; + MOTH_END_INSTR(LoadScopedLocal) + + MOTH_BEGIN_INSTR(StoreScopedLocal) + if (index < nLocals) + d << ", " << "l" << index << "@" << scope; + else + d << ", " << "a" << (index - nLocals) << "@" << scope; + MOTH_END_INSTR(StoreScopedLocal) + + MOTH_BEGIN_INSTR(LoadRuntimeString) + d << stringId; + MOTH_END_INSTR(LoadRuntimeString) + + MOTH_BEGIN_INSTR(LoadRegExp) + d << regExpId; + MOTH_END_INSTR(LoadRegExp) + + MOTH_BEGIN_INSTR(LoadClosure) + d << value; + MOTH_END_INSTR(LoadClosure) + + MOTH_BEGIN_INSTR(LoadName) + d << name; + MOTH_END_INSTR(LoadName) + + MOTH_BEGIN_INSTR(LoadGlobalLookup) + d << index; + MOTH_END_INSTR(LoadGlobalLookup) + + MOTH_BEGIN_INSTR(StoreNameSloppy) + d << name; + MOTH_END_INSTR(StoreNameSloppy) + + MOTH_BEGIN_INSTR(StoreNameStrict) + d << name; + MOTH_END_INSTR(StoreNameStrict) + + MOTH_BEGIN_INSTR(LoadElement) + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(LoadElementA) + d << dumpRegister(base, nFormals) << "[acc]"; + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(StoreElement) + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; + MOTH_END_INSTR(StoreElement) + + MOTH_BEGIN_INSTR(LoadProperty) + d << dumpRegister(base, nFormals) << "[" << name << "]"; + MOTH_END_INSTR(LoadProperty) + + MOTH_BEGIN_INSTR(LoadPropertyA) + d << "acc[" << name << "]"; + MOTH_END_INSTR(LoadElementA) + + MOTH_BEGIN_INSTR(GetLookup) + d << dumpRegister(base, nFormals) << "(" << index << ")"; + MOTH_END_INSTR(GetLookup) + + MOTH_BEGIN_INSTR(GetLookupA) + d << "acc(" << index << ")"; + MOTH_END_INSTR(GetLookupA) + + MOTH_BEGIN_INSTR(StoreProperty) + d << dumpRegister(base, nFormals) << "[" << name<< "]"; + MOTH_END_INSTR(StoreProperty) + + MOTH_BEGIN_INSTR(SetLookup) + d << dumpRegister(base, nFormals) << "(" << index << ")"; + MOTH_END_INSTR(SetLookup) + + MOTH_BEGIN_INSTR(StoreScopeObjectProperty) + d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]"; + MOTH_END_INSTR(StoreScopeObjectProperty) + + MOTH_BEGIN_INSTR(LoadScopeObjectProperty) + d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)"); + MOTH_END_INSTR(LoadScopeObjectProperty) + + MOTH_BEGIN_INSTR(StoreContextObjectProperty) + d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]"; + MOTH_END_INSTR(StoreContextObjectProperty) + + MOTH_BEGIN_INSTR(LoadContextObjectProperty) + d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)"); + MOTH_END_INSTR(LoadContextObjectProperty) + + MOTH_BEGIN_INSTR(LoadIdObject) + d << dumpRegister(base, nFormals) << "[" << index << "]"; + MOTH_END_INSTR(LoadIdObject) + + MOTH_BEGIN_INSTR(CallValue) + d << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallValue) + + MOTH_BEGIN_INSTR(CallProperty) + d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallProperty) + + MOTH_BEGIN_INSTR(CallPropertyLookup) + d << dumpRegister(base, nFormals) << "." << lookupIndex << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallPropertyLookup) + + MOTH_BEGIN_INSTR(CallElement) + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallElement) + + MOTH_BEGIN_INSTR(CallName) + d << name << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallName) + + MOTH_BEGIN_INSTR(CallPossiblyDirectEval) + d << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallPossiblyDirectEval) + + MOTH_BEGIN_INSTR(CallGlobalLookup) + d << index << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallGlobalLookup) + + MOTH_BEGIN_INSTR(SetExceptionHandler) + if (offset) + d << ABSOLUTE_OFFSET(); + else + d << "<null>"; + MOTH_END_INSTR(SetExceptionHandler) + + MOTH_BEGIN_INSTR(ThrowException) + MOTH_END_INSTR(ThrowException) + + MOTH_BEGIN_INSTR(GetException) + MOTH_END_INSTR(HasException) + + MOTH_BEGIN_INSTR(SetException) + MOTH_END_INSTR(SetExceptionFlag) + + MOTH_BEGIN_INSTR(CreateCallContext) + MOTH_END_INSTR(CreateCallContext) + + MOTH_BEGIN_INSTR(PushCatchContext) + d << dumpRegister(reg, nFormals) << ", " << name; + MOTH_END_INSTR(PushCatchContext) + + MOTH_BEGIN_INSTR(PushWithContext) + d << dumpRegister(reg, nFormals); + MOTH_END_INSTR(PushWithContext) + + MOTH_BEGIN_INSTR(PopContext) + d << dumpRegister(reg, nFormals); + MOTH_END_INSTR(PopContext) + + MOTH_BEGIN_INSTR(ForeachIteratorObject) + MOTH_END_INSTR(ForeachIteratorObject) + + MOTH_BEGIN_INSTR(ForeachNextPropertyName) + MOTH_END_INSTR(ForeachNextPropertyName) + + MOTH_BEGIN_INSTR(DeleteMember) + d << dumpRegister(base, nFormals) << "[" << member << "]"; + MOTH_END_INSTR(DeleteMember) + + MOTH_BEGIN_INSTR(DeleteSubscript) + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; + MOTH_END_INSTR(DeleteSubscript) + + MOTH_BEGIN_INSTR(DeleteName) + d << name; + MOTH_END_INSTR(DeleteName) + + MOTH_BEGIN_INSTR(TypeofName) + d << name; + MOTH_END_INSTR(TypeofName) + + MOTH_BEGIN_INSTR(TypeofValue) + MOTH_END_INSTR(TypeofValue) + + MOTH_BEGIN_INSTR(DeclareVar) + d << isDeletable << ", " << varName; + MOTH_END_INSTR(DeclareVar) + + MOTH_BEGIN_INSTR(DefineArray) + d << dumpRegister(args, nFormals) << ", " << argc; + MOTH_END_INSTR(DefineArray) + + MOTH_BEGIN_INSTR(DefineObjectLiteral) + d << dumpRegister(args, nFormals) + << ", " << internalClassId + << ", " << arrayValueCount + << ", " << arrayGetterSetterCountAndFlags; + MOTH_END_INSTR(DefineObjectLiteral) + + MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) + MOTH_END_INSTR(CreateMappedArgumentsObject) + + MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject) + MOTH_END_INSTR(CreateUnmappedArgumentsObject) + + MOTH_BEGIN_INSTR(ConvertThisToObject) + MOTH_END_INSTR(ConvertThisToObject) + + MOTH_BEGIN_INSTR(Construct) + d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(Construct) + + MOTH_BEGIN_INSTR(Jump) + d << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(Jump) + + MOTH_BEGIN_INSTR(JumpTrue) + d << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(JumpTrue) + + MOTH_BEGIN_INSTR(JumpFalse) + d << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(JumpFalse) + + MOTH_BEGIN_INSTR(CmpEqNull) + MOTH_END_INSTR(CmpEqNull) + + MOTH_BEGIN_INSTR(CmpNeNull) + MOTH_END_INSTR(CmpNeNull) + + MOTH_BEGIN_INSTR(CmpEqInt) + d << lhs; + MOTH_END_INSTR(CmpEq) + + MOTH_BEGIN_INSTR(CmpNeInt) + d << lhs; + MOTH_END_INSTR(CmpNeInt) + + MOTH_BEGIN_INSTR(CmpEq) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpEq) + + MOTH_BEGIN_INSTR(CmpNe) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpNe) + + MOTH_BEGIN_INSTR(CmpGt) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpGt) + + MOTH_BEGIN_INSTR(CmpGe) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpGe) + + MOTH_BEGIN_INSTR(CmpLt) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpLt) + + MOTH_BEGIN_INSTR(CmpLe) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpLe) + + MOTH_BEGIN_INSTR(CmpStrictEqual) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpStrictEqual) + + MOTH_BEGIN_INSTR(CmpStrictNotEqual) + d << dumpRegister(lhs, nFormals); + MOTH_END_INSTR(CmpStrictNotEqual) + + MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt) + d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(JumpStrictEqualStackSlotInt) + + MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt) + d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) + + MOTH_BEGIN_INSTR(UNot) + MOTH_END_INSTR(UNot) + + MOTH_BEGIN_INSTR(UPlus) + MOTH_END_INSTR(UPlus) + + MOTH_BEGIN_INSTR(UMinus) + MOTH_END_INSTR(UMinus) + + MOTH_BEGIN_INSTR(UCompl) + MOTH_END_INSTR(UCompl) + + MOTH_BEGIN_INSTR(Increment) + MOTH_END_INSTR(PreIncrement) + + MOTH_BEGIN_INSTR(Decrement) + MOTH_END_INSTR(PreDecrement) + + MOTH_BEGIN_INSTR(Add) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Add) + + MOTH_BEGIN_INSTR(BitAnd) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(BitAnd) + + MOTH_BEGIN_INSTR(BitOr) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(BitOr) + + MOTH_BEGIN_INSTR(BitXor) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(BitXor) + + MOTH_BEGIN_INSTR(UShr) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(UShr) + + MOTH_BEGIN_INSTR(Shr) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Shr) + + MOTH_BEGIN_INSTR(Shl) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Shl) + + MOTH_BEGIN_INSTR(BitAndConst) + d << "acc, " << rhs; + MOTH_END_INSTR(BitAndConst) + + MOTH_BEGIN_INSTR(BitOrConst) + d << "acc, " << rhs; + MOTH_END_INSTR(BitOr) + + MOTH_BEGIN_INSTR(BitXorConst) + d << "acc, " << rhs; + MOTH_END_INSTR(BitXor) + + MOTH_BEGIN_INSTR(UShrConst) + d << "acc, " << rhs; + MOTH_END_INSTR(UShrConst) + + MOTH_BEGIN_INSTR(ShrConst) + d << "acc, " << rhs; + MOTH_END_INSTR(ShrConst) + + MOTH_BEGIN_INSTR(ShlConst) + d << "acc, " << rhs; + MOTH_END_INSTR(ShlConst) + + MOTH_BEGIN_INSTR(Mul) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Mul) + + MOTH_BEGIN_INSTR(Div) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Div) + + MOTH_BEGIN_INSTR(Mod) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Mod) + + MOTH_BEGIN_INSTR(Sub) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Sub) + + MOTH_BEGIN_INSTR(CmpIn) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(CmpIn) + + MOTH_BEGIN_INSTR(CmpInstanceOf) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(CmpInstanceOf) + + MOTH_BEGIN_INSTR(Ret) + MOTH_END_INSTR(Ret) + +#ifndef QT_NO_QML_DEBUGGER + MOTH_BEGIN_INSTR(Debug) + MOTH_END_INSTR(Debug) +#endif // QT_NO_QML_DEBUGGER + + MOTH_BEGIN_INSTR(LoadQmlContext) + d << dumpRegister(result, nFormals); + MOTH_END_INSTR(LoadQmlContext) + + MOTH_BEGIN_INSTR(LoadQmlImportedScripts) + d << dumpRegister(result, nFormals); + MOTH_END_INSTR(LoadQmlImportedScripts) + + MOTH_BEGIN_INSTR(LoadQmlSingleton) + d << name; + MOTH_END_INSTR(LoadQmlSingleton) + } +} + +} +} +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 1bdb8df414..e0b013eb95 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -64,851 +64,451 @@ QT_BEGIN_NAMESPACE #define MOTH_DEBUG_INSTR(F) #else #define MOTH_DEBUG_INSTR(F) \ - F(Line, line) \ - F(Debug, debug) + F(Debug) #endif +#define INSTRUCTION(op, name, nargs, ...) \ + op##_INSTRUCTION(name, nargs, __VA_ARGS__) + +/* for all jump instructions, the offset has to come last, to simplify the job of the bytecode generator */ +#define INSTR_Ret(op) INSTRUCTION(op, Ret, 0) +#define INSTR_Debug(op) INSTRUCTION(op, Debug, 0) +#define INSTR_LoadConst(op) INSTRUCTION(op, LoadConst, 1, index) +#define INSTR_LoadZero(op) INSTRUCTION(op, LoadZero, 0) +#define INSTR_LoadTrue(op) INSTRUCTION(op, LoadTrue, 0) +#define INSTR_LoadFalse(op) INSTRUCTION(op, LoadFalse, 0) +#define INSTR_LoadNull(op) INSTRUCTION(op, LoadNull, 0) +#define INSTR_LoadUndefined(op) INSTRUCTION(op, LoadUndefined, 0) +#define INSTR_LoadInt(op) INSTRUCTION(op, LoadInt, 1, value) +#define INSTR_MoveConst(op) INSTRUCTION(op, MoveConst, 2, constIndex, destTemp) +#define INSTR_LoadReg(op) INSTRUCTION(op, LoadReg, 1, reg) +#define INSTR_StoreReg(op) INSTRUCTION(op, StoreReg, 1, reg) +#define INSTR_MoveReg(op) INSTRUCTION(op, MoveReg, 2, srcReg, destReg) +#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 1, index) +#define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index) +#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 2, scope, index) +#define INSTR_StoreScopedLocal(op) INSTRUCTION(op, StoreScopedLocal, 2, scope, index) +#define INSTR_LoadRuntimeString(op) INSTRUCTION(op, LoadRuntimeString, 1, stringId) +#define INSTR_LoadRegExp(op) INSTRUCTION(op, LoadRegExp, 1, regExpId) +#define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value) +#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 1, name) +#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index) +#define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name) +#define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name) +#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, base) +#define INSTR_LoadPropertyA(op) INSTRUCTION(op, LoadPropertyA, 1, name) +#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, base) +#define INSTR_GetLookupA(op) INSTRUCTION(op, GetLookupA, 1, index) +#define INSTR_LoadScopeObjectProperty(op) INSTRUCTION(op, LoadScopeObjectProperty, 3, propertyIndex, base, captureRequired) +#define INSTR_LoadContextObjectProperty(op) INSTRUCTION(op, LoadContextObjectProperty, 3, propertyIndex, base, captureRequired) +#define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base) +#define INSTR_StoreProperty(op) INSTRUCTION(op, StoreProperty, 2, name, base) +#define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base) +#define INSTR_StoreScopeObjectProperty(op) INSTRUCTION(op, StoreScopeObjectProperty, 2, base, propertyIndex) +#define INSTR_StoreContextObjectProperty(op) INSTRUCTION(op, StoreContextObjectProperty, 2, base, propertyIndex) +#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, index) +#define INSTR_LoadElementA(op) INSTRUCTION(op, LoadElementA, 1, base) +#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index) +#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 2, argc, argv) +#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv) +#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 4, lookupIndex, base, argc, argv) +#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 4, base, index, argc, argv) +#define INSTR_CallName(op) INSTRUCTION(op, CallName, 3, name, argc, argv) +#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv) +#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv) +#define INSTR_SetExceptionHandler(op) INSTRUCTION(op, SetExceptionHandler, 1, offset) +#define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0) +#define INSTR_GetException(op) INSTRUCTION(op, GetException, 0) +#define INSTR_SetException(op) INSTRUCTION(op, SetException, 0) +#define INSTR_CreateCallContext(op) INSTRUCTION(op, CreateCallContext, 0) +#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, name, reg) +#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 1, reg) +#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 1, reg) +#define INSTR_ForeachIteratorObject(op) INSTRUCTION(op, ForeachIteratorObject, 0) +#define INSTR_ForeachNextPropertyName(op) INSTRUCTION(op, ForeachNextPropertyName, 0) +#define INSTR_DeleteMember(op) INSTRUCTION(op, DeleteMember, 2, member, base) +#define INSTR_DeleteSubscript(op) INSTRUCTION(op, DeleteSubscript, 2, base, index) +#define INSTR_DeleteName(op) INSTRUCTION(op, DeleteName, 1, name) +#define INSTR_TypeofName(op) INSTRUCTION(op, TypeofName, 1, name) +#define INSTR_TypeofValue(op) INSTRUCTION(op, TypeofValue, 0) +#define INSTR_DeclareVar(op) INSTRUCTION(op, DeclareVar, 2, varName, isDeletable) +#define INSTR_DefineArray(op) INSTRUCTION(op, DefineArray, 2, argc, args) +// arrayGetterSetterCountAndFlags contains 30 bits for count, 1 bit for needsSparseArray boolean +#define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 4, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags, args) +#define INSTR_CreateMappedArgumentsObject(op) INSTRUCTION(op, CreateMappedArgumentsObject, 0) +#define INSTR_CreateUnmappedArgumentsObject(op) INSTRUCTION(op, CreateUnmappedArgumentsObject, 0) +#define INSTR_ConvertThisToObject(op) INSTRUCTION(op, ConvertThisToObject, 0) +#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv) +#define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset) +#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset) +#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset) +#define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0) +#define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0) +#define INSTR_CmpEqInt(op) INSTRUCTION(op, CmpEqInt, 1, lhs) +#define INSTR_CmpNeInt(op) INSTRUCTION(op, CmpNeInt, 1, lhs) +#define INSTR_CmpEq(op) INSTRUCTION(op, CmpEq, 1, lhs) +#define INSTR_CmpNe(op) INSTRUCTION(op, CmpNe, 1, lhs) +#define INSTR_CmpGt(op) INSTRUCTION(op, CmpGt, 1, lhs) +#define INSTR_CmpGe(op) INSTRUCTION(op, CmpGe, 1, lhs) +#define INSTR_CmpLt(op) INSTRUCTION(op, CmpLt, 1, lhs) +#define INSTR_CmpLe(op) INSTRUCTION(op, CmpLe, 1, lhs) +#define INSTR_CmpStrictEqual(op) INSTRUCTION(op, CmpStrictEqual, 1, lhs) +#define INSTR_CmpStrictNotEqual(op) INSTRUCTION(op, CmpStrictNotEqual, 1, lhs) +#define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs) +#define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs) +#define INSTR_JumpStrictEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictEqualStackSlotInt, 3, lhs, rhs, offset) +#define INSTR_JumpStrictNotEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictNotEqualStackSlotInt, 3, lhs, rhs, offset) +#define INSTR_UNot(op) INSTRUCTION(op, UNot, 0) +#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0) +#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0) +#define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0) +#define INSTR_Increment(op) INSTRUCTION(op, Increment, 0) +#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 0) +#define INSTR_Add(op) INSTRUCTION(op, Add, 1, lhs) +#define INSTR_BitAnd(op) INSTRUCTION(op, BitAnd, 1, lhs) +#define INSTR_BitOr(op) INSTRUCTION(op, BitOr, 1, lhs) +#define INSTR_BitXor(op) INSTRUCTION(op, BitXor, 1, lhs) +#define INSTR_UShr(op) INSTRUCTION(op, UShr, 1, lhs) +#define INSTR_Shr(op) INSTRUCTION(op, Shr, 1, lhs) +#define INSTR_Shl(op) INSTRUCTION(op, Shl, 1, lhs) +#define INSTR_BitAndConst(op) INSTRUCTION(op, BitAndConst, 1, rhs) +#define INSTR_BitOrConst(op) INSTRUCTION(op, BitOrConst, 1, rhs) +#define INSTR_BitXorConst(op) INSTRUCTION(op, BitXorConst, 1, rhs) +#define INSTR_UShrConst(op) INSTRUCTION(op, UShrConst, 1, rhs) +#define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs) +#define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs) +#define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs) +#define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs) +#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs) +#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs) +#define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result) +#define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) +#define INSTR_LoadQmlSingleton(op) INSTRUCTION(op, LoadQmlSingleton, 1, name) + + #define FOR_EACH_MOTH_INSTR(F) \ - F(Ret, ret) \ - MOTH_DEBUG_INSTR(F) \ - F(LoadRuntimeString, loadRuntimeString) \ - F(LoadRegExp, loadRegExp) \ - F(LoadClosure, loadClosure) \ - F(Move, move) \ - F(MoveConst, moveConst) \ - F(SwapTemps, swapTemps) \ - F(LoadName, loadName) \ - F(GetGlobalLookup, getGlobalLookup) \ - F(StoreName, storeName) \ - F(LoadElement, loadElement) \ - F(LoadElementLookup, loadElementLookup) \ - F(StoreElement, storeElement) \ - F(StoreElementLookup, storeElementLookup) \ - F(LoadProperty, loadProperty) \ - F(GetLookup, getLookup) \ - F(StoreProperty, storeProperty) \ - F(SetLookup, setLookup) \ - F(StoreQObjectProperty, storeQObjectProperty) \ - F(LoadQObjectProperty, loadQObjectProperty) \ - F(StoreScopeObjectProperty, storeScopeObjectProperty) \ - F(StoreContextObjectProperty, storeContextObjectProperty) \ - F(LoadScopeObjectProperty, loadScopeObjectProperty) \ - F(LoadContextObjectProperty, loadContextObjectProperty) \ - F(LoadIdObject, loadIdObject) \ - F(LoadAttachedQObjectProperty, loadAttachedQObjectProperty) \ - F(LoadSingletonQObjectProperty, loadQObjectProperty) \ - F(Push, push) \ - F(CallValue, callValue) \ - F(CallProperty, callProperty) \ - F(CallPropertyLookup, callPropertyLookup) \ - F(CallScopeObjectProperty, callScopeObjectProperty) \ - F(CallContextObjectProperty, callContextObjectProperty) \ - F(CallElement, callElement) \ - F(CallActivationProperty, callActivationProperty) \ - F(CallGlobalLookup, callGlobalLookup) \ - F(SetExceptionHandler, setExceptionHandler) \ - F(CallBuiltinThrow, callBuiltinThrow) \ - F(CallBuiltinUnwindException, callBuiltinUnwindException) \ - F(CallBuiltinPushCatchScope, callBuiltinPushCatchScope) \ - F(CallBuiltinPushScope, callBuiltinPushScope) \ - F(CallBuiltinPopScope, callBuiltinPopScope) \ - F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ - F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ - F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ - F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ - F(CallBuiltinDeleteName, callBuiltinDeleteName) \ - F(CallBuiltinTypeofScopeObjectProperty, callBuiltinTypeofScopeObjectProperty) \ - F(CallBuiltinTypeofContextObjectProperty, callBuiltinTypeofContextObjectProperty) \ - F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ - F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ - F(CallBuiltinTypeofName, callBuiltinTypeofName) \ - F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ - F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ - F(CallBuiltinDefineArray, callBuiltinDefineArray) \ - F(CallBuiltinDefineObjectLiteral, callBuiltinDefineObjectLiteral) \ - F(CallBuiltinSetupArgumentsObject, callBuiltinSetupArgumentsObject) \ - F(CallBuiltinConvertThisToObject, callBuiltinConvertThisToObject) \ - F(CreateValue, createValue) \ - F(CreateProperty, createProperty) \ - F(ConstructPropertyLookup, constructPropertyLookup) \ - F(CreateActivationProperty, createActivationProperty) \ - F(ConstructGlobalLookup, constructGlobalLookup) \ - F(Jump, jump) \ - F(JumpEq, jumpEq) \ - F(JumpNe, jumpNe) \ - F(UNot, unot) \ - F(UNotBool, unotBool) \ - F(UPlus, uplus) \ - F(UMinus, uminus) \ - F(UCompl, ucompl) \ - F(UComplInt, ucomplInt) \ - F(Increment, increment) \ - F(Decrement, decrement) \ - F(Binop, binop) \ - F(Add, add) \ - F(BitAnd, bitAnd) \ - F(BitOr, bitOr) \ - F(BitXor, bitXor) \ - F(Shr, shr) \ - F(Shl, shl) \ - F(BitAndConst, bitAndConst) \ - F(BitOrConst, bitOrConst) \ - F(BitXorConst, bitXorConst) \ - F(ShrConst, shrConst) \ - F(ShlConst, shlConst) \ - F(Mul, mul) \ - F(Sub, sub) \ - F(BinopContext, binopContext) \ - F(LoadThis, loadThis) \ - F(LoadQmlContext, loadQmlContext) \ - F(LoadQmlImportedScripts, loadQmlImportedScripts) \ - F(LoadQmlSingleton, loadQmlSingleton) + F(Ret) \ + F(Debug) \ + F(LoadConst) \ + F(LoadZero) \ + F(LoadTrue) \ + F(LoadFalse) \ + F(LoadNull) \ + F(LoadUndefined) \ + F(LoadInt) \ + F(MoveConst) \ + F(LoadReg) \ + F(StoreReg) \ + F(MoveReg) \ + F(LoadLocal) \ + F(StoreLocal) \ + F(LoadScopedLocal) \ + F(StoreScopedLocal) \ + F(LoadRuntimeString) \ + F(LoadRegExp) \ + F(LoadClosure) \ + F(LoadName) \ + F(LoadGlobalLookup) \ + F(StoreNameSloppy) \ + F(StoreNameStrict) \ + F(LoadElement) \ + F(LoadElementA) \ + F(StoreElement) \ + F(LoadProperty) \ + F(LoadPropertyA) \ + F(GetLookup) \ + F(GetLookupA) \ + F(StoreProperty) \ + F(SetLookup) \ + F(StoreScopeObjectProperty) \ + F(StoreContextObjectProperty) \ + F(LoadScopeObjectProperty) \ + F(LoadContextObjectProperty) \ + F(LoadIdObject) \ + F(CallValue) \ + F(CallProperty) \ + F(CallPropertyLookup) \ + F(CallElement) \ + F(CallName) \ + F(CallPossiblyDirectEval) \ + F(CallGlobalLookup) \ + F(SetExceptionHandler) \ + F(ThrowException) \ + F(GetException) \ + F(SetException) \ + F(CreateCallContext) \ + F(PushCatchContext) \ + F(PushWithContext) \ + F(PopContext) \ + F(ForeachIteratorObject) \ + F(ForeachNextPropertyName) \ + F(DeleteMember) \ + F(DeleteSubscript) \ + F(DeleteName) \ + F(TypeofName) \ + F(TypeofValue) \ + F(DeclareVar) \ + F(DefineArray) \ + F(DefineObjectLiteral) \ + F(CreateMappedArgumentsObject) \ + F(CreateUnmappedArgumentsObject) \ + F(ConvertThisToObject) \ + F(Construct) \ + F(Jump) \ + F(JumpTrue) \ + F(JumpFalse) \ + F(CmpEqNull) \ + F(CmpNeNull) \ + F(CmpEqInt) \ + F(CmpNeInt) \ + F(CmpEq) \ + F(CmpNe) \ + F(CmpGt) \ + F(CmpGe) \ + F(CmpLt) \ + F(CmpLe) \ + F(CmpStrictEqual) \ + F(CmpStrictNotEqual) \ + F(CmpIn) \ + F(CmpInstanceOf) \ + F(JumpStrictEqualStackSlotInt) \ + F(JumpStrictNotEqualStackSlotInt) \ + F(UNot) \ + F(UPlus) \ + F(UMinus) \ + F(UCompl) \ + F(Increment) \ + F(Decrement) \ + F(Add) \ + F(BitAnd) \ + F(BitOr) \ + F(BitXor) \ + F(UShr) \ + F(Shr) \ + F(Shl) \ + F(BitAndConst) \ + F(BitOrConst) \ + F(BitXorConst) \ + F(UShrConst) \ + F(ShrConst) \ + F(ShlConst) \ + F(Mul) \ + F(Div) \ + F(Mod) \ + F(Sub) \ + F(LoadQmlContext) \ + F(LoadQmlImportedScripts) \ + F(LoadQmlSingleton) +#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::LoadQmlSingleton) + 1) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) -# define MOTH_THREADED_INTERPRETER +# define MOTH_COMPUTED_GOTO #endif #define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1) -#define MOTH_INSTR_HEADER quint32 instructionType; +#define MOTH_INSTR_ENUM(I) I, +#define MOTH_INSTR_SIZE(I) (sizeof(QV4::Moth::Instr::instr_##I)) + +#define MOTH_EXPAND_FOR_MSVC(x) x +#define MOTH_DEFINE_ARGS(nargs, ...) \ + MOTH_EXPAND_FOR_MSVC(MOTH_DEFINE_ARGS##nargs(__VA_ARGS__)) + +#define MOTH_DEFINE_ARGS0() +#define MOTH_DEFINE_ARGS1(arg) \ + int arg; +#define MOTH_DEFINE_ARGS2(arg1, arg2) \ + int arg1; \ + int arg2; +#define MOTH_DEFINE_ARGS3(arg1, arg2, arg3) \ + int arg1; \ + int arg2; \ + int arg3; +#define MOTH_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \ + int arg1; \ + int arg2; \ + int arg3; \ + int arg4; + +#define MOTH_COLLECT_ENUMS(instr) \ + INSTR_##instr(MOTH_GET_ENUM) +#define MOTH_GET_ENUM_INSTRUCTION(name, ...) \ + name, + +#define MOTH_EMIT_STRUCTS(instr) \ + INSTR_##instr(MOTH_EMIT_STRUCT) +#define MOTH_EMIT_STRUCT_INSTRUCTION(name, nargs, ...) \ + struct instr_##name { \ + MOTH_DEFINE_ARGS(nargs, __VA_ARGS__) \ + }; + +#define MOTH_EMIT_INSTR_MEMBERS(instr) \ + INSTR_##instr(MOTH_EMIT_INSTR_MEMBER) +#define MOTH_EMIT_INSTR_MEMBER_INSTRUCTION(name, nargs, ...) \ + instr_##name name; + +#define MOTH_COLLECT_NARGS(instr) \ + INSTR_##instr(MOTH_COLLECT_ARG_COUNT) +#define MOTH_COLLECT_ARG_COUNT_INSTRUCTION(name, nargs, ...) \ + nargs, + +#define MOTH_DECODE_ARG(arg, type, nargs, offset) \ + arg = reinterpret_cast<const type *>(code)[-nargs + offset]; +#define MOTH_ADJUST_CODE(type, nargs) \ + code += static_cast<quintptr>(nargs*sizeof(type) + 1) + +#define MOTH_DECODE_INSTRUCTION(name, nargs, ...) \ + MOTH_DEFINE_ARGS(nargs, __VA_ARGS__) \ + op_int_##name: \ + MOTH_ADJUST_CODE(int, nargs); \ + MOTH_DECODE_ARGS(name, int, nargs, __VA_ARGS__) \ + goto op_main_##name; \ + op_char_##name: \ + MOTH_ADJUST_CODE(char, nargs); \ + MOTH_DECODE_ARGS(name, char, nargs, __VA_ARGS__) \ + op_main_##name: \ + ; \ + +#define MOTH_DECODE_WITH_BASE_INSTRUCTION(name, nargs, ...) \ + MOTH_DEFINE_ARGS(nargs, __VA_ARGS__) \ + const char *base_ptr; \ + op_int_##name: \ + base_ptr = code; \ + MOTH_ADJUST_CODE(int, nargs); \ + MOTH_DECODE_ARGS(name, int, nargs, __VA_ARGS__) \ + goto op_main_##name; \ + op_char_##name: \ + base_ptr = code; \ + MOTH_ADJUST_CODE(char, nargs); \ + MOTH_DECODE_ARGS(name, char, nargs, __VA_ARGS__) \ + op_main_##name: \ + ; \ + +#define MOTH_DECODE_ARGS(name, type, nargs, ...) \ + MOTH_EXPAND_FOR_MSVC(MOTH_DECODE_ARGS##nargs(name, type, nargs, __VA_ARGS__)) + +#define MOTH_DECODE_ARGS0(name, type, nargs, dummy) +#define MOTH_DECODE_ARGS1(name, type, nargs, arg) \ + MOTH_DECODE_ARG(arg, type, nargs, 0); +#define MOTH_DECODE_ARGS2(name, type, nargs, arg1, arg2) \ + MOTH_DECODE_ARGS1(name, type, nargs, arg1); \ + MOTH_DECODE_ARG(arg2, type, nargs, 1); +#define MOTH_DECODE_ARGS3(name, type, nargs, arg1, arg2, arg3) \ + MOTH_DECODE_ARGS2(name, type, nargs, arg1, arg2); \ + MOTH_DECODE_ARG(arg3, type, nargs, 2); +#define MOTH_DECODE_ARGS4(name, type, nargs, arg1, arg2, arg3, arg4) \ + MOTH_DECODE_ARGS3(name, type, nargs, arg1, arg2, arg3); \ + MOTH_DECODE_ARG(arg4, type, nargs, 3); + +#ifdef MOTH_COMPUTED_GOTO +/* collect jump labels */ +#define COLLECT_LABELS(instr) \ + INSTR_##instr(GET_LABEL) +#define GET_LABEL_INSTRUCTION(name, ...) \ + &&op_char_##name, +#define COLLECT_LABELS_WIDE(instr) \ + INSTR_##instr(GET_LABEL_WIDE) +#define GET_LABEL_WIDE_INSTRUCTION(name, ...) \ + &&op_int_##name, + +#define MOTH_JUMP_TABLE \ + static const void *jumpTable[] = { \ + FOR_EACH_MOTH_INSTR(COLLECT_LABELS) \ + FOR_EACH_MOTH_INSTR(COLLECT_LABELS_WIDE) \ + }; + +#define MOTH_DISPATCH() \ + goto *jumpTable[*reinterpret_cast<const uchar *>(code)]; +#else +#define MOTH_JUMP_TABLE + +#define MOTH_INSTR_CASE_AND_JUMP(instr) \ + INSTR_##instr(GET_CASE_AND_JUMP) +#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \ + case static_cast<uchar>(Instr::Type::name): goto op_char_##name; +#define MOTH_INSTR_CASE_AND_JUMP_WIDE(instr) \ + INSTR_##instr(GET_CASE_AND_JUMP_WIDE) +#define GET_CASE_AND_JUMP_WIDE_INSTRUCTION(name, ...) \ + case (static_cast<uchar>(Instr::Type::name) + MOTH_NUM_INSTRUCTIONS()): goto op_int_##name; + +#define MOTH_DISPATCH() \ + switch (static_cast<uchar>(*code)) { \ + FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP) \ + FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP_WIDE) \ + } +#endif -#define MOTH_INSTR_ENUM(I, FMT) I, -#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QV4::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) +namespace QV4 { +namespace CompiledData { +struct CodeOffsetToLine; +} -namespace QV4 { namespace Moth { - // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h - -struct Param { - // Params are looked up as follows: - // Constant: 0 - // Temp: 1 - // Argument: 2 - // Local: 3 - // Arg(outer): 4 - // Local(outer): 5 - // ... - unsigned scope : 12; - unsigned index : 20; - - bool isConstant() const { return !scope; } - bool isArgument() const { return scope >= 2 && !(scope &1); } - bool isLocal() const { return scope == 3; } - bool isTemp() const { return scope == 1; } - bool isScopedLocal() const { return scope >= 3 && (scope & 1); } - - static Param createConstant(int index) - { - Param p; - p.scope = 0; - p.index = index; - return p; - } +class StackSlot { + int index; - static Param createArgument(unsigned idx, uint scope) - { - Param p; - p.scope = 2 + 2*scope; - p.index = idx; - return p; +public: + static StackSlot createRegister(int index) { + Q_ASSERT(index >= 0); + StackSlot t; + t.index = index; + return t; } - static Param createLocal(unsigned idx) - { - Param p; - p.scope = 3; - p.index = idx; - return p; - } + int stackSlot() const { return index; } + operator int() const { return index; } +}; - static Param createTemp(unsigned idx) - { - Param p; - p.scope = 1; - p.index = idx; - return p; - } +inline bool operator==(const StackSlot &l, const StackSlot &r) { return l.stackSlot() == r.stackSlot(); } +inline bool operator!=(const StackSlot &l, const StackSlot &r) { return l.stackSlot() != r.stackSlot(); } - static Param createScopedLocal(unsigned idx, uint scope) - { - Param p; - p.scope = 3 + 2*scope; - p.index = idx; - return p; - } +// When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h - inline bool operator==(const Param &other) const - { return scope == other.scope && index == other.index; } - - inline bool operator!=(const Param &other) const - { return !(*this == other); } -}; +void dumpConstantTable(const Value *constants, uint count); +void dumpBytecode(const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1, + const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping = QVector<CompiledData::CodeOffsetToLine>()); +inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1, + const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping = QVector<CompiledData::CodeOffsetToLine>()) { + dumpBytecode(bytecode.constData(), bytecode.length(), nLocals, nFormals, startLine, lineNumberMapping); +} union Instr { - enum Type { + enum class Type { FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) - LastInstruction }; - struct instr_common { - MOTH_INSTR_HEADER - }; - struct instr_ret { - MOTH_INSTR_HEADER - Param result; - }; - -#ifndef QT_NO_QML_DEBUGGING - struct instr_line { - MOTH_INSTR_HEADER - qint32 lineNumber; - }; - struct instr_debug { - MOTH_INSTR_HEADER - qint32 lineNumber; - }; -#endif // QT_NO_QML_DEBUGGING + FOR_EACH_MOTH_INSTR(MOTH_EMIT_STRUCTS) - struct instr_loadRuntimeString { - MOTH_INSTR_HEADER - int stringId; - Param result; - }; - struct instr_loadRegExp { - MOTH_INSTR_HEADER - int regExpId; - Param result; - }; - struct instr_move { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_moveConst { - MOTH_INSTR_HEADER - QV4::ReturnedValue source; - Param result; - }; - struct instr_swapTemps { - MOTH_INSTR_HEADER - Param left; - Param right; - }; - struct instr_loadClosure { - MOTH_INSTR_HEADER - int value; - Param result; - }; - struct instr_loadName { - MOTH_INSTR_HEADER - int name; - Param result; - }; - struct instr_getGlobalLookup { - MOTH_INSTR_HEADER - int index; - Param result; - }; - struct instr_storeName { - MOTH_INSTR_HEADER - int name; - Param source; - }; - struct instr_loadProperty { - MOTH_INSTR_HEADER - int name; - Param base; - Param result; - }; - struct instr_getLookup { - MOTH_INSTR_HEADER - int index; - Param base; - Param result; - }; - struct instr_loadScopeObjectProperty { - MOTH_INSTR_HEADER - int propertyIndex; - Param base; - Param result; - bool captureRequired; - }; - struct instr_loadContextObjectProperty { - MOTH_INSTR_HEADER - int propertyIndex; - Param base; - Param result; - bool captureRequired; - }; - struct instr_loadIdObject { - MOTH_INSTR_HEADER - int index; - Param base; - Param result; - }; - struct instr_loadQObjectProperty { - MOTH_INSTR_HEADER - int propertyIndex; - Param base; - Param result; - bool captureRequired; - }; - struct instr_loadAttachedQObjectProperty { - MOTH_INSTR_HEADER - int propertyIndex; - Param result; - int attachedPropertiesId; - }; - struct instr_storeProperty { - MOTH_INSTR_HEADER - int name; - Param base; - Param source; - }; - struct instr_setLookup { - MOTH_INSTR_HEADER - int index; - Param base; - Param source; - }; - struct instr_storeScopeObjectProperty { - MOTH_INSTR_HEADER - Param base; - int propertyIndex; - Param source; - }; - struct instr_storeContextObjectProperty { - MOTH_INSTR_HEADER - Param base; - int propertyIndex; - Param source; - }; - struct instr_storeQObjectProperty { - MOTH_INSTR_HEADER - Param base; - int propertyIndex; - Param source; - }; - struct instr_loadElement { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_loadElementLookup { - MOTH_INSTR_HEADER - uint lookup; - Param base; - Param index; - Param result; - }; - struct instr_storeElement { - MOTH_INSTR_HEADER - Param base; - Param index; - Param source; - }; - struct instr_storeElementLookup { - MOTH_INSTR_HEADER - uint lookup; - Param base; - Param index; - Param source; - }; - struct instr_push { - MOTH_INSTR_HEADER - quint32 value; - }; - struct instr_callValue { - MOTH_INSTR_HEADER - quint32 argc; - quint32 callData; - Param dest; - Param result; - }; - struct instr_callProperty { - MOTH_INSTR_HEADER - int name; - quint32 argc; - quint32 callData; - Param base; - Param result; - }; - struct instr_callPropertyLookup { - MOTH_INSTR_HEADER - int lookupIndex; - quint32 argc; - quint32 callData; - Param base; - Param result; - }; - struct instr_callScopeObjectProperty { - MOTH_INSTR_HEADER - int index; - quint32 argc; - quint32 callData; - Param base; - Param result; - }; - struct instr_callContextObjectProperty { - MOTH_INSTR_HEADER - int index; - quint32 argc; - quint32 callData; - Param base; - Param result; - }; - struct instr_callElement { - MOTH_INSTR_HEADER - Param base; - Param index; - quint32 argc; - quint32 callData; - Param result; - }; - struct instr_callActivationProperty { - MOTH_INSTR_HEADER - int name; - quint32 argc; - quint32 callData; - Param result; - }; - struct instr_callGlobalLookup { - MOTH_INSTR_HEADER - int index; - quint32 argc; - quint32 callData; - Param result; - }; - struct instr_setExceptionHandler { - MOTH_INSTR_HEADER - qptrdiff offset; - }; - struct instr_callBuiltinThrow { - MOTH_INSTR_HEADER - Param arg; - }; - struct instr_callBuiltinUnwindException { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_callBuiltinPushCatchScope { - MOTH_INSTR_HEADER - int name; - }; - struct instr_callBuiltinPushScope { - MOTH_INSTR_HEADER - Param arg; - }; - struct instr_callBuiltinPopScope { - MOTH_INSTR_HEADER - }; - struct instr_callBuiltinForeachIteratorObject { - MOTH_INSTR_HEADER - Param arg; - Param result; - }; - struct instr_callBuiltinForeachNextPropertyName { - MOTH_INSTR_HEADER - Param arg; - Param result; - }; - struct instr_callBuiltinDeleteMember { - MOTH_INSTR_HEADER - int member; - Param base; - Param result; - }; - struct instr_callBuiltinDeleteSubscript { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_callBuiltinDeleteName { - MOTH_INSTR_HEADER - int name; - Param result; - }; - struct instr_callBuiltinTypeofScopeObjectProperty { - MOTH_INSTR_HEADER - int index; - Param base; - Param result; - }; - struct instr_callBuiltinTypeofContextObjectProperty { - MOTH_INSTR_HEADER - int index; - Param base; - Param result; - }; - struct instr_callBuiltinTypeofMember { - MOTH_INSTR_HEADER - int member; - Param base; - Param result; - }; - struct instr_callBuiltinTypeofSubscript { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_callBuiltinTypeofName { - MOTH_INSTR_HEADER - int name; - Param result; - }; - struct instr_callBuiltinTypeofValue { - MOTH_INSTR_HEADER - Param value; - Param result; - }; - struct instr_callBuiltinDeclareVar { - MOTH_INSTR_HEADER - int varName; - bool isDeletable; - }; - struct instr_callBuiltinDefineArray { - MOTH_INSTR_HEADER - quint32 argc; - quint32 args; - Param result; - }; - struct instr_callBuiltinDefineObjectLiteral { - MOTH_INSTR_HEADER - int internalClassId; - uint arrayValueCount; - uint arrayGetterSetterCountAndFlags; // 30 bits for count, 1 bit for needsSparseArray boolean - quint32 args; - Param result; - }; - struct instr_callBuiltinSetupArgumentsObject { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_callBuiltinConvertThisToObject { - MOTH_INSTR_HEADER - }; - struct instr_createValue { - MOTH_INSTR_HEADER - quint32 argc; - quint32 callData; - Param func; - Param result; - }; - struct instr_createProperty { - MOTH_INSTR_HEADER - int name; - quint32 argc; - quint32 callData; - Param base; - Param result; - }; - struct instr_constructPropertyLookup { - MOTH_INSTR_HEADER - int index; - quint32 argc; - quint32 callData; - Param base; - Param result; - }; - struct instr_createActivationProperty { - MOTH_INSTR_HEADER - int name; - quint32 argc; - quint32 callData; - Param result; - }; - struct instr_constructGlobalLookup { - MOTH_INSTR_HEADER - int index; - quint32 argc; - quint32 callData; - Param result; - }; - struct instr_jump { - MOTH_INSTR_HEADER - ptrdiff_t offset; - }; - struct instr_jumpEq { - MOTH_INSTR_HEADER - ptrdiff_t offset; - Param condition; - }; - struct instr_jumpNe { - MOTH_INSTR_HEADER - ptrdiff_t offset; - Param condition; - }; - struct instr_unot { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_unotBool { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_uplus { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_uminus { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_ucompl { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_ucomplInt { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_increment { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_decrement { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_binop { - MOTH_INSTR_HEADER - int alu; // QV4::Runtime::RuntimeMethods enum value - Param lhs; - Param rhs; - Param result; - }; - struct instr_add { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_bitAnd { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_bitOr { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_bitXor { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_shr { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_shl { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_bitAndConst { - MOTH_INSTR_HEADER - Param lhs; - int rhs; - Param result; - }; - struct instr_bitOrConst { - MOTH_INSTR_HEADER - Param lhs; - int rhs; - Param result; - }; - struct instr_bitXorConst { - MOTH_INSTR_HEADER - Param lhs; - int rhs; - Param result; - }; - struct instr_shrConst { - MOTH_INSTR_HEADER - Param lhs; - int rhs; - Param result; - }; - struct instr_shlConst { - MOTH_INSTR_HEADER - Param lhs; - int rhs; - Param result; - }; - struct instr_mul { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_sub { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_binopContext { - MOTH_INSTR_HEADER - uint alu; // offset inside the runtime methods - Param lhs; - Param rhs; - Param result; - }; - struct instr_loadThis { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_loadQmlContext { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_loadQmlImportedScripts { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_loadQmlSingleton { - MOTH_INSTR_HEADER - Param result; - int name; - }; + FOR_EACH_MOTH_INSTR(MOTH_EMIT_INSTR_MEMBERS) +}; - instr_common common; - instr_ret ret; - instr_line line; - instr_debug debug; - instr_loadRuntimeString loadRuntimeString; - instr_loadRegExp loadRegExp; - instr_move move; - instr_moveConst moveConst; - instr_swapTemps swapTemps; - instr_loadClosure loadClosure; - instr_loadName loadName; - instr_getGlobalLookup getGlobalLookup; - instr_storeName storeName; - instr_loadElement loadElement; - instr_loadElementLookup loadElementLookup; - instr_storeElement storeElement; - instr_storeElementLookup storeElementLookup; - instr_loadProperty loadProperty; - instr_getLookup getLookup; - instr_loadScopeObjectProperty loadScopeObjectProperty; - instr_loadContextObjectProperty loadContextObjectProperty; - instr_loadIdObject loadIdObject; - instr_loadQObjectProperty loadQObjectProperty; - instr_loadAttachedQObjectProperty loadAttachedQObjectProperty; - instr_storeProperty storeProperty; - instr_setLookup setLookup; - instr_storeScopeObjectProperty storeScopeObjectProperty; - instr_storeContextObjectProperty storeContextObjectProperty; - instr_storeQObjectProperty storeQObjectProperty; - instr_push push; - instr_callValue callValue; - instr_callProperty callProperty; - instr_callPropertyLookup callPropertyLookup; - instr_callScopeObjectProperty callScopeObjectProperty; - instr_callContextObjectProperty callContextObjectProperty; - instr_callElement callElement; - instr_callActivationProperty callActivationProperty; - instr_callGlobalLookup callGlobalLookup; - instr_callBuiltinThrow callBuiltinThrow; - instr_setExceptionHandler setExceptionHandler; - instr_callBuiltinUnwindException callBuiltinUnwindException; - instr_callBuiltinPushCatchScope callBuiltinPushCatchScope; - instr_callBuiltinPushScope callBuiltinPushScope; - instr_callBuiltinPopScope callBuiltinPopScope; - instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; - instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; - instr_callBuiltinDeleteMember callBuiltinDeleteMember; - instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; - instr_callBuiltinDeleteName callBuiltinDeleteName; - instr_callBuiltinTypeofScopeObjectProperty callBuiltinTypeofScopeObjectProperty; - instr_callBuiltinTypeofContextObjectProperty callBuiltinTypeofContextObjectProperty; - instr_callBuiltinTypeofMember callBuiltinTypeofMember; - instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; - instr_callBuiltinTypeofName callBuiltinTypeofName; - instr_callBuiltinTypeofValue callBuiltinTypeofValue; - instr_callBuiltinDeclareVar callBuiltinDeclareVar; - instr_callBuiltinDefineArray callBuiltinDefineArray; - instr_callBuiltinDefineObjectLiteral callBuiltinDefineObjectLiteral; - instr_callBuiltinSetupArgumentsObject callBuiltinSetupArgumentsObject; - instr_callBuiltinConvertThisToObject callBuiltinConvertThisToObject; - instr_createValue createValue; - instr_createProperty createProperty; - instr_constructPropertyLookup constructPropertyLookup; - instr_createActivationProperty createActivationProperty; - instr_constructGlobalLookup constructGlobalLookup; - instr_jump jump; - instr_jumpEq jumpEq; - instr_jumpNe jumpNe; - instr_unot unot; - instr_unotBool unotBool; - instr_uplus uplus; - instr_uminus uminus; - instr_ucompl ucompl; - instr_ucomplInt ucomplInt; - instr_increment increment; - instr_decrement decrement; - instr_binop binop; - instr_add add; - instr_bitAnd bitAnd; - instr_bitOr bitOr; - instr_bitXor bitXor; - instr_shr shr; - instr_shl shl; - instr_bitAndConst bitAndConst; - instr_bitOrConst bitOrConst; - instr_bitXorConst bitXorConst; - instr_shrConst shrConst; - instr_shlConst shlConst; - instr_mul mul; - instr_sub sub; - instr_binopContext binopContext; - instr_loadThis loadThis; - instr_loadQmlContext loadQmlContext; - instr_loadQmlImportedScripts loadQmlImportedScripts; - instr_loadQmlSingleton loadQmlSingleton; - - static int size(Type type); +struct InstrInfo +{ + static const int argumentCount[]; + static int size(Instr::Type type); }; +Q_STATIC_ASSERT(MOTH_NUM_INSTRUCTIONS() < 128); + template<int N> struct InstrMeta { }; QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wuninitialized") -#define MOTH_INSTR_META_TEMPLATE(I, FMT) \ - template<> struct InstrMeta<(int)Instr::I> { \ - enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \ - typedef Instr::instr_##FMT DataType; \ - static const DataType &data(const Instr &instr) { return instr.FMT; } \ - static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \ - static void setDataNoCommon(Instr &instr, const DataType &v) \ - { memcpy(reinterpret_cast<char *>(&instr.FMT) + sizeof(Instr::instr_common), \ - reinterpret_cast<const char *>(&v) + sizeof(Instr::instr_common), \ - Size - sizeof(Instr::instr_common)); } \ +QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") +#define MOTH_INSTR_META_TEMPLATE(I) \ + template<> struct InstrMeta<int(Instr::Type::I)> { \ + enum { Size = MOTH_INSTR_SIZE(I) }; \ + typedef Instr::instr_##I DataType; \ + static const DataType &data(const Instr &instr) { return instr.I; } \ + static void setData(Instr &instr, const DataType &v) \ + { memcpy(reinterpret_cast<char *>(&instr.I), \ + reinterpret_cast<const char *>(&v), \ + Size); } \ }; FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); #undef MOTH_INSTR_META_TEMPLATE @@ -919,6 +519,14 @@ class InstrData : public InstrMeta<InstrType>::DataType { }; +struct Instruction { +#define MOTH_INSTR_DATA_TYPEDEF(I) typedef InstrData<int(Instr::Type::I)> I; +FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) +#undef MOTH_INSTR_DATA_TYPEDEF +private: + Instruction(); +}; + } // namespace Moth } // namespace QV4 diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp deleted file mode 100644 index d25d1733c8..0000000000 --- a/src/qml/compiler/qv4isel_moth.cpp +++ /dev/null @@ -1,1522 +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]; - 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::Push push; - push.value = quint32(locals); - addInstruction(push); - - 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; -#if QT_CONFIG(qml_debug) - 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(); - - codeRefs.insert(_function, squeezeCode()); - - 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++] = codeRefs[irFunction]; - 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_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::OpIncrement: { - Instruction::Increment inc; - inc.source = getParam(source); - inc.result = getResultParam(target); - addInstruction(inc); - return; - } - case IR::OpDecrement: { - Instruction::Decrement 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()) { - 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() -{ -#if QT_CONFIG(qml_debug) - 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) -{ - 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(); - - const int classId = registerJSClass(keyValuePairCount, keyValuePairs); - - // Process key/value pairs first - IR::ExprList *it = keyValuePairs; - for (int i = 0; i < keyValuePairCount; ++i, it = it->next) { - // Skip name - it = it->next; - - bool isData = it->expr->asConst()->value; - it = it->next; - - if (IR::Const *c = it->expr->asConst()) { - 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; - } - } - - // Process array values - uint arrayValueCount = 0; - it = arrayEntries; - while (it) { - IR::Const *index = it->expr->asConst(); - 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(); - 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(); - } -} - - -CompilationUnit::~CompilationUnit() -{ -} - -#if !defined(V4_BOOTSTRAP) - -void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) -{ - runtimeFunctions.resize(data->functionTableSize); - runtimeFunctions.fill(0); - for (int i = 0 ;i < runtimeFunctions.size(); ++i) { - const QV4::CompiledData::Function *compiledFunction = data->functionAt(i); - - QV4::Function *runtimeFunction = new QV4::Function(engine, this, compiledFunction, &VME::exec); - runtimeFunction->codeData = reinterpret_cast<const uchar *>(codeRefs.at(i).constData()); - 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); - const char *codePtr = const_cast<const char *>(reinterpret_cast<const char *>(basePtr + compiledFunction->codeOffset)); - QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize); - codeRefs[i] = code; - } - - return true; -} - -#endif // 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; - } - - QByteArray code = codeRefs.at(i); - - written = device->write(code.constData(), compiledFunction->codeSize); - if (written != qint64(compiledFunction->codeSize)) { - *errorString = device->errorString(); - return false; - } - } - return true; -} - -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 db49177783..0000000000 --- a/src/qml/compiler/qv4isel_moth_p.h +++ /dev/null @@ -1,240 +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" - -#if !defined(V4_BOOTSTRAP) -QT_REQUIRE_CONFIG(qml_interpreter); -#endif - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace Moth { - -struct CompilationUnit : public QV4::CompiledData::CompilationUnit -{ - virtual ~CompilationUnit(); -#if !defined(V4_BOOTSTRAP) - void linkBackendToEngine(QV4::ExecutionEngine *engine) override; - bool memoryMapCode(QString *errorString) override; -#endif - void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) override; - bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) override; - - QVector<QByteArray> codeRefs; - -}; - -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); - - struct Instruction { -#define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData<Instr::I> I; - FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) -#undef MOTH_INSTR_DATA_TYPEDEF - private: - Instruction(); - }; - - 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; - QHash<IR::Function *, QByteArray> codeRefs; -}; - -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) override final - { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); } - bool jitCompileRegexps() const override final - { return false; } - QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() 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 037c02e5ea..0000000000 --- a/src/qml/compiler/qv4isel_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_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(int count, IR::ExprList *args) { return jsGenerator->registerJSClass(count, args); } - 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 0b0ed391fb..0000000000 --- a/src/qml/compiler/qv4jsir.cpp +++ /dev/null @@ -1,993 +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 OpIncrement: return "incr"; - case OpDecrement: return "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) - , usesThis(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 7bdad31260..0000000000 --- a/src/qml/compiler/qv4jsir_p.h +++ /dev/null @@ -1,1813 +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> -#ifndef V4_BOOTSTRAP -#include <private/qqmlmetatype_p.h> -#endif - -#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; -struct QQmlImportRef; -class QQmlTypeNameCache; - -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, - OpIncrement, - OpDecrement, - - 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), import(nullptr), propertyCache(nullptr), typenameCache(nullptr), owner(nullptr), flags(0) {} - - bool isValid() const { return !!resolveMember; } - void clear() { *this = MemberExpressionResolver(); } - - ResolveFunction resolveMember; -#ifndef V4_BOOTSTRAP - QQmlType qmlType; -#endif - const QQmlImportRef *import; - QQmlPropertyCache *propertyCache; - QQmlTypeNameCache *typenameCache; - 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 -#if !QT_CONFIG(qml_debug) - 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) -#if QT_CONFIG(qml_debug) - , 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; - - 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 cf79168c6c..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 OpIncrement: - case OpDecrement: - 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) override 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 OpIncrement: - case OpDecrement: - 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 OpIncrement: - constOperand->value = constOperand->value + 1; - doneSomething = true; - break; - case OpDecrement: - 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 *) override 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 |