diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2017-04-07 14:48:56 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2017-04-07 12:53:23 +0000 |
commit | 017350a8a9b4ac866c9b79186bf5a1dd6f6f06ec (patch) | |
tree | f98070d9bf0bd5659d34355b5eb9647637874187 /src/qml/compiler | |
parent | b361a59c699fca02379c149cf0b9c59490a1ba62 (diff) | |
parent | 67d1d7843ab5e1c904c8f0f76eadb9fc3f1bbb17 (diff) |
Merge remote-tracking branch 'origin/5.9' into dev
Conflicts:
src/qml/jit/qv4assembler.cpp
src/qml/jit/qv4assembler_p.h
src/qml/jit/qv4isel_masm.cpp
src/qml/jsruntime/qv4vme_moth.cpp
Change-Id: I865d794e550a263387a39ca8d051ebf48b70cbc0
Diffstat (limited to 'src/qml/compiler')
-rw-r--r-- | src/qml/compiler/compiler.pri | 6 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler.cpp | 343 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler_p.h | 80 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 37 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 2 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_util_p.h | 76 | ||||
-rw-r--r-- | src/qml/compiler/qv4jsir_p.h | 1 | ||||
-rw-r--r-- | src/qml/compiler/qv4jssimplifier.cpp | 384 | ||||
-rw-r--r-- | src/qml/compiler/qv4jssimplifier_p.h | 154 |
10 files changed, 642 insertions, 444 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index fa66d3a6e3..871f28f2d0 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -10,7 +10,8 @@ HEADERS += \ $$PWD/qv4isel_util_p.h \ $$PWD/qv4ssa_p.h \ $$PWD/qqmlirbuilder_p.h \ - $$PWD/qqmltypecompiler_p.h + $$PWD/qqmltypecompiler_p.h \ + $$PWD/qv4jssimplifier_p.h SOURCES += \ $$PWD/qv4compileddata.cpp \ @@ -19,7 +20,8 @@ SOURCES += \ $$PWD/qv4isel_p.cpp \ $$PWD/qv4jsir.cpp \ $$PWD/qv4ssa.cpp \ - $$PWD/qqmlirbuilder.cpp + $$PWD/qqmlirbuilder.cpp \ + $$PWD/qv4jssimplifier.cpp !qmldevtools_build { diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index a3b8784fc8..d1d22be0ac 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -47,6 +47,7 @@ #include <private/qv4ssa_p.h> #include "qqmlpropertycachecreator_p.h" +#include "qv4jssimplifier_p.h" #define COMPILE_EXCEPTION(token, desc) \ { \ @@ -144,7 +145,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() if (!jsCodeGen.generateCodeForComponents()) return nullptr; - QQmlJavaScriptBindingExpressionSimplificationPass pass(this); + QQmlJavaScriptBindingExpressionSimplificationPass pass(document->objects, &document->jsModule, &document->jsGenerator); pass.reduceTranslationBindings(); QV4::ExecutionEngine *v4 = engine->v4engine(); @@ -1429,344 +1430,4 @@ void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex) } } -QQmlJavaScriptBindingExpressionSimplificationPass::QQmlJavaScriptBindingExpressionSimplificationPass(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , jsModule(typeCompiler->jsIRModule()) -{ - -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings() -{ - for (int i = 0; i < qmlObjects.count(); ++i) - reduceTranslationBindings(i); - if (!irFunctionsToRemove.isEmpty()) { - QQmlIRFunctionCleanser cleanser(compiler, irFunctionsToRemove); - cleanser.clean(); - } -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings(int objectIndex) -{ - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Script) - continue; - - const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex); - QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex); - if (simplifyBinding(irFunction, binding)) { - irFunctionsToRemove.append(irFunctionIndex); - jsModule->functions[irFunctionIndex] = 0; - delete irFunction; - } - } -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::visitMove(QV4::IR::Move *move) -{ - QV4::IR::Temp *target = move->target->asTemp(); - if (!target || target->kind != QV4::IR::Temp::VirtualRegister) { - discard(); - return; - } - - if (QV4::IR::Call *call = move->source->asCall()) { - if (QV4::IR::Name *n = call->base->asName()) { - if (n->builtin == QV4::IR::Name::builtin_invalid) { - visitFunctionCall(n->id, call->args, target); - return; - } - } - discard(); - return; - } - - if (QV4::IR::Name *n = move->source->asName()) { - if (n->builtin == QV4::IR::Name::builtin_qml_context - || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object) { - // these are free of side-effects - return; - } - discard(); - return; - } - - if (!move->source->asTemp() && !move->source->asString() && !move->source->asConst()) { - discard(); - return; - } - - _temps[target->index] = move->source; -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target) -{ - // more than one function call? - if (_nameOfFunctionCalled) { - discard(); - return; - } - - _nameOfFunctionCalled = name; - - _functionParameters.clear(); - while (args) { - int slot; - if (QV4::IR::Temp *param = args->expr->asTemp()) { - if (param->kind != QV4::IR::Temp::VirtualRegister) { - discard(); - return; - } - slot = param->index; - _functionParameters.append(slot); - } else if (QV4::IR::Const *param = args->expr->asConst()) { - slot = --_synthesizedConsts; - Q_ASSERT(!_temps.contains(slot)); - _temps[slot] = param; - _functionParameters.append(slot); - } - args = args->next; - } - - _functionCallReturnValue = target->index; -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::visitRet(QV4::IR::Ret *ret) -{ - // nothing initialized earlier? - if (_returnValueOfBindingExpression != -1) { - discard(); - return; - } - QV4::IR::Temp *target = ret->expr->asTemp(); - if (!target || target->kind != QV4::IR::Temp::VirtualRegister) { - discard(); - return; - } - _returnValueOfBindingExpression = target->index; -} - -bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding) -{ - _canSimplify = true; - _nameOfFunctionCalled = 0; - _functionParameters.clear(); - _functionCallReturnValue = -1; - _temps.clear(); - _returnValueOfBindingExpression = -1; - _synthesizedConsts = 0; - - // It would seem unlikely that function with some many basic blocks (after optimization) - // consists merely of a qsTr call or a constant value return ;-) - if (function->basicBlockCount() > 10) - return false; - - for (QV4::IR::BasicBlock *bb : function->basicBlocks()) { - for (QV4::IR::Stmt *s : bb->statements()) { - visit(s); - if (!_canSimplify) - return false; - } - } - - if (_returnValueOfBindingExpression == -1) - return false; - - if (_nameOfFunctionCalled) { - if (_functionCallReturnValue != _returnValueOfBindingExpression) - return false; - return detectTranslationCallAndConvertBinding(binding); - } - - return false; -} - -bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAndConvertBinding(QmlIR::Binding *binding) -{ - if (*_nameOfFunctionCalled == QLatin1String("qsTr")) { - QString translation; - QV4::CompiledData::TranslationData translationData; - translationData.number = -1; - translationData.commentIndex = 0; // empty string - - QVector<int>::ConstIterator param = _functionParameters.constBegin(); - QVector<int>::ConstIterator end = _functionParameters.constEnd(); - if (param == end) - return false; - - QV4::IR::String *stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - - translation = *stringParam->value; - - ++param; - if (param != end) { - stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - translationData.commentIndex = compiler->registerString(*stringParam->value); - ++param; - - if (param != end) { - QV4::IR::Const *constParam = _temps[*param]->asConst(); - if (!constParam || constParam->type != QV4::IR::SInt32Type) - return false; - - translationData.number = int(constParam->value); - ++param; - } - } - - if (param != end) - return false; - - binding->type = QV4::CompiledData::Binding::Type_Translation; - binding->stringIndex = compiler->registerString(translation); - binding->value.translationData = translationData; - return true; - } else if (*_nameOfFunctionCalled == QLatin1String("qsTrId")) { - QString id; - QV4::CompiledData::TranslationData translationData; - translationData.number = -1; - translationData.commentIndex = 0; // empty string, but unused - - QVector<int>::ConstIterator param = _functionParameters.constBegin(); - QVector<int>::ConstIterator end = _functionParameters.constEnd(); - if (param == end) - return false; - - QV4::IR::String *stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - - id = *stringParam->value; - - ++param; - if (param != end) { - QV4::IR::Const *constParam = _temps[*param]->asConst(); - if (!constParam || constParam->type != QV4::IR::SInt32Type) - return false; - - translationData.number = int(constParam->value); - ++param; - } - - if (param != end) - return false; - - binding->type = QV4::CompiledData::Binding::Type_TranslationById; - binding->stringIndex = compiler->registerString(id); - binding->value.translationData = translationData; - return true; - } else if (*_nameOfFunctionCalled == QLatin1String("QT_TR_NOOP") || *_nameOfFunctionCalled == QLatin1String("QT_TRID_NOOP")) { - QVector<int>::ConstIterator param = _functionParameters.constBegin(); - QVector<int>::ConstIterator end = _functionParameters.constEnd(); - if (param == end) - return false; - - QV4::IR::String *stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - - ++param; - if (param != end) - return false; - - binding->type = QV4::CompiledData::Binding::Type_String; - binding->stringIndex = compiler->registerString(*stringParam->value); - return true; - } else if (*_nameOfFunctionCalled == QLatin1String("QT_TRANSLATE_NOOP")) { - QVector<int>::ConstIterator param = _functionParameters.constBegin(); - QVector<int>::ConstIterator end = _functionParameters.constEnd(); - if (param == end) - return false; - - ++param; - if (param == end) - return false; - - QV4::IR::String *stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - - ++param; - if (param != end) - return false; - - binding->type = QV4::CompiledData::Binding::Type_String; - binding->stringIndex = compiler->registerString(*stringParam->value); - return true; - } - return false; -} - -QQmlIRFunctionCleanser::QQmlIRFunctionCleanser(QQmlTypeCompiler *typeCompiler, const QVector<int> &functionsToRemove) - : QQmlCompilePass(typeCompiler) - , module(typeCompiler->jsIRModule()) - , functionsToRemove(functionsToRemove) -{ -} - -void QQmlIRFunctionCleanser::clean() -{ - QVector<QV4::IR::Function*> newFunctions; - newFunctions.reserve(module->functions.count() - functionsToRemove.count()); - - newFunctionIndices.resize(module->functions.count()); - - for (int i = 0; i < module->functions.count(); ++i) { - QV4::IR::Function *f = module->functions.at(i); - Q_ASSERT(f || functionsToRemove.contains(i)); - if (f) { - newFunctionIndices[i] = newFunctions.count(); - newFunctions << f; - } - } - - module->functions = newFunctions; - - for (QV4::IR::Function *function : qAsConst(module->functions)) { - for (QV4::IR::BasicBlock *block : function->basicBlocks()) { - for (QV4::IR::Stmt *s : block->statements()) { - visit(s); - } - } - } - - for (QmlIR::Object *obj : qAsConst(*compiler->qmlObjects())) { - for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i) - obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)]; - } -} - -void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s) -{ - - switch (s->stmtKind) { - case QV4::IR::Stmt::PhiStmt: - // nothing to do - break; - default: - STMT_VISIT_ALL_KINDS(s); - break; - } -} - -void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e) -{ - switch (e->exprKind) { - case QV4::IR::Expr::ClosureExpr: { - auto closure = e->asClosure(); - closure->value = newFunctionIndices.at(closure->value); - } break; - default: - EXPR_VISIT_ALL_KINDS(e); - break; - } -} - QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 11261e3099..79fc073d8b 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -345,86 +345,6 @@ private: const QQmlPropertyCacheVector * const propertyCaches; }; -class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass -{ -public: - QQmlJavaScriptBindingExpressionSimplificationPass(QQmlTypeCompiler *typeCompiler); - - void reduceTranslationBindings(); - -private: - void reduceTranslationBindings(int objectIndex); - - void visit(QV4::IR::Stmt *s) - { - switch (s->stmtKind) { - case QV4::IR::Stmt::MoveStmt: - visitMove(s->asMove()); - break; - case QV4::IR::Stmt::RetStmt: - visitRet(s->asRet()); - break; - case QV4::IR::Stmt::CJumpStmt: - discard(); - break; - case QV4::IR::Stmt::ExpStmt: - discard(); - break; - case QV4::IR::Stmt::JumpStmt: - break; - case QV4::IR::Stmt::PhiStmt: - break; - } - } - - void visitMove(QV4::IR::Move *move); - void visitRet(QV4::IR::Ret *ret); - - void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target); - - void discard() { _canSimplify = false; } - - bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding); - bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding); - - const QVector<QmlIR::Object*> &qmlObjects; - QV4::IR::Module *jsModule; - - bool _canSimplify; - const QString *_nameOfFunctionCalled; - QVector<int> _functionParameters; - int _functionCallReturnValue; - - QHash<int, QV4::IR::Expr*> _temps; - int _returnValueOfBindingExpression; - int _synthesizedConsts; - - QVector<int> irFunctionsToRemove; -}; - -class QQmlIRFunctionCleanser : public QQmlCompilePass -{ -public: - QQmlIRFunctionCleanser(QQmlTypeCompiler *typeCompiler, const QVector<int> &functionsToRemove); - - void clean(); - -private: - virtual void visitMove(QV4::IR::Move *s) { - visit(s->source); - visit(s->target); - } - - void visit(QV4::IR::Stmt *s); - void visit(QV4::IR::Expr *e); - -private: - QV4::IR::Module *module; - const QVector<int> &functionsToRemove; - - QVector<int> newFunctionIndices; -}; - QT_END_NAMESPACE #endif // QQMLTYPECOMPILER_P_H diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 3234e7ee63..693a4230ba 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -92,6 +92,27 @@ static bool cjumpCanHandle(IR::AluOp op) } } +static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body, + const SourceLocation &fallback) +{ + switch (body->kind) { + // Statements where we might never execute the last line. + // Use the fallback. + case Statement::Kind_ConditionalExpression: + case Statement::Kind_ForEachStatement: + case Statement::Kind_ForStatement: + case Statement::Kind_IfStatement: + case Statement::Kind_LocalForEachStatement: + case Statement::Kind_LocalForStatement: + case Statement::Kind_WhileStatement: + setLocation(s, fallback); + break; + default: + setLocation(s, body->lastSourceLocation()); + break; + } +} + Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) : _cg(cg) , _sourceCode(sourceCode) @@ -2269,7 +2290,7 @@ bool Codegen::visit(DoWhileStatement *ast) _block = loopbody; statement(ast->statement); - setLocation(_block->JUMP(loopcond), ast->statement->lastSourceLocation()); + setJumpOutLocation(_block->JUMP(loopcond), ast->statement, ast->semicolonToken); _block = loopcond; condition(ast->expression, loopbody, loopend); @@ -2334,7 +2355,7 @@ bool Codegen::visit(ForEachStatement *ast) return false; move(*init, _block->TEMP(temp)); statement(ast->statement); - setLocation(_block->JUMP(foreachin), ast->lastSourceLocation()); + setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken); _block = foreachin; @@ -2373,7 +2394,7 @@ bool Codegen::visit(ForStatement *ast) _block = forbody; statement(ast->statement); - setLocation(_block->JUMP(forstep), ast->lastSourceLocation()); + setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken); _block = forstep; statement(ast->expression); @@ -2399,12 +2420,12 @@ bool Codegen::visit(IfStatement *ast) _block = iftrue; statement(ast->ok); - _block->JUMP(endif); + setJumpOutLocation(_block->JUMP(endif), ast->ok, ast->ifToken); if (ast->ko) { _block = iffalse; statement(ast->ko); - _block->JUMP(endif); + setJumpOutLocation(_block->JUMP(endif), ast->ko, ast->elseToken); } _block = endif; @@ -2473,7 +2494,7 @@ bool Codegen::visit(LocalForEachStatement *ast) int temp = _block->newTemp(); move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); statement(ast->statement); - setLocation(_block->JUMP(foreachin), ast->lastSourceLocation()); + setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken); _block = foreachin; @@ -2512,7 +2533,7 @@ bool Codegen::visit(LocalForStatement *ast) _block = forbody; statement(ast->statement); - setLocation(_block->JUMP(forstep), ast->lastSourceLocation()); + setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken); _block = forstep; statement(ast->expression); @@ -2813,7 +2834,7 @@ bool Codegen::visit(WhileStatement *ast) _block = whilebody; statement(ast->statement); - setLocation(_block->JUMP(whilecond), ast->lastSourceLocation()); + setJumpOutLocation(_block->JUMP(whilecond), ast->statement, ast->whileToken); _block = whileend; leaveLoop(); diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 7940e5715e..d5e91a1bc6 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -461,6 +461,7 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return true; #else + Q_UNUSED(outputFileName) *errorString = QStringLiteral("features.temporaryfile is disabled."); return false; #endif // QT_CONFIG(temporaryfile) @@ -745,7 +746,7 @@ static QByteArray ownLibraryChecksum() if (dladdr(reinterpret_cast<const void *>(&ownLibraryChecksum), &libInfo) != 0) { QFile library(QFile::decodeName(libInfo.dli_fname)); if (library.open(QIODevice::ReadOnly)) { - QCryptographicHash hash(QCryptographicHash::Sha1); + QCryptographicHash hash(QCryptographicHash::Md5); hash.addData(&library); libraryChecksum = hash.result(); } diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index b81d724fe7..f7e63437e1 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -376,7 +376,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.version = QV4_DATA_STRUCTURE_VERSION; unit.qtVersion = QT_VERSION; memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum)); - unit.architectureIndex = registerString(QSysInfo::buildAbi()); + unit.architectureIndex = registerString(irModule->targetABI.isEmpty() ? QSysInfo::buildAbi() : irModule->targetABI); unit.codeGeneratorIndex = registerString(codeGeneratorName); memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum)); diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h index 1755193d32..e949e6f0ad 100644 --- a/src/qml/compiler/qv4isel_util_p.h +++ b/src/qml/compiler/qv4isel_util_p.h @@ -58,6 +58,59 @@ 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; @@ -72,36 +125,37 @@ inline bool canConvertToUnsignedInteger(double value) return uval == value && !(value == 0 && isNegative(value)); } -inline Primitive convertToValue(IR::Const *c) +template <typename PrimitiveType = Primitive> +inline PrimitiveType convertToValue(IR::Const *c) { switch (c->type) { case IR::MissingType: - return Primitive::emptyValue(); + return PrimitiveType::emptyValue(); case IR::NullType: - return Primitive::nullValue(); + return PrimitiveType::nullValue(); case IR::UndefinedType: - return Primitive::undefinedValue(); + return PrimitiveType::undefinedValue(); case IR::BoolType: - return Primitive::fromBoolean(c->value != 0); + return PrimitiveType::fromBoolean(c->value != 0); case IR::SInt32Type: - return Primitive::fromInt32(int(c->value)); + return PrimitiveType::fromInt32(int(c->value)); case IR::UInt32Type: - return Primitive::fromUInt32(unsigned(c->value)); + return PrimitiveType::fromUInt32(unsigned(c->value)); case IR::DoubleType: - return Primitive::fromDouble(c->value); + return PrimitiveType::fromDouble(c->value); case IR::NumberType: { int ival = (int)c->value; if (canConvertToSignedInteger(c->value)) { - return Primitive::fromInt32(ival); + return PrimitiveType::fromInt32(ival); } else { - return Primitive::fromDouble(c->value); + return PrimitiveType::fromDouble(c->value); } } default: Q_UNREACHABLE(); } // unreachable, but the function must return something - return Primitive::undefinedValue(); + return PrimitiveType::undefinedValue(); } class ConvertTemps diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index f7c7b76ea8..35cf0fc174 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -946,6 +946,7 @@ struct Q_QML_PRIVATE_EXPORT Module { QDateTime sourceTimeStamp; bool isQmlModule; // implies rootFunction is always 0 uint unitFlags; // flags merged into CompiledData::Unit::flags + QString targetABI; // fallback to QSysInfo::buildAbi() if empty #ifdef QT_NO_QML_DEBUGGER static const bool debugMode = false; #else diff --git a/src/qml/compiler/qv4jssimplifier.cpp b/src/qml/compiler/qv4jssimplifier.cpp new file mode 100644 index 0000000000..7d09218fe6 --- /dev/null +++ b/src/qml/compiler/qv4jssimplifier.cpp @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 "qv4jssimplifier_p.h" + +QT_BEGIN_NAMESPACE + +QQmlJavaScriptBindingExpressionSimplificationPass::QQmlJavaScriptBindingExpressionSimplificationPass(const QVector<QmlIR::Object*> &qmlObjects, QV4::IR::Module *jsModule, QV4::Compiler::JSUnitGenerator *unitGenerator) + : qmlObjects(qmlObjects) + , jsModule(jsModule) + , unitGenerator(unitGenerator) +{ + +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings() +{ + for (int i = 0; i < qmlObjects.count(); ++i) + reduceTranslationBindings(i); + if (!irFunctionsToRemove.isEmpty()) { + QQmlIRFunctionCleanser cleanser(jsModule, qmlObjects, irFunctionsToRemove); + cleanser.clean(); + } +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings(int objectIndex) +{ + const QmlIR::Object *obj = qmlObjects.at(objectIndex); + + for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + if (binding->type != QV4::CompiledData::Binding::Type_Script) + continue; + + const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex); + QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex); + if (simplifyBinding(irFunction, binding)) { + irFunctionsToRemove.append(irFunctionIndex); + jsModule->functions[irFunctionIndex] = 0; + delete irFunction; + } + } +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::visitMove(QV4::IR::Move *move) +{ + QV4::IR::Temp *target = move->target->asTemp(); + if (!target || target->kind != QV4::IR::Temp::VirtualRegister) { + discard(); + return; + } + + if (QV4::IR::Call *call = move->source->asCall()) { + if (QV4::IR::Name *n = call->base->asName()) { + if (n->builtin == QV4::IR::Name::builtin_invalid) { + visitFunctionCall(n->id, call->args, target); + return; + } + } + discard(); + return; + } + + if (QV4::IR::Name *n = move->source->asName()) { + if (n->builtin == QV4::IR::Name::builtin_qml_context + || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object) { + // these are free of side-effects + return; + } + discard(); + return; + } + + if (!move->source->asTemp() && !move->source->asString() && !move->source->asConst()) { + discard(); + return; + } + + _temps[target->index] = move->source; +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target) +{ + // more than one function call? + if (_nameOfFunctionCalled) { + discard(); + return; + } + + _nameOfFunctionCalled = name; + + _functionParameters.clear(); + while (args) { + int slot; + if (QV4::IR::Temp *param = args->expr->asTemp()) { + if (param->kind != QV4::IR::Temp::VirtualRegister) { + discard(); + return; + } + slot = param->index; + _functionParameters.append(slot); + } else if (QV4::IR::Const *param = args->expr->asConst()) { + slot = --_synthesizedConsts; + Q_ASSERT(!_temps.contains(slot)); + _temps[slot] = param; + _functionParameters.append(slot); + } + args = args->next; + } + + _functionCallReturnValue = target->index; +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::visitRet(QV4::IR::Ret *ret) +{ + // nothing initialized earlier? + if (_returnValueOfBindingExpression != -1) { + discard(); + return; + } + QV4::IR::Temp *target = ret->expr->asTemp(); + if (!target || target->kind != QV4::IR::Temp::VirtualRegister) { + discard(); + return; + } + _returnValueOfBindingExpression = target->index; +} + +bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding) +{ + _canSimplify = true; + _nameOfFunctionCalled = 0; + _functionParameters.clear(); + _functionCallReturnValue = -1; + _temps.clear(); + _returnValueOfBindingExpression = -1; + _synthesizedConsts = 0; + + // It would seem unlikely that function with some many basic blocks (after optimization) + // consists merely of a qsTr call or a constant value return ;-) + if (function->basicBlockCount() > 10) + return false; + + for (QV4::IR::BasicBlock *bb : function->basicBlocks()) { + for (QV4::IR::Stmt *s : bb->statements()) { + visit(s); + if (!_canSimplify) + return false; + } + } + + if (_returnValueOfBindingExpression == -1) + return false; + + if (_nameOfFunctionCalled) { + if (_functionCallReturnValue != _returnValueOfBindingExpression) + return false; + return detectTranslationCallAndConvertBinding(binding); + } + + return false; +} + +bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAndConvertBinding(QmlIR::Binding *binding) +{ + if (*_nameOfFunctionCalled == QLatin1String("qsTr")) { + QString translation; + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + translationData.commentIndex = 0; // empty string + + QVector<int>::ConstIterator param = _functionParameters.constBegin(); + QVector<int>::ConstIterator end = _functionParameters.constEnd(); + if (param == end) + return false; + + QV4::IR::String *stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + + translation = *stringParam->value; + + ++param; + if (param != end) { + stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + translationData.commentIndex = unitGenerator->registerString(*stringParam->value); + ++param; + + if (param != end) { + QV4::IR::Const *constParam = _temps[*param]->asConst(); + if (!constParam || constParam->type != QV4::IR::SInt32Type) + return false; + + translationData.number = int(constParam->value); + ++param; + } + } + + if (param != end) + return false; + + binding->type = QV4::CompiledData::Binding::Type_Translation; + binding->stringIndex = unitGenerator->registerString(translation); + binding->value.translationData = translationData; + return true; + } else if (*_nameOfFunctionCalled == QLatin1String("qsTrId")) { + QString id; + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + translationData.commentIndex = 0; // empty string, but unused + + QVector<int>::ConstIterator param = _functionParameters.constBegin(); + QVector<int>::ConstIterator end = _functionParameters.constEnd(); + if (param == end) + return false; + + QV4::IR::String *stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + + id = *stringParam->value; + + ++param; + if (param != end) { + QV4::IR::Const *constParam = _temps[*param]->asConst(); + if (!constParam || constParam->type != QV4::IR::SInt32Type) + return false; + + translationData.number = int(constParam->value); + ++param; + } + + if (param != end) + return false; + + binding->type = QV4::CompiledData::Binding::Type_TranslationById; + binding->stringIndex = unitGenerator->registerString(id); + binding->value.translationData = translationData; + return true; + } else if (*_nameOfFunctionCalled == QLatin1String("QT_TR_NOOP") || *_nameOfFunctionCalled == QLatin1String("QT_TRID_NOOP")) { + QVector<int>::ConstIterator param = _functionParameters.constBegin(); + QVector<int>::ConstIterator end = _functionParameters.constEnd(); + if (param == end) + return false; + + QV4::IR::String *stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + + ++param; + if (param != end) + return false; + + binding->type = QV4::CompiledData::Binding::Type_String; + binding->stringIndex = unitGenerator->registerString(*stringParam->value); + return true; + } else if (*_nameOfFunctionCalled == QLatin1String("QT_TRANSLATE_NOOP")) { + QVector<int>::ConstIterator param = _functionParameters.constBegin(); + QVector<int>::ConstIterator end = _functionParameters.constEnd(); + if (param == end) + return false; + + ++param; + if (param == end) + return false; + + QV4::IR::String *stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + + ++param; + if (param != end) + return false; + + binding->type = QV4::CompiledData::Binding::Type_String; + binding->stringIndex = unitGenerator->registerString(*stringParam->value); + return true; + } + return false; +} + +QQmlIRFunctionCleanser::QQmlIRFunctionCleanser(QV4::IR::Module *module, const QVector<QmlIR::Object *> &qmlObjects, const QVector<int> &functionsToRemove) + : module(module) + , qmlObjects(qmlObjects) + , functionsToRemove(functionsToRemove) +{ +} + +void QQmlIRFunctionCleanser::clean() +{ + QVector<QV4::IR::Function*> newFunctions; + newFunctions.reserve(module->functions.count() - functionsToRemove.count()); + + newFunctionIndices.resize(module->functions.count()); + + for (int i = 0; i < module->functions.count(); ++i) { + QV4::IR::Function *f = module->functions.at(i); + Q_ASSERT(f || functionsToRemove.contains(i)); + if (f) { + newFunctionIndices[i] = newFunctions.count(); + newFunctions << f; + } + } + + module->functions = newFunctions; + + for (QV4::IR::Function *function : qAsConst(module->functions)) { + for (QV4::IR::BasicBlock *block : function->basicBlocks()) { + for (QV4::IR::Stmt *s : block->statements()) { + visit(s); + } + } + } + + for (QmlIR::Object *obj : qmlObjects) { + for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i) + obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)]; + } +} + +void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s) +{ + + switch (s->stmtKind) { + case QV4::IR::Stmt::PhiStmt: + // nothing to do + break; + default: + STMT_VISIT_ALL_KINDS(s); + break; + } +} + +void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e) +{ + switch (e->exprKind) { + case QV4::IR::Expr::ClosureExpr: { + auto closure = e->asClosure(); + closure->value = newFunctionIndices.at(closure->value); + } break; + default: + EXPR_VISIT_ALL_KINDS(e); + break; + } +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4jssimplifier_p.h b/src/qml/compiler/qv4jssimplifier_p.h new file mode 100644 index 0000000000..ae8d74135c --- /dev/null +++ b/src/qml/compiler/qv4jssimplifier_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 QV4JSSIMPLIFIER +#define QV4JSSIMPLIFIER + +// +// 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 "qqmlirbuilder_p.h" + +QT_BEGIN_NAMESPACE + +namespace QmlIR { +struct Document; +} + +namespace QV4 { +namespace CompiledData { +struct QmlUnit; +struct Location; +} +} + +class QQmlJavaScriptBindingExpressionSimplificationPass +{ +public: + QQmlJavaScriptBindingExpressionSimplificationPass(const QVector<QmlIR::Object*> &qmlObjects, QV4::IR::Module *jsModule, QV4::Compiler::JSUnitGenerator *unitGenerator); + + void reduceTranslationBindings(); + +private: + void reduceTranslationBindings(int objectIndex); + + void visit(QV4::IR::Stmt *s) + { + switch (s->stmtKind) { + case QV4::IR::Stmt::MoveStmt: + visitMove(s->asMove()); + break; + case QV4::IR::Stmt::RetStmt: + visitRet(s->asRet()); + break; + case QV4::IR::Stmt::CJumpStmt: + discard(); + break; + case QV4::IR::Stmt::ExpStmt: + discard(); + break; + case QV4::IR::Stmt::JumpStmt: + break; + case QV4::IR::Stmt::PhiStmt: + break; + } + } + + void visitMove(QV4::IR::Move *move); + void visitRet(QV4::IR::Ret *ret); + + void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target); + + void discard() { _canSimplify = false; } + + bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding); + bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding); + + const QVector<QmlIR::Object*> &qmlObjects; + QV4::IR::Module *jsModule; + QV4::Compiler::JSUnitGenerator *unitGenerator; + + bool _canSimplify; + const QString *_nameOfFunctionCalled; + QVector<int> _functionParameters; + int _functionCallReturnValue; + + QHash<int, QV4::IR::Expr*> _temps; + int _returnValueOfBindingExpression; + int _synthesizedConsts; + + QVector<int> irFunctionsToRemove; +}; + +class QQmlIRFunctionCleanser +{ +public: + QQmlIRFunctionCleanser(QV4::IR::Module *module, const QVector<QmlIR::Object*> &qmlObjects, const QVector<int> &functionsToRemove); + + void clean(); + +private: + virtual void visitMove(QV4::IR::Move *s) { + visit(s->source); + visit(s->target); + } + + void visit(QV4::IR::Stmt *s); + void visit(QV4::IR::Expr *e); + +private: + QV4::IR::Module *module; + const QVector<QmlIR::Object*> &qmlObjects; + const QVector<int> &functionsToRemove; + + QVector<int> newFunctionIndices; +}; + +QT_END_NAMESPACE + +#endif // QV4JSSIMPLIFIER |